#lang scheme/base
(require (for-syntax scheme/base)
scheme/contract
srfi/26/cut)
(define yield-prompt
(make-continuation-prompt-tag 'yield))
(define (make-yieldable/composable-continuations yield->body)
(define caller #f)
(define resume #f)
(define (yield . args)
(apply values
(call/cc
(lambda (k)
(set! resume k)
(apply caller args))
yield-prompt)))
(define (return . args)
(apply values
(call/cc
(lambda (k)
(set! resume #f)
(apply caller args))
yield-prompt)))
(define body
(yield->body yield))
(lambda args
(call-with-continuation-prompt
(lambda ()
(call/cc
(lambda (k)
(set! caller k)
(if resume
(resume args)
(call-with-values (cut apply body args)
return)))
yield-prompt))
yield-prompt)))
(define (make-yieldable/full-continuations yield->body)
(define caller #f)
(define resume #f)
(define (yield . args)
(apply values
(let/cc k
(set! resume k)
(apply caller args))))
(define (return . args)
(apply values
(let/cc k
(set! resume #f)
(apply caller args))))
(define body
(yield->body yield))
(lambda args
(let/cc k
(set! caller k)
(if resume
(resume args)
(call-with-values (cut apply body args)
return)))))
(define make-yieldable
make-yieldable/composable-continuations)
(define-syntax (yieldable stx)
(syntax-case stx ()
[(_ yield statement ...)
#'(make-yieldable (lambda (yield) statement ...))]))
(provide yieldable)
(provide/contract
[make-yieldable (-> procedure? procedure?)]
[make-yieldable/composable-continuations (-> procedure? procedure?)]
[make-yieldable/full-continuations (-> procedure? procedure?)])