* boot-9.scm (duplicate-handlers): Make sure the merge-generics
[bpt/guile.git] / ice-9 / syncase.scm
CommitLineData
7906d57d 1;;;; Copyright (C) 1997, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
a63812a2
JB
2;;;;
3;;;; This program is free software; you can redistribute it and/or modify
4;;;; it under the terms of the GNU General Public License as published by
5;;;; the Free Software Foundation; either version 2, or (at your option)
6;;;; any later version.
7;;;;
8;;;; This program 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
11;;;; GNU General Public License for more details.
12;;;;
13;;;; You should have received a copy of the GNU General Public License
14;;;; along with this software; see the file COPYING. If not, write to
c6e23ea2
JB
15;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16;;;; Boston, MA 02111-1307 USA
a482f2cc
MV
17;;;;
18;;;; As a special exception, the Free Software Foundation gives permission
19;;;; for additional uses of the text contained in its release of GUILE.
20;;;;
21;;;; The exception is that, if you link the GUILE library with other files
22;;;; to produce an executable, this does not by itself cause the
23;;;; resulting executable to be covered by the GNU General Public License.
24;;;; Your use of that executable is in no way restricted on account of
25;;;; linking the GUILE library code into it.
26;;;;
27;;;; This exception does not however invalidate any other reasons why
28;;;; the executable file might be covered by the GNU General Public License.
29;;;;
30;;;; This exception applies only to the code released by the
31;;;; Free Software Foundation under the name GUILE. If you copy
32;;;; code from other Free Software Foundation releases into a copy of
33;;;; GUILE, as the General Public License permits, the exception does
34;;;; not apply to the code that you add in this way. To avoid misleading
35;;;; anyone as to the status of such modified files, you must delete
36;;;; this exception notice from them.
37;;;;
38;;;; If you write modifications of your own for GUILE, it is your choice
39;;;; whether to permit this exception to apply to your modifications.
40;;;; If you do not wish that, delete this exception notice.
a63812a2
JB
41;;;;
42\f
43
44(define-module (ice-9 syncase)
1a179b03 45 :use-module (ice-9 debug)
5f1bde67 46 :use-module (ice-9 threads)
719fb3f3
MV
47 :export-syntax (sc-macro define-syntax define-syntax-public
48 eval-when fluid-let-syntax
1a179b03
MD
49 identifier-syntax let-syntax
50 letrec-syntax syntax syntax-case syntax-rules
51 with-syntax
52 include)
53 :export (sc-expand sc-expand3 install-global-transformer
54 syntax-dispatch syntax-error bound-identifier=?
55 datum->syntax-object free-identifier=?
56 generate-temporaries identifier? syntax-object->datum
d57da08b
MD
57 void syncase)
58 :replace (eval))
a63812a2
JB
59
60\f
61
db3f1c7e
MV
62(define expansion-eval-closure (make-fluid))
63
64(define (env->eval-closure env)
65 (or (and env
66 (car (last-pair env)))
67 (module-eval-closure the-root-module)))
68
1a179b03 69(define sc-macro
a63812a2
JB
70 (procedure->memoizing-macro
71 (lambda (exp env)
db3f1c7e
MV
72 (with-fluids ((expansion-eval-closure (env->eval-closure env)))
73 (sc-expand exp)))))
74
a63812a2
JB
75;;; Exported variables
76
1a179b03
MD
77(define sc-expand #f)
78(define sc-expand3 #f)
80f225df 79(define sc-chi #f)
1a179b03
MD
80(define install-global-transformer #f)
81(define syntax-dispatch #f)
82(define syntax-error #f)
83
84(define bound-identifier=? #f)
85(define datum->syntax-object #f)
1a179b03
MD
86(define free-identifier=? #f)
87(define generate-temporaries #f)
88(define identifier? #f)
1a179b03 89(define syntax-object->datum #f)
c55bcb32 90
a63812a2 91(define primitive-syntax '(quote lambda letrec if set! begin define or
719fb3f3
MV
92 and let let* cond do quasiquote unquote
93 unquote-splicing case))
a63812a2
JB
94
95(for-each (lambda (symbol)
96 (set-symbol-property! symbol 'primitive-syntax #t))
97 primitive-syntax)
98
99;;; Hooks needed by the syntax-case macro package
100
1a179b03 101(define (void) *unspecified*)
a63812a2
JB
102
103(define andmap
104 (lambda (f first . rest)
105 (or (null? first)
106 (if (null? rest)
107 (let andmap ((first first))
108 (let ((x (car first)) (first (cdr first)))
109 (if (null? first)
110 (f x)
111 (and (f x) (andmap first)))))
112 (let andmap ((first first) (rest rest))
113 (let ((x (car first))
114 (xr (map car rest))
115 (first (cdr first))
116 (rest (map cdr rest)))
117 (if (null? first)
118 (apply f (cons x xr))
119 (and (apply f (cons x xr)) (andmap first rest)))))))))
120
121(define (error who format-string why what)
122 (start-stack 'syncase-stack
123 (scm-error 'misc-error
124 who
8641dd9e 125 "~A ~S"
a63812a2
JB
126 (list why what)
127 '())))
128
129(define the-syncase-module (current-module))
db3f1c7e 130(define the-syncase-eval-closure (module-eval-closure the-syncase-module))
a63812a2 131
7906d57d
MD
132(fluid-set! expansion-eval-closure the-syncase-eval-closure)
133
a63812a2 134(define (putprop symbol key binding)
80f225df
MD
135 (let* ((eval-closure (fluid-ref expansion-eval-closure))
136 ;; Why not simply do (eval-closure symbol #t)?
137 ;; Answer: That would overwrite imported bindings
138 (v (or (eval-closure symbol #f) ;lookup
139 (eval-closure symbol #t) ;create it locally
140 )))
141 ;; Don't destroy Guile macros corresponding to
142 ;; primitive syntax when syncase boots.
143 (if (not (and (symbol-property symbol 'primitive-syntax)
144 (eq? eval-closure the-syncase-eval-closure)))
a63812a2 145 (variable-set! v sc-macro))
80f225df 146 ;; Properties are tied to variable objects
a63812a2
JB
147 (set-object-property! v key binding)))
148
149(define (getprop symbol key)
db3f1c7e 150 (let* ((v ((fluid-ref expansion-eval-closure) symbol #f)))
80f225df
MD
151 (and v
152 (or (object-property v key)
153 (and (variable-bound? v)
154 (macro? (variable-ref v))
155 (macro-transformer (variable-ref v)) ;non-primitive
156 guile-macro)))))
157
158(define guile-macro
159 (cons 'external-macro
160 (lambda (e r w s)
e963ac2c
MD
161 (let ((e (syntax-object->datum e)))
162 (if (symbol? e)
163 ;; pass the expression through
164 e
165 (let* ((eval-closure (fluid-ref expansion-eval-closure))
166 (m (variable-ref (eval-closure (car e) #f))))
167 (if (eq? (macro-type m) 'syntax)
168 ;; pass the expression through
169 e
170 ;; perform Guile macro transform
171 (let ((e ((macro-transformer m)
172 e
173 (append r (list eval-closure)))))
174 (if (null? r)
175 (sc-expand e)
176 (sc-chi e r w))))))))))
a63812a2
JB
177
178(define generated-symbols (make-weak-key-hash-table 1019))
179
51a317b3
RB
180;; We define our own gensym here because the Guile built-in one will
181;; eventually produce uninterned and unreadable symbols (as needed for
182;; safe macro expansions) and will the be inappropriate for dumping to
183;; pssyntax.pp.
184;;
185;; syncase is supposed to only require that gensym produce unique
186;; readable symbols, and they only need be unique with respect to
187;; multiple calls to gensym, not globally unique.
188;;
189(define gensym
190 (let ((counter 0))
56fd1933
RB
191
192 (define next-id
193 (if (provided? 'threads)
194 (let ((symlock (make-mutex)))
195 (lambda ()
196 (let ((result #f))
197 (with-mutex symlock
198 (set! result counter)
199 (set! counter (+ counter 1)))
200 result)))
201 ;; faster, non-threaded case.
202 (lambda ()
203 (let ((result counter))
204 (set! counter (+ counter 1))
205 result))))
206
207 ;; actual gensym body code.
51a317b3 208 (lambda (. rest)
56fd1933
RB
209 (let* ((next-val (next-id))
210 (valstr (number->string next-val)))
211 (cond
212 ((null? rest)
213 (string->symbol (string-append "syntmp-" valstr)))
214 ((null? (cdr rest))
215 (string->symbol (string-append "syntmp-" (car rest) "-" valstr)))
216 (else
217 (error
218 (string-append
219 "syncase's gensym expected 0 or 1 arguments, got "
220 (length rest)))))))))
51a317b3 221
a63812a2
JB
222;;; Load the preprocessed code
223
224(let ((old-debug #f)
225 (old-read #f))
226 (dynamic-wind (lambda ()
227 (set! old-debug (debug-options))
228 (set! old-read (read-options)))
229 (lambda ()
230 (debug-disable 'debug 'procnames)
231 (read-disable 'positions)
232 (load-from-path "ice-9/psyntax.pp"))
233 (lambda ()
234 (debug-options old-debug)
235 (read-options old-read))))
236
237
4be092bc
MD
238;;; The following lines are necessary only if we start making changes
239;; (use-syntax sc-expand)
a63812a2
JB
240;; (load-from-path "ice-9/psyntax.ss")
241
242(define internal-eval (nested-ref the-scm-module '(app modules guile eval)))
243
1a179b03 244(define (eval x environment)
a63812a2 245 (internal-eval (if (and (pair? x)
f304437e 246 (equal? (car x) "noexpand"))
a63812a2 247 (cadr x)
6232c3dd
MD
248 (sc-expand x))
249 environment))
a63812a2
JB
250
251;;; Hack to make syncase macros work in the slib module
252(let ((m (nested-ref the-root-module '(app modules ice-9 slib))))
253 (if m
254 (set-object-property! (module-local-variable m 'define)
255 '*sc-expander*
256 '(define))))
257
cf743aea
MD
258(define (syncase exp)
259 (with-fluids ((expansion-eval-closure
260 (module-eval-closure (current-module))))
261 (sc-expand exp)))
719fb3f3
MV
262
263(set-module-transformer! the-syncase-module syncase)
264
265(define-syntax define-syntax-public
266 (syntax-rules ()
267 ((_ name rules ...)
268 (begin
269 ;(eval-case ((load-toplevel) (export-syntax name)))
270 (define-syntax name rules ...)))))
7906d57d
MD
271
272(fluid-set! expansion-eval-closure (env->eval-closure #f))