spec/coroutines.sx: define-library with make-coroutine, coroutine-resume, coroutine-yield, coroutine?, coroutine-alive?. Built on existing perform/ cek-step-loop/cek-resume suspension machinery. spec/tests/test-coroutines.sx: 17 tests — multi-yield, final return, arg passthrough, alive? predicate, nested coroutines, recursive iteration, independent coroutine interleaving. Key: coroutine body must use (define loop (fn…)) not named let — named let transpiles to cek_call→cek_run which rejects IO suspension. All 17/17 pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
57 lines
1.9 KiB
Plaintext
57 lines
1.9 KiB
Plaintext
(define-library
|
|
(sx coroutines)
|
|
(export
|
|
make-coroutine
|
|
coroutine?
|
|
coroutine-alive?
|
|
coroutine-yield
|
|
coroutine-handle-result
|
|
coroutine-resume)
|
|
(begin
|
|
(define make-coroutine (fn (thunk) {:suspension nil :thunk thunk :type "coroutine" :state "ready"}))
|
|
(define
|
|
coroutine?
|
|
(fn (v) (and (dict? v) (= (get v "type") "coroutine"))))
|
|
(define
|
|
coroutine-alive?
|
|
(fn (c) (and (coroutine? c) (not (= (get c "state") "dead")))))
|
|
(define coroutine-yield (fn (val) (perform {:value val :op "coroutine-yield"})))
|
|
(define
|
|
coroutine-handle-result
|
|
(fn
|
|
(c result)
|
|
(if
|
|
(cek-terminal? result)
|
|
(do (dict-set! c "state" "dead") {:done true :value (cek-value result)})
|
|
(let
|
|
((request (cek-io-request result)))
|
|
(if
|
|
(and (dict? request) (= (get request "op") "coroutine-yield"))
|
|
(do
|
|
(dict-set! c "state" "suspended")
|
|
(dict-set! c "suspension" result)
|
|
{:done false :value (get request "value")})
|
|
(perform request))))))
|
|
(define
|
|
coroutine-resume
|
|
(fn
|
|
(c val)
|
|
(cond
|
|
(not (coroutine? c))
|
|
(error "coroutine-resume: not a coroutine")
|
|
(= (get c "state") "dead")
|
|
(error "coroutine-resume: coroutine is dead")
|
|
(= (get c "state") "ready")
|
|
(do
|
|
(dict-set! c "state" "running")
|
|
(coroutine-handle-result
|
|
c
|
|
(cek-step-loop
|
|
(make-cek-state (list (get c "thunk")) (make-env) (list)))))
|
|
(= (get c "state") "suspended")
|
|
(do
|
|
(dict-set! c "state" "running")
|
|
(coroutine-handle-result c (cek-resume (get c "suspension") val)))
|
|
:else (error
|
|
(str "coroutine-resume: unexpected state: " (get c "state"))))))))
|