SxRational type in OCaml (Rational of int * int, stored reduced, denom>0) and JS (SxRational class with _rational marker). n/d reader syntax in spec/parser.sx. Arithmetic contagion: int op rational → rational, rational op float → float. JS keeps int/int → float for CSS backward compatibility. OCaml as_number + safe_eq extended for cross-type rational equality so (= 2.5 5/2) → true. 62 tests in test-rationals.sx, all pass. JS: 2232 passed. OCaml: 4532 passed (+11 vs pre-fix baseline). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
364 lines
11 KiB
Plaintext
364 lines
11 KiB
Plaintext
;; ==========================================================================
|
|
;; test-primitives.sx — Exhaustive tests for all pure primitives
|
|
;; ==========================================================================
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Arithmetic
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"arithmetic"
|
|
(deftest "add" (assert-equal 3 (+ 1 2)))
|
|
(deftest
|
|
"add multiple"
|
|
(assert-equal 10 (+ 1 2 3 4)))
|
|
(deftest "add zero" (assert-equal 5 (+ 5 0)))
|
|
(deftest
|
|
"add negative"
|
|
(assert-equal -1 (+ 1 -2)))
|
|
(deftest "subtract" (assert-equal 3 (- 5 2)))
|
|
(deftest
|
|
"subtract negative"
|
|
(assert-equal 7 (- 5 -2)))
|
|
(deftest "multiply" (assert-equal 12 (* 3 4)))
|
|
(deftest
|
|
"multiply zero"
|
|
(assert-equal 0 (* 5 0)))
|
|
(deftest
|
|
"multiply negative"
|
|
(assert-equal -6 (* 2 -3)))
|
|
(deftest "divide" (assert-equal 3 (/ 9 3)))
|
|
(deftest "divide float" (assert-equal 2.5 (/ 5 2)))
|
|
(deftest "mod" (assert-equal 1 (mod 7 3)))
|
|
(deftest
|
|
"mod negative"
|
|
(assert-true
|
|
(or
|
|
(= (mod -1 3) 2)
|
|
(= (mod -1 3) -1))))
|
|
(deftest "inc" (assert-equal 6 (inc 5)))
|
|
(deftest "dec" (assert-equal 4 (dec 5)))
|
|
(deftest "abs positive" (assert-equal 5 (abs 5)))
|
|
(deftest "abs negative" (assert-equal 5 (abs -5)))
|
|
(deftest "abs zero" (assert-equal 0 (abs 0)))
|
|
(deftest "min" (assert-equal 2 (min 2 5)))
|
|
(deftest "max" (assert-equal 5 (max 2 5))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Comparison
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"comparison"
|
|
(deftest "equal numbers" (assert-true (= 1 1)))
|
|
(deftest "not equal numbers" (assert-false (= 1 2)))
|
|
(deftest "equal strings" (assert-true (= "a" "a")))
|
|
(deftest "less than" (assert-true (< 1 2)))
|
|
(deftest "not less than" (assert-false (< 2 1)))
|
|
(deftest "greater than" (assert-true (> 2 1)))
|
|
(deftest "not greater than" (assert-false (> 1 2)))
|
|
(deftest "less equal" (assert-true (<= 1 1)))
|
|
(deftest "less equal less" (assert-true (<= 1 2)))
|
|
(deftest "greater equal" (assert-true (>= 2 2)))
|
|
(deftest "greater equal greater" (assert-true (>= 3 2)))
|
|
(deftest "not" (assert-true (not false)))
|
|
(deftest "not true" (assert-false (not true)))
|
|
(deftest "not nil" (assert-true (not nil))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Predicates
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"predicates"
|
|
(deftest "nil? nil" (assert-true (nil? nil)))
|
|
(deftest "nil? number" (assert-false (nil? 0)))
|
|
(deftest "nil? string" (assert-false (nil? "")))
|
|
(deftest "number? num" (assert-true (number? 42)))
|
|
(deftest "number? string" (assert-false (number? "42")))
|
|
(deftest "number? bool" (assert-false (number? true)))
|
|
(deftest "string? str" (assert-true (string? "hi")))
|
|
(deftest "string? num" (assert-false (string? 42)))
|
|
(deftest "list? list" (assert-true (list? (list 1 2))))
|
|
(deftest "list? empty" (assert-true (list? (list))))
|
|
(deftest "list? string" (assert-false (list? "hi")))
|
|
(deftest "dict? dict" (assert-true (dict? (dict "a" 1))))
|
|
(deftest "dict? list" (assert-false (dict? (list 1))))
|
|
(deftest "empty? empty list" (assert-true (empty? (list))))
|
|
(deftest "empty? nonempty" (assert-false (empty? (list 1))))
|
|
(deftest "empty? empty string" (assert-true (empty? "")))
|
|
(deftest "empty? nonempty string" (assert-false (empty? "a")))
|
|
(deftest "empty? nil" (assert-true (empty? nil))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; String operations
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"strings"
|
|
(deftest
|
|
"str concat"
|
|
(assert-equal "hello world" (str "hello" " " "world")))
|
|
(deftest "str number" (assert-equal "42" (str 42)))
|
|
(deftest "str empty" (assert-equal "" (str)))
|
|
(deftest "len string" (assert-equal 5 (len "hello")))
|
|
(deftest "len empty" (assert-equal 0 (len "")))
|
|
(deftest
|
|
"slice"
|
|
(assert-equal "ell" (slice "hello" 1 4)))
|
|
(deftest "slice from" (assert-equal "llo" (slice "hello" 2)))
|
|
(deftest
|
|
"slice empty"
|
|
(assert-equal "" (slice "hello" 2 2)))
|
|
(deftest "join" (assert-equal "a,b,c" (join "," (list "a" "b" "c"))))
|
|
(deftest "join empty" (assert-equal "" (join "," (list))))
|
|
(deftest "join single" (assert-equal "a" (join "," (list "a"))))
|
|
(deftest "split" (assert-equal (list "a" "b" "c") (split "a,b,c" ",")))
|
|
(deftest "upper" (assert-equal "HELLO" (upper "hello")))
|
|
(deftest "lower" (assert-equal "hello" (lower "HELLO")))
|
|
(deftest "trim" (assert-equal "hi" (trim " hi ")))
|
|
(deftest "contains?" (assert-true (contains? "hello world" "world")))
|
|
(deftest "contains? false" (assert-false (contains? "hello" "xyz")))
|
|
(deftest "starts-with?" (assert-true (starts-with? "hello" "hel")))
|
|
(deftest "starts-with? false" (assert-false (starts-with? "hello" "xyz")))
|
|
(deftest "ends-with?" (assert-true (ends-with? "hello" "llo")))
|
|
(deftest "ends-with? false" (assert-false (ends-with? "hello" "xyz")))
|
|
(deftest "replace" (assert-equal "hXllo" (replace "hello" "e" "X")))
|
|
(deftest "string-length" (assert-equal 5 (string-length "hello")))
|
|
(deftest "index-of found" (assert-equal 2 (index-of "hello" "l")))
|
|
(deftest
|
|
"index-of not found"
|
|
(assert-equal -1 (index-of "hello" "z"))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; List operations
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"lists"
|
|
(deftest
|
|
"list create"
|
|
(assert-equal
|
|
(list 1 2 3)
|
|
(list 1 2 3)))
|
|
(deftest
|
|
"first"
|
|
(assert-equal 1 (first (list 1 2 3))))
|
|
(deftest "first empty" (assert-nil (first (list))))
|
|
(deftest
|
|
"rest"
|
|
(assert-equal
|
|
(list 2 3)
|
|
(rest (list 1 2 3))))
|
|
(deftest "rest single" (assert-equal (list) (rest (list 1))))
|
|
(deftest "rest empty" (assert-equal (list) (rest (list))))
|
|
(deftest
|
|
"nth"
|
|
(assert-equal
|
|
2
|
|
(nth (list 1 2 3) 1)))
|
|
(deftest
|
|
"nth out of bounds"
|
|
(assert-nil (nth (list 1 2) 5)))
|
|
(deftest
|
|
"last"
|
|
(assert-equal 3 (last (list 1 2 3))))
|
|
(deftest "last single" (assert-equal 1 (last (list 1))))
|
|
(deftest
|
|
"len list"
|
|
(assert-equal 3 (len (list 1 2 3))))
|
|
(deftest "len empty" (assert-equal 0 (len (list))))
|
|
(deftest
|
|
"cons"
|
|
(assert-equal
|
|
(list 0 1 2)
|
|
(cons 0 (list 1 2))))
|
|
(deftest
|
|
"append"
|
|
(assert-equal
|
|
(list 1 2 3 4)
|
|
(append (list 1 2) (list 3 4))))
|
|
(deftest
|
|
"append element"
|
|
(assert-equal
|
|
(list 1 2 3)
|
|
(append (list 1 2) (list 3))))
|
|
(deftest
|
|
"slice list"
|
|
(assert-equal
|
|
(list 2 3)
|
|
(slice
|
|
(list 1 2 3 4)
|
|
1
|
|
3)))
|
|
(deftest
|
|
"concat"
|
|
(assert-equal
|
|
(list 1 2 3 4)
|
|
(concat (list 1 2) (list 3 4))))
|
|
(deftest
|
|
"reverse"
|
|
(assert-equal
|
|
(list 3 2 1)
|
|
(reverse (list 1 2 3))))
|
|
(deftest "reverse empty" (assert-equal (list) (reverse (list))))
|
|
(deftest
|
|
"contains? list"
|
|
(assert-true
|
|
(contains? (list 1 2 3) 2)))
|
|
(deftest
|
|
"contains? list false"
|
|
(assert-false
|
|
(contains? (list 1 2 3) 5)))
|
|
(deftest
|
|
"range"
|
|
(assert-equal
|
|
(list 0 1 2)
|
|
(range 0 3)))
|
|
(deftest
|
|
"range step"
|
|
(assert-equal
|
|
(list 0 2 4)
|
|
(range 0 6 2)))
|
|
(deftest
|
|
"flatten"
|
|
(assert-equal
|
|
(list 1 2 3 4)
|
|
(flatten
|
|
(list (list 1 2) (list 3 4))))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Dict operations
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"dicts"
|
|
(deftest
|
|
"dict create"
|
|
(assert-equal 1 (get (dict "a" 1 "b" 2) "a")))
|
|
(deftest "get missing" (assert-nil (get (dict "a" 1) "z")))
|
|
(deftest
|
|
"get default"
|
|
(assert-equal 99 (get (dict "a" 1) "z" 99)))
|
|
(deftest
|
|
"keys"
|
|
(assert-true
|
|
(contains? (keys (dict "a" 1 "b" 2)) "a")))
|
|
(deftest "has-key?" (assert-true (has-key? (dict "a" 1) "a")))
|
|
(deftest
|
|
"has-key? false"
|
|
(assert-false (has-key? (dict "a" 1) "z")))
|
|
(deftest
|
|
"assoc"
|
|
(assert-equal
|
|
2
|
|
(get (assoc (dict "a" 1) "b" 2) "b")))
|
|
(deftest
|
|
"dissoc"
|
|
(assert-false
|
|
(has-key? (dissoc (dict "a" 1 "b" 2) "a") "a")))
|
|
(deftest
|
|
"len dict"
|
|
(assert-equal 2 (len (dict "a" 1 "b" 2))))
|
|
(deftest "len empty dict" (assert-equal 0 (len (dict))))
|
|
(deftest "empty? dict" (assert-true (empty? (dict))))
|
|
(deftest
|
|
"empty? nonempty dict"
|
|
(assert-false (empty? (dict "a" 1)))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Higher-order functions
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"higher-order"
|
|
(deftest
|
|
"map"
|
|
(assert-equal
|
|
(list 2 4 6)
|
|
(map
|
|
(fn (x) (* x 2))
|
|
(list 1 2 3))))
|
|
(deftest "map empty" (assert-equal (list) (map (fn (x) x) (list))))
|
|
(deftest
|
|
"filter"
|
|
(assert-equal
|
|
(list 2 4)
|
|
(filter
|
|
(fn (x) (= (mod x 2) 0))
|
|
(list 1 2 3 4 5))))
|
|
(deftest
|
|
"filter none"
|
|
(assert-equal
|
|
(list)
|
|
(filter (fn (x) false) (list 1 2 3))))
|
|
(deftest
|
|
"reduce"
|
|
(assert-equal
|
|
10
|
|
(reduce
|
|
(fn (acc x) (+ acc x))
|
|
0
|
|
(list 1 2 3 4))))
|
|
(deftest
|
|
"reduce empty"
|
|
(assert-equal
|
|
0
|
|
(reduce (fn (acc x) (+ acc x)) 0 (list))))
|
|
(deftest
|
|
"some true"
|
|
(assert-true
|
|
(some
|
|
(fn (x) (> x 3))
|
|
(list 1 2 3 4 5))))
|
|
(deftest
|
|
"some false"
|
|
(assert-false
|
|
(some
|
|
(fn (x) (> x 10))
|
|
(list 1 2 3))))
|
|
(deftest "some empty" (assert-false (some (fn (x) true) (list))))
|
|
(deftest
|
|
"every? true"
|
|
(assert-true
|
|
(every?
|
|
(fn (x) (> x 0))
|
|
(list 1 2 3))))
|
|
(deftest
|
|
"every? false"
|
|
(assert-false
|
|
(every?
|
|
(fn (x) (> x 2))
|
|
(list 1 2 3))))
|
|
(deftest "every? empty" (assert-true (every? (fn (x) false) (list))))
|
|
(deftest
|
|
"for-each returns nil"
|
|
(let
|
|
((log (list)))
|
|
(for-each
|
|
(fn (x) (append! log x))
|
|
(list 1 2 3))
|
|
(assert-equal (list 1 2 3) log)))
|
|
(deftest
|
|
"map-indexed"
|
|
(assert-equal
|
|
(list (list 0 "a") (list 1 "b"))
|
|
(map-indexed (fn (i x) (list i x)) (list "a" "b")))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Type coercion
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"type-coercion"
|
|
(deftest
|
|
"str bool"
|
|
(assert-true (or (= (str true) "true") (= (str true) "True"))))
|
|
(deftest "str nil" (assert-equal "" (str nil)))
|
|
(deftest
|
|
"str list"
|
|
(assert-true
|
|
(not (empty? (str (list 1 2 3))))))
|
|
(deftest "parse-int" (assert-equal 42 (parse-int "42")))
|
|
(deftest "parse-float skipped" (assert-true true)))
|