diff --git a/hosts/ocaml/bin/run_tests.ml b/hosts/ocaml/bin/run_tests.ml index b09a482a..e0431121 100644 --- a/hosts/ocaml/bin/run_tests.ml +++ b/hosts/ocaml/bin/run_tests.ml @@ -3516,6 +3516,32 @@ let run_spec_tests env test_files = reg "host-await" (fun _args -> Nil); + (* host-call-fn: hyperscript's call bridge — (host-call-fn fn arg-list). In a + real host it reaches into JS; in the headless mock it is simply apply. This + was the single biggest gap: ~900 behavioral tests failed on "Undefined + symbol: host-call-fn". host-call-fn-raising propagates errors (used by the + try/catch machinery); host-new-function can't run raw JS headless so stubs. *) + let list_elems = function + | List l | ListRef { contents = l } -> l + | Nil -> [] + | v -> [v] in + reg "host-call-fn" (fun args -> + match args with + | fn :: rest -> + let call_args = match rest with a :: _ -> list_elems a | [] -> [] in + (try Sx_ref.cek_call fn (List call_args) + with e -> Printf.eprintf "[mock] host-call-fn error: %s\n%!" (Printexc.to_string e); Nil) + | _ -> Nil); + reg "host-call-fn-raising" (fun args -> + match args with + | fn :: rest -> + let call_args = match rest with a :: _ -> list_elems a | [] -> [] in + Sx_ref.cek_call fn (List call_args) + | _ -> Nil); + reg "host-new-function" (fun _ -> NativeFn ("host-new-function-stub", fun _ -> Nil)); + reg "host-iter?" (fun _ -> Bool false); + reg "host-to-list" (fun args -> match args with [v] -> List (list_elems v) | _ -> List []); + (* Minimal JSON parse/stringify used by hs-coerce (as JSON / as JSONString). *) let rec json_of_value = function | Nil -> `Null diff --git a/hosts/ocaml/lib/sx_primitives.ml b/hosts/ocaml/lib/sx_primitives.ml index 93f82e17..95d68983 100644 --- a/hosts/ocaml/lib/sx_primitives.ml +++ b/hosts/ocaml/lib/sx_primitives.ml @@ -2306,7 +2306,13 @@ let () = | Some r -> r | None -> raise (Eval_error "regex: handle not found")) | _ -> raise (Eval_error "regex: missing id")) - | _ -> raise (Eval_error "regex: expected regex dict") + | String s -> + (* Accept a raw pattern string: auto-compile it. Lets regex-split / + regex-match-all / regex-test / etc. take a pattern directly, not only + a pre-compiled regex dict from regex-compile. *) + (try (Re.compile (Re.Pcre.re s), s, "") + with _ -> raise (Eval_error ("regex: invalid pattern " ^ s))) + | _ -> raise (Eval_error "regex: expected regex dict or pattern string") in let group_to_dict g input = let d = Hashtbl.create 4 in @@ -2368,6 +2374,14 @@ let () = | _ -> raise (Eval_error "regex-match-all: (regex string)")); register "regex-replace" (fun args -> match args with + | [String pattern; String replacement; String input] -> + (* string API: (regex-replace pattern replacement input) — replace ALL + matches of a raw pattern. Disambiguated from the compiled-regex API + below by the first arg being a String rather than a regex Dict. *) + (try + let re = Re.Pcre.re pattern |> Re.compile in + String (Re.replace_string re ~by:replacement input) + with _ -> String input) | [rx; String s; String replacement] -> let (re, _, flags) = regex_of_value rx in let expand g =