lua: coroutines (create/resume/yield/status/wrap) via call/cc +8 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled

This commit is contained in:
2026-04-24 18:36:41 +00:00
parent 0934c4bd28
commit a5947e1295
3 changed files with 152 additions and 1 deletions

View File

@@ -529,3 +529,125 @@
(fn
(args i)
(if (< i (len args)) (nth args i) nil)))
;; ── Coroutines (call/cc based) ────────────────────────────────
(define __current-co nil)
(define
lua-coroutine-create
(fn
(f)
(let ((co {}))
(begin
(dict-set! co "__co" true)
(dict-set! co "status" "suspended")
(dict-set! co "body" f)
(dict-set! co "resume-k" nil)
(dict-set! co "caller-k" nil)
co))))
(define
lua-coroutine-status
(fn
(co)
(if (and (= (type-of co) "dict") (has-key? co "__co"))
(get co "status")
(error "lua: not a coroutine"))))
(define
lua-co-wrap-result
(fn (r)
(cond
((lua-multi? r) (cons (quote lua-multi) (cons true (rest r))))
(else (list (quote lua-multi) true r)))))
(define
lua-co-first-call
(fn (co rvals prev)
(let ((r (lua-apply (get co "body") rvals)))
(begin
(dict-set! co "status" "dead")
(set! __current-co prev)
((get co "caller-k") (lua-co-wrap-result r))))))
(define
lua-co-continue-call
(fn (co rvals)
(let ((rk (get co "resume-k")))
(begin
(dict-set! co "resume-k" nil)
(rk (if (> (len rvals) 0) (first rvals) nil))))))
(define
lua-coroutine-resume
(fn
(&rest args)
(let ((co (first args)) (rvals (rest args)))
(cond
((not (and (= (type-of co) "dict") (has-key? co "__co")))
(list (quote lua-multi) false "not a coroutine"))
((= (get co "status") "dead")
(list (quote lua-multi) false "cannot resume dead coroutine"))
((= (get co "status") "running")
(list (quote lua-multi) false "cannot resume running coroutine"))
(else
(call/cc
(fn (k)
(let ((prev __current-co))
(begin
(dict-set! co "caller-k" k)
(dict-set! co "status" "running")
(set! __current-co co)
(guard
(e (true
(begin
(dict-set! co "status" "dead")
(set! __current-co prev)
(list (quote lua-multi) false e))))
(cond
((= (get co "resume-k") nil) (lua-co-first-call co rvals prev))
(else (lua-co-continue-call co rvals)))))))))))))
(define
lua-coroutine-yield
(fn
(&rest yvals)
(cond
((= __current-co nil) (error "lua: attempt to yield from outside a coroutine"))
(else
(call/cc
(fn (k)
(let ((co __current-co))
(begin
(dict-set! co "resume-k" k)
(dict-set! co "status" "suspended")
(set! __current-co nil)
((get co "caller-k") (cons (quote lua-multi) (cons true yvals)))))))))))
(define
lua-co-wrap-caller
(fn (co args)
(let ((r (sx-apply-ref lua-coroutine-resume (cons co args))))
(cond
((and (lua-multi? r) (> (len r) 1) (= (nth r 1) true))
(cond
((<= (len r) 2) nil)
((= (len r) 3) (nth r 2))
(else (cons (quote lua-multi) (rest (rest r))))))
((and (lua-multi? r) (> (len r) 1))
(error (if (> (len r) 2) (nth r 2) "coroutine error")))
(else nil)))))
(define
lua-coroutine-wrap
(fn (f)
(let ((co (lua-coroutine-create f)))
(fn (&rest args) (lua-co-wrap-caller co args)))))
(define coroutine {})
(dict-set! coroutine "create" lua-coroutine-create)
(dict-set! coroutine "resume" lua-coroutine-resume)
(dict-set! coroutine "yield" lua-coroutine-yield)
(dict-set! coroutine "status" lua-coroutine-status)
(dict-set! coroutine "wrap" lua-coroutine-wrap)