merge from guile master
[bpt/guile.git] / ice-9 / syncase.scm
1 ;;;; Copyright (C) 1997, 2000, 2001, 2002, 2003, 2006 Free Software Foundation, Inc.
2 ;;;;
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 2.1 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 ;;;;
17 \f
18
19 (define-module (ice-9 syncase)
20 :use-module (ice-9 debug)
21 :use-module (ice-9 threads)
22 :export-syntax (sc-macro define-syntax define-syntax-public
23 eval-when fluid-let-syntax
24 identifier-syntax let-syntax
25 letrec-syntax syntax syntax-case syntax-rules
26 with-syntax
27 include)
28 :export (sc-expand sc-expand3 install-global-transformer
29 syntax-dispatch syntax-error bound-identifier=?
30 datum->syntax-object free-identifier=?
31 generate-temporaries identifier? syntax-object->datum
32 void syncase)
33 :replace (eval))
34
35 \f
36
37 (define expansion-eval-closure (make-fluid))
38 (define (current-eval-closure)
39 (or (fluid-ref expansion-eval-closure)
40 (module-eval-closure (current-module))))
41
42 (define (env->eval-closure env)
43 (and env (car (last-pair env))))
44
45 (define sc-macro
46 (procedure->memoizing-macro
47 (lambda (exp env)
48 (with-fluids ((expansion-eval-closure (env->eval-closure env)))
49 (sc-expand exp)))))
50
51 ;;; Exported variables
52
53 (define sc-expand #f)
54 (define sc-expand3 #f)
55 (define sc-chi #f)
56 (define install-global-transformer #f)
57 (define syntax-dispatch #f)
58 (define syntax-error #f)
59
60 (define bound-identifier=? #f)
61 (define datum->syntax-object #f)
62 (define free-identifier=? #f)
63 (define generate-temporaries #f)
64 (define identifier? #f)
65 (define syntax-object->datum #f)
66
67 (define primitive-syntax '(quote lambda letrec if set! begin define or
68 and let let* cond do quasiquote unquote
69 unquote-splicing case))
70
71 (for-each (lambda (symbol)
72 (set-symbol-property! symbol 'primitive-syntax #t))
73 primitive-syntax)
74
75 ;;; Hooks needed by the syntax-case macro package
76
77 (define (void) *unspecified*)
78
79 (define andmap
80 (lambda (f first . rest)
81 (or (null? first)
82 (if (null? rest)
83 (let andmap ((first first))
84 (let ((x (car first)) (first (cdr first)))
85 (if (null? first)
86 (f x)
87 (and (f x) (andmap first)))))
88 (let andmap ((first first) (rest rest))
89 (let ((x (car first))
90 (xr (map car rest))
91 (first (cdr first))
92 (rest (map cdr rest)))
93 (if (null? first)
94 (apply f (cons x xr))
95 (and (apply f (cons x xr)) (andmap first rest)))))))))
96
97 (define (error who format-string why what)
98 (start-stack 'syncase-stack
99 (scm-error 'misc-error
100 who
101 "~A ~S"
102 (list why what)
103 '())))
104
105 (define the-syncase-module (current-module))
106 (define the-syncase-eval-closure (module-eval-closure the-syncase-module))
107
108 (fluid-set! expansion-eval-closure the-syncase-eval-closure)
109
110 (define (putprop symbol key binding)
111 (let* ((eval-closure (current-eval-closure))
112 ;; Why not simply do (eval-closure symbol #t)?
113 ;; Answer: That would overwrite imported bindings
114 (v (or (eval-closure symbol #f) ;lookup
115 (eval-closure symbol #t) ;create it locally
116 )))
117 ;; Don't destroy Guile macros corresponding to
118 ;; primitive syntax when syncase boots.
119 (if (not (and (symbol-property symbol 'primitive-syntax)
120 (eq? eval-closure the-syncase-eval-closure)))
121 (variable-set! v sc-macro))
122 ;; Properties are tied to variable objects
123 (set-object-property! v key binding)))
124
125 (define (getprop symbol key)
126 (let* ((v ((current-eval-closure) symbol #f)))
127 (and v
128 (or (object-property v key)
129 (and (variable-bound? v)
130 (macro? (variable-ref v))
131 (macro-transformer (variable-ref v)) ;non-primitive
132 guile-macro)))))
133
134 (define guile-macro
135 (cons 'external-macro
136 (lambda (e r w s)
137 (let ((e (syntax-object->datum e)))
138 (if (symbol? e)
139 ;; pass the expression through
140 e
141 (let* ((eval-closure (current-eval-closure))
142 (m (variable-ref (eval-closure (car e) #f))))
143 (if (eq? (macro-type m) 'syntax)
144 ;; pass the expression through
145 e
146 ;; perform Guile macro transform
147 (let ((e ((macro-transformer m)
148 e
149 (append r (list eval-closure)))))
150 (if (null? r)
151 (sc-expand e)
152 (sc-chi e r w))))))))))
153
154 (define generated-symbols (make-weak-key-hash-table 1019))
155
156 ;; We define our own gensym here because the Guile built-in one will
157 ;; eventually produce uninterned and unreadable symbols (as needed for
158 ;; safe macro expansions) and will the be inappropriate for dumping to
159 ;; pssyntax.pp.
160 ;;
161 ;; syncase is supposed to only require that gensym produce unique
162 ;; readable symbols, and they only need be unique with respect to
163 ;; multiple calls to gensym, not globally unique.
164 ;;
165 (define gensym
166 (let ((counter 0))
167
168 (define next-id
169 (if (provided? 'threads)
170 (let ((symlock (make-mutex)))
171 (lambda ()
172 (let ((result #f))
173 (with-mutex symlock
174 (set! result counter)
175 (set! counter (+ counter 1)))
176 result)))
177 ;; faster, non-threaded case.
178 (lambda ()
179 (let ((result counter))
180 (set! counter (+ counter 1))
181 result))))
182
183 ;; actual gensym body code.
184 (lambda (. rest)
185 (let* ((next-val (next-id))
186 (valstr (number->string next-val)))
187 (cond
188 ((null? rest)
189 (string->symbol (string-append "syntmp-" valstr)))
190 ((null? (cdr rest))
191 (string->symbol (string-append "syntmp-" (car rest) "-" valstr)))
192 (else
193 (error
194 (string-append
195 "syncase's gensym expected 0 or 1 arguments, got "
196 (length rest)))))))))
197
198 ;;; Load the preprocessed code
199
200 (let ((old-debug #f)
201 (old-read #f))
202 (dynamic-wind (lambda ()
203 (set! old-debug (debug-options))
204 (set! old-read (read-options)))
205 (lambda ()
206 (debug-disable 'debug 'procnames)
207 (read-disable 'positions)
208 (load-from-path "ice-9/psyntax.pp"))
209 (lambda ()
210 (debug-options old-debug)
211 (read-options old-read))))
212
213
214 ;;; The following lines are necessary only if we start making changes
215 ;; (use-syntax sc-expand)
216 ;; (load-from-path "ice-9/psyntax.ss")
217
218 (define internal-eval (nested-ref the-scm-module '(%app modules guile eval)))
219
220 (define (eval x environment)
221 (internal-eval (if (and (pair? x)
222 (equal? (car x) "noexpand"))
223 (cadr x)
224 (sc-expand x))
225 environment))
226
227 ;;; Hack to make syncase macros work in the slib module
228 (let ((m (nested-ref the-root-module '(%app modules ice-9 slib))))
229 (if m
230 (set-object-property! (module-local-variable m 'define)
231 '*sc-expander*
232 '(define))))
233
234 (define (syncase exp)
235 (with-fluids ((expansion-eval-closure
236 (module-eval-closure (current-module))))
237 (sc-expand exp)))
238
239 (set-module-transformer! the-syncase-module syncase)
240
241 (define-syntax define-syntax-public
242 (syntax-rules ()
243 ((_ name rules ...)
244 (begin
245 ;(eval-case ((load-toplevel) (export-syntax name)))
246 (define-syntax name rules ...)))))
247
248 (fluid-set! expansion-eval-closure #f)