(use gauche.partcont)

;; 部分継続でcoroutine作ってみる

(define *current-coroutine* ())

(define coroutine-done (list 'coroutine-done))

(define (make-coroutine thunk)
  (let ([co (list #f)])
    (lambda args
      (let ([old *current-coroutine*])
        (set! *current-coroutine* co)
        (let ([v (if (car co)
                     (apply (car co) args)
                     (reset
                      (thunk)
                      (set-car! co (lambda _ coroutine-done))
                      coroutine-done))])
          (set! *current-coroutine* old)
          v)))))

(define (yield . v)
  (shift k
         (set-car! *current-coroutine* k)
         (unless (null? v) (car v))))

(define-syntax coroutine
  (syntax-rules ()
    [(_ body ...)
     (make-coroutine (lambda () body ...))]))

(let ([co ()])
  (set! co
        (coroutine
         (yield :a)
         (yield :b)
         (yield :c)))
  (print (co))
  (print (co))
  (print (co))
  (print (co))
  (print (co)))
