mirror of
https://github.com/ashinn/chibi-scheme.git
synced 2025-05-18 21:29:19 +02:00
If you set the macro-aux of a macro outside of (chibi syntax-case), it would previously case `syntax` to think that it was a pattern variable and try to substitute it, even if the macro-aux was being used for something else. This patch fixes that by wrapping pattern variable values in an extra typed box and checking that it has the right type before deciding that it’s actually a pattern variable.
395 lines
15 KiB
Scheme
395 lines
15 KiB
Scheme
;; Written by Marc Nieper-Wißkirchen
|
|
|
|
;; TODO: make-synthetic-identifier should return a truly unique (that
|
|
;; is not free-identifier=? to any other) identifier.
|
|
|
|
;; TODO: Consecutive ellipses in syntax templates.
|
|
|
|
;; TODO: Write many more tests.
|
|
|
|
(define current-renamer (make-parameter (lambda (x) x)))
|
|
(define current-usage-environment (make-parameter (current-environment)))
|
|
|
|
(define (free-identifier=? x y)
|
|
(let ((env (or (current-usage-environment) (current-environment))))
|
|
(identifier=? env x env y)))
|
|
|
|
(define (%make-transformer transformer)
|
|
(cond
|
|
((and (= 1 (procedure-arity transformer))
|
|
(not (procedure-variadic? transformer)))
|
|
(lambda (expr use-env mac-env)
|
|
(let ((old-use-env (current-usage-environment))
|
|
(old-renamer (current-renamer)))
|
|
(current-usage-environment use-env)
|
|
(current-renamer (make-renamer mac-env))
|
|
(let ((result (transformer expr)))
|
|
(current-usage-environment old-use-env)
|
|
(current-renamer old-renamer)
|
|
result))))
|
|
(else
|
|
(lambda (expr use-env mac-env)
|
|
(let ((old-use-env (current-usage-environment))
|
|
(old-renamer (current-renamer)))
|
|
(current-usage-environment use-env)
|
|
(current-renamer (make-renamer mac-env))
|
|
(let ((result (transformer expr use-env mac-env)))
|
|
(current-usage-environment old-use-env)
|
|
(current-renamer old-renamer)
|
|
result))))))
|
|
|
|
(define (make-transformer base-transformer)
|
|
(let ((wrapped-transformer (%make-transformer base-transformer)))
|
|
(if (procedure-variable-transformer? base-transformer)
|
|
(make-variable-transformer wrapped-transformer)
|
|
wrapped-transformer)))
|
|
|
|
(%define-syntax define-syntax
|
|
(lambda (expr use-env mac-env)
|
|
(list (close-syntax '%define-syntax mac-env)
|
|
(cadr expr)
|
|
(list (close-syntax 'make-transformer mac-env)
|
|
(car (cddr expr))))))
|
|
|
|
(define-syntax let-syntax
|
|
(syntax-rules ()
|
|
((let-syntax ((keyword transformer) ...) . body)
|
|
(%let-syntax ((keyword (make-transformer transformer)) ...) . body))))
|
|
|
|
(define-syntax letrec-syntax
|
|
(syntax-rules ()
|
|
((letrec-syntax ((keyword transformer) ...) . body)
|
|
(%letrec-syntax ((keyword (make-transformer transformer)) ...) . body))))
|
|
|
|
(define-record-type Pattern-Cell
|
|
(make-pattern-cell val) pattern-cell?
|
|
(val pattern-cell-value))
|
|
|
|
(define-syntax define-pattern-variable
|
|
(er-macro-transformer
|
|
(lambda (expr rename compare)
|
|
(let ((id (cadr expr))
|
|
(binding (cddr expr)))
|
|
(let ((cell (env-cell (current-usage-environment) id)))
|
|
(if cell
|
|
(macro-aux-set! (cdr cell) (make-pattern-cell binding))))
|
|
(rename '(begin))))))
|
|
|
|
(define (make-pattern-variable pvar)
|
|
(lambda (expr)
|
|
(error "reference to pattern variable outside syntax" pvar)))
|
|
|
|
(define (pattern-variable x)
|
|
(and-let*
|
|
((cell (env-cell (current-usage-environment) x))
|
|
(cell-ref (cdr cell))
|
|
((macro? cell-ref))
|
|
(aux (macro-aux cell-ref))
|
|
((pattern-cell? aux)))
|
|
(pattern-cell-value aux)))
|
|
|
|
(define (rename id)
|
|
((current-renamer) id))
|
|
|
|
(define current-ellipsis-id
|
|
(make-syntactic-closure (current-environment) '() 'current-ellipsis))
|
|
|
|
(define (ellipsis-identifier? id)
|
|
(let* ((cell (env-cell (current-usage-environment) current-ellipsis-id))
|
|
(ellipsis (if cell
|
|
(macro-aux (cdr cell))
|
|
(rename '...))))
|
|
(free-identifier=? id ellipsis)))
|
|
|
|
(define bound-identifier=?
|
|
(lambda (x y)
|
|
(eq? x y)))
|
|
|
|
(define (syntax-transformer level)
|
|
(er-macro-transformer
|
|
(lambda (expr rename compare)
|
|
(let*-values (((out envs)
|
|
(gen-template (cadr expr) '() ellipsis-identifier? level)))
|
|
out))))
|
|
|
|
(define (syntax->datum stx)
|
|
(strip-syntactic-closures stx))
|
|
|
|
(define-syntax syntax (syntax-transformer #f))
|
|
(define-syntax quasisyntax (syntax-transformer 0))
|
|
(define-auxiliary-syntax unsyntax)
|
|
(define-auxiliary-syntax unsyntax-splicing)
|
|
|
|
(define (gen-template tmpl envs ell? level)
|
|
(cond
|
|
((pair? tmpl)
|
|
(cond
|
|
((and (identifier? (car tmpl))
|
|
(free-identifier=? (car tmpl) (rename 'unsyntax)))
|
|
(if (and level (zero? level))
|
|
(values (cadr tmpl) envs)
|
|
(let*-values (((out envs) (gen-template (cadr tmpl) envs ell? (and level (- level 1)))))
|
|
(values `(,(rename 'list) ,(gen-data (car tmpl)) ,out) envs))))
|
|
((and (identifier? (car tmpl))
|
|
(free-identifier=? (car tmpl) (rename 'quasisyntax)))
|
|
(let*-values (((out envs) (gen-template (cadr tmpl) envs ell? (and level (+ level 1)))))
|
|
(values `(,(rename 'list) ,(gen-data (car tmpl)) ,out) envs)))
|
|
((and (pair? (car tmpl))
|
|
(free-identifier=? (caar tmpl) (rename 'unsyntax)))
|
|
(if (and level (zero? level))
|
|
(let*-values (((out envs) (gen-template (cdr tmpl) envs ell? level)))
|
|
(values `(,(rename 'cons*) ,@(cdar tmpl) ,out) envs))
|
|
(let*-values (((out1 envs) (gen-template (cdar tmpl) envs ell? (and level (- level 1))))
|
|
((out2 envs) (gen-template (cdr tmpl) envs ell? level)))
|
|
(values `(,(rename 'cons) (,(rename 'cons) ,(gen-data (caar tmpl)) ,out1)
|
|
,out2) envs))))
|
|
((and (pair? (car tmpl))
|
|
(free-identifier=? (caar tmpl) (rename 'unsyntax-splicing)))
|
|
(if (and level (zero? level))
|
|
(let*-values (((out envs) (gen-template (cdr tmpl) envs ell? level)))
|
|
(values `(,(rename 'append) ,@(cdar tmpl) ,out) envs))
|
|
(let*-values (((out1 envs) (gen-template (cdar tmpl) envs ell? (and level (- level 1))))
|
|
((out2 envs) (gen-template (cdr tmpl) envs ell? level)))
|
|
(values `(,(rename 'cons) (,(rename 'cons) ,(gen-data (caar tmpl)) ,out1)
|
|
,out2) envs))))
|
|
((and (identifier? (car tmpl))
|
|
(ell? (car tmpl)))
|
|
(gen-template (cadr tmpl) envs (lambda (id) #f) level))
|
|
((and (pair? (cdr tmpl))
|
|
(identifier? (cadr tmpl))
|
|
(ell? (cadr tmpl)))
|
|
(let*-values (((out* envs)
|
|
(gen-template (cddr tmpl) envs ell? level))
|
|
((out envs)
|
|
(gen-template (car tmpl) (cons '() envs) ell? level)))
|
|
(if (null? (car envs))
|
|
(error "too many ellipses following syntax template" (car tmpl)))
|
|
(values `(,(rename 'fold-right) (,(rename 'lambda) (,@(car envs) ,(rename 'stx))
|
|
(,(rename 'cons) ,out ,(rename 'stx)))
|
|
,out* ,@(car envs))
|
|
(cdr envs))))
|
|
(else
|
|
(let*-values (((out1 envs)
|
|
(gen-template (car tmpl) envs ell? level))
|
|
((out2 envs)
|
|
(gen-template (cdr tmpl) envs ell? level)))
|
|
(values `(,(rename 'cons) ,out1 ,out2) envs)))))
|
|
((vector? tmpl)
|
|
(let*-values (((out envs)
|
|
(gen-template (vector->list tmpl) envs ell? level)))
|
|
(values `(,(rename 'list->vector) ,out) envs)))
|
|
((identifier? tmpl)
|
|
(cond ((ell? tmpl)
|
|
(error "misplaced ellipsis in syntax template" tmpl))
|
|
((pattern-variable tmpl) =>
|
|
(lambda (binding)
|
|
(values (car binding)
|
|
(update-envs tmpl (car binding) (cadr binding) envs))))
|
|
(else
|
|
(values (gen-data tmpl) envs))))
|
|
(else
|
|
(values `(,(rename 'quote) ,tmpl) envs))))
|
|
|
|
(define (gen-data id)
|
|
`((,(rename 'current-renamer))
|
|
(,(rename 'syntax-quote) ,id)))
|
|
|
|
(define (update-envs id x level envs)
|
|
(let loop ((level level) (envs envs))
|
|
(cond ((zero? level)
|
|
envs)
|
|
((null? envs)
|
|
(error "too few ellipses following syntax template" id))
|
|
(else
|
|
(let ((outer-envs (loop (- level 1) (cdr envs))))
|
|
(cond ((member x (car envs) bound-identifier=?)
|
|
envs)
|
|
(else
|
|
(cons (cons x (car envs))
|
|
outer-envs))))))))
|
|
|
|
(define-syntax syntax-case
|
|
(er-macro-transformer
|
|
(lambda (expr rename compare)
|
|
(let ((expr (cadr expr))
|
|
(lit* (car (cddr expr)))
|
|
(clause* (reverse (cdr (cddr expr))))
|
|
(error #'(error "syntax error" e)))
|
|
#`(let ((e #,expr))
|
|
#,(if (null? clause*)
|
|
error
|
|
#`(let ((fail (lambda () #,error)))
|
|
#,(let loop ((clause (car clause*))
|
|
(clause* (cdr clause*)))
|
|
(if (null? clause*)
|
|
(gen-clause lit* clause)
|
|
#`(let ((fail (lambda ()
|
|
#,(gen-clause lit* clause))))
|
|
#,(loop (car clause*) (cdr clause*))))))))))))
|
|
|
|
(define (gen-clause lit* clause)
|
|
(if (= 3 (length clause))
|
|
(gen-output lit* (car clause) (cadr clause) (car (cddr clause)))
|
|
(gen-output lit* (car clause) #t (cadr clause))))
|
|
|
|
(define (gen-output lit* pattern fender output-expr)
|
|
(let*-values (((matcher vars)
|
|
(gen-matcher #'e lit* pattern '())))
|
|
(matcher
|
|
(lambda ()
|
|
#`(let-syntax
|
|
#,(map (lambda (var)
|
|
#`(#,(car var)
|
|
(make-pattern-variable (syntax-quote #,(car var)))))
|
|
vars)
|
|
#,@(map (lambda (var)
|
|
#`(define-pattern-variable . #,var))
|
|
vars)
|
|
(if #,fender
|
|
#,output-expr
|
|
(fail)))))))
|
|
|
|
(define (gen-matcher e lit* pattern vars)
|
|
(cond
|
|
((pair? pattern)
|
|
(cond
|
|
((and (pair? (cdr pattern))
|
|
(identifier? (cadr pattern))
|
|
(ellipsis-identifier? (cadr pattern)))
|
|
(let* ((l (length+ (cddr pattern)))
|
|
(h (car (generate-temporaries '(#f))))
|
|
(t (car (generate-temporaries '(#f)))))
|
|
(let*-values (((head-matcher vars)
|
|
(gen-map h lit* (car pattern) vars))
|
|
((tail-matcher vars)
|
|
(gen-matcher* t lit* (cddr pattern) vars)))
|
|
(values (lambda (k)
|
|
#`(let ((n (length+ #,e)))
|
|
(if (and n (>= n #,l))
|
|
(let*-values (((#,h #,t) (split-at #,e (- n #,l))))
|
|
#,(head-matcher (lambda ()
|
|
(tail-matcher k))))
|
|
(fail))))
|
|
vars))))
|
|
(else
|
|
(let ((e1 (car (generate-temporaries '(#f))))
|
|
(e2 (car (generate-temporaries '(#f)))))
|
|
(let*-values (((car-matcher vars)
|
|
(gen-matcher e1 lit* (car pattern) vars))
|
|
((cdr-matcher vars)
|
|
(gen-matcher e2 lit* (cdr pattern) vars)))
|
|
(values (lambda (k)
|
|
#`(if (pair? #,e)
|
|
(let ((#,e1 (car #,e))
|
|
(#,e2 (cdr #,e)))
|
|
#,(car-matcher (lambda ()
|
|
(cdr-matcher k))))
|
|
(fail)))
|
|
vars))))))
|
|
((identifier? pattern)
|
|
(cond ((member pattern lit* free-identifier=?)
|
|
(values (lambda (k)
|
|
#`(if (free-identifier=? #'#,pattern #,e)
|
|
#,(k)
|
|
(fail)))
|
|
vars))
|
|
((ellipsis-identifier? pattern)
|
|
(error "misplaced ellipsis" pattern))
|
|
((free-identifier=? pattern #'_)
|
|
(values (lambda (k)
|
|
(k))
|
|
vars))
|
|
(else
|
|
(values (lambda (k)
|
|
(k))
|
|
(alist-cons pattern (list e 0) vars)))))
|
|
(else
|
|
(values (lambda (k)
|
|
#`(if (equal? (syntax->datum #,e) '#,pattern)
|
|
#,(k)
|
|
(fail)))
|
|
vars))))
|
|
|
|
(define (gen-map h lit* pattern vars)
|
|
(let*-values (((matcher inner-vars) (gen-matcher #'g lit* pattern '())))
|
|
(let ((loop (car (generate-temporaries '(#f))))
|
|
(g* (generate-temporaries inner-vars)))
|
|
(values
|
|
(lambda (k)
|
|
#`(let #,loop ((#,h (reverse #,h))
|
|
#,@(map (lambda (g)
|
|
#`(#,g '()))
|
|
g*))
|
|
(if (null? #,h)
|
|
#,(k)
|
|
(let ((g (car #,h)))
|
|
#,(matcher
|
|
(lambda ()
|
|
#`(#,loop (cdr #,h)
|
|
#,@(map (lambda (var g)
|
|
#`(cons #,(cadr var) #,g))
|
|
inner-vars g*))))))))
|
|
(fold (lambda (g var vars)
|
|
(alist-cons (car var) (list g (+ (cadr (cdr var)) 1)) vars))
|
|
vars g* inner-vars)))))
|
|
|
|
(define (gen-matcher* e lit* pattern* vars)
|
|
(let loop ((e e) (pattern* pattern*) (vars vars))
|
|
(cond ((null? pattern*)
|
|
(values (lambda (k)
|
|
#`(if (null? #,e)
|
|
#,(k)
|
|
(fail)))
|
|
vars))
|
|
((pair? pattern*)
|
|
(let ((e1 (car (generate-temporaries '(#f))))
|
|
(e2 (car (generate-temporaries '(#f)))))
|
|
(let*-values (((car-matcher vars)
|
|
(gen-matcher e1 lit* (car pattern*) vars))
|
|
((cdr-matcher vars)
|
|
(loop e2 (cdr pattern*) vars)))
|
|
(values (lambda (k)
|
|
#`(let ((#,e1 (car #,e))
|
|
(#,e2 (cdr #,e)))
|
|
#,(car-matcher (lambda ()
|
|
(cdr-matcher k)))))
|
|
vars))))
|
|
(else
|
|
(gen-matcher e lit* pattern* vars)))))
|
|
|
|
(define (make-synthetic-identifier id)
|
|
(close-syntax id (environment)))
|
|
|
|
(define (generate-temporaries l)
|
|
(map (lambda (x) (make-synthetic-identifier 't)) l))
|
|
|
|
(define-syntax with-syntax
|
|
(lambda (x)
|
|
(syntax-case x ()
|
|
((_ ((p e0) ...) e1 e2 ...)
|
|
#'(syntax-case (list e0 ...) ()
|
|
((p ...) (let () e1 e2 ...)))))))
|
|
|
|
(define (syntax-violation who message . form*)
|
|
(apply error message form*))
|
|
|
|
(define-syntax define-current-ellipsis
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
((_ ellipsis)
|
|
(let ((mac (cdr (env-cell (current-usage-environment) current-ellipsis-id))))
|
|
(macro-aux-set! mac #'ellipsis))
|
|
#'(begin)))))
|
|
|
|
(define-syntax with-ellipsis
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
((_ ellipsis . body)
|
|
(with-syntax ((current-ellipsis current-ellipsis-id))
|
|
#'(let-syntax ((current-ellipsis (syntax-rules ())))
|
|
(define-current-ellipsis ellipsis)
|
|
. body))))))
|
|
|
|
;; Local variables:
|
|
;; eval: (put '%define-syntax 'scheme-indent-function 1)
|
|
;; End:
|