~ chicken-core (chicken-5) e65a1b47c1a586b9a95f2540622ff7660f8b6761
commit e65a1b47c1a586b9a95f2540622ff7660f8b6761 Author: felix <felix@call-with-current-continuation.org> AuthorDate: Thu Jul 14 16:46:32 2011 +0200 Commit: felix <felix@call-with-current-continuation.org> CommitDate: Thu Jul 14 16:46:32 2011 +0200 first attempt at introducing wrapper structs for transformers; documented er/ir-macro-transformer; make check seems to work but self-compile is not yet tested diff --git a/compiler-syntax.scm b/compiler-syntax.scm index 67a919e2..76fb4e85 100644 --- a/compiler-syntax.scm +++ b/compiler-syntax.scm @@ -43,7 +43,10 @@ (alist-update! name (add1 a) compiler-syntax-statistics))))) (define (r-c-s names transformer #!optional (se '())) - (let ((t (cons (##sys#er-transformer transformer) se))) + (let ((t (cons (##sys#ensure-transformer + (##sys#er-transformer transformer) + 'define-compiler-syntax) + se))) (for-each (lambda (name) (##sys#put! name '##compiler#compiler-syntax t) ) diff --git a/compiler.scm b/compiler.scm index 1779d6f4..2217bc6d 100644 --- a/compiler.scm +++ b/compiler.scm @@ -666,8 +666,9 @@ (list (car b) se - (##sys#er-transformer - (##sys#eval/meta (cadr b))))) + (##sys#ensure-transformer + (##sys#eval/meta (cadr b)) + 'let-syntax))) (cadr x) ) se) ) ) (walk @@ -680,8 +681,9 @@ (list (car b) #f - (##sys#er-transformer - (##sys#eval/meta (cadr b))))) + (##sys#ensure-transformer + (##sys#eval/meta (cadr b)) + 'letrec-syntax))) (cadr x) ) ) (se2 (append ms se)) ) (for-each @@ -708,13 +710,12 @@ (##sys#extend-macro-environment name (##sys#current-environment) - (##sys#er-transformer (##sys#eval/meta body))) + (##sys#eval/meta body)) (walk (if ##sys#enable-runtime-macros `(##sys#extend-macro-environment ',var - (##sys#current-environment) - (##sys#er-transformer ,body)) ;XXX possibly wrong se? + (##sys#current-environment) ,body) ;XXX possibly wrong se? '(##core#undefined) ) e se dest ldest h)) ) @@ -731,7 +732,9 @@ name '##compiler#compiler-syntax (and body (##sys#cons - (##sys#er-transformer (##sys#eval/meta body)) + (##sys#ensure-transformer + (##sys#eval/meta body) + 'define-compiler-syntax) (##sys#current-environment)))) (walk (if ##sys#enable-runtime-macros @@ -740,7 +743,9 @@ '##compiler#compiler-syntax ,(and body `(##sys#cons - (##sys#er-transformer ,body) + (##sys#ensure-transformer + ,body + 'define-compiler-syntax) (##sys#current-environment)))) '(##core#undefined) ) e se dest ldest h))) @@ -753,8 +758,10 @@ (list name (and (pair? (cdr b)) - (cons (##sys#er-transformer - (##sys#eval/meta (cadr b))) se)) + (cons (##sys#ensure-transformer + (##sys#eval/meta (cadr b)) + 'let-compiler-syntax) + se)) (##sys#get name '##compiler#compiler-syntax) ) ) ) (cadr x)))) (dynamic-wind diff --git a/eval.scm b/eval.scm index c7580a5e..c2d942f8 100644 --- a/eval.scm +++ b/eval.scm @@ -555,8 +555,9 @@ (list (car b) se - (##sys#er-transformer - (##sys#eval/meta (cadr b))))) + (##sys#ensure-transformer + (##sys#eval/meta (cadr b)) + 'let-syntax))) (cadr x) ) se) ) ) (compile @@ -568,8 +569,9 @@ (list (car b) #f - (##sys#er-transformer - (##sys#eval/meta (cadr b))))) + (##sys#ensure-transformer + (##sys#eval/meta (cadr b)) + 'letrec-syntax))) (cadr x) ) ) (se2 (append ms se)) ) (for-each @@ -590,7 +592,7 @@ (##sys#extend-macro-environment name (##sys#current-environment) - (##sys#er-transformer (##sys#eval/meta body))) + (##sys#eval/meta body)) (compile '(##core#undefined) e #f tf cntr se) ) ) ((##core#define-compiler-syntax) diff --git a/expand.scm b/expand.scm index 13825f70..697e3205 100644 --- a/expand.scm +++ b/expand.scm @@ -135,8 +135,14 @@ (define ##sys#chicken-macro-environment '()) ; used later in chicken.import.scm (define ##sys#chicken-ffi-macro-environment '()) ; used later in foreign.import.scm -(define (##sys#extend-macro-environment name se handler) - (let ((me (##sys#macro-environment))) +(define (##sys#ensure-transformer t #!optional loc) + (cond ((procedure? t) (##sys#slot (##sys#er-transformer t) 1)) ; DEPRECATED + ((##sys#structure? t 'transformer) (##sys#slot t 1)) + (else (##sys#error loc "expected syntax-transformer, but got" t)))) + +(define (##sys#extend-macro-environment name se transformer) + (let ((me (##sys#macro-environment)) + (handler (##sys#ensure-transformer transformer 'define-syntax))) (cond ((lookup name me) => (lambda (a) (set-car! a se) @@ -741,128 +747,131 @@ (walk (cdr x) (cdr p)) ) ) ) ) ) -;;; explicit-renaming transformer - -(define ((make-er/ir-transformer handler explicit-renaming?) form se dse) - (let ((renv '())) ; keep rename-environment for this expansion - (assert (list? se) "not a list" se) ;XXX remove later - (define (rename sym) - (cond ((pair? sym) - (cons (rename (car sym)) (rename (cdr sym)))) - ((vector? sym) - (list->vector (rename (vector->list sym)))) - ((not (symbol? sym)) sym) - ((assq sym renv) => - (lambda (a) - (dd `(RENAME/RENV: ,sym --> ,(cdr a))) - (cdr a))) - ((lookup sym se) => - (lambda (a) - (cond ((symbol? a) - ;; Add an extra level of indirection for already aliased - ;; symbols. This prevents aliased symbols from popping up - ;; in syntax-stripped output. - (cond ((or (getp a '##core#aliased) - (getp a '##core#primitive)) - (let ((a2 (macro-alias sym se))) - (dd `(RENAME/LOOKUP/ALIASED: ,sym --> ,a ==> ,a2)) - (set! renv (cons (cons sym a2) renv)) - a2)) - (else (dd `(RENAME/LOOKUP: ,sym --> ,a)) - (set! renv (cons (cons sym a) renv)) - a))) - (else - (let ((a2 (macro-alias sym se))) - (dd `(RENAME/LOOKUP/MACRO: ,sym --> ,a2)) - (set! renv (cons (cons sym a2) renv)) - a2))))) - (else - (let ((a (macro-alias sym se))) - (dd `(RENAME: ,sym --> ,a)) - (set! renv (cons (cons sym a) renv)) - a)))) - (define (compare s1 s2) - (let ((result - (cond ((pair? s1) - (and (pair? s2) - (compare (car s1) (car s2)) - (compare (cdr s1) (cdr s2)))) - ((vector? s1) - (and (vector? s2) - (let ((len (vector-length s1))) - (and (fx= len (vector-length s2)) - (do ((i 0 (fx+ i 1)) - (f #t (compare (vector-ref s1 i) (vector-ref s2 i)))) - ((or (fx>= i len) (not f)) f)))))) - ((and (symbol? s1) (symbol? s2)) - (let ((ss1 (or (getp s1 '##core#macro-alias) - (lookup2 1 s1 dse) - s1) ) - (ss2 (or (getp s2 '##core#macro-alias) - (lookup2 2 s2 dse) - s2) ) ) - (cond ((symbol? ss1) - (cond ((symbol? ss2) - (eq? (or (getp ss1 '##core#primitive) ss1) - (or (getp ss2 '##core#primitive) ss2))) - ((assq ss1 (##sys#macro-environment)) => - (lambda (a) (eq? (cdr a) ss2))) - (else #f) ) ) - ((symbol? ss2) - (cond ((assq ss2 (##sys#macro-environment)) => - (lambda (a) (eq? ss1 (cdr a)))) - (else #f))) - (else (eq? ss1 ss2))))) - (else (eq? s1 s2))) ) ) - (dd `(COMPARE: ,s1 ,s2 --> ,result)) - result)) - (define (lookup2 n sym dse) - (let ((r (lookup sym dse))) - (dd " (lookup/DSE " (list n) ": " sym " --> " - (if (and r (pair? r)) - '<macro> - r) - ")") - r)) - (define (assq-reverse s l) - (cond - ((null? l) #f) - ((eq? (cdar l) s) (car l)) - (else (assq-reverse s (cdr l))))) - (define (mirror-rename sym) - (cond ((pair? sym) - (cons (mirror-rename (car sym)) (mirror-rename (cdr sym)))) - ((vector? sym) - (list->vector (mirror-rename (vector->list sym)))) - ((not (symbol? sym)) sym) - (else ; Code stolen from ##sys#strip-syntax - (let ((renamed (lookup sym se) ) ) - (cond ((assq-reverse sym renv) => - (lambda (a) - (dd "REVERSING RENAME: " sym " --> " (car a)) (car a))) - ((not renamed) - (dd "IMPLICITLY RENAMED: " sym) (rename sym)) - ((pair? renamed) - (dd "MACRO: " sym) (rename sym)) - ((getp sym '##core#real-name) => - (lambda (name) - (dd "STRIP SYNTAX ON " sym " ---> " name) - name)) - (else (dd "BUILTIN ALIAS:" renamed) renamed)))))) - (if explicit-renaming? - ;; Let the user handle renaming - (handler form rename compare) - ;; Implicit renaming: - ;; Rename everything in the input first, feed it to the transformer - ;; and then swap out all renamed identifiers by their non-renamed - ;; versions, and vice versa. User can decide when to inject code - ;; unhygienically this way. - (mirror-rename (handler (rename form) rename compare)) ) ) ) +;;; explicit/implicit-renaming transformer + +(define (make-er/ir-transformer handler explicit-renaming?) + (##sys#make-structure + 'transformer + (lambda (form se dse) + (let ((renv '())) ; keep rename-environment for this expansion + (assert (list? se) "not a list" se) ;XXX remove later + (define (rename sym) + (cond ((pair? sym) + (cons (rename (car sym)) (rename (cdr sym)))) + ((vector? sym) + (list->vector (rename (vector->list sym)))) + ((not (symbol? sym)) sym) + ((assq sym renv) => + (lambda (a) + (dd `(RENAME/RENV: ,sym --> ,(cdr a))) + (cdr a))) + ((lookup sym se) => + (lambda (a) + (cond ((symbol? a) + ;; Add an extra level of indirection for already aliased + ;; symbols. This prevents aliased symbols from popping up + ;; in syntax-stripped output. + (cond ((or (getp a '##core#aliased) + (getp a '##core#primitive)) + (let ((a2 (macro-alias sym se))) + (dd `(RENAME/LOOKUP/ALIASED: ,sym --> ,a ==> ,a2)) + (set! renv (cons (cons sym a2) renv)) + a2)) + (else (dd `(RENAME/LOOKUP: ,sym --> ,a)) + (set! renv (cons (cons sym a) renv)) + a))) + (else + (let ((a2 (macro-alias sym se))) + (dd `(RENAME/LOOKUP/MACRO: ,sym --> ,a2)) + (set! renv (cons (cons sym a2) renv)) + a2))))) + (else + (let ((a (macro-alias sym se))) + (dd `(RENAME: ,sym --> ,a)) + (set! renv (cons (cons sym a) renv)) + a)))) + (define (compare s1 s2) + (let ((result + (cond ((pair? s1) + (and (pair? s2) + (compare (car s1) (car s2)) + (compare (cdr s1) (cdr s2)))) + ((vector? s1) + (and (vector? s2) + (let ((len (vector-length s1))) + (and (fx= len (vector-length s2)) + (do ((i 0 (fx+ i 1)) + (f #t (compare (vector-ref s1 i) (vector-ref s2 i)))) + ((or (fx>= i len) (not f)) f)))))) + ((and (symbol? s1) (symbol? s2)) + (let ((ss1 (or (getp s1 '##core#macro-alias) + (lookup2 1 s1 dse) + s1) ) + (ss2 (or (getp s2 '##core#macro-alias) + (lookup2 2 s2 dse) + s2) ) ) + (cond ((symbol? ss1) + (cond ((symbol? ss2) + (eq? (or (getp ss1 '##core#primitive) ss1) + (or (getp ss2 '##core#primitive) ss2))) + ((assq ss1 (##sys#macro-environment)) => + (lambda (a) (eq? (cdr a) ss2))) + (else #f) ) ) + ((symbol? ss2) + (cond ((assq ss2 (##sys#macro-environment)) => + (lambda (a) (eq? ss1 (cdr a)))) + (else #f))) + (else (eq? ss1 ss2))))) + (else (eq? s1 s2))) ) ) + (dd `(COMPARE: ,s1 ,s2 --> ,result)) + result)) + (define (lookup2 n sym dse) + (let ((r (lookup sym dse))) + (dd " (lookup/DSE " (list n) ": " sym " --> " + (if (and r (pair? r)) + '<macro> + r) + ")") + r)) + (define (assq-reverse s l) + (cond + ((null? l) #f) + ((eq? (cdar l) s) (car l)) + (else (assq-reverse s (cdr l))))) + (define (mirror-rename sym) + (cond ((pair? sym) + (cons (mirror-rename (car sym)) (mirror-rename (cdr sym)))) + ((vector? sym) + (list->vector (mirror-rename (vector->list sym)))) + ((not (symbol? sym)) sym) + (else ; Code stolen from ##sys#strip-syntax + (let ((renamed (lookup sym se) ) ) + (cond ((assq-reverse sym renv) => + (lambda (a) + (dd "REVERSING RENAME: " sym " --> " (car a)) (car a))) + ((not renamed) + (dd "IMPLICITLY RENAMED: " sym) (rename sym)) + ((pair? renamed) + (dd "MACRO: " sym) (rename sym)) + ((getp sym '##core#real-name) => + (lambda (name) + (dd "STRIP SYNTAX ON " sym " ---> " name) + name)) + (else (dd "BUILTIN ALIAS:" renamed) renamed)))))) + (if explicit-renaming? + ;; Let the user handle renaming + (handler form rename compare) + ;; Implicit renaming: + ;; Rename everything in the input first, feed it to the transformer + ;; and then swap out all renamed identifiers by their non-renamed + ;; versions, and vice versa. User can decide when to inject code + ;; unhygienically this way. + (mirror-rename (handler (rename form) rename compare)) ) ) ))) (define (##sys#er-transformer handler) (make-er/ir-transformer handler #t)) (define (##sys#ir-transformer handler) (make-er/ir-transformer handler #f)) -(define (er-macro-transformer x) x) +(define er-macro-transformer ##sys#er-transformer) (define ir-macro-transformer ##sys#ir-transformer) diff --git a/manual/Macros b/manual/Macros index 321e5d3d..36be848b 100644 --- a/manual/Macros +++ b/manual/Macros @@ -17,14 +17,15 @@ macro system based on ''explicit renaming''. Defines a macro named {{IDENTIFIER}} that will transform an expression with {{IDENTIFIER}} in operator position according to {{TRANSFORMER}}. -The transformer expression must be a procedure with three arguments or +The transformer expression must the result of a call +to{{er-macro-transformer}} or {{ir-macro-transformer}}, or it must be a {{syntax-rules}} form. If {{syntax-rules}} is used, the usual R5RS -semantics apply. If {{TRANSFORMER}} is a procedure, then it will -be called on expansion with the complete s-expression of the macro -invocation, a rename procedure that hygienically renames identifiers -and a comparison procedure that compares (possibly renamed) identifiers -(see the section "Explicit renaming macros" below for a detailed explanation -on non-R5RS macros). +semantics apply. If {{TRANSFORMER}} is a transformer, then its +transformer procedure will be called on expansion with the complete +s-expression of the macro invocation, a rename procedure that +hygienically renames identifiers and a comparison procedure that +compares (possibly renamed) identifiers (see the section "Explicit +renaming macros" below for a detailed explanation on non-R5RS macros). {{define-syntax}} may be used to define local macros that are visible throughout the rest of the body in which the definition occurred, i.e. @@ -56,12 +57,26 @@ The effect of destructively modifying the s-expression passed to a transformer procedure is undefined. -==== syntax +==== er-macro-transformer -<macro>(syntax EXPRESSION)</macro> +<procedure>(er-macro-transformer PROCEDURE)</procedure> -Similar to {{quote}} but retains syntactical context information for -embedded identifiers. +Returns an explicit-remnaming transformer object wrapping the +syntax-transformer procedure {{PROCEDURE}}. The procedure will be +called with the form to be expanded and rename and compare procedures +and perform explicit renaming to maintain hygiene. See below for +more information about explicit renaming macros. + + +==== ir-macro-transformer + +<procedure>(ir-macro-transformer PROCEDURE)</procedure> + +Returns a implicit-renaming transformer object wrapping the +syntax-transformer procedure {{PROCEDURE}}. The procedure will be +called with the form to be expanded and an inject and compare +procedure and perform implicit renaming to maintain hygiene. See +below for more information about implicit renaming macros. ==== strip-syntax diff --git a/modules.scm b/modules.scm index c38323c0..f42972b1 100644 --- a/modules.scm +++ b/modules.scm @@ -335,17 +335,17 @@ (map (lambda (se) (if (symbol? se) (find-reexport se) - (list (car se) #f (##sys#er-transformer (cdr se))))) + (list (car se) #f (##sys#ensure-transformer (cdr se))))) sexports)) (iexps (map (lambda (ie) (if (pair? (cdr ie)) - (list (car ie) (cadr ie) (##sys#er-transformer (caddr ie))) + (list (car ie) (cadr ie) (##sys#ensure-transformer (caddr ie))) ie)) iexports)) (nexps (map (lambda (ne) - (list (car ne) #f (##sys#er-transformer (cdr ne)))) + (list (car ne) #f (##sys#ensure-transformer (cdr ne)))) sdefs)) (mod (make-module name '() vexports sexps)) (senv (merge-se diff --git a/srfi-13.import.scm b/srfi-13.import.scm index 799c0833..b2748d66 100644 --- a/srfi-13.import.scm +++ b/srfi-13.import.scm @@ -110,21 +110,22 @@ xsubstring) `((let-string-start+end () - ,(##sys#er-transformer - (lambda (form r c) - (##sys#check-syntax 'let-string-start+end form '(_ _ _ _ _ . _)) - (let ((s-e-r (cadr form)) - (proc (caddr form)) - (s-exp (cadddr form)) - (args-exp (car (cddddr form))) - (body (cdr (cddddr form))) - (%receive (r 'receive)) - (%string-parse-start+end (r 'string-parse-start+end)) - (%string-parse-final-start+end (r 'string-parse-final-start+end))) - (if (pair? (cddr s-e-r)) - `(,%receive (,(caddr s-e-r) ,(car s-e-r) ,(cadr s-e-r)) - (,%string-parse-start+end ,proc ,s-exp ,args-exp) - ,@body) - `(,%receive ,s-e-r - (,%string-parse-final-start+end ,proc ,s-exp ,args-exp) - ,@body) ) )))))) + ,(##sys#ensure-transformer + (##sys#er-transformer + (lambda (form r c) + (##sys#check-syntax 'let-string-start+end form '(_ _ _ _ _ . _)) + (let ((s-e-r (cadr form)) + (proc (caddr form)) + (s-exp (cadddr form)) + (args-exp (car (cddddr form))) + (body (cdr (cddddr form))) + (%receive (r 'receive)) + (%string-parse-start+end (r 'string-parse-start+end)) + (%string-parse-final-start+end (r 'string-parse-final-start+end))) + (if (pair? (cddr s-e-r)) + `(,%receive (,(caddr s-e-r) ,(car s-e-r) ,(cadr s-e-r)) + (,%string-parse-start+end ,proc ,s-exp ,args-exp) + ,@body) + `(,%receive ,s-e-r + (,%string-parse-final-start+end ,proc ,s-exp ,args-exp) + ,@body) ) ))))))) diff --git a/synrules.scm b/synrules.scm index 37515c7d..cf8912ec 100644 --- a/synrules.scm +++ b/synrules.scm @@ -104,11 +104,12 @@ (c x %ellipsis)) (define (make-transformer rules) - `(,%lambda (,%input ,%rename ,%compare) - (,%let ((,%tail (,%cdr ,%input))) - (,%cond ,@(map process-rule rules) - (,%else - (##sys#syntax-rules-mismatch ,%input)))))) + `(##sys#er-transformer + (,%lambda (,%input ,%rename ,%compare) + (,%let ((,%tail (,%cdr ,%input))) + (,%cond ,@(map process-rule rules) + (,%else + (##sys#syntax-rules-mismatch ,%input))))))) (define (process-rule rule) (if (and (pair? rule) diff --git a/tests/syntax-tests.scm b/tests/syntax-tests.scm index 969420cf..b20ca7b4 100644 --- a/tests/syntax-tests.scm +++ b/tests/syntax-tests.scm @@ -40,9 +40,11 @@ ;; some basic contrived testing (define (fac n) - (let-syntax ((m1 (lambda (n r c) - (pp `(M1: ,n)) - (list (r 'sub1) (cadr n))))) + (let-syntax ((m1 + (er-macro-transformer + (lambda (n r c) + (pp `(M1: ,n)) + (list (r 'sub1) (cadr n)))))) (define (sub1 . _) ; ref. transp.? (should not be used here) (error "argh.") ) #;(print "fac: " n) @@ -365,11 +367,12 @@ (define-syntax loop - (lambda (x r c) - (let ((body (cdr x))) - `(,(r 'call/cc) - (,(r 'lambda) (exit) - (,(r 'let) ,(r 'f) () ,@body (,(r 'f)))))))) + (er-macro-transformer + (lambda (x r c) + (let ((body (cdr x))) + `(,(r 'call/cc) + (,(r 'lambda) (exit) + (,(r 'let) ,(r 'f) () ,@body (,(r 'f))))))))) (let ((n 10)) (loop @@ -387,10 +390,11 @@ (f (while0 #f (print "no."))) (define-syntax while - (lambda (x r c) - `(,(r 'loop) - (,(r 'if) (,(r 'not) ,(cadr x)) (exit #f)) - ,@(cddr x)))) + (er-macro-transformer + (lambda (x r c) + `(,(r 'loop) + (,(r 'if) (,(r 'not) ,(cadr x)) (exit #f)) + ,@(cddr x))))) (let ((n 10)) (while (not (zero? n)) @@ -409,8 +413,9 @@ (syntax-rules () ((_ (name . llist) body ...) (define-syntax name - (lambda (x r c) - (apply (lambda llist body ...) (strip-syntax (cdr x)))))))) + (er-macro-transformer + (lambda (x r c) + (apply (lambda llist body ...) (strip-syntax (cdr x))))))))) (define-macro (loop . body) (let ((loop (gensym))) @@ -549,8 +554,9 @@ (define-syntax s1 (syntax-rules () ((_ x) (list x)))) (define-syntax s2 - (lambda (x r c) - (r `(vector (s1 ,(cadr x)))))) ) ; without renaming the local version of `s1' + (er-macro-transformer + (lambda (x r c) + (r `(vector (s1 ,(cadr x))))))) ) ; without renaming the local version of `s1' ; below will be captured (import m1) diff --git a/types.db b/types.db index 8aca3a10..7363afb0 100644 --- a/types.db +++ b/types.db @@ -269,6 +269,7 @@ (delete-file (procedure delete-file (string) string)) (enable-warnings (procedure enable-warnings (#!optional *) *)) (equal=? (procedure equal=? (* *) boolean)) +(er-macro-transformer (procedure er-macro-transformer ((procedure (* * *) *)) (struct transformer))) (errno (procedure errno () fixnum)) (error (procedure error (#!rest) noreturn)) (exit (procedure exit (#!optional fixnum) noreturn)) @@ -358,6 +359,7 @@ (getenv (deprecated get-environment-variable)) (getter-with-setter (procedure getter-with-setter (procedure procedure #!optional string) procedure)) (implicit-exit-handler (procedure implicit-exit-handler (#!optional procedure) procedure)) +(ir-macro-transformer (procedure ir-macro-transformer ((procedure (* * *) *)) (struct transformer))) (keyword->string (procedure keyword->string (symbol) string)) (keyword-style (procedure keyword-style (#!optional *) *)) (keyword? (procedure keyword? (*) boolean))Trap