~ chicken-core (chicken-5) /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; The syntax-rules macro (new in R5RS)
27
28;;; [Hacked slightly by Taylor R. Campbell to make it work in his
29;;; macro expander `riaxpander'.]
30
31;; [Hacked even more by Felix L. Winkelmann to make it work in his
32;; Hi-Lo expander]
33
34; Example:
35;
36; (define-syntax or
37; (syntax-rules ()
38; ((or) #f)
39; ((or e) e)
40; ((or e1 e ...) (let ((temp e1))
41; (if temp temp (or e ...))))))
42
43(##sys#extend-macro-environment
44 'syntax-rules
45 '()
46 (##sys#er-transformer
47 (lambda (exp r c)
48 (##sys#check-syntax 'syntax-rules exp '#(_ 2))
49 (let ((subkeywords (cadr exp))
50 (rules (cddr exp))
51 (ellipsis '...))
52 (when (symbol? subkeywords)
53 (##sys#check-syntax 'syntax-rules exp '(_ _ list . #(_ 0)))
54 (set! ellipsis subkeywords)
55 (set! subkeywords (car rules))
56 (set! rules (cdr rules)))
57 (chicken.internal.syntax-rules#process-syntax-rules
58 ellipsis rules subkeywords r c)))))
59
60
61;; Runtime internal support module exclusively for syntax-rules
62(module chicken.internal.syntax-rules
63 (drop-right take-right syntax-rules-mismatch)
64
65(import scheme)
66
67(define (syntax-rules-mismatch input)
68 (##sys#syntax-error-hook "no rule matches form" input))
69
70(define (drop-right input temp)
71 ;;XXX use unsafe accessors
72 (let loop ((len (length input))
73 (input input))
74 (cond
75 ((> len temp)
76 (cons (car input)
77 (loop (- len 1) (cdr input))))
78 (else '()))))
79
80(define (take-right input temp)
81 ;;XXX use unsafe accessors
82 (let loop ((len (length input))
83 (input input))
84 (cond
85 ((> len temp)
86 (loop (- len 1) (cdr input)))
87 (else input))))
88
89(define (process-syntax-rules ellipsis rules subkeywords r c)
90
91 (define %append '##sys#append)
92 (define %apply '##sys#apply)
93 (define %and (r 'and))
94 (define %car '##sys#car)
95 (define %cdr '##sys#cdr)
96 (define %length '##sys#length)
97 (define %vector? '##sys#vector?)
98 (define %vector-length '##sys#vector-length)
99 (define %vector-ref '##sys#vector-ref)
100 (define %vector->list '##sys#vector->list)
101 (define %list->vector '##sys#list->vector)
102 (define %>= '##sys#>=)
103 (define %= '##sys#=)
104 (define %+ '##sys#+)
105 (define %compare (r 'compare))
106 (define %cond (r 'cond))
107 (define %cons '##sys#cons)
108 (define %else (r 'else))
109 (define %eq? '##sys#eq?)
110 (define %equal? '##sys#equal?)
111 (define %input (r 'input))
112 (define %l (r 'l))
113 (define %len (r 'len))
114 (define %lambda (r 'lambda))
115 (define %let (r 'let))
116 (define %let* (r 'let*))
117 (define %list? '##sys#list?)
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-hook)
129 (define %ellipsis (r ellipsis))
130 (define %take-right (r 'chicken.internal.syntax-rules#take-right))
131 (define %drop-right (r 'chicken.internal.syntax-rules#drop-right))
132 (define %syntax-rules-mismatch
133 (r 'chicken.internal.syntax-rules#syntax-rules-mismatch))
134
135 (define (ellipsis? x)
136 (c x %ellipsis))
137
138 (define (make-transformer rules)
139 `(##sys#er-transformer
140 (,%lambda (,%input ,%rename ,%compare)
141 (,%let ((,%tail (,%cdr ,%input)))
142 (,%cond ,@(map process-rule rules)
143 (,%else (,%syntax-rules-mismatch ,%input)))))))
144
145 (define (process-rule rule)
146 (if (and (pair? rule)
147 (pair? (cdr rule))
148 (null? (cddr rule)))
149 (let ((pattern (cdar rule))
150 (template (cadr rule)))
151 `((,%and ,@(process-match %tail pattern #f))
152 (,%let* ,(process-pattern pattern
153 %tail
154 (lambda (x) x) #f)
155 ,(process-template template
156 0
157 (meta-variables pattern 0 '() #f)))))
158 (##sys#syntax-error-hook "ill-formed syntax rule" rule)))
159
160 ;; Generate code to test whether input expression matches pattern
161
162 (define (process-match input pattern seen-segment?)
163 (cond ((symbol? pattern)
164 (if (memq pattern subkeywords)
165 `((,%compare ,input (,%rename (##core#syntax ,pattern))))
166 `()))
167 ((segment-pattern? pattern seen-segment?)
168 (process-segment-match input pattern))
169 ((pair? pattern)
170 `((,%let ((,%temp ,input))
171 (,%and (,%pair? ,%temp)
172 ,@(process-match `(,%car ,%temp) (car pattern) #f)
173 ,@(process-match `(,%cdr ,%temp) (cdr pattern) #f)))))
174 ((vector? pattern)
175 `((,%let ((,%temp ,input))
176 (,%and (,%vector? ,%temp)
177 ,@(process-match `(,%vector->list ,%temp)
178 (vector->list pattern) #f)))))
179 ((or (null? pattern) (boolean? pattern) (char? pattern))
180 `((,%eq? ,input ',pattern)))
181 (else
182 `((,%equal? ,input ',pattern)))))
183
184 (define (process-segment-match input pattern)
185 (let ((conjuncts (process-match `(,%car ,%l) (car pattern) #f)))
186 `((,%and (,%list? ,input) ; Can't ask for its length if not a proper list
187 (,%let ((,%len (,%length ,input)))
188 (,%and (,%>= ,%len ,(length (cddr pattern)))
189 (,%let ,%loop ((,%l ,input)
190 (,%len ,%len))
191 (,%cond
192 ((,%= ,%len ,(length (cddr pattern)))
193 ,@(process-match %l (cddr pattern) #t))
194 (,%else
195 (,%and ,@conjuncts
196 (,%loop (,%cdr ,%l) (,%+ ,%len -1))))))))))))
197
198 ;; Generate code to take apart the input expression
199 ;; This is pretty bad, but it seems to work (can't say why).
200
201 (define (process-pattern pattern path mapit seen-segment?)
202 (cond ((symbol? pattern)
203 (if (memq pattern subkeywords)
204 '()
205 (list (list pattern (mapit path)))))
206 ((segment-pattern? pattern seen-segment?)
207 (let* ((tail-length (length (cddr pattern)))
208 (%match (if (zero? tail-length) ; Simple segment?
209 path ; No list traversing overhead at runtime!
210 `(,%drop-right ,path ,tail-length))))
211 (append
212 (process-pattern (car pattern)
213 %temp
214 (lambda (x) ;temp is free in x
215 (mapit
216 (if (eq? %temp x)
217 %match ; Optimization: no map+lambda
218 `(,%map1 (,%lambda (,%temp) ,x) ,%match))))
219 #f)
220 (process-pattern (cddr pattern)
221 `(,%take-right ,path ,tail-length) mapit #t))))
222 ((pair? pattern)
223 (append (process-pattern (car pattern) `(,%car ,path) mapit #f)
224 (process-pattern (cdr pattern) `(,%cdr ,path) mapit #f)))
225 ((vector? pattern)
226 (process-pattern (vector->list pattern)
227 `(,%vector->list ,path) mapit #f))
228 (else '())))
229
230 ;; Generate code to compose the output expression according to template
231
232 (define (process-template template dim env)
233 (cond ((symbol? template)
234 (let ((probe (assq template env)))
235 (if probe
236 (if (<= (cdr probe) dim)
237 template
238 (##sys#syntax-error-hook "template dimension error (too few ellipses?)"
239 template))
240 `(,%rename (##core#syntax ,template)))))
241 ((segment-template? template)
242 (let* ((depth (segment-depth template))
243 (seg-dim (+ dim depth))
244 (vars
245 (free-meta-variables (car template) seg-dim env '())))
246 (if (null? vars)
247 (##sys#syntax-error-hook "too many ellipses" template)
248 (let* ((x (process-template (car template)
249 seg-dim
250 env))
251 (gen (if (and (pair? vars)
252 (null? (cdr vars))
253 (symbol? x)
254 (eq? x (car vars)))
255 x ;+++
256 `(,%map (,%lambda ,vars ,x)
257 ,@vars)))
258 (gen (do ((d depth (- d 1))
259 (gen gen `(,%apply ,%append ,gen)))
260 ((= d 1)
261 gen))))
262 (if (null? (segment-tail template))
263 gen ;+++
264 `(,%append ,gen ,(process-template (segment-tail template)
265 dim env)))))))
266 ((pair? template)
267 `(,%cons ,(process-template (car template) dim env)
268 ,(process-template (cdr template) dim env)))
269 ((vector? template)
270 `(,%list->vector
271 ,(process-template (vector->list template) dim env)))
272 (else
273 `(,%quote ,template))))
274
275 ;; Return an association list of (var . dim)
276
277 (define (meta-variables pattern dim vars seen-segment?)
278 (cond ((symbol? pattern)
279 (if (memq pattern subkeywords)
280 vars
281 (cons (cons pattern dim) vars)))
282 ((segment-pattern? pattern seen-segment?)
283 (meta-variables (car pattern) (+ dim 1)
284 (meta-variables (cddr pattern) dim vars #t) #f))
285 ((pair? pattern)
286 (meta-variables (car pattern) dim
287 (meta-variables (cdr pattern) dim vars #f) #f))
288 ((vector? pattern)
289 (meta-variables (vector->list pattern) dim vars #f))
290 (else vars)))
291
292 ;; Return a list of meta-variables of given higher dim
293
294 (define (free-meta-variables template dim env free)
295 (cond ((symbol? template)
296 (if (and (not (memq template free))
297 (let ((probe (assq template env)))
298 (and probe (>= (cdr probe) dim))))
299 (cons template free)
300 free))
301 ((segment-template? template)
302 (free-meta-variables (car template)
303 dim env
304 (free-meta-variables (cddr template)
305 dim env free)))
306 ((pair? template)
307 (free-meta-variables (car template)
308 dim env
309 (free-meta-variables (cdr template)
310 dim env free)))
311 ((vector? template)
312 (free-meta-variables (vector->list template) dim env free))
313 (else free)))
314
315 (define (segment-pattern? p seen-segment?)
316 (and (segment-template? p)
317 (cond
318 (seen-segment?
319 (##sys#syntax-error-hook "Only one segment per level is allowed" p))
320 ((not (list? p)) ; Improper list
321 (##sys#syntax-error-hook "Cannot combine dotted tail and ellipsis" p))
322 (else #t))))
323
324 (define (segment-template? pattern)
325 (and (pair? pattern)
326 (pair? (cdr pattern))
327 (ellipsis? (cadr pattern))))
328
329 ;; Count the number of `...'s in PATTERN.
330
331 (define (segment-depth pattern)
332 (if (segment-template? pattern)
333 (+ 1 (segment-depth (cdr pattern)))
334 0))
335
336 ;; Get whatever is after the `...'s in PATTERN.
337
338 (define (segment-tail pattern)
339 (let loop ((pattern (cdr pattern)))
340 (if (and (pair? pattern)
341 (ellipsis? (car pattern)))
342 (loop (cdr pattern))
343 pattern)))
344
345 (make-transformer rules))
346
347) ; chicken.internal.syntax-rules