Files
rose-ash/lib/prolog/tests/parse.sx
giles 99753580b4 Recover agent-loop progress: lua/prolog/forth/erlang/haskell phases 1-2
Salvaged from worktree-agent-* branches killed during sx-tree MCP outage:
- lua: tokenizer + parser + phase-2 transpile (~157 tests)
- prolog: tokenizer + parser + unification (72 tests, plan update lost to WIP)
- forth: phase-1 reader/interpreter + phase-2 colon/VARIABLE (134 tests)
- erlang: tokenizer + parser (114 tests)
- haskell: tokenizer + parse tests (43 tests)

Cherry-picked file contents only, not branch history, to avoid pulling in
unrelated ocaml-vm merge commits that were in those branches' bases.
2026-04-24 16:03:00 +00:00

216 lines
5.1 KiB
Plaintext

;; lib/prolog/tests/parse.sx — parser unit tests
;;
;; Run: bash lib/prolog/tests/run-parse.sh
;; Or via sx-server: (load "lib/prolog/tokenizer.sx") (load "lib/prolog/parser.sx")
;; (load "lib/prolog/tests/parse.sx") (pl-parse-tests-run!)
(define pl-test-count 0)
(define pl-test-pass 0)
(define pl-test-fail 0)
(define pl-test-failures (list))
(define
pl-test!
(fn
(name got expected)
(do
(set! pl-test-count (+ pl-test-count 1))
(if
(= got expected)
(set! pl-test-pass (+ pl-test-pass 1))
(do
(set! pl-test-fail (+ pl-test-fail 1))
(append!
pl-test-failures
(str name "\n expected: " expected "\n got: " got)))))))
;; Atoms & variables
(pl-test!
"atom fact"
(pl-parse "foo.")
(list (list "clause" (list "atom" "foo") (list "atom" "true"))))
(pl-test! "number literal" (pl-parse-goal "42") (list "num" 42))
(pl-test!
"negative number — not supported yet (parsed as op atom + num)"
(pl-parse-goal "-5")
(list "atom" "-"))
(pl-test! "variable" (pl-parse-goal "X") (list "var" "X"))
(pl-test!
"underscore variable"
(pl-parse-goal "_Ignored")
(list "var" "_Ignored"))
(pl-test! "anonymous variable" (pl-parse-goal "_") (list "var" "_"))
(pl-test!
"compound 1-arg"
(pl-parse-goal "foo(a)")
(list "compound" "foo" (list (list "atom" "a"))))
(pl-test!
"compound 3-args mixed"
(pl-parse-goal "p(X, 1, hello)")
(list
"compound"
"p"
(list (list "var" "X") (list "num" 1) (list "atom" "hello"))))
(pl-test!
"nested compound"
(pl-parse-goal "f(g(X), h(Y, Z))")
(list
"compound"
"f"
(list
(list "compound" "g" (list (list "var" "X")))
(list "compound" "h" (list (list "var" "Y") (list "var" "Z"))))))
;; Lists
(pl-test! "empty list" (pl-parse-goal "[]") (list "atom" "[]"))
(pl-test!
"single-element list"
(pl-parse-goal "[a]")
(list "compound" "." (list (list "atom" "a") (list "atom" "[]"))))
(pl-test!
"three-element list"
(pl-parse-goal "[1, 2, 3]")
(list
"compound"
"."
(list
(list "num" 1)
(list
"compound"
"."
(list
(list "num" 2)
(list "compound" "." (list (list "num" 3) (list "atom" "[]"))))))))
(pl-test!
"head-tail list"
(pl-parse-goal "[H|T]")
(list "compound" "." (list (list "var" "H") (list "var" "T"))))
(pl-test!
"two-head-tail list"
(pl-parse-goal "[A, B|T]")
(list
"compound"
"."
(list
(list "var" "A")
(list "compound" "." (list (list "var" "B") (list "var" "T"))))))
;; Clauses
(pl-test!
"fact"
(pl-parse "parent(tom, bob).")
(list
(list
"clause"
(list
"compound"
"parent"
(list (list "atom" "tom") (list "atom" "bob")))
(list "atom" "true"))))
(pl-test!
"rule with single-goal body"
(pl-parse "q(X) :- p(X).")
(list
(list
"clause"
(list "compound" "q" (list (list "var" "X")))
(list "compound" "p" (list (list "var" "X"))))))
(pl-test!
"rule with conjunctive body"
(pl-parse "r(X, Y) :- p(X), q(Y).")
(list
(list
"clause"
(list "compound" "r" (list (list "var" "X") (list "var" "Y")))
(list
"compound"
","
(list
(list "compound" "p" (list (list "var" "X")))
(list "compound" "q" (list (list "var" "Y"))))))))
;; Cut in body
(pl-test!
"cut in body"
(pl-parse "foo(X) :- p(X), !, q(X).")
(list
(list
"clause"
(list "compound" "foo" (list (list "var" "X")))
(list
"compound"
","
(list
(list "compound" "p" (list (list "var" "X")))
(list
"compound"
","
(list
(list "cut")
(list "compound" "q" (list (list "var" "X"))))))))))
;; Symbolic-atom compound terms (phase 1 form)
(pl-test!
"= as compound"
(pl-parse-goal "=(X, 5)")
(list "compound" "=" (list (list "var" "X") (list "num" 5))))
(pl-test!
"is with +"
(pl-parse-goal "is(Y, +(X, 1))")
(list
"compound"
"is"
(list
(list "var" "Y")
(list "compound" "+" (list (list "var" "X") (list "num" 1))))))
;; Strings
(pl-test!
"double-quoted string"
(pl-parse-goal "\"hello\"")
(list "str" "hello"))
;; Single-quoted atom
(pl-test!
"quoted atom"
(pl-parse-goal "'Hello World'")
(list "atom" "Hello World"))
;; Multi-clause program
(pl-test!
"append program"
(len
(pl-parse "append([], L, L).\nappend([H|T], L, [H|R]) :- append(T, L, R).\n"))
2)
;; Comments
(pl-test!
"line comment ignored"
(pl-parse "foo.\n% this is a comment\nbar.")
(list
(list "clause" (list "atom" "foo") (list "atom" "true"))
(list "clause" (list "atom" "bar") (list "atom" "true"))))
(pl-test!
"block comment ignored"
(pl-parse "/* hello */\nfoo.")
(list (list "clause" (list "atom" "foo") (list "atom" "true"))))
;; ── Runner ───────────────────────────────────────────────────────
(define pl-parse-tests-run! (fn () {:failed pl-test-fail :passed pl-test-pass :total pl-test-count :failures pl-test-failures}))