~ chicken-r7rs (master) /synrules.scm


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