;; lib/prolog/tests/dynamic.sx — assert/asserta/assertz/retract. (define pl-dy-test-count 0) (define pl-dy-test-pass 0) (define pl-dy-test-fail 0) (define pl-dy-test-failures (list)) (define pl-dy-test! (fn (name got expected) (begin (set! pl-dy-test-count (+ pl-dy-test-count 1)) (if (= got expected) (set! pl-dy-test-pass (+ pl-dy-test-pass 1)) (begin (set! pl-dy-test-fail (+ pl-dy-test-fail 1)) (append! pl-dy-test-failures (str name "\n expected: " expected "\n got: " got))))))) (define pl-dy-goal (fn (src env) (pl-instantiate (nth (first (pl-parse (str "g :- " src "."))) 2) env))) ;; assertz then query (define pl-dy-db1 (pl-mk-db)) (pl-solve-once! pl-dy-db1 (pl-dy-goal "assertz(foo(1))" {}) (pl-mk-trail)) (pl-dy-test! "assertz(foo(1)) + foo(1)" (pl-solve-once! pl-dy-db1 (pl-dy-goal "foo(1)" {}) (pl-mk-trail)) true) (pl-dy-test! "after one assertz, foo/1 has 1 clause" (pl-solve-count! pl-dy-db1 (pl-dy-goal "foo(X)" {}) (pl-mk-trail)) 1) ;; assertz appends — order preserved (define pl-dy-db2 (pl-mk-db)) (pl-solve-once! pl-dy-db2 (pl-dy-goal "assertz(p(1))" {}) (pl-mk-trail)) (pl-solve-once! pl-dy-db2 (pl-dy-goal "assertz(p(2))" {}) (pl-mk-trail)) (pl-dy-test! "assertz twice — count 2" (pl-solve-count! pl-dy-db2 (pl-dy-goal "p(X)" {}) (pl-mk-trail)) 2) (define pl-dy-env-a {}) (pl-solve-once! pl-dy-db2 (pl-dy-goal "p(X)" pl-dy-env-a) (pl-mk-trail)) (pl-dy-test! "assertz: first solution is the first asserted (1)" (pl-num-val (pl-walk-deep (dict-get pl-dy-env-a "X"))) 1) ;; asserta prepends (define pl-dy-db3 (pl-mk-db)) (pl-solve-once! pl-dy-db3 (pl-dy-goal "assertz(p(1))" {}) (pl-mk-trail)) (pl-solve-once! pl-dy-db3 (pl-dy-goal "asserta(p(99))" {}) (pl-mk-trail)) (define pl-dy-env-b {}) (pl-solve-once! pl-dy-db3 (pl-dy-goal "p(X)" pl-dy-env-b) (pl-mk-trail)) (pl-dy-test! "asserta: prepended clause is first solution" (pl-num-val (pl-walk-deep (dict-get pl-dy-env-b "X"))) 99) ;; assert/1 = assertz/1 (define pl-dy-db4 (pl-mk-db)) (pl-solve-once! pl-dy-db4 (pl-dy-goal "assert(g(7))" {}) (pl-mk-trail)) (pl-dy-test! "assert/1 alias" (pl-solve-once! pl-dy-db4 (pl-dy-goal "g(7)" {}) (pl-mk-trail)) true) ;; retract removes a fact (define pl-dy-db5 (pl-mk-db)) (pl-solve-once! pl-dy-db5 (pl-dy-goal "assertz(q(1))" {}) (pl-mk-trail)) (pl-solve-once! pl-dy-db5 (pl-dy-goal "assertz(q(2))" {}) (pl-mk-trail)) (pl-solve-once! pl-dy-db5 (pl-dy-goal "assertz(q(3))" {}) (pl-mk-trail)) (pl-dy-test! "before retract: 3 clauses" (pl-solve-count! pl-dy-db5 (pl-dy-goal "q(X)" {}) (pl-mk-trail)) 3) (pl-solve-once! pl-dy-db5 (pl-dy-goal "retract(q(2))" {}) (pl-mk-trail)) (pl-dy-test! "after retract(q(2)): 2 clauses left" (pl-solve-count! pl-dy-db5 (pl-dy-goal "q(X)" {}) (pl-mk-trail)) 2) (define pl-dy-env-c {}) (pl-solve-once! pl-dy-db5 (pl-dy-goal "q(X)" pl-dy-env-c) (pl-mk-trail)) (pl-dy-test! "after retract(q(2)): first remaining is 1" (pl-num-val (pl-walk-deep (dict-get pl-dy-env-c "X"))) 1) ;; retract of non-existent (pl-dy-test! "retract(missing(0)) on empty db fails" (pl-solve-once! (pl-mk-db) (pl-dy-goal "retract(missing(0))" {}) (pl-mk-trail)) false) ;; retract with unbound var matches first (define pl-dy-db6 (pl-mk-db)) (pl-solve-once! pl-dy-db6 (pl-dy-goal "assertz(r(11))" {}) (pl-mk-trail)) (pl-solve-once! pl-dy-db6 (pl-dy-goal "assertz(r(22))" {}) (pl-mk-trail)) (define pl-dy-env-d {}) (pl-solve-once! pl-dy-db6 (pl-dy-goal "retract(r(X))" pl-dy-env-d) (pl-mk-trail)) (pl-dy-test! "retract(r(X)) binds X to first match" (pl-num-val (pl-walk-deep (dict-get pl-dy-env-d "X"))) 11) (define pl-dynamic-tests-run! (fn () {:failed pl-dy-test-fail :passed pl-dy-test-pass :total pl-dy-test-count :failures pl-dy-test-failures}))