This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,39 @@
(library
(name re_tests)
(libraries
re_private
;; This is because of the (implicit_transitive_deps false)
;; in dune-project
ppx_expect.config
ppx_expect.config_types
ppx_expect
ppx_expect_common
base
str
ppx_inline_test.config)
(inline_tests
(modes native js))
(preprocess
(pps ppx_expect)))
;; ppx_expect v16 depends on ppx_expect.common
(subdir
ppx_expect_common
(library
(name ppx_expect_common)
(enabled_if
(< %{ocaml_version} 5.0))
(libraries (re_export ppx_expect.common)))
(library
(name ppx_expect_common)
(enabled_if
(>= %{ocaml_version} 5.0))))
;; this hackery is needed because ppx_expect itself uses re, therefore we need to mangle
;; the library name
(subdir
private_re
(library
(name re_private))
(copy_files %{project_root}/lib/*.{ml,mli}))

View file

@ -0,0 +1,85 @@
module Re = Re_private.Re
include Re_private.Import
module Fmt = Re_private.Fmt
module Dyn = Re_private.Dyn
let printf = Printf.printf
let t re s =
let group = Re.exec_opt (Re.compile re) s in
Format.printf "%a@." (Fmt.opt Re.Group.pp) group
;;
let re_whitespace = Re.Pcre.regexp "[\t ]+"
let re_eol = Re.compile Re.eol
let re_bow = Re.compile Re.bow
let re_eow = Re.compile Re.eow
let strings = Format.printf "[%a]@." Fmt.(list ~pp_sep:(Fmt.lit "; ") Fmt.quoted_string)
let re_empty = Re.Posix.compile_pat ""
let invalid_argument f =
match f () with
| s -> ignore s
| exception Invalid_argument s -> Format.printf "Invalid_argument %S@." s
;;
let exec_partial_detailed ?pos re s =
let re = Re.compile re in
let res = Re.exec_partial_detailed ?pos re s in
match res with
| `Mismatch -> Format.printf "`Mismatch@."
| `Partial position -> Format.printf "`Partial %d@." position
| `Full groups ->
Re.Group.all_offset groups
|> Array.to_list
|> List.map ~f:(fun (a, b) ->
Printf.sprintf
"%d,%d,%s"
a
b
(match String.sub s a (b - a) with
| exception Invalid_argument _ -> "<No match>"
| s -> Printf.sprintf "%S" s))
|> String.concat ";"
|> Format.printf "`Full [|%s|]@."
;;
let or_not_found f fmt v =
match v () with
| exception Not_found -> Format.fprintf fmt "Not_found"
| s -> f fmt s
;;
let array f fmt v =
Format.fprintf fmt "[| %a |]" (Fmt.list ~pp_sep:(Fmt.lit "; ") f) (Array.to_list v)
;;
let offset fmt (x, y) = Format.fprintf fmt "(%d, %d)" x y
let test_re ?pos ?len r s =
let offsets () = Re.Group.all_offset (Re.exec ?pos ?len (Re.compile r) s) in
Format.printf "%a@." (or_not_found (array offset)) offsets
;;
let rec sexp_of_dyn (t : Re_private.Dyn.t) : Base.Sexp.t =
match t with
| Int i -> Atom (Int.to_string i)
| String s -> Atom s
| Tuple xs -> List (List.map xs ~f:sexp_of_dyn)
| Enum s -> Atom s
| List xs -> List (List.map ~f:sexp_of_dyn xs)
| Variant (name, []) -> Atom name
| Variant (name, xs) ->
let xs = List.map xs ~f:sexp_of_dyn in
(match xs with
| [] -> List []
| xs -> List (Atom name :: xs))
| Record fields ->
List
(List.filter_map fields ~f:(fun (name, v) ->
match sexp_of_dyn v with
| List [] -> None
| sexp -> Some (Base.Sexp.List [ Atom name; sexp ])))
;;
let print_dyn dyn = sexp_of_dyn dyn |> Base.Sexp.to_string_hum |> print_endline

View file

@ -0,0 +1,76 @@
open Import
let print re result =
Printf.printf
"%s: %s\n"
re
(match result with
| Ok _ -> "backward range parsed"
| Error `Parse_error -> "parse error"
| Error `Not_supported -> "not supported")
;;
let cases = [ "[1-0]"; "[5-1]"; "[6-6]"; "[z-a]"; "[b-b]" ]
let test f =
List.iter cases ~f:(fun re ->
let result = f re in
print re result)
;;
let%expect_test "perl" =
test Re.Perl.re_result;
[%expect
{|
[1-0]: backward range parsed
[5-1]: backward range parsed
[6-6]: backward range parsed
[z-a]: backward range parsed
[b-b]: backward range parsed
|}]
;;
let%expect_test "pcre" =
test Re.Pcre.re_result;
[%expect
{|
[1-0]: backward range parsed
[5-1]: backward range parsed
[6-6]: backward range parsed
[z-a]: backward range parsed
[b-b]: backward range parsed
|}]
;;
let%expect_test "posix" =
test Re.Posix.re_result;
[%expect
{|
[1-0]: backward range parsed
[5-1]: backward range parsed
[6-6]: backward range parsed
[z-a]: backward range parsed
[b-b]: backward range parsed
|}]
;;
(* CR-someday rgrinberg: is this correct? *)
let%expect_test "emacs" =
test Re.Emacs.re_result;
[%expect
{|
[1-0]: backward range parsed
[5-1]: backward range parsed
[6-6]: backward range parsed
[z-a]: backward range parsed
[b-b]: backward range parsed
|}]
;;
(* We allow backward ranges in re. We could forbid them? *)
let%expect_test "re" =
Format.printf "%a@." Re.pp (Re.rg '5' '0');
[%expect {| (Set 48-53) |}];
Format.printf "%a@." Re.pp (Re.rg '0' '5');
[%expect {| (Set 48-53) |}]
;;

View file

@ -0,0 +1,28 @@
open Import
module Ast = Re_private.Ast
let test f string =
match f string with
| Ok res -> print_dyn (Ast.to_dyn res)
| Error _ -> assert false
;;
let%expect_test "pcre" =
test Re.Pcre.re_result "(a|b|c)";
[%expect {| (Group (Set (Cast (Alternative (Cset 97) (Cset 98) (Cset 99))))) |}]
;;
let%expect_test "emacs" =
test Re.Emacs.re_result {|\(a\|b\|c\)|};
[%expect {| (Group (Set (Cast (Alternative (Cset 97) (Cset 98) (Cset 99))))) |}]
;;
let%expect_test "perl" =
test Re.Perl.re_result "(a|b|c)";
[%expect {| (Group (Set (Cast (Alternative (Cset 97) (Cset 98) (Cset 99))))) |}]
;;
let%expect_test "posix" =
test Re.Posix.re_result "(a|b|c)";
[%expect {| (Group (Set (Cast (Alternative (Cset 97) (Cset 98) (Cset 99))))) |}]
;;

View file

@ -0,0 +1,218 @@
open! Import
module Cset = Re_private.Cset
module Category = Re_private.Category
module Automata = Re_private.Automata
include struct
open Automata
module Ids = Ids
module Working_area = Working_area
module State = State
let empty = empty
let eps = eps
let cst = cst
let seq = seq
let rep = rep
end
let pp_state state = print_dyn (State.to_dyn state)
let pp_expr fmt expr = Automata.pp fmt expr
let cat = Category.dummy
let str ids sem str =
let rec loop (s : Char.t Seq.t) =
match (s () : _ Seq.node) with
| Nil -> eps ids
| Cons (c, rest) ->
let c = cst ids (Cset.csingle c) in
seq ids sem c (loop rest)
in
loop (String.to_seq str)
;;
let loop ?(max = 100) wa d c =
let cset = Cset.of_char c in
let rec loop d n =
if n > 0
then (
print_dyn (State.to_dyn d);
match State.status_no_mutex d with
| Failed -> Format.printf "> failed@."
| Match _ -> Format.printf "> matched@."
| Running ->
let d = Automata.delta wa cat cset d in
loop d (n - 1))
in
loop d max
;;
let%expect_test "string" =
let re =
let n = 4 in
let s =
let c = 'a' in
String.make n c
in
let ids = Ids.create () in
str ids `First s
in
let wa = Working_area.create () in
loop wa (State.create cat re) 'a';
[%expect
{|
((TExp (first (Seq 97 97 97 97))))
((TExp (first (Seq 97 97 97))))
((TExp (first (Seq 97 97))))
((TExp 97))
((TExp Eps))
((TMarks ()))
> matched
|}];
loop wa (State.create cat re) 'b';
[%expect {|
((TExp (first (Seq 97 97 97 97))))
()
> failed
|}]
;;
let%expect_test "alternation" =
let re =
let ids = Ids.create () in
let n = 4 in
let s =
let c = 'a' in
String.make n c
in
List.init ~len:n ~f:(fun i ->
let prefix = str ids `First s in
let suffix =
let c = Char.chr (Char.code 'b' + i) in
cst ids (Cset.csingle c)
in
seq ids `First prefix suffix)
|> Automata.alt ids
in
let wa = Working_area.create () in
loop wa (State.create cat re) 'a';
[%expect
{|
((TExp
(Alt (first (Seq (Seq 97 97 97 97) 98)) (first (Seq (Seq 97 97 97 97) 99))
(first (Seq (Seq 97 97 97 97) 100)) (first (Seq (Seq 97 97 97 97) 101)))))
((first (TSeq ((TExp (Seq 97 97 97))) 98))
(first (TSeq ((TExp (Seq 97 97 97))) 99))
(first (TSeq ((TExp (Seq 97 97 97))) 100))
(first (TSeq ((TExp (Seq 97 97 97))) 101)))
((first (TSeq ((TExp (Seq 97 97))) 98))
(first (TSeq ((TExp (Seq 97 97))) 99))
(first (TSeq ((TExp (Seq 97 97))) 100))
(first (TSeq ((TExp (Seq 97 97))) 101)))
((first (TSeq ((TExp 97)) 98)) (first (TSeq ((TExp 97)) 99))
(first (TSeq ((TExp 97)) 100)) (first (TSeq ((TExp 97)) 101)))
((TExp 98) (TExp 99) (TExp 100) (TExp 101))
()
> failed
|}]
;;
let%expect_test "alternation shared prefix" =
let n = 4 in
let re =
let ids = Ids.create () in
let prefix =
let s =
let c = 'a' in
String.make n c
in
str ids `First s
in
let suffix =
List.init ~len:n ~f:(fun i ->
let c = Char.chr (Char.code 'b' + i) in
cst ids (Cset.csingle c))
|> Automata.alt ids
in
seq ids `First prefix suffix
in
let wa = Working_area.create () in
loop wa (State.create cat re) 'a';
[%expect
{|
((TExp (first (Seq (Seq 97 97 97 97) (Alt 98 99 100 101)))))
((first (TSeq ((TExp (Seq 97 97 97))) (Alt 98 99 100 101))))
((first (TSeq ((TExp (Seq 97 97))) (Alt 98 99 100 101))))
((first (TSeq ((TExp 97)) (Alt 98 99 100 101))))
((TExp (Alt 98 99 100 101)))
()
> failed
|}]
;;
let%expect_test "kleene star" =
let re =
let ids = Ids.create () in
rep ids `Greedy `First (cst ids (Cset.csingle 'z'))
in
let wa = Working_area.create () in
loop ~max:4 wa (State.create cat re) 'z';
[%expect
{|
((TExp (first (Rep 122))))
((TExp (first (Rep 122))) (TMarks ()))
((TExp (first (Rep 122))) (TMarks ()))
((TExp (first (Rep 122))) (TMarks ()))
|}];
loop ~max:3 wa (State.create cat re) 'a';
[%expect {|
((TExp (first (Rep 122))))
((TMarks ()))
> matched
|}]
;;
let%expect_test "derivative recomputation" =
let sem = `Longest in
let re =
let ids = Ids.create () in
let lhs = rep ids `Non_greedy sem (cst ids Cset.cany) in
let rhs =
seq
ids
sem
(Automata.mark ids Automata.Mark.start)
(Automata.alt ids [ cst ids (Cset.csingle 'z'); cst ids (Cset.csingle 'b') ])
in
seq ids sem lhs rhs
in
let wa = Working_area.create () in
loop ~max:7 wa (State.create cat re) 'z';
[%expect
{|
((TExp (long (Seq (Rep ((0 255))) (Seq (Mark 0) (Alt 122 98))))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98))))
(TExp ((marks ((0 0)))) Eps))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98))))
(TExp ((marks ((0 1)))) Eps) (TMarks ((marks ((0 0))))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98))))
(TExp ((marks ((0 0)))) Eps) (TMarks ((marks ((0 1))))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98))))
(TExp ((marks ((0 1)))) Eps) (TMarks ((marks ((0 0))))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98))))
(TExp ((marks ((0 0)))) Eps) (TMarks ((marks ((0 1))))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98))))
(TExp ((marks ((0 1)))) Eps) (TMarks ((marks ((0 0))))))
|}];
loop ~max:7 wa (State.create cat re) 'a';
[%expect
{|
((TExp (long (Seq (Rep ((0 255))) (Seq (Mark 0) (Alt 122 98))))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98)))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98)))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98)))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98)))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98)))))
((long (TSeq ((TExp (Rep ((0 255))))) (Seq (Mark 0) (Alt 122 98)))))
|}]
;;

View file

@ -0,0 +1,26 @@
open! Import
module Bit_vector = Re_private.Bit_vector
let%expect_test "reset_zero" =
let n = Bit_vector.create_zero 10 in
let print () = Format.printf "%a@." Bit_vector.pp n in
print ();
[%expect {|
(len 10)
(bits "\000\000") |}];
Bit_vector.reset_zero n;
print ();
[%expect {|
(len 10)
(bits "\000\000") |}];
Bit_vector.set n 1 true;
print ();
[%expect {|
(len 10)
(bits "\002\000") |}];
Bit_vector.reset_zero n;
print ();
[%expect {|
(len 10)
(bits "\000\000") |}]
;;

View file

@ -0,0 +1,16 @@
module Category = Re_private.Category
module Cset = Re_private.Cset
let%expect_test "Category.from_char" =
for i = 0 to 255 do
let char = Char.chr i in
let cat = Category.from_char char in
if Cset.(mem (of_char char) cword) then assert (Category.(intersect letter cat))
done
;;
let%expect_test "newline" =
let cat = Category.from_char '\n' in
assert (Category.(intersect cat newline));
assert (Category.(intersect cat not_letter))
;;

View file

@ -0,0 +1,13 @@
open Import
let all_chars = String.init 256 Char.chr
let%expect_test "match an re that distinguishes every single char" =
let re =
let open Re in
set all_chars |> whole_string |> compile
in
for i = 0 to String.length all_chars - 1 do
assert (Re.execp re (String.make 1 all_chars.[i]))
done
;;

View file

@ -0,0 +1,163 @@
open! Import
module Fmt = Re_private.Fmt
module Cset = Re_private.Cset
let%expect_test "empty" =
Format.printf "%a@." Cset.pp Cset.empty;
[%expect {| |}]
;;
let%expect_test "ascii" =
Format.printf "%a@." Cset.pp Cset.ascii;
[%expect {| 0-127 |}]
;;
let%expect_test "cdigit" =
Format.printf "%a@." Cset.pp Cset.cdigit;
[%expect {| 48-57 |}]
;;
let%expect_test "calpha" =
Format.printf "%a@." Cset.pp Cset.calpha;
[%expect {|
65-90, 97-122, 170, 181, 186, 192-214, 216-246, 248-255 |}]
;;
let%expect_test "cword" =
Format.printf "%a@." Cset.pp Cset.cword;
[%expect {|
48-57, 65-90, 95, 97-122, 170, 181, 186, 192-214, 216-246, 248-255 |}]
;;
let%expect_test "notnl" =
Format.printf "%a@." Cset.pp Cset.notnl;
[%expect {|
0-9, 11-255 |}]
;;
let%expect_test "nl" =
Format.printf "%a@." Cset.pp Cset.nl;
[%expect {| 10 |}]
;;
let%expect_test "blank" =
Format.printf "%a@." Cset.pp Cset.nl;
[%expect {| 10 |}]
;;
let%expect_test "space" =
Format.printf "%a@." Cset.pp Cset.space;
[%expect {|
9-13, 32 |}]
;;
let%expect_test "xdigit" =
Format.printf "%a@." Cset.pp Cset.xdigit;
[%expect {|
48-57, 65-70, 97-102 |}]
;;
let%expect_test "lower" =
Format.printf "%a@." Cset.pp Cset.lower;
[%expect {|
97-122, 181, 223-246, 248-255 |}]
;;
let%expect_test "upper" =
Format.printf "%a@." Cset.pp Cset.upper;
[%expect {|
65-90, 192-214, 216-222 |}]
;;
let%expect_test "alpha" =
Format.printf "%a@." Cset.pp Cset.alpha;
[%expect {|
65-90, 97-122, 170, 181, 186, 192-214, 216-246, 248-255 |}]
;;
let%expect_test "alnum" =
Format.printf "%a@." Cset.pp Cset.alnum;
[%expect {|
48-57, 65-90, 97-122, 170, 181, 186, 192-214, 216-246, 248-255 |}]
;;
let%expect_test "wordc" =
Format.printf "%a@." Cset.pp Cset.wordc;
[%expect {|
48-57, 65-90, 95, 97-122, 170, 181, 186, 192-214, 216-246, 248-255 |}]
;;
let%expect_test "cntrl" =
Format.printf "%a@." Cset.pp Cset.cntrl;
[%expect {|
0-31, 127-159 |}]
;;
let%expect_test "graph" =
Format.printf "%a@." Cset.pp Cset.graph;
[%expect {|
33-126, 160-255 |}]
;;
let%expect_test "print" =
Format.printf "%a@." Cset.pp Cset.print;
[%expect {|
32-126, 160-255 |}]
;;
let%expect_test "punct" =
Format.printf "%a@." Cset.pp Cset.punct;
[%expect
{|
33-47, 58-64, 91-96, 123-126, 160-169, 171-180, 182-185, 187-191, 215, 247 |}]
;;
let%expect_test "cany" =
Format.printf "%a@." Cset.pp Cset.cany;
[%expect {| 0-255 |}]
;;
let%expect_test "case_insens" =
let cset = Cset.diff (Cset.case_insens Cset.lower) (Cset.case_insens Cset.upper) in
Format.printf "%a@." Cset.pp cset;
[%expect {| 181, 223, 255 |}]
;;
let%expect_test "one_char" =
let test set =
let pp fmt c =
let c = Option.map Cset.to_char c in
Fmt.(opt char) fmt c
in
Format.printf "%a@." pp (Cset.one_char set)
in
test Cset.empty;
[%expect {| <None> |}];
test (Cset.csingle 'c');
[%expect {| c |}];
test Cset.cany;
[%expect {| <None> |}]
;;
let%expect_test "is_empty" =
let test set = Format.printf "%a@." Fmt.bool (Cset.is_empty set) in
test Cset.empty;
[%expect {| true |}];
test (Cset.csingle 'c');
[%expect {| false |}];
test Cset.cany;
[%expect {| false |}]
;;
let%expect_test "Cset mem" =
let test set c = Format.printf "%a@." Fmt.bool (Cset.mem c set) in
test Cset.cany Cset.null_char;
[%expect {| false |}];
test Cset.cany (Cset.of_char 'a');
[%expect {| true |}];
let c = Cset.csingle 'c' in
test c (Cset.of_char 'c');
[%expect {| true |}];
test c (Cset.of_char '.');
[%expect {| false |}]
;;

View file

@ -0,0 +1,132 @@
open Import
(*
* Tests based on description of emacs regular expressions given at
* http://www.gnu.org/manual/elisp-manual-20-2.5/html_chapter/elisp_34.html
*)
let re re = Format.printf "%a@." Re.pp (Re.Emacs.re re)
let%expect_test "not supported" =
let re s =
try ignore (Re.Emacs.re s) with
| Re.Emacs.Parse_error -> print_endline "Parse error"
| Re.Emacs.Not_supported -> print_endline "Not supported"
in
re "*ab";
[%expect {| Parse error |}];
re "+ab";
[%expect {| Parse error |}];
re "?ab";
[%expect {| Parse error |}];
re "\\0";
[%expect {| Not supported |}]
;;
let%expect_test "escaping special characters" =
re "\\.";
[%expect {| (Set 46) |}];
re "\\*";
[%expect {| (Set 42) |}];
re "\\+";
[%expect {| (Set 43) |}];
re "\\?";
[%expect {| (Set 63) |}];
re "\\[";
[%expect {| (Set 91) |}];
re "\\]";
[%expect {| (Set 93) |}];
re "\\^";
[%expect {| (Set 94) |}];
re "\\$";
[%expect {| (Set 36) |}];
re "\\\\";
[%expect {| (Set 92) |}]
;;
let%expect_test "special characeters" =
re ".";
[%expect {| (Set 0-9, 11-255) |}];
re "a*";
[%expect {| (Repeat (Set 97) 0) |}];
re "a+";
[%expect {| (Repeat (Set 97) 1) |}];
re "a?";
[%expect {| (Repeat (Set 97) 0 1) |}];
re "[ab]";
[%expect {| (Alternative (Set 98)(Set 97)) |}];
re "[a-z]";
[%expect {| (Set 97-122) |}];
re "[a-z$%.]";
[%expect {| (Alternative (Set 46)(Set 37)(Set 36)(Set 97-122)) |}];
re "[]a]";
[%expect {| (Alternative (Set 97)(Set 93)) |}];
re "[]-]";
[%expect {| (Alternative (Set 93)(Set 45)) |}];
re "[a^]";
[%expect {| (Alternative (Set 94)(Set 97)) |}];
re "[^a-z]";
[%expect {| (Complement (Set 97-122)) |}];
re "[^a-z$]";
[%expect {| (Complement (Set 36)(Set 97-122)) |}];
re "^";
[%expect {| Beg_of_line |}];
re "$";
[%expect {| End_of_line |}]
;;
let%expect_test "alternatives" =
re "a\\|b";
[%expect {| (Alternative (Set 97)(Set 98)) |}];
re "aa\\|bb";
[%expect {| (Alternative (Sequence (Set 97)(Set 97))(Sequence (Set 98)(Set 98))) |}]
;;
let%expect_test "contexts" =
re "\\`";
[%expect {| Beg_of_str |}];
re "\\'";
[%expect {| End_of_str |}];
re "\\=";
[%expect {| Start |}];
re "\\b";
[%expect {| (Alternative Beg_of_wordEnd_of_word) |}];
re "\\B";
[%expect {| Not_bound |}];
re "\\<";
[%expect {| Beg_of_word |}];
re "\\>";
[%expect {| End_of_word |}]
;;
let%expect_test "word-constituent" =
re "\\w";
[%expect
{|
(Alternative
(Set 48-57, 65-90, 97-122, 170, 181, 186, 192-214, 216-246, 248-255)
(Set 95)) |}];
re "\\W";
[%expect
{|
(Complement
(Set 48-57, 65-90, 97-122, 170, 181, 186, 192-214, 216-246, 248-255)
(Set 95)) |}]
;;
let%expect_test "grouping" =
re "\\(a\\)";
[%expect {| (Group (Set 97)) |}];
re "\\(a\\|b\\)c";
[%expect {| (Sequence (Group (Alternative (Set 97)(Set 98)))(Set 99)) |}]
;;
let%expect_test "concatenation" =
re "ab";
[%expect {| (Sequence (Set 97)(Set 98)) |}]
;;
let%expect_test "ordinary characters" =
re "a";
[%expect {| (Set 97) |}]
;;

View file

@ -0,0 +1,258 @@
open Import
let glob ?match_backslashes ?expand_braces ?anchored ?pathname ?period re s =
let re =
Re.Glob.glob ?match_backslashes ?expand_braces ?anchored ?pathname ?period re
|> Re.compile
in
Format.printf "%b@." (Re.execp re s)
;;
let%expect_test "glob" =
glob "foo*" "foobar";
[%expect {| true |}];
glob "fo?bar" "fobar";
[%expect {| false |}];
glob "fo?bar" "foobar";
[%expect {| true |}];
glob "fo?bar" "foo0bar";
[%expect {| false |}];
glob "?oobar" "foobar";
[%expect {| true |}];
glob "*bar" "foobar";
[%expect {| true |}];
glob "\\*bar" "foobar";
[%expect {| false |}];
glob "\\*bar" "*bar";
[%expect {| true |}];
glob "[ab]foo" "afoo";
[%expect {| true |}];
glob "[ab]foo" "bfoo";
[%expect {| true |}];
glob "[ab]foo" "cfoo";
[%expect {| false |}];
glob "c[ab]foo" "cabfoo";
[%expect {| false |}];
glob ".foo" ".foo";
[%expect {| true |}];
glob ".foo" "afoo";
[%expect {| false |}];
glob "*[.]foo" "a.foo";
[%expect {| true |}];
glob "*[.]foo" "ba.foo";
[%expect {| true |}];
glob "*.foo" ".foo";
[%expect {| false |}];
glob "*[.]foo" ".foo";
[%expect {| false |}];
glob ~anchored:true "*/foo" "/foo";
[%expect {| true |}];
glob ~anchored:true "foo/*" "foo/";
[%expect {| true |}];
glob "/[^f]" "/foo";
[%expect {| false |}];
glob "/[^f]" "/bar";
[%expect {| true |}];
glob ~anchored:true "/[^f]" "/bar";
[%expect {| false |}];
glob ~anchored:true "*" ".bar";
[%expect {| false |}];
glob "foo[.]bar" "foo.bar";
[%expect {| true |}];
glob "[.]foo" ".foo";
[%expect {| false |}];
glob "foo[/]bar" "foo/bar";
[%expect {| false |}];
glob ~anchored:true "*bar" "foobar";
[%expect {| true |}];
glob "foo" "foobar";
[%expect {| true |}];
glob "bar" "foobar";
[%expect {| true |}];
glob ~anchored:true "foo" "foobar";
[%expect {| false |}];
glob ~anchored:true "bar" "foobar";
[%expect {| false |}];
glob "{foo,bar}bar" "foobar";
[%expect {| false |}];
glob "{foo,bar}bar" "{foo,bar}bar";
[%expect {| true |}];
glob "foo?bar" "foo/bar";
[%expect {| false |}];
let pathname = true in
let period = true in
glob ~pathname ~period "?oobar" ".oobar";
[%expect {| false |}];
glob ~pathname ~period "?oobar" "/oobar";
[%expect {| false |}];
glob ~pathname ~period "f?obar" "f/obar";
[%expect {| false |}];
glob ~pathname ~period "f?obar" "f.obar";
[%expect {| true |}];
glob ~pathname ~period "f*.bar" "f.bar";
[%expect {| true |}];
glob ~pathname ~period "f?.bar" "fo.bar";
[%expect {| true |}];
glob ~pathname ~period "/.bar" "/.bar";
[%expect {| true |}];
glob ~pathname ~period "*.bar" ".bar";
[%expect {| false |}];
glob ~pathname ~period "?" ".";
[%expect {| false |}];
glob ~pathname ~period "/*bar" "/.bar";
[%expect {| false |}];
glob "?oobar" ".oobar";
[%expect {| false |}];
glob "?oobar" "/oobar";
[%expect {| false |}];
let pathname = true in
let period = false in
glob ~pathname ~period "?oobar" "/oobar";
[%expect {| false |}];
glob ~pathname ~period "?oobar" ".oobar";
[%expect {| true |}];
glob ~pathname ~period "f?obar" "f/obar";
[%expect {| false |}];
glob ~pathname ~period "f?obar" "f.obar";
[%expect {| true |}];
let pathname = false in
let period = false in
glob ~pathname ~period "?oobar" ".oobar";
[%expect {| true |}];
glob ~pathname ~period "?oobar" "/oobar";
[%expect {| true |}];
glob ~expand_braces:true "{foo,far}bar" "foobar";
[%expect {| true |}];
glob ~expand_braces:true "{foo,far}bar" "farbar";
[%expect {| true |}];
glob ~expand_braces:true "{foo,far}bar" "{foo,far}bar";
[%expect {| false |}]
;;
let%expect_test "double asterisk" =
let glob = glob ~anchored:true in
glob "**" "foobar";
[%expect {| true |}];
glob "**" "foo/bar";
[%expect {| true |}];
glob "**/bar" "foo/bar";
[%expect {| true |}];
glob "**/bar" "foo/far/bar";
[%expect {| true |}];
glob "foo/**" "foo";
[%expect {| false |}];
glob "foo/**" "foo/bar";
[%expect {| true |}];
glob "foo/**" "foo/far/bar";
[%expect {| true |}];
glob "foo/**/bar" "foo/far/bar";
[%expect {| true |}];
glob "foo/**/bar" "foo/far/oof/bar";
[%expect {| true |}];
glob "foo/**bar" "foo/far/oofbar";
[%expect {| true |}];
glob "foo/**bar" "foo/bar";
[%expect {| true |}];
glob "foo/**bar" "foo/foobar";
[%expect {| true |}];
glob "/**" "//foo";
[%expect {| true |}];
glob "/**" "/";
[%expect {| true |}];
glob "/**" "/x";
[%expect {| true |}];
glob "**" "foo//bar";
[%expect {| true |}];
glob "foo/bar/**/*.ml" "foo/bar/baz/foobar.ml";
[%expect {| true |}];
glob "foo/bar/**/*.ml" "foo/bar/foobar.ml";
[%expect {| true |}];
glob "foo/**/bar/**/baz" "foo/bar/baz";
[%expect {| true |}];
glob "foo/**/bar/**/baz" "foo/bar/x/y/z/baz";
[%expect {| true |}];
glob "foo/**/bar/**/baz" "foo/x/y/z/bar/baz";
[%expect {| true |}];
glob "foo/**/bar/**/baz" "foo/bar/x/bar/x/baz";
[%expect {| true |}];
glob "foo/**/bar/**/baz" "foo/bar/../x/baz";
[%expect {| false |}];
glob "foo/**/bar/**/baz" "foo/bar/./x/baz";
[%expect {| false |}];
((* Interaction with [~period] *)
let glob = glob ~period:true in
glob "**" ".foobar";
[%expect {| false |}];
glob "**" ".foo/bar";
[%expect {| false |}];
glob "foo/**" "foo/.bar";
[%expect {| false |}];
glob "**" "foo/.bar/bat";
[%expect {| false |}];
glob "foo/**/bat" "foo/.bar/bat";
[%expect {| false |}];
glob "/**/bat" "/foo/.bar/bat";
[%expect {| false |}];
glob "/**/bat" "/.bar/bat";
[%expect {| false |}];
glob "/**bat" "/bar/.bat";
[%expect {| false |}];
glob ".**" ".foobar";
[%expect {| true |}];
glob ".**" ".foo/bar";
[%expect {| true |}];
glob "foo/.**" "foo/.bar";
[%expect {| true |}]);
let glob = glob ~period:false in
glob "**" ".foobar";
[%expect {| true |}];
glob "**" ".foo/bar";
[%expect {| true |}];
glob "foo/**" "foo/.bar";
[%expect {| true |}];
glob "**" "foo/.bar/bat";
[%expect {| true |}];
glob "foo/**/bat" "foo/.bar/bat";
[%expect {| true |}];
glob "/**/bat" "/foo/.bar/bat";
[%expect {| true |}];
glob "/**/bat" "/.bar/bat";
[%expect {| true |}];
glob "/**bat" "/bar/.bat";
[%expect {| true |}]
;;
let%expect_test "backslash handling" =
let anchored = true in
let glob = glob ~anchored in
(let glob = glob ~match_backslashes:false in
glob "a/b/c" "a\\b/c";
[%expect {| false |}];
glob "a\\b" "ab";
[%expect {| true |}];
glob "a/*.ml" "a/b\\c.ml";
[%expect {| true |}];
glob "a/b/*.ml" "a\\b\\c.ml";
[%expect {| false |}];
glob "/" "\\";
[%expect {| false |}];
glob "/?" "\\a";
[%expect {| false |}];
glob "a/**.ml" "a\\c\\.b.ml";
[%expect {| true |}]);
let glob = glob ~match_backslashes:true in
glob "a/b/c" "a\\b/c";
[%expect {| true |}];
glob "a\\b" "ab";
[%expect {| true |}];
glob "a/*.ml" "a/b\\c.ml";
[%expect {| false |}];
glob "a/b/*.ml" "a\\b\\c.ml";
[%expect {| true |}];
glob "/" "\\";
[%expect {| true |}];
glob "/?" "\\a";
[%expect {| true |}];
glob "a/**.ml" "a\\c\\.b.ml";
[%expect {| false |}]
;;

View file

@ -0,0 +1,143 @@
open Import
open Re
let%expect_test "empty group" =
let empty = group empty in
t empty "";
[%expect {| <None> |}];
t empty "x";
[%expect {| <None> |}]
;;
let%expect_test "zero length group" =
let empty = group bos in
t empty "";
[%expect {| (Group ( (0 0))( (0 0))) |}];
t empty "x";
[%expect {| (Group ( (0 0))( (0 0))) |}]
;;
let%expect_test "no group" =
let re = any in
t re "";
[%expect {| <None> |}];
t re ".";
[%expect {| (Group (. (0 1))) |}]
;;
let%expect_test "two groups" =
let re = seq [ group any; group any ] in
t re "a";
[%expect {| <None> |}];
t re "ab";
[%expect {| (Group (ab (0 2))(a (0 1))(b (1 2))) |}];
t re "abc";
[%expect {| (Group (ab (0 2))(a (0 1))(b (1 2))) |}]
;;
let%expect_test "maybe group" =
let twoany = seq [ any; any ] in
let re = alt [ twoany; group twoany ] in
t re "aa";
[%expect {| (Group (aa (0 2))( (-1 -1))) |}];
t re "a";
[%expect {| <None> |}]
;;
let%expect_test "nesting of groups" =
let re = group (seq [ group (char 'a'); char 'b' ]) in
t re "ab";
[%expect {| (Group (ab (0 2))(ab (0 2))(a (0 1))) |}]
;;
let%expect_test "group choice" =
let t = Import.exec_partial_detailed in
(* Alternation of character sets isn't flattened *)
let lhs_group =
let open Re in
alt [ group (char 'a'); char 'b' ]
in
t lhs_group "a";
[%expect {| `Full [|0,1,"a";0,1,"a"|] |}];
t lhs_group "b";
[%expect {| `Full [|0,1,"b";-1,-1,<No match>|] |}];
t
(let open Re in
alt [ group (char 'a'); group (char 'b') ])
"b";
[%expect {| `Full [|0,1,"b";-1,-1,<No match>;0,1,"b"|] |}];
(* No_group inside char set: *)
let no_group_charset =
let a = Re.group (Re.char 'a') in
let b = Re.char 'b' in
Re.no_group (Re.alt [ a; b ])
in
t no_group_charset "a";
[%expect {| `Full [|0,1,"a"|] |}];
t no_group_charset "b";
[%expect {| `Full [|0,1,"b"|] |}];
(* No_group outside char set *)
let no_group_string =
let aa = Re.group (Re.str "aa") in
let bb = Re.str "bb" in
Re.no_group (Re.alt [ aa; bb ])
in
t no_group_string "aa";
[%expect {| `Full [|0,2,"aa"|] |}];
t no_group_string "bb";
[%expect {| `Full [|0,2,"bb"|] |}]
;;
let%expect_test "Group.{get,get_opt,offset,test}" =
let r = seq [ group (char 'a'); opt (group (char 'a')); group (char 'b') ] in
let m = exec (compile r) "ab" in
let test idx =
Format.printf "get_opt = %a@." (Fmt.opt Fmt.str) (Group.get_opt m idx);
Format.printf "get = %a@." (or_not_found Fmt.str) (fun () -> Group.get m idx);
Format.printf "test = %b@." (Group.test m idx);
Format.printf "offset = %a@." (or_not_found offset) (fun () -> Group.offset m idx)
in
test 0;
[%expect {|
get_opt = ab
get = ab
test = true
offset = (0, 2) |}];
test 1;
[%expect {|
get_opt = a
get = a
test = true
offset = (0, 1) |}];
test 2;
[%expect
{|
get_opt = <None>
get = Not_found
test = false
offset = Not_found |}];
test 3;
[%expect {|
get_opt = b
get = b
test = true
offset = (1, 2) |}];
Format.printf "%a@." (array offset) (Group.all_offset m);
[%expect {| [| (0, 2); (0, 1); (-1, -1); (1, 2) |] |}]
;;
let%expect_test "nest" =
let r = rep (nest (alt [ group (char 'a'); char 'b' ])) in
test_re r "ab";
[%expect {| [| (0, 2); (-1, -1) |] |}];
test_re r "ba";
[%expect {| [| (0, 2); (1, 2) |] |}]
;;
let%expect_test "group/no_group" =
let r = seq [ group (char 'a'); opt (group (char 'a')); group (char 'b') ] in
test_re r "ab";
[%expect {| [| (0, 2); (0, 1); (-1, -1); (1, 2) |] |}];
test_re (no_group r) "ab";
[%expect {| [| (0, 2) |] |}]
;;

View file

@ -0,0 +1,49 @@
open Import
let () = Printexc.record_backtrace true
module Hash_set = Re_private.Hash_set
let id1 = 1
let id2 = 2
let id3 = 3
let test table f =
if f table
then print_endline "[PASS]"
else (
print_endline "[FAIL]";
Format.printf "%a@." Hash_set.pp table)
;;
let%expect_test "basic set" =
let set = Hash_set.create () in
test set Hash_set.is_empty;
[%expect {| [PASS] |}];
test set (fun set -> not (Hash_set.mem set id1));
[%expect {|
[PASS] |}]
;;
let%expect_test "add 1 element" =
let set = Hash_set.create () in
Hash_set.add set id1;
test set (fun set -> not (Hash_set.is_empty set));
[%expect {|
[PASS] |}];
test set (fun set -> Hash_set.mem set id1);
[%expect {|
[PASS] |}];
Hash_set.add set id1;
test set (fun set -> Hash_set.mem set id1);
[%expect {| [PASS] |}];
Hash_set.add set id2;
test set (fun set -> Hash_set.mem set id2);
[%expect {| [PASS] |}];
Hash_set.add set id3;
test set (fun set -> Hash_set.mem set id3);
[%expect {|
[PASS] |}];
test set (fun set -> List.for_all [ id1; id2; id3 ] ~f:(fun id -> Hash_set.mem set id));
[%expect {| [PASS] |}]
;;

View file

@ -0,0 +1,13 @@
open Import
let%expect_test "iter" =
let re = Re.Posix.compile_pat "(ab)+" in
strings (Re.matches re "aabab aaabba dab ");
[%expect {| ["abab"; "ab"; "ab"] |}];
strings (Re.matches ~pos:2 ~len:7 re "abab ababab");
[%expect {| ["ab"; "abab"] |}];
strings (Re.matches re_empty "ab");
[%expect {| [""; ""; ""] |}];
strings (Re.matches (Re.compile (Re.rep (Re.char 'a'))) "cat");
[%expect {| [""; "a"; ""] |}]
;;

View file

@ -0,0 +1,53 @@
open Import
open Re
let test_mark ?pos ?len r s il1 il2 =
let subs = exec ?pos ?len (compile r) s in
Format.printf
"%b@."
(List.for_all ~f:(Mark.test subs) il1
&& List.for_all ~f:(fun x -> not (Mark.test subs x)) il2)
;;
let%expect_test "mark" =
let i, r = mark digit in
test_mark r "0" [ i ] [];
[%expect {| true |}]
;;
let%expect_test "mark seq" =
let i, r = mark digit in
let r = seq [ r; r ] in
test_mark r "02" [ i ] [];
[%expect {| true |}]
;;
let%expect_test "mark rep" =
let i, r = mark digit in
let r = rep r in
test_mark r "02" [ i ] [];
[%expect {| true |}]
;;
let%expect_test "mark alt" =
let ia, ra = mark (char 'a') in
let ib, rb = mark (char 'b') in
let r = alt [ ra; rb ] in
test_mark r "a" [ ia ] [ ib ];
test_mark r "b" [ ib ] [ ia ];
[%expect {|
true
true |}];
let r = rep r in
test_mark r "ab" [ ia; ib ] [];
[%expect {| true |}]
;;
let%expect_test "mark prefers lhs" =
let two_chars = seq [ any; any ] in
let lhs, x = mark two_chars in
let rhs, x' = mark two_chars in
let r = alt [ x; x' ] in
test_mark r "aa" [ lhs ] [ rhs ];
[%expect {| true |}]
;;

View file

@ -0,0 +1,69 @@
open Import
let t re s =
let re = Re.compile re in
let res = Re.exec_partial re s in
Format.printf
"`%s@."
(match res with
| `Partial -> "Partial"
| `Full -> "Full"
| `Mismatch -> "Mismatch")
;;
let%expect_test "partial matches" =
let open Re in
t (str "hello") "he";
[%expect {| `Partial |}];
t (str "hello") "goodbye";
[%expect {| `Partial |}];
(* exec_partial 3 should be `Full *)
t (str "hello") "hello";
[%expect {| `Partial |}];
t (whole_string (str "hello")) "hello";
[%expect {| `Partial |}];
t (whole_string (str "hello")) "goodbye";
[%expect {| `Mismatch |}];
t (str "hello") "";
[%expect {| `Partial |}];
t (str "") "hello";
[%expect {| `Full |}];
t (whole_string (str "hello")) "";
[%expect {| `Partial |}]
;;
let t = exec_partial_detailed
let%expect_test "partial detailed" =
let open Re in
t (str "hello") "he";
[%expect {| `Partial 0 |}];
(* Because of how the matching engine currently works, situations where
the entirety of the input string cannot be a match like the test below
actually return the last character as a potential start instead of just
return `Partial (String.length input). This is still fine however as
it still respects the mli contract, as no match could start before
the given position, and is fine in practice as testing an extra
character on extra input doesn't add much more in terms of workload.
*)
t (str "hello") "goodbye";
[%expect {| `Partial 6 |}];
t (str "hello") "hello";
[%expect {| `Full [|0,5,"hello"|] |}];
t (whole_string (str "hello")) "hello";
[%expect {| `Full [|0,5,"hello"|] |}];
t (whole_string (str "hello")) "goodbye";
[%expect {| `Mismatch |}];
t (str "hello") "";
[%expect {| `Partial 0 |}];
t (str "") "hello";
[%expect {| `Full [|0,0,""|] |}];
t (whole_string (str "hello")) "";
[%expect {| `Partial 0 |}];
t (str "abc") ".ab.ab";
[%expect {| `Partial 4 |}];
t ~pos:1 (seq [ not_boundary; str "b" ]) "ab";
[%expect {| `Full [|1,2,"b"|] |}];
t (seq [ group (str "a"); rep any; group (str "b") ]) ".acb.";
[%expect {| `Full [|1,4,"acb";1,2,"a";3,4,"b"|] |}]
;;

View file

@ -0,0 +1,105 @@
open Import
module Pcre = Re_private.Pcre
let test re s =
match Pcre.re re with
| exception _ -> Format.printf "failed to parse@."
| re -> t re s
;;
let%expect_test "quoted strings" =
test {|\Qfoo\E|} "foo";
[%expect {| (Group (foo (0 3))) |}];
test {|\Qbar|} "";
[%expect {| failed to parse |}];
test {|\Qbaz\|} "";
[%expect {| failed to parse |}];
test {|\Qba\Xz\E|} {|ba\Xz|};
[%expect {| (Group (ba\Xz (0 5))) |}]
;;
let%expect_test "octal" =
test {|\025|} (String.make 1 '\o025');
[%expect {| (Group ( (0 1))) |}];
test {|\999|} "";
[%expect {| failed to parse |}];
test {|\111|} (String.make 1 '\o111');
[%expect {| (Group (I (0 1))) |}]
;;
let%expect_test "\\x and \\o form" =
test {|\o{111}|} (String.make 1 '\o111');
[%expect {| <None> |}];
test {|\o{111|} "";
[%expect {| failed to parse |}];
test {|\x{ff}|} (String.make 1 '\xff');
[%expect {| (Group (ÿ (0 1))) |}];
test {|\x{ff|} "";
[%expect {| failed to parse |}]
;;
let%expect_test "substitute" =
let open Pcre in
let substitute ~rex ~subst s = substitute ~rex ~subst s |> print_endline in
let rex = regexp "[a-zA-Z]+" in
let subst = String.capitalize_ascii in
substitute ~rex ~subst " hello world; I love chips!";
[%expect {| Hello World; I Love Chips! |}];
substitute ~rex:re_empty ~subst:(fun _ -> "a") "";
[%expect {| a |}];
substitute ~rex:(regexp "a*") ~subst:(fun _ -> "*") "cat";
[%expect {| *c*t* |}];
let rex = regexp "^ *" in
substitute ~rex ~subst:(fun _ -> "A ") "test";
[%expect {| A test |}]
;;
let%expect_test "test_blank_class" =
let re = Re.Perl.compile_pat "\\d[[:blank:]]\\d[[:blank:]]+[a-z]" in
let successes = [ "1 2 a"; "2\t3 z"; "9\t0 \t a" ] in
let failures = [ ""; "123"; " "; "1 3z" ] in
List.iter successes ~f:(fun s -> printf "String %S should match %b\n" s (Re.execp re s));
[%expect
{|
String "1 2 a" should match true
String "2\t3 z" should match true
String "9\t0 \t a" should match true |}];
List.iter failures ~f:(fun s ->
printf "String %S should not match %b\n" s (Re.execp re s));
[%expect
{|
String "" should not match false
String "123" should not match false
String " " should not match false
String "1 3z" should not match false |}]
;;
let%expect_test "named groups" =
let open Pcre in
let rex = regexp "(?<many_x>x+)" in
let s = exec ~rex "testxxxyyy" in
print_endline (get_named_substring rex "many_x" s);
[%expect {| xxx |}]
;;
let%expect_test "quote" =
let test s = Printf.printf "%S\n" (Re.Pcre.quote s) in
test "";
[%expect {| "" |}];
test "\000";
[%expect {| "\000" |}];
test "";
[%expect {| "" |}];
test (String.init (126 - 32) (fun x -> Char.chr (x + 32)));
[%expect
{xxx| " !\"#\\$%&'\\(\\)\\*\\+,-\\./0123456789:;<=>\\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\[\\\\]\\^_`abcdefghijklmnopqrstuvwxyz\\{\\|}" |xxx}];
let b = Buffer.create 100 in
for i = 0 to 255 do
let c = Char.chr i in
let s = Pcre.quote (String.make 1 c) in
if String.length s > 1 then Buffer.add_char b c
done;
let b = Buffer.contents b in
Printf.printf "%S\n" b;
[%expect {xxx| "$()*+.?[\\^{|" |xxx}]
;;

View file

@ -0,0 +1,15 @@
open Import
module Pcre = Re_private.Pcre
let whitespace_re = Pcre.regexp "\\s+"
let%expect_test "split1" =
strings (Pcre.split ~rex:whitespace_re "");
[%expect {| [] |}]
;;
let%expect_test "split2" =
strings (Pcre.split ~rex:whitespace_re " ");
[%expect {|
[] |}]
;;

View file

@ -0,0 +1,48 @@
open Import
let split ~rex s = Re.Pcre.split ~rex s |> strings
let%expect_test "split" =
split ~rex:re_whitespace "aa bb c d ";
[%expect {| ["aa"; "bb"; "c"; "d"] |}];
split ~rex:re_whitespace " a full_word bc ";
[%expect {| ["a"; "full_word"; "bc"] |}];
split ~rex:re_empty "abcd";
[%expect {| ["a"; "b"; "c"; "d"] |}];
split ~rex:re_eol "a\nb";
[%expect {| ["a"; "\nb"] |}];
split ~rex:re_bow "a b";
[%expect {| ["a "; "b"] |}];
split ~rex:re_eow "a b";
[%expect {| ["a"; " b"] |}];
let rex = Re.Pcre.regexp "" in
split ~rex "xx";
[%expect {| ["x"; "x"] |}]
;;
let full_split ?max ~rex s =
let res = Re.Pcre.full_split ?max ~rex s in
Format.printf
"[%a]@."
Fmt.(
list ~pp_sep:(Fmt.lit "; ") (fun fmt what ->
match (what : Re.Pcre.split_result) with
| Text s -> Format.fprintf fmt "Text %S" s
| Delim s -> Format.fprintf fmt "Delim %S" s
| NoGroup -> Format.fprintf fmt "NoGroup"
| Group (x, s) -> Format.fprintf fmt "Group (%d, %S)" x s))
res
;;
let%expect_test "full split" =
(let full_split = full_split ~rex:(Re.Pcre.regexp "x(x)?") in
full_split "testxxyyy";
[%expect {| [Text "test"; Delim "xx"; Group (1, "x"); Text "yyy"] |}];
full_split "testxyyy";
[%expect {| [Text "test"; Delim "x"; NoGroup; Text "yyy"] |}]);
let full_split = full_split ~rex:(Re.Pcre.regexp "[:_]") in
full_split "";
[%expect {| [] |}];
full_split ~max:1 "xxx:yyy";
[%expect {| [Text "xxx:yyy"] |}]
;;

View file

@ -0,0 +1,212 @@
open Import
(* Tests based on description of Perl regular expressions given at
http://www.perl.com/CPAN-local/doc/manual/html/pod/perlre.html *)
let re ?opts s = Format.printf "%a@." Re.pp (Re.Perl.re ?opts s)
let try_parse ?opts s =
try
ignore (Re.Perl.re ?opts s);
print_endline "Prased successfully"
with
| Re.Perl.Parse_error -> print_endline "Parse error"
| Re.Perl.Not_supported -> print_endline "Not supported"
;;
let%expect_test "escaping meta characters" =
re "\\^";
[%expect {| (Set 94) |}];
re "\\.";
[%expect {| (Set 46) |}];
re "\\$";
[%expect {| (Set 36) |}];
re "\\|";
[%expect {| (Set 124) |}];
re "\\(";
[%expect {| (Set 40) |}];
re "\\)";
[%expect {| (Set 41) |}];
re "\\[";
[%expect {| (Set 91) |}];
re "\\]";
[%expect {| (Set 93) |}];
re "\\*";
[%expect {| (Set 42) |}];
re "\\+";
[%expect {| (Set 43) |}];
re "\\?";
[%expect {| (Set 63) |}];
re "\\\\";
[%expect {| (Set 92) |}]
;;
let%expect_test "basic metacharacters" =
re "^";
[%expect {| Beg_of_str |}];
re ".";
[%expect {| (Set 0-9, 11-255) |}];
re "$";
[%expect {| End_of_str |}];
re "a|b";
[%expect {| (Alternative (Set 97)(Set 98)) |}];
re "aa|bb";
[%expect {| (Alternative (Sequence (Set 97)(Set 97))(Sequence (Set 98)(Set 98))) |}];
re "(a)";
[%expect {| (Group (Set 97)) |}];
re "(a|b)c";
[%expect {| (Sequence (Group (Alternative (Set 97)(Set 98)))(Set 99)) |}];
re "[ab]";
[%expect {| (Alternative (Set 98)(Set 97)) |}];
re "[a-z]";
[%expect {| (Set 97-122) |}];
re "[a-z$%.]";
[%expect {| (Alternative (Set 46)(Set 37)(Set 36)(Set 97-122)) |}];
re "[-az]";
[%expect {| (Alternative (Set 122)(Set 97)(Set 45)) |}];
re "[az-]";
[%expect {| (Alternative (Set 122)(Set 45)(Set 97)) |}];
re "[a\\-z]";
[%expect {| (Alternative (Set 122)(Set 45)(Set 97)) |}];
re "[]a]";
[%expect {| (Alternative (Set 97)(Set 93)) |}];
re "[]-]";
[%expect {| (Alternative (Set 93)(Set 45)) |}];
re "[a^]";
[%expect {| (Alternative (Set 94)(Set 97)) |}];
re "[^a-z]";
[%expect {| (Complement (Set 97-122)) |}];
re "[^a-z$]";
[%expect {| (Complement (Set 36)(Set 97-122)) |}];
re "[a-\\sz]";
[%expect {| (Alternative (Set 122)(Set 97)(Set 45)(Set 9-13, 32)) |}]
;;
let%expect_test "greedy quantifiers" =
re "a*";
[%expect {| (Sem_greedy Greedy (Repeat (Set 97) 0)) |}];
re "a+";
[%expect {| (Sem_greedy Greedy (Repeat (Set 97) 1)) |}];
re "a?";
[%expect {| (Sem_greedy Greedy (Repeat (Set 97) 0 1)) |}];
re "a{10}";
[%expect {| (Sem_greedy Greedy (Repeat (Set 97) 10 10)) |}];
re "a{10,}";
[%expect {| (Sem_greedy Greedy (Repeat (Set 97) 10)) |}];
re "a{10,12}";
[%expect {| (Sem_greedy Greedy (Repeat (Set 97) 10 12)) |}]
;;
let%expect_test "non-greedy quantifiers" =
re "a*?";
[%expect {| (Sem_greedy Non_greedy (Repeat (Set 97) 0)) |}];
re "a+?";
[%expect {| (Sem_greedy Non_greedy (Repeat (Set 97) 1)) |}];
re "a??";
[%expect {| (Sem_greedy Non_greedy (Repeat (Set 97) 0 1)) |}];
re "a{10}?";
[%expect {| (Sem_greedy Non_greedy (Repeat (Set 97) 10 10)) |}];
re "a{10,}?";
[%expect {| (Sem_greedy Non_greedy (Repeat (Set 97) 10)) |}];
re "a{10,12}?";
[%expect {| (Sem_greedy Non_greedy (Repeat (Set 97) 10 12)) |}]
;;
let%expect_test "character sets" =
re "\\w";
[%expect
{|
(Alternative
(Set 48-57, 65-90, 97-122, 170, 181, 186, 192-214, 216-246, 248-255)
(Set 95)) |}];
re "\\W";
[%expect
{|
(Complement
(Set 48-57, 65-90, 97-122, 170, 181, 186, 192-214, 216-246, 248-255)
(Set 95)) |}];
re "\\s";
[%expect {| (Set 9-13, 32) |}];
re "\\S";
[%expect {| (Complement (Set 9-13, 32)) |}];
re "\\d";
[%expect {| (Set 48-57) |}];
re "\\D";
[%expect {| (Complement (Set 48-57)) |}]
;;
let%expect_test "zero-width assertions" =
re "\\b";
[%expect {| (Alternative Beg_of_wordEnd_of_word) |}];
re "\\B";
[%expect {| Not_bound |}];
re "\\A";
[%expect {| Beg_of_str |}];
re "\\Z";
[%expect {| Last_end_of_line |}];
re "\\z";
[%expect {| End_of_str |}];
re "\\G";
[%expect {| Start |}]
;;
let%expect_test "options" =
re ~opts:[ `Anchored ] "a";
[%expect {| (Sequence Start(Set 97)) |}];
re ~opts:[ `Caseless ] "b";
[%expect {| (No_case (Set 98)) |}];
re ~opts:[ `Dollar_endonly ] "$";
[%expect {| Last_end_of_line |}];
re ~opts:[ `Dollar_endonly; `Multiline ] "$";
[%expect {| End_of_line |}];
re ~opts:[ `Dotall ] ".";
[%expect {| (Set 0-255) |}];
re ~opts:[ `Multiline ] "^";
[%expect {| Beg_of_line |}];
re ~opts:[ `Multiline ] "$";
[%expect {| End_of_line |}];
re ~opts:[ `Ungreedy ] "a*";
[%expect {| (Sem_greedy Non_greedy (Repeat (Set 97) 0)) |}];
re ~opts:[ `Ungreedy ] "a*?";
[%expect {| (Sem_greedy Greedy (Repeat (Set 97) 0)) |}]
;;
let%expect_test "clustering" =
re "(?:a)";
[%expect {| (Set 97) |}];
re "(?:a|b)c";
[%expect {| (Sequence (Alternative (Set 97)(Set 98))(Set 99)) |}]
;;
let%expect_test "comment" =
re "a(?#comment)b";
[%expect {| (Sequence (Set 97)(Sequence )(Set 98)) |}];
try_parse "(?#";
[%expect {| Parse error |}]
;;
let%expect_test "backrefs" =
try_parse "\\0";
[%expect {| Not supported |}]
;;
let%expect_test "ordinary characters" =
re "a";
[%expect {| (Set 97) |}]
;;
let%expect_test "concacentation" =
re "ab";
[%expect {| (Sequence (Set 97)(Set 98)) |}]
;;
let%expect_test "sets in classes" =
re "[a\\s]";
[%expect {| (Alternative (Set 9-13, 32)(Set 97)) |}]
;;
let%expect_test "fixed bug" =
(try ignore (Re.compile (Re.Perl.re "(.*?)(\\WPl|\\Bpl)(.*)")) with
| _ -> failwith "bug in Re.handle_case");
[%expect {||}]
;;

View file

@ -0,0 +1,10 @@
open Import
let%expect_test "class space" =
let re = Re.Posix.compile_pat {|a[[:space:]]b|} in
let exec = Re.execp re in
assert (exec "a b");
assert (not (exec "ab"));
assert (not (exec "a_b"));
[%expect {||}]
;;

View file

@ -0,0 +1,330 @@
open Import
open Re
let%expect_test "str" =
test_re (str "a") "a";
[%expect {| [| (0, 1) |] |}];
test_re (str "a") "b";
[%expect {| Not_found |}]
;;
let%expect_test "char" =
test_re (alt [ char 'a'; char 'b' ]) "a";
[%expect {| [| (0, 1) |] |}];
test_re (alt [ char 'a'; char 'b' ]) "b";
[%expect {| [| (0, 1) |] |}];
test_re (alt [ char 'a'; char 'b' ]) "c";
[%expect {| Not_found |}]
;;
let%expect_test "alt" =
test_re (alt [ char 'a'; char 'b' ]) "a";
[%expect {| [| (0, 1) |] |}];
test_re (alt [ char 'a'; char 'b' ]) "b";
[%expect {| [| (0, 1) |] |}];
test_re (alt [ char 'a'; char 'b' ]) "c";
[%expect {| Not_found |}]
;;
let%expect_test "seq" =
test_re (seq [ char 'a'; char 'b' ]) "ab";
[%expect {| [| (0, 2) |] |}];
test_re (seq [ char 'a'; char 'b' ]) "ac";
[%expect {| Not_found |}]
;;
let%expect_test "empty" =
test_re empty "";
[%expect {| Not_found |}];
test_re empty "a";
[%expect {| Not_found |}]
;;
let%expect_test "epsilon" =
test_re epsilon "";
[%expect {| [| (0, 0) |] |}];
test_re epsilon "a";
[%expect {| [| (0, 0) |] |}]
;;
let%expect_test "rep" =
test_re (rep (char 'a')) "";
[%expect {| [| (0, 0) |] |}];
test_re (rep (char 'a')) "a";
[%expect {| [| (0, 1) |] |}];
test_re (rep (char 'a')) "aa";
[%expect {| [| (0, 2) |] |}];
test_re (rep (char 'a')) "b";
[%expect {| [| (0, 0) |] |}]
;;
let%expect_test "bol" =
test_re (seq [ bol; char 'a' ]) "ab";
[%expect {| [| (0, 1) |] |}];
test_re (seq [ bol; char 'a' ]) "b\na";
[%expect {| [| (2, 3) |] |}];
test_re (seq [ bol; char 'a' ]) "ba";
[%expect {| Not_found |}]
;;
let%expect_test "eol" =
test_re (seq [ char 'a'; eol ]) "ba";
[%expect {| [| (1, 2) |] |}];
test_re (seq [ char 'a'; eol ]) "a\nb";
[%expect {| [| (0, 1) |] |}];
test_re (seq [ char 'a'; eol ]) "ba\n";
[%expect {| [| (1, 2) |] |}];
test_re (seq [ char 'a'; eol ]) "ab";
[%expect {| Not_found |}]
;;
let%expect_test "bow" =
test_re (seq [ bow; char 'a' ]) "a";
[%expect {| [| (0, 1) |] |}];
test_re (seq [ bow; char 'a' ]) "bb aa";
[%expect {| [| (3, 4) |] |}];
test_re (seq [ bow; char 'a' ]) "ba ba";
[%expect {| Not_found |}];
test_re bow ";";
[%expect {| Not_found |}];
test_re bow "";
[%expect {| Not_found |}]
;;
let%expect_test "eow" =
test_re (seq [ char 'a'; eow ]) "a";
[%expect {| [| (0, 1) |] |}];
test_re (seq [ char 'a'; eow ]) "bb aa";
[%expect {| [| (4, 5) |] |}];
test_re (seq [ char 'a'; eow ]) "ab ab";
[%expect {| Not_found |}];
test_re eow ";";
[%expect {| Not_found |}];
test_re eow "";
[%expect {| Not_found |}]
;;
let%expect_test "bos" =
test_re (seq [ bos; char 'a' ]) "ab";
[%expect {| [| (0, 1) |] |}];
test_re (seq [ bos; char 'a' ]) "b\na";
[%expect {| Not_found |}];
test_re (seq [ bos; char 'a' ]) "ba";
[%expect {| Not_found |}]
;;
let%expect_test "eos" =
test_re (seq [ char 'a'; eos ]) "ba";
[%expect {| [| (1, 2) |] |}];
test_re (seq [ char 'a'; eos ]) "a\nb";
[%expect {| Not_found |}];
test_re (seq [ char 'a'; eos ]) "ba\n";
[%expect {| Not_found |}];
test_re (seq [ char 'a'; eos ]) "ab";
[%expect {| Not_found |}]
;;
let%expect_test "leol" =
test_re (seq [ char 'a'; leol ]) "ba";
[%expect {| [| (1, 2) |] |}];
test_re (seq [ char 'a'; leol ]) "a\nb";
[%expect {| Not_found |}];
test_re (seq [ char 'a'; leol ]) "ba\n";
[%expect {| [| (1, 2) |] |}];
test_re (seq [ char 'a'; leol ]) "ab";
[%expect {| Not_found |}];
test_re (alt [ str "b\n"; seq [ char 'a'; leol ] ]) "ab\n";
[%expect {| [| (1, 3) |] |}]
;;
let%expect_test "start" =
test_re ~pos:1 (seq [ start; char 'a' ]) "xab";
[%expect {| [| (1, 2) |] |}];
test_re ~pos:1 (seq [ start; char 'a' ]) "xb\na";
[%expect {| Not_found |}];
test_re ~pos:1 (seq [ start; char 'a' ]) "xba";
[%expect {| Not_found |}]
;;
let%expect_test "stop" =
test_re ~len:2 (seq [ char 'a'; stop ]) "bax";
[%expect {| [| (1, 2) |] |}];
test_re ~len:3 (seq [ char 'a'; stop ]) "a\nbx";
[%expect {| Not_found |}];
test_re ~len:3 (seq [ char 'a'; stop ]) "ba\nx";
[%expect {| Not_found |}];
test_re ~len:2 (seq [ char 'a'; stop ]) "abx";
[%expect {| Not_found |}]
;;
let%expect_test "word" =
test_re (word (str "aa")) "aa";
[%expect {| [| (0, 2) |] |}];
test_re (word (str "aa")) "bb aa";
[%expect {| [| (3, 5) |] |}];
test_re (word (str "aa")) "aaa";
[%expect {| Not_found |}];
test_re (word (str "")) "";
[%expect {| Not_found |}]
;;
let%expect_test "not_boundary" =
test_re (seq [ not_boundary; char 'b'; not_boundary ]) "abc";
[%expect {| [| (1, 2) |] |}];
test_re (seq [ char ';'; not_boundary; char ';' ]) ";;";
[%expect {| [| (0, 2) |] |}];
test_re (seq [ not_boundary; char ';'; not_boundary ]) ";";
[%expect {| [| (0, 1) |] |}];
test_re (seq [ not_boundary; char 'a' ]) "abc";
[%expect {| Not_found |}];
test_re (seq [ char 'c'; not_boundary ]) "abc";
[%expect {| Not_found |}]
;;
let%expect_test "default match semantics" =
test_re (seq [ rep (alt [ char 'a'; char 'b' ]); char 'b' ]) "aabaab";
[%expect {| [| (0, 6) |] |}];
test_re (alt [ str "aa"; str "aaa" ]) "aaaa";
[%expect {| [| (0, 2) |] |}];
test_re (alt [ str "aaa"; str "aa" ]) "aaaa";
[%expect {| [| (0, 3) |] |}]
;;
let%expect_test "shortest match" =
test_re (shortest (seq [ rep (alt [ char 'a'; char 'b' ]); char 'b' ])) "aabaab";
[%expect {| [| (0, 3) |] |}];
test_re (shortest (alt [ str "aa"; str "aaa" ])) "aaaa";
[%expect {| [| (0, 2) |] |}];
test_re (shortest (alt [ str "aaa"; str "aa" ])) "aaaa";
[%expect {| [| (0, 2) |] |}]
;;
let%expect_test "longest match" =
test_re (longest (seq [ rep (alt [ char 'a'; char 'b' ]); char 'b' ])) "aabaab";
[%expect {| [| (0, 6) |] |}];
test_re (longest (alt [ str "aa"; str "aaa" ])) "aaaa";
[%expect {| [| (0, 3) |] |}];
test_re (longest (alt [ str "aaa"; str "aa" ])) "aaaa";
[%expect {| [| (0, 3) |] |}]
;;
let%expect_test "first match" =
test_re (first (seq [ rep (alt [ char 'a'; char 'b' ]); char 'b' ])) "aabaab";
[%expect {| [| (0, 6) |] |}];
test_re (first (alt [ str "aa"; str "aaa" ])) "aaaa";
[%expect {| [| (0, 2) |] |}];
test_re (first (alt [ str "aaa"; str "aa" ])) "aaaa";
[%expect {| [| (0, 3) |] |}]
;;
let%expect_test "match_semantics" =
let r = rep (group (alt [ str "aaa"; str "aa" ])) in
test_re (longest r) "aaaaaaa";
[%expect {| [| (0, 7); (5, 7) |] |}];
test_re (first r) "aaaaaaa";
[%expect {| [| (0, 6); (3, 6) |] |}];
test_re (first (non_greedy r)) "aaaaaaa";
[%expect {| [| (0, 0); (-1, -1) |] |}];
test_re (shortest r) "aaaaaaa";
[%expect {| [| (0, 0); (-1, -1) |] |}];
let r' = rep (group (shortest (alt [ str "aaa"; str "aa" ]))) in
test_re (longest r') "aaaaaaa";
[%expect {| [| (0, 7); (4, 7) |] |}];
test_re (first r') "aaaaaaa";
[%expect {| [| (0, 6); (4, 6) |] |}]
;;
let%expect_test "greedy" =
test_re (greedy (seq [ rep (alt [ char 'a'; char 'b' ]); char 'b' ])) "aabaab";
[%expect {| [| (0, 6) |] |}];
test_re (greedy (rep (group (opt (char 'a'))))) "aa";
[%expect {| [| (0, 2); (2, 2) |] |}]
;;
let%expect_test "non_greedy" =
test_re
(non_greedy (longest (seq [ rep (alt [ char 'a'; char 'b' ]); char 'b' ])))
"aabaab";
[%expect {| [| (0, 6) |] |}];
test_re
(non_greedy (first (seq [ rep (alt [ char 'a'; char 'b' ]); char 'b' ])))
"aabaab";
[%expect {| [| (0, 3) |] |}];
test_re (non_greedy (longest (rep (group (opt (char 'a')))))) "aa";
[%expect {| [| (0, 2); (1, 2) |] |}]
;;
let%expect_test "set" =
test_re (rep1 (set "abcd")) "bcbadbabcdba";
[%expect {| [| (0, 12) |] |}];
test_re (set "abcd") "e";
[%expect {| Not_found |}]
;;
let%expect_test "rg" =
test_re (rep1 (rg '0' '9')) "0123456789";
[%expect {| [| (0, 10) |] |}];
test_re (rep1 (rg '0' '9')) "a";
[%expect {| Not_found |}]
;;
let%expect_test "inter" =
test_re (rep1 (inter [ rg '0' '9'; rg '4' '6' ])) "456";
[%expect {| [| (0, 3) |] |}];
test_re (rep1 (inter [ rg '0' '9'; rg '4' '6' ])) "7";
[%expect {| Not_found |}];
test_re (inter [ alt [ char 'a'; char 'b' ]; char 'b' ]) "b";
[%expect {| [| (0, 1) |] |}]
;;
let%expect_test "diff" =
test_re (rep1 (diff (rg '0' '9') (rg '4' '6'))) "0123789";
[%expect {| [| (0, 7) |] |}];
test_re (rep1 (diff (rg '0' '9') (rg '4' '6'))) "4";
[%expect {| Not_found |}]
;;
let%expect_test "compl" =
test_re (rep1 (compl [ rg '0' '9'; rg 'a' 'z' ])) "A:Z+";
[%expect {| [| (0, 4) |] |}];
test_re (rep1 (compl [ rg '0' '9'; rg 'a' 'z' ])) "0";
[%expect {| Not_found |}];
test_re (rep1 (compl [ rg '0' '9'; rg 'a' 'z' ])) "a";
[%expect {| Not_found |}]
;;
let%expect_test "case" =
test_re (case (str "abc")) "abc";
[%expect {| [| (0, 3) |] |}];
test_re (no_case (case (str "abc"))) "abc";
[%expect {| [| (0, 3) |] |}];
test_re (case (str "abc")) "ABC";
[%expect {| Not_found |}];
test_re (no_case (case (str "abc"))) "ABC";
[%expect {| Not_found |}]
;;
let%expect_test "no_case" =
test_re (no_case (str "abc")) "abc";
[%expect {| [| (0, 3) |] |}];
test_re (no_case (str "abc")) "ABC";
[%expect {| [| (0, 3) |] |}];
test_re (case (no_case (str "abc"))) "abc";
[%expect {| [| (0, 3) |] |}];
test_re (case (no_case (str "abc"))) "ABC";
[%expect {| [| (0, 3) |] |}]
;;
let%expect_test "witness" =
let t re = print_endline (witness re) in
t (set "ac");
[%expect {| a |}];
t (repn (str "foo") 3 None);
[%expect {| foofoofoo |}];
t (alt [ char 'c'; char 'd' ]);
[%expect {| c |}];
t (no_case (str "test"));
[%expect {| TEST |}];
t eol;
[%expect {| |}]
;;

View file

@ -0,0 +1,33 @@
open Import
let%expect_test "test_replace" =
let re = Re.Posix.compile_pat "[a-zA-Z]+" in
let f sub = String.capitalize_ascii (Re.Group.get sub 0) in
print_endline (Re.replace re ~f " hello world; I love chips!");
[%expect {| Hello World; I Love Chips! |}];
print_endline (Re.replace ~all:false re ~f " allo maman, bobo");
[%expect {| Allo maman, bobo |}];
print_endline (Re.replace re_empty ~f:(fun _ -> "a") "");
[%expect {| a |}];
print_endline (Re.replace (Re.compile (Re.rep (Re.char 'a'))) ~f:(fun _ -> "*") "cat");
[%expect {| *c*t* |}]
;;
let%expect_test "test_replace_string" =
let re = Re.Posix.compile_pat "_[a-zA-Z]+_" in
print_endline (Re.replace_string re ~by:"goodbye" "_hello_ world");
[%expect {| goodbye world |}];
print_endline (Re.replace_string ~all:false re ~by:"brown" "The quick _XXX_ fox");
[%expect {| The quick brown fox |}]
;;
let%expect_test "test_bug_55" =
let re = Re.(compile bol) in
let res = Re.replace_string re ~by:"z" "abc" in
print_endline res;
[%expect {| zabc |}];
let re = Re.(compile eow) in
let res = Re.replace_string re ~by:"X" "one two three" in
print_endline res;
[%expect {| oneX twoX threeX |}]
;;

View file

@ -0,0 +1,78 @@
open Import
open Re
let%expect_test "fixed repetition" =
let re = Re.compile @@ Re.(repn (char 'a') 3 (Some 3)) in
let test s = printf "%b\n" (Re.execp re s) in
test "";
[%expect {| false |}];
test "aa";
[%expect {| false |}];
test "aaa";
[%expect {| true |}];
test "aaaa";
[%expect {| true |}]
;;
let%expect_test "repn" =
let a = char 'a' in
test_re (repn a 0 None) "";
[%expect {| [| (0, 0) |] |}];
test_re (repn a 2 None) "a";
[%expect {| Not_found |}];
test_re (repn a 2 None) "aa";
[%expect {| [| (0, 2) |] |}];
test_re (repn a 0 (Some 0)) "";
[%expect {| [| (0, 0) |] |}];
test_re (repn a 1 (Some 2)) "a";
[%expect {| [| (0, 1) |] |}];
test_re (repn a 1 (Some 2)) "aa";
[%expect {| [| (0, 2) |] |}];
test_re (repn a 1 (Some 2)) "";
[%expect {| Not_found |}];
test_re (repn a 1 (Some 2)) "aaa";
[%expect {| [| (0, 2) |] |}];
invalid_argument (fun () -> repn empty (-1) None);
[%expect {| Invalid_argument "Re.repn" |}];
invalid_argument (fun () -> repn empty 1 (Some 0));
[%expect {| Invalid_argument "Re.repn" |}];
invalid_argument (fun () -> repn empty 4 (Some 3));
[%expect {| Invalid_argument "Re.repn" |}]
;;
let%expect_test "rep1" =
test_re (rep1 (char 'a')) "a";
[%expect {| [| (0, 1) |] |}];
test_re (rep1 (char 'a')) "aa";
[%expect {| [| (0, 2) |] |}];
test_re (rep1 (char 'a')) "";
[%expect {| Not_found |}];
test_re (rep1 (char 'a')) "b";
[%expect {| Not_found |}]
;;
let%expect_test "opt" =
test_re (opt (char 'a')) "";
[%expect {| [| (0, 0) |] |}];
test_re (opt (char 'a')) "a";
[%expect {| [| (0, 1) |] |}]
;;
let copy s n =
let len = String.length s in
let b = Bytes.make (len * n) '\000' in
for i = 0 to n - 1 do
Bytes.blit_string s 0 b (i * len) len
done;
Bytes.to_string b
;;
let%expect_test "repeat sequence" =
let s = "abcde" in
let re = str s |> rep |> whole_string |> compile in
for i = 0 to 3 do
let r = copy s i in
assert (Re.execp re r)
done;
[%expect {||}]
;;

View file

@ -0,0 +1,76 @@
open Import
let re_whitespace = Re.Posix.compile_pat "[\t ]+"
let re_eol = Re.compile Re.eol
let re_bow = Re.compile Re.bow
let re_eow = Re.compile Re.eow
let%expect_test "split" =
let split ?pos ?len re s = strings (Re.split ?pos ?len re s) in
split re_whitespace "aa bb c d ";
[%expect {| ["aa"; "bb"; "c"; "d"] |}];
split ~pos:1 ~len:4 re_whitespace "aa b c d";
[%expect {| ["a"; "b"] |}];
split re_whitespace " a full_word bc ";
[%expect {| ["a"; "full_word"; "bc"] |}];
split re_empty "abcd";
[%expect {| ["a"; "b"; "c"; "d"] |}];
split re_eol "a\nb";
[%expect {|
["a"; "\nb"] |}];
split re_bow "a b";
[%expect {| ["a "; "b"] |}];
split re_eow "a b";
[%expect {| ["a"; " b"] |}];
split re_whitespace "";
[%expect {| [] |}];
split re_empty "";
[%expect {| [] |}]
;;
let%expect_test "split_delim" =
let split_delim ?pos ?len re s = strings (Re.split_delim ?pos ?len re s) in
split_delim re_whitespace "aa bb c d ";
[%expect {| ["aa"; "bb"; "c"; "d"; ""] |}];
split_delim ~pos:1 ~len:4 re_whitespace "aa b c d";
[%expect {| ["a"; "b"; ""] |}];
split_delim re_whitespace " a full_word bc ";
[%expect {| [""; "a"; "full_word"; "bc"; ""] |}];
split_delim re_empty "abcd";
[%expect {| [""; "a"; "b"; "c"; "d"; ""] |}];
split_delim re_eol "a\nb";
[%expect {| ["a"; "\nb"; ""] |}];
split_delim re_bow "a b";
[%expect {| [""; "a "; "b"] |}];
split_delim re_eow "a b";
[%expect {| ["a"; " b"; ""] |}];
split_delim re_whitespace "";
[%expect {| [""] |}];
split_delim re_empty "";
[%expect {| [""; ""] |}]
;;
let%expect_test "split_full" =
let split_full ?pos ?len re s =
let res = Re.split_full ?pos ?len re s in
Format.printf
"[%a]@."
Fmt.(
list ~pp_sep:(Fmt.lit "; ") (fun fmt what ->
match what with
| `Text s -> Format.fprintf fmt "`T %S" s
| `Delim s -> Format.fprintf fmt "`D %S" (Re.Group.get s 0)))
res
in
split_full re_whitespace "aa bb c d ";
[%expect {| [`T "aa"; `D " "; `T "bb"; `D " "; `T "c"; `D " "; `T "d"; `D " "] |}];
split_full ~pos:1 ~len:5 re_whitespace "aa \tb c d";
[%expect {| [`T "a"; `D " \t"; `T "b"; `D " "] |}];
split_full re_whitespace " a full_word bc ";
[%expect {| [`D " "; `T "a"; `D " "; `T "full_word"; `D " "; `T "bc"; `D " "] |}];
split_full re_empty "ab";
[%expect {| [`D ""; `T "a"; `D ""; `T "b"; `D ""] |}];
split_full Re.(compile (rep (char 'a'))) "cat";
[%expect {| [`D ""; `T "c"; `D "a"; `T "t"; `D ""] |}];
()
;;

View file

@ -0,0 +1,330 @@
open Import
module type Str_intf = module type of Str
module Test_matches (R : Str_intf) = struct
let groups () =
let group i =
try `Found (R.group_beginning i) with
| Not_found -> `Not_found
| Invalid_argument _ -> `Not_exists
in
let rec loop acc i =
match group i with
| `Found p -> loop ((p, R.group_end i) :: acc) (i + 1)
| `Not_found -> loop ((-1, -1) :: acc) (i + 1)
| `Not_exists -> List.rev acc
in
loop [] 0
;;
let eq_match ?(pos = 0) ?(case = true) r s =
let pat = if case then R.regexp r else R.regexp_case_fold r in
try
ignore (R.search_forward pat s pos);
Some (groups ())
with
| Not_found -> None
;;
let eq_match' ?(pos = 0) ?(case = true) r s =
let pat = if case then R.regexp r else R.regexp_case_fold r in
try
ignore (R.string_match pat s pos);
Some (groups ())
with
| Not_found -> None
;;
end
module T_str = Test_matches (Str)
module T_re = Test_matches (Re.Str)
let test dyn_of_ok str re args =
let run f =
match f () with
| s -> Ok s
| exception exn -> Error exn
in
let str = run (fun () -> str args) in
let re = run (fun () -> re args) in
if not (Poly.equal str re)
then (
let printer x =
let dyn =
let open Dyn in
result dyn_of_ok (fun x -> string (Printexc.to_string x)) x
in
sexp_of_dyn dyn |> Base.Sexp.to_string_hum
in
Printf.printf "str: %s\n" (printer str);
Printf.printf "re: %s\n" (printer re))
;;
let dyn_of_pairs x =
Dyn.option
(fun x ->
List.map x ~f:(fun (start, stop) ->
let open Dyn in
pair (int start) (int stop))
|> Dyn.list)
x
;;
let split_result_conv =
List.map ~f:(function
| Str.Delim x -> Re.Str.Delim x
| Str.Text x -> Re.Str.Text x)
;;
let dyn_split_result_list list =
List.map
list
~f:
(let open Dyn in
function
| Re.Str.Delim x -> variant "Delim" [ string x ]
| Text s -> variant "Text" [ string s ])
|> Dyn.list
;;
type ('a, 'b) test =
{ name : string
; dyn_of_ok : 'b -> Dyn.t
; re_str : Re.Str.regexp -> 'a -> 'b
; str : Str.regexp -> 'a -> 'b
}
let bounded_split_t =
{ name = "bounded_split"
; dyn_of_ok = (fun x -> Dyn.list (List.map x ~f:Dyn.string))
; re_str = (fun re (s, n) -> Re.Str.bounded_split re s n)
; str = (fun re (s, n) -> Str.bounded_split re s n)
}
;;
let bounded_full_split_t =
{ name = "bounded_full_split"
; dyn_of_ok = dyn_split_result_list
; re_str = (fun re (s, n) -> Re.Str.bounded_full_split re s n)
; str = (fun re (s, n) -> split_result_conv (Str.bounded_full_split re s n))
}
;;
let full_split_t =
{ bounded_full_split_t with
name = "full_split"
; re_str = (fun re s -> Re.Str.full_split re s)
; str = (fun re s -> split_result_conv (Str.full_split re s))
}
;;
let split_delim_t =
{ name = "split_delim"
; dyn_of_ok = (fun x -> Dyn.list (List.map x ~f:Dyn.string))
; re_str = Re.Str.split_delim
; str = Str.split_delim
}
;;
let split_t =
{ name = "split"
; dyn_of_ok = (fun x -> Dyn.list (List.map x ~f:Dyn.string))
; re_str = Re.Str.split
; str = Str.split
}
;;
let global_replace_t =
{ name = "global_replace"
; dyn_of_ok = Dyn.string
; re_str = (fun re (r, s) -> Re.Str.global_replace re r s)
; str = (fun re (r, s) -> Str.global_replace re r s)
}
;;
let eq_match ?pos ?case re =
test dyn_of_pairs (T_str.eq_match ?pos ?case re) (T_re.eq_match ?pos ?case re)
;;
let eq_match' ?pos ?case re =
test dyn_of_pairs (T_str.eq_match' ?pos ?case re) (T_re.eq_match' ?pos ?case re)
;;
let test t re args =
test t.dyn_of_ok (t.re_str (Re.Str.regexp re)) (t.str (Str.regexp re)) args
;;
let split_delim re s = test split_delim_t re s
let split re s = test split_t re s
let full_split re s = test full_split_t re s
let bounded_split re s n = test bounded_split_t re (s, n)
let bounded_full_split re s n = test bounded_full_split_t re (s, n)
let global_replace re r s = test global_replace_t re (r, s)
let%expect_test "literal match" =
eq_match "a" "a";
eq_match "a" "b";
[%expect {||}]
;;
let%expect_test "alt" =
eq_match "a\\|b" "a";
eq_match "a\\|b" "b";
eq_match "a\\|b" "c";
[%expect {||}]
;;
let%expect_test "seq" =
eq_match "ab" "ab";
eq_match "ab" "ac";
[%expect {||}]
;;
let%expect_test "epsilon" =
eq_match "" "";
eq_match "" "a";
[%expect {||}]
;;
let%expect_test "rep" =
eq_match "a*" "";
eq_match "a*" "a";
eq_match "a*" "aa";
eq_match "a*" "b";
[%expect {||}]
;;
let%expect_test "rep1" =
eq_match "a+" "a";
eq_match "a+" "aa";
eq_match "a+" "";
eq_match "a+" "b";
[%expect {| |}]
;;
let%expect_test "opt" =
eq_match "a?" "";
eq_match "a?" "a";
[%expect {| |}]
;;
let%expect_test "bol" =
eq_match "^a" "ab";
eq_match "^a" "b\na";
eq_match "^a" "ba";
[%expect {| |}]
;;
let%expect_test "eol" =
eq_match "a$" "ba";
eq_match "a$" "a\nb";
eq_match "a$" "ba\n";
eq_match "a$" "ab";
[%expect {| |}]
;;
let%expect_test "start" =
eq_match ~pos:1 "Za" "xab";
eq_match ~pos:1 "Za" "xb\na";
eq_match ~pos:1 "Za" "xba";
[%expect {||}]
;;
let%expect_test "match semantics" =
eq_match "\\(a\\|b\\)*b" "aabaab";
eq_match "aa\\|aaa" "aaaa";
eq_match "aaa\\|aa" "aaaa";
[%expect {||}]
;;
let%expect_test "Group (or submatch)" =
eq_match "\\(a\\)\\(a\\)?\\(b\\)" "ab";
[%expect {| |}];
eq_match "\\(foo" "foo";
[%expect {|
str: (Error "Failure(\"\\\\( group not closed by \\\\)\")")
re: (Error Re_private.Emacs.Parse_error)
|}]
;;
let%expect_test "Character set" =
eq_match "[0-9]+" "0123456789";
eq_match "[0-9]+" "a";
eq_match "[9-0]+" "2";
eq_match "[5-5]" "5";
eq_match "[5-4]" "1";
eq_match' "[]]" "]";
eq_match' "[a-]" "-";
eq_match' "[-a]" "-";
eq_match' "]" "]";
eq_match' "[^b-f]" "z";
eq_match' "[^b-f]" "a";
[%expect {||}];
(* These errors aren't correct *)
eq_match' "[]" "x";
eq_match' "[" "[";
[%expect
{|
str: (Error "Failure(\"[ class not closed by ]\")")
re: (Error Re_private.Emacs.Parse_error)
str: (Error "Failure(\"[ class not closed by ]\")")
re: (Error Re_private.Emacs.Parse_error)
|}]
;;
let%expect_test "compl" =
eq_match "[^0-9a-z]+" "A:Z+";
eq_match "[^0-9a-z]+" "0";
eq_match "[^0-9a-z]+" "a";
[%expect {||}]
;;
let%expect_test "Word modifiers" =
eq_match' "\\bfoo" "foo";
eq_match' "\\<foo" "foo";
eq_match' "foo\\>" "foo";
eq_match' "z\\Bfoo" "zfoo";
eq_match' "\\`foo" "foo";
eq_match' "foo\\'" "foo";
[%expect {||}]
;;
let%expect_test "Case modifiers" =
eq_match ~case:false "abc" "abc";
eq_match ~case:false "abc" "ABC";
[%expect {| |}]
;;
let%expect_test "global_replace" =
global_replace "needle" "test" "needlehaystack";
global_replace "needle" "" "";
global_replace "needle" "" "needle";
global_replace "xxx" "yyy" "zzz";
global_replace "test\\([0-9]*\\)" "\\1-foo-\\1" "test100 test200 test";
global_replace "test\\([0-9]*\\)" "'\\-0'" "test100 test200 test";
(* Regrssion test for #129 *)
global_replace "\\(X+\\)" "A\\1YY" "XXXXXXZZZZ";
[%expect {||}]
;;
let%expect_test "bounded_split, bounded_full_split" =
[ ",", "foo,bar,baz", 5
; ",", "foo,bar,baz", 1
; ",", "foo,bar,baz", 0
; ",\\|", "foo,bar|baz", 4
]
|> List.iter ~f:(fun (re, s, n) ->
bounded_full_split re s n;
bounded_split re s n);
[%expect {||}]
;;
let%expect_test "split, full_split, split_delim" =
[ "re", ""; " ", "foo bar"; "\b", "one-two three"; "[0-9]", "One3TwoFive" ]
|> List.iter ~f:(fun (re, s) ->
split re s;
full_split re s;
split_delim re s);
[%expect {||}]
;;

View file

@ -0,0 +1,203 @@
open Import
module Stream = Re.Stream
let feed t str =
let res = Stream.feed t str ~pos:0 ~len:(String.length str) in
let () =
match res with
| No_match -> Printf.printf "%S did not match\n" str
| Ok s ->
let status =
match Stream.finalize s "" ~pos:0 ~len:0 with
| true -> "matched"
| false -> "unmatched"
in
Printf.printf "%S not matched (status = %s)\n" str status
in
res
;;
let%expect_test "out out of bounds" =
let stream = Re.any |> Re.compile |> Stream.create in
invalid_argument (fun () -> ignore (Stream.feed stream "foo" ~pos:2 ~len:3));
[%expect {| Invalid_argument "index out of bounds" |}];
invalid_argument (fun () -> ignore (Stream.finalize stream "foo" ~pos:2 ~len:3));
[%expect {| Invalid_argument "index out of bounds" |}];
let stream = Stream.Group.create stream in
invalid_argument (fun () -> ignore (Stream.Group.feed stream "foo" ~pos:2 ~len:3));
[%expect {| Invalid_argument "index out of bounds" |}];
invalid_argument (fun () -> ignore (Stream.Group.finalize stream "foo" ~pos:2 ~len:3));
[%expect {| Invalid_argument "index out of bounds" |}]
;;
let%expect_test "basic" =
let s = [ Re.bos; Re.str "abab" ] |> Re.seq |> Re.compile |> Stream.create in
ignore (feed s "x");
[%expect {| "x" did not match |}];
let suffix = "ab" in
let s =
match feed s suffix with
| Ok s -> s
| No_match -> assert false
in
[%expect {|
"ab" not matched (status = unmatched) |}];
(let (_ : _ Stream.feed) = feed s "ab" in
[%expect {|
"ab" not matched (status = matched) |}]);
let (_ : _ Stream.feed) = feed s "xy" in
[%expect {|
"xy" did not match |}]
;;
let%expect_test "eos" =
let s = [ Re.str "zzz"; Re.eos ] |> Re.seq |> Re.compile |> Stream.create in
ignore (feed s "zzz");
[%expect {| "zzz" not matched (status = matched) |}];
let s =
match feed s "z" with
| Ok s -> s
| No_match -> assert false
in
[%expect {| "z" not matched (status = unmatched) |}];
(let str = "zz" in
match Stream.finalize s str ~pos:0 ~len:(String.length str) with
| true -> ()
| false -> assert false);
[%expect {||}]
;;
let%expect_test "finalize empty" =
let s = "abde" in
let stream =
let stream = Re.str s |> Re.whole_string |> Re.compile |> Stream.create in
match feed stream s with
| Ok s -> s
| No_match -> assert false
in
assert (Stream.finalize stream "" ~pos:0 ~len:0);
[%expect {| "abde" not matched (status = matched) |}]
;;
let%expect_test "group - basic" =
let s =
let open Re in
str "foo" |> whole_string |> group |> compile |> Stream.create
in
let g = Stream.Group.create s in
let g =
match Stream.Group.feed g "f" ~pos:0 ~len:1 with
| No_match -> assert false
| Ok s -> s
in
(match Stream.Group.finalize g "oo" ~pos:0 ~len:2 with
| Ok _ -> ()
| No_match -> assert false);
[%expect {| |}]
;;
let pmarks set m =
Printf.printf "mark present %b\n" (Re.Stream.Group.Match.test_mark set m)
;;
let%expect_test "group - mark entire string must match" =
let m1, f = Re.(mark (char 'f')) in
let m2, oo = Re.(mark (str "oo")) in
let re =
let open Re in
[ f; oo ] |> seq |> compile
in
let s = Stream.create re in
let g = Stream.Group.create s in
let g =
match Stream.Group.feed g "f" ~pos:0 ~len:1 with
| No_match -> assert false
| Ok s -> s
in
let g =
match Stream.Group.finalize g "oo" ~pos:0 ~len:2 with
| Ok g -> g
| No_match -> assert false
in
pmarks g m1;
[%expect {| mark present true |}];
pmarks g m2;
[%expect {| mark present true |}]
;;
let%expect_test "group - partial mark match" =
let m, foo = Re.(mark (str "foo")) in
let re = Re.compile foo in
let s = Stream.create re in
let g = Stream.Group.create s in
let g =
match Stream.Group.feed g "xx" ~pos:0 ~len:2 with
| No_match -> assert false
| Ok g -> g
in
let g =
match Stream.Group.feed g "foo" ~pos:0 ~len:3 with
| Ok g -> g
| No_match -> assert false
in
let g =
match Stream.Group.finalize g "garb" ~pos:0 ~len:4 with
| Ok g -> g
| No_match -> assert false
in
pmarks g m;
[%expect {| mark present true |}]
;;
let print_match match_ n =
match Stream.Group.Match.get match_ n with
| None -> Printf.printf "match %d: <not found>\n" n
| Some s -> Printf.printf "match %d: %s\n" n s
;;
let%expect_test "group - match group" =
let stream =
let re = Re.Pcre.re "_([a-z]+)_" |> Re.whole_string |> Re.compile in
Stream.Group.create (Stream.create re)
in
let s = "_abc_" in
let () =
match Stream.Group.finalize stream s ~pos:0 ~len:(String.length s) with
| No_match -> assert false
| Ok m ->
for i = 0 to 1 do
print_match m i
done
in
[%expect {|
match 0: _abc_
match 1: abc
|}]
;;
let%expect_test "group - match group" =
let stream =
let re = Re.Pcre.re "_([a-z]+)__([a-z]+)_" |> Re.whole_string |> Re.compile in
Stream.Group.create (Stream.create re)
in
let s = "_abc_" in
let stream =
match Stream.Group.feed stream s ~pos:0 ~len:(String.length s) with
| No_match -> assert false
| Ok m -> m
in
let s = "_de_" in
let () =
match Stream.Group.finalize stream s ~pos:0 ~len:(String.length s) with
| No_match -> assert false
| Ok m ->
for i = 0 to 2 do
print_match m i
done
in
[%expect {|
match 0: _abc__de_
match 1: abc
match 2: de
|}]
;;

View file

@ -0,0 +1,12 @@
open Import
let () = Printexc.record_backtrace false
let any = Re.(compile (rep any))
let%expect_test "bound errors" =
let (_ : bool) = Re.execp any ~pos:4 "foo" in
[%expect {| |}];
let (_ : bool) = Re.execp any ~pos:1 ~len:3 "foo" in
[%expect.unreachable]
[@@expect.uncaught_exn {| (Invalid_argument "Re.exec: out of bounds") |}]
;;

View file

@ -0,0 +1,6 @@
open Import
let%expect_test "view" =
let view = Re.View.view (Re.str "foo") in
ignore view
;;