;; lib/prolog/tests/atoms.sx — type predicates + string/atom built-ins (define pl-at-test-count 0) (define pl-at-test-pass 0) (define pl-at-test-fail 0) (define pl-at-test-failures (list)) (define pl-at-test! (fn (name got expected) (begin (set! pl-at-test-count (+ pl-at-test-count 1)) (if (= got expected) (set! pl-at-test-pass (+ pl-at-test-pass 1)) (begin (set! pl-at-test-fail (+ pl-at-test-fail 1)) (append! pl-at-test-failures (str name "\n expected: " expected "\n got: " got))))))) (define pl-at-goal (fn (src env) (pl-instantiate (nth (first (pl-parse (str "g :- " src "."))) 2) env))) (define pl-at-db (pl-mk-db)) ;; ── var/1 + nonvar/1 ── (pl-at-test! "var(X) for unbound var" (pl-solve-once! pl-at-db (pl-at-goal "var(X)" {}) (pl-mk-trail)) true) (pl-at-test! "var(foo) fails" (pl-solve-once! pl-at-db (pl-at-goal "var(foo)" {}) (pl-mk-trail)) false) (pl-at-test! "nonvar(foo) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "nonvar(foo)" {}) (pl-mk-trail)) true) (pl-at-test! "nonvar(X) for unbound var fails" (pl-solve-once! pl-at-db (pl-at-goal "nonvar(X)" {}) (pl-mk-trail)) false) ;; ── atom/1 ── (pl-at-test! "atom(foo) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "atom(foo)" {}) (pl-mk-trail)) true) (pl-at-test! "atom([]) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "atom([])" {}) (pl-mk-trail)) true) (pl-at-test! "atom(42) fails" (pl-solve-once! pl-at-db (pl-at-goal "atom(42)" {}) (pl-mk-trail)) false) (pl-at-test! "atom(f(x)) fails" (pl-solve-once! pl-at-db (pl-at-goal "atom(f(x))" {}) (pl-mk-trail)) false) ;; ── number/1 + integer/1 ── (pl-at-test! "number(42) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "number(42)" {}) (pl-mk-trail)) true) (pl-at-test! "number(foo) fails" (pl-solve-once! pl-at-db (pl-at-goal "number(foo)" {}) (pl-mk-trail)) false) (pl-at-test! "integer(7) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "integer(7)" {}) (pl-mk-trail)) true) ;; ── compound/1 + callable/1 + atomic/1 ── (pl-at-test! "compound(f(x)) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "compound(f(x))" {}) (pl-mk-trail)) true) (pl-at-test! "compound(foo) fails" (pl-solve-once! pl-at-db (pl-at-goal "compound(foo)" {}) (pl-mk-trail)) false) (pl-at-test! "callable(foo) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "callable(foo)" {}) (pl-mk-trail)) true) (pl-at-test! "callable(f(x)) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "callable(f(x))" {}) (pl-mk-trail)) true) (pl-at-test! "callable(42) fails" (pl-solve-once! pl-at-db (pl-at-goal "callable(42)" {}) (pl-mk-trail)) false) (pl-at-test! "atomic(foo) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "atomic(foo)" {}) (pl-mk-trail)) true) (pl-at-test! "atomic(42) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "atomic(42)" {}) (pl-mk-trail)) true) (pl-at-test! "atomic(f(x)) fails" (pl-solve-once! pl-at-db (pl-at-goal "atomic(f(x))" {}) (pl-mk-trail)) false) ;; ── is_list/1 ── (pl-at-test! "is_list([]) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "is_list([])" {}) (pl-mk-trail)) true) (pl-at-test! "is_list([1,2,3]) succeeds" (pl-solve-once! pl-at-db (pl-at-goal "is_list([1,2,3])" {}) (pl-mk-trail)) true) (pl-at-test! "is_list(foo) fails" (pl-solve-once! pl-at-db (pl-at-goal "is_list(foo)" {}) (pl-mk-trail)) false) ;; ── atom_length/2 ── (define pl-at-env-al {}) (pl-solve-once! pl-at-db (pl-at-goal "atom_length(hello, N)" pl-at-env-al) (pl-mk-trail)) (pl-at-test! "atom_length(hello, N) -> N=5" (pl-num-val (pl-walk-deep (dict-get pl-at-env-al "N"))) 5) (pl-at-test! "atom_length empty atom" (pl-solve-once! pl-at-db (pl-at-goal "atom_length('', 0)" {}) (pl-mk-trail)) true) ;; ── atom_concat/3 ── (define pl-at-env-ac {}) (pl-solve-once! pl-at-db (pl-at-goal "atom_concat(foo, bar, X)" pl-at-env-ac) (pl-mk-trail)) (pl-at-test! "atom_concat(foo, bar, X) -> X=foobar" (pl-atom-name (pl-walk-deep (dict-get pl-at-env-ac "X"))) "foobar") (pl-at-test! "atom_concat(foo, bar, foobar) check" (pl-solve-once! pl-at-db (pl-at-goal "atom_concat(foo, bar, foobar)" {}) (pl-mk-trail)) true) (pl-at-test! "atom_concat(foo, bar, foobaz) fails" (pl-solve-once! pl-at-db (pl-at-goal "atom_concat(foo, bar, foobaz)" {}) (pl-mk-trail)) false) (define pl-at-env-ac2 {}) (pl-solve-once! pl-at-db (pl-at-goal "atom_concat(foo, Y, foobar)" pl-at-env-ac2) (pl-mk-trail)) (pl-at-test! "atom_concat(foo, Y, foobar) -> Y=bar" (pl-atom-name (pl-walk-deep (dict-get pl-at-env-ac2 "Y"))) "bar") ;; ── atom_chars/2 ── (define pl-at-env-ach {}) (pl-solve-once! pl-at-db (pl-at-goal "atom_chars(cat, Cs)" pl-at-env-ach) (pl-mk-trail)) (pl-at-test! "atom_chars(cat, Cs) -> Cs=[c,a,t]" (pl-solve-once! pl-at-db (pl-at-goal "atom_chars(cat, [c,a,t])" {}) (pl-mk-trail)) true) (define pl-at-env-ach2 {}) (pl-solve-once! pl-at-db (pl-at-goal "atom_chars(A, [h,i])" pl-at-env-ach2) (pl-mk-trail)) (pl-at-test! "atom_chars(A, [h,i]) -> A=hi" (pl-atom-name (pl-walk-deep (dict-get pl-at-env-ach2 "A"))) "hi") ;; ── char_code/2 ── (define pl-at-env-cc {}) (pl-solve-once! pl-at-db (pl-at-goal "char_code(a, N)" pl-at-env-cc) (pl-mk-trail)) (pl-at-test! "char_code(a, N) -> N=97" (pl-num-val (pl-walk-deep (dict-get pl-at-env-cc "N"))) 97) (define pl-at-env-cc2 {}) (pl-solve-once! pl-at-db (pl-at-goal "char_code(C, 65)" pl-at-env-cc2) (pl-mk-trail)) (pl-at-test! "char_code(C, 65) -> C='A'" (pl-atom-name (pl-walk-deep (dict-get pl-at-env-cc2 "C"))) "A") ;; ── number_codes/2 ── (pl-at-test! "number_codes(42, [52,50])" (pl-solve-once! pl-at-db (pl-at-goal "number_codes(42, [52,50])" {}) (pl-mk-trail)) true) ;; ── number_chars/2 ── (pl-at-test! "number_chars(42, ['4','2'])" (pl-solve-once! pl-at-db (pl-at-goal "number_chars(42, ['4','2'])" {}) (pl-mk-trail)) true) (define pl-atom-tests-run! (fn () {:failed pl-at-test-fail :passed pl-at-test-pass :total pl-at-test-count :failures pl-at-test-failures}))