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>
150 lines
4.4 KiB
Plaintext
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" "->")))) |