prompt.lisp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. "### Continuation objects
  2. The value given to the handler in [[call-with-prompt]], much like
  3. the value captured by [[shift]], is not a coroutine: it is a
  4. continuation object. Continuation objects (or just \"continuations\")
  5. can be applied, much like functions, to continue their execution.
  6. They may also be given to [[call-with-prompt]]."
  7. (import lua/coroutine c)
  8. (import data/struct ())
  9. (defstruct (continuation (hide reify-continuation) continuation?)
  10. (fields
  11. (immutable thread (hide cont-thread)))
  12. (constructor new
  13. (lambda (k)
  14. (setmetatable (new k)
  15. { :__call (lambda (k &args)
  16. (apply continue (cont-thread k) args)) }))))
  17. (defmethod (pretty continuation) (_) "«continuation»")
  18. (defun call-with-prompt (prompt-tag body handler)
  19. "Call the thunk BODY with a prompt PROMPT-TAG in scope. If BODY
  20. aborts to PROMPT-TAG, then HANDLER is invoked with the coroutine
  21. representing the rest of BODY along with any extra arguments to
  22. [[abort-to-prompt]].
  23. **NOTE**: The given HANDLER is not executed in the scope of the
  24. prompt, so subsequent calls to [[abort-to-prompt]] in the
  25. continuation will not be handled.
  26. ### Example
  27. ```cl
  28. > (call-with-prompt 'tag
  29. . (lambda ()
  30. . (+ 1 (abort-to-prompt 'tag)))
  31. . (lambda (k)
  32. . (k 1)))
  33. out = 2
  34. ```"
  35. (let* [(k (cond
  36. [(= (type body) "thread") body]
  37. ; we reify the continuation before handing it off to the
  38. ; handler anyway
  39. [(= (type body) "continuation") (cont-thread body)]
  40. [(= (type body) "function") (c/create body)]
  41. [else (error! (.. "expected a coroutine or a function, got " (type body)))]))]
  42. (loop [(k k)
  43. (res nil)]
  44. [(= (c/status k) :dead) res]
  45. (let* [((ok err) (c/resume k))]
  46. (cond
  47. [(and ok
  48. (list? err)
  49. (>= (n err) 2)
  50. (eq? (car err) :abort))
  51. (if (eq? (cadr err) prompt-tag)
  52. (handler (reify-continuation k) (cddr err))
  53. (apply abort-to-prompt (cadr err) (cddr err)))]
  54. [(not ok)
  55. (error! err)]
  56. [ok
  57. (recur k err)])))))
  58. (define call/p call-with-prompt)
  59. (defmacro let-prompt (tg e h)
  60. "Evaluate E in a prompt with the tag TG and handler H."
  61. `(call-with-prompt ,tg (lambda () ,e) ,h))
  62. (define-macro let/p let-prompt)
  63. (defun call-with-escape-continuation (body)
  64. "Invoke the thunk BODY with an escape continuation.
  65. ### Example
  66. ```cl
  67. > (call-with-escape-continuation
  68. . (lambda (return)
  69. . (print! \"this is printed\")
  70. . (return 1)
  71. . (print! \"this is not\")))
  72. this is printed
  73. out = 1
  74. ```"
  75. (call-with-prompt 'escape-continuation
  76. (lambda ()
  77. (body (lambda (&rest)
  78. (apply abort-to-prompt
  79. 'escape-continuation rest))))
  80. (lambda (_ &rest)
  81. (splice (car rest)))))
  82. (defmacro let-escape-continuation (k &body)
  83. "Bind K within BODY to an escape continuation.
  84. ### Example
  85. ```cl
  86. > (let-escape-continuation return
  87. . (print! 1)
  88. . (return 2)
  89. . (print! 3))
  90. 1
  91. out = 2
  92. ```"
  93. `(call-with-escape-continuation (lambda (,k) ,@body)))
  94. (define call/ec call-with-escape-continuation)
  95. (define-macro let/ec let-escape-continuation)
  96. (defun continue (k &args) :hidden
  97. (let* [(last-res nil)]
  98. (while (/= (c/status k) :dead)
  99. (let* [((ok err) (apply c/resume k args))]
  100. (if (not ok)
  101. (error! err)
  102. (progn
  103. (set! args '())
  104. (when err
  105. (set! last-res err))))))
  106. last-res))
  107. (defun abort-to-prompt (tag &rest)
  108. "Abort to the prompt TAG, giving REST as arguments to the handler."
  109. (c/yield (cons :abort tag rest)))
  110. (defun abort/p (tag &rest)
  111. "Abort to the prompt TAG, giving REST as arguments to the handler."
  112. (c/yield (cons :abort tag rest)))
  113. (defmacro reset (&body)
  114. "Establish a prompt, and evaluate BODY within that prompt.
  115. ### Example
  116. ```
  117. > (* 2 (reset (+ 1 (shift k (k 5)))))
  118. out = 12
  119. ```"
  120. (let* [(cont (gensym))
  121. (f (gensym))]
  122. `(call-with-prompt ','reset-tag
  123. (lambda () ,@body)
  124. (lambda (,cont ,f)
  125. ((car ,f) ,cont)))))
  126. (defmacro shift (k &body)
  127. "Abort to the nearest [[reset]], and evaluate BODY in a scope where
  128. the captured continuation is bound to K.
  129. ### Example
  130. ```
  131. > (* 2 (reset (+ 1 (shift k (k 5)))))
  132. out = 12
  133. ```"
  134. `(abort-to-prompt ','reset-tag
  135. (lambda (,k)
  136. ,@body)))
  137. (defun alive? (k)
  138. "Check that the continuation K may be executed further.
  139. ### Example:
  140. ```
  141. > (alive? (reset (shift k k)))
  142. out = true
  143. ```"
  144. (cond
  145. [(continuation? k)
  146. (/= (c/status (cont-thread k)) "dead")]
  147. [(= (type k) :thread)
  148. (/= (c/status k) "dead")]
  149. [(function? k)
  150. true]
  151. [else false]))
  152. (defmacro block (&body)
  153. "Estabilish an escape continuation called `break` and evaluate BODY.
  154. ### Example:
  155. ```cl
  156. > (block
  157. . (print! 1)
  158. . (break)
  159. . (print! 2))
  160. 1
  161. out = nil
  162. ```"
  163. `(let-escape-continuation ,'break ,@body))