180 lines
4.4 KiB
OCaml
180 lines
4.4 KiB
OCaml
|
|
module Re = Core
|
||
|
|
|
||
|
|
exception Parse_error = Perl.Parse_error
|
||
|
|
exception Not_supported = Perl.Not_supported
|
||
|
|
|
||
|
|
type regexp = Re.re
|
||
|
|
|
||
|
|
type flag =
|
||
|
|
[ `CASELESS
|
||
|
|
| `MULTILINE
|
||
|
|
| `ANCHORED
|
||
|
|
| `DOTALL
|
||
|
|
]
|
||
|
|
|
||
|
|
type split_result =
|
||
|
|
| Text of string
|
||
|
|
| Delim of string
|
||
|
|
| Group of int * string
|
||
|
|
| NoGroup
|
||
|
|
|
||
|
|
type groups = Core.Group.t
|
||
|
|
|
||
|
|
let re ?(flags = []) pat =
|
||
|
|
let opts =
|
||
|
|
List.map
|
||
|
|
(function
|
||
|
|
| `CASELESS -> `Caseless
|
||
|
|
| `MULTILINE -> `Multiline
|
||
|
|
| `ANCHORED -> `Anchored
|
||
|
|
| `DOTALL -> `Dotall)
|
||
|
|
flags
|
||
|
|
in
|
||
|
|
Perl.re ~opts pat
|
||
|
|
;;
|
||
|
|
|
||
|
|
let re_result ?flags s =
|
||
|
|
match re ?flags s with
|
||
|
|
| s -> Ok s
|
||
|
|
| exception Not_supported -> Error `Not_supported
|
||
|
|
| exception Parse_error -> Error `Parse_error
|
||
|
|
;;
|
||
|
|
|
||
|
|
let regexp ?flags pat = Re.compile (re ?flags pat)
|
||
|
|
let extract ~rex s = Re.Group.all (Re.exec rex s)
|
||
|
|
let exec ~rex ?pos s = Re.exec rex ?pos s
|
||
|
|
let names rex = Re.group_names rex |> List.map fst |> Array.of_list
|
||
|
|
|
||
|
|
let get_named_substring_opt rex name s =
|
||
|
|
let rec loop = function
|
||
|
|
| [] -> None
|
||
|
|
| (n, i) :: rem when n = name ->
|
||
|
|
(match Re.Group.get_opt s i with
|
||
|
|
| None -> loop rem
|
||
|
|
| Some _ as s -> s)
|
||
|
|
| _ :: rem -> loop rem
|
||
|
|
in
|
||
|
|
loop (Re.group_names rex)
|
||
|
|
;;
|
||
|
|
|
||
|
|
let get_substring_ofs s i = Re.Group.offset s i
|
||
|
|
let pmatch ~rex s = Re.execp rex s
|
||
|
|
|
||
|
|
let substitute ~rex ~subst str =
|
||
|
|
let b = Buffer.create 1024 in
|
||
|
|
let rec loop pos on_match =
|
||
|
|
if Re.execp ~pos rex str
|
||
|
|
then (
|
||
|
|
let ss = Re.exec ~pos rex str in
|
||
|
|
let start, fin = Re.Group.offset ss 0 in
|
||
|
|
if on_match && start = pos && start = fin
|
||
|
|
then (
|
||
|
|
if (* Empty match following a match *)
|
||
|
|
pos < String.length str
|
||
|
|
then (
|
||
|
|
Buffer.add_char b str.[pos];
|
||
|
|
loop (pos + 1) false))
|
||
|
|
else (
|
||
|
|
let pat = Re.Group.get ss 0 in
|
||
|
|
Buffer.add_substring b str pos (start - pos);
|
||
|
|
Buffer.add_string b (subst pat);
|
||
|
|
if start = fin
|
||
|
|
then (
|
||
|
|
if (* Manually advance by one after an empty match *)
|
||
|
|
fin < String.length str
|
||
|
|
then (
|
||
|
|
Buffer.add_char b str.[fin];
|
||
|
|
loop (fin + 1) false))
|
||
|
|
else loop fin true))
|
||
|
|
else Buffer.add_substring b str pos (String.length str - pos)
|
||
|
|
in
|
||
|
|
loop 0 false;
|
||
|
|
Buffer.contents b
|
||
|
|
;;
|
||
|
|
|
||
|
|
let split ~rex s =
|
||
|
|
let rec split accu start =
|
||
|
|
if start = String.length s
|
||
|
|
then accu
|
||
|
|
else (
|
||
|
|
match
|
||
|
|
let g = Re.exec rex s ~pos:start in
|
||
|
|
if Group.stop g 0 = start then Re.exec rex s ~pos:(start + 1) else g
|
||
|
|
with
|
||
|
|
| exception Not_found -> String.sub s start (String.length s - start) :: accu
|
||
|
|
| g ->
|
||
|
|
let next = Group.stop g 0 in
|
||
|
|
split (String.sub s start (Group.start g 0 - start) :: accu) next)
|
||
|
|
in
|
||
|
|
match Re.exec rex s ~pos:0 with
|
||
|
|
| g ->
|
||
|
|
List.rev
|
||
|
|
(if Group.start g 0 = 0
|
||
|
|
then split [] (Group.stop g 0)
|
||
|
|
else split [ String.sub s 0 (Group.start g 0) ] (Group.stop g 0))
|
||
|
|
| exception Not_found -> if s = "" then [] else [ s ]
|
||
|
|
;;
|
||
|
|
|
||
|
|
(* From PCRE *)
|
||
|
|
let string_unsafe_sub s ofs len =
|
||
|
|
let r = Bytes.create len in
|
||
|
|
Bytes.unsafe_blit s ofs r 0 len;
|
||
|
|
Bytes.unsafe_to_string r
|
||
|
|
;;
|
||
|
|
|
||
|
|
let quote s =
|
||
|
|
let len = String.length s in
|
||
|
|
let buf = Bytes.create (len lsl 1) in
|
||
|
|
let pos = ref 0 in
|
||
|
|
for i = 0 to len - 1 do
|
||
|
|
match String.unsafe_get s i with
|
||
|
|
| ('\\' | '^' | '$' | '.' | '[' | '|' | '(' | ')' | '?' | '*' | '+' | '{') as c ->
|
||
|
|
Bytes.unsafe_set buf !pos '\\';
|
||
|
|
incr pos;
|
||
|
|
Bytes.unsafe_set buf !pos c;
|
||
|
|
incr pos
|
||
|
|
| c ->
|
||
|
|
Bytes.unsafe_set buf !pos c;
|
||
|
|
incr pos
|
||
|
|
done;
|
||
|
|
string_unsafe_sub buf 0 !pos
|
||
|
|
;;
|
||
|
|
|
||
|
|
let full_split ?(max = 0) ~rex s =
|
||
|
|
if String.length s = 0
|
||
|
|
then []
|
||
|
|
else if max = 1
|
||
|
|
then [ Text s ]
|
||
|
|
else (
|
||
|
|
let results = Re.split_full rex s in
|
||
|
|
let matches =
|
||
|
|
List.map
|
||
|
|
(function
|
||
|
|
| `Text s -> [ Text s ]
|
||
|
|
| `Delim d ->
|
||
|
|
let matches = Re.Group.all_offset d in
|
||
|
|
let delim = Re.Group.get d 0 in
|
||
|
|
Delim delim
|
||
|
|
::
|
||
|
|
(let l = ref [] in
|
||
|
|
for i = 1 to Array.length matches - 1 do
|
||
|
|
l
|
||
|
|
:= (if matches.(i) = (-1, -1) then NoGroup else Group (i, Re.Group.get d i))
|
||
|
|
:: !l
|
||
|
|
done;
|
||
|
|
List.rev !l))
|
||
|
|
results
|
||
|
|
in
|
||
|
|
List.concat matches)
|
||
|
|
;;
|
||
|
|
|
||
|
|
type substrings = Group.t
|
||
|
|
|
||
|
|
let get_substring s i = Re.Group.get s i
|
||
|
|
|
||
|
|
let get_named_substring rex name s =
|
||
|
|
match get_named_substring_opt rex name s with
|
||
|
|
| None -> raise Not_found
|
||
|
|
| Some s -> s
|
||
|
|
;;
|