Files
rose-ash/spec/tests/test-string-regex.sx
giles 5d88b363e4 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>
2026-04-05 20:38:40 +00:00

150 lines
4.4 KiB
Plaintext

;; String/regex primitive tests
(defsuite
"string-char-at"
(deftest "char-at first" (assert= "h" (char-at "hello" 0)))
(deftest "char-at middle" (assert= "l" (char-at "hello" 2)))
(deftest "char-at last" (assert= "o" (char-at "hello" 4)))
(deftest "char-at out of bounds" (assert= nil (char-at "hello" 10)))
(deftest "char-at negative" (assert= nil (char-at "hello" -1))))
(defsuite
"string-char-code"
(deftest "char-code a" (assert= 97 (char-code "a")))
(deftest "char-code A" (assert= 65 (char-code "A")))
(deftest "char-code 0" (assert= 48 (char-code "0")))
(deftest "char-code space" (assert= 32 (char-code " ")))
(deftest
"char-code roundtrip"
(assert= "a" (char-from-code (char-code "a"))))
(deftest
"char-from-code roundtrip"
(assert= 65 (char-code (char-from-code 65)))))
(defsuite
"string-parse-number"
(deftest "parse-number integer" (assert= 42 (parse-number "42")))
(deftest "parse-number float" (assert= 3.14 (parse-number "3.14")))
(deftest "parse-number negative" (assert= -7 (parse-number "-7")))
(deftest
"parse-number negative float"
(assert= -2.5 (parse-number "-2.5")))
(deftest "parse-number zero" (assert= 0 (parse-number "0")))
(deftest
"parse-number invalid returns nil"
(assert= nil (parse-number "abc")))
(deftest "parse-number empty returns nil" (assert= nil (parse-number ""))))
(defsuite
"regex-match"
(deftest
"regex-match simple"
(let
((r (regex-match "h.llo" "hello world")))
(assert (list? r))
(assert= "hello" (first r))))
(deftest "regex-match no match" (assert= nil (regex-match "xyz" "hello")))
(deftest
"regex-match with group"
(let
((r (regex-match "(h)ello" "hello")))
(assert (list? r))
(assert= "hello" (first r))
(assert= "h" (nth r 1))))
(deftest
"regex-match digits"
(let
((r (regex-match "[0-9]+" "abc123def")))
(assert= "123" (first r))))
(deftest
"regex-match anchored"
(assert= nil (regex-match "^world" "hello world")))
(deftest
"regex-match start"
(let
((r (regex-match "^hello" "hello world")))
(assert= "hello" (first r)))))
(defsuite
"regex-match?"
(deftest "regex-match? true" (assert (regex-match? "h.llo" "hello")))
(deftest "regex-match? false" (assert (not (regex-match? "xyz" "hello"))))
(deftest
"regex-match? digit pattern"
(assert (regex-match? "[0-9]" "abc1")))
(deftest
"regex-match? empty pattern"
(assert (regex-match? "" "anything"))))
(defsuite
"regex-find-all"
(deftest
"find-all digits"
(let
((result (regex-find-all "[0-9]" "a1b2c3")))
(assert= 3 (len result))
(assert= "1" (first result))
(assert= "3" (nth result 2))))
(deftest
"find-all words"
(let
((result (regex-find-all "[a-z]+" "hello 123 world")))
(assert= 2 (len result))
(assert= "hello" (first result))
(assert= "world" (nth result 1))))
(deftest
"find-all no matches"
(assert= (list) (regex-find-all "[0-9]" "abc")))
(deftest
"find-all multi-char"
(let
((result (regex-find-all "ab" "xababx")))
(assert= 2 (len result))
(assert= "ab" (first result))
(assert= "ab" (nth result 1)))))
(defsuite
"regex-replace"
(deftest
"replace all digits"
(assert= "a_b_c_" (regex-replace "[0-9]" "_" "a1b2c3")))
(deftest
"replace word"
(assert= "hi hi" (regex-replace "hello" "hi" "hello hello")))
(deftest
"replace no match"
(assert= "hello" (regex-replace "xyz" "!" "hello")))
(deftest
"replace empty pattern"
(assert= "hello" (regex-replace "^$" "!" "hello"))))
(defsuite
"regex-replace-first"
(deftest
"replace-first digit"
(assert= "a_b2c3" (regex-replace-first "[0-9]" "_" "a1b2c3")))
(deftest
"replace-first no match"
(assert= "hello" (regex-replace-first "xyz" "!" "hello"))))
(defsuite
"regex-split"
(deftest
"split on whitespace"
(assert= (list "hello" "world") (regex-split "[ \t]+" "hello world")))
(deftest
"split on comma-space"
(assert= (list "a" "b" "c") (regex-split ", *" "a, b,c")))
(deftest
"split no match"
(assert= (list "hello") (regex-split ";" "hello")))
(deftest
"split digits"
(assert= (list "a" "b" "c") (regex-split "[0-9]+" "a1b23c"))))
(defsuite
"string-split-multichar"
(deftest
"split on multi-char separator"
(assert= (list "a" "b" "c") (split "a::b::c" "::")))
(deftest "split on arrow" (assert= (list "a" "b") (split "a->b" "->"))))