mte/unikernel/duniverse/ocaml-re/lib/perl.ml

361 lines
9.9 KiB
OCaml
Raw Normal View History

2025-11-11 02:07:51 +01:00
(*
RE - A regular expression library
Copyright (C) 2001 Jerome Vouillon
email: Jerome.Vouillon@pps.jussieu.fr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, with
linking exception; either version 2.1 of the License, or (at
your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*)
module Re = Core
exception Parse_error = Parse_buffer.Parse_error
exception Not_supported
let acc_digits =
let rec loop base digits acc i =
match digits with
| [] -> acc
| d :: digits ->
let acc = acc + (d * i) in
let i = i * i in
loop base digits acc i
in
fun ~base ~digits -> loop base digits 0 1
;;
let char_of_int x =
match char_of_int x with
| x -> x
| exception _ -> raise Parse_error
;;
type elem =
| Char of char
| Set of Ast.t
let char_b = Char '\008'
let char_newline = Char '\n'
let char_cr = Char '\r'
let char_tab = Char '\t'
let word_char = [ Re.alnum; Re.char '_' ]
let word = Set (Re.alt word_char)
let not_word = Set (Re.alt word_char)
let space = Set Re.space
let not_space = Set (Re.compl [ Re.space ])
let digit = Set Re.digit
let not_digit = Set (Re.compl [ Re.digit ])
let parse ~multiline ~dollar_endonly ~dotall ~ungreedy s =
let buf = Parse_buffer.create s in
let accept = Parse_buffer.accept buf in
let eos () = Parse_buffer.eos buf in
let test c = Parse_buffer.test buf c in
let unget () = Parse_buffer.unget buf in
let get () = Parse_buffer.get buf in
let greedy_mod r =
let gr = accept '?' in
let gr = if ungreedy then not gr else gr in
if gr then Re.non_greedy r else Re.greedy r
in
let rec regexp () = regexp' [ branch () ]
and regexp' left =
if accept '|' then regexp' (branch () :: left) else Re.alt (List.rev left)
and branch () = branch' []
and branch' left =
if eos () || test '|' || test ')'
then Re.seq (List.rev left)
else branch' (piece () :: left)
and in_brace ~f ~init =
match accept '{' with
| false -> None
| true ->
let rec loop acc =
if accept '}'
then acc
else (
let acc = f acc in
loop acc)
in
Some (loop init)
and piece () =
let r = atom () in
if accept '*'
then greedy_mod (Re.rep r)
else if accept '+'
then greedy_mod (Re.rep1 r)
else if accept '?'
then greedy_mod (Re.opt r)
else if accept '{'
then (
match Parse_buffer.integer buf with
| Some i ->
let j = if accept ',' then Parse_buffer.integer buf else Some i in
if not (accept '}') then raise Parse_error;
(match j with
| Some j when j < i -> raise Parse_error
| _ -> ());
greedy_mod (Re.repn r i j)
| None ->
unget ();
r)
else r
and atom () =
if accept '.'
then if dotall then Re.any else Re.notnl
else if accept '('
then
if accept '?'
then
if accept ':'
then (
let r = regexp () in
if not (accept ')') then raise Parse_error;
r)
else if accept '#'
then comment ()
else if accept '<'
then (
let name = name () in
let r = regexp () in
if not (accept ')') then raise Parse_error;
Re.group ~name r)
else raise Parse_error
else (
let r = regexp () in
if not (accept ')') then raise Parse_error;
Re.group r)
else if accept '^'
then if multiline then Re.bol else Re.bos
else if accept '$'
then if multiline then Re.eol else if dollar_endonly then Re.leol else Re.eos
else if accept '['
then if accept '^' then Re.compl (bracket []) else Re.alt (bracket [])
else if accept '\\'
then (
(* XXX
- Back-references
- \cx (control-x), \ddd
*)
if eos () then raise Parse_error;
match get () with
| 'w' -> Re.alt [ Re.alnum; Re.char '_' ]
| 'W' -> Re.compl [ Re.alnum; Re.char '_' ]
| 's' -> Re.space
| 'S' -> Re.compl [ Re.space ]
| 'd' -> Re.digit
| 'D' -> Re.compl [ Re.digit ]
| 'b' -> Re.alt [ Re.bow; Re.eow ]
| 'B' -> Re.not_boundary
| 'A' -> Re.bos
| 'Z' -> Re.leol
| 'z' -> Re.eos
| 'G' -> Re.start
| 'e' -> Re.char '\x1b'
| 'f' -> Re.char '\x0c'
| 'n' -> Re.char '\n'
| 'r' -> Re.char '\r'
| 't' -> Re.char '\t'
| 'Q' -> quote (Buffer.create 12)
| 'E' -> raise Parse_error
| 'x' ->
let c1, c2 =
match in_brace ~init:[] ~f:(fun acc -> hexdigit () :: acc) with
| Some [ c1; c2 ] -> c1, c2
| Some [ c2 ] -> 0, c2
| Some _ -> raise Parse_error
| None ->
let c1 = hexdigit () in
let c2 = hexdigit () in
c1, c2
in
let code = (c1 * 16) + c2 in
Re.char (char_of_int code)
| 'o' ->
(match
in_brace ~init:[] ~f:(fun acc ->
match maybe_octaldigit () with
| None -> raise Parse_error
| Some p -> p :: acc)
with
| None -> raise Parse_error
| Some digits -> Re.char (char_of_int (acc_digits ~base:8 ~digits)))
| 'a' .. 'z' | 'A' .. 'Z' -> raise Parse_error
| '0' .. '7' as n1 ->
let n2 = maybe_octaldigit () in
let n3 = maybe_octaldigit () in
(match n2, n3 with
| Some n2, Some n3 ->
let n1 = Char.code n1 - Char.code '0' in
Re.char (char_of_int ((n1 * (8 * 8)) + (n2 * 8) + n3))
| _, _ -> raise Not_supported)
| '8' .. '9' -> raise Not_supported
| c -> Re.char c)
else (
if eos () then raise Parse_error;
match get () with
| '*' | '+' | '?' | '{' | '\\' -> raise Parse_error
| c -> Re.char c)
and quote buf =
if accept '\\'
then (
if eos () then raise Parse_error;
match get () with
| 'E' -> Re.str (Buffer.contents buf)
| c ->
Buffer.add_char buf '\\';
Buffer.add_char buf c;
quote buf)
else (
if eos () then raise Parse_error;
Buffer.add_char buf (get ());
quote buf)
and hexdigit () =
if eos () then raise Parse_error;
match get () with
| '0' .. '9' as d -> Char.code d - Char.code '0'
| 'a' .. 'f' as d -> Char.code d - Char.code 'a' + 10
| 'A' .. 'F' as d -> Char.code d - Char.code 'A' + 10
| _ -> raise Parse_error
and maybe_octaldigit () =
if eos ()
then None
else (
match get () with
| '0' .. '7' as d -> Some (Char.code d - Char.code '0')
| _ -> None)
and name () =
if eos ()
then raise Parse_error
else (
match get () with
| ('_' | 'a' .. 'z' | 'A' .. 'Z') as c ->
let b = Buffer.create 32 in
Buffer.add_char b c;
name' b
| _ -> raise Parse_error)
and name' b =
if eos ()
then raise Parse_error
else (
match get () with
| ('_' | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9') as c ->
Buffer.add_char b c;
name' b
| '>' -> Buffer.contents b
| _ -> raise Parse_error)
and bracket s =
if s <> [] && accept ']'
then s
else (
match char () with
| Set st -> bracket (st :: s)
| Char c ->
if accept '-'
then
if accept ']'
then Re.char c :: Re.char '-' :: s
else
bracket
(match char () with
| Char c' -> Re.rg c c' :: s
| Set st' -> Re.char c :: Re.char '-' :: st' :: s)
else bracket (Re.char c :: s))
and char () =
if eos () then raise Parse_error;
let c = get () in
if c = '['
then (
if accept '=' then raise Not_supported;
match Posix_class.parse buf with
| Some set -> Set set
| None ->
if accept '.'
then (
if eos () then raise Parse_error;
let c = get () in
if not (accept '.') then raise Not_supported;
if not (accept ']') then raise Parse_error;
Char c)
else Char c)
else if c = '\\'
then (
if eos () then raise Parse_error;
let c = get () in
(* XXX
\127, ...
*)
match c with
| 'b' -> char_b
| 'n' -> char_newline (*XXX*)
| 'r' -> char_cr (*XXX*)
| 't' -> char_tab (*XXX*)
| 'w' -> word
| 'W' -> not_word
| 's' -> space
| 'S' -> not_space
| 'd' -> digit
| 'D' -> not_digit
| 'a' .. 'z' | 'A' .. 'Z' -> raise Parse_error
| '0' .. '9' -> raise Not_supported
| _ -> Char c)
else Char c
and comment () =
if eos () then raise Parse_error;
if accept ')'
then Re.epsilon
else (
Parse_buffer.junk buf;
comment ())
in
let res = regexp () in
if not (eos ()) then raise Parse_error;
res
;;
type opt =
[ `Ungreedy
| `Dotall
| `Dollar_endonly
| `Multiline
| `Anchored
| `Caseless
]
let re ?(opts = []) s =
let r =
parse
~multiline:(List.memq `Multiline opts)
~dollar_endonly:(List.memq `Dollar_endonly opts)
~dotall:(List.memq `Dotall opts)
~ungreedy:(List.memq `Ungreedy opts)
s
in
let r = if List.memq `Anchored opts then Re.seq [ Re.start; r ] else r in
let r = if List.memq `Caseless opts then Re.no_case r else r in
r
;;
let compile = Re.compile
let compile_pat ?(opts = []) s = compile (re ~opts s)
let re_result ?opts s =
match re ?opts s with
| s -> Ok s
| exception Not_supported -> Error `Not_supported
| exception Parse_error -> Error `Parse_error
;;