;; ========================================================================== ;; test-parser.sx — Tests for the SX parser and serializer ;; ;; Requires: test-framework.sx loaded first. ;; Modules tested: parser.sx ;; ;; Platform functions required (beyond test framework): ;; sx-parse (source) -> list of AST expressions ;; sx-serialize (expr) -> SX source string ;; make-symbol (name) -> Symbol value ;; make-keyword (name) -> Keyword value ;; symbol-name (sym) -> string ;; keyword-name (kw) -> string ;; ========================================================================== ;; -------------------------------------------------------------------------- ;; Literal parsing ;; -------------------------------------------------------------------------- (defsuite "parser-literals" (deftest "parse integers" (assert-equal (list 42) (sx-parse "42")) (assert-equal (list 0) (sx-parse "0")) (assert-equal (list -7) (sx-parse "-7"))) (deftest "parse floats" (assert-equal (list 3.14) (sx-parse "3.14")) (assert-equal (list -0.5) (sx-parse "-0.5"))) (deftest "parse strings" (assert-equal (list "hello") (sx-parse "\"hello\"")) (assert-equal (list "") (sx-parse "\"\""))) (deftest "parse escape: newline" (assert-equal (list "a\nb") (sx-parse "\"a\\nb\""))) (deftest "parse escape: tab" (assert-equal (list "a\tb") (sx-parse "\"a\\tb\""))) (deftest "parse escape: quote" (assert-equal (list "a\"b") (sx-parse "\"a\\\"b\""))) (deftest "parse booleans" (assert-equal (list true) (sx-parse "true")) (assert-equal (list false) (sx-parse "false"))) (deftest "parse nil" (assert-equal (list nil) (sx-parse "nil"))) (deftest "parse keywords" (let ((result (sx-parse ":hello"))) (assert-length 1 result) (assert-equal "hello" (keyword-name (first result))))) (deftest "parse symbols" (let ((result (sx-parse "foo"))) (assert-length 1 result) (assert-equal "foo" (symbol-name (first result)))))) ;; -------------------------------------------------------------------------- ;; Composite parsing ;; -------------------------------------------------------------------------- (defsuite "parser-lists" (deftest "parse empty list" (let ((result (sx-parse "()"))) (assert-length 1 result) (assert-equal (list) (first result)))) (deftest "parse list of numbers" (let ((result (sx-parse "(1 2 3)"))) (assert-length 1 result) (assert-equal (list 1 2 3) (first result)))) (deftest "parse nested lists" (let ((result (sx-parse "(1 (2 3) 4)"))) (assert-length 1 result) (assert-equal (list 1 (list 2 3) 4) (first result)))) (deftest "parse square brackets as list" (let ((result (sx-parse "[1 2 3]"))) (assert-length 1 result) (assert-equal (list 1 2 3) (first result)))) (deftest "parse mixed types" (let ((result (sx-parse "(42 \"hello\" true nil)"))) (assert-length 1 result) (let ((lst (first result))) (assert-equal 42 (nth lst 0)) (assert-equal "hello" (nth lst 1)) (assert-equal true (nth lst 2)) (assert-nil (nth lst 3)))))) ;; -------------------------------------------------------------------------- ;; Dict parsing ;; -------------------------------------------------------------------------- (defsuite "parser-dicts" (deftest "parse empty dict" (let ((result (sx-parse "{}"))) (assert-length 1 result) (assert-type "dict" (first result)))) (deftest "parse dict with keyword keys" (let ((result (sx-parse "{:a 1 :b 2}"))) (assert-length 1 result) (let ((d (first result))) (assert-type "dict" d) (assert-equal 1 (get d "a")) (assert-equal 2 (get d "b"))))) (deftest "parse dict with string values" (let ((result (sx-parse "{:name \"alice\"}"))) (assert-length 1 result) (assert-equal "alice" (get (first result) "name"))))) ;; -------------------------------------------------------------------------- ;; Comments and whitespace ;; -------------------------------------------------------------------------- (defsuite "parser-whitespace" (deftest "skip line comments" (assert-equal (list 42) (sx-parse ";; comment\n42")) (assert-equal (list 1 2) (sx-parse "1 ;; middle\n2"))) (deftest "skip whitespace" (assert-equal (list 42) (sx-parse " 42 ")) (assert-equal (list 1 2) (sx-parse " 1 \n\t 2 "))) (deftest "parse multiple top-level expressions" (assert-length 3 (sx-parse "1 2 3")) (assert-equal (list 1 2 3) (sx-parse "1 2 3"))) (deftest "empty input" (assert-equal (list) (sx-parse ""))) (deftest "only comments" (assert-equal (list) (sx-parse ";; just a comment\n;; another")))) ;; -------------------------------------------------------------------------- ;; Quote sugar ;; -------------------------------------------------------------------------- (defsuite "parser-quote-sugar" (deftest "quasiquote" (let ((result (sx-parse "`foo"))) (assert-length 1 result) (let ((expr (first result))) (assert-type "list" expr) (assert-equal "quasiquote" (symbol-name (first expr)))))) (deftest "unquote" (let ((result (sx-parse ",foo"))) (assert-length 1 result) (let ((expr (first result))) (assert-type "list" expr) (assert-equal "unquote" (symbol-name (first expr)))))) (deftest "splice-unquote" (let ((result (sx-parse ",@foo"))) (assert-length 1 result) (let ((expr (first result))) (assert-type "list" expr) (assert-equal "splice-unquote" (symbol-name (first expr))))))) ;; -------------------------------------------------------------------------- ;; Serializer ;; -------------------------------------------------------------------------- (defsuite "serializer" (deftest "serialize number" (assert-equal "42" (sx-serialize 42))) (deftest "serialize string" (assert-equal "\"hello\"" (sx-serialize "hello"))) (deftest "serialize boolean" (assert-equal "true" (sx-serialize true)) (assert-equal "false" (sx-serialize false))) (deftest "serialize nil" (assert-equal "nil" (sx-serialize nil))) (deftest "serialize keyword" (assert-equal ":foo" (sx-serialize (make-keyword "foo")))) (deftest "serialize symbol" (assert-equal "bar" (sx-serialize (make-symbol "bar")))) (deftest "serialize list" (assert-equal "(1 2 3)" (sx-serialize (list 1 2 3)))) (deftest "serialize empty list" (assert-equal "()" (sx-serialize (list)))) (deftest "serialize nested" (assert-equal "(1 (2 3) 4)" (sx-serialize (list 1 (list 2 3) 4))))) ;; -------------------------------------------------------------------------- ;; Round-trip: parse then serialize ;; -------------------------------------------------------------------------- (defsuite "parser-roundtrip" (deftest "roundtrip number" (assert-equal "42" (sx-serialize (first (sx-parse "42"))))) (deftest "roundtrip string" (assert-equal "\"hello\"" (sx-serialize (first (sx-parse "\"hello\""))))) (deftest "roundtrip list" (assert-equal "(1 2 3)" (sx-serialize (first (sx-parse "(1 2 3)"))))) (deftest "roundtrip nested" (assert-equal "(a (b c))" (sx-serialize (first (sx-parse "(a (b c))"))))))