prolog: copy_term/2 + functor/3 + arg/3, 14 tests; =.. deferred
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled

This commit is contained in:
2026-04-25 08:39:32 +00:00
parent 76ee8cc39b
commit c6f58116bf
6 changed files with 261 additions and 7 deletions

View File

@@ -23,6 +23,7 @@ SUITES=(
"operators:lib/prolog/tests/operators.sx:pl-operators-tests-run!"
"dynamic:lib/prolog/tests/dynamic.sx:pl-dynamic-tests-run!"
"findall:lib/prolog/tests/findall.sx:pl-findall-tests-run!"
"term_inspect:lib/prolog/tests/term_inspect.sx:pl-term-inspect-tests-run!"
"append:lib/prolog/tests/programs/append.sx:pl-append-tests-run!"
"reverse:lib/prolog/tests/programs/reverse.sx:pl-reverse-tests-run!"
"member:lib/prolog/tests/programs/member.sx:pl-member-tests-run!"

View File

@@ -470,6 +470,90 @@
((rl (pl-mk-list-term sorted (pl-nil-term))))
(pl-solve-eq! third-rt rl trail k))))))))
(define
pl-solve-eq2!
(fn
(a1 b1 a2 b2 trail k)
(let
((mark (pl-trail-mark trail)))
(cond
((and (pl-unify! a1 b1 trail) (pl-unify! a2 b2 trail))
(let
((r (k)))
(cond
(r true)
(true (begin (pl-trail-undo-to! trail mark) false)))))
(true (begin (pl-trail-undo-to! trail mark) false))))))
(define
pl-make-fresh-args
(fn
(n)
(cond
((<= n 0) (list))
(true (cons (pl-mk-rt-var "_") (pl-make-fresh-args (- n 1)))))))
(define
pl-solve-functor-construct!
(fn
(term-rt name-rt arity-rt trail k)
(let
((wn (pl-walk name-rt)) (wa (pl-walk arity-rt)))
(cond
((and (pl-num? wa) (= (pl-num-val wa) 0))
(cond
((or (pl-atom? wn) (pl-num? wn))
(pl-solve-eq! term-rt wn trail k))
(true false)))
((and (pl-num? wa) (> (pl-num-val wa) 0) (pl-atom? wn))
(let
((new-args (pl-make-fresh-args (pl-num-val wa))))
(pl-solve-eq!
term-rt
(list "compound" (pl-atom-name wn) new-args)
trail
k)))
(true false)))))
(define
pl-solve-functor!
(fn
(term-rt name-rt arity-rt trail k)
(let
((wt (pl-walk term-rt)))
(cond
((pl-var? wt)
(pl-solve-functor-construct! term-rt name-rt arity-rt trail k))
((pl-atom? wt)
(pl-solve-eq2! name-rt wt arity-rt (list "num" 0) trail k))
((pl-num? wt)
(pl-solve-eq2! name-rt wt arity-rt (list "num" 0) trail k))
((pl-compound? wt)
(pl-solve-eq2!
name-rt
(list "atom" (pl-fun wt))
arity-rt
(list "num" (len (pl-args wt)))
trail
k))
(true false)))))
(define
pl-solve-arg!
(fn
(n-rt term-rt arg-rt trail k)
(let
((wn (pl-walk n-rt)) (wt (pl-walk term-rt)))
(cond
((and (pl-num? wn) (pl-compound? wt))
(let
((idx (pl-num-val wn)) (args (pl-args wt)))
(cond
((and (>= idx 1) (<= idx (len args)))
(pl-solve-eq! arg-rt (nth args (- idx 1)) trail k))
(true false))))
(true false)))))
(define
pl-retract-try-each
(fn
@@ -622,6 +706,26 @@
(nth (pl-args g) 2)
trail
k))
((and (pl-compound? g) (= (pl-fun g) "copy_term") (= (len (pl-args g)) 2))
(pl-solve-eq!
(nth (pl-args g) 1)
(pl-deep-copy (first (pl-args g)) {})
trail
k))
((and (pl-compound? g) (= (pl-fun g) "functor") (= (len (pl-args g)) 3))
(pl-solve-functor!
(first (pl-args g))
(nth (pl-args g) 1)
(nth (pl-args g) 2)
trail
k))
((and (pl-compound? g) (= (pl-fun g) "arg") (= (len (pl-args g)) 3))
(pl-solve-arg!
(first (pl-args g))
(nth (pl-args g) 1)
(nth (pl-args g) 2)
trail
k))
(true (pl-solve-user! db g trail cut-box k))))))
(define

View File

@@ -1,7 +1,7 @@
{
"total_passed": 224,
"total_passed": 238,
"total_failed": 0,
"total": 224,
"suites": {"parse":{"passed":25,"total":25,"failed":0},"unify":{"passed":47,"total":47,"failed":0},"clausedb":{"passed":14,"total":14,"failed":0},"solve":{"passed":62,"total":62,"failed":0},"operators":{"passed":19,"total":19,"failed":0},"dynamic":{"passed":11,"total":11,"failed":0},"findall":{"passed":11,"total":11,"failed":0},"append":{"passed":6,"total":6,"failed":0},"reverse":{"passed":6,"total":6,"failed":0},"member":{"passed":7,"total":7,"failed":0},"nqueens":{"passed":6,"total":6,"failed":0},"family":{"passed":10,"total":10,"failed":0}},
"generated": "2026-04-25T08:06:14+00:00"
"total": 238,
"suites": {"parse":{"passed":25,"total":25,"failed":0},"unify":{"passed":47,"total":47,"failed":0},"clausedb":{"passed":14,"total":14,"failed":0},"solve":{"passed":62,"total":62,"failed":0},"operators":{"passed":19,"total":19,"failed":0},"dynamic":{"passed":11,"total":11,"failed":0},"findall":{"passed":11,"total":11,"failed":0},"term_inspect":{"passed":14,"total":14,"failed":0},"append":{"passed":6,"total":6,"failed":0},"reverse":{"passed":6,"total":6,"failed":0},"member":{"passed":7,"total":7,"failed":0},"nqueens":{"passed":6,"total":6,"failed":0},"family":{"passed":10,"total":10,"failed":0}},
"generated": "2026-04-25T08:39:07+00:00"
}

View File

@@ -1,7 +1,7 @@
# Prolog scoreboard
**224 / 224 passing** (0 failure(s)).
Generated 2026-04-25T08:06:14+00:00.
**238 / 238 passing** (0 failure(s)).
Generated 2026-04-25T08:39:07+00:00.
| Suite | Passed | Total | Status |
|-------|--------|-------|--------|
@@ -12,6 +12,7 @@ Generated 2026-04-25T08:06:14+00:00.
| operators | 19 | 19 | ok |
| dynamic | 11 | 11 | ok |
| findall | 11 | 11 | ok |
| term_inspect | 14 | 14 | ok |
| append | 6 | 6 | ok |
| reverse | 6 | 6 | ok |
| member | 7 | 7 | ok |

View File

@@ -0,0 +1,147 @@
;; lib/prolog/tests/term_inspect.sx — copy_term/2, functor/3, arg/3.
(define pl-tt-test-count 0)
(define pl-tt-test-pass 0)
(define pl-tt-test-fail 0)
(define pl-tt-test-failures (list))
(define
pl-tt-test!
(fn
(name got expected)
(begin
(set! pl-tt-test-count (+ pl-tt-test-count 1))
(if
(= got expected)
(set! pl-tt-test-pass (+ pl-tt-test-pass 1))
(begin
(set! pl-tt-test-fail (+ pl-tt-test-fail 1))
(append!
pl-tt-test-failures
(str name "\n expected: " expected "\n got: " got)))))))
(define
pl-tt-goal
(fn
(src env)
(pl-instantiate (nth (first (pl-parse (str "g :- " src "."))) 2) env)))
(define pl-tt-db (pl-mk-db))
;; ── copy_term/2 ──
(pl-tt-test!
"copy_term ground compound succeeds + copy = original"
(pl-solve-once!
pl-tt-db
(pl-tt-goal "copy_term(foo(a, b), X), X = foo(a, b)" {})
(pl-mk-trail))
true)
(pl-tt-test!
"copy_term preserves var aliasing in source"
(pl-solve-once!
pl-tt-db
(pl-tt-goal "copy_term(p(Y, Y), p(A, B)), A = 5, B = 5" {})
(pl-mk-trail))
true)
(pl-tt-test!
"copy_term distinct vars stay distinct"
(pl-solve-once!
pl-tt-db
(pl-tt-goal "copy_term(p(Y, Y), p(A, B)), A = 5, B = 6" {})
(pl-mk-trail))
false)
(define pl-tt-env-1 {})
(pl-solve-once!
pl-tt-db
(pl-tt-goal "copy_term(X, Y), Y = 5" pl-tt-env-1)
(pl-mk-trail))
(pl-tt-test!
"copy_term: binding the copy doesn't bind the source"
(pl-var-bound? (dict-get pl-tt-env-1 "X"))
false)
;; ── functor/3 ──
(define pl-tt-env-2 {})
(pl-solve-once!
pl-tt-db
(pl-tt-goal "functor(foo(a, b, c), F, N)" pl-tt-env-2)
(pl-mk-trail))
(pl-tt-test!
"functor of compound: F = foo"
(pl-atom-name (pl-walk-deep (dict-get pl-tt-env-2 "F")))
"foo")
(pl-tt-test!
"functor of compound: N = 3"
(pl-num-val (pl-walk-deep (dict-get pl-tt-env-2 "N")))
3)
(define pl-tt-env-3 {})
(pl-solve-once!
pl-tt-db
(pl-tt-goal "functor(hello, F, N)" pl-tt-env-3)
(pl-mk-trail))
(pl-tt-test!
"functor of atom: F = hello"
(pl-atom-name (pl-walk-deep (dict-get pl-tt-env-3 "F")))
"hello")
(pl-tt-test!
"functor of atom: N = 0"
(pl-num-val (pl-walk-deep (dict-get pl-tt-env-3 "N")))
0)
(pl-tt-test!
"functor construct compound: T unifies with foo(a, b)"
(pl-solve-once!
pl-tt-db
(pl-tt-goal "functor(T, foo, 2), T = foo(a, b)" {})
(pl-mk-trail))
true)
(pl-tt-test!
"functor construct atom: T = hello"
(pl-solve-once!
pl-tt-db
(pl-tt-goal "functor(T, hello, 0), T = hello" {})
(pl-mk-trail))
true)
;; ── arg/3 ──
(pl-tt-test!
"arg(1, foo(a, b, c), a)"
(pl-solve-once!
pl-tt-db
(pl-tt-goal "arg(1, foo(a, b, c), a)" {})
(pl-mk-trail))
true)
(pl-tt-test!
"arg(2, foo(a, b, c), X) → X = b"
(pl-solve-once!
pl-tt-db
(pl-tt-goal "arg(2, foo(a, b, c), X), X = b" {})
(pl-mk-trail))
true)
(pl-tt-test!
"arg out-of-range high fails"
(pl-solve-once!
pl-tt-db
(pl-tt-goal "arg(4, foo(a, b, c), X)" {})
(pl-mk-trail))
false)
(pl-tt-test!
"arg(0, ...) fails (1-indexed)"
(pl-solve-once!
pl-tt-db
(pl-tt-goal "arg(0, foo(a), X)" {})
(pl-mk-trail))
false)
(define pl-term-inspect-tests-run! (fn () {:failed pl-tt-test-fail :passed pl-tt-test-pass :total pl-tt-test-count :failures pl-tt-test-failures}))