Step 5: CEK IO suspension + R7RS modules (define-library/import)
Third CEK phase "io-suspended": perform suspends evaluation, host resolves IO, cek-resume feeds result back. VM OP_PERFORM (opcode 112) enables JIT-compiled functions to suspend. VM→CEK→suspend chain propagates suspension across the JIT/CEK boundary via pending_cek. R7RS define-library creates isolated environments with export control. import checks the library registry and suspends for unknown libraries, enabling lazy on-demand loading. Import qualifiers: only, prefix. Server-side cek_run_with_io handles suspension by dispatching IO requests to the Python bridge and resuming. guard composes cleanly with perform for structured error recovery across IO boundaries. 2598/2598 tests (30 new: 15 core suspension, 3 JIT, 1 cross-boundary, 9 modules, 2 error handling). Zero regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
209
spec/tests/test-io-suspension.sx
Normal file
209
spec/tests/test-io-suspension.sx
Normal file
@@ -0,0 +1,209 @@
|
||||
;; IO suspension tests — verifies perform/cek-step-loop/cek-resume
|
||||
(defsuite
|
||||
"io-suspend-basic"
|
||||
(deftest
|
||||
"perform creates suspended state"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (perform {:op "test"})) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(assert (not (cek-terminal? state)))))
|
||||
(deftest
|
||||
"suspended state carries IO request"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (perform {:service "blog" :op "query"})) (make-env) (list)))))
|
||||
(let
|
||||
((req (cek-io-request state)))
|
||||
(assert= (get req "op") "query")
|
||||
(assert= (get req "service") "blog"))))
|
||||
(deftest
|
||||
"cek-resume delivers result"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (perform {:op "test"})) (make-env) (list)))))
|
||||
(let
|
||||
((final (cek-resume state 42)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) 42))))
|
||||
(deftest
|
||||
"cek-resume with string result"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (perform {:op "test"})) (make-env) (list)))))
|
||||
(let
|
||||
((final (cek-resume state "hello")))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) "hello"))))
|
||||
(deftest
|
||||
"cek-run errors on suspension"
|
||||
(let
|
||||
((result (cek-try (fn () (cek-run (make-cek-state (quote (perform {:op "test"})) (make-env) (list)))))))
|
||||
(assert= (symbol-name (first result)) "error"))))
|
||||
|
||||
(defsuite
|
||||
"io-suspend-control-flow"
|
||||
(deftest
|
||||
"perform inside let — result used in binding"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (let ((x (perform {:op "get-value"}))) (+ x 10))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(let
|
||||
((final (cek-resume state 32)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) 42))))
|
||||
(deftest
|
||||
"perform inside if condition"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (if (perform {:op "check"}) "yes" "no")) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(let
|
||||
((final (cek-resume state true)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) "yes"))))
|
||||
(deftest
|
||||
"perform inside if — false branch"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (if (perform {:op "check"}) "yes" "no")) (make-env) (list)))))
|
||||
(let
|
||||
((final (cek-resume state false)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) "no"))))
|
||||
(deftest
|
||||
"sequential performs — two suspensions"
|
||||
(let
|
||||
((state1 (cek-step-loop (make-cek-state (quote (let ((a (perform {:op "first"}))) (let ((b (perform {:op "second"}))) (+ a b)))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state1))
|
||||
(assert= (get (cek-io-request state1) "op") "first")
|
||||
(let
|
||||
((state2 (cek-resume state1 10)))
|
||||
(assert (cek-suspended? state2))
|
||||
(assert= (get (cek-io-request state2) "op") "second")
|
||||
(let
|
||||
((final (cek-resume state2 32)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) 42)))))
|
||||
(deftest
|
||||
"perform inside begin — not last expr"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (begin (perform {:op "side-effect"}) "done")) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(let
|
||||
((final (cek-resume state nil)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) "done")))))
|
||||
|
||||
(defsuite
|
||||
"io-suspend-functions"
|
||||
(deftest
|
||||
"perform inside lambda"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote ((fn (x) (+ x (perform {:op "get"}))) 10)) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(let
|
||||
((final (cek-resume state 32)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) 42))))
|
||||
(deftest
|
||||
"perform result passed to function"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (let ((double (fn (x) (* x 2)))) (double (perform {:op "get-val"})))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(let
|
||||
((final (cek-resume state 21)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) 42)))))
|
||||
|
||||
(defsuite
|
||||
"io-suspend-values"
|
||||
(deftest
|
||||
"resume with nil"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (perform {:op "test"})) (make-env) (list)))))
|
||||
(let
|
||||
((final (cek-resume state nil)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert (nil? (cek-value final))))))
|
||||
(deftest
|
||||
"resume with list"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (perform {:op "fetch"})) (make-env) (list)))))
|
||||
(let
|
||||
((final (cek-resume state (list 1 2 3))))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (len (cek-value final)) 3))))
|
||||
(deftest
|
||||
"resume with dict"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (let ((result (perform {:op "query"}))) (get result "name"))) (make-env) (list)))))
|
||||
(let
|
||||
((final (cek-resume state {:name "alice"})))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) "alice")))))
|
||||
|
||||
(defsuite
|
||||
"io-suspend-jit"
|
||||
(deftest
|
||||
"named function with perform suspends"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (begin (define fetch-data (fn (key) (perform {:op "fetch" :key key}))) (fetch-data "users"))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(assert= (get (cek-io-request state) "op") "fetch")
|
||||
(let
|
||||
((final (cek-resume state (list "alice" "bob"))))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (len (cek-value final)) 2))))
|
||||
(deftest
|
||||
"named function with perform and computation"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (begin (define fetch-and-count (fn (key) (let ((data (perform {:op "fetch" :key key}))) (len data)))) (fetch-and-count "items"))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(let
|
||||
((final (cek-resume state (list 1 2 3 4 5))))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) 5))))
|
||||
(deftest
|
||||
"two named functions with sequential performs"
|
||||
(let
|
||||
((state1 (cek-step-loop (make-cek-state (quote (begin (define get-name (fn () (perform {:op "get-name"}))) (define get-age (fn () (perform {:op "get-age"}))) (str (get-name) " is " (get-age) " years old"))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state1))
|
||||
(assert= (get (cek-io-request state1) "op") "get-name")
|
||||
(let
|
||||
((state2 (cek-resume state1 "Alice")))
|
||||
(assert (cek-suspended? state2))
|
||||
(assert= (get (cek-io-request state2) "op") "get-age")
|
||||
(let
|
||||
((final (cek-resume state2 30)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) "Alice is 30 years old"))))))
|
||||
|
||||
(defsuite
|
||||
"io-suspend-cross-boundary"
|
||||
(deftest
|
||||
"function calling component that performs IO"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (begin (defcomp ~data-loader (&key source) (perform {:op "load" :source source})) (define render (fn (src) (let ((data (~data-loader :source src))) (str "loaded: " (len data) " items")))) (render "products"))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(assert= (get (cek-io-request state) "op") "load")
|
||||
(assert= (get (cek-io-request state) "source") "products")
|
||||
(let
|
||||
((final (cek-resume state (list "a" "b" "c"))))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) "loaded: 3 items")))))
|
||||
|
||||
(defsuite
|
||||
"io-suspend-error-handling"
|
||||
(deftest
|
||||
"guard wraps perform — normal completion"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (guard (e (true (str "caught: " e))) (perform {:op "get"}))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(let
|
||||
((final (cek-resume state "ok-result")))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) "ok-result"))))
|
||||
(deftest
|
||||
"perform result flows through let in guard body"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (guard (e (true "error")) (let ((x (perform {:op "get"}))) (+ x 1)))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(let
|
||||
((final (cek-resume state 41)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) 42)))))
|
||||
91
spec/tests/test-modules.sx
Normal file
91
spec/tests/test-modules.sx
Normal file
@@ -0,0 +1,91 @@
|
||||
;; R7RS module system tests — define-library / import
|
||||
|
||||
(defsuite
|
||||
"define-library-basic"
|
||||
(deftest
|
||||
"define and import a library"
|
||||
(define-library
|
||||
(test math)
|
||||
(export add square)
|
||||
(begin
|
||||
(define add (fn (a b) (+ a b)))
|
||||
(define square (fn (x) (* x x)))))
|
||||
(import (test math))
|
||||
(assert= (add 3 4) 7)
|
||||
(assert= (square 5) 25))
|
||||
(deftest
|
||||
"library isolation — internal not exported"
|
||||
(define-library
|
||||
(test internal)
|
||||
(export public-fn)
|
||||
(begin
|
||||
(define helper (fn (x) (* x 2)))
|
||||
(define public-fn (fn (x) (helper (+ x 1))))))
|
||||
(import (test internal))
|
||||
(assert= (public-fn 5) 12))
|
||||
(deftest
|
||||
"multiple libraries"
|
||||
(define-library
|
||||
(test greet)
|
||||
(export greet)
|
||||
(begin (define greet (fn (name) (str "Hello, " name "!")))))
|
||||
(define-library
|
||||
(test format)
|
||||
(export shout)
|
||||
(begin (define shout (fn (s) (upper s)))))
|
||||
(import (test greet))
|
||||
(import (test format))
|
||||
(assert= (greet "world") "Hello, world!")
|
||||
(assert= (shout "hello") "HELLO")))
|
||||
|
||||
(defsuite
|
||||
"import-qualifiers"
|
||||
(deftest
|
||||
"import with only"
|
||||
(define-library
|
||||
(test utils)
|
||||
(export inc dec double)
|
||||
(begin
|
||||
(define inc (fn (x) (+ x 1)))
|
||||
(define dec (fn (x) (- x 1)))
|
||||
(define double (fn (x) (* x 2)))))
|
||||
(import (only (test utils) inc double))
|
||||
(assert= (inc 5) 6)
|
||||
(assert= (double 5) 10))
|
||||
(deftest
|
||||
"import with prefix"
|
||||
(define-library
|
||||
(test prefixed)
|
||||
(export value)
|
||||
(begin (define value 42)))
|
||||
(import (prefix (test prefixed) tp:))
|
||||
(assert= tp:value 42)))
|
||||
|
||||
(defsuite
|
||||
"library-registry"
|
||||
(deftest
|
||||
"library-loaded? returns true after define"
|
||||
(define-library (test check) (export x) (begin (define x 1)))
|
||||
(assert (library-loaded? (quote (test check)))))
|
||||
(deftest
|
||||
"library-loaded? returns false for unknown"
|
||||
(assert (not (library-loaded? (quote (nonexistent lib)))))))
|
||||
|
||||
(defsuite
|
||||
"import-suspension"
|
||||
(deftest
|
||||
"import of unknown library suspends"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (import (remote data))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(assert= (get (cek-io-request state) "op") "import")))
|
||||
(deftest
|
||||
"import suspension resumes after library registered"
|
||||
(let
|
||||
((state (cek-step-loop (make-cek-state (quote (begin (import (lazy lib)) (get-value))) (make-env) (list)))))
|
||||
(assert (cek-suspended? state))
|
||||
(register-library (quote (lazy lib)) {:get-value (fn () 42)})
|
||||
(let
|
||||
((final (cek-resume state nil)))
|
||||
(assert (cek-terminal? final))
|
||||
(assert= (cek-value final) 42)))))
|
||||
Reference in New Issue
Block a user