Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 45s
Tokens → list of {:head :body} / {:query} clauses. SX symbols for
constants and variables (case-distinguished). not(literal) in body
desugars to {:neg literal}. Nested compounds permitted in arg
position for arithmetic; safety analysis (Phase 3) will gate them.
Conformance harness wraps lib/guest/conformance.sh; produces
lib/datalog/scoreboard.{json,md}.
148 lines
5.3 KiB
Plaintext
148 lines
5.3 KiB
Plaintext
;; lib/datalog/tests/parse.sx — parser unit tests
|
|
;;
|
|
;; Run via: bash lib/datalog/conformance.sh
|
|
;; Or: (load "lib/datalog/tokenizer.sx") (load "lib/datalog/parser.sx")
|
|
;; (load "lib/datalog/tests/parse.sx") (dl-parse-tests-run!)
|
|
|
|
(define dl-pt-pass 0)
|
|
(define dl-pt-fail 0)
|
|
(define dl-pt-failures (list))
|
|
|
|
;; Order-independent structural equality. Lists compared positionally,
|
|
;; dicts as sets of (key, value) pairs. Numbers via = (so 30.0 = 30).
|
|
(define
|
|
dl-deep-equal?
|
|
(fn
|
|
(a b)
|
|
(cond
|
|
((and (list? a) (list? b))
|
|
(and (= (len a) (len b)) (dl-deep-equal-list? a b 0)))
|
|
((and (dict? a) (dict? b))
|
|
(let
|
|
((ka (keys a)) (kb (keys b)))
|
|
(and
|
|
(= (len ka) (len kb))
|
|
(dl-deep-equal-dict? a b ka 0))))
|
|
((and (number? a) (number? b)) (= a b))
|
|
(else (equal? a b)))))
|
|
|
|
(define
|
|
dl-deep-equal-list?
|
|
(fn
|
|
(a b i)
|
|
(cond
|
|
((>= i (len a)) true)
|
|
((not (dl-deep-equal? (nth a i) (nth b i))) false)
|
|
(else (dl-deep-equal-list? a b (+ i 1))))))
|
|
|
|
(define
|
|
dl-deep-equal-dict?
|
|
(fn
|
|
(a b ka i)
|
|
(cond
|
|
((>= i (len ka)) true)
|
|
((let ((k (nth ka i))) (not (dl-deep-equal? (get a k) (get b k))))
|
|
false)
|
|
(else (dl-deep-equal-dict? a b ka (+ i 1))))))
|
|
|
|
(define
|
|
dl-pt-test!
|
|
(fn
|
|
(name got expected)
|
|
(if
|
|
(dl-deep-equal? got expected)
|
|
(set! dl-pt-pass (+ dl-pt-pass 1))
|
|
(do
|
|
(set! dl-pt-fail (+ dl-pt-fail 1))
|
|
(append!
|
|
dl-pt-failures
|
|
(str name "\n expected: " expected "\n got: " got))))))
|
|
|
|
(define
|
|
dl-pt-throws?
|
|
(fn
|
|
(thunk)
|
|
(let
|
|
((threw false))
|
|
(do (guard (e (#t (set! threw true))) (thunk)) threw))))
|
|
|
|
(define
|
|
dl-pt-run-all!
|
|
(fn
|
|
()
|
|
(do
|
|
(dl-pt-test! "empty program" (dl-parse "") (list))
|
|
(dl-pt-test! "fact" (dl-parse "parent(tom, bob).") (list {:body (list) :head (list (quote parent) (quote tom) (quote bob))}))
|
|
(dl-pt-test!
|
|
"two facts"
|
|
(dl-parse "parent(tom, bob). parent(bob, ann).")
|
|
(list {:body (list) :head (list (quote parent) (quote tom) (quote bob))} {:body (list) :head (list (quote parent) (quote bob) (quote ann))}))
|
|
(dl-pt-test! "zero-ary fact" (dl-parse "ready.") (list {:body (list) :head (list (quote ready))}))
|
|
(dl-pt-test!
|
|
"rule one body lit"
|
|
(dl-parse "ancestor(X, Y) :- parent(X, Y).")
|
|
(list {:body (list (list (quote parent) (quote X) (quote Y))) :head (list (quote ancestor) (quote X) (quote Y))}))
|
|
(dl-pt-test!
|
|
"recursive rule"
|
|
(dl-parse "ancestor(X, Z) :- parent(X, Y), ancestor(Y, Z).")
|
|
(list {:body (list (list (quote parent) (quote X) (quote Y)) (list (quote ancestor) (quote Y) (quote Z))) :head (list (quote ancestor) (quote X) (quote Z))}))
|
|
(dl-pt-test!
|
|
"query single"
|
|
(dl-parse "?- ancestor(tom, X).")
|
|
(list {:query (list (list (quote ancestor) (quote tom) (quote X)))}))
|
|
(dl-pt-test!
|
|
"query multi"
|
|
(dl-parse "?- p(X), q(X).")
|
|
(list {:query (list (list (quote p) (quote X)) (list (quote q) (quote X)))}))
|
|
(dl-pt-test!
|
|
"negation"
|
|
(dl-parse "safe(X) :- person(X), not(parent(X, _)).")
|
|
(list {:body (list (list (quote person) (quote X)) {:neg (list (quote parent) (quote X) (quote _))}) :head (list (quote safe) (quote X))}))
|
|
(dl-pt-test!
|
|
"number arg"
|
|
(dl-parse "age(alice, 30).")
|
|
(list {:body (list) :head (list (quote age) (quote alice) 30)}))
|
|
(dl-pt-test!
|
|
"string arg"
|
|
(dl-parse "label(x, \"hi\").")
|
|
(list {:body (list) :head (list (quote label) (quote x) "hi")}))
|
|
(dl-pt-test!
|
|
"comparison literal"
|
|
(dl-parse "p(X) :- <(X, 5).")
|
|
(list {:body (list (list (string->symbol "<") (quote X) 5)) :head (list (quote p) (quote X))}))
|
|
(dl-pt-test!
|
|
"is with arith"
|
|
(dl-parse "succ(X, Y) :- nat(X), is(Y, +(X, 1)).")
|
|
(list {:body (list (list (quote nat) (quote X)) (list (quote is) (quote Y) (list (string->symbol "+") (quote X) 1))) :head (list (quote succ) (quote X) (quote Y))}))
|
|
(dl-pt-test!
|
|
"mixed program"
|
|
(dl-parse "p(a). p(b). q(X) :- p(X). ?- q(Y).")
|
|
(list {:body (list) :head (list (quote p) (quote a))} {:body (list) :head (list (quote p) (quote b))} {:body (list (list (quote p) (quote X))) :head (list (quote q) (quote X))} {:query (list (list (quote q) (quote Y)))}))
|
|
(dl-pt-test!
|
|
"comments skipped"
|
|
(dl-parse "% comment\nfoo(a).\n/* block */ bar(b).")
|
|
(list {:body (list) :head (list (quote foo) (quote a))} {:body (list) :head (list (quote bar) (quote b))}))
|
|
(dl-pt-test!
|
|
"underscore var"
|
|
(dl-parse "p(X) :- q(X, _).")
|
|
(list {:body (list (list (quote q) (quote X) (quote _))) :head (list (quote p) (quote X))}))
|
|
(dl-pt-test!
|
|
"missing dot raises"
|
|
(dl-pt-throws? (fn () (dl-parse "p(a)")))
|
|
true)
|
|
(dl-pt-test!
|
|
"trailing comma raises"
|
|
(dl-pt-throws? (fn () (dl-parse "p(a,).")))
|
|
true))))
|
|
|
|
(define
|
|
dl-parse-tests-run!
|
|
(fn
|
|
()
|
|
(do
|
|
(set! dl-pt-pass 0)
|
|
(set! dl-pt-fail 0)
|
|
(set! dl-pt-failures (list))
|
|
(dl-pt-run-all!)
|
|
{:failures dl-pt-failures :total (+ dl-pt-pass dl-pt-fail) :passed dl-pt-pass :failed dl-pt-fail})))
|