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