~ chicken-core (chicken-5) /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; The syntax-rules macro (new in R5RS)2728;;; [Hacked slightly by Taylor R. Campbell to make it work in his29;;; macro expander `riaxpander'.]3031;; [Hacked even more by Felix L. Winkelmann to make it work in his32;; Hi-Lo expander]3334; Example:35;36; (define-syntax or37; (syntax-rules ()38; ((or) #f)39; ((or e) e)40; ((or e1 e ...) (let ((temp e1))41; (if temp temp (or e ...))))))4243(##sys#extend-macro-environment44 'syntax-rules45 '()46 (##sys#er-transformer47 (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-rules58 ellipsis rules subkeywords r c)))))596061;; Runtime internal support module exclusively for syntax-rules62(module chicken.internal.syntax-rules63 (drop-right take-right syntax-rules-mismatch)6465(import scheme)6667(define (syntax-rules-mismatch input)68 (##sys#syntax-error-hook "no rule matches form" input))6970(define (drop-right input temp)71 ;;XXX use unsafe accessors72 (let loop ((len (length input))73 (input input))74 (cond75 ((> len temp)76 (cons (car input)77 (loop (- len 1) (cdr input))))78 (else '()))))7980(define (take-right input temp)81 ;;XXX use unsafe accessors82 (let loop ((len (length input))83 (input input))84 (cond85 ((> len temp)86 (loop (- len 1) (cdr input)))87 (else input))))8889(define (process-syntax-rules ellipsis rules subkeywords r c)9091 (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-mismatch133 (r 'chicken.internal.syntax-rules#syntax-rules-mismatch))134135 (define (ellipsis? x)136 (c x %ellipsis))137138 (define (make-transformer rules)139 `(##sys#er-transformer140 (,%lambda (,%input ,%rename ,%compare)141 (,%let ((,%tail (,%cdr ,%input)))142 (,%cond ,@(map process-rule rules)143 (,%else (,%syntax-rules-mismatch ,%input)))))))144145 (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 pattern153 %tail154 (lambda (x) x) #f)155 ,(process-template template156 0157 (meta-variables pattern 0 '() #f)))))158 (##sys#syntax-error-hook "ill-formed syntax rule" rule)))159160 ;; Generate code to test whether input expression matches pattern161162 (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 (else182 `((,%equal? ,input ',pattern)))))183184 (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 list187 (,%let ((,%len (,%length ,input)))188 (,%and (,%>= ,%len ,(length (cddr pattern)))189 (,%let ,%loop ((,%l ,input)190 (,%len ,%len))191 (,%cond192 ((,%= ,%len ,(length (cddr pattern)))193 ,@(process-match %l (cddr pattern) #t))194 (,%else195 (,%and ,@conjuncts196 (,%loop (,%cdr ,%l) (,%+ ,%len -1))))))))))))197198 ;; Generate code to take apart the input expression199 ;; This is pretty bad, but it seems to work (can't say why).200201 (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 (append212 (process-pattern (car pattern)213 %temp214 (lambda (x) ;temp is free in x215 (mapit216 (if (eq? %temp x)217 %match ; Optimization: no map+lambda218 `(,%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 '())))229230 ;; Generate code to compose the output expression according to template231232 (define (process-template template dim env)233 (cond ((symbol? template)234 (let ((probe (assq template env)))235 (if probe236 (if (<= (cdr probe) dim)237 template238 (##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 (vars245 (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-dim250 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->vector271 ,(process-template (vector->list template) dim env)))272 (else273 `(,%quote ,template))))274275 ;; Return an association list of (var . dim)276277 (define (meta-variables pattern dim vars seen-segment?)278 (cond ((symbol? pattern)279 (if (memq pattern subkeywords)280 vars281 (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) dim287 (meta-variables (cdr pattern) dim vars #f) #f))288 ((vector? pattern)289 (meta-variables (vector->list pattern) dim vars #f))290 (else vars)))291292 ;; Return a list of meta-variables of given higher dim293294 (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 env304 (free-meta-variables (cddr template)305 dim env free)))306 ((pair? template)307 (free-meta-variables (car template)308 dim env309 (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)))314315 (define (segment-pattern? p seen-segment?)316 (and (segment-template? p)317 (cond318 (seen-segment?319 (##sys#syntax-error-hook "Only one segment per level is allowed" p))320 ((not (list? p)) ; Improper list321 (##sys#syntax-error-hook "Cannot combine dotted tail and ellipsis" p))322 (else #t))))323324 (define (segment-template? pattern)325 (and (pair? pattern)326 (pair? (cdr pattern))327 (ellipsis? (cadr pattern))))328329 ;; Count the number of `...'s in PATTERN.330331 (define (segment-depth pattern)332 (if (segment-template? pattern)333 (+ 1 (segment-depth (cdr pattern)))334 0))335336 ;; Get whatever is after the `...'s in PATTERN.337338 (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)))344345 (make-transformer rules))346347) ; chicken.internal.syntax-rules