;;; Ported from Scheme 48 1.9. See file COPYING for notices and license. ;;; ;;; Port Author: Andrew Whatson ;;; ;;; Original Authors: Richard Kelsey, Jonathan Rees, Mike Sperber, Will Noble ;;; ;;; scheme48-1.9.2/scheme/bcomp/transform.scm ;;; ;;; Transforms ;;; ;;; A transform represents a source-to-source rewrite rule: either a ;;; macro or an in-line procedure. (define-module (prescheme bcomp transform) #:use-module (srfi srfi-9) #:use-module (prescheme scheme48) #:use-module (prescheme record-discloser) #:use-module (prescheme bcomp binding) #:use-module (prescheme bcomp cenv) #:use-module (prescheme bcomp mtype) #:use-module (prescheme bcomp name) #:use-module (prescheme bcomp transform4) #:export (make-transform/macro make-transform/inline maybe-apply-macro-transform apply-inline-transform transform? transform-kind transform-type transform-env ;; These are used to reify transforms. transform-aux-names transform-source transform-id make-transform)) (define-record-type :transform (really-make-transform kind xformer env type aux-names source id) transform? ;; macro or inline (kind transform-kind) (xformer transform-procedure) (env transform-env) (type transform-type) (aux-names transform-aux-names) ;;for reification (source transform-source) ;;for reification (id transform-id)) (define (make-transform/macro thing env type source id) (let ((type (if (or (pair? type) (symbol? type)) (sexp->type type #t) type))) (call-with-values (lambda () (if (pair? thing) (values (car thing) (cdr thing)) (values thing #f))) (lambda (transformer aux-names) ;; The usual old-style transformers take 3 args: exp rename compare. ;; However, syntax-rules-generated transformers need a 4th arg, name?. ;; Distinguish between the two kinds. (let ((proc (cond ((explicit-renaming-transformer/4? transformer) (explicit-renaming-transformer/4-proc transformer)) (else ;; standard explicit-renaming transformers take only 3 args (lambda (exp name? rename compare) (transformer exp rename compare)))))) (make-immutable! (really-make-transform 'macro proc env type aux-names source id))))))) ;; for backwards compatibility with the PreScheme compiler (define make-transform make-transform/macro) (define (make-transform/inline thing env type source id) (let ((type (if (or (pair? type) (symbol? type)) (sexp->type type #t) type))) (make-immutable! (really-make-transform 'inline (car thing) env type (cdr thing) source id)))) (define-record-discloser :transform (lambda (m) (list 'transform (transform-id m)))) ;; See also: Rees, "Implementing Lexically Scoped Macros", ;; Lisp Pointers VI(1), January-March 1993 (define (maybe-apply-macro-transform transform exp parent-name env-of-use) (let* ((token (cons #f #f)) (new-env (bind-aliases token transform env-of-use)) (rename (make-name-generator (transform-env transform) token parent-name)) (compare (make-keyword-comparator new-env))) (values ((transform-procedure transform) exp name? rename compare) new-env))) (define (apply-inline-transform transform exp parent-name) (let* ((env (transform-env transform)) (rename (make-name-generator env (cons #f #f) parent-name))) ((transform-procedure transform) exp env rename))) ;; Two keywords are the same if: ;; - they really are the same ;; - neither one is bound and they have the same symbol in the source ;; - they are bound to the same denotation (macro or location or ...) (define (make-keyword-comparator environment) (lambda (name1 name2) (or (eqv? name1 name2) (and (name? name1) ;; why might they not be names? (name? name2) (let ((v1 (lookup environment name1)) (v2 (lookup environment name2))) (if v1 (and v2 (same-denotation? v1 v2)) (and (not v2) (equal? (name->source-name name1) (name->source-name name2))))))))) ;; Get the name that appeared in the source. (define (name->source-name name) (if (generated? name) (name->source-name (generated-name name)) name)) ;; The env-of-definition for macros defined at top-level is a package, ;; and the package system will take care of looking up the generated ;; names. (define (bind-aliases token transform env-of-use) (let ((env-of-definition (transform-env transform))) (if (compiler-env? env-of-definition) (make-compiler-env (lambda (name) (if (and (generated? name) (eq? (generated-token name) token)) (lookup env-of-definition (generated-name name)) (lookup env-of-use name))) (lambda (name type . rest) (assertion-violation 'bind-aliases "no definitions allowed" name)) (comp-env-macro-eval env-of-use) #f) env-of-use))) ;; Generate names for bindings reached in ENV reached via PARENT-NAME. ;; The names are cached to preserve identity if they are bound. TOKEN ;; is used to identify names made by this generator. (define (make-name-generator env token parent-name) (let ((alist '())) ;list of (symbol . generated) (lambda (name) (if (name? name) (let ((probe (assq name alist))) (if probe (cdr probe) (let ((new-name (make-generated name token env parent-name))) (set! alist (cons (cons name new-name) alist)) new-name))) (assertion-violation 'make-name-generator "non-name argument to rename procedure" name parent-name)))))