local-eval.scm 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. ;;; -*- mode: scheme; coding: utf-8; -*-
  2. ;;;
  3. ;;; Copyright (C) 2012, 2013 Free Software Foundation, Inc.
  4. ;;;
  5. ;;; This library is free software; you can redistribute it and/or
  6. ;;; modify it under the terms of the GNU Lesser General Public
  7. ;;; License as published by the Free Software Foundation; either
  8. ;;; version 3 of the License, or (at your option) any later version.
  9. ;;;
  10. ;;; This library is distributed in the hope that it will be useful,
  11. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. ;;; Lesser General Public License for more details.
  14. ;;;
  15. ;;; You should have received a copy of the GNU Lesser General Public
  16. ;;; License along with this library; if not, write to the Free Software
  17. ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. (define-module (ice-9 local-eval)
  19. #:use-module (ice-9 format)
  20. #:use-module (srfi srfi-9)
  21. #:use-module (srfi srfi-9 gnu)
  22. #:use-module (system base compile)
  23. #:use-module (system syntax)
  24. #:export (the-environment local-eval local-compile))
  25. (define-record-type lexical-environment-type
  26. (make-lexical-environment scope wrapper boxes patterns)
  27. lexical-environment?
  28. (scope lexenv-scope)
  29. (wrapper lexenv-wrapper)
  30. (boxes lexenv-boxes)
  31. (patterns lexenv-patterns))
  32. (set-record-type-printer!
  33. lexical-environment-type
  34. (lambda (e port)
  35. (format port "#<lexical-environment ~S (~S bindings)>"
  36. (syntax-module (lexenv-scope e))
  37. (+ (length (lexenv-boxes e)) (length (lexenv-patterns e))))))
  38. (define-syntax syntax-object-of
  39. (lambda (form)
  40. (syntax-case form ()
  41. ((_ x) #`(quote #,(datum->syntax #'x #'x))))))
  42. (define-syntax-rule (make-box v)
  43. (case-lambda
  44. (() v)
  45. ((x) (set! v x))))
  46. (define (make-transformer-from-box id trans)
  47. (set-procedure-property! trans 'identifier-syntax-box id)
  48. trans)
  49. (define-syntax-rule (identifier-syntax-from-box box)
  50. (make-transformer-from-box
  51. (syntax-object-of box)
  52. (identifier-syntax (id (box))
  53. ((set! id x) (box x)))))
  54. (define (unsupported-binding name)
  55. (make-variable-transformer
  56. (lambda (x)
  57. (syntax-violation
  58. 'local-eval
  59. "unsupported binding captured by (the-environment)"
  60. x))))
  61. (define (within-nested-ellipses id lvl)
  62. (let loop ((s id) (n lvl))
  63. (if (zero? n)
  64. s
  65. (loop #`(#,s (... ...)) (- n 1)))))
  66. ;; Analyze the set of bound identifiers IDS. Return four values:
  67. ;;
  68. ;; capture: A list of forms that will be emitted in the expansion of
  69. ;; `the-environment' to capture lexical variables.
  70. ;;
  71. ;; formals: Corresponding formal parameters for use in the lambda that
  72. ;; re-introduces those variables. These are temporary identifiers, and
  73. ;; as such if we have a nested `the-environment', there is no need to
  74. ;; capture them. (See the notes on nested `the-environment' and
  75. ;; proxies, below.)
  76. ;;
  77. ;; wrappers: A list of procedures of type SYNTAX -> SYNTAX, used to wrap
  78. ;; the expression to be evaluated in forms that re-introduce the
  79. ;; variable. The forms will be nested so that the variable shadowing
  80. ;; semantics of the original form are maintained.
  81. ;;
  82. ;; patterns: A terrible hack. The issue is that for pattern variables,
  83. ;; we can't emit lexically nested with-syntax forms, like:
  84. ;;
  85. ;; (with-syntax ((foo 1)) (the-environment))
  86. ;; => (with-syntax ((foo 1))
  87. ;; ... #'(with-syntax ((foo ...)) ... exp) ...)
  88. ;;
  89. ;; The reason is that the outer "foo" substitutes into the inner "foo",
  90. ;; yielding something like:
  91. ;;
  92. ;; (with-syntax ((foo 1))
  93. ;; ... (with-syntax ((1 ...)) ...)
  94. ;;
  95. ;; Which ain't what we want. So we hide the information needed to
  96. ;; re-make the inner pattern binding form in the lexical environment
  97. ;; object, and then introduce those identifiers via another with-syntax.
  98. ;;
  99. ;;
  100. ;; There are four different kinds of lexical bindings: normal lexicals,
  101. ;; macros, displaced lexicals, and pattern variables. See the
  102. ;; documentation of syntax-local-binding for more info on these.
  103. ;;
  104. ;; We capture normal lexicals via `make-box', which creates a
  105. ;; case-lambda that can reference or set a variable. These get
  106. ;; re-introduced with an identifier-syntax.
  107. ;;
  108. ;; We can't capture macros currently. However we do recognize our own
  109. ;; macros that are actually proxying lexicals, so that nested
  110. ;; `the-environment' forms are possible. In that case we drill down to
  111. ;; the identifier for the already-existing box, and just capture that
  112. ;; box.
  113. ;;
  114. ;; And that's it: we skip displaced lexicals, and the pattern variables
  115. ;; are discussed above.
  116. ;;
  117. (define (analyze-identifiers ids)
  118. (define (mktmp)
  119. (datum->syntax #'here (gensym "t ")))
  120. (let lp ((ids ids) (capture '()) (formals '()) (wrappers '()) (patterns '()))
  121. (cond
  122. ((null? ids)
  123. (values capture formals wrappers patterns))
  124. (else
  125. (let ((id (car ids)) (ids (cdr ids)))
  126. (call-with-values (lambda () (syntax-local-binding id))
  127. (lambda (type val)
  128. (case type
  129. ((lexical)
  130. (if (or-map (lambda (x) (bound-identifier=? x id)) formals)
  131. (lp ids capture formals wrappers patterns)
  132. (let ((t (mktmp)))
  133. (lp ids
  134. (cons #`(make-box #,id) capture)
  135. (cons t formals)
  136. (cons (lambda (x)
  137. #`(let-syntax ((#,id (identifier-syntax-from-box #,t)))
  138. #,x))
  139. wrappers)
  140. patterns))))
  141. ((displaced-lexical)
  142. (lp ids capture formals wrappers patterns))
  143. ((macro)
  144. (let ((b (procedure-property val 'identifier-syntax-box)))
  145. (if b
  146. (lp ids (cons b capture) (cons b formals)
  147. (cons (lambda (x)
  148. #`(let-syntax ((#,id (identifier-syntax-from-box #,b)))
  149. #,x))
  150. wrappers)
  151. patterns)
  152. (lp ids capture formals
  153. (cons (lambda (x)
  154. #`(let-syntax ((#,id (unsupported-binding '#,id)))
  155. #,x))
  156. wrappers)
  157. patterns))))
  158. ((pattern-variable)
  159. (let ((t (datum->syntax id (gensym "p ")))
  160. (nested (within-nested-ellipses id (cdr val))))
  161. (lp ids capture formals
  162. (cons (lambda (x)
  163. #`(with-syntax ((#,t '#,nested))
  164. #,x))
  165. wrappers)
  166. ;; This dance is to hide these pattern variables
  167. ;; from the expander.
  168. (cons (list (datum->syntax #'here (syntax->datum id))
  169. (cdr val)
  170. t)
  171. patterns))))
  172. ((ellipsis)
  173. (lp ids capture formals
  174. (cons (lambda (x)
  175. #`(with-ellipsis #,val #,x))
  176. wrappers)
  177. patterns))
  178. (else
  179. ;; Interestingly, this case can include globals (and
  180. ;; global macros), now that Guile tracks which globals it
  181. ;; introduces. Not sure what to do here! For now, punt.
  182. ;;
  183. (lp ids capture formals wrappers patterns))))))))))
  184. (define-syntax the-environment
  185. (lambda (x)
  186. (syntax-case x ()
  187. ((the-environment)
  188. #'(the-environment the-environment))
  189. ((the-environment scope)
  190. (call-with-values (lambda ()
  191. (analyze-identifiers
  192. (syntax-locally-bound-identifiers #'scope)))
  193. (lambda (capture formals wrappers patterns)
  194. (define (wrap-expression x)
  195. (let lp ((x x) (wrappers wrappers))
  196. (if (null? wrappers)
  197. x
  198. (lp ((car wrappers) x) (cdr wrappers)))))
  199. (with-syntax (((f ...) formals)
  200. ((c ...) capture)
  201. (((pname plvl pformal) ...) patterns)
  202. (wrapped (wrap-expression #'(begin #f exp))))
  203. #'(make-lexical-environment
  204. #'scope
  205. (lambda (exp pformal ...)
  206. (with-syntax ((exp exp)
  207. (pformal pformal)
  208. ...)
  209. #'(lambda (f ...)
  210. wrapped)))
  211. (list c ...)
  212. (list (list 'pname plvl #'pformal) ...)))))))))
  213. (define (env-module e)
  214. (cond
  215. ((lexical-environment? e) (resolve-module (syntax-module (lexenv-scope e))))
  216. ((module? e) e)
  217. (else (error "invalid lexical environment" e))))
  218. (define (env-boxes e)
  219. (cond
  220. ((lexical-environment? e) (lexenv-boxes e))
  221. ((module? e) '())
  222. (else (error "invalid lexical environment" e))))
  223. (define (local-wrap x e)
  224. (cond
  225. ((lexical-environment? e)
  226. (apply (lexenv-wrapper e)
  227. (datum->syntax (lexenv-scope e) x)
  228. (map (lambda (l)
  229. (let ((name (car l))
  230. (lvl (cadr l))
  231. (scope (caddr l)))
  232. (within-nested-ellipses (datum->syntax scope name) lvl)))
  233. (lexenv-patterns e))))
  234. ((module? e) #`(lambda () #f #,x))
  235. (else (error "invalid lexical environment" e))))
  236. (define (local-eval x e)
  237. "Evaluate the expression @var{x} within the lexical environment @var{e}."
  238. (apply (eval (local-wrap x e) (env-module e))
  239. (env-boxes e)))
  240. (define* (local-compile x e #:key (opts '()))
  241. "Compile and evaluate the expression @var{x} within the lexical
  242. environment @var{e}."
  243. (apply (compile (local-wrap x e) #:env (env-module e)
  244. #:from 'scheme #:opts opts)
  245. (env-boxes e)))