diff --git a/lib/ocaml/eval.sx b/lib/ocaml/eval.sx index 35115a33..03a93208 100644 --- a/lib/ocaml/eval.sx +++ b/lib/ocaml/eval.sx @@ -58,6 +58,15 @@ (list "_string_upper" (fn (s) (upper s))) (list "_string_lower" (fn (s) (lower s))) (list "_string_starts_with" (fn (p) (fn (s) (starts-with? s p)))) + (list "_string_ends_with" (fn (p) (fn (s) (ends-with? s p)))) + (list "_string_contains" (fn (s) (fn (sub) (contains? s sub)))) + (list "_string_trim" (fn (s) (trim s))) + (list "_string_split_on_char" + (fn (sep) (fn (s) (split s sep)))) + (list "_string_replace" + (fn (s) (fn (a) (fn (b) (replace s a b))))) + (list "_string_index_of" + (fn (s) (fn (sub) (index-of s sub)))) (list "_int_of_string" (fn (s) (parse-number s))) (list "_string_of_int" (fn (i) (str i))) (list "_string_of_float" (fn (f) (str f))) diff --git a/lib/ocaml/runtime.sx b/lib/ocaml/runtime.sx index 0ea5c882..e1af957d 100644 --- a/lib/ocaml/runtime.sx +++ b/lib/ocaml/runtime.sx @@ -326,6 +326,12 @@ let uppercase_ascii s = _string_upper s let lowercase_ascii s = _string_lower s let starts_with prefix s = _string_starts_with prefix s + let ends_with suffix s = _string_ends_with suffix s + let contains s sub = _string_contains s sub + let trim s = _string_trim s + let split_on_char c s = _string_split_on_char c s + let replace_all s a b = _string_replace s a b + let index_of s sub = _string_index_of s sub end ;; module Char = struct diff --git a/lib/ocaml/test.sh b/lib/ocaml/test.sh index 4c39a5ae..43e2da40 100755 --- a/lib/ocaml/test.sh +++ b/lib/ocaml/test.sh @@ -990,6 +990,20 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 2905) (eval "(ocaml-run \"List.take 100 [1;2;3]\")") +;; ── String extensions ────────────────────────────────────────── +(epoch 3000) +(eval "(ocaml-run \"String.ends_with \\\"lo\\\" \\\"hello\\\"\")") +(epoch 3001) +(eval "(ocaml-run \"String.contains \\\"hello\\\" \\\"ell\\\"\")") +(epoch 3002) +(eval "(ocaml-run \"String.trim \\\" hi \\\"\")") +(epoch 3003) +(eval "(ocaml-run \"String.split_on_char \\\" \\\" \\\"a b c\\\"\")") +(epoch 3004) +(eval "(ocaml-run \"String.replace_all \\\"hello\\\" \\\"l\\\" \\\"r\\\"\")") +(epoch 3005) +(eval "(ocaml-run \"String.index_of \\\"hello\\\" \\\"ll\\\"\")") + EPOCHS OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1566,6 +1580,14 @@ check 2903 "List.flat_map double" '(1 1 2 2 3 3)' check 2904 "List.take 0" '()' check 2905 "List.take overflow" '(1 2 3)' +# ── String extensions ────────────────────────────────────────── +check 3000 "String.ends_with" 'true' +check 3001 "String.contains" 'true' +check 3002 "String.trim" '"hi"' +check 3003 "String.split_on_char" '("a" "b" "c")' +check 3004 "String.replace_all" '"herro"' +check 3005 "String.index_of" '2' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL OCaml-on-SX tests passed" diff --git a/plans/ocaml-on-sx.md b/plans/ocaml-on-sx.md index 2d6d0a26..37288d34 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -262,9 +262,9 @@ SX CEK evaluator (both JS and OCaml hosts) - [~] `Hashtbl`: `create`, `add`, `find`, `find_opt`, `replace`, `mem`, `length`. Backed by a one-element list cell holding a SX dict; keys coerced to strings via `str` for polymorphic-key support. -- [~] `String`: `length`, `get`, `sub`, `concat`, `uppercase_ascii`, - `lowercase_ascii`, `starts_with`. _(Pending: split_on_char, trim, - contains, ends_with, index_opt, replace_all.)_ +- [x] `String`: `length`, `get`, `sub`, `concat`, `uppercase_ascii`, + `lowercase_ascii`, `starts_with`, `ends_with`, `contains`, `trim`, + `split_on_char`, `replace_all`, `index_of`. - [~] `Char`: `code`, `chr`, `lowercase_ascii`, `uppercase_ascii`. _(Pending: escaped.)_ - [~] `Int`: `to_string`, `of_string`, `abs`, `max`, `min`. @@ -377,6 +377,9 @@ the "mother tongue" closure: OCaml → SX → OCaml. This means: _Newest first._ +- 2026-05-08 Phase 6 — String extensions: ends_with/contains/trim/ + split_on_char/replace_all/index_of (+6 tests, 406 total). Wraps host + primitives via `_string_*` builtins. - 2026-05-08 Phase 6 — `List.take/drop/filter_map/flat_map/concat_map` (+6 tests, 400 total). Common functional helpers, all written in OCaml. **400-test milestone.**