with-cps.scm 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. ;;; Continuation-passing style (CPS) intermediate language (IL)
  2. ;; Copyright (C) 2013, 2014, 2015 Free Software Foundation, Inc.
  3. ;;;; This library is free software; you can redistribute it and/or
  4. ;;;; modify it under the terms of the GNU Lesser General Public
  5. ;;;; License as published by the Free Software Foundation; either
  6. ;;;; version 3 of the License, or (at your option) any later version.
  7. ;;;;
  8. ;;;; This library is distributed in the hope that it will be useful,
  9. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;;;; Lesser General Public License for more details.
  12. ;;;;
  13. ;;;; You should have received a copy of the GNU Lesser General Public
  14. ;;;; License along with this library; if not, write to the Free Software
  15. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. ;;; Commentary:
  17. ;;;
  18. ;;; Guile's CPS language is a label->cont mapping, which seems simple
  19. ;;; enough. However it's often cumbersome to thread around the output
  20. ;;; CPS program when doing non-trivial transformations, or when building
  21. ;;; a CPS program from scratch. For example, when visiting an
  22. ;;; expression during CPS conversion, we usually already know the label
  23. ;;; and the $kargs wrapper for the cont, and just need to know the body
  24. ;;; of that cont. However when building the body of that possibly
  25. ;;; nested Tree-IL expression we will also need to add conts to the
  26. ;;; result, so really it's a process that takes an incoming program,
  27. ;;; adds conts to that program, and returns the result program and the
  28. ;;; result term.
  29. ;;;
  30. ;;; It's a bit treacherous to do in a functional style as once you start
  31. ;;; adding to a program, you shouldn't add to previous versions of that
  32. ;;; program. Getting that right in the context of this program seed
  33. ;;; that is threaded through the conversion requires the use of a
  34. ;;; pattern, with-cps.
  35. ;;;
  36. ;;; with-cps goes like this:
  37. ;;;
  38. ;;; (with-cps cps clause ... tail-clause)
  39. ;;;
  40. ;;; Valid clause kinds are:
  41. ;;;
  42. ;;; (letk LABEL CONT)
  43. ;;; (setk LABEL CONT)
  44. ;;; (letv VAR ...)
  45. ;;; (let$ X (PROC ARG ...))
  46. ;;;
  47. ;;; letk and letv create fresh CPS labels and variable names,
  48. ;;; respectively. Labels and vars bound by letk and letv are in scope
  49. ;;; from their point of definition onward. letv just creates fresh
  50. ;;; variable names for use in other parts of with-cps, while letk binds
  51. ;;; fresh labels to values and adds them to the resulting program. The
  52. ;;; right-hand-side of letk, CONT, is passed to build-cont, so it should
  53. ;;; be a valid production of that language. setk is like letk but it
  54. ;;; doesn't create a fresh label name.
  55. ;;;
  56. ;;; let$ delegates processing to a sub-computation. The form (PROC ARG
  57. ;;; ...) is syntactically altered to be (PROC CPS ARG ...), where CPS is
  58. ;;; the value of the program being built, at that point in the
  59. ;;; left-to-right with-cps execution. That form is is expected to
  60. ;;; evaluate to two values: the new CPS term, and the value to bind to
  61. ;;; X. X is in scope for the following with-cps clauses. The name was
  62. ;;; chosen because the $ is reminiscent of the $ in CPS data types.
  63. ;;;
  64. ;;; The result of the with-cps form is determined by the tail clause,
  65. ;;; which may be of these kinds:
  66. ;;;
  67. ;;; ($ (PROC ARG ...))
  68. ;;; (setk LABEL CONT)
  69. ;;; EXP
  70. ;;;
  71. ;;; $ is like let$, but in tail position. If the tail clause is setk,
  72. ;;; then only one value is returned, the resulting CPS program.
  73. ;;; Otherwise EXP is any kind of expression, which should not add to the
  74. ;;; resulting program. Ending the with-cps with EXP is equivalant to
  75. ;;; returning (values CPS EXP).
  76. ;;;
  77. ;;; It's a bit of a monad, innit? Don't tell anyone though!
  78. ;;;
  79. ;;; Sometimes you need to just bind some constants to CPS values.
  80. ;;; with-cps-constants is there for you. For example:
  81. ;;;
  82. ;;; (with-cps-constants cps ((foo 34))
  83. ;;; (build-term ($values (foo))))
  84. ;;;
  85. ;;; The body of with-cps-constants is a with-cps clause, or a sequence
  86. ;;; of such clauses. But usually you will want with-cps-constants
  87. ;;; inside a with-cps, so it usually looks like this:
  88. ;;;
  89. ;;; (with-cps cps
  90. ;;; ...
  91. ;;; ($ (with-cps-constants ((foo 34))
  92. ;;; (build-term ($values (foo))))))
  93. ;;;
  94. ;;; which is to say that the $ or the let$ adds the CPS argument for us.
  95. ;;;
  96. ;;; Code:
  97. (define-module (language cps with-cps)
  98. #:use-module (language cps)
  99. #:use-module (language cps utils)
  100. #:use-module (language cps intmap)
  101. #:export (with-cps with-cps-constants))
  102. (define-syntax with-cps
  103. (syntax-rules (letk setk letv let$ $)
  104. ((_ (exp ...) clause ...)
  105. (let ((cps (exp ...)))
  106. (with-cps cps clause ...)))
  107. ((_ cps (letk label cont) clause ...)
  108. (let-fresh (label) ()
  109. (with-cps (intmap-add! cps label (build-cont cont))
  110. clause ...)))
  111. ((_ cps (setk label cont))
  112. (intmap-add! cps label (build-cont cont)
  113. (lambda (old new) new)))
  114. ((_ cps (setk label cont) clause ...)
  115. (with-cps (with-cps cps (setk label cont))
  116. clause ...))
  117. ((_ cps (letv v ...) clause ...)
  118. (let-fresh () (v ...)
  119. (with-cps cps clause ...)))
  120. ((_ cps (let$ var (proc arg ...)) clause ...)
  121. (call-with-values (lambda () (proc cps arg ...))
  122. (lambda (cps var)
  123. (with-cps cps clause ...))))
  124. ((_ cps ($ (proc arg ...)))
  125. (proc cps arg ...))
  126. ((_ cps exp)
  127. (values cps exp))))
  128. (define-syntax with-cps-constants
  129. (syntax-rules ()
  130. ((_ cps () clause ...)
  131. (with-cps cps clause ...))
  132. ((_ cps ((var val) (var* val*) ...) clause ...)
  133. (let ((x val))
  134. (with-cps cps
  135. (letv var)
  136. (let$ body (with-cps-constants ((var* val*) ...)
  137. clause ...))
  138. (letk label ($kargs ('var) (var) ,body))
  139. (build-term ($continue label #f ($const x))))))))