Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Adds 6 new built-in predicates to the Prolog runtime and 24 tests covering term<->atom conversion (bidirectional), output capture, format directives (~w/~a/~d/~n/~~). 456/456 tests passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
327 lines
8.5 KiB
Plaintext
327 lines
8.5 KiB
Plaintext
;; lib/prolog/tests/io_predicates.sx — term_to_atom/2, term_string/2,
|
|
;; with_output_to/2, writeln/1, format/1, format/2
|
|
|
|
(define pl-io-test-count 0)
|
|
(define pl-io-test-pass 0)
|
|
(define pl-io-test-fail 0)
|
|
(define pl-io-test-failures (list))
|
|
|
|
(define
|
|
pl-io-test!
|
|
(fn
|
|
(name got expected)
|
|
(begin
|
|
(set! pl-io-test-count (+ pl-io-test-count 1))
|
|
(if
|
|
(= got expected)
|
|
(set! pl-io-test-pass (+ pl-io-test-pass 1))
|
|
(begin
|
|
(set! pl-io-test-fail (+ pl-io-test-fail 1))
|
|
(append!
|
|
pl-io-test-failures
|
|
(str name "\n expected: " expected "\n got: " got)))))))
|
|
|
|
(define
|
|
pl-io-goal
|
|
(fn
|
|
(src env)
|
|
(pl-instantiate (nth (first (pl-parse (str "g :- " src "."))) 2) env)))
|
|
|
|
(define pl-io-db (pl-mk-db))
|
|
|
|
;; helper: get output buffer after running a goal
|
|
(define
|
|
pl-io-capture!
|
|
(fn
|
|
(goal)
|
|
(do
|
|
(pl-output-clear!)
|
|
(pl-solve-once! pl-io-db goal (pl-mk-trail))
|
|
pl-output-buffer)))
|
|
|
|
;; ─── term_to_atom/2 — bound Term direction ─────────────────────────────────
|
|
|
|
(pl-io-test!
|
|
"term_to_atom(foo(a,b), A) — compound"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "term_to_atom(foo(a,b), A)" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "A"))))
|
|
"foo(a, b)")
|
|
|
|
(pl-io-test!
|
|
"term_to_atom(hello, A) — atom"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "term_to_atom(hello, A)" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "A"))))
|
|
"hello")
|
|
|
|
(pl-io-test!
|
|
"term_to_atom(42, A) — number"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "term_to_atom(42, A)" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "A"))))
|
|
"42")
|
|
|
|
(pl-io-test!
|
|
"term_to_atom(foo(a,b), 'foo(a, b)') — succeeds when Atom matches"
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "term_to_atom(foo(a,b), 'foo(a, b)')" {})
|
|
(pl-mk-trail))
|
|
true)
|
|
|
|
(pl-io-test!
|
|
"term_to_atom(hello, world) — fails on mismatch"
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "term_to_atom(hello, world)" {})
|
|
(pl-mk-trail))
|
|
false)
|
|
|
|
;; ─── term_to_atom/2 — parse direction (Atom bound, Term unbound) ───────────
|
|
|
|
(pl-io-test!
|
|
"term_to_atom(T, 'foo(a)') — parse direction gives compound"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "term_to_atom(T, 'foo(a)')" env)
|
|
(pl-mk-trail))
|
|
(let
|
|
((t (pl-walk-deep (dict-get env "T"))))
|
|
(and (pl-compound? t) (= (pl-fun t) "foo"))))
|
|
true)
|
|
|
|
(pl-io-test!
|
|
"term_to_atom(T, hello) — parse direction gives atom"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "term_to_atom(T, hello)" env)
|
|
(pl-mk-trail))
|
|
(let
|
|
((t (pl-walk-deep (dict-get env "T"))))
|
|
(and (pl-atom? t) (= (pl-atom-name t) "hello"))))
|
|
true)
|
|
|
|
;; ─── term_string/2 — alias ──────────────────────────────────────────────────
|
|
|
|
(pl-io-test!
|
|
"term_string(bar(x), A) — same as term_to_atom"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "term_string(bar(x), A)" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "A"))))
|
|
"bar(x)")
|
|
|
|
(pl-io-test!
|
|
"term_string(42, A) — number to string"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "term_string(42, A)" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "A"))))
|
|
"42")
|
|
|
|
;; ─── writeln/1 ─────────────────────────────────────────────────────────────
|
|
|
|
(pl-io-test!
|
|
"writeln(hello) writes 'hello\n'"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), writeln(hello))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"hello
|
|
")
|
|
|
|
(pl-io-test!
|
|
"writeln(42) writes '42\n'"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), writeln(42))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"42
|
|
")
|
|
|
|
;; ─── with_output_to/2 ──────────────────────────────────────────────────────
|
|
|
|
(pl-io-test!
|
|
"with_output_to(atom(X), write(foo)) — captures write output"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), write(foo))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"foo")
|
|
|
|
(pl-io-test!
|
|
"with_output_to(atom(X), (write(a), write(b))) — concat output"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), (write(a), write(b)))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"ab")
|
|
|
|
(pl-io-test!
|
|
"with_output_to(atom(X), nl) — captures newline"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), nl)" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"
|
|
")
|
|
|
|
(pl-io-test!
|
|
"with_output_to(atom(X), true) — captures empty string"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), true)" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"")
|
|
|
|
(pl-io-test!
|
|
"with_output_to(string(X), write(hello)) — string sink works"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(string(X), write(hello))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"hello")
|
|
|
|
(pl-io-test!
|
|
"with_output_to(atom(X), fail) — fails when goal fails"
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), fail)" {})
|
|
(pl-mk-trail))
|
|
false)
|
|
|
|
;; ─── format/1 ──────────────────────────────────────────────────────────────
|
|
|
|
(pl-io-test!
|
|
"format('hello~n') — tilde-n becomes newline"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), format('hello~n'))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"hello
|
|
")
|
|
|
|
(pl-io-test!
|
|
"format('~~') — double tilde becomes single tilde"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), format('~~'))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"~")
|
|
|
|
(pl-io-test!
|
|
"format('abc') — plain text passes through"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), format(abc))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"abc")
|
|
|
|
;; ─── format/2 ──────────────────────────────────────────────────────────────
|
|
|
|
(pl-io-test!
|
|
"format('~w+~w', [1,2]) — two ~w args"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), format('~w+~w', [1,2]))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"1+2")
|
|
|
|
(pl-io-test!
|
|
"format('hello ~a!', [world]) — ~a with atom arg"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), format('hello ~a!', [world]))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"hello world!")
|
|
|
|
(pl-io-test!
|
|
"format('n=~d', [42]) — ~d with integer arg"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), format('n=~d', [42]))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"n=42")
|
|
|
|
(pl-io-test!
|
|
"format('~w', [foo(a)]) — ~w with compound"
|
|
(let
|
|
((env {}))
|
|
(pl-solve-once!
|
|
pl-io-db
|
|
(pl-io-goal "with_output_to(atom(X), format('~w', [foo(a)]))" env)
|
|
(pl-mk-trail))
|
|
(pl-atom-name (pl-walk-deep (dict-get env "X"))))
|
|
"foo(a)")
|
|
|
|
(define
|
|
pl-io-predicates-tests-run!
|
|
(fn
|
|
()
|
|
{:failed pl-io-test-fail
|
|
:passed pl-io-test-pass
|
|
:total pl-io-test-count
|
|
:failures pl-io-test-failures}))
|