ref: ab876b803ac6efb812fddc8a154202b80632eb5e
dir: /lib/psyntax.ss/
;;; Portable implementation of syntax-case ;;; Extracted from Chez Scheme Version 7.3 (Feb 26, 2007) ;;; Authors: R. Kent Dybvig, Oscar Waddell, Bob Hieb, Carl Bruggeman ;;; Copyright (c) 1992-2002 Cadence Research Systems ;;; Permission to copy this software, in whole or in part, to use this ;;; software for any lawful purpose, and to redistribute this software ;;; is granted subject to the restriction that all copies made of this ;;; software must include this copyright notice in full. This software ;;; is provided AS IS, with NO WARRANTY, EITHER EXPRESS OR IMPLIED, ;;; INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY ;;; OR FITNESS FOR ANY PARTICULAR PURPOSE. IN NO EVENT SHALL THE ;;; AUTHORS BE LIABLE FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES OF ANY ;;; NATURE WHATSOEVER. ;;; Before attempting to port this code to a new implementation of ;;; Scheme, please read the notes below carefully. ;;; This file defines the syntax-case expander, sc-expand, and a set ;;; of associated syntactic forms and procedures. Of these, the ;;; following are documented in The Scheme Programming Language, ;;; Third Edition (R. Kent Dybvig, MIT Press, 2003), which can be ;;; found online at http://www.scheme.com/tspl3/. Most are also documented ;;; in the R4RS and draft R5RS. ;;; ;;; bound-identifier=? ;;; datum->syntax-object ;;; define-syntax ;;; fluid-let-syntax ;;; free-identifier=? ;;; generate-temporaries ;;; identifier? ;;; identifier-syntax ;;; let-syntax ;;; letrec-syntax ;;; syntax ;;; syntax-case ;;; syntax-object->datum ;;; syntax-rules ;;; with-syntax ;;; ;;; All standard Scheme syntactic forms are supported by the expander ;;; or syntactic abstractions defined in this file. Only the R4RS ;;; delay is omitted, since its expansion is implementation-dependent. ;;; Also defined are three forms that support modules: module, import, ;;; and import-only. These are documented in the Chez Scheme User's ;;; Guide (R. Kent Dybvig, Cadence Research Systems, 1998), which can ;;; also be found online at http://www.scheme.com/csug/. They are ;;; described briefly here as well. ;;; All are definitions and may appear where and only where other ;;; definitions may appear. modules may be named: ;;; ;;; (module id (ex ...) defn ... init ...) ;;; ;;; or anonymous: ;;; ;;; (module (ex ...) defn ... init ...) ;;; ;;; The latter form is semantically equivalent to: ;;; ;;; (module T (ex ...) defn ... init ...) ;;; (import T) ;;; ;;; where T is a fresh identifier. ;;; ;;; In either form, each of the exports in (ex ...) is either an ;;; identifier or of the form (id ex ...). In the former case, the ;;; single identifier ex is exported. In the latter, the identifier ;;; id is exported and the exports ex ... are "implicitly" exported. ;;; This listing of implicit exports is useful only when id is a ;;; keyword bound to a transformer that expands into references to ;;; the listed implicit exports. In the present implementation, ;;; listing of implicit exports is necessary only for top-level ;;; modules and allows the implementation to avoid placing all ;;; identifiers into the top-level environment where subsequent passes ;;; of the compiler will be unable to deal effectively with them. ;;; ;;; Named modules may be referenced in import statements, which ;;; always take one of the forms: ;;; ;;; (import id) ;;; (import-only id) ;;; ;;; id must name a module. Each exported identifier becomes visible ;;; within the scope of the import form. In the case of import-only, ;;; all other identifiers become invisible in the scope of the ;;; import-only form, except for those established by definitions ;;; that appear textually after the import-only form. ;;; import and import-only also support a variety of identifier ;;; selection and renaming forms: only, except, add-prefix, ;;; drop-prefix, rename, and alias. ;;; ;;; (import (only m x y)) ;;; ;;; imports x and y (and nothing else) from m. ;;; ;;; (import (except m x y)) ;;; ;;; imports all of m's imports except for x and y. ;;; ;;; (import (add-prefix (only m x y) m:)) ;;; ;;; imports x and y as m:x and m:y. ;;; ;;; (import (drop-prefix m foo:)) ;;; ;;; imports all of m's imports, dropping the common foo: prefix ;;; (which must appear on all of m's exports). ;;; ;;; (import (rename (except m a b) (m-c c) (m-d d))) ;;; ;;; imports all of m's imports except for x and y, renaming c ;;; m-c and d m-d. ;;; ;;; (import (alias (except m a b) (m-c c) (m-d d))) ;;; ;;; imports all of m's imports except for x and y, with additional ;;; aliases m-c for c and m-d for d. ;;; ;;; multiple imports may be specified with one import form: ;;; ;;; (import (except m1 x) (only m2 x)) ;;; ;;; imports all of m1's exports except for x plus x from m2. ;;; Another form, meta, may be used as a prefix for any definition and ;;; causes any resulting variable bindings to be created at expansion ;;; time. Meta variables (variables defined using meta) are available ;;; only at expansion time. Meta definitions are often used to create ;;; data and helpers that can be shared by multiple macros, for example: ;;; (module (alpha beta) ;;; (meta define key-error ;;; (lambda (key) ;;; (syntax-error key "invalid key"))) ;;; (meta define parse-keys ;;; (lambda (keys) ;;; (let f ((keys keys) (c #'white) (s 10)) ;;; (syntax-case keys (color size) ;;; (() (list c s)) ;;; (((color c) . keys) (f #'keys #'c s)) ;;; (((size s) . keys) (f #'keys c #'s)) ;;; ((k . keys) (key-error #'k)))))) ;;; (define-syntax alpha ;;; (lambda (x) ;;; (syntax-case x () ;;; ((_ (k ...) <other stuff>) ;;; (with-syntax (((c s) (parse-keys (syntax (k ...))))) ;;; ---))))) ;;; (define-syntax beta ;;; (lambda (x) ;;; (syntax-case x () ;;; ((_ (k ...) <other stuff>) ;;; (with-syntax (((c s) (parse-keys (syntax (k ...))))) ;;; ---)))))) ;;; As with define-syntax rhs expressions, meta expressions can evaluate ;;; references only to identifiers whose values are (already) available ;;; in the compile-time environment, e.g., macros and meta variables. ;;; They can, however, like define-syntax rhs expressions, build syntax ;;; objects containing occurrences of any identifiers in their scope. ;;; meta definitions propagate through macro expansion, so one can write, ;;; for example: ;;; ;;; (module (a) ;;; (meta define-structure (foo x)) ;;; (define-syntax a ;;; (let ((q (make-foo (syntax 'q)))) ;;; (lambda (x) ;;; (foo-x q))))) ;;; a -> q ;;; ;;; where define-record is a macro that expands into a set of defines. ;;; ;;; It is also sometimes convenient to write ;;; ;;; (meta begin defn ...) ;;; ;;; or ;;; ;;; (meta module {exports} defn ...) ;;; ;;; to create groups of meta bindings. ;;; Another form, alias, is used to create aliases from one identifier ;;; to another. This is used primarily to support the extended import ;;; syntaxes (add-prefix, drop-prefix, rename, and alias). ;;; (let ((x 3)) (alias y x) y) -> 3 ;;; The remaining exports are listed below. sc-expand, eval-when, and ;;; syntax-error are described in the Chez Scheme User's Guide. ;;; ;;; (sc-expand datum) ;;; if datum represents a valid expression, sc-expand returns an ;;; expanded version of datum in a core language that includes no ;;; syntactic abstractions. The core language includes begin, ;;; define, if, lambda, letrec, quote, and set!. ;;; (eval-when situations expr ...) ;;; conditionally evaluates expr ... at compile-time or run-time ;;; depending upon situations ;;; (syntax-error object message) ;;; used to report errors found during expansion ;;; ($syntax-dispatch e p) ;;; used by expanded code to handle syntax-case matching ;;; ($sc-put-cte symbol val top-token) ;;; used to establish top-level compile-time (expand-time) bindings. ;;; The following nonstandard procedures must be provided by the ;;; implementation for this code to run. ;;; ;;; (void) ;;; returns the implementation's cannonical "unspecified value". The ;;; following usually works: ;;; ;;; (define void (lambda () (if #f #f))). ;;; ;;; (andmap proc list1 list2 ...) ;;; returns true if proc returns true when applied to each element of list1 ;;; along with the corresponding elements of list2 .... The following ;;; definition works but does no error checking: ;;; ;;; (define andmap ;;; (lambda (f first . rest) ;;; (or (null? first) ;;; (if (null? rest) ;;; (let andmap ((first first)) ;;; (let ((x (car first)) (first (cdr first))) ;;; (if (null? first) ;;; (f x) ;;; (and (f x) (andmap first))))) ;;; (let andmap ((first first) (rest rest)) ;;; (let ((x (car first)) ;;; (xr (map car rest)) ;;; (first (cdr first)) ;;; (rest (map cdr rest))) ;;; (if (null? first) ;;; (apply f (cons x xr)) ;;; (and (apply f (cons x xr)) (andmap first rest))))))))) ;;; ;;; (ormap proc list1) ;;; returns the first non-false return result of proc applied to ;;; the elements of list1 or false if none. The following definition ;;; works but does no error checking: ;;; ;;; (define ormap ;;; (lambda (proc list1) ;;; (and (not (null? list1)) ;;; (or (proc (car list1)) (ormap proc (cdr list1)))))) ;;; ;;; The following nonstandard procedures must also be provided by the ;;; implementation for this code to run using the standard portable ;;; hooks and output constructors. They are not used by expanded code, ;;; and so need be present only at expansion time. ;;; ;;; (eval x) ;;; where x is always in the form ("noexpand" expr). ;;; returns the value of expr. the "noexpand" flag is used to tell the ;;; evaluator/expander that no expansion is necessary, since expr has ;;; already been fully expanded to core forms. ;;; ;;; eval will not be invoked during the loading of psyntax.pp. After ;;; psyntax.pp has been loaded, the expansion of any macro definition, ;;; whether local or global, results in a call to eval. If, however, ;;; sc-expand has already been registered as the expander to be used ;;; by eval, and eval accepts one argument, nothing special must be done ;;; to support the "noexpand" flag, since it is handled by sc-expand. ;;; ;;; (error who format-string why what) ;;; where who is either a symbol or #f, format-string is always "~a ~s", ;;; why is always a string, and what may be any object. error should ;;; signal an error with a message something like ;;; ;;; "error in <who>: <why> <what>" ;;; ;;; (gensym) ;;; returns a unique symbol each time it's called. In Chez Scheme, gensym ;;; returns a symbol with a "globally" unique name so that gensyms that ;;; end up in the object code of separately compiled files cannot conflict. ;;; This is necessary only if you intend to support compiled files. ;;; ;;; (gensym? x) ;;; returns #t if x is a gensym, otherwise false. ;;; ;;; (putprop symbol key value) ;;; (getprop symbol key) ;;; (remprop symbol key) ;;; key is always a symbol; value may be any object. putprop should ;;; associate the given value with the given symbol and key in some way ;;; that it can be retrieved later with getprop. getprop should return ;;; #f if no value is associated with the given symbol and key. remprop ;;; should remove the association between the given symbol and key. ;;; When porting to a new Scheme implementation, you should define the ;;; procedures listed above, load the expanded version of psyntax.ss ;;; (psyntax.pp, which should be available whereever you found ;;; psyntax.ss), and register sc-expand as the current expander (how ;;; you do this depends upon your implementation of Scheme). You may ;;; change the hooks and constructors defined toward the beginning of ;;; the code below, but to avoid bootstrapping problems, do so only ;;; after you have a working version of the expander. ;;; Chez Scheme allows the syntactic form (syntax <template>) to be ;;; abbreviated to #'<template>, just as (quote <datum>) may be ;;; abbreviated to '<datum>. The #' syntax makes programs written ;;; using syntax-case shorter and more readable and draws out the ;;; intuitive connection between syntax and quote. If you have access ;;; to the source code of your Scheme system's reader, you might want ;;; to implement this extension. ;;; If you find that this code loads or runs slowly, consider ;;; switching to faster hardware or a faster implementation of ;;; Scheme. In Chez Scheme on a 200Mhz Pentium Pro, expanding, ;;; compiling (with full optimization), and loading this file takes ;;; between one and two seconds. ;;; In the expander implementation, we sometimes use syntactic abstractions ;;; when procedural abstractions would suffice. For example, we define ;;; top-wrap and top-marked? as ;;; (define-syntax top-wrap (identifier-syntax '((top)))) ;;; (define-syntax top-marked? ;;; (syntax-rules () ;;; ((_ w) (memq 'top (wrap-marks w))))) ;;; rather than ;;; (define top-wrap '((top))) ;;; (define top-marked? ;;; (lambda (w) (memq 'top (wrap-marks w)))) ;;; On ther other hand, we don't do this consistently; we define make-wrap, ;;; wrap-marks, and wrap-subst simply as ;;; (define make-wrap cons) ;;; (define wrap-marks car) ;;; (define wrap-subst cdr) ;;; In Chez Scheme, the syntactic and procedural forms of these ;;; abstractions are equivalent, since the optimizer consistently ;;; integrates constants and small procedures. Some Scheme ;;; implementations, however, may benefit from more consistent use ;;; of one form or the other. ;;; Implementation notes: ;;; "begin" is treated as a splicing construct at top level and at ;;; the beginning of bodies. Any sequence of expressions that would ;;; be allowed where the "begin" occurs is allowed. ;;; "let-syntax" and "letrec-syntax" are also treated as splicing ;;; constructs, in violation of the R5RS. A consequence is that let-syntax ;;; and letrec-syntax do not create local contours, as do let and letrec. ;;; Although the functionality is greater as it is presently implemented, ;;; we will probably change it to conform to the R5RS. modules provide ;;; similar functionality to nonsplicing letrec-syntax when the latter is ;;; used as a definition. ;;; Objects with no standard print syntax, including objects containing ;;; cycles and syntax objects, are allowed in quoted data as long as they ;;; are contained within a syntax form or produced by datum->syntax-object. ;;; Such objects are never copied. ;;; When the expander encounters a reference to an identifier that has ;;; no global or lexical binding, it treats it as a global-variable ;;; reference. This allows one to write mutually recursive top-level ;;; definitions, e.g.: ;;; ;;; (define f (lambda (x) (g x))) ;;; (define g (lambda (x) (f x))) ;;; ;;; but may not always yield the intended when the variable in question ;;; is later defined as a keyword. ;;; Top-level variable definitions of syntax keywords are permitted. ;;; In order to make this work, top-level define not only produces a ;;; top-level definition in the core language, but also modifies the ;;; compile-time environment (using $sc-put-cte) to record the fact ;;; that the identifier is a variable. ;;; Top-level definitions of macro-introduced identifiers are visible ;;; only in code produced by the macro. That is, a binding for a ;;; hidden (generated) identifier is created instead, and subsequent ;;; references within the macro output are renamed accordingly. For ;;; example: ;;; ;;; (define-syntax a ;;; (syntax-rules () ;;; ((_ var exp) ;;; (begin ;;; (define secret exp) ;;; (define var ;;; (lambda () ;;; (set! secret (+ secret 17)) ;;; secret)))))) ;;; (a x 0) ;;; (x) => 17 ;;; (x) => 34 ;;; secret => Error: variable secret is not bound ;;; ;;; The definition above would fail if the definition for secret ;;; were placed after the definition for var, since the expander would ;;; encounter the references to secret before the definition that ;;; establishes the compile-time map from the identifier secret to ;;; the generated identifier. ;;; Identifiers and syntax objects are implemented as vectors for ;;; portability. As a result, it is possible to "forge" syntax ;;; objects. ;;; The input to sc-expand may contain "annotations" describing, e.g., the ;;; source file and character position from where each object was read if ;;; it was read from a file. These annotations are handled properly by ;;; sc-expand only if the annotation? hook (see hooks below) is implemented ;;; properly and the operators annotation-expression and annotation-stripped ;;; are supplied. If annotations are supplied, the proper annotated ;;; expression is passed to the various output constructors, allowing ;;; implementations to accurately correlate source and expanded code. ;;; Contact one of the authors for details if you wish to make use of ;;; this feature. ;;; Implementation of modules: ;;; ;;; The implementation of modules requires that implicit top-level exports ;;; be listed with the exported macro at some level where both are visible, ;;; e.g., ;;; ;;; (module M (alpha (beta b)) ;;; (module ((alpha a) b) ;;; (define-syntax alpha (identifier-syntax a)) ;;; (define a 'a) ;;; (define b 'b)) ;;; (define-syntax beta (identifier-syntax b))) ;;; ;;; Listing of implicit imports is not needed for macros that do not make ;;; it out to top level, including all macros that are local to a "body". ;;; (They may be listed in this case, however.) We need this information ;;; for top-level modules since a top-level module expands into a letrec ;;; for non-top-level variables and top-level definitions (assignments) for ;;; top-level variables. Because of the general nature of macro ;;; transformers, we cannot determine the set of implicit exports from the ;;; transformer code, so without the user's help, we'd have to put all ;;; variables at top level. ;;; ;;; Each such top-level identifier is given a generated name (gensym). ;;; When a top-level module is imported at top level, a compile-time ;;; alias is established from the top-level name to the generated name. ;;; The expander follows these aliases transparently. When any module is ;;; imported anywhere other than at top level, the id-var-name of the ;;; import identifier is set to the id-var-name of the export identifier. ;;; Since we can't determine the actual labels for identifiers defined in ;;; top-level modules until we determine which are placed in the letrec ;;; and which make it to top level, we give each an "indirect" label---a ;;; pair whose car will eventually contain the actual label. Import does ;;; not follow the indirect, but id-var-name does. ;;; ;;; All identifiers defined within a local module are folded into the ;;; letrec created for the enclosing body. Visibility is controlled in ;;; this case and for nested top-level modules by introducing a new wrap ;;; for each module. ;;; Bootstrapping: ;;; When changing syntax-object representations, it is necessary to support ;;; both old and new syntax-object representations in id-var-name. It ;;; should be sufficient to redefine syntax-object-expression to work for ;;; both old and new representations and syntax-object-wrap to return the ;;; empty-wrap for old representations. ;;; The following set of definitions establishes bindings for the ;;; top-level variables assigned values in the let expression below. ;;; Uncomment them here and copy them to the front of psyntax.pp if ;;; required by your system. ; (define $sc-put-cte #f) ; (define sc-expand #f) ; (define $make-environment #f) ; (define environment? #f) ; (define interaction-environment #f) ; (define identifier? #f) ; (define syntax->list #f) ; (define syntax-object->datum #f) ; (define datum->syntax-object #f) ; (define generate-temporaries #f) ; (define free-identifier=? #f) ; (define bound-identifier=? #f) ; (define literal-identifier=? #f) ; (define syntax-error #f) ; (define $syntax-dispatch #f) (let () (define-syntax when (syntax-rules () ((_ test e1 e2 ...) (if test (begin e1 e2 ...))))) (define-syntax unless (syntax-rules () ((_ test e1 e2 ...) (when (not test) (begin e1 e2 ...))))) (define-syntax define-structure (lambda (x) (define construct-name (lambda (template-identifier . args) (datum->syntax-object template-identifier (string->symbol (apply string-append (map (lambda (x) (if (string? x) x (symbol->string (syntax-object->datum x)))) args)))))) (syntax-case x () ((_ (name id1 ...)) (andmap identifier? (syntax (name id1 ...))) (with-syntax ((constructor (construct-name (syntax name) "make-" (syntax name))) (predicate (construct-name (syntax name) (syntax name) "?")) ((access ...) (map (lambda (x) (construct-name x (syntax name) "-" x)) (syntax (id1 ...)))) ((assign ...) (map (lambda (x) (construct-name x "set-" (syntax name) "-" x "!")) (syntax (id1 ...)))) (structure-length (+ (length (syntax (id1 ...))) 1)) ((index ...) (let f ((i 1) (ids (syntax (id1 ...)))) (if (null? ids) '() (cons i (f (+ i 1) (cdr ids))))))) (syntax (begin (define constructor (lambda (id1 ...) (vector 'name id1 ... ))) (define predicate (lambda (x) (and (vector? x) (= (vector-length x) structure-length) (eq? (vector-ref x 0) 'name)))) (define access (lambda (x) (vector-ref x index))) ... (define assign (lambda (x update) (vector-set! x index update))) ...))))))) (define-syntax let-values ; impoverished one-clause version (syntax-rules () ((_ ((formals expr)) form1 form2 ...) (call-with-values (lambda () expr) (lambda formals form1 form2 ...))))) (define noexpand "noexpand") (define-structure (syntax-object expression wrap)) ;;; hooks to nonportable run-time helpers (begin (define-syntax fx+ (identifier-syntax +)) (define-syntax fx- (identifier-syntax -)) (define-syntax fx= (identifier-syntax =)) (define-syntax fx< (identifier-syntax <)) (define-syntax fx> (identifier-syntax >)) (define-syntax fx<= (identifier-syntax <=)) (define-syntax fx>= (identifier-syntax >=)) (define annotation? (lambda (x) #f)) ; top-level-eval-hook is used to create "permanent" code (e.g., top-level ; transformers), so it might be a good idea to compile it (define top-level-eval-hook (lambda (x) (eval `(,noexpand ,x)))) ; local-eval-hook is used to create "temporary" code (e.g., local ; transformers), so it might be a good idea to interpret it (define local-eval-hook (lambda (x) (eval `(,noexpand ,x)))) (define define-top-level-value-hook (lambda (sym val) (top-level-eval-hook (build-global-definition no-source sym (build-data no-source val))))) (define error-hook (lambda (who why what) (error who "~a ~s" why what))) (define put-cte-hook (lambda (symbol val) ($sc-put-cte symbol val '*top*))) (define get-global-definition-hook (lambda (symbol) (getprop symbol '*sc-expander*))) (define put-global-definition-hook (lambda (symbol x) (if (not x) (remprop symbol '*sc-expander*) (putprop symbol '*sc-expander* x)))) ; if you treat certain bindings (say from environments like ieee or r5rs) ; read-only, this should return #t for those bindings (define read-only-binding? (lambda (symbol) #f)) ; should return #f if symbol has no binding for token (define get-import-binding (lambda (symbol token) (getprop symbol token))) ; remove binding if x is false (define update-import-binding! (lambda (symbol token p) (let ((x (p (get-import-binding symbol token)))) (if (not x) (remprop symbol token) (putprop symbol token x))))) ;;; generate-id ideally produces globally unique symbols, i.e., symbols ;;; unique across system runs, to support separate compilation/expansion. ;;; Use gensyms if you do not need to support separate compilation/ ;;; expansion or if your system's gensym creates globally unique ;;; symbols (as in Chez Scheme). Otherwise, use the following code ;;; as a starting point. session-key should be a unique string for each ;;; system run to support separate compilation; the default value given ;;; is satisfactory during initial development only. (define generate-id (let ((digits "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!$%&*/:<=>?~_^.+-")) (let ((base (string-length digits)) (session-key "_")) (define make-digit (lambda (x) (string-ref digits x))) (define fmt (lambda (n) (let fmt ((n n) (a '())) (if (< n base) (list->string (cons (make-digit n) a)) (let ((r (modulo n base)) (rest (quotient n base))) (fmt rest (cons (make-digit r) a))))))) (let ((n -1)) (lambda (name) ; name is #f or a symbol (set! n (+ n 1)) (string->symbol (string-append session-key (fmt n)))))))) ) ;;; output constructors (begin (define-syntax build-application (syntax-rules () ((_ ae fun-exp arg-exps) `(,fun-exp . ,arg-exps)))) (define-syntax build-conditional (syntax-rules () ((_ ae test-exp then-exp else-exp) `(if ,test-exp ,then-exp ,else-exp)))) (define-syntax build-lexical-reference (syntax-rules () ((_ type ae var) var))) (define-syntax build-lexical-assignment (syntax-rules () ((_ ae var exp) `(set! ,var ,exp)))) (define-syntax build-global-reference (syntax-rules () ((_ ae var) var))) (define-syntax build-global-assignment (syntax-rules () ((_ ae var exp) `(set! ,var ,exp)))) (define-syntax build-global-definition (syntax-rules () ((_ ae var exp) `(define ,var ,exp)))) (define-syntax build-cte-install ; should build a call that has the same effect as calling put-cte-hook (syntax-rules () ((_ sym exp token) `($sc-put-cte ',sym ,exp ',token)))) (define-syntax build-visit-only ; should mark the result as "visit only" for compile-file ; in implementations that support visit/revisit (syntax-rules () ((_ exp) exp))) (define-syntax build-revisit-only ; should mark the result as "revisit only" for compile-file, ; in implementations that support visit/revisit (syntax-rules () ((_ exp) exp))) (define-syntax build-lambda (syntax-rules () ((_ ae vars exp) `(lambda ,vars ,exp)))) (define built-lambda? (lambda (x) (and (pair? x) (eq? (car x) 'lambda)))) (define-syntax build-primref (syntax-rules () ((_ ae name) name) ((_ ae level name) name))) (define-syntax build-data (syntax-rules () ((_ ae exp) `',exp))) (define build-sequence (lambda (ae exps) (let loop ((exps exps)) (if (null? (cdr exps)) (car exps) ; weed out leading void calls, assuming ordinary list representation (if (equal? (car exps) '(void)) (loop (cdr exps)) `(begin ,@exps)))))) (define build-letrec (lambda (ae vars val-exps body-exp) (if (null? vars) body-exp `(letrec ,(map list vars val-exps) ,body-exp)))) (define build-body (lambda (ae vars val-exps body-exp) (build-letrec ae vars val-exps body-exp))) (define build-top-module ; each type is either global (exported) or local (not exported) ; we produce global definitions and assignments for globals and ; letrec bindings for locals. if you don't need the definitions, ; (just assignments) you can eliminate them. if you wish to ; have your module definitions ordered from left-to-right (ala ; letrec*), you can replace the global var-exps with dummy vars ; and global val-exps with global assignments, and produce a letrec* ; in place of a letrec. (lambda (ae types vars val-exps body-exp) (let-values (((vars defns sets) (let f ((types types) (vars vars)) (if (null? types) (values '() '() '()) (let ((var (car vars))) (let-values (((vars defns sets) (f (cdr types) (cdr vars)))) (if (eq? (car types) 'global) (let ((x (build-lexical-var no-source var))) (values (cons x vars) (cons (build-global-definition no-source var (chi-void)) defns) (cons (build-global-assignment no-source var (build-lexical-reference 'value no-source x)) sets))) (values (cons var vars) defns sets)))))))) (if (null? defns) (build-letrec ae vars val-exps body-exp) (build-sequence no-source (append defns (list (build-letrec ae vars val-exps (build-sequence no-source (append sets (list body-exp))))))))))) (define-syntax build-lexical-var (syntax-rules () ((_ ae id) (gensym)))) (define-syntax lexical-var? gensym?) (define-syntax self-evaluating? (syntax-rules () ((_ e) (let ((x e)) (or (boolean? x) (number? x) (string? x) (char? x) (null? x)))))) ) (define-syntax unannotate (syntax-rules () ((_ x) (let ((e x)) (if (annotation? e) (annotation-expression e) e))))) (define-syntax no-source (identifier-syntax #f)) (define-syntax arg-check (syntax-rules () ((_ pred? e who) (let ((x e)) (if (not (pred? x)) (error-hook who "invalid argument" x)))))) ;;; compile-time environments ;;; wrap and environment comprise two level mapping. ;;; wrap : id --> label ;;; env : label --> <element> ;;; environments are represented in two parts: a lexical part and a global ;;; part. The lexical part is a simple list of associations from labels ;;; to bindings. The global part is implemented by ;;; {put,get}-global-definition-hook and associates symbols with ;;; bindings. ;;; global (assumed global variable) and displaced-lexical (see below) ;;; do not show up in any environment; instead, they are fabricated by ;;; lookup when it finds no other bindings. ;;; <environment> ::= ((<label> . <binding>)*) ;;; identifier bindings include a type and a value ;;; <binding> ::= <procedure> macro keyword ;;; (macro . <procedure>) macro keyword ;;; (deferred . <thunk>) macro keyword w/lazily evaluated transformer ;;; (macro! . <procedure>) extended identifier macro keyword ;;; (core . <procedure>) core keyword ;;; (begin) begin keyword ;;; (define) define keyword ;;; (define-syntax) define-syntax keyword ;;; (local-syntax . <boolean>) let-syntax (#f)/letrec-syntax (#t) keyword ;;; (eval-when) eval-when keyword ;;; (set!) set! keyword ;;; (meta) meta keyword ;;; ($module-key) $module keyword ;;; ($import) $import keyword ;;; ($module . <interface>) modules ;;; (syntax . (<var> . <level>)) pattern variables ;;; (global . <symbol>) assumed global variable ;;; (meta-variable . <symbol>) meta variable ;;; (lexical . <var>) lexical variables ;;; (displaced-lexical . #f) id-var-name not found in store ;;; <level> ::= <nonnegative integer> ;;; <var> ::= variable returned by build-lexical-var ;;; a macro is a user-defined syntactic-form. a core is a system-defined ;;; syntactic form. begin, define, define-syntax, let-syntax, letrec-syntax, ;;; eval-when, and meta are treated specially since they are sensitive to ;;; whether the form is at top-level and can denote valid internal ;;; definitions. ;;; a pattern variable is a variable introduced by syntax-case and can ;;; be referenced only within a syntax form. ;;; any identifier for which no top-level syntax definition or local ;;; binding of any kind has been seen is assumed to be a global ;;; variable. ;;; a lexical variable is a lambda- or letrec-bound variable. ;;; a displaced-lexical identifier is a lexical identifier removed from ;;; it's scope by the return of a syntax object containing the identifier. ;;; a displaced lexical can also appear when a letrec-syntax-bound ;;; keyword is referenced on the rhs of one of the letrec-syntax clauses. ;;; a displaced lexical should never occur with properly written macros. (define sanitize-binding (lambda (b) (cond ((procedure? b) (make-binding 'macro b)) ((binding? b) (and (case (binding-type b) ((core macro macro! deferred) (and (procedure? (binding-value b)))) (($module) (interface? (binding-value b))) ((lexical) (lexical-var? (binding-value b))) ((global meta-variable) (symbol? (binding-value b))) ((syntax) (let ((x (binding-value b))) (and (pair? x) (lexical-var? (car x)) (let ((n (cdr x))) (and (integer? n) (exact? n) (>= n 0)))))) ((begin define define-syntax set! $module-key $import eval-when meta) (null? (binding-value b))) ((local-syntax) (boolean? (binding-value b))) ((displaced-lexical) (eq? (binding-value b) #f)) (else #t)) b)) (else #f)))) (define-syntax make-binding (syntax-rules (quote) ((_ 'type #f) '(type . #f)) ((_ type value) (cons type value)))) (define binding-type car) (define binding-value cdr) (define set-binding-type! set-car!) (define set-binding-value! set-cdr!) (define binding? (lambda (x) (and (pair? x) (symbol? (car x))))) (define-syntax null-env (identifier-syntax '())) (define extend-env (lambda (label binding r) (cons (cons label binding) r))) (define extend-env* (lambda (labels bindings r) (if (null? labels) r (extend-env* (cdr labels) (cdr bindings) (extend-env (car labels) (car bindings) r))))) (define extend-var-env* ; variant of extend-env* that forms "lexical" binding (lambda (labels vars r) (if (null? labels) r (extend-var-env* (cdr labels) (cdr vars) (extend-env (car labels) (make-binding 'lexical (car vars)) r))))) (define (displaced-lexical? id r) (let ((n (id-var-name id empty-wrap))) (and n (let ((b (lookup n r))) (eq? (binding-type b) 'displaced-lexical))))) (define displaced-lexical-error (lambda (id) (syntax-error id (if (id-var-name id empty-wrap) "identifier out of context" "identifier not visible")))) (define lookup* ; x may be a label or a symbol ; although symbols are usually global, we check the environment first ; anyway because a temporary binding may have been established by ; fluid-let-syntax (lambda (x r) (cond ((assq x r) => cdr) ((symbol? x) (or (get-global-definition-hook x) (make-binding 'global x))) (else (make-binding 'displaced-lexical #f))))) (define lookup (lambda (x r) (define whack-binding! (lambda (b *b) (set-binding-type! b (binding-type *b)) (set-binding-value! b (binding-value *b)))) (let ((b (lookup* x r))) (when (eq? (binding-type b) 'deferred) (whack-binding! b (make-transformer-binding ((binding-value b))))) b))) (define make-transformer-binding (lambda (b) (or (sanitize-binding b) (syntax-error b "invalid transformer")))) (define defer-or-eval-transformer (lambda (eval x) (if (built-lambda? x) (make-binding 'deferred (lambda () (eval x))) (make-transformer-binding (eval x))))) (define global-extend (lambda (type sym val) (put-cte-hook sym (make-binding type val)))) ;;; Conceptually, identifiers are always syntax objects. Internally, ;;; however, the wrap is sometimes maintained separately (a source of ;;; efficiency and confusion), so that symbols are also considered ;;; identifiers by id?. Externally, they are always wrapped. (define nonsymbol-id? (lambda (x) (and (syntax-object? x) (symbol? (unannotate (syntax-object-expression x)))))) (define id? (lambda (x) (cond ((symbol? x) #t) ((syntax-object? x) (symbol? (unannotate (syntax-object-expression x)))) ((annotation? x) (symbol? (annotation-expression x))) (else #f)))) (define-syntax id-sym-name (syntax-rules () ((_ e) (let ((x e)) (unannotate (if (syntax-object? x) (syntax-object-expression x) x)))))) (define id-marks (lambda (id) (if (syntax-object? id) (wrap-marks (syntax-object-wrap id)) (wrap-marks top-wrap)))) (define id-subst (lambda (id) (if (syntax-object? id) (wrap-subst (syntax-object-wrap id)) (wrap-marks top-wrap)))) (define id-sym-name&marks (lambda (x w) (if (syntax-object? x) (values (unannotate (syntax-object-expression x)) (join-marks (wrap-marks w) (wrap-marks (syntax-object-wrap x)))) (values (unannotate x) (wrap-marks w))))) ;;; syntax object wraps ;;; <wrap> ::= ((<mark> ...) . (<subst> ...)) ;;; <subst> ::= <ribcage> | <shift> ;;; <ribcage> ::= #((<ex-symname> ...) (<mark> ...) (<label> ...)) ; extensible, for chi-internal/external ;;; | #(#(<symname> ...) #(<mark> ...) #(<label> ...)) ; nonextensible ;;; <ex-symname> ::= <symname> | <import token> | <barrier> ;;; <shift> ::= shift ;;; <barrier> ::= #f ; inserted by import-only ;;; <import interface> ::= #<import-interface interface new-marks> ;;; <token> ::= <generated id> (define make-wrap cons) (define wrap-marks car) (define wrap-subst cdr) (define-syntax empty-wrap (identifier-syntax '(()))) (define-syntax top-wrap (identifier-syntax '((top)))) (define-syntax tmp-wrap (identifier-syntax '((tmp)))) ; for generate-temporaries (define-syntax top-marked? (syntax-rules () ((_ w) (memq 'top (wrap-marks w))))) (define-syntax only-top-marked? (syntax-rules () ((_ id) (same-marks? (wrap-marks (syntax-object-wrap id)) (wrap-marks top-wrap))))) ;;; labels ;;; simple labels must be comparable with "eq?" and distinct from symbols ;;; and pairs. ;;; indirect labels, which are implemented as pairs, are used to support ;;; import aliasing for identifiers exported (explictly or implicitly) from ;;; top-level modules. chi-external creates an indirect label for each ;;; defined identifier, import causes the pair to be shared with aliases it ;;; establishes, and chi-top-module whacks the pair to hold the top-level ;;; identifier name (symbol) if the id is to be placed at top level, before ;;; expanding the right-hand sides of the definitions in the module. (module (gen-indirect-label indirect-label? get-indirect-label set-indirect-label!) (define-structure (indirect-label label)) (define gen-indirect-label (lambda () (make-indirect-label (gen-label)))) (define get-indirect-label (lambda (x) (indirect-label-label x))) (define set-indirect-label! (lambda (x v) (set-indirect-label-label! x v)))) (define gen-label (lambda () (string #\i))) (define label? (lambda (x) (or (string? x) ; normal lexical labels (symbol? x) ; global labels (symbolic names) (indirect-label? x)))) (define gen-labels (lambda (ls) (if (null? ls) '() (cons (gen-label) (gen-labels (cdr ls)))))) (define-structure (ribcage symnames marks labels)) (define-structure (top-ribcage key mutable?)) (define-structure (import-interface interface new-marks)) (define-structure (env top-ribcage wrap)) ;;; Marks must be comparable with "eq?" and distinct from pairs and ;;; the symbol top. We do not use integers so that marks will remain ;;; unique even across file compiles. (define-syntax the-anti-mark (identifier-syntax #f)) (define anti-mark (lambda (w) (make-wrap (cons the-anti-mark (wrap-marks w)) (cons 'shift (wrap-subst w))))) (define-syntax new-mark (syntax-rules () ((_) (string #\m)))) (define barrier-marker #f) ;;; make-empty-ribcage and extend-ribcage maintain list-based ribcages for ;;; internal definitions, in which the ribcages are built incrementally (define-syntax make-empty-ribcage (syntax-rules () ((_) (make-ribcage '() '() '())))) (define extend-ribcage! ; must receive ids with complete wraps ; ribcage guaranteed to be list-based (lambda (ribcage id label) (set-ribcage-symnames! ribcage (cons (unannotate (syntax-object-expression id)) (ribcage-symnames ribcage))) (set-ribcage-marks! ribcage (cons (wrap-marks (syntax-object-wrap id)) (ribcage-marks ribcage))) (set-ribcage-labels! ribcage (cons label (ribcage-labels ribcage))))) (define import-extend-ribcage! ; must receive ids with complete wraps ; ribcage guaranteed to be list-based (lambda (ribcage new-marks id label) (set-ribcage-symnames! ribcage (cons (unannotate (syntax-object-expression id)) (ribcage-symnames ribcage))) (set-ribcage-marks! ribcage (cons (join-marks new-marks (wrap-marks (syntax-object-wrap id))) (ribcage-marks ribcage))) (set-ribcage-labels! ribcage (cons label (ribcage-labels ribcage))))) (define extend-ribcage-barrier! ; must receive ids with complete wraps ; ribcage guaranteed to be list-based (lambda (ribcage killer-id) (extend-ribcage-barrier-help! ribcage (syntax-object-wrap killer-id)))) (define extend-ribcage-barrier-help! (lambda (ribcage wrap) (set-ribcage-symnames! ribcage (cons barrier-marker (ribcage-symnames ribcage))) (set-ribcage-marks! ribcage (cons (wrap-marks wrap) (ribcage-marks ribcage))))) (define extend-ribcage-subst! ; ribcage guaranteed to be list-based (lambda (ribcage import-iface) (set-ribcage-symnames! ribcage (cons import-iface (ribcage-symnames ribcage))))) (define lookup-import-binding-name (lambda (sym marks token new-marks) (let ((new (get-import-binding sym token))) (and new (let f ((new new)) (cond ((pair? new) (or (f (car new)) (f (cdr new)))) ((symbol? new) (and (same-marks? marks (join-marks new-marks (wrap-marks top-wrap))) new)) ((same-marks? marks (join-marks new-marks (wrap-marks (syntax-object-wrap new)))) new) (else #f))))))) (define store-import-binding (lambda (id token new-marks) (define cons-id (lambda (id x) (if (not x) id (cons id x)))) (define weed ; remove existing binding for id, if any (lambda (marks x) (if (pair? x) (if (same-marks? (id-marks (car x)) marks) (weed marks (cdr x)) (cons-id (car x) (weed marks (cdr x)))) (and x (not (same-marks? (id-marks x) marks)) x)))) (let ((id (if (null? new-marks) id (make-syntax-object (id-sym-name id) (make-wrap (join-marks new-marks (id-marks id)) (id-subst id)))))) (let ((sym (id-sym-name id))) ; no need to record bindings mapping symbol to self, since this ; assumed by default. (unless (eq? id sym) (let ((marks (id-marks id))) (update-import-binding! sym token (lambda (old-binding) (let ((x (weed marks old-binding))) (cons-id (if (same-marks? marks (wrap-marks top-wrap)) ; need full id only if more than top-marked. (resolved-id-var-name id) id) x)))))))))) ;;; make-binding-wrap creates vector-based ribcages (define make-binding-wrap (lambda (ids labels w) (if (null? ids) w (make-wrap (wrap-marks w) (cons (let ((labelvec (list->vector labels))) (let ((n (vector-length labelvec))) (let ((symnamevec (make-vector n)) (marksvec (make-vector n))) (let f ((ids ids) (i 0)) (unless (null? ids) (let-values (((symname marks) (id-sym-name&marks (car ids) w))) (vector-set! symnamevec i symname) (vector-set! marksvec i marks) (f (cdr ids) (fx+ i 1))))) (make-ribcage symnamevec marksvec labelvec)))) (wrap-subst w)))))) ;;; resolved ids contain no unnecessary substitutions or marks. they are ;;; used essentially as indirects or aliases in modules interfaces. (define make-resolved-id (lambda (fromsym marks tosym) (make-syntax-object fromsym (make-wrap marks (list (make-ribcage (vector fromsym) (vector marks) (vector tosym))))))) (define id->resolved-id (lambda (id) (let-values (((tosym marks) (id-var-name&marks id empty-wrap))) (unless tosym (syntax-error id "identifier not visible for export")) (make-resolved-id (id-sym-name id) marks tosym)))) (define resolved-id-var-name (lambda (id) (vector-ref (ribcage-labels (car (wrap-subst (syntax-object-wrap id)))) 0))) ;;; Scheme's append should not copy the first argument if the second is ;;; nil, but it does, so we define a smart version here. (define smart-append (lambda (m1 m2) (if (null? m2) m1 (append m1 m2)))) (define join-wraps (lambda (w1 w2) (let ((m1 (wrap-marks w1)) (s1 (wrap-subst w1))) (if (null? m1) (if (null? s1) w2 (make-wrap (wrap-marks w2) (join-subst s1 (wrap-subst w2)))) (make-wrap (join-marks m1 (wrap-marks w2)) (join-subst s1 (wrap-subst w2))))))) (define join-marks (lambda (m1 m2) (smart-append m1 m2))) (define join-subst (lambda (s1 s2) (smart-append s1 s2))) (define same-marks? (lambda (x y) (or (eq? x y) (and (not (null? x)) (not (null? y)) (eq? (car x) (car y)) (same-marks? (cdr x) (cdr y)))))) (define diff-marks (lambda (m1 m2) (let ((n1 (length m1)) (n2 (length m2))) (let f ((n1 n1) (m1 m1)) (cond ((> n1 n2) (cons (car m1) (f (- n1 1) (cdr m1)))) ((equal? m1 m2) '()) (else (error 'sc-expand "internal error in diff-marks: ~s is not a tail of ~s" m1 m2))))))) (module (top-id-bound-var-name top-id-free-var-name) ;; top-id-bound-var-name is used to look up or establish new top-level ;; substitutions, while top-id-free-var-name is used to look up existing ;; (possibly implicit) substitutions. Implicit substitutions exist ;; for top-marked names in all environments, but we represent them ;; explicitly only on demand. ;; ;; In both cases, we first look for an existing substitution for sym ;; and the given marks. If we find one, we return it. Otherwise, we ;; extend the appropriate top-level environment ;; ;; For top-id-bound-var-name, we extend the environment with a substition ;; keyed by the given marks, so that top-level definitions introduced by ;; a macro are distinct from other top-level definitions for the same ;; name. For example, if macros a and b both introduce definitions and ;; bound references to identifier x, the two x's should be different, ;; i.e., keyed by their own marks. ;; ;; For top-id-free-var-name, we extend the environment with a substition ;; keyed by the top marks, since top-level free identifier references ;; should refer to the existing implicit (top-marked) substitution. For ;; example, if macros a and b both introduce free references to identifier ;; x, they should both refer to the same (global, unmarked) x. ;; ;; If the environment is *top*, we map a symbol to itself (define leave-implicit? (lambda (token) (eq? token '*top*))) (define new-binding (lambda (sym marks token) (let ((loc (if (and (leave-implicit? token) (same-marks? marks (wrap-marks top-wrap))) sym (generate-id sym)))) (let ((id (make-resolved-id sym marks loc))) (store-import-binding id token '()) (values loc id))))) (define top-id-bound-var-name ; should be called only when top-ribcage is mutable (lambda (sym marks top-ribcage) (let ((token (top-ribcage-key top-ribcage))) (cond ((lookup-import-binding-name sym marks token '()) => (lambda (id) (if (symbol? id) ; symbol iff marks == (wrap-marks top-wrap) (if (read-only-binding? id) (new-binding sym marks token) (values id (make-resolved-id sym marks id))) (values (resolved-id-var-name id) id)))) (else (new-binding sym marks token)))))) (define top-id-free-var-name (lambda (sym marks top-ribcage) (let ((token (top-ribcage-key top-ribcage))) (cond ((lookup-import-binding-name sym marks token '()) => (lambda (id) (if (symbol? id) id (resolved-id-var-name id)))) ((and (top-ribcage-mutable? top-ribcage) (same-marks? marks (wrap-marks top-wrap))) (let-values (((sym id) (new-binding sym (wrap-marks top-wrap) token))) sym)) (else #f)))))) (define id-var-name-loc&marks (lambda (id w) (define search (lambda (sym subst marks) (if (null? subst) (values #f marks) (let ((fst (car subst))) (cond ((eq? fst 'shift) (search sym (cdr subst) (cdr marks))) ((ribcage? fst) (let ((symnames (ribcage-symnames fst))) (if (vector? symnames) (search-vector-rib sym subst marks symnames fst) (search-list-rib sym subst marks symnames fst)))) ((top-ribcage? fst) (cond ((top-id-free-var-name sym marks fst) => (lambda (var-name) (values var-name marks))) (else (search sym (cdr subst) marks)))) (else (error 'sc-expand "internal error in id-var-name-loc&marks: improper subst ~s" subst))))))) (define search-list-rib (lambda (sym subst marks symnames ribcage) (let f ((symnames symnames) (i 0)) (if (null? symnames) (search sym (cdr subst) marks) (let ((x (car symnames))) (cond ((and (eq? x sym) (same-marks? marks (list-ref (ribcage-marks ribcage) i))) (values (list-ref (ribcage-labels ribcage) i) marks)) ((import-interface? x) (let ((iface (import-interface-interface x)) (new-marks (import-interface-new-marks x))) (cond ((interface-token iface) => (lambda (token) (cond ((lookup-import-binding-name sym marks token new-marks) => (lambda (id) (values (if (symbol? id) id (resolved-id-var-name id)) marks))) (else (f (cdr symnames) i))))) (else (let* ((ie (interface-exports iface)) (n (vector-length ie))) (let g ((j 0)) (if (fx= j n) (f (cdr symnames) i) (let ((id (vector-ref ie j))) (let ((id.sym (id-sym-name id)) (id.marks (join-marks new-marks (id-marks id)))) (if (help-bound-id=? id.sym id.marks sym marks) (values (lookup-import-label id) marks) (g (fx+ j 1)))))))))))) ((and (eq? x barrier-marker) (same-marks? marks (list-ref (ribcage-marks ribcage) i))) (values #f marks)) (else (f (cdr symnames) (fx+ i 1))))))))) (define search-vector-rib (lambda (sym subst marks symnames ribcage) (let ((n (vector-length symnames))) (let f ((i 0)) (cond ((fx= i n) (search sym (cdr subst) marks)) ((and (eq? (vector-ref symnames i) sym) (same-marks? marks (vector-ref (ribcage-marks ribcage) i))) (values (vector-ref (ribcage-labels ribcage) i) marks)) (else (f (fx+ i 1)))))))) (cond ((symbol? id) (search id (wrap-subst w) (wrap-marks w))) ((syntax-object? id) (let ((sym (unannotate (syntax-object-expression id))) (w1 (syntax-object-wrap id))) (let-values (((name marks) (search sym (wrap-subst w) (join-marks (wrap-marks w) (wrap-marks w1))))) (if name (values name marks) (search sym (wrap-subst w1) marks))))) ((annotation? id) (search (unannotate id) (wrap-subst w) (wrap-marks w))) (else (error-hook 'id-var-name "invalid id" id))))) (define id-var-name&marks ; this version follows indirect labels (lambda (id w) (let-values (((label marks) (id-var-name-loc&marks id w))) (values (if (indirect-label? label) (get-indirect-label label) label) marks)))) (define id-var-name-loc ; this version doesn't follow indirect labels (lambda (id w) (let-values (((label marks) (id-var-name-loc&marks id w))) label))) (define id-var-name ; this version follows indirect labels (lambda (id w) (let-values (((label marks) (id-var-name-loc&marks id w))) (if (indirect-label? label) (get-indirect-label label) label)))) ;;; free-id=? must be passed fully wrapped ids since (free-id=? x y) ;;; may be true even if (free-id=? (wrap x w) (wrap y w)) is not. (define free-id=? (lambda (i j) (and (eq? (id-sym-name i) (id-sym-name j)) ; accelerator (eq? (id-var-name i empty-wrap) (id-var-name j empty-wrap))))) (define literal-id=? (lambda (id literal) (and (eq? (id-sym-name id) (id-sym-name literal)) (let ((n-id (id-var-name id empty-wrap)) (n-literal (id-var-name literal empty-wrap))) (or (eq? n-id n-literal) (and (or (not n-id) (symbol? n-id)) (or (not n-literal) (symbol? n-literal)))))))) ;;; bound-id=? may be passed unwrapped (or partially wrapped) ids as ;;; long as the missing portion of the wrap is common to both of the ids ;;; since (bound-id=? x y) iff (bound-id=? (wrap x w) (wrap y w)) (define help-bound-id=? (lambda (i.sym i.marks j.sym j.marks) (and (eq? i.sym j.sym) (same-marks? i.marks j.marks)))) (define bound-id=? (lambda (i j) (help-bound-id=? (id-sym-name i) (id-marks i) (id-sym-name j) (id-marks j)))) ;;; "valid-bound-ids?" returns #t if it receives a list of distinct ids. ;;; valid-bound-ids? may be passed unwrapped (or partially wrapped) ids ;;; as long as the missing portion of the wrap is common to all of the ;;; ids. (define valid-bound-ids? (lambda (ids) (and (let all-ids? ((ids ids)) (or (null? ids) (and (id? (car ids)) (all-ids? (cdr ids))))) (distinct-bound-ids? ids)))) ;;; distinct-bound-ids? expects a list of ids and returns #t if there are ;;; no duplicates. It is quadratic on the length of the id list; long ;;; lists could be sorted to make it more efficient. distinct-bound-ids? ;;; may be passed unwrapped (or partially wrapped) ids as long as the ;;; missing portion of the wrap is common to all of the ids. (define distinct-bound-ids? (lambda (ids) (let distinct? ((ids ids)) (or (null? ids) (and (not (bound-id-member? (car ids) (cdr ids))) (distinct? (cdr ids))))))) (define invalid-ids-error ; find first bad one and complain about it (lambda (ids exp class) (let find ((ids ids) (gooduns '())) (if (null? ids) (syntax-error exp) ; shouldn't happen (if (id? (car ids)) (if (bound-id-member? (car ids) gooduns) (syntax-error (car ids) "duplicate " class) (find (cdr ids) (cons (car ids) gooduns))) (syntax-error (car ids) "invalid " class)))))) (define bound-id-member? (lambda (x list) (and (not (null? list)) (or (bound-id=? x (car list)) (bound-id-member? x (cdr list)))))) ;;; wrapping expressions and identifiers (define wrap (lambda (x w) (cond ((and (null? (wrap-marks w)) (null? (wrap-subst w))) x) ((syntax-object? x) (make-syntax-object (syntax-object-expression x) (join-wraps w (syntax-object-wrap x)))) ((null? x) x) (else (make-syntax-object x w))))) (define source-wrap (lambda (x w ae) (wrap (if (annotation? ae) (begin (unless (eq? (annotation-expression ae) x) (error 'sc-expand "internal error in source-wrap: ae/x mismatch")) ae) x) w))) ;;; expanding (define chi-when-list (lambda (when-list w) ; when-list is syntax'd version of list of situations (map (lambda (x) (cond ((literal-id=? x (syntax compile)) 'compile) ((literal-id=? x (syntax load)) 'load) ((literal-id=? x (syntax visit)) 'visit) ((literal-id=? x (syntax revisit)) 'revisit) ((literal-id=? x (syntax eval)) 'eval) (else (syntax-error (wrap x w) "invalid eval-when situation")))) when-list))) ;;; syntax-type returns five values: type, value, e, w, and ae. The first ;;; two are described in the table below. ;;; ;;; type value explanation ;;; ------------------------------------------------------------------- ;;; alias none alias keyword ;;; alias-form none alias expression ;;; begin none begin keyword ;;; begin-form none begin expression ;;; call none any other call ;;; constant none self-evaluating datum ;;; core procedure core form (including singleton) ;;; define none define keyword ;;; define-form none variable definition ;;; define-syntax none define-syntax keyword ;;; define-syntax-form none syntax definition ;;; displaced-lexical none displaced lexical identifier ;;; eval-when none eval-when keyword ;;; eval-when-form none eval-when form ;;; global name global variable reference ;;; $import none $import keyword ;;; $import-form none $import form ;;; lexical name lexical variable reference ;;; lexical-call name call to lexical variable ;;; local-syntax rec? letrec-syntax/let-syntax keyword ;;; local-syntax-form rec? syntax definition ;;; meta none meta keyword ;;; meta-form none meta form ;;; meta-variable name meta variable ;;; $module none $module keyword ;;; $module-form none $module definition ;;; syntax level pattern variable ;;; other none anything else ;;; ;;; For all forms, e is the form, w is the wrap for e. and ae is the ;;; (possibly) source-annotated form. ;;; ;;; syntax-type expands macros and unwraps as necessary to get to ;;; one of the forms above. (define syntax-type (lambda (e r w ae rib) (cond ((symbol? e) (let* ((n (id-var-name e w)) (b (lookup n r)) (type (binding-type b))) (case type ((macro macro!) (syntax-type (chi-macro (binding-value b) e r w ae rib) r empty-wrap #f rib)) (else (values type (binding-value b) e w ae))))) ((pair? e) (let ((first (car e))) (if (id? first) (let* ((n (id-var-name first w)) (b (lookup n r)) (type (binding-type b))) (case type ((lexical) (values 'lexical-call (binding-value b) e w ae)) ((macro macro!) (syntax-type (chi-macro (binding-value b) e r w ae rib) r empty-wrap #f rib)) ((core) (values type (binding-value b) e w ae)) ((begin) (values 'begin-form #f e w ae)) ((alias) (values 'alias-form #f e w ae)) ((define) (values 'define-form #f e w ae)) ((define-syntax) (values 'define-syntax-form #f e w ae)) ((set!) (chi-set! e r w ae rib)) (($module-key) (values '$module-form #f e w ae)) (($import) (values '$import-form #f e w ae)) ((eval-when) (values 'eval-when-form #f e w ae)) ((meta) (values 'meta-form #f e w ae)) ((local-syntax) (values 'local-syntax-form (binding-value b) e w ae)) (else (values 'call #f e w ae)))) (values 'call #f e w ae)))) ((syntax-object? e) (syntax-type (syntax-object-expression e) r (join-wraps w (syntax-object-wrap e)) #f rib)) ((annotation? e) (syntax-type (annotation-expression e) r w e rib)) ((self-evaluating? e) (values 'constant #f e w ae)) (else (values 'other #f e w ae))))) (define chi-top* (lambda (e r w ctem rtem meta? top-ribcage) (let ((meta-residuals '())) (define meta-residualize! (lambda (x) (set! meta-residuals (cons x meta-residuals)))) (let ((e (chi-top e r w ctem rtem meta? top-ribcage meta-residualize! #f))) (build-sequence no-source (reverse (cons e meta-residuals))))))) (define chi-top-sequence (lambda (body r w ae ctem rtem meta? ribcage meta-residualize!) (build-sequence ae (let dobody ((body body)) (if (null? body) '() (let ((first (chi-top (car body) r w ctem rtem meta? ribcage meta-residualize! #f))) (cons first (dobody (cdr body))))))))) (define chi-top (lambda (e r w ctem rtem meta? top-ribcage meta-residualize! meta-seen?) (let-values (((type value e w ae) (syntax-type e r w no-source top-ribcage))) (case type ((begin-form) (let ((forms (parse-begin e w ae #t))) (if (null? forms) (chi-void) (chi-top-sequence forms r w ae ctem rtem meta? top-ribcage meta-residualize!)))) ((local-syntax-form) (let-values (((forms r mr w ae) (chi-local-syntax value e r r w ae))) ; mr should be same as r here (chi-top-sequence forms r w ae ctem rtem meta? top-ribcage meta-residualize!))) ((eval-when-form) (let-values (((when-list forms) (parse-eval-when e w ae))) (let ((ctem (update-mode-set when-list ctem)) (rtem (update-mode-set when-list rtem))) (if (and (null? ctem) (null? rtem)) (chi-void) (chi-top-sequence forms r w ae ctem rtem meta? top-ribcage meta-residualize!))))) ((meta-form) (chi-top (parse-meta e w ae) r w ctem rtem #t top-ribcage meta-residualize! #t)) ((define-syntax-form) (let-values (((id rhs w) (parse-define-syntax e w ae))) (let ((id (wrap id w))) (when (displaced-lexical? id r) (displaced-lexical-error id)) (unless (top-ribcage-mutable? top-ribcage) (syntax-error (source-wrap e w ae) "invalid definition in read-only environment")) (let ((sym (id-sym-name id))) (let-values (((valsym bound-id) (top-id-bound-var-name sym (wrap-marks (syntax-object-wrap id)) top-ribcage))) (unless (eq? (id-var-name id empty-wrap) valsym) (syntax-error (source-wrap e w ae) "definition not permitted")) (when (read-only-binding? valsym) (syntax-error (source-wrap e w ae) "invalid definition of read-only identifier")) (ct-eval/residualize2 ctem (lambda () (build-cte-install bound-id (chi rhs r r w #t) (top-ribcage-key top-ribcage))))))))) ((define-form) (let-values (((id rhs w) (parse-define e w ae))) (let ((id (wrap id w))) (when (displaced-lexical? id r) (displaced-lexical-error id)) (unless (top-ribcage-mutable? top-ribcage) (syntax-error (source-wrap e w ae) "invalid definition in read-only environment")) (let ((sym (id-sym-name id))) (let-values (((valsym bound-id) (top-id-bound-var-name sym (wrap-marks (syntax-object-wrap id)) top-ribcage))) (unless (eq? (id-var-name id empty-wrap) valsym) (syntax-error (source-wrap e w ae) "definition not permitted")) (when (read-only-binding? valsym) (syntax-error (source-wrap e w ae) "invalid definition of read-only identifier")) (if meta? (ct-eval/residualize2 ctem (lambda () (build-sequence no-source (list (build-cte-install bound-id (build-data no-source (make-binding 'meta-variable valsym)) (top-ribcage-key top-ribcage)) (build-global-definition ae valsym (chi rhs r r w #t)))))) ; make sure compile-time definitions occur before we ; expand the run-time code (let ((x (ct-eval/residualize2 ctem (lambda () (build-cte-install bound-id (build-data no-source (make-binding 'global valsym)) (top-ribcage-key top-ribcage)))))) (build-sequence no-source (list x (rt-eval/residualize rtem (lambda () (build-global-definition ae valsym (chi rhs r r w #f))))))))) )))) (($module-form) (let ((ribcage (make-empty-ribcage))) (let-values (((orig id exports forms) (parse-module e w ae (make-wrap (wrap-marks w) (cons ribcage (wrap-subst w)))))) (when (displaced-lexical? id r) (displaced-lexical-error (wrap id w))) (unless (top-ribcage-mutable? top-ribcage) (syntax-error orig "invalid definition in read-only environment")) (chi-top-module orig r r top-ribcage ribcage ctem rtem meta? id exports forms meta-residualize!)))) (($import-form) (let-values (((orig only? mid) (parse-import e w ae))) (unless (top-ribcage-mutable? top-ribcage) (syntax-error orig "invalid definition in read-only environment")) (ct-eval/residualize2 ctem (lambda () (let ((binding (lookup (id-var-name mid empty-wrap) null-env))) (case (binding-type binding) (($module) (do-top-import only? top-ribcage mid (interface-token (binding-value binding)))) ((displaced-lexical) (displaced-lexical-error mid)) (else (syntax-error mid "unknown module")))))))) ((alias-form) (let-values (((new-id old-id) (parse-alias e w ae))) (let ((new-id (wrap new-id w))) (when (displaced-lexical? new-id r) (displaced-lexical-error new-id)) (unless (top-ribcage-mutable? top-ribcage) (syntax-error (source-wrap e w ae) "invalid definition in read-only environment")) (let ((sym (id-sym-name new-id))) (let-values (((valsym bound-id) (top-id-bound-var-name sym (wrap-marks (syntax-object-wrap new-id)) top-ribcage))) (unless (eq? (id-var-name new-id empty-wrap) valsym) (syntax-error (source-wrap e w ae) "definition not permitted")) (when (read-only-binding? valsym) (syntax-error (source-wrap e w ae) "invalid definition of read-only identifier")) (ct-eval/residualize2 ctem (lambda () (build-cte-install (make-resolved-id sym (wrap-marks (syntax-object-wrap new-id)) (id-var-name old-id w)) (build-data no-source (make-binding 'do-alias #f)) (top-ribcage-key top-ribcage))))))))) (else (when meta-seen? (syntax-error (source-wrap e w ae) "invalid meta definition")) (if meta? (let ((x (chi-expr type value e r r w ae #t))) (top-level-eval-hook x) (ct-eval/residualize3 ctem void (lambda () x))) (rt-eval/residualize rtem (lambda () (chi-expr type value e r r w ae #f))))))))) (define flatten-exports (lambda (exports) (let loop ((exports exports) (ls '())) (if (null? exports) ls (loop (cdr exports) (if (pair? (car exports)) (loop (car exports) ls) (cons (car exports) ls))))))) (define-structure (interface marks exports token)) ;; leaves interfaces unresolved so that indirect labels can be followed. ;; (can't resolve until indirect labels have their final value) (define make-unresolved-interface ; trim out implicit exports (lambda (mid exports) (make-interface (wrap-marks (syntax-object-wrap mid)) (list->vector (map (lambda (x) (if (pair? x) (car x) x)) exports)) #f))) (define make-resolved-interface ; trim out implicit exports & resolve others to actual top-level symbol (lambda (mid exports token) (make-interface (wrap-marks (syntax-object-wrap mid)) (list->vector (map (lambda (x) (id->resolved-id (if (pair? x) (car x) x))) exports)) token))) (define-structure (module-binding type id label imps val exported)) (define create-module-binding (lambda (type id label imps val) (make-module-binding type id label imps val #f))) ;;; frobs represent body forms (define-structure (frob e meta?)) (define chi-top-module (lambda (orig r mr top-ribcage ribcage ctem rtem meta? id exports forms meta-residualize!) (let ((fexports (flatten-exports exports))) (let-values (((r mr bindings inits) (chi-external ribcage orig (map (lambda (d) (make-frob d meta?)) forms) r mr ctem exports fexports meta-residualize!))) ; identify exported identifiers, create ctdefs (let process-exports ((fexports fexports) (ctdefs (lambda () '()))) (if (null? fexports) ; remaining bindings are either identified global vars, ; local vars, or local compile-time entities ; dts: type (local/global) ; dvs & des: define lhs & rhs (let process-locals ((bs bindings) (r r) (dts '()) (dvs '()) (des '())) (if (null? bs) (let ((des (chi-frobs des r mr #f)) (inits (chi-frobs inits r mr #f))) (build-sequence no-source (append ; we wait to establish global compile-time definitions so that ; expansion of des use local versions of modules and macros ; in case ctem tells us not to eval ctdefs now. this means that ; local code can use exported compile-time values (modules, macros, ; meta variables) just as it can unexported ones. (ctdefs) (list (ct-eval/residualize2 ctem (lambda () (let ((sym (id-sym-name id))) (let* ((token (generate-id sym)) (b (build-data no-source (make-binding '$module (make-resolved-interface id exports token))))) (let-values (((valsym bound-id) (top-id-bound-var-name sym (wrap-marks (syntax-object-wrap id)) top-ribcage))) (unless (eq? (id-var-name id empty-wrap) valsym) (syntax-error orig "definition not permitted")) (when (read-only-binding? valsym) (syntax-error orig "invalid definition of read-only identifier")) (build-cte-install bound-id b (top-ribcage-key top-ribcage))))))) (rt-eval/residualize rtem (lambda () (build-top-module no-source dts dvs des (if (null? inits) (chi-void) (build-sequence no-source (append inits (list (chi-void)))))))))))) (let ((b (car bs)) (bs (cdr bs))) (let ((t (module-binding-type b))) (case (module-binding-type b) ((define-form) (let ((label (get-indirect-label (module-binding-label b)))) (if (module-binding-exported b) (let ((var (module-binding-id b))) (process-locals bs r (cons 'global dts) (cons label dvs) (cons (module-binding-val b) des))) (let ((var (gen-var (module-binding-id b)))) (process-locals bs ; add lexical bindings only to run-time environment (extend-env label (make-binding 'lexical var) r) (cons 'local dts) (cons var dvs) (cons (module-binding-val b) des)))))) ((ctdefine-form define-syntax-form $module-form alias-form) (process-locals bs r dts dvs des)) (else (error 'sc-expand-internal "unexpected module binding type ~s" t))))))) (let ((id (car fexports)) (fexports (cdr fexports))) (let loop ((bs bindings)) (if (null? bs) ; must be rexport from an imported module (process-exports fexports ctdefs) (let ((b (car bs)) (bs (cdr bs))) ; following formerly used bound-id=?, but free-id=? can prevent false positives ; and is okay since the substitutions have already been applied (if (free-id=? (module-binding-id b) id) (if (module-binding-exported b) (process-exports fexports ctdefs) (let* ((t (module-binding-type b)) (label (module-binding-label b)) (imps (module-binding-imps b)) (fexports (append imps fexports))) (set-module-binding-exported! b #t) (case t ((define-form) (let ((sym (generate-id (id-sym-name id)))) (set-indirect-label! label sym) (process-exports fexports ctdefs))) ((ctdefine-form) (let ((b (module-binding-val b))) (process-exports fexports (lambda () (let ((sym (binding-value b))) (set-indirect-label! label sym) (cons (ct-eval/residualize3 ctem (lambda () (put-cte-hook sym b)) (lambda () (build-cte-install sym (build-data no-source b) #f))) (ctdefs))))))) ((define-syntax-form) (let ((sym (generate-id (id-sym-name id)))) (process-exports fexports (lambda () (let ((local-label (get-indirect-label label))) (set-indirect-label! label sym) (cons (ct-eval/residualize3 ctem (lambda () (put-cte-hook sym (car (module-binding-val b)))) (lambda () (build-cte-install sym (cdr (module-binding-val b)) #f))) (ctdefs))))))) (($module-form) (let ((sym (generate-id (id-sym-name id))) (exports (module-binding-val b))) (process-exports (append (flatten-exports exports) fexports) (lambda () (set-indirect-label! label sym) (let ((rest (ctdefs))) ; set indirect labels before resolving (let ((x (make-binding '$module (make-resolved-interface id exports sym)))) (cons (ct-eval/residualize3 ctem (lambda () (put-cte-hook sym x)) (lambda () (build-cte-install sym (build-data no-source x) #f))) rest))))))) ((alias-form) (process-exports fexports (lambda () (let ((rest (ctdefs))) ; set indirect labels before resolving (when (indirect-label? label) (unless (symbol? (get-indirect-label label)) (syntax-error (module-binding-id b) "unexported target of alias"))) rest)))) (else (error 'sc-expand-internal "unexpected module binding type ~s" t))))) (loop bs)))))))))))) (define id-set-diff (lambda (exports defs) (cond ((null? exports) '()) ((bound-id-member? (car exports) defs) (id-set-diff (cdr exports) defs)) (else (cons (car exports) (id-set-diff (cdr exports) defs)))))) (define check-module-exports ; After processing the definitions of a module this is called to verify that the ; module has defined or imported each exported identifier. Because ids in fexports are ; wrapped with the given ribcage, they will contain substitutions for anything defined ; or imported here. These subsitutions can be used by do-import! and do-import-top! to ; provide access to reexported bindings, for example. (lambda (source-exp fexports ids) (define defined? (lambda (e ids) (ormap (lambda (x) (if (import-interface? x) (let ((x.iface (import-interface-interface x)) (x.new-marks (import-interface-new-marks x))) (cond ((interface-token x.iface) => (lambda (token) (lookup-import-binding-name (id-sym-name e) (id-marks e) token x.new-marks))) (else (let ((v (interface-exports x.iface))) (let lp ((i (fx- (vector-length v) 1))) (and (fx>= i 0) (or (let ((id (vector-ref v i))) (help-bound-id=? (id-sym-name id) (join-marks x.new-marks (id-marks id)) (id-sym-name e) (id-marks e))) (lp (fx- i 1))))))))) (bound-id=? e x))) ids))) (let loop ((fexports fexports) (missing '())) (if (null? fexports) (unless (null? missing) (syntax-error (car missing) (if (= (length missing) 1) "missing definition for export" "missing definition for multiple exports, including"))) (let ((e (car fexports)) (fexports (cdr fexports))) (if (defined? e ids) (loop fexports missing) (loop fexports (cons e missing)))))))) (define check-defined-ids (lambda (source-exp ls) (define vfold (lambda (v p cls) (let ((len (vector-length v))) (let lp ((i 0) (cls cls)) (if (fx= i len) cls (lp (fx+ i 1) (p (vector-ref v i) cls))))))) (define conflicts (lambda (x y cls) (if (import-interface? x) (let ((x.iface (import-interface-interface x)) (x.new-marks (import-interface-new-marks x))) (if (import-interface? y) (let ((y.iface (import-interface-interface y)) (y.new-marks (import-interface-new-marks y))) (let ((xe (interface-exports x.iface)) (ye (interface-exports y.iface))) (if (fx> (vector-length xe) (vector-length ye)) (vfold ye (lambda (id cls) (id-iface-conflicts id y.new-marks x.iface x.new-marks cls)) cls) (vfold xe (lambda (id cls) (id-iface-conflicts id x.new-marks y.iface y.new-marks cls)) cls)))) (id-iface-conflicts y '() x.iface x.new-marks cls))) (if (import-interface? y) (let ((y.iface (import-interface-interface y)) (y.new-marks (import-interface-new-marks y))) (id-iface-conflicts x '() y.iface y.new-marks cls)) (if (bound-id=? x y) (cons x cls) cls))))) (define id-iface-conflicts (lambda (id id.new-marks iface iface.new-marks cls) (let ((id.sym (id-sym-name id)) (id.marks (join-marks id.new-marks (id-marks id)))) (cond ((interface-token iface) => (lambda (token) (if (lookup-import-binding-name id.sym id.marks token iface.new-marks) (cons id cls) cls))) (else (vfold (interface-exports iface) (lambda (*id cls) (let ((*id.sym (id-sym-name *id)) (*id.marks (join-marks iface.new-marks (id-marks *id)))) (if (help-bound-id=? *id.sym *id.marks id.sym id.marks) (cons *id cls) cls))) cls)))))) (unless (null? ls) (let lp ((x (car ls)) (ls (cdr ls)) (cls '())) (if (null? ls) (unless (null? cls) (let ((cls (syntax-object->datum cls))) (syntax-error source-exp "duplicate definition for " (symbol->string (car cls)) " in"))) (let lp2 ((ls2 ls) (cls cls)) (if (null? ls2) (lp (car ls) (cdr ls) cls) (lp2 (cdr ls2) (conflicts x (car ls2) cls))))))))) (define chi-external (lambda (ribcage source-exp body r mr ctem exports fexports meta-residualize!) (define return (lambda (r mr bindings ids inits) (check-defined-ids source-exp ids) (check-module-exports source-exp fexports ids) (values r mr bindings inits))) (define get-implicit-exports (lambda (id) (let f ((exports exports)) (if (null? exports) '() (if (and (pair? (car exports)) (bound-id=? id (caar exports))) (flatten-exports (cdar exports)) (f (cdr exports))))))) (define update-imp-exports (lambda (bindings exports) (let ((exports (map (lambda (x) (if (pair? x) (car x) x)) exports))) (map (lambda (b) (let ((id (module-binding-id b))) (if (not (bound-id-member? id exports)) b (create-module-binding (module-binding-type b) id (module-binding-label b) (append (get-implicit-exports id) (module-binding-imps b)) (module-binding-val b))))) bindings)))) (let parse ((body body) (r r) (mr mr) (ids '()) (bindings '()) (inits '()) (meta-seen? #f)) (if (null? body) (return r mr bindings ids inits) (let* ((fr (car body)) (e (frob-e fr)) (meta? (frob-meta? fr))) (let-values (((type value e w ae) (syntax-type e r empty-wrap no-source ribcage))) (case type ((define-form) (let-values (((id rhs w) (parse-define e w ae))) (let* ((id (wrap id w)) (label (gen-indirect-label)) (imps (get-implicit-exports id))) (extend-ribcage! ribcage id label) (cond (meta? (let* ((sym (generate-id (id-sym-name id))) (b (make-binding 'meta-variable sym))) ; add meta bindings only to meta environment (let ((mr (extend-env (get-indirect-label label) b mr))) (let ((exp (chi rhs mr mr w #t))) (define-top-level-value-hook sym (top-level-eval-hook exp)) (meta-residualize! (ct-eval/residualize3 ctem void (lambda () (build-global-definition no-source sym exp)))) (parse (cdr body) r mr (cons id ids) (cons (create-module-binding 'ctdefine-form id label imps b) bindings) inits #f))))) (else (parse (cdr body) r mr (cons id ids) (cons (create-module-binding type id label imps (make-frob (wrap rhs w) meta?)) bindings) inits #f)))))) ((define-syntax-form) (let-values (((id rhs w) (parse-define-syntax e w ae))) (let* ((id (wrap id w)) (label (gen-indirect-label)) (imps (get-implicit-exports id)) (exp (chi rhs mr mr w #t))) (extend-ribcage! ribcage id label) (let ((l (get-indirect-label label)) (b (defer-or-eval-transformer top-level-eval-hook exp))) (parse (cdr body) (extend-env l b r) (extend-env l b mr) (cons id ids) (cons (create-module-binding type id label imps (cons b exp)) bindings) inits #f))))) (($module-form) (let* ((*ribcage (make-empty-ribcage)) (*w (make-wrap (wrap-marks w) (cons *ribcage (wrap-subst w))))) (let-values (((orig id *exports forms) (parse-module e w ae *w))) (let-values (((r mr *bindings *inits) (chi-external *ribcage orig (map (lambda (d) (make-frob d meta?)) forms) r mr ctem *exports (flatten-exports *exports) meta-residualize!))) (let ((iface (make-unresolved-interface id *exports)) (bindings (append *bindings bindings)) (inits (append inits *inits)) (label (gen-indirect-label)) (imps (get-implicit-exports id))) (extend-ribcage! ribcage id label) (let ((l (get-indirect-label label)) (b (make-binding '$module iface))) (parse (cdr body) (extend-env l b r) (extend-env l b mr) (cons id ids) (cons (create-module-binding type id label imps *exports) bindings) inits #f))))))) (($import-form) (let-values (((orig only? mid) (parse-import e w ae))) (let ((mlabel (id-var-name mid empty-wrap))) (let ((binding (lookup mlabel r))) (case (binding-type binding) (($module) (let* ((iface (binding-value binding)) (import-iface (make-import-interface iface (import-mark-delta mid iface)))) (when only? (extend-ribcage-barrier! ribcage mid)) (do-import! import-iface ribcage) (parse (cdr body) r mr (cons import-iface ids) (update-imp-exports bindings (vector->list (interface-exports iface))) inits #f))) ((displaced-lexical) (displaced-lexical-error mid)) (else (syntax-error mid "unknown module"))))))) ((alias-form) (let-values (((new-id old-id) (parse-alias e w ae))) (let* ((new-id (wrap new-id w)) (label (id-var-name-loc old-id w)) (imps (get-implicit-exports new-id))) (extend-ribcage! ribcage new-id label) (parse (cdr body) r mr (cons new-id ids) (cons (create-module-binding type new-id label imps #f) bindings) inits #f)))) ((begin-form) (parse (let f ((forms (parse-begin e w ae #t))) (if (null? forms) (cdr body) (cons (make-frob (wrap (car forms) w) meta?) (f (cdr forms))))) r mr ids bindings inits #f)) ((eval-when-form) (let-values (((when-list forms) (parse-eval-when e w ae))) (parse (if (memq 'eval when-list) ; mode set is implicitly (E) (let f ((forms forms)) (if (null? forms) (cdr body) (cons (make-frob (wrap (car forms) w) meta?) (f (cdr forms))))) (cdr body)) r mr ids bindings inits #f))) ((meta-form) (parse (cons (make-frob (wrap (parse-meta e w ae) w) #t) (cdr body)) r mr ids bindings inits #t)) ((local-syntax-form) (let-values (((forms r mr w ae) (chi-local-syntax value e r mr w ae))) (parse (let f ((forms forms)) (if (null? forms) (cdr body) (cons (make-frob (wrap (car forms) w) meta?) (f (cdr forms))))) r mr ids bindings inits #f))) (else ; found an init expression (when meta-seen? (syntax-error (source-wrap e w ae) "invalid meta definition")) (let f ((body (cons (make-frob (source-wrap e w ae) meta?) (cdr body)))) (if (or (null? body) (not (frob-meta? (car body)))) (return r mr bindings ids (append inits body)) (begin ; expand and eval meta inits for effect only (let ((x (chi-meta-frob (car body) mr))) (top-level-eval-hook x) (meta-residualize! (ct-eval/residualize3 ctem void (lambda () x)))) (f (cdr body))))))))))))) (define vmap (lambda (fn v) (do ((i (fx- (vector-length v) 1) (fx- i 1)) (ls '() (cons (fn (vector-ref v i)) ls))) ((fx< i 0) ls)))) (define vfor-each (lambda (fn v) (let ((len (vector-length v))) (do ((i 0 (fx+ i 1))) ((fx= i len)) (fn (vector-ref v i)))))) (define do-top-import (lambda (import-only? top-ribcage mid token) ; silently treat import-only like regular import at top level (build-cte-install mid (build-data no-source (make-binding 'do-import token)) (top-ribcage-key top-ribcage)))) (define update-mode-set (let ((table '((L (load . L) (compile . C) (visit . V) (revisit . R) (eval . -)) (C (load . -) (compile . -) (visit . -) (revisit . -) (eval . C)) (V (load . V) (compile . C) (visit . V) (revisit . -) (eval . -)) (R (load . R) (compile . C) (visit . -) (revisit . R) (eval . -)) (E (load . -) (compile . -) (visit . -) (revisit . -) (eval . E))))) (lambda (when-list mode-set) (define remq (lambda (x ls) (if (null? ls) '() (if (eq? (car ls) x) (remq x (cdr ls)) (cons (car ls) (remq x (cdr ls))))))) (remq '- (apply append (map (lambda (m) (let ((row (cdr (assq m table)))) (map (lambda (s) (cdr (assq s row))) when-list))) mode-set)))))) (define initial-mode-set (lambda (when-list compiling-a-file) (apply append (map (lambda (s) (if compiling-a-file (case s ((compile) '(C)) ((load) '(L)) ((visit) '(V)) ((revisit) '(R)) (else '())) (case s ((eval) '(E)) (else '())))) when-list)))) (define rt-eval/residualize (lambda (rtem thunk) (if (memq 'E rtem) (thunk) (let ((thunk (if (memq 'C rtem) (let ((x (thunk))) (top-level-eval-hook x) (lambda () x)) thunk))) (if (memq 'V rtem) (if (or (memq 'L rtem) (memq 'R rtem)) (thunk) ; visit-revisit (build-visit-only (thunk))) (if (or (memq 'L rtem) (memq 'R rtem)) (build-revisit-only (thunk)) (chi-void))))))) (define ct-eval/residualize2 (lambda (ctem thunk) (let ((t #f)) (ct-eval/residualize3 ctem (lambda () (unless t (set! t (thunk))) (top-level-eval-hook t)) (lambda () (or t (thunk))))))) (define ct-eval/residualize3 (lambda (ctem eval-thunk residualize-thunk) (if (memq 'E ctem) (begin (eval-thunk) (chi-void)) (begin (when (memq 'C ctem) (eval-thunk)) (if (memq 'R ctem) (if (or (memq 'L ctem) (memq 'V ctem)) (residualize-thunk) ; visit-revisit (build-revisit-only (residualize-thunk))) (if (or (memq 'L ctem) (memq 'V ctem)) (build-visit-only (residualize-thunk)) (chi-void))))))) (define chi-frobs (lambda (frob* r mr m?) (map (lambda (x) (chi (frob-e x) r mr empty-wrap m?)) frob*))) (define chi-meta-frob (lambda (x mr) (chi (frob-e x) mr mr empty-wrap #t))) (define chi-sequence (lambda (body r mr w ae m?) (build-sequence ae (let dobody ((body body)) (if (null? body) '() (let ((first (chi (car body) r mr w m?))) (cons first (dobody (cdr body))))))))) (define chi (lambda (e r mr w m?) (let-values (((type value e w ae) (syntax-type e r w no-source #f))) (chi-expr type value e r mr w ae m?)))) (define chi-expr (lambda (type value e r mr w ae m?) (case type ((lexical) (build-lexical-reference 'value ae value)) ((core) (value e r mr w ae m?)) ((lexical-call) (chi-application (build-lexical-reference 'fun (let ((x (car e))) (if (syntax-object? x) (syntax-object-expression x) x)) value) e r mr w ae m?)) ((constant) (build-data ae (strip (source-wrap e w ae) empty-wrap))) ((global) (build-global-reference ae value)) ((meta-variable) (if m? (build-global-reference ae value) (displaced-lexical-error (source-wrap e w ae)))) ((call) (chi-application (chi (car e) r mr w m?) e r mr w ae m?)) ((begin-form) (chi-sequence (parse-begin e w ae #f) r mr w ae m?)) ((local-syntax-form) (let-values (((forms r mr w ae) (chi-local-syntax value e r mr w ae))) (chi-sequence forms r mr w ae m?))) ((eval-when-form) (let-values (((when-list forms) (parse-eval-when e w ae))) (if (memq 'eval when-list) ; mode set is implicitly (E) (chi-sequence forms r mr w ae m?) (chi-void)))) ((meta-form) (syntax-error (source-wrap e w ae) "invalid context for meta definition")) ((define-form) (parse-define e w ae) (syntax-error (source-wrap e w ae) "invalid context for definition")) ((define-syntax-form) (parse-define-syntax e w ae) (syntax-error (source-wrap e w ae) "invalid context for definition")) (($module-form) (let-values (((orig id exports forms) (parse-module e w ae w))) (syntax-error orig "invalid context for definition"))) (($import-form) (let-values (((orig only? mid) (parse-import e w ae))) (syntax-error orig "invalid context for definition"))) ((alias-form) (parse-alias e w ae) (syntax-error (source-wrap e w ae) "invalid context for definition")) ((syntax) (syntax-error (source-wrap e w ae) "reference to pattern variable outside syntax form")) ((displaced-lexical) (displaced-lexical-error (source-wrap e w ae))) (else (syntax-error (source-wrap e w ae)))))) (define chi-application (lambda (x e r mr w ae m?) (syntax-case e () ((e0 e1 ...) (build-application ae x (map (lambda (e) (chi e r mr w m?)) (syntax (e1 ...))))) (_ (syntax-error (source-wrap e w ae)))))) (define chi-set! (lambda (e r w ae rib) (syntax-case e () ((_ id val) (id? (syntax id)) (let ((n (id-var-name (syntax id) w))) (let ((b (lookup n r))) (case (binding-type b) ((macro!) (let ((id (wrap (syntax id) w)) (val (wrap (syntax val) w))) (syntax-type (chi-macro (binding-value b) `(,(syntax set!) ,id ,val) r empty-wrap #f rib) r empty-wrap #f rib))) (else (values 'core (lambda (e r mr w ae m?) ; repeat lookup in case we were first expression (init) in ; module or lambda body. we repeat id-var-name as well, ; although this is only necessary if we allow inits to ; preced definitions (let ((val (chi (syntax val) r mr w m?)) (n (id-var-name (syntax id) w))) (let ((b (lookup n r))) (case (binding-type b) ((lexical) (build-lexical-assignment ae (binding-value b) val)) ((global) (let ((sym (binding-value b))) (when (read-only-binding? n) (syntax-error (source-wrap e w ae) "invalid assignment to read-only variable")) (build-global-assignment ae sym val))) ((meta-variable) (if m? (build-global-assignment ae (binding-value b) val) (displaced-lexical-error (wrap (syntax id) w)))) ((displaced-lexical) (displaced-lexical-error (wrap (syntax id) w))) (else (syntax-error (source-wrap e w ae))))))) e w ae)))))) (_ (syntax-error (source-wrap e w ae)))))) (define chi-macro (lambda (p e r w ae rib) (define rebuild-macro-output (lambda (x m) (cond ((pair? x) (cons (rebuild-macro-output (car x) m) (rebuild-macro-output (cdr x) m))) ((syntax-object? x) (let ((w (syntax-object-wrap x))) (let ((ms (wrap-marks w)) (s (wrap-subst w))) (make-syntax-object (syntax-object-expression x) (if (and (pair? ms) (eq? (car ms) the-anti-mark)) (make-wrap (cdr ms) (cdr s)) (make-wrap (cons m ms) (if rib (cons rib (cons 'shift s)) (cons 'shift s)))))))) ((vector? x) (let* ((n (vector-length x)) (v (make-vector n))) (do ((i 0 (fx+ i 1))) ((fx= i n) v) (vector-set! v i (rebuild-macro-output (vector-ref x i) m))))) ((symbol? x) (syntax-error (source-wrap e w ae) "encountered raw symbol " (symbol->string x) " in output of macro")) (else x)))) (rebuild-macro-output (let ((out (p (source-wrap e (anti-mark w) ae)))) (if (procedure? out) (out (lambda (id) (unless (identifier? id) (syntax-error id "environment argument is not an identifier")) (lookup (id-var-name id empty-wrap) r))) out)) (new-mark)))) (define chi-body (lambda (body outer-form r mr w m?) (let* ((ribcage (make-empty-ribcage)) (w (make-wrap (wrap-marks w) (cons ribcage (wrap-subst w)))) (body (map (lambda (x) (make-frob (wrap x w) #f)) body))) (let-values (((r mr exprs ids vars vals inits) (chi-internal ribcage outer-form body r mr m?))) (when (null? exprs) (syntax-error outer-form "no expressions in body")) (build-body no-source (reverse vars) (chi-frobs (reverse vals) r mr m?) (build-sequence no-source (chi-frobs (append inits exprs) r mr m?))))))) (define chi-internal ;; In processing the forms of the body, we create a new, empty wrap. ;; This wrap is augmented (destructively) each time we discover that ;; the next form is a definition. This is done: ;; ;; (1) to allow the first nondefinition form to be a call to ;; one of the defined ids even if the id previously denoted a ;; definition keyword or keyword for a macro expanding into a ;; definition; ;; (2) to prevent subsequent definition forms (but unfortunately ;; not earlier ones) and the first nondefinition form from ;; confusing one of the bound identifiers for an auxiliary ;; keyword; and ;; (3) so that we do not need to restart the expansion of the ;; first nondefinition form, which is problematic anyway ;; since it might be the first element of a begin that we ;; have just spliced into the body (meaning if we restarted, ;; we'd really need to restart with the begin or the macro ;; call that expanded into the begin, and we'd have to give ;; up allowing (begin <defn>+ <expr>+), which is itself ;; problematic since we don't know if a begin contains only ;; definitions until we've expanded it). ;; ;; Subforms of a begin, let-syntax, or letrec-syntax are spliced ;; into the body. ;; ;; outer-form is fully wrapped w/source (lambda (ribcage source-exp body r mr m?) (define return (lambda (r mr exprs ids vars vals inits) (check-defined-ids source-exp ids) (values r mr exprs ids vars vals inits))) (let parse ((body body) (r r) (mr mr) (ids '()) (vars '()) (vals '()) (inits '()) (meta-seen? #f)) (if (null? body) (return r mr body ids vars vals inits) (let* ((fr (car body)) (e (frob-e fr)) (meta? (frob-meta? fr))) (let-values (((type value e w ae) (syntax-type e r empty-wrap no-source ribcage))) (case type ((define-form) (let-values (((id rhs w) (parse-define e w ae))) (let ((id (wrap id w)) (label (gen-label))) (cond (meta? (let ((sym (generate-id (id-sym-name id)))) (extend-ribcage! ribcage id label) ; add meta bindings only to meta environment ; so visible only to next higher level and beyond (let ((mr (extend-env label (make-binding 'meta-variable sym) mr))) (define-top-level-value-hook sym (top-level-eval-hook (chi rhs mr mr w #t))) (parse (cdr body) r mr (cons id ids) vars vals inits #f)))) (else (let ((var (gen-var id))) (extend-ribcage! ribcage id label) ; add lexical bindings only to run-time environment (parse (cdr body) (extend-env label (make-binding 'lexical var) r) mr (cons id ids) (cons var vars) (cons (make-frob (wrap rhs w) meta?) vals) inits #f))))))) ((define-syntax-form) (let-values (((id rhs w) (parse-define-syntax e w ae))) (let ((id (wrap id w)) (label (gen-label)) (exp (chi rhs mr mr w #t))) (extend-ribcage! ribcage id label) (let ((b (defer-or-eval-transformer local-eval-hook exp))) (parse (cdr body) (extend-env label b r) (extend-env label b mr) (cons id ids) vars vals inits #f))))) (($module-form) (let* ((*ribcage (make-empty-ribcage)) (*w (make-wrap (wrap-marks w) (cons *ribcage (wrap-subst w))))) (let-values (((orig id exports forms) (parse-module e w ae *w))) (let-values (((r mr *body *ids *vars *vals *inits) (chi-internal *ribcage orig (map (lambda (d) (make-frob d meta?)) forms) r mr m?))) ; valid bound ids checked already by chi-internal (check-module-exports source-exp (flatten-exports exports) *ids) (let ((iface (make-resolved-interface id exports #f)) (vars (append *vars vars)) (vals (append *vals vals)) (inits (append inits *inits *body)) (label (gen-label))) (extend-ribcage! ribcage id label) (let ((b (make-binding '$module iface))) (parse (cdr body) (extend-env label b r) (extend-env label b mr) (cons id ids) vars vals inits #f))))))) (($import-form) (let-values (((orig only? mid) (parse-import e w ae))) (let ((mlabel (id-var-name mid empty-wrap))) (let ((binding (lookup mlabel r))) (case (binding-type binding) (($module) (let* ((iface (binding-value binding)) (import-iface (make-import-interface iface (import-mark-delta mid iface)))) (when only? (extend-ribcage-barrier! ribcage mid)) (do-import! import-iface ribcage) (parse (cdr body) r mr (cons import-iface ids) vars vals inits #f))) ((displaced-lexical) (displaced-lexical-error mid)) (else (syntax-error mid "unknown module"))))))) ((alias-form) (let-values (((new-id old-id) (parse-alias e w ae))) (let ((new-id (wrap new-id w))) (extend-ribcage! ribcage new-id (id-var-name-loc old-id w)) (parse (cdr body) r mr (cons new-id ids) vars vals inits #f)))) ((begin-form) (parse (let f ((forms (parse-begin e w ae #t))) (if (null? forms) (cdr body) (cons (make-frob (wrap (car forms) w) meta?) (f (cdr forms))))) r mr ids vars vals inits #f)) ((eval-when-form) (let-values (((when-list forms) (parse-eval-when e w ae))) (parse (if (memq 'eval when-list) ; mode set is implicitly (E) (let f ((forms forms)) (if (null? forms) (cdr body) (cons (make-frob (wrap (car forms) w) meta?) (f (cdr forms))))) (cdr body)) r mr ids vars vals inits #f))) ((meta-form) (parse (cons (make-frob (wrap (parse-meta e w ae) w) #t) (cdr body)) r mr ids vars vals inits #t)) ((local-syntax-form) (let-values (((forms r mr w ae) (chi-local-syntax value e r mr w ae))) (parse (let f ((forms forms)) (if (null? forms) (cdr body) (cons (make-frob (wrap (car forms) w) meta?) (f (cdr forms))))) r mr ids vars vals inits #f))) (else ; found a non-definition (when meta-seen? (syntax-error (source-wrap e w ae) "invalid meta definition")) (let f ((body (cons (make-frob (source-wrap e w ae) meta?) (cdr body)))) (if (or (null? body) (not (frob-meta? (car body)))) (return r mr body ids vars vals inits) (begin ; expand meta inits for effect only (top-level-eval-hook (chi-meta-frob (car body) mr)) (f (cdr body))))))))))))) (define import-mark-delta ; returns list of marks layered on top of module id beyond those ; cached in the interface (lambda (mid iface) (diff-marks (id-marks mid) (interface-marks iface)))) (define lookup-import-label (lambda (id) (let ((label (id-var-name-loc id empty-wrap))) (unless label (syntax-error id "exported identifier not visible")) label))) (define do-import! (lambda (import-iface ribcage) (let ((ie (interface-exports (import-interface-interface import-iface)))) (if (<= (vector-length ie) 20) (let ((new-marks (import-interface-new-marks import-iface))) (vfor-each (lambda (id) (import-extend-ribcage! ribcage new-marks id (lookup-import-label id))) ie)) (extend-ribcage-subst! ribcage import-iface))))) (define parse-module (lambda (e w ae *w) (define listify (lambda (exports) (if (null? exports) '() (cons (syntax-case (car exports) () ((ex ...) (listify (syntax (ex ...)))) (x (if (id? (syntax x)) (wrap (syntax x) *w) (syntax-error (source-wrap e w ae) "invalid exports list in")))) (listify (cdr exports)))))) (syntax-case e () ((_ orig mid (ex ...) form ...) (id? (syntax mid)) ; id receives old wrap so it won't be confused with id of same name ; defined within the module (values (syntax orig) (wrap (syntax mid) w) (listify (syntax (ex ...))) (map (lambda (x) (wrap x *w)) (syntax (form ...))))) (_ (syntax-error (source-wrap e w ae)))))) (define parse-import (lambda (e w ae) (syntax-case e () ((_ orig #t mid) (id? (syntax mid)) (values (syntax orig) #t (wrap (syntax mid) w))) ((_ orig #f mid) (id? (syntax mid)) (values (syntax orig) #f (wrap (syntax mid) w))) (_ (syntax-error (source-wrap e w ae)))))) (define parse-define (lambda (e w ae) (syntax-case e () ((_ name val) (id? (syntax name)) (values (syntax name) (syntax val) w)) ((_ (name . args) e1 e2 ...) (and (id? (syntax name)) (valid-bound-ids? (lambda-var-list (syntax args)))) (values (wrap (syntax name) w) (cons (syntax lambda) (wrap (syntax (args e1 e2 ...)) w)) empty-wrap)) ((_ name) (id? (syntax name)) (values (wrap (syntax name) w) (syntax (void)) empty-wrap)) (_ (syntax-error (source-wrap e w ae)))))) (define parse-define-syntax (lambda (e w ae) (syntax-case e () ((_ (name id) e1 e2 ...) (and (id? (syntax name)) (id? (syntax id))) (values (wrap (syntax name) w) `(,(syntax lambda) ,(wrap (syntax (id)) w) ,@(wrap (syntax (e1 e2 ...)) w)) empty-wrap)) ((_ name val) (id? (syntax name)) (values (syntax name) (syntax val) w)) (_ (syntax-error (source-wrap e w ae)))))) (define parse-meta (lambda (e w ae) (syntax-case e () ((_ . form) (syntax form)) (_ (syntax-error (source-wrap e w ae)))))) (define parse-eval-when (lambda (e w ae) (syntax-case e () ((_ (x ...) e1 e2 ...) (values (chi-when-list (syntax (x ...)) w) (syntax (e1 e2 ...)))) (_ (syntax-error (source-wrap e w ae)))))) (define parse-alias (lambda (e w ae) (syntax-case e () ((_ new-id old-id) (and (id? (syntax new-id)) (id? (syntax old-id))) (values (syntax new-id) (syntax old-id))) (_ (syntax-error (source-wrap e w ae)))))) (define parse-begin (lambda (e w ae empty-okay?) (syntax-case e () ((_) empty-okay? '()) ((_ e1 e2 ...) (syntax (e1 e2 ...))) (_ (syntax-error (source-wrap e w ae)))))) (define chi-lambda-clause (lambda (e c r mr w m?) (syntax-case c () (((id ...) e1 e2 ...) (let ((ids (syntax (id ...)))) (if (not (valid-bound-ids? ids)) (syntax-error e "invalid parameter list in") (let ((labels (gen-labels ids)) (new-vars (map gen-var ids))) (values new-vars (chi-body (syntax (e1 e2 ...)) e (extend-var-env* labels new-vars r) mr (make-binding-wrap ids labels w) m?)))))) ((ids e1 e2 ...) (let ((old-ids (lambda-var-list (syntax ids)))) (if (not (valid-bound-ids? old-ids)) (syntax-error e "invalid parameter list in") (let ((labels (gen-labels old-ids)) (new-vars (map gen-var old-ids))) (values (let f ((ls1 (cdr new-vars)) (ls2 (car new-vars))) (if (null? ls1) ls2 (f (cdr ls1) (cons (car ls1) ls2)))) (chi-body (syntax (e1 e2 ...)) e (extend-var-env* labels new-vars r) mr (make-binding-wrap old-ids labels w) m?)))))) (_ (syntax-error e))))) (define chi-local-syntax (lambda (rec? e r mr w ae) (syntax-case e () ((_ ((id val) ...) e1 e2 ...) (let ((ids (syntax (id ...)))) (if (not (valid-bound-ids? ids)) (invalid-ids-error (map (lambda (x) (wrap x w)) ids) (source-wrap e w ae) "keyword") (let ((labels (gen-labels ids))) (let ((new-w (make-binding-wrap ids labels w))) (let ((b* (let ((w (if rec? new-w w))) (map (lambda (x) (defer-or-eval-transformer local-eval-hook (chi x mr mr w #t))) (syntax (val ...)))))) (values (syntax (e1 e2 ...)) (extend-env* labels b* r) (extend-env* labels b* mr) new-w ae))))))) (_ (syntax-error (source-wrap e w ae)))))) (define chi-void (lambda () (build-application no-source (build-primref no-source 'void) '()))) (define ellipsis? (lambda (x) (and (nonsymbol-id? x) (literal-id=? x (syntax (... ...)))))) ;;; data ;;; strips all annotations from potentially circular reader output. (define strip-annotation (lambda (x) (cond ((pair? x) (cons (strip-annotation (car x)) (strip-annotation (cdr x)))) ((annotation? x) (annotation-stripped x)) (else x)))) ;;; strips syntax-objects down to top-wrap; if top-wrap is layered directly ;;; on an annotation, strips the annotation as well. ;;; since only the head of a list is annotated by the reader, not each pair ;;; in the spine, we also check for pairs whose cars are annotated in case ;;; we've been passed the cdr of an annotated list (define strip* (lambda (x w fn) (if (top-marked? w) (fn x) (let f ((x x)) (cond ((syntax-object? x) (strip* (syntax-object-expression x) (syntax-object-wrap x) fn)) ((pair? x) (let ((a (f (car x))) (d (f (cdr x)))) (if (and (eq? a (car x)) (eq? d (cdr x))) x (cons a d)))) ((vector? x) (let ((old (vector->list x))) (let ((new (map f old))) (if (andmap eq? old new) x (list->vector new))))) (else x)))))) (define strip (lambda (x w) (strip* x w (lambda (x) (if (or (annotation? x) (and (pair? x) (annotation? (car x)))) (strip-annotation x) x))))) ;;; lexical variables (define gen-var (lambda (id) (let ((id (if (syntax-object? id) (syntax-object-expression id) id))) (if (annotation? id) (build-lexical-var id (annotation-expression id)) (build-lexical-var id id))))) (define lambda-var-list (lambda (vars) (let lvl ((vars vars) (ls '()) (w empty-wrap)) (cond ((pair? vars) (lvl (cdr vars) (cons (wrap (car vars) w) ls) w)) ((id? vars) (cons (wrap vars w) ls)) ((null? vars) ls) ((syntax-object? vars) (lvl (syntax-object-expression vars) ls (join-wraps w (syntax-object-wrap vars)))) ((annotation? vars) (lvl (annotation-expression vars) ls w)) ; include anything else to be caught by subsequent error ; checking (else (cons vars ls)))))) ; must precede global-extends (set! $sc-put-cte (lambda (id b top-token) (define sc-put-module (lambda (exports token new-marks) (vfor-each (lambda (id) (store-import-binding id token new-marks)) exports))) (define (put-cte id binding token) (let ((sym (if (symbol? id) id (id-var-name id empty-wrap)))) (store-import-binding id token '()) (put-global-definition-hook sym ; global binding is assumed; if global pass #f to remove existing binding, if any (if (and (eq? (binding-type binding) 'global) (eq? (binding-value binding) sym)) #f binding)))) (let ((binding (make-transformer-binding b))) (case (binding-type binding) (($module) (let ((iface (binding-value binding))) (sc-put-module (interface-exports iface) (interface-token iface) '())) (put-cte id binding top-token)) ((do-alias) (store-import-binding id top-token '())) ((do-import) ; fake binding: id is module id binding-value is token (let ((token (binding-value b))) (let ((b (lookup (id-var-name id empty-wrap) null-env))) (case (binding-type b) (($module) (let* ((iface (binding-value b)) (exports (interface-exports iface))) (unless (eq? (interface-token iface) token) (syntax-error id "import mismatch for module")) (sc-put-module (interface-exports iface) top-token (import-mark-delta id iface)))) (else (syntax-error id "unknown module")))))) (else (put-cte id binding top-token)))) )) ;;; core transformers (global-extend 'local-syntax 'letrec-syntax #t) (global-extend 'local-syntax 'let-syntax #f) (global-extend 'core 'fluid-let-syntax (lambda (e r mr w ae m?) (syntax-case e () ((_ ((var val) ...) e1 e2 ...) (valid-bound-ids? (syntax (var ...))) (let ((names (map (lambda (x) (id-var-name x w)) (syntax (var ...))))) (for-each (lambda (id n) (case (binding-type (lookup n r)) ((displaced-lexical) (displaced-lexical-error (wrap id w))))) (syntax (var ...)) names) (let ((b* (map (lambda (x) (defer-or-eval-transformer local-eval-hook (chi x mr mr w #t))) (syntax (val ...))))) (chi-body (syntax (e1 e2 ...)) (source-wrap e w ae) (extend-env* names b* r) (extend-env* names b* mr) w m?)))) (_ (syntax-error (source-wrap e w ae)))))) (global-extend 'core 'quote (lambda (e r mr w ae m?) (syntax-case e () ((_ e) (build-data ae (strip (syntax e) w))) (_ (syntax-error (source-wrap e w ae)))))) (global-extend 'core 'syntax (let () (define gen-syntax (lambda (src e r maps ellipsis? vec?) (if (id? e) (let ((label (id-var-name e empty-wrap))) (let ((b (lookup label r))) (if (eq? (binding-type b) 'syntax) (let-values (((var maps) (let ((var.lev (binding-value b))) (gen-ref src (car var.lev) (cdr var.lev) maps)))) (values `(ref ,var) maps)) (if (ellipsis? e) (syntax-error src "misplaced ellipsis in syntax form") (values `(quote ,e) maps))))) (syntax-case e () ((dots e) (ellipsis? (syntax dots)) (if vec? (syntax-error src "misplaced ellipsis in syntax template") (gen-syntax src (syntax e) r maps (lambda (x) #f) #f))) ((x dots . y) ; this could be about a dozen lines of code, except that we ; choose to handle (syntax (x ... ...)) forms (ellipsis? (syntax dots)) (let f ((y (syntax y)) (k (lambda (maps) (let-values (((x maps) (gen-syntax src (syntax x) r (cons '() maps) ellipsis? #f))) (if (null? (car maps)) (syntax-error src "extra ellipsis in syntax form") (values (gen-map x (car maps)) (cdr maps))))))) (syntax-case y () ((dots . y) (ellipsis? (syntax dots)) (f (syntax y) (lambda (maps) (let-values (((x maps) (k (cons '() maps)))) (if (null? (car maps)) (syntax-error src "extra ellipsis in syntax form") (values (gen-mappend x (car maps)) (cdr maps))))))) (_ (let-values (((y maps) (gen-syntax src y r maps ellipsis? vec?))) (let-values (((x maps) (k maps))) (values (gen-append x y) maps))))))) ((x . y) (let-values (((xnew maps) (gen-syntax src (syntax x) r maps ellipsis? #f))) (let-values (((ynew maps) (gen-syntax src (syntax y) r maps ellipsis? vec?))) (values (gen-cons e (syntax x) (syntax y) xnew ynew) maps)))) (#(x1 x2 ...) (let ((ls (syntax (x1 x2 ...)))) (let-values (((lsnew maps) (gen-syntax src ls r maps ellipsis? #t))) (values (gen-vector e ls lsnew) maps)))) (_ (values `(quote ,e) maps)))))) (define gen-ref (lambda (src var level maps) (if (fx= level 0) (values var maps) (if (null? maps) (syntax-error src "missing ellipsis in syntax form") (let-values (((outer-var outer-maps) (gen-ref src var (fx- level 1) (cdr maps)))) (let ((b (assq outer-var (car maps)))) (if b (values (cdr b) maps) (let ((inner-var (gen-var 'tmp))) (values inner-var (cons (cons (cons outer-var inner-var) (car maps)) outer-maps)))))))))) (define gen-append (lambda (x y) (if (equal? y '(quote ())) x `(append ,x ,y)))) (define gen-mappend (lambda (e map-env) `(apply (primitive append) ,(gen-map e map-env)))) (define gen-map (lambda (e map-env) (let ((formals (map cdr map-env)) (actuals (map (lambda (x) `(ref ,(car x))) map-env))) (cond ((eq? (car e) 'ref) ; identity map equivalence: ; (map (lambda (x) x) y) == y (car actuals)) ((andmap (lambda (x) (and (eq? (car x) 'ref) (memq (cadr x) formals))) (cdr e)) ; eta map equivalence: ; (map (lambda (x ...) (f x ...)) y ...) == (map f y ...) `(map (primitive ,(car e)) ,@(map (let ((r (map cons formals actuals))) (lambda (x) (cdr (assq (cadr x) r)))) (cdr e)))) (else `(map (lambda ,formals ,e) ,@actuals)))))) ; 12/12/00: semantic change: we now return original syntax object (e) ; if no pattern variables were found within, to avoid dropping ; source annotations prematurely. the "syntax returns lists" for ; lists in its input guarantee counts only for substructure that ; contains pattern variables (define gen-cons (lambda (e x y xnew ynew) (case (car ynew) ((quote) (if (eq? (car xnew) 'quote) (let ((xnew (cadr xnew)) (ynew (cadr ynew))) (if (and (eq? xnew x) (eq? ynew y)) `',e `'(,xnew . ,ynew))) (if (eq? (cadr ynew) '()) `(list ,xnew) `(cons ,xnew ,ynew)))) ((list) `(list ,xnew ,@(cdr ynew))) (else `(cons ,xnew ,ynew))))) (define gen-vector (lambda (e ls lsnew) (cond ((eq? (car lsnew) 'quote) (if (eq? (cadr lsnew) ls) `',e `(quote #(,@(cadr lsnew))))) ((eq? (car lsnew) 'list) `(vector ,@(cdr lsnew))) (else `(list->vector ,lsnew))))) (define regen (lambda (x) (case (car x) ((ref) (build-lexical-reference 'value no-source (cadr x))) ((primitive) (build-primref no-source (cadr x))) ((quote) (build-data no-source (cadr x))) ((lambda) (build-lambda no-source (cadr x) (regen (caddr x)))) ((map) (let ((ls (map regen (cdr x)))) (build-application no-source (if (fx= (length ls) 2) (build-primref no-source 'map) ; really need to do our own checking here (build-primref no-source 2 'map)) ; require error check ls))) (else (build-application no-source (build-primref no-source (car x)) (map regen (cdr x))))))) (lambda (e r mr w ae m?) (let ((e (source-wrap e w ae))) (syntax-case e () ((_ x) (let-values (((e maps) (gen-syntax e (syntax x) r '() ellipsis? #f))) (regen e))) (_ (syntax-error e))))))) (global-extend 'core 'lambda (lambda (e r mr w ae m?) (syntax-case e () ((_ . c) (let-values (((vars body) (chi-lambda-clause (source-wrap e w ae) (syntax c) r mr w m?))) (build-lambda ae vars body)))))) (global-extend 'core 'letrec (lambda (e r mr w ae m?) (syntax-case e () ((_ ((id val) ...) e1 e2 ...) (let ((ids (syntax (id ...)))) (if (not (valid-bound-ids? ids)) (invalid-ids-error (map (lambda (x) (wrap x w)) ids) (source-wrap e w ae) "bound variable") (let ((labels (gen-labels ids)) (new-vars (map gen-var ids))) (let ((w (make-binding-wrap ids labels w)) (r (extend-var-env* labels new-vars r))) (build-letrec ae new-vars (map (lambda (x) (chi x r mr w m?)) (syntax (val ...))) (chi-body (syntax (e1 e2 ...)) (source-wrap e w ae) r mr w m?))))))) (_ (syntax-error (source-wrap e w ae)))))) (global-extend 'core 'if (lambda (e r mr w ae m?) (syntax-case e () ((_ test then) (build-conditional ae (chi (syntax test) r mr w m?) (chi (syntax then) r mr w m?) (chi-void))) ((_ test then else) (build-conditional ae (chi (syntax test) r mr w m?) (chi (syntax then) r mr w m?) (chi (syntax else) r mr w m?))) (_ (syntax-error (source-wrap e w ae)))))) (global-extend 'set! 'set! '()) (global-extend 'alias 'alias '()) (global-extend 'begin 'begin '()) (global-extend '$module-key '$module '()) (global-extend '$import '$import '()) (global-extend 'define 'define '()) (global-extend 'define-syntax 'define-syntax '()) (global-extend 'eval-when 'eval-when '()) (global-extend 'meta 'meta '()) (global-extend 'core 'syntax-case (let () (define convert-pattern ; accepts pattern & keys ; returns syntax-dispatch pattern & ids (lambda (pattern keys) (define cvt* (lambda (p* n ids) (if (null? p*) (values '() ids) (let-values (((y ids) (cvt* (cdr p*) n ids))) (let-values (((x ids) (cvt (car p*) n ids))) (values (cons x y) ids)))))) (define cvt (lambda (p n ids) (if (id? p) (if (bound-id-member? p keys) (values (vector 'free-id p) ids) (values 'any (cons (cons p n) ids))) (syntax-case p () ((x dots) (ellipsis? (syntax dots)) (let-values (((p ids) (cvt (syntax x) (fx+ n 1) ids))) (values (if (eq? p 'any) 'each-any (vector 'each p)) ids))) ((x dots y ... . z) (ellipsis? (syntax dots)) (let-values (((z ids) (cvt (syntax z) n ids))) (let-values (((y ids) (cvt* (syntax (y ...)) n ids))) (let-values (((x ids) (cvt (syntax x) (fx+ n 1) ids))) (values `#(each+ ,x ,(reverse y) ,z) ids))))) ((x . y) (let-values (((y ids) (cvt (syntax y) n ids))) (let-values (((x ids) (cvt (syntax x) n ids))) (values (cons x y) ids)))) (() (values '() ids)) (#(x ...) (let-values (((p ids) (cvt (syntax (x ...)) n ids))) (values (vector 'vector p) ids))) (x (values (vector 'atom (strip p empty-wrap)) ids)))))) (cvt pattern 0 '()))) (define build-dispatch-call (lambda (pvars exp y r mr m?) (let ((ids (map car pvars)) (levels (map cdr pvars))) (let ((labels (gen-labels ids)) (new-vars (map gen-var ids))) (build-application no-source (build-primref no-source 'apply) (list (build-lambda no-source new-vars (chi exp (extend-env* labels (map (lambda (var level) (make-binding 'syntax `(,var . ,level))) new-vars (map cdr pvars)) r) mr (make-binding-wrap ids labels empty-wrap) m?)) y)))))) (define gen-clause (lambda (x keys clauses r mr m? pat fender exp) (let-values (((p pvars) (convert-pattern pat keys))) (cond ((not (distinct-bound-ids? (map car pvars))) (invalid-ids-error (map car pvars) pat "pattern variable")) ((not (andmap (lambda (x) (not (ellipsis? (car x)))) pvars)) (syntax-error pat "misplaced ellipsis in syntax-case pattern")) (else (let ((y (gen-var 'tmp))) ; fat finger binding and references to temp variable y (build-application no-source (build-lambda no-source (list y) (let-syntax ((y (identifier-syntax (build-lexical-reference 'value no-source y)))) (build-conditional no-source (syntax-case fender () (#t y) (_ (build-conditional no-source y (build-dispatch-call pvars fender y r mr m?) (build-data no-source #f)))) (build-dispatch-call pvars exp y r mr m?) (gen-syntax-case x keys clauses r mr m?)))) (list (if (eq? p 'any) (build-application no-source (build-primref no-source 'list) (list (build-lexical-reference no-source 'value x))) (build-application no-source (build-primref no-source '$syntax-dispatch) (list (build-lexical-reference no-source 'value x) (build-data no-source p)))))))))))) (define gen-syntax-case (lambda (x keys clauses r mr m?) (if (null? clauses) (build-application no-source (build-primref no-source 'syntax-error) (list (build-lexical-reference 'value no-source x))) (syntax-case (car clauses) () ((pat exp) (if (and (id? (syntax pat)) (not (bound-id-member? (syntax pat) keys)) (not (ellipsis? (syntax pat)))) (let ((label (gen-label)) (var (gen-var (syntax pat)))) (build-application no-source (build-lambda no-source (list var) (chi (syntax exp) (extend-env label (make-binding 'syntax `(,var . 0)) r) mr (make-binding-wrap (syntax (pat)) (list label) empty-wrap) m?)) (list (build-lexical-reference 'value no-source x)))) (gen-clause x keys (cdr clauses) r mr m? (syntax pat) #t (syntax exp)))) ((pat fender exp) (gen-clause x keys (cdr clauses) r mr m? (syntax pat) (syntax fender) (syntax exp))) (_ (syntax-error (car clauses) "invalid syntax-case clause")))))) (lambda (e r mr w ae m?) (let ((e (source-wrap e w ae))) (syntax-case e () ((_ val (key ...) m ...) (if (andmap (lambda (x) (and (id? x) (not (ellipsis? x)))) (syntax (key ...))) (let ((x (gen-var 'tmp))) ; fat finger binding and references to temp variable x (build-application ae (build-lambda no-source (list x) (gen-syntax-case x (syntax (key ...)) (syntax (m ...)) r mr m?)) (list (chi (syntax val) r mr empty-wrap m?)))) (syntax-error e "invalid literals list in")))))))) (put-cte-hook 'module (lambda (x) (define proper-export? (lambda (e) (syntax-case e () ((id e ...) (and (identifier? (syntax id)) (andmap proper-export? (syntax (e ...))))) (id (identifier? (syntax id)))))) (with-syntax ((orig x)) (syntax-case x () ((_ (e ...) d ...) (if (andmap proper-export? (syntax (e ...))) (syntax (begin ($module orig anon (e ...) d ...) ($import orig #f anon))) (syntax-error x "invalid exports list in"))) ((_ m (e ...) d ...) (identifier? (syntax m)) (if (andmap proper-export? (syntax (e ...))) (syntax ($module orig m (e ...) d ...)) (syntax-error x "invalid exports list in"))))))) (let () (define $module-exports (lambda (m r) (let ((b (r m))) (case (binding-type b) (($module) (let* ((interface (binding-value b)) (new-marks (import-mark-delta m interface))) (vmap (lambda (x) (let ((id (if (pair? x) (car x) x))) (make-syntax-object (syntax-object->datum id) (let ((marks (join-marks new-marks (wrap-marks (syntax-object-wrap id))))) (make-wrap marks ; the anti mark should always be present at the head ; of new-marks, but we paranoically check anyway (if (eq? (car marks) the-anti-mark) (cons 'shift (wrap-subst top-wrap)) (wrap-subst top-wrap))))))) (interface-exports interface)))) ((displaced-lexical) (displaced-lexical-error m)) (else (syntax-error m "unknown module")))))) (define $import-help (lambda (orig import-only?) (lambda (r) (define difference (lambda (ls1 ls2) (if (null? ls1) ls1 (if (bound-id-member? (car ls1) ls2) (difference (cdr ls1) ls2) (cons (car ls1) (difference (cdr ls1) ls2)))))) (define prefix-add (lambda (prefix-id) (let ((prefix (symbol->string (syntax-object->datum prefix-id)))) (lambda (id) (datum->syntax-object id (string->symbol (string-append prefix (symbol->string (syntax-object->datum id))))))))) (define prefix-drop (lambda (prefix-id) (let ((prefix (symbol->string (syntax-object->datum prefix-id)))) (lambda (id) (let ((s (symbol->string (syntax-object->datum id)))) (let ((np (string-length prefix)) (ns (string-length s))) (unless (and (>= ns np) (string=? (substring s 0 np) prefix)) (syntax-error id (string-append "missing expected prefix " prefix))) (datum->syntax-object id (string->symbol (substring s np ns))))))))) (define gen-mid (lambda (mid) ; introduced module ids must have same marks as original ; for import-only, since the barrier carries the marks of ; the module id (datum->syntax-object mid (generate-id (id-sym-name mid))))) (define (modspec m exports?) (with-syntax ((orig orig) (import-only? import-only?)) (syntax-case m (only-for-syntax also-for-syntax only except add-prefix drop-prefix rename alias) ((only m id ...) (andmap identifier? (syntax (id ...))) (let-values (((mid d exports) (modspec (syntax m) #f))) (with-syntax ((d d) (tmid (gen-mid mid))) (values mid (syntax (begin ($module orig tmid (id ...) d) ($import orig import-only? tmid))) (and exports? (syntax (id ...))))))) ((except m id ...) (andmap identifier? (syntax (id ...))) (let-values (((mid d exports) (modspec (syntax m) #t))) (with-syntax ((d d) (tmid (gen-mid mid)) ((id ...) (difference exports (syntax (id ...))))) (values mid (syntax (begin ($module orig tmid (id ...) d) ($import orig import-only? tmid))) (and exports? (syntax (id ...))))))) ((add-prefix m prefix-id) (identifier? (syntax prefix-id)) (let-values (((mid d exports) (modspec (syntax m) #t))) (with-syntax ((d d) (tmid (gen-mid mid)) ((old-id ...) exports) ((tmp ...) (generate-temporaries exports)) ((id ...) (map (prefix-add (syntax prefix-id)) exports))) (values mid (syntax (begin ($module orig tmid ((id tmp) ...) ($module orig tmid ((tmp old-id) ...) d (alias tmp old-id) ...) ($import orig import-only? tmid) (alias id tmp) ...) ($import orig import-only? tmid))) (and exports? (syntax (id ...))))))) ((drop-prefix m prefix-id) (identifier? (syntax prefix-id)) (let-values (((mid d exports) (modspec (syntax m) #t))) (with-syntax ((d d) (tmid (gen-mid mid)) ((old-id ...) exports) ((tmp ...) (generate-temporaries exports)) ((id ...) (map (prefix-drop (syntax prefix-id)) exports))) (values mid (syntax (begin ($module orig tmid ((id tmp) ...) ($module orig tmid ((tmp old-id) ...) d (alias tmp old-id) ...) ($import orig import-only? tmid) (alias id tmp) ...) ($import orig import-only? tmid))) (and exports? (syntax (id ...))))))) ((rename m (new-id old-id) ...) (and (andmap identifier? (syntax (new-id ...))) (andmap identifier? (syntax (old-id ...)))) (let-values (((mid d exports) (modspec (syntax m) #t))) (with-syntax ((d d) (tmid (gen-mid mid)) ((tmp ...) (generate-temporaries (syntax (old-id ...)))) ((other-id ...) (difference exports (syntax (old-id ...))))) (values mid (syntax (begin ($module orig tmid ((new-id tmp) ... other-id ...) ($module orig tmid (other-id ... (tmp old-id) ...) d (alias tmp old-id) ...) ($import orig import-only? tmid) (alias new-id tmp) ...) ($import orig import-only? tmid))) (and exports? (syntax (new-id ... other-id ...))))))) ((alias m (new-id old-id) ...) (and (andmap identifier? (syntax (new-id ...))) (andmap identifier? (syntax (old-id ...)))) (let-values (((mid d exports) (modspec (syntax m) #t))) (with-syntax ((d d) (tmid (gen-mid mid)) ((other-id ...) exports)) (values mid (syntax (begin ($module orig tmid ((new-id old-id) ... other-id ...) d (alias new-id old-id) ...) ($import orig import-only? tmid))) (and exports? (syntax (new-id ... other-id ...))))))) ; base cases (mid (identifier? (syntax mid)) (values (syntax mid) (syntax ($import orig import-only? mid)) (and exports? ($module-exports (syntax mid) r)))) ((mid) (identifier? (syntax mid)) (values (syntax mid) (syntax ($import orig import-only? mid)) (and exports? ($module-exports (syntax mid) r)))) (_ (syntax-error m "invalid module specifier"))))) (define modspec* (lambda (m) (let-values (((mid d exports) (modspec m #f))) d))) (syntax-case orig () ((_ m ...) (with-syntax (((d ...) (map modspec* (syntax (m ...))))) (syntax (begin d ...)))))))) (put-cte-hook 'import (lambda (orig) ($import-help orig #f))) (put-cte-hook 'import-only (lambda (orig) ($import-help orig #t))) ) ;;; To support eval-when, we maintain two mode sets: ;;; ;;; ctem (compile-time-expression mode) ;;; determines whether/when to evaluate compile-time expressions such ;;; as macro definitions, module definitions, and compile-time ;;; registration of variable definitions ;;; ;;; rtem (run-time-expression mode) ;;; determines whether/when to evaluate run-time expressions such ;;; as the actual assignment performed by a variable definition or ;;; arbitrary top-level expressions ;;; Possible modes in the mode set are: ;;; ;;; L (load): evaluate at load time. implies V for compile-time ;;; expressions and R for run-time expressions. ;;; ;;; C (compile): evaluate at compile (file) time ;;; ;;; E (eval): evaluate at evaluation (compile or interpret) time ;;; ;;; V (visit): evaluate at visit time ;;; ;;; R (revisit): evaluate at revisit time ;;; The mode set for the body of an eval-when is determined by ;;; translating each mode in the old mode set based on the situations ;;; present in the eval-when form and combining these into a set, ;;; using the following table. See also update-mode-set. ;;; load compile visit revisit eval ;;; ;;; L L C V R - ;;; ;;; C - - - - C ;;; ;;; V V C V - - ;;; ;;; R R C - R - ;;; ;;; E - - - - E ;;; When we complete the expansion of a compile or run-time expression, ;;; the current ctem or rtem determines how the expression will be ;;; treated. See ct-eval/residualize and rt-eval/residualize. ;;; Initial mode sets ;;; ;;; when compiling a file: ;;; ;;; initial ctem: (L C) ;;; ;;; initial rtem: (L) ;;; ;;; when not compiling a file: ;;; ;;; initial ctem: (E) ;;; ;;; initial rtem: (E) ;;; ;;; ;;; This means that top-level syntactic definitions are evaluated ;;; immediately after they are expanded, and the expanded definitions ;;; are also residualized into the object file if we are compiling ;;; a file. (set! sc-expand (let ((ctem '(E)) (rtem '(E))) (lambda (x) (let ((env (interaction-environment))) (if (and (pair? x) (equal? (car x) noexpand)) (cadr x) (chi-top* x null-env (env-wrap env) ctem rtem #f (env-top-ribcage env))))))) (set! $make-environment (lambda (token mutable?) (let ((top-ribcage (make-top-ribcage token mutable?))) (make-env top-ribcage (make-wrap (wrap-marks top-wrap) (cons top-ribcage (wrap-subst top-wrap))))))) (set! environment? (lambda (x) (env? x))) (set! interaction-environment (let ((e ($make-environment '*top* #t))) (lambda () e))) (set! identifier? (lambda (x) (nonsymbol-id? x))) (set! datum->syntax-object (lambda (id datum) (arg-check nonsymbol-id? id 'datum->syntax-object) (make-syntax-object datum (syntax-object-wrap id)))) (set! syntax->list (lambda (orig-ls) (let f ((ls orig-ls)) (syntax-case ls () (() '()) ((x . r) (cons #'x (f #'r))) (_ (error 'syntax->list "invalid argument ~s" orig-ls)))))) (set! syntax->vector (lambda (v) (syntax-case v () (#(x ...) (apply vector (syntax->list #'(x ...)))) (_ (error 'syntax->vector "invalid argument ~s" v))))) (set! syntax-object->datum ; accepts any object, since syntax objects may consist partially ; or entirely of unwrapped, nonsymbolic data (lambda (x) (strip x empty-wrap))) (set! generate-temporaries (let ((n 0)) (lambda (ls) (arg-check list? ls 'generate-temporaries) (map (lambda (x) (set! n (+ n 1)) (wrap ; unique name to distinguish from other temporaries (string->symbol (string-append "t" (number->string n))) ; unique mark (in tmp-wrap) to distinguish from non-temporaries tmp-wrap)) ls)))) (set! free-identifier=? (lambda (x y) (arg-check nonsymbol-id? x 'free-identifier=?) (arg-check nonsymbol-id? y 'free-identifier=?) (free-id=? x y))) (set! bound-identifier=? (lambda (x y) (arg-check nonsymbol-id? x 'bound-identifier=?) (arg-check nonsymbol-id? y 'bound-identifier=?) (bound-id=? x y))) (set! literal-identifier=? (lambda (x y) (arg-check nonsymbol-id? x 'literal-identifier=?) (arg-check nonsymbol-id? y 'literal-identifier=?) (literal-id=? x y))) (set! syntax-error (lambda (object . messages) (for-each (lambda (x) (arg-check string? x 'syntax-error)) messages) (let ((message (if (null? messages) "invalid syntax" (apply string-append messages)))) (error-hook #f message (strip object empty-wrap))))) ;;; syntax-dispatch expects an expression and a pattern. If the expression ;;; matches the pattern a list of the matching expressions for each ;;; "any" is returned. Otherwise, #f is returned. (This use of #f will ;;; not work on r4rs implementations that violate the ieee requirement ;;; that #f and () be distinct.) ;;; The expression is matched with the pattern as follows: ;;; p in pattern: matches: ;;; () empty list ;;; any anything ;;; (p1 . p2) pair (list) ;;; #(free-id <key>) <key> with literal-identifier=? ;;; each-any any proper list ;;; #(each p) (p*) ;;; #(each+ p1 (p2_1 ...p2_n) p3) (p1* (p2_n ... p2_1) . p3) ;;; #(vector p) (list->vector p) ;;; #(atom <object>) <object> with "equal?" ;;; Vector cops out to pair under assumption that vectors are rare. If ;;; not, should convert to: ;;; #(vector p) #(p*) (let () (define match-each (lambda (e p w) (cond ((annotation? e) (match-each (annotation-expression e) p w)) ((pair? e) (let ((first (match (car e) p w '()))) (and first (let ((rest (match-each (cdr e) p w))) (and rest (cons first rest)))))) ((null? e) '()) ((syntax-object? e) (match-each (syntax-object-expression e) p (join-wraps w (syntax-object-wrap e)))) (else #f)))) (define match-each+ (lambda (e x-pat y-pat z-pat w r) (let f ((e e) (w w)) (cond ((pair? e) (let-values (((xr* y-pat r) (f (cdr e) w))) (if r (if (null? y-pat) (let ((xr (match (car e) x-pat w '()))) (if xr (values (cons xr xr*) y-pat r) (values #f #f #f))) (values '() (cdr y-pat) (match (car e) (car y-pat) w r))) (values #f #f #f)))) ((annotation? e) (f (annotation-expression e) w)) ((syntax-object? e) (f (syntax-object-expression e) (join-wraps w (syntax-object-wrap e)))) (else (values '() y-pat (match e z-pat w r))))))) (define match-each-any (lambda (e w) (cond ((annotation? e) (match-each-any (annotation-expression e) w)) ((pair? e) (let ((l (match-each-any (cdr e) w))) (and l (cons (wrap (car e) w) l)))) ((null? e) '()) ((syntax-object? e) (match-each-any (syntax-object-expression e) (join-wraps w (syntax-object-wrap e)))) (else #f)))) (define match-empty (lambda (p r) (cond ((null? p) r) ((eq? p 'any) (cons '() r)) ((pair? p) (match-empty (car p) (match-empty (cdr p) r))) ((eq? p 'each-any) (cons '() r)) (else (case (vector-ref p 0) ((each) (match-empty (vector-ref p 1) r)) ((each+) (match-empty (vector-ref p 1) (match-empty (reverse (vector-ref p 2)) (match-empty (vector-ref p 3) r)))) ((free-id atom) r) ((vector) (match-empty (vector-ref p 1) r))))))) (define combine (lambda (r* r) (if (null? (car r*)) r (cons (map car r*) (combine (map cdr r*) r))))) (define match* (lambda (e p w r) (cond ((null? p) (and (null? e) r)) ((pair? p) (and (pair? e) (match (car e) (car p) w (match (cdr e) (cdr p) w r)))) ((eq? p 'each-any) (let ((l (match-each-any e w))) (and l (cons l r)))) (else (case (vector-ref p 0) ((each) (if (null? e) (match-empty (vector-ref p 1) r) (let ((r* (match-each e (vector-ref p 1) w))) (and r* (combine r* r))))) ((free-id) (and (id? e) (literal-id=? (wrap e w) (vector-ref p 1)) r)) ((each+) (let-values (((xr* y-pat r) (match-each+ e (vector-ref p 1) (vector-ref p 2) (vector-ref p 3) w r))) (and r (null? y-pat) (if (null? xr*) (match-empty (vector-ref p 1) r) (combine xr* r))))) ((atom) (and (equal? (vector-ref p 1) (strip e w)) r)) ((vector) (and (vector? e) (match (vector->list e) (vector-ref p 1) w r)))))))) (define match (lambda (e p w r) (cond ((not r) #f) ((eq? p 'any) (cons (wrap e w) r)) ((syntax-object? e) (match* (unannotate (syntax-object-expression e)) p (join-wraps w (syntax-object-wrap e)) r)) (else (match* (unannotate e) p w r))))) (set! $syntax-dispatch (lambda (e p) (cond ((eq? p 'any) (list e)) ((syntax-object? e) (match* (unannotate (syntax-object-expression e)) p (syntax-object-wrap e) '())) (else (match* (unannotate e) p empty-wrap '()))))) )) (define-syntax with-syntax (lambda (x) (syntax-case x () ((_ () e1 e2 ...) (syntax (begin e1 e2 ...))) ((_ ((out in)) e1 e2 ...) (syntax (syntax-case in () (out (begin e1 e2 ...))))) ((_ ((out in) ...) e1 e2 ...) (syntax (syntax-case (list in ...) () ((out ...) (begin e1 e2 ...)))))))) (define-syntax with-implicit (syntax-rules () ((_ (tid id ...) e1 e2 ...) (andmap identifier? (syntax (tid id ...))) (begin (unless (identifier? (syntax tid)) (syntax-error (syntax tid) "non-identifier with-implicit template")) (with-syntax ((id (datum->syntax-object (syntax tid) 'id)) ...) e1 e2 ...))))) (define-syntax datum (syntax-rules () ((_ x) (syntax-object->datum (syntax x))))) (define-syntax syntax-rules (lambda (x) (define clause (lambda (y) (syntax-case y () (((keyword . pattern) template) (syntax ((dummy . pattern) (syntax template)))) (((keyword . pattern) fender template) (syntax ((dummy . pattern) fender (syntax template)))) (_ (syntax-error x))))) (syntax-case x () ((_ (k ...) cl ...) (andmap identifier? (syntax (k ...))) (with-syntax (((cl ...) (map clause (syntax (cl ...))))) (syntax (lambda (x) (syntax-case x (k ...) cl ...)))))))) (define-syntax or (lambda (x) (syntax-case x () ((_) (syntax #f)) ((_ e) (syntax e)) ((_ e1 e2 e3 ...) (syntax (let ((t e1)) (if t t (or e2 e3 ...)))))))) (define-syntax and (lambda (x) (syntax-case x () ((_ e1 e2 e3 ...) (syntax (if e1 (and e2 e3 ...) #f))) ((_ e) (syntax e)) ((_) (syntax #t))))) (define-syntax let (lambda (x) (syntax-case x () ((_ ((x v) ...) e1 e2 ...) (andmap identifier? (syntax (x ...))) (syntax ((lambda (x ...) e1 e2 ...) v ...))) ((_ f ((x v) ...) e1 e2 ...) (andmap identifier? (syntax (f x ...))) (syntax ((letrec ((f (lambda (x ...) e1 e2 ...))) f) v ...)))))) (define-syntax let* (lambda (x) (syntax-case x () ((let* ((x v) ...) e1 e2 ...) (andmap identifier? (syntax (x ...))) (let f ((bindings (syntax ((x v) ...)))) (if (null? bindings) (syntax (let () e1 e2 ...)) (with-syntax ((body (f (cdr bindings))) (binding (car bindings))) (syntax (let (binding) body))))))))) (define-syntax cond (lambda (x) (syntax-case x () ((_ m1 m2 ...) (let f ((clause (syntax m1)) (clauses (syntax (m2 ...)))) (if (null? clauses) (syntax-case clause (else =>) ((else e1 e2 ...) (syntax (begin e1 e2 ...))) ((e0) (syntax (let ((t e0)) (if t t)))) ((e0 => e1) (syntax (let ((t e0)) (if t (e1 t))))) ((e0 e1 e2 ...) (syntax (if e0 (begin e1 e2 ...)))) (_ (syntax-error x))) (with-syntax ((rest (f (car clauses) (cdr clauses)))) (syntax-case clause (else =>) ((e0) (syntax (let ((t e0)) (if t t rest)))) ((e0 => e1) (syntax (let ((t e0)) (if t (e1 t) rest)))) ((e0 e1 e2 ...) (syntax (if e0 (begin e1 e2 ...) rest))) (_ (syntax-error x)))))))))) (define-syntax do (lambda (orig-x) (syntax-case orig-x () ((_ ((var init . step) ...) (e0 e1 ...) c ...) (with-syntax (((step ...) (map (lambda (v s) (syntax-case s () (() v) ((e) (syntax e)) (_ (syntax-error orig-x)))) (syntax (var ...)) (syntax (step ...))))) (syntax-case (syntax (e1 ...)) () (() (syntax (let do ((var init) ...) (if (not e0) (begin c ... (do step ...)))))) ((e1 e2 ...) (syntax (let do ((var init) ...) (if e0 (begin e1 e2 ...) (begin c ... (do step ...)))))))))))) (define-syntax quasiquote (let () (define (quasi p lev) (syntax-case p (unquote quasiquote) ((unquote p) (if (= lev 0) #'("value" p) (quasicons #'("quote" unquote) (quasi #'(p) (- lev 1))))) ((quasiquote p) (quasicons #'("quote" quasiquote) (quasi #'(p) (+ lev 1)))) ((p . q) (syntax-case #'p (unquote unquote-splicing) ((unquote p ...) (if (= lev 0) (quasilist* #'(("value" p) ...) (quasi #'q lev)) (quasicons (quasicons #'("quote" unquote) (quasi #'(p ...) (- lev 1))) (quasi #'q lev)))) ((unquote-splicing p ...) (if (= lev 0) (quasiappend #'(("value" p) ...) (quasi #'q lev)) (quasicons (quasicons #'("quote" unquote-splicing) (quasi #'(p ...) (- lev 1))) (quasi #'q lev)))) (_ (quasicons (quasi #'p lev) (quasi #'q lev))))) (#(x ...) (quasivector (vquasi #'(x ...) lev))) (p #'("quote" p)))) (define (vquasi p lev) (syntax-case p () ((p . q) (syntax-case #'p (unquote unquote-splicing) ((unquote p ...) (if (= lev 0) (quasilist* #'(("value" p) ...) (vquasi #'q lev)) (quasicons (quasicons #'("quote" unquote) (quasi #'(p ...) (- lev 1))) (vquasi #'q lev)))) ((unquote-splicing p ...) (if (= lev 0) (quasiappend #'(("value" p) ...) (vquasi #'q lev)) (quasicons (quasicons #'("quote" unquote-splicing) (quasi #'(p ...) (- lev 1))) (vquasi #'q lev)))) (_ (quasicons (quasi #'p lev) (vquasi #'q lev))))) (() #'("quote" ())))) (define (quasicons x y) (with-syntax ((x x) (y y)) (syntax-case #'y () (("quote" dy) (syntax-case #'x () (("quote" dx) #'("quote" (dx . dy))) (_ (if (null? #'dy) #'("list" x) #'("list*" x y))))) (("list" . stuff) #'("list" x . stuff)) (("list*" . stuff) #'("list*" x . stuff)) (_ #'("list*" x y))))) (define (quasiappend x y) (syntax-case y () (("quote" ()) (cond ((null? x) #'("quote" ())) ((null? (cdr x)) (car x)) (else (with-syntax (((p ...) x)) #'("append" p ...))))) (_ (cond ((null? x) y) (else (with-syntax (((p ...) x) (y y)) #'("append" p ... y))))))) (define (quasilist* x y) (let f ((x x)) (if (null? x) y (quasicons (car x) (f (cdr x)))))) (define (quasivector x) (syntax-case x () (("quote" (x ...)) #'("quote" #(x ...))) (_ (let f ((y x) (k (lambda (ls) #`("vector" #,@ls)))) (syntax-case y () (("quote" (y ...)) (k #'(("quote" y) ...))) (("list" y ...) (k #'(y ...))) (("list*" y ... z) (f #'z (lambda (ls) (k (append #'(y ...) ls))))) (else #`("list->vector" #,x))))))) (define (emit x) (syntax-case x () (("quote" x) #''x) (("list" x ...) #`(list #,@(map emit #'(x ...)))) ; could emit list* for 3+ arguments if implementation supports list* (("list*" x ... y) (let f ((x* #'(x ...))) (if (null? x*) (emit #'y) #`(cons #,(emit (car x*)) #,(f (cdr x*)))))) (("append" x ...) #`(append #,@(map emit #'(x ...)))) (("vector" x ...) #`(vector #,@(map emit #'(x ...)))) (("list->vector" x) #`(list->vector #,(emit #'x))) (("value" x) #'x))) (lambda (x) (syntax-case x () ; convert to intermediate language, combining introduced (but not ; unquoted source) quote expressions where possible and choosing ; optimal construction code otherwise, then emit Scheme code ; corresponding to the intermediate language forms. ((_ e) (emit (quasi #'e 0))))))) (define-syntax unquote (lambda (x) (syntax-error x "misplaced"))) (define-syntax unquote-splicing (lambda (x) (syntax-error x "misplaced"))) (define-syntax quasisyntax (lambda (x) (define (qs q n b* k) (syntax-case q (quasisyntax unsyntax unsyntax-splicing) ((quasisyntax . d) (qs #'d (+ n 1) b* (lambda (b* dnew) (k b* (if (eq? dnew #'d) q (with-syntax ((d dnew)) #'(quasisyntax . d))))))) ((unsyntax . d) (not (= n 0)) (qs #'d (- n 1) b* (lambda (b* dnew) (k b* (if (eq? dnew #'d) q (with-syntax ((d dnew)) #'(unsyntax . d))))))) ((unsyntax-splicing . d) (not (= n 0)) (qs #'d (- n 1) b* (lambda (b* dnew) (k b* (if (eq? dnew #'d) q (with-syntax ((d dnew)) #'(unsyntax-splicing . d))))))) ((unsyntax q) (= n 0) (with-syntax (((t) (generate-temporaries #'(q)))) (k (cons #'(t q) b*) #'t))) (((unsyntax q ...) . d) (= n 0) (qs #'d n b* (lambda (b* dnew) (with-syntax (((t ...) (generate-temporaries #'(q ...)))) (k (append #'((t q) ...) b*) (with-syntax ((d dnew)) #'(t ... . d))))))) (((unsyntax-splicing q ...) . d) (= n 0) (qs #'d n b* (lambda (b* dnew) (with-syntax (((t ...) (generate-temporaries #'(q ...)))) (k (append #'(((t (... ...)) q) ...) b*) (with-syntax ((((m ...) ...) #'((t (... ...)) ...))) (with-syntax ((d dnew)) #'(m ... ... . d)))))))) ((a . d) (qs #'a n b* (lambda (b* anew) (qs #'d n b* (lambda (b* dnew) (k b* (if (and (eq? anew #'a) (eq? dnew #'d)) q (with-syntax ((a anew) (d dnew)) #'(a . d))))))))) (#(x ...) (vqs #'(x ...) n b* (lambda (b* xnew*) (k b* (if (let same? ((x* #'(x ...)) (xnew* xnew*)) (if (null? x*) (null? xnew*) (and (not (null? xnew*)) (eq? (car x*) (car xnew*)) (same? (cdr x*) (cdr xnew*))))) q (with-syntax (((x ...) xnew*)) #'#(x ...))))))) (_ (k b* q)))) (define (vqs x* n b* k) (if (null? x*) (k b* '()) (vqs (cdr x*) n b* (lambda (b* xnew*) (syntax-case (car x*) (unsyntax unsyntax-splicing) ((unsyntax q ...) (= n 0) (with-syntax (((t ...) (generate-temporaries #'(q ...)))) (k (append #'((t q) ...) b*) (append #'(t ...) xnew*)))) ((unsyntax-splicing q ...) (= n 0) (with-syntax (((t ...) (generate-temporaries #'(q ...)))) (k (append #'(((t (... ...)) q) ...) b*) (with-syntax ((((m ...) ...) #'((t (... ...)) ...))) (append #'(m ... ...) xnew*))))) (_ (qs (car x*) n b* (lambda (b* xnew) (k b* (cons xnew xnew*)))))))))) (syntax-case x () ((_ x) (qs #'x 0 '() (lambda (b* xnew) (if (eq? xnew #'x) #'(syntax x) (with-syntax (((b ...) b*) (x xnew)) #'(with-syntax (b ...) (syntax x)))))))))) (define-syntax unsyntax (lambda (x) (syntax-error x "misplaced"))) (define-syntax unsyntax-splicing (lambda (x) (syntax-error x "misplaced"))) (define-syntax include (lambda (x) (define read-file (lambda (fn k) (let ((p (open-input-file fn))) (let f () (let ((x (read p))) (if (eof-object? x) (begin (close-input-port p) '()) (cons (datum->syntax-object k x) (f)))))))) (syntax-case x () ((k filename) (let ((fn (syntax-object->datum (syntax filename)))) (with-syntax (((exp ...) (read-file fn (syntax k)))) (syntax (begin exp ...)))))))) (define-syntax case (lambda (x) (syntax-case x () ((_ e m1 m2 ...) (with-syntax ((body (let f ((clause (syntax m1)) (clauses (syntax (m2 ...)))) (if (null? clauses) (syntax-case clause (else) ((else e1 e2 ...) (syntax (begin e1 e2 ...))) (((k ...) e1 e2 ...) (syntax (if (memv t '(k ...)) (begin e1 e2 ...)))) (_ (syntax-error x))) (with-syntax ((rest (f (car clauses) (cdr clauses)))) (syntax-case clause (else) (((k ...) e1 e2 ...) (syntax (if (memv t '(k ...)) (begin e1 e2 ...) rest))) (_ (syntax-error x)))))))) (syntax (let ((t e)) body))))))) (define-syntax identifier-syntax (syntax-rules (set!) ((_ e) (lambda (x) (syntax-case x () (id (identifier? (syntax id)) (syntax e)) ((_ x (... ...)) (syntax (e x (... ...))))))) ((_ (id exp1) ((set! var val) exp2)) (and (identifier? (syntax id)) (identifier? (syntax var))) (cons 'macro! (lambda (x) (syntax-case x (set!) ((set! var val) (syntax exp2)) ((id x (... ...)) (syntax (exp1 x (... ...)))) (id (identifier? (syntax id)) (syntax exp1))))))))