mte/unikernel/duniverse/ocaml-re/lib_test/expect/test_str.ml
2025-11-11 02:07:51 +01:00

330 lines
7.5 KiB
OCaml

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 {||}]
;;