~ chicken-core (master) /synrules.scm
Trap1;; Copyright (c) 1993-2001 by Richard Kelsey and Jonathan Rees.2;; All rights reserved.34;; Redistribution and use in source and binary forms, with or without5;; modification, are permitted provided that the following conditions6;; are met:7;; 1. Redistributions of source code must retain the above copyright8;; notice, this list of conditions and the following disclaimer.9;; 2. Redistributions in binary form must reproduce the above copyright10;; notice, this list of conditions and the following disclaimer in the11;; documentation and/or other materials provided with the distribution.12;; 3. The name of the authors may not be used to endorse or promote products13;; derived from this software without specific prior written permission.1415;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR16;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES17;; 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, BUT20;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,21;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY22;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT23;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF24;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2526;;; [Hacked slightly by Taylor R. Campbell to make it work in his27;;; macro expander `riaxpander'.]2829;; [Hacked even more by Felix L. Winkelmann to make it work in the30;; Hi-Lo expander]3132; Example:33;34; (define-syntax or35; (syntax-rules ()36; ((or) #f)37; ((or e) e)38; ((or e1 e ...) (let ((temp e1))39; (if temp temp (or e ...))))))4041(##sys#extend-macro-environment42 'syntax-rules43 '()44 (##sys#er-transformer45 (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-rules56 ellipsis rules subkeywords r c #t)))))5758;; Runtime internal support module exclusively for syntax-rules59(module chicken.internal.syntax-rules60 (drop-right take-right safe-length syntax-rules-mismatch)6162(import scheme)6364(define (syntax-rules-mismatch input)65 (##sys#syntax-error "no rule matches form" input))6667(define (drop-right input temp)68 (let loop ((len (safe-length input))69 (input input))70 (cond71 ((> len temp)72 (cons (car input)73 (loop (- len 1) (cdr input))))74 (else '()))))7576(define (take-right input temp)77 (let loop ((len (safe-length input))78 (input input))79 (cond80 ((> len temp)81 (loop (- len 1) (cdr input)))82 (else input))))8384(define (safe-length lst)85 (let loop ((lst lst) (len 0))86 (if (pair? lst)87 (loop (cdr lst) (+ 1 len))88 len)))8990(define (process-syntax-rules ellipsis rules subkeywords r c r7ext)9192 (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-mismatch134 (r 'chicken.internal.syntax-rules#syntax-rules-mismatch))135136 (define (ellipsis? x)137 (c x %ellipsis))138139 ;; R7RS support: underscore matches anything140 (define (underscore? x)141 (c x (r '_)))142143 (define (make-transformer rules)144 `(##sys#er-transformer145 (,%lambda (,%input ,%rename ,%compare)146 (,%let ((,%tail (,%cdr ,%input)))147 (,%cond ,@(map process-rule rules)148 (,%else (,%syntax-rules-mismatch ,%input)))))))149150 (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 pattern158 %tail159 (lambda (x) x) #f ellipsis?)160 ,(process-template template161 0 ellipsis?162 (meta-variables pattern 0 ellipsis? '() #f)))))163 (##sys#syntax-error "ill-formed syntax rule" rule)))164165 ;; Generate code to test whether input expression matches pattern166167 (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 (else187 `((,%equal? ,input ',pattern)))))188189 (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 ,@conjuncts199 (,%loop (,%cdr ,%l)200 (,%+ ,%len -1)))))))))))201202 ;; Generate code to take apart the input expression203 ;; This is pretty bad, but it seems to work (can't say why).204205 (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 (append218 (process-pattern (car pattern)219 %temp220 (lambda (x) ;temp is free in x221 (mapit222 (if (eq? %temp x)223 %match ; Optimization: no map+lambda224 `(,%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 '())))237238 ;; Generate code to compose the output expression according to template239240 (define (process-template template dim el? env)241 (cond ((symbol? template)242 (let ((probe (assq template env)))243 (if probe244 (if (<= (cdr probe) dim)245 template246 (##sys#syntax-error "template dimension error (too few ellipses?)"247 template))248 `(,%rename (##core#syntax ,template)))))249 ((and r7ext250 (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 (vars258 (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->vector284 ,(process-template (vector->list template) dim el? env)))285 (else286 `(,%quote ,template))))287288 ;; Return an association list of (var . dim)289290 (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 vars295 (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)))305306 ;; Return a list of meta-variables of given higher dim307308 (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? env318 (free-meta-variables (cddr template)319 dim el? env free)))320 ((pair? template)321 (free-meta-variables (car template)322 dim el? env323 (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)))328329 (define (ellipsis-escaped-pattern? pattern el?)330 (and (pair? pattern) (el? (car pattern))))331332 (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)))337338 (define (segment-template? pattern el?)339 (and (pair? pattern)340 (pair? (cdr pattern))341 ((if r7ext el? ellipsis?) (cadr pattern))))342343 ;; Count the number of `...'s in PATTERN.344345 (define (segment-depth pattern el?)346 (if (segment-template? pattern el?)347 (+ 1 (segment-depth (cdr pattern) el?))348 0))349350 ;; Get whatever is after the `...'s in PATTERN.351352 (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)))358359 (make-transformer rules))360361) ; chicken.internal.syntax-rules