Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
252 lines
5.2 KiB
Plaintext
252 lines
5.2 KiB
Plaintext
;; lib/prolog/tests/meta_predicates.sx — \+/1, not/1, once/1, ignore/1, ground/1, sort/2, msort/2, atom_number/2, number_string/2
|
|
|
|
(define pl-mp-test-count 0)
|
|
(define pl-mp-test-pass 0)
|
|
(define pl-mp-test-fail 0)
|
|
(define pl-mp-test-failures (list))
|
|
|
|
(define
|
|
pl-mp-test!
|
|
(fn
|
|
(name got expected)
|
|
(begin
|
|
(set! pl-mp-test-count (+ pl-mp-test-count 1))
|
|
(if
|
|
(= got expected)
|
|
(set! pl-mp-test-pass (+ pl-mp-test-pass 1))
|
|
(begin
|
|
(set! pl-mp-test-fail (+ pl-mp-test-fail 1))
|
|
(append!
|
|
pl-mp-test-failures
|
|
(str name "\n expected: " expected "\n got: " got)))))))
|
|
|
|
(define
|
|
pl-mp-goal
|
|
(fn
|
|
(src env)
|
|
(pl-instantiate (nth (first (pl-parse (str "g :- " src "."))) 2) env)))
|
|
|
|
(define pl-mp-db (pl-mk-db))
|
|
(pl-db-load!
|
|
pl-mp-db
|
|
(pl-parse "member(X, [X|_]). member(X, [_|T]) :- member(X, T)."))
|
|
|
|
;; -- \+/1 --
|
|
|
|
(pl-mp-test!
|
|
"\\+(fail) succeeds"
|
|
(pl-solve-once! pl-mp-db (pl-mp-goal "\\+(fail)" {}) (pl-mk-trail))
|
|
true)
|
|
|
|
(pl-mp-test!
|
|
"\\+(true) fails"
|
|
(pl-solve-once! pl-mp-db (pl-mp-goal "\\+(true)" {}) (pl-mk-trail))
|
|
false)
|
|
|
|
(pl-mp-test!
|
|
"\\+(member(d, [a,b,c])) succeeds"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "\\+(member(d, [a,b,c]))" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
(pl-mp-test!
|
|
"\\+(member(a, [a,b,c])) fails"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "\\+(member(a, [a,b,c]))" {})
|
|
(pl-mk-trail))
|
|
false)
|
|
|
|
(define pl-mp-env-neg {})
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "\\+(X = 5)" pl-mp-env-neg)
|
|
(pl-mk-trail))
|
|
(pl-mp-test!
|
|
"\\+(X=5) fails, X stays unbound (bindings undone)"
|
|
(nil? (pl-var-binding (dict-get pl-mp-env-neg "X")))
|
|
true)
|
|
|
|
;; -- not/1 --
|
|
|
|
(pl-mp-test!
|
|
"not(fail) succeeds"
|
|
(pl-solve-once! pl-mp-db (pl-mp-goal "not(fail)" {}) (pl-mk-trail))
|
|
true)
|
|
|
|
(pl-mp-test!
|
|
"not(true) fails"
|
|
(pl-solve-once! pl-mp-db (pl-mp-goal "not(true)" {}) (pl-mk-trail))
|
|
false)
|
|
|
|
;; -- once/1 --
|
|
|
|
(pl-mp-test!
|
|
"once(member(X,[1,2,3])) succeeds once"
|
|
(pl-solve-count!
|
|
pl-mp-db
|
|
(pl-mp-goal "once(member(X,[1,2,3]))" {})
|
|
(pl-mk-trail))
|
|
1)
|
|
|
|
(define pl-mp-env-once {})
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "once(member(X,[1,2,3]))" pl-mp-env-once)
|
|
(pl-mk-trail))
|
|
(pl-mp-test!
|
|
"once(member(X,[1,2,3])): X=1 (first solution)"
|
|
(pl-num-val (pl-walk-deep (dict-get pl-mp-env-once "X")))
|
|
1)
|
|
|
|
(pl-mp-test!
|
|
"once(fail) fails"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "once(fail)" {})
|
|
(pl-mk-trail))
|
|
false)
|
|
|
|
;; -- ignore/1 --
|
|
|
|
(pl-mp-test!
|
|
"ignore(true) succeeds"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "ignore(true)" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
(pl-mp-test!
|
|
"ignore(fail) still succeeds"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "ignore(fail)" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
;; -- ground/1 --
|
|
|
|
(pl-mp-test!
|
|
"ground(foo(1, a)) succeeds"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "ground(foo(1, a))" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
(pl-mp-test!
|
|
"ground(foo(X, a)) fails (X unbound)"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "ground(foo(X, a))" {})
|
|
(pl-mk-trail))
|
|
false)
|
|
|
|
(pl-mp-test!
|
|
"ground(42) succeeds"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "ground(42)" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
;; -- sort/2 --
|
|
|
|
(pl-mp-test!
|
|
"sort([b,a,c], [a,b,c])"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "sort([b,a,c], [a,b,c])" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
(pl-mp-test!
|
|
"sort([b,a,a,c], [a,b,c]) (removes duplicates)"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "sort([b,a,a,c], [a,b,c])" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
(pl-mp-test!
|
|
"sort([], [])"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "sort([], [])" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
;; -- msort/2 --
|
|
|
|
(pl-mp-test!
|
|
"msort([b,a,a,c], [a,a,b,c]) (keeps duplicates)"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "msort([b,a,a,c], [a,a,b,c])" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
(pl-mp-test!
|
|
"msort([3,1,2,1], [1,1,2,3])"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "msort([3,1,2,1], [1,1,2,3])" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
;; -- atom_number/2 --
|
|
|
|
(define pl-mp-env-an1 {})
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "atom_number('42', N)" pl-mp-env-an1)
|
|
(pl-mk-trail))
|
|
(pl-mp-test!
|
|
"atom_number('42', N) -> N=42"
|
|
(pl-num-val (pl-walk-deep (dict-get pl-mp-env-an1 "N")))
|
|
42)
|
|
|
|
(define pl-mp-env-an2 {})
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "atom_number(A, 7)" pl-mp-env-an2)
|
|
(pl-mk-trail))
|
|
(pl-mp-test!
|
|
"atom_number(A, 7) -> A='7'"
|
|
(pl-atom-name (pl-walk-deep (dict-get pl-mp-env-an2 "A")))
|
|
"7")
|
|
|
|
(pl-mp-test!
|
|
"atom_number(foo, N) fails (not a number)"
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "atom_number(foo, N)" {})
|
|
(pl-mk-trail))
|
|
false)
|
|
|
|
;; -- number_string/2 --
|
|
|
|
(define pl-mp-env-ns1 {})
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "number_string(42, S)" pl-mp-env-ns1)
|
|
(pl-mk-trail))
|
|
(pl-mp-test!
|
|
"number_string(42, S) -> S='42'"
|
|
(pl-atom-name (pl-walk-deep (dict-get pl-mp-env-ns1 "S")))
|
|
"42")
|
|
|
|
(define pl-mp-env-ns2 {})
|
|
(pl-solve-once!
|
|
pl-mp-db
|
|
(pl-mp-goal "number_string(N, '3.14')" pl-mp-env-ns2)
|
|
(pl-mk-trail))
|
|
(pl-mp-test!
|
|
"number_string(N, '3.14') -> N=3.14"
|
|
(pl-num-val (pl-walk-deep (dict-get pl-mp-env-ns2 "N")))
|
|
3.14)
|
|
|
|
(define pl-meta-predicates-tests-run! (fn () {:failed pl-mp-test-fail :passed pl-mp-test-pass :total pl-mp-test-count :failures pl-mp-test-failures})) |