123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- ; Copyright (c) 1993-2007 by Richard Kelsey and Jonathan Rees. See file COPYING.
- ; Rewrite-rule compiler (a.k.a. "extend-syntax")
- ; Example:
- ;
- ; (define-syntax or
- ; (syntax-rules ()
- ; ((or) #f)
- ; ((or e) e)
- ; ((or e1 e ...) (let ((temp e1))
- ; (if temp temp (or e ...))))))
- (define-syntax syntax-rules
- (let ()
- (define name? symbol?)
- (define (segment-pattern? pattern)
- (and (segment-template? pattern)
- (or (null? (cddr pattern))
- (syntax-error "segment matching not implemented" pattern))))
- (define (segment-template? pattern)
- (and (pair? pattern)
- (pair? (cdr pattern))
- (memq (cadr pattern) indicators-for-zero-or-more)))
- (define indicators-for-zero-or-more (list (string->symbol "...") '---))
- (lambda (exp r c)
- (define %input (r '%input)) ;Gensym these, if you like.
- (define %compare (r '%compare))
- (define %rename (r '%rename))
- (define %tail (r '%tail))
- (define %temp (r '%temp))
- (define rules (cddr exp))
- (define subkeywords (cadr exp))
- (define (make-transformer rules)
- `(lambda (,%input ,%rename ,%compare)
- (let ((,%tail (cdr ,%input)))
- (cond ,@(map process-rule rules)
- (else
- (syntax-error
- "use of macro doesn't match definition"
- ,%input))))))
- (define (process-rule rule)
- (if (and (pair? rule)
- (pair? (cdr rule))
- (null? (cddr rule)))
- (let ((pattern (cdar rule))
- (template (cadr rule)))
- `((and ,@(process-match %tail pattern))
- (let* ,(process-pattern pattern
- %tail
- (lambda (x) x))
- ,(process-template template
- 0
- (meta-variables pattern 0 '())))))
- (syntax-error "ill-formed syntax rule" rule)))
- ; Generate code to test whether input expression matches pattern
- (define (process-match input pattern)
- (cond ((name? pattern)
- (if (member pattern subkeywords)
- `((,%compare ,input (,%rename ',pattern)))
- `()))
- ((segment-pattern? pattern)
- (process-segment-match input (car pattern)))
- ((pair? pattern)
- `((let ((,%temp ,input))
- (and (pair? ,%temp)
- ,@(process-match `(car ,%temp) (car pattern))
- ,@(process-match `(cdr ,%temp) (cdr pattern))))))
- ((or (null? pattern) (boolean? pattern) (char? pattern))
- `((eq? ,input ',pattern)))
- (else
- `((equal? ,input ',pattern)))))
- (define (process-segment-match input pattern)
- (let ((conjuncts (process-match '(car l) pattern)))
- (if (null? conjuncts)
- `((list? ,input)) ;+++
- `((let loop ((l ,input))
- (or (null? l)
- (and (pair? l)
- ,@conjuncts
- (loop (cdr l)))))))))
- ; Generate code to take apart the input expression
- ; This is pretty bad, but it seems to work (can't say why).
- (define (process-pattern pattern path mapit)
- (cond ((name? pattern)
- (if (memq pattern subkeywords)
- '()
- (list (list pattern (mapit path)))))
- ((segment-pattern? pattern)
- (process-pattern (car pattern)
- %temp
- (lambda (x) ;temp is free in x
- (mapit (if (eq? %temp x)
- path ;+++
- `(map (lambda (,%temp) ,x)
- ,path))))))
- ((pair? pattern)
- (append (process-pattern (car pattern) `(car ,path) mapit)
- (process-pattern (cdr pattern) `(cdr ,path) mapit)))
- (else '())))
- ; Generate code to compose the output expression according to template
- (define (process-template template rank env)
- (cond ((name? template)
- (let ((probe (assq template env)))
- (if probe
- (if (<= (cdr probe) rank)
- template
- (syntax-error "template rank error (too few ...'s?)"
- template))
- `(,%rename ',template))))
- ((segment-template? template)
- (let ((vars
- (free-meta-variables (car template) (+ rank 1) env '())))
- (if (null? vars)
- (syntax-error "too many ...'s" template)
- (let* ((x (process-template (car template)
- (+ rank 1)
- env))
- (gen (if (equal? (list x) vars)
- x ;+++
- `(map (lambda ,vars ,x)
- ,@vars))))
- (if (null? (cddr template))
- gen ;+++
- `(append ,gen ,(process-template (cddr template)
- rank env)))))))
- ((pair? template)
- `(cons ,(process-template (car template) rank env)
- ,(process-template (cdr template) rank env)))
- (else `(quote ,template))))
- ; Return an association list of (var . rank)
- (define (meta-variables pattern rank vars)
- (cond ((name? pattern)
- (if (memq pattern subkeywords)
- vars
- (cons (cons pattern rank) vars)))
- ((segment-pattern? pattern)
- (meta-variables (car pattern) (+ rank 1) vars))
- ((pair? pattern)
- (meta-variables (car pattern) rank
- (meta-variables (cdr pattern) rank vars)))
- (else vars)))
- ; Return a list of meta-variables of given higher rank
- (define (free-meta-variables template rank env free)
- (cond ((name? template)
- (if (and (not (memq template free))
- (let ((probe (assq template env)))
- (and probe (>= (cdr probe) rank))))
- (cons template free)
- free))
- ((segment-template? template)
- (free-meta-variables (car template)
- rank env
- (free-meta-variables (cddr template)
- rank env free)))
- ((pair? template)
- (free-meta-variables (car template)
- rank env
- (free-meta-variables (cdr template)
- rank env free)))
- (else free)))
- c ;ignored
- ;; Kludge for Scheme 48 static linker.
- ;; `(cons ,(make-transformer rules)
- ;; ',(find-free-names-in-syntax-rules subkeywords rules))
- (make-transformer rules))))
|