Files
rose-ash/spec/coroutines.sx
giles 21cb9cf51a spec: coroutine primitive — make-coroutine/resume/yield via perform/cek-step-loop
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>
2026-04-26 16:15:48 +00:00

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"))))))))