Step 13: String/regex primitives — PCRE-compatible, cross-host
New primitives in sx_primitives.ml: char-at, char-code, parse-number — string inspection + conversion regex-match, regex-match?, regex-find-all — PCRE pattern matching regex-replace, regex-replace-first — PCRE substitution regex-split — split by PCRE pattern Uses Re.Pcre (OCaml re library) so regex patterns use the same syntax as JS RegExp — patterns in .sx files work identically on browser and server. Replaces the old test-only regex-find-all stub. Also: split now handles multi-char separators via Re. 176 new tests (10 suites). 2912/2912 total, zero failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -470,52 +470,7 @@ let make_test_env () =
|
||||
let stack = try Hashtbl.find _scope_stacks name with Not_found -> [] in
|
||||
(match stack with _ :: rest -> Hashtbl.replace _scope_stacks name (List [] :: rest) | [] -> ()); Nil
|
||||
| _ -> Nil);
|
||||
bind "regex-find-all" (fun args ->
|
||||
(* Stub: supports ~name patterns for component scanning *)
|
||||
match args with
|
||||
| [String pattern; String text] ->
|
||||
(* Extract the literal prefix from patterns like:
|
||||
"(~[a-z/.-]+" → prefix "~", has_group=true
|
||||
"\(~([a-zA-Z_]..." → prefix "(~", has_group=true *)
|
||||
let prefix, has_group =
|
||||
if String.length pattern >= 4 && pattern.[0] = '\\' && pattern.[1] = '(' then
|
||||
(* Pattern like \(~(...) — literal "(" + "~" prefix, group after *)
|
||||
let s = String.sub pattern 2 (String.length pattern - 2) in
|
||||
let lit_end = try String.index s '(' with Not_found -> try String.index s '[' with Not_found -> String.length s in
|
||||
let lit = String.sub s 0 lit_end in
|
||||
("(" ^ lit, true)
|
||||
else if String.length pattern > 2 && pattern.[0] = '(' then
|
||||
let s = String.sub pattern 1 (String.length pattern - 1) in
|
||||
let p = try String.sub s 0 (String.index s '[')
|
||||
with Not_found -> try String.sub s 0 (String.index s '(')
|
||||
with Not_found -> s in
|
||||
((if String.length p > 0 then p else "~"), true)
|
||||
else (pattern, false)
|
||||
in
|
||||
let results = ref [] in
|
||||
let len = String.length text in
|
||||
let plen = String.length prefix in
|
||||
let i = ref 0 in
|
||||
while !i <= len - plen do
|
||||
if String.sub text !i plen = prefix then begin
|
||||
(* Find end of identifier *)
|
||||
let j = ref (!i + plen) in
|
||||
while !j < len && let c = text.[!j] in
|
||||
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
|
||||
|| c = '-' || c = '/' || c = '_' || c = '.' do
|
||||
incr j
|
||||
done;
|
||||
let full_match = String.sub text !i (!j - !i) in
|
||||
(* If pattern has capture group, strip the literal prefix to simulate group 1 *)
|
||||
let result = if has_group then
|
||||
String.sub full_match plen (String.length full_match - plen)
|
||||
else full_match in
|
||||
results := String result :: !results;
|
||||
i := !j
|
||||
end else incr i
|
||||
done;
|
||||
List (List.rev !results)
|
||||
| _ -> List []);
|
||||
(* regex-find-all now provided by sx_primitives.ml *)
|
||||
bind "callable?" (fun args ->
|
||||
match args with
|
||||
| [NativeFn _] | [Lambda _] | [Component _] | [Island _] -> Bool true
|
||||
|
||||
Reference in New Issue
Block a user