;; 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}))