~ chicken-core (master) /synrules.scm
Trap1;; Copyright (c) 1993-2001 by Richard Kelsey and Jonathan Rees.
2;; All rights reserved.
3
4;; Redistribution and use in source and binary forms, with or without
5;; modification, are permitted provided that the following conditions
6;; are met:
7;; 1. Redistributions of source code must retain the above copyright
8;; notice, this list of conditions and the following disclaimer.
9;; 2. Redistributions in binary form must reproduce the above copyright
10;; notice, this list of conditions and the following disclaimer in the
11;; documentation and/or other materials provided with the distribution.
12;; 3. The name of the authors may not be used to endorse or promote products
13;; derived from this software without specific prior written permission.
14
15;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
16;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26;;; [Hacked slightly by Taylor R. Campbell to make it work in his
27;;; macro expander `riaxpander'.]
28
29;; [Hacked even more by Felix L. Winkelmann to make it work in the
30;; Hi-Lo expander]
31
32; Example:
33;
34; (define-syntax or
35; (syntax-rules ()
36; ((or) #f)
37; ((or e) e)
38; ((or e1 e ...) (let ((temp e1))
39; (if temp temp (or e ...))))))
40
41(##sys#extend-macro-environment
42 'syntax-rules
43 '()
44 (##sys#er-transformer
45 (lambda (exp r c)
46 (##sys#check-syntax 'syntax-rules exp '#(_ 2))
47 (let ((subkeywords (cadr exp))
48 (rules (cddr exp))
49 (ellipsis '...))
50 (when (symbol? subkeywords)
51 (##sys#check-syntax 'syntax-rules exp '(_ _ list . #(_ 0)))
52 (set! ellipsis subkeywords)
53 (set! subkeywords (car rules))
54 (set! rules (cdr rules)))
55 (chicken.internal.syntax-rules#process-syntax-rules
56 ellipsis rules subkeywords r c #t)))))
57
58;; Runtime internal support module exclusively for syntax-rules
59(module chicken.internal.syntax-rules
60 (drop-right take-right safe-length syntax-rules-mismatch)
61
62(import scheme)
63
64(define (syntax-rules-mismatch input)
65 (##sys#syntax-error "no rule matches form" input))
66
67(define (drop-right input temp)
68 (let loop ((len (safe-length input))
69 (input input))
70 (cond
71 ((> len temp)
72 (cons (car input)
73 (loop (- len 1) (cdr input))))
74 (else '()))))
75
76(define (take-right input temp)
77 (let loop ((len (safe-length input))
78 (input input))
79 (cond
80 ((> len temp)
81 (loop (- len 1) (cdr input)))
82 (else input))))
83
84(define (safe-length lst)
85 (let loop ((lst lst) (len 0))
86 (if (pair? lst)
87 (loop (cdr lst) (+ 1 len))
88 len)))
89
90(define (process-syntax-rules ellipsis rules subkeywords r c r7ext)
91
92 (define %append '##sys#append)
93 (define %apply '##sys#apply)
94 (define %and (r 'and))
95 (define %car '##sys#car)
96 (define %cdr '##sys#cdr)
97 (define %length '##sys#length)
98 (define %vector? '##sys#vector?)
99 (define %vector-length '##sys#vector-length)
100 (define %vector-ref '##sys#vector-ref)
101 (define %vector->list '##sys#vector->list)
102 (define %list->vector '##sys#list->vector)
103 (define %>= '##sys#>=)
104 (define %= '##sys#=)
105 (define %+ '##sys#+)
106 (define %compare (r 'compare))
107 (define %cond (r 'cond))
108 (define %cons '##sys#cons)
109 (define %else (r 'else))
110 (define %eq? '##sys#eq?)
111 (define %equal? '##sys#equal?)
112 (define %input (r 'input))
113 (define %l (r 'l))
114 (define %len (r 'len))
115 (define %lambda (r 'lambda))
116 (define %let (r 'let))
117 (define %let* (r 'let*))
118 (define %loop (r 'loop))
119 (define %map1 '##sys#map)
120 (define %map '##sys#map-n)
121 (define %null? '##sys#null?)
122 (define %or (r 'or))
123 (define %pair? '##sys#pair?)
124 (define %quote (r 'quote))
125 (define %rename (r 'rename))
126 (define %tail (r 'tail))
127 (define %temp (r 'temp))
128 (define %syntax-error '##sys#syntax-error)
129 (define %ellipsis (r ellipsis))
130 (define %safe-length (r 'chicken.internal.syntax-rules#safe-length))
131 (define %take-right (r 'chicken.internal.syntax-rules#take-right))
132 (define %drop-right (r 'chicken.internal.syntax-rules#drop-right))
133 (define %syntax-rules-mismatch
134 (r 'chicken.internal.syntax-rules#syntax-rules-mismatch))
135
136 (define (ellipsis? x)
137 (c x %ellipsis))
138
139 ;; R7RS support: underscore matches anything
140 (define (underscore? x)
141 (c x (r '_)))
142
143 (define (make-transformer rules)
144 `(##sys#er-transformer
145 (,%lambda (,%input ,%rename ,%compare)
146 (,%let ((,%tail (,%cdr ,%input)))
147 (,%cond ,@(map process-rule rules)
148 (,%else (,%syntax-rules-mismatch ,%input)))))))
149
150 (define (process-rule rule)
151 (if (and (pair? rule)
152 (pair? (cdr rule))
153 (null? (cddr rule)))
154 (let ((pattern (cdar rule))
155 (template (cadr rule)))
156 `((,%and ,@(process-match %tail pattern #f ellipsis?))
157 (,%let* ,(process-pattern pattern
158 %tail
159 (lambda (x) x) #f ellipsis?)
160 ,(process-template template
161 0 ellipsis?
162 (meta-variables pattern 0 ellipsis? '() #f)))))
163 (##sys#syntax-error "ill-formed syntax rule" rule)))
164
165 ;; Generate code to test whether input expression matches pattern
166
167 (define (process-match input pattern seen-segment? el?)
168 (cond ((symbol? pattern)
169 (if (memq pattern subkeywords)
170 `((,%compare ,input (,%rename (##core#syntax ,pattern))))
171 `()))
172 ((segment-pattern? pattern seen-segment? el?)
173 (process-segment-match input pattern el?))
174 ((pair? pattern)
175 `((,%let ((,%temp ,input))
176 (,%and (,%pair? ,%temp)
177 ,@(process-match `(,%car ,%temp) (car pattern) #f el?)
178 ,@(process-match `(,%cdr ,%temp) (cdr pattern) #f el?)))))
179 ((vector? pattern)
180 `((,%let ((,%temp ,input))
181 (,%and (,%vector? ,%temp)
182 ,@(process-match `(,%vector->list ,%temp)
183 (vector->list pattern) #f el?)))))
184 ((or (null? pattern) (boolean? pattern) (char? pattern))
185 `((,%eq? ,input ',pattern)))
186 (else
187 `((,%equal? ,input ',pattern)))))
188
189 (define (process-segment-match input pattern el?)
190 (let ((conjuncts (process-match `(,%car ,%l) (car pattern) #f el?))
191 (plen (safe-length (cddr pattern))))
192 `((,%let ((,%len (,%safe-length ,input)))
193 (,%and (,%>= ,%len ,plen)
194 (,%let ,%loop ((,%l ,input)
195 (,%len ,%len))
196 (,%cond ((,%= ,%len ,plen)
197 ,@(process-match %l (cddr pattern) #t el?))
198 (,%else (,%and ,@conjuncts
199 (,%loop (,%cdr ,%l)
200 (,%+ ,%len -1)))))))))))
201
202 ;; Generate code to take apart the input expression
203 ;; This is pretty bad, but it seems to work (can't say why).
204
205 (define (process-pattern pattern path mapit seen-segment? el?)
206 (cond ((symbol? pattern)
207 (if (or (memq pattern subkeywords)
208 (and r7ext (underscore? pattern)))
209 '()
210 (list (list pattern (mapit path)))))
211 ((segment-pattern? pattern seen-segment? el?)
212 (let* ((tail-length (safe-length (cddr pattern)))
213 (%match (if (and (list? (cddr pattern))
214 (zero? tail-length) ) ; Simple segment?
215 path ; No list traversing overhead at runtime!
216 `(,%drop-right ,path ,tail-length))))
217 (append
218 (process-pattern (car pattern)
219 %temp
220 (lambda (x) ;temp is free in x
221 (mapit
222 (if (eq? %temp x)
223 %match ; Optimization: no map+lambda
224 `(,%map1 (,%lambda (,%temp) ,x) ,%match))))
225 #f el?)
226 (process-pattern (cddr pattern)
227 `(,%take-right ,path ,tail-length)
228 mapit #t el?))))
229 ((pair? pattern)
230 (append (process-pattern (car pattern) `(,%car ,path) mapit #f el?)
231 (process-pattern (cdr pattern) `(,%cdr ,path) mapit #f el?)))
232 ((vector? pattern)
233 (process-pattern (vector->list pattern)
234 `(,%vector->list ,path)
235 mapit #f el?))
236 (else '())))
237
238 ;; Generate code to compose the output expression according to template
239
240 (define (process-template template dim el? env)
241 (cond ((symbol? template)
242 (let ((probe (assq template env)))
243 (if probe
244 (if (<= (cdr probe) dim)
245 template
246 (##sys#syntax-error "template dimension error (too few ellipses?)"
247 template))
248 `(,%rename (##core#syntax ,template)))))
249 ((and r7ext
250 (ellipsis-escaped-pattern? template el?))
251 (if (or (not (pair? (cdr template))) (pair? (cddr template)))
252 (##sys#syntax-error "Invalid escaped ellipsis template" template)
253 (process-template (cadr template) dim (lambda _ #f) env)))
254 ((segment-template? template el?)
255 (let* ((depth (segment-depth template el?))
256 (seg-dim (+ dim depth))
257 (vars
258 (free-meta-variables (car template) seg-dim el? env '())))
259 (if (null? vars)
260 (##sys#syntax-error "too many ellipses" template)
261 (let* ((x (process-template (car template)
262 seg-dim el?
263 env))
264 (gen (if (and (pair? vars)
265 (null? (cdr vars))
266 (symbol? x)
267 (eq? x (car vars)))
268 x ;+++
269 `(,%map (,%lambda ,vars ,x)
270 ,@vars)))
271 (gen (do ((d depth (- d 1))
272 (gen gen `(,%apply ,%append ,gen)))
273 ((= d 1)
274 gen))))
275 (if (null? (segment-tail template el?))
276 gen ;+++
277 `(,%append ,gen ,(process-template (segment-tail template el?)
278 dim el? env)))))))
279 ((pair? template)
280 `(,%cons ,(process-template (car template) dim el? env)
281 ,(process-template (cdr template) dim el? env)))
282 ((vector? template)
283 `(,%list->vector
284 ,(process-template (vector->list template) dim el? env)))
285 (else
286 `(,%quote ,template))))
287
288 ;; Return an association list of (var . dim)
289
290 (define (meta-variables pattern dim el? vars seen-segment?)
291 (cond ((symbol? pattern)
292 (if (or (memq pattern subkeywords)
293 (and r7ext (underscore? pattern)))
294 vars
295 (cons (cons pattern dim) vars)))
296 ((segment-pattern? pattern seen-segment? el?)
297 (meta-variables (car pattern) (+ dim 1) el?
298 (meta-variables (cddr pattern) dim el? vars #t) #f))
299 ((pair? pattern)
300 (meta-variables (car pattern) dim el?
301 (meta-variables (cdr pattern) dim el? vars #f) #f))
302 ((vector? pattern)
303 (meta-variables (vector->list pattern) dim el? vars #f))
304 (else vars)))
305
306 ;; Return a list of meta-variables of given higher dim
307
308 (define (free-meta-variables template dim el? env free)
309 (cond ((symbol? template)
310 (if (and (not (memq template free))
311 (let ((probe (assq template env)))
312 (and probe (>= (cdr probe) dim))))
313 (cons template free)
314 free))
315 ((segment-template? template el?)
316 (free-meta-variables (car template)
317 dim el? env
318 (free-meta-variables (cddr template)
319 dim el? env free)))
320 ((pair? template)
321 (free-meta-variables (car template)
322 dim el? env
323 (free-meta-variables (cdr template)
324 dim el? env free)))
325 ((vector? template)
326 (free-meta-variables (vector->list template) dim el? env free))
327 (else free)))
328
329 (define (ellipsis-escaped-pattern? pattern el?)
330 (and (pair? pattern) (el? (car pattern))))
331
332 (define (segment-pattern? p seen-segment? el?)
333 (and (segment-template? p el?)
334 (if seen-segment?
335 (##sys#syntax-error "Only one segment per level is allowed" p)
336 #t)))
337
338 (define (segment-template? pattern el?)
339 (and (pair? pattern)
340 (pair? (cdr pattern))
341 ((if r7ext el? ellipsis?) (cadr pattern))))
342
343 ;; Count the number of `...'s in PATTERN.
344
345 (define (segment-depth pattern el?)
346 (if (segment-template? pattern el?)
347 (+ 1 (segment-depth (cdr pattern) el?))
348 0))
349
350 ;; Get whatever is after the `...'s in PATTERN.
351
352 (define (segment-tail pattern el?)
353 (let loop ((pattern (cdr pattern)))
354 (if (and (pair? pattern)
355 ((if r7ext el? ellipsis?) (car pattern)))
356 (loop (cdr pattern))
357 pattern)))
358
359 (make-transformer rules))
360
361) ; chicken.internal.syntax-rules