;; lib/prolog/tests/findall.sx — findall/3, bagof/3, setof/3. (define pl-fb-test-count 0) (define pl-fb-test-pass 0) (define pl-fb-test-fail 0) (define pl-fb-test-failures (list)) (define pl-fb-test! (fn (name got expected) (begin (set! pl-fb-test-count (+ pl-fb-test-count 1)) (if (= got expected) (set! pl-fb-test-pass (+ pl-fb-test-pass 1)) (begin (set! pl-fb-test-fail (+ pl-fb-test-fail 1)) (append! pl-fb-test-failures (str name "\n expected: " expected "\n got: " got))))))) (define pl-fb-term-to-sx (fn (t) (cond ((pl-num? t) (pl-num-val t)) ((pl-atom? t) (pl-atom-name t)) (true (list :complex))))) (define pl-fb-list-walked (fn (w) (cond ((and (pl-atom? w) (= (pl-atom-name w) "[]")) (list)) ((and (pl-compound? w) (= (pl-fun w) ".") (= (len (pl-args w)) 2)) (cons (pl-fb-term-to-sx (first (pl-args w))) (pl-fb-list-walked (nth (pl-args w) 1)))) (true (list :not-list))))) (define pl-fb-list-to-sx (fn (t) (pl-fb-list-walked (pl-walk-deep t)))) (define pl-fb-goal (fn (src env) (pl-instantiate (nth (first (pl-parse (str "g :- " src "."))) 2) env))) (define pl-fb-prog-src "member(X, [X|_]). member(X, [_|T]) :- member(X, T).") (define pl-fb-db (pl-mk-db)) (pl-db-load! pl-fb-db (pl-parse pl-fb-prog-src)) ;; ── findall ── (define pl-fb-env-1 {}) (pl-solve-once! pl-fb-db (pl-fb-goal "findall(X, member(X, [a, b, c]), L)" pl-fb-env-1) (pl-mk-trail)) (pl-fb-test! "findall member [a, b, c]" (pl-fb-list-to-sx (dict-get pl-fb-env-1 "L")) (list "a" "b" "c")) (define pl-fb-env-2 {}) (pl-solve-once! pl-fb-db (pl-fb-goal "findall(X, (member(X, [1, 2, 3]), X >= 2), L)" pl-fb-env-2) (pl-mk-trail)) (pl-fb-test! "findall with comparison filter" (pl-fb-list-to-sx (dict-get pl-fb-env-2 "L")) (list 2 3)) (define pl-fb-env-3 {}) (pl-solve-once! pl-fb-db (pl-fb-goal "findall(X, fail, L)" pl-fb-env-3) (pl-mk-trail)) (pl-fb-test! "findall on fail succeeds with empty list" (pl-fb-list-to-sx (dict-get pl-fb-env-3 "L")) (list)) (pl-fb-test! "findall(X, fail, L) the goal succeeds" (pl-solve-once! pl-fb-db (pl-fb-goal "findall(X, fail, L)" {}) (pl-mk-trail)) true) (define pl-fb-env-4 {}) (pl-solve-once! pl-fb-db (pl-fb-goal "findall(p(X, Y), (member(X, [1, 2]), member(Y, [a, b])), L)" pl-fb-env-4) (pl-mk-trail)) (pl-fb-test! "findall over compound template — count = 4" (len (pl-fb-list-to-sx (dict-get pl-fb-env-4 "L"))) 4) ;; ── bagof ── (pl-fb-test! "bagof succeeds when results exist" (pl-solve-once! pl-fb-db (pl-fb-goal "bagof(X, member(X, [1, 2, 3]), L)" {}) (pl-mk-trail)) true) (pl-fb-test! "bagof fails on empty" (pl-solve-once! pl-fb-db (pl-fb-goal "bagof(X, fail, L)" {}) (pl-mk-trail)) false) (define pl-fb-env-5 {}) (pl-solve-once! pl-fb-db (pl-fb-goal "bagof(X, member(X, [c, a, b]), L)" pl-fb-env-5) (pl-mk-trail)) (pl-fb-test! "bagof preserves order" (pl-fb-list-to-sx (dict-get pl-fb-env-5 "L")) (list "c" "a" "b")) ;; ── setof ── (define pl-fb-env-6 {}) (pl-solve-once! pl-fb-db (pl-fb-goal "setof(X, member(X, [c, a, b, a, c]), L)" pl-fb-env-6) (pl-mk-trail)) (pl-fb-test! "setof sorts + dedupes atoms" (pl-fb-list-to-sx (dict-get pl-fb-env-6 "L")) (list "a" "b" "c")) (pl-fb-test! "setof fails on empty" (pl-solve-once! pl-fb-db (pl-fb-goal "setof(X, fail, L)" {}) (pl-mk-trail)) false) (define pl-fb-env-7 {}) (pl-solve-once! pl-fb-db (pl-fb-goal "setof(X, member(X, [3, 1, 2, 1, 3]), L)" pl-fb-env-7) (pl-mk-trail)) (pl-fb-test! "setof sorts + dedupes nums" (pl-fb-list-to-sx (dict-get pl-fb-env-7 "L")) (list 1 2 3)) (define pl-findall-tests-run! (fn () {:failed pl-fb-test-fail :passed pl-fb-test-pass :total pl-fb-test-count :failures pl-fb-test-failures}))