Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Phase 14: port type + eof-object. Input ports track _pos cursor; output ports accumulate _buffer. All 15 port primitives in spec/primitives.sx (stdlib.ports module), platform.py (JS), and 39/39 tests in spec/tests/test-ports.sx. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
233 lines
6.1 KiB
Plaintext
233 lines
6.1 KiB
Plaintext
;; Phase 14 — String ports + eof-object
|
|
|
|
(deftest
|
|
"eof-object"
|
|
(deftest
|
|
"eof-object is eof"
|
|
(assert=
|
|
true
|
|
(eof-object? (eof-object))
|
|
"eof-object? returns true for eof-object"))
|
|
(deftest
|
|
"non-eof values are not eof"
|
|
(assert= false (eof-object? nil) "nil is not eof")
|
|
(assert= false (eof-object? "") "string is not eof")
|
|
(assert= false (eof-object? 0) "zero is not eof")
|
|
(assert= false (eof-object? false) "false is not eof"))
|
|
(deftest
|
|
"type-of eof-object"
|
|
(assert=
|
|
"eof-object"
|
|
(type-of (eof-object))
|
|
"type-of eof-object is eof-object")))
|
|
|
|
(deftest
|
|
"open-input-string"
|
|
(deftest
|
|
"creates input port"
|
|
(let
|
|
(p (open-input-string "hello"))
|
|
(assert= true (port? p) "is a port")
|
|
(assert= true (input-port? p) "is an input port")
|
|
(assert= false (output-port? p) "is not an output port")))
|
|
(deftest
|
|
"type-of input port"
|
|
(let
|
|
(p (open-input-string "x"))
|
|
(assert= "input-port" (type-of p) "type-of is input-port"))))
|
|
|
|
(deftest
|
|
"open-output-string"
|
|
(deftest
|
|
"creates output port"
|
|
(let
|
|
(p (open-output-string))
|
|
(assert= true (port? p) "is a port")
|
|
(assert= true (output-port? p) "is an output port")
|
|
(assert= false (input-port? p) "is not an input port")))
|
|
(deftest
|
|
"type-of output port"
|
|
(let
|
|
(p (open-output-string))
|
|
(assert= "output-port" (type-of p) "type-of is output-port"))))
|
|
|
|
(deftest
|
|
"read-char"
|
|
(deftest
|
|
"reads chars sequentially"
|
|
(let
|
|
(p (open-input-string "ab"))
|
|
(let
|
|
(c1 (read-char p))
|
|
(assert= true (char? c1) "first result is char")
|
|
(assert= 97 (char->integer c1) "first char is a"))))
|
|
(deftest
|
|
"reads second char"
|
|
(let
|
|
(p (open-input-string "ab"))
|
|
(read-char p)
|
|
(let
|
|
(c2 (read-char p))
|
|
(assert= true (char? c2) "second result is char")
|
|
(assert= 98 (char->integer c2) "second char is b"))))
|
|
(deftest
|
|
"returns eof at end"
|
|
(let
|
|
(p (open-input-string "x"))
|
|
(read-char p)
|
|
(assert= true (eof-object? (read-char p)) "eof after last char")))
|
|
(deftest
|
|
"empty string yields eof immediately"
|
|
(let
|
|
(p (open-input-string ""))
|
|
(assert= true (eof-object? (read-char p)) "eof from empty string"))))
|
|
|
|
(deftest
|
|
"peek-char"
|
|
(deftest
|
|
"peeks without consuming"
|
|
(let
|
|
(p (open-input-string "x"))
|
|
(let
|
|
(c1 (peek-char p))
|
|
(let
|
|
(c2 (peek-char p))
|
|
(assert=
|
|
(char->integer c1)
|
|
(char->integer c2)
|
|
"peek twice gives same char")))))
|
|
(deftest
|
|
"peek then read"
|
|
(let
|
|
(p (open-input-string "z"))
|
|
(let
|
|
(peeked (peek-char p))
|
|
(let
|
|
(read (read-char p))
|
|
(assert=
|
|
(char->integer peeked)
|
|
(char->integer read)
|
|
"peek and read agree")))))
|
|
(deftest
|
|
"peek at end returns eof"
|
|
(let
|
|
(p (open-input-string ""))
|
|
(assert= true (eof-object? (peek-char p)) "eof on empty peek"))))
|
|
|
|
(deftest
|
|
"read-line"
|
|
(deftest
|
|
"reads a single line"
|
|
(let
|
|
(p (open-input-string "hello"))
|
|
(assert= "hello" (read-line p) "reads whole string as line")))
|
|
(deftest
|
|
"reads line up to newline"
|
|
(let
|
|
(p (open-input-string "foo\nbar"))
|
|
(assert= "foo" (read-line p) "first line is foo")))
|
|
(deftest
|
|
"reads second line"
|
|
(let
|
|
(p (open-input-string "foo\nbar"))
|
|
(read-line p)
|
|
(assert= "bar" (read-line p) "second line is bar")))
|
|
(deftest
|
|
"returns eof on empty port"
|
|
(let
|
|
(p (open-input-string ""))
|
|
(assert= true (eof-object? (read-line p)) "eof on empty")))
|
|
(deftest
|
|
"returns eof after last line"
|
|
(let
|
|
(p (open-input-string "hi"))
|
|
(read-line p)
|
|
(assert= true (eof-object? (read-line p)) "eof after reading"))))
|
|
|
|
(deftest
|
|
"write-char and get-output-string"
|
|
(deftest
|
|
"write single char"
|
|
(let
|
|
(p (open-output-string))
|
|
(write-char (make-char 65) p)
|
|
(assert= "A" (get-output-string p) "write char A")))
|
|
(deftest
|
|
"write multiple chars"
|
|
(let
|
|
(p (open-output-string))
|
|
(write-char (make-char 72) p)
|
|
(write-char (make-char 105) p)
|
|
(assert= "Hi" (get-output-string p) "write Hi"))))
|
|
|
|
(deftest
|
|
"write-string"
|
|
(deftest
|
|
"write a string to port"
|
|
(let
|
|
(p (open-output-string))
|
|
(write-string "hello" p)
|
|
(assert= "hello" (get-output-string p) "write-string result")))
|
|
(deftest
|
|
"multiple writes concatenate"
|
|
(let
|
|
(p (open-output-string))
|
|
(write-string "foo" p)
|
|
(write-string "bar" p)
|
|
(assert= "foobar" (get-output-string p) "concatenated writes"))))
|
|
|
|
(deftest
|
|
"get-output-string idempotent"
|
|
(let
|
|
(p (open-output-string))
|
|
(write-string "test" p)
|
|
(assert= "test" (get-output-string p) "first call")
|
|
(assert= "test" (get-output-string p) "second call same result")))
|
|
|
|
(deftest
|
|
"char-ready?"
|
|
(deftest
|
|
"ready when chars available"
|
|
(let
|
|
(p (open-input-string "x"))
|
|
(assert= true (char-ready? p) "ready with content")))
|
|
(deftest
|
|
"not ready when empty"
|
|
(let
|
|
(p (open-input-string ""))
|
|
(assert= false (char-ready? p) "not ready when empty"))))
|
|
|
|
(deftest
|
|
"close-port"
|
|
(deftest
|
|
"close input port"
|
|
(let
|
|
(p (open-input-string "hello"))
|
|
(close-port p)
|
|
(assert= true (eof-object? (read-char p)) "read after close gives eof")))
|
|
(deftest
|
|
"close output port"
|
|
(let
|
|
(p (open-output-string))
|
|
(write-string "ok" p)
|
|
(close-port p)
|
|
(assert= "ok" (get-output-string p) "output preserved after close"))))
|
|
|
|
(deftest
|
|
"roundtrip string via ports"
|
|
(let
|
|
(in (open-input-string "abc"))
|
|
(let
|
|
(out (open-output-string))
|
|
(do
|
|
(let
|
|
(c1 (read-char in))
|
|
(when (not (eof-object? c1)) (write-char c1 out)))
|
|
(let
|
|
(c2 (read-char in))
|
|
(when (not (eof-object? c2)) (write-char c2 out)))
|
|
(let
|
|
(c3 (read-char in))
|
|
(when (not (eof-object? c3)) (write-char c3 out)))
|
|
(assert= "abc" (get-output-string out) "roundtrip via ports")))))
|