diff options
Diffstat (limited to 'module/language/r5rs/psyntax.ss')
-rw-r--r-- | module/language/r5rs/psyntax.ss | 3202 |
1 files changed, 3202 insertions, 0 deletions
diff --git a/module/language/r5rs/psyntax.ss b/module/language/r5rs/psyntax.ss new file mode 100644 index 000000000..c8ac3e503 --- /dev/null +++ b/module/language/r5rs/psyntax.ss @@ -0,0 +1,3202 @@ +;;; Portable implementation of syntax-case +;;; Extracted from Chez Scheme Version 6.3 +;;; Authors: R. Kent Dybvig, Oscar Waddell, Bob Hieb, Carl Bruggeman + +;;; Copyright (c) 1992-2000 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, +;;; Second Edition (R. Kent Dybvig, Prentice Hall, 1996), which can be +;;; found online at http://www.scheme.com. 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. They are described +;;; briefly here as well. +;;; +;;; Both 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. + +;;; 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) +;;; 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. +;;; +;;; (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 make-annotation, annotation-expression, +;;; annotation-source, annotation-stripped, and set-annotation-stripped! +;;; are supplied. If annotations are supplied, the proper annotation +;;; source 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 recognize old representations and treat +;;; them as not lexically bound. + + +(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 noexpand "noexpand") + +;;; 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 annotation? (lambda (x) #f)) + +(define top-level-eval-hook + (lambda (x) + (eval `(,noexpand ,x)))) + +(define local-eval-hook + (lambda (x) + (eval `(,noexpand ,x)))) + +(define error-hook + (lambda (who why what) + (error who "~a ~s" why what))) + +(define-syntax gensym-hook + (syntax-rules () + ((_) (gensym)))) + +(define put-global-definition-hook + (lambda (symbol val) + ($sc-put-cte symbol val))) + +(define get-global-definition-hook + (lambda (symbol) + (getprop symbol '*sc-expander*))) + +(define get-import-binding + (lambda (symbol token) + (getprop symbol token))) + +(define generate-id + (let ((b (- 127 32 2))) + ; session-key should generate a unique integer for each system run + ; to support separate compilation + (define session-key (lambda () 0)) + (define make-digit (lambda (x) (integer->char (fx+ x 33)))) + (define fmt + (lambda (n) + (let fmt ((n n) (a '())) + (if (< n b) + (list->string (cons (make-digit n) a)) + (let ((r (modulo n b)) (rest (quotient n b))) + (fmt rest (cons (make-digit r) a))))))) + (let ((prefix (fmt (session-key))) (n -1)) + (lambda (name) + (set! n (+ n 1)) + (let ((newsym (string->symbol (string-append "#" prefix (fmt n))))) + newsym))))) +) + + +;;; output constructors +(begin +(define-syntax build-application + (syntax-rules () + ((_ source fun-exp arg-exps) + `(,fun-exp . ,arg-exps)))) + +(define-syntax build-conditional + (syntax-rules () + ((_ source test-exp then-exp else-exp) + `(if ,test-exp ,then-exp ,else-exp)))) + +(define-syntax build-lexical-reference + (syntax-rules () + ((_ type source var) + var))) + +(define-syntax build-lexical-assignment + (syntax-rules () + ((_ source var exp) + `(set! ,var ,exp)))) + +(define-syntax build-global-reference + (syntax-rules () + ((_ source var) + var))) + +(define-syntax build-global-assignment + (syntax-rules () + ((_ source var exp) + `(set! ,var ,exp)))) + +(define-syntax build-global-definition + (syntax-rules () + ((_ source var exp) + `(define ,var ,exp)))) + +(define-syntax build-module-definition + ; should have the effect of a global definition but may not appear at top level + (identifier-syntax build-global-assignment)) + +(define-syntax build-cte-install + ; should build a call that has the same effect as calling the + ; global definition hook + (syntax-rules () + ((_ sym exp) `($sc-put-cte ',sym ,exp)))) + +(define-syntax build-lambda + (syntax-rules () + ((_ src vars exp) + `(lambda ,vars ,exp)))) + +(define-syntax build-primref + (syntax-rules () + ((_ src name) name) + ((_ src level name) name))) + +(define-syntax build-data + (syntax-rules () + ((_ src exp) `',exp))) + +(define build-sequence + (lambda (src exps) + (if (null? (cdr exps)) + (car exps) + `(begin ,@exps)))) + +(define build-letrec + (lambda (src vars val-exps body-exp) + (if (null? vars) + body-exp + `(letrec ,(map list vars val-exps) ,body-exp)))) + +(define-syntax build-lexical-var + (syntax-rules () + ((_ src id) (gensym)))) + +(define-syntax self-evaluating? + (syntax-rules () + ((_ e) + (let ((x e)) + (or (boolean? x) (number? x) (string? x) (char? x) (null? x)))))) +) + +(define-structure (syntax-object expression wrap)) + +(define-syntax unannotate + (syntax-rules () + ((_ x) + (let ((e x)) + (if (annotation? e) + (annotation-expression e) + e))))) + +(define-syntax no-source (identifier-syntax #f)) + +(define source-annotation + (lambda (x) + (cond + ((annotation? x) (annotation-source x)) + ((syntax-object? x) (source-annotation (syntax-object-expression x))) + (else no-source)))) + +(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> ::= (macro . <procedure>) macros +;;; (deferred . <expanded code>) lazy-evaluation of transformers +;;; (core . <procedure>) core forms +;;; (begin) begin +;;; (define) define +;;; (define-syntax) define-syntax +;;; (local-syntax . rec?) let-syntax/letrec-syntax +;;; (eval-when) eval-when +;;; (syntax . (<var> . <level>)) pattern variables +;;; (global . <symbol>) assumed global 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, and eval-when are +;;; treated specially since they are sensitive to whether the form is +;;; at top-level and (except for eval-when) 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 make-binding (lambda (x y) (cons x y))) +(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))))) + +;;; we use a "macros only" environment in expansion of local macro +;;; definitions so that their definitions can use local macros without +;;; attempting to use other lexical identifiers. +;;; +;;; - can make this null-env if we don't want to allow macros to use other +;;; macros in defining their transformers +;;; - can add a cache here if it pays off +(define transformer-env + (lambda (r) + (if (null? r) + '() + (let ((a (car r))) + (if (eq? (cadr a) 'lexical) ; only strip out lexical so that (transformer x) works + (transformer-env (cdr r)) + (cons a (transformer-env (cdr r)))))))) + +(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 sanitize-binding + (lambda (b) + (cond + ((procedure? b) (make-binding 'macro b)) + ((binding? b) + (case (binding-type b) + ((core macro macro!) (and (procedure? (binding-value b)) b)) + ((module) (and (interface? (binding-value b)) b)) + (else b))) + (else #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))) + (case (binding-type b) +; ((*alias) (lookup (id-var-name (binding-value b) empty-wrap) r)) + ((deferred) + (whack-binding! b + (let ((*b (local-eval-hook (binding-value b)))) + (or (sanitize-binding *b) + (syntax-error *b "invalid transformer")))) + (case (binding-type b) +; ((*alias) (lookup (id-var-name (binding-value b) empty-wrap) r)) + (else b))) + (else b))))) + +(define global-extend + (lambda (type sym val) + (put-global-definition-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-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 token> ::= #<"import-token" <token>> +;;; <token> ::= <generated id> + +(define make-wrap cons) +(define wrap-marks car) +(define wrap-subst cdr) + +(define-syntax subst-rename? (identifier-syntax vector?)) +(define-syntax rename-old (syntax-rules () ((_ x) (vector-ref x 0)))) +(define-syntax rename-new (syntax-rules () ((_ x) (vector-ref x 1)))) +(define-syntax rename-marks (syntax-rules () ((_ x) (vector-ref x 2)))) +(define-syntax make-rename + (syntax-rules () + ((_ old new marks) (vector old new marks)))) + +;;; 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 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. + +(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 gen-indirect-label + (lambda () (list (gen-label)))) + +(define indirect-label? pair?) +(define get-indirect-label car) +(define set-indirect-label! set-car!) + +(define-structure (ribcage symnames marks labels)) +(define-syntax empty-wrap (identifier-syntax '(()))) + +(define-syntax top-wrap (identifier-syntax '((top)))) + +(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))))) + +;;; 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) +(module (make-import-token import-token? import-token-key) + (define tag 'import-token) + (define make-import-token (lambda (x) (cons tag x))) + (define import-token? (lambda (x) (and (pair? x) (eq? (car x) tag)))) + (define import-token-key cdr)) + +;;; 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 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 token) + (set-ribcage-symnames! ribcage + (cons (make-import-token token) (ribcage-symnames ribcage))))) + +(define lookup-import-binding-name + (lambda (sym key marks) + (let ((new (get-import-binding sym key))) + (and new + (let f ((new new)) + (cond + ((pair? new) (or (f (car new)) (f (cdr new)))) + ((same-marks? marks (wrap-marks (syntax-object-wrap new))) new) + (else #f))))))) + +;;; 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)) + (if (not (null? ids)) + (call-with-values + (lambda () (id-sym-name&marks (car ids) w)) + (lambda (symname marks) + (vector-set! symnamevec i symname) + (vector-set! marksvec i marks) + (f (cdr ids) (fx+ i 1)))))) + (make-ribcage symnamevec marksvec labelvec)))) + (wrap-subst w)))))) + +;;; make-trimmed-syntax-object is used by make-resolved-interface to support +;;; creation of module export lists whose constituent ids do not contain +;;; unnecessary substitutions or marks. +(define make-trimmed-syntax-object + (lambda (id) + (call-with-values + (lambda () (id-var-name&marks id empty-wrap)) + (lambda (tosym marks) + (unless tosym + (syntax-error id "identifier not visible for export")) + (let ((fromsym (id-sym-name id))) + (make-syntax-object fromsym + (make-wrap marks + (list (make-ribcage (vector fromsym) (vector marks) (vector tosym)))))))))) + +;;; 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) + (smart-append s1 (wrap-subst w2)))) + (make-wrap + (smart-append m1 (wrap-marks w2)) + (smart-append s1 (wrap-subst w2))))))) + +(define join-marks + (lambda (m1 m2) + (smart-append m1 m2))) + +(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 id-var-name-loc&marks + (lambda (id w) + (define search + (lambda (sym subst marks) + (if (null? subst) + (values sym marks) + (let ((fst (car subst))) + (if (eq? fst 'shift) + (search sym (cdr subst) (cdr marks)) + (let ((symnames (ribcage-symnames fst))) + (if (vector? symnames) + (search-vector-rib sym subst marks symnames fst) + (search-list-rib sym subst marks symnames fst)))))))) + (define search-list-rib + (lambda (sym subst marks symnames ribcage) + (let f ((symnames symnames) (i 0)) + (cond + ((null? symnames) (search sym (cdr subst) marks)) + ((and (eq? (car symnames) sym) + (same-marks? marks (list-ref (ribcage-marks ribcage) i))) + (values (list-ref (ribcage-labels ribcage) i) marks)) + ((import-token? (car symnames)) + (cond + ((lookup-import-binding-name sym (import-token-key (car symnames)) marks) => + (lambda (id) + (if (symbol? id) + (values id marks) + (id-var-name&marks id empty-wrap)))) ; could be more efficient: new is a resolved id + (else (f (cdr symnames) i)))) + ((and (eq? (car symnames) 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 ((marks (join-marks (wrap-marks w) (wrap-marks w1)))) + (call-with-values (lambda () (search sym (wrap-subst w) marks)) + (lambda (new-id marks) + (if (eq? new-id sym) + (search sym (wrap-subst w1) marks) + (values new-id 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) + (call-with-values + (lambda () (id-var-name-loc&marks id w)) + (lambda (label marks) + (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) + (call-with-values + (lambda () (id-var-name-loc&marks id w)) + (lambda (label marks) label)))) + +(define id-var-name + ; this version follows indirect labels + (lambda (id w) + (call-with-values + (lambda () (id-var-name-loc&marks id w)) + (lambda (label marks) + (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-syntax literal-id=? (identifier-syntax free-id=?)) + +;;; 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 bound-id=? + (lambda (i j) + (if (and (syntax-object? i) (syntax-object? j)) + (and (eq? (unannotate (syntax-object-expression i)) + (unannotate (syntax-object-expression j))) + (same-marks? (wrap-marks (syntax-object-wrap i)) + (wrap-marks (syntax-object-wrap j)))) + (eq? (unannotate i) (unannotate 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 s) + (wrap (if s (make-annotation x s #f) x) w))) + +;;; expanding + +(define chi-sequence + (lambda (body r w s) + (build-sequence s + (let dobody ((body body) (r r) (w w)) + (if (null? body) + '() + (let ((first (chi (car body) r w))) + (cons first (dobody (cdr body) r w)))))))) + +(define chi-top-sequence + (lambda (body r w s m esew ribcage) + (build-sequence s + (let dobody ((body body) (r r) (w w) (m m) (esew esew)) + (if (null? body) + '() + (let ((first (chi-top (car body) r w m esew ribcage))) + (cons first (dobody (cdr body) r w m esew)))))))) + +(define chi-when-list + (lambda (e when-list w) + ; when-list is syntax'd version of list of situations + (let f ((when-list when-list) (situations '())) + (if (null? when-list) + situations + (f (cdr when-list) + (cons (let ((x (car when-list))) + (cond + ((literal-id=? x (syntax compile)) 'compile) + ((literal-id=? x (syntax load)) 'load) + ((literal-id=? x (syntax eval)) 'eval) + (else (syntax-error (wrap x w) + "invalid eval-when situation")))) + situations)))))) + +;;; syntax-type returns five values: type, value, e, w, and s. The first +;;; two are described in the table below. +;;; +;;; type value explanation +;;; ------------------------------------------------------------------- +;;; 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 +;;; module none module keyword +;;; module-form none module definition +;;; other none anything else +;;; syntax level pattern variable +;;; +;;; For all forms, e is the form, w is the wrap for e. and s is the source. +;;; +;;; syntax-type expands macros and unwraps as necessary to get to +;;; one of the forms above. + +(define syntax-type + (lambda (e r w s rib) + (cond + ((symbol? e) + (let* ((n (id-var-name e w)) + (b (lookup n r)) + (type (binding-type b))) + (case type + ((lexical) (values type (binding-value b) e w s)) + ((global) (values type (binding-value b) e w s)) + ((macro macro!) (syntax-type (chi-macro (binding-value b) e r w s rib) r empty-wrap #f rib)) + (else (values type (binding-value b) e w s))))) + ((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 s)) + ((macro macro!) + (syntax-type (chi-macro (binding-value b) e r w s rib) + r empty-wrap #f rib)) + ((core) (values type (binding-value b) e w s)) + ((local-syntax) + (values 'local-syntax-form (binding-value b) e w s)) + ((begin) (values 'begin-form #f e w s)) + ((eval-when) (values 'eval-when-form #f e w s)) + ((define) (values 'define-form #f e w s)) + ((define-syntax) (values 'define-syntax-form #f e w s)) + ((module-key) (values 'module-form #f e w s)) + ((import) (values 'import-form (and (binding-value b) (wrap first w)) e w s)) + ((set!) (chi-set! e r w s rib)) + (else (values 'call #f e w s)))) + (values 'call #f e w s)))) + ((syntax-object? e) + ;; s can't be valid source if we've unwrapped + (syntax-type (syntax-object-expression e) + r + (join-wraps w (syntax-object-wrap e)) + no-source rib)) + ((annotation? e) + (syntax-type (annotation-expression e) r w (annotation-source e) rib)) + ((self-evaluating? e) (values 'constant #f e w s)) + (else (values 'other #f e w s))))) + +(define chi-top-expr + (lambda (e r w top-ribcage) + (call-with-values + (lambda () (syntax-type e r w no-source top-ribcage)) + (lambda (type value e w s) + (chi-expr type value e r w s))))) + +(define chi-top + (lambda (e r w m esew top-ribcage) + (define-syntax eval-if-c&e + (syntax-rules () + ((_ m e) + (let ((x e)) + (if (eq? m 'c&e) (top-level-eval-hook x)) + x)))) + (call-with-values + (lambda () (syntax-type e r w no-source top-ribcage)) + (lambda (type value e w s) + (case type + ((begin-form) + (syntax-case e () + ((_) (chi-void)) + ((_ e1 e2 ...) + (chi-top-sequence (syntax (e1 e2 ...)) r w s m esew top-ribcage)))) + ((local-syntax-form) + (chi-local-syntax value e r w s + (lambda (body r w s) + (chi-top-sequence body r w s m esew top-ribcage)))) + ((eval-when-form) + (syntax-case e () + ((_ (x ...) e1 e2 ...) + (let ((when-list (chi-when-list e (syntax (x ...)) w)) + (body (syntax (e1 e2 ...)))) + (cond + ((eq? m 'e) + (if (memq 'eval when-list) + (chi-top-sequence body r w s 'e '(eval) top-ribcage) + (chi-void))) + ((memq 'load when-list) + (if (or (memq 'compile when-list) + (and (eq? m 'c&e) (memq 'eval when-list))) + (chi-top-sequence body r w s 'c&e '(compile load) top-ribcage) + (if (memq m '(c c&e)) + (chi-top-sequence body r w s 'c '(load) top-ribcage) + (chi-void)))) + ((or (memq 'compile when-list) + (and (eq? m 'c&e) (memq 'eval when-list))) + (top-level-eval-hook + (chi-top-sequence body r w s 'e '(eval) top-ribcage)) + (chi-void)) + (else (chi-void))))))) + ((define-syntax-form) + (parse-define-syntax e w s + (lambda (id rhs w) + (let ((id (wrap id w))) + (let ((n (id-var-name id empty-wrap))) + (let ((b (lookup n r))) + (case (binding-type b) + ((displaced-lexical) (displaced-lexical-error id))))) + (ct-eval/residualize m esew + (lambda () + (build-cte-install + (let ((sym (id-sym-name id))) + (if (only-top-marked? id) + sym + (let ((marks (wrap-marks (syntax-object-wrap id)))) + (make-syntax-object sym + (make-wrap marks + (list (make-ribcage (vector sym) + (vector marks) (vector (generate-id sym))))))))) + (chi rhs (transformer-env r) w)))))))) + ((define-form) + (parse-define e w s + (lambda (id rhs w) + (let ((id (wrap id w))) + (let ((n (id-var-name id empty-wrap))) + (let ((b (lookup n r))) + (case (binding-type b) + ((displaced-lexical) (displaced-lexical-error id))))) + (let ((sym (id-sym-name id))) + (let ((valsym (if (only-top-marked? id) sym (generate-id sym)))) + (build-sequence no-source + (list + (ct-eval/residualize m esew + (lambda () + (build-cte-install + (if (eq? sym valsym) + sym + (let ((marks (wrap-marks (syntax-object-wrap id)))) + (make-syntax-object sym + (make-wrap marks + (list (make-ribcage (vector sym) + (vector marks) (vector valsym))))))) + (build-data no-source (make-binding 'global valsym))))) + (eval-if-c&e m (build-global-definition s valsym (chi rhs r w)))))) + ))))) + ((module-form) + (let ((r (cons '("top-level module placeholder" . (placeholder)) r)) + (ribcage (make-empty-ribcage))) + (parse-module e w s (make-wrap (wrap-marks w) (cons ribcage (wrap-subst w))) + (lambda (id exports forms) + (if id + (begin + (let ((n (id-var-name id empty-wrap))) + (let ((b (lookup n r))) + (case (binding-type b) + ((displaced-lexical) (displaced-lexical-error (wrap id w)))))) + (chi-top-module e r ribcage w s m esew id exports forms)) + (chi-top-module e r ribcage w s m esew #f exports forms)))))) + ((import-form) + (parse-import e w s + (lambda (mid) + (ct-eval/residualize m esew + (lambda () + (when value (syntax-error (source-wrap e w s) "not valid at top-level")) + (let ((binding (lookup (id-var-name mid empty-wrap) null-env))) + (case (binding-type binding) + ((module) (do-top-import mid (interface-token (binding-value binding)))) + ((displaced-lexical) (displaced-lexical-error mid)) + (else (syntax-error mid "import from unknown module"))))))))) + (else (eval-if-c&e m (chi-expr type value e r w s)))))))) + +(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 exports token)) + +(define make-trimmed-interface + ; trim out implicit exports + (lambda (exports) + (make-interface + (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 (exports import-token) + (make-interface + (list->vector (map (lambda (x) (make-trimmed-syntax-object (if (pair? x) (car x) x))) exports)) + import-token))) + +(define-structure (module-binding type id label imps val)) + +(define chi-top-module + (lambda (e r ribcage w s m esew id exports forms) + (let ((fexports (flatten-exports exports))) + (chi-external ribcage (source-wrap e w s) + (map (lambda (d) (cons r d)) forms) r exports fexports m esew + (lambda (bindings inits) + ; dvs & des: "defined" (letrec-bound) vars & rhs expressions + ; svs & ses: "set!" (top-level) vars & rhs expressions + (let partition ((fexports fexports) (bs bindings) (svs '()) (ses '()) (ctdefs '())) + (if (null? fexports) + ; remaining bindings are either local vars or local macros/modules + (let partition ((bs bs) (dvs '()) (des '())) + (if (null? bs) + (let ((ses (map (lambda (x) (chi (cdr x) (car x) empty-wrap)) ses)) + (des (map (lambda (x) (chi (cdr x) (car x) empty-wrap)) des)) + (inits (map (lambda (x) (chi (cdr x) (car x) empty-wrap)) inits))) + ; we wait to do this here so that expansion of des & ses use + ; local versions, which in particular, allows us to use macros + ; locally even if esew tells us not to eval them + (for-each (lambda (x) + (apply (lambda (t label sym val) + (when label (set-indirect-label! label sym))) + x)) + ctdefs) + (build-sequence no-source + (list (ct-eval/residualize m esew + (lambda () + (if (null? ctdefs) + (chi-void) + (build-sequence no-source + (map (lambda (x) + (apply (lambda (t label sym val) + (build-cte-install sym + (if (eq? t 'define-syntax-form) + val + (build-data no-source + (make-binding 'module + (make-resolved-interface val sym)))))) + x)) + ctdefs))))) + (ct-eval/residualize m esew + (lambda () + (let ((n (if id (id-sym-name id) #f))) + (let* ((token (generate-id n)) + (b (build-data no-source + (make-binding 'module + (make-resolved-interface exports token))))) + (if n + (build-cte-install + (if (only-top-marked? id) + n + (let ((marks (wrap-marks (syntax-object-wrap id)))) + (make-syntax-object n + (make-wrap marks + (list (make-ribcage (vector n) + (vector marks) (vector (generate-id n)))))))) + b) + (let ((n (generate-id 'tmp))) + (build-sequence no-source + (list (build-cte-install n b) + (do-top-import n token))))))))) + ; Some systems complain when undefined variables are assigned. + (build-sequence no-source + (map (lambda (v) (build-global-definition no-source v (chi-void))) svs)) + (build-letrec no-source + dvs + des + (build-sequence no-source + (list + (if (null? svs) + (chi-void) + (build-sequence no-source + (map (lambda (v e) + (build-module-definition no-source v e)) + svs + ses))) + (if (null? inits) + (chi-void) + (build-sequence no-source inits))))) + (chi-void)))) + (let ((b (car bs))) + (case (module-binding-type b) + ((define-form) + (let ((var (gen-var (module-binding-id b)))) + (extend-store! r + (get-indirect-label (module-binding-label b)) + (make-binding 'lexical var)) + (partition (cdr bs) (cons var dvs) + (cons (module-binding-val b) des)))) + ((define-syntax-form module-form) (partition (cdr bs) dvs des)) + (else (error 'sc-expand-internal "unexpected module binding type")))))) + (let ((id (car fexports)) (fexports (cdr fexports))) + (define pluck-binding + (lambda (id bs succ fail) + (let loop ((bs bs) (new-bs '())) + (if (null? bs) + (fail) + (if (bound-id=? (module-binding-id (car bs)) id) + (succ (car bs) (smart-append (reverse new-bs) (cdr bs))) + (loop (cdr bs) (cons (car bs) new-bs))))))) + (pluck-binding id bs + (lambda (b bs) + (let ((t (module-binding-type b)) + (label (module-binding-label b)) + (imps (module-binding-imps b))) + (let ((fexports (append imps fexports)) + (sym (generate-id (id-sym-name id)))) + (case t + ((define-form) + (set-indirect-label! label sym) + (partition fexports bs (cons sym svs) + (cons (module-binding-val b) ses) + ctdefs)) + ((define-syntax-form) + (partition fexports bs svs ses + (cons (list t label sym (module-binding-val b)) ctdefs))) + ((module-form) + (let ((exports (module-binding-val b))) + (partition (append (flatten-exports exports) fexports) bs + svs ses + (cons (list t label sym exports) ctdefs)))) + (else (error 'sc-expand-internal "unexpected module binding type")))))) + (lambda () (partition fexports bs svs ses ctdefs))))))))))) + +(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 extend-store! + (lambda (r label binding) + (set-cdr! r (extend-env label binding (cdr r))))) + +(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 (interface? x) + (let ((token (interface-token x))) + (if token + (lookup-import-binding-name (id-sym-name e) token (wrap-marks (syntax-object-wrap e))) + (let ((v (interface-exports x))) + (let lp ((i (fx- (vector-length v) 1))) + (and (fx>= i 0) + (or (bound-id=? e (vector-ref v i)) + (lp (fx- i 1)))))))) + (bound-id=? e x))) + ids))) + (let loop ((fexports fexports) (missing '())) + (if (null? fexports) + (unless (null? missing) (syntax-error missing "missing definition for export(s)")) + (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 b-i=? + ; cope with fat-fingered top-level + (lambda (x y) + (if (symbol? x) + (if (symbol? y) + (eq? x y) + (and (eq? x (id-sym-name y)) + (same-marks? (wrap-marks (syntax-object-wrap y)) (wrap-marks top-wrap)))) + (if (symbol? y) + (and (eq? y (id-sym-name x)) + (same-marks? (wrap-marks (syntax-object-wrap x)) (wrap-marks top-wrap))) + (bound-id=? x y))))) + (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 (interface? x) + (if (interface? y) + (call-with-values + (lambda () + (let ((xe (interface-exports x)) (ye (interface-exports y))) + (if (fx> (vector-length xe) (vector-length ye)) + (values x ye) + (values y xe)))) + (lambda (iface exports) + (vfold exports (lambda (id cls) (id-iface-conflicts id iface cls)) cls))) + (id-iface-conflicts y x cls)) + (if (interface? y) + (id-iface-conflicts x y cls) + (if (b-i=? x y) (cons x cls) cls))))) + (define id-iface-conflicts + (lambda (id iface cls) + (let ((token (interface-token iface))) + (if token + (if (lookup-import-binding-name (id-sym-name id) token + (if (symbol? id) + (wrap-marks top-wrap) + (wrap-marks (syntax-object-wrap id)))) + (cons id cls) + cls) + (vfold (interface-exports iface) + (lambda (*id cls) (if (b-i=? *id id) (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 exports fexports m esew k) + (define return + (lambda (bindings ids inits) + (check-defined-ids source-exp ids) + (check-module-exports source-exp fexports ids) + (k 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 + (make-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) (ids '()) (bindings '()) (inits '())) + (if (null? body) + (return bindings ids inits) + (let ((e (cdar body)) (er (caar body))) + (call-with-values + (lambda () (syntax-type e er empty-wrap no-source ribcage)) + (lambda (type value e w s) + (case type + ((define-form) + (parse-define e w s + (lambda (id rhs w) + (let* ((id (wrap id w)) + (label (gen-indirect-label)) + (imps (get-implicit-exports id))) + (extend-ribcage! ribcage id label) + (parse + (cdr body) + (cons id ids) + (cons (make-module-binding type id label + imps (cons er (wrap rhs w))) + bindings) + inits))))) + ((define-syntax-form) + (parse-define-syntax e w s + (lambda (id rhs w) + (let* ((id (wrap id w)) + (label (gen-indirect-label)) + (imps (get-implicit-exports id)) + (exp (chi rhs (transformer-env er) w))) + ; arrange to evaluate the transformer lazily + (extend-store! r (get-indirect-label label) (cons 'deferred exp)) + (extend-ribcage! ribcage id label) + (parse + (cdr body) + (cons id ids) + (cons (make-module-binding type id label imps exp) + bindings) + inits))))) + ((module-form) + (let* ((*ribcage (make-empty-ribcage)) + (*w (make-wrap (wrap-marks w) (cons *ribcage (wrap-subst w))))) + (parse-module e w s *w + (lambda (id *exports forms) + (chi-external *ribcage (source-wrap e w s) + (map (lambda (d) (cons er d)) forms) + r *exports (flatten-exports *exports) m esew + (lambda (*bindings *inits) + (let* ((iface (make-trimmed-interface *exports)) + (bindings (append (if id *bindings (update-imp-exports *bindings *exports)) bindings)) + (inits (append inits *inits))) + (if id + (let ((label (gen-indirect-label)) + (imps (get-implicit-exports id))) + (extend-store! r (get-indirect-label label) + (make-binding 'module iface)) + (extend-ribcage! ribcage id label) + (parse + (cdr body) + (cons id ids) + (cons (make-module-binding type id label imps *exports) bindings) + inits)) + (let () + (do-import! iface ribcage) + (parse (cdr body) (cons iface ids) bindings inits)))))))))) + ((import-form) + (parse-import e w s + (lambda (mid) + (let ((mlabel (id-var-name mid empty-wrap))) + (let ((binding (lookup mlabel r))) + (case (binding-type binding) + ((module) + (let ((iface (binding-value binding))) + (when value (extend-ribcage-barrier! ribcage value)) + (do-import! iface ribcage) + (parse + (cdr body) + (cons iface ids) + (update-imp-exports bindings (vector->list (interface-exports iface))) + inits))) + ((displaced-lexical) (displaced-lexical-error mid)) + (else (syntax-error mid "import from unknown module")))))))) + ((begin-form) + (syntax-case e () + ((_ e1 ...) + (parse (let f ((forms (syntax (e1 ...)))) + (if (null? forms) + (cdr body) + (cons (cons er (wrap (car forms) w)) + (f (cdr forms))))) + ids bindings inits)))) + ((local-syntax-form) + (chi-local-syntax value e er w s + (lambda (forms er w s) + (parse (let f ((forms forms)) + (if (null? forms) + (cdr body) + (cons (cons er (wrap (car forms) w)) + (f (cdr forms))))) + ids bindings inits)))) + (else ; found an init expression + (return bindings ids + (append inits (cons (cons er (source-wrap e w s)) (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 (mid token) + (build-cte-install mid + (build-data no-source + (make-binding 'do-import token))))) + +(define ct-eval/residualize + (lambda (m esew thunk) + (case m + ((c) (if (memq 'compile esew) + (let ((e (thunk))) + (top-level-eval-hook e) + (if (memq 'load esew) e (chi-void))) + (if (memq 'load esew) (thunk) (chi-void)))) + ((c&e) (let ((e (thunk))) (top-level-eval-hook e) e)) + (else (if (memq 'eval esew) (top-level-eval-hook (thunk))) (chi-void))))) + +(define chi + (lambda (e r w) + (call-with-values + (lambda () (syntax-type e r w no-source #f)) + (lambda (type value e w s) + (chi-expr type value e r w s))))) + +(define chi-expr + (lambda (type value e r w s) + (case type + ((lexical) + (build-lexical-reference 'value s value)) + ((core) (value e r w s)) + ((lexical-call) + (chi-application + (build-lexical-reference 'fun (source-annotation (car e)) value) + e r w s)) + ((constant) (build-data s (strip (source-wrap e w s) empty-wrap))) + ((global) (build-global-reference s value)) + ((call) (chi-application (chi (car e) r w) e r w s)) + ((begin-form) + (syntax-case e () + ((_ e1 e2 ...) (chi-sequence (syntax (e1 e2 ...)) r w s)))) + ((local-syntax-form) + (chi-local-syntax value e r w s chi-sequence)) + ((eval-when-form) + (syntax-case e () + ((_ (x ...) e1 e2 ...) + (let ((when-list (chi-when-list e (syntax (x ...)) w))) + (if (memq 'eval when-list) + (chi-sequence (syntax (e1 e2 ...)) r w s) + (chi-void)))))) + ((define-form define-syntax-form module-form import-form) + (syntax-error (source-wrap e w s) "invalid context for definition")) + ((syntax) + (syntax-error (source-wrap e w s) + "reference to pattern variable outside syntax form")) + ((displaced-lexical) (displaced-lexical-error (source-wrap e w s))) + (else (syntax-error (source-wrap e w s)))))) + +(define chi-application + (lambda (x e r w s) + (syntax-case e () + ((e0 e1 ...) + (build-application s x + (map (lambda (e) (chi e r w)) (syntax (e1 ...))))) + (_ (syntax-error (source-wrap e w s)))))) + +(define chi-set! + (lambda (e r w s 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 s rib) r empty-wrap s rib))) + (else + (values 'core + (lambda (e r w s) + ; 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 w)) + (n (id-var-name (syntax id) w))) + (let ((b (lookup n r))) + (case (binding-type b) + ((lexical) (build-lexical-assignment s (binding-value b) val)) + ((global) (build-global-assignment s (binding-value b) val)) + ((displaced-lexical) + (syntax-error (wrap (syntax id) w) "identifier out of context")) + (else (syntax-error (source-wrap e w s))))))) + e w s)))))) + (_ (syntax-error (source-wrap e w s)))))) + +(define chi-macro + (lambda (p e r w s 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) + (if rib (cons rib (cdr s)) (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 s) + "encountered raw symbol " + (format "~s" x) + " in output of macro")) + (else x)))) + (rebuild-macro-output + (let ((out (p (source-wrap e (anti-mark w) s)))) + (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 + ;; Here we create the empty wrap and new environment with placeholder + ;; as required by chi-internal. On return we extend the environment + ;; to recognize the var-labels as lexical variables and build a letrec + ;; binding them to the var-vals which we expand here. + (lambda (body outer-form r w) + (let* ((r (cons '("placeholder" . (placeholder)) r)) + (ribcage (make-empty-ribcage)) + (w (make-wrap (wrap-marks w) (cons ribcage (wrap-subst w)))) + (body (map (lambda (x) (cons r (wrap x w))) body))) + (chi-internal ribcage outer-form body r + (lambda (exprs ids vars vals inits) + (when (null? exprs) (syntax-error outer-form "no expressions in body")) + (build-letrec no-source + vars + (map (lambda (x) (chi (cdr x) (car x) empty-wrap)) vals) + (build-sequence no-source + (map (lambda (x) (chi (cdr x) (car x) empty-wrap)) (append inits exprs))))))))) + +(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). + ;; + ;; Before processing the body, we also create a new environment + ;; containing a placeholder for the bindings we will add later and + ;; associate this environment with each form. In processing a + ;; let-syntax or letrec-syntax, the associated environment may be + ;; augmented with local keyword bindings, so the environment may + ;; be different for different forms in the body. Once we have + ;; gathered up all of the definitions, we evaluate the transformer + ;; expressions and splice into r at the placeholder the new variable + ;; and keyword bindings. This allows let-syntax or letrec-syntax + ;; forms local to a portion or all of the body to shadow the + ;; definition bindings. + ;; + ;; 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 k) + (define return + (lambda (exprs ids vars vals inits) + (check-defined-ids source-exp ids) + (k exprs ids vars vals inits))) + (let parse ((body body) (ids '()) (vars '()) (vals '()) (inits '())) + (if (null? body) + (return body ids vars vals inits) + (let ((e (cdar body)) (er (caar body))) + (call-with-values + (lambda () (syntax-type e er empty-wrap no-source ribcage)) + (lambda (type value e w s) + (case type + ((define-form) + (parse-define e w s + (lambda (id rhs w) + (let ((id (wrap id w)) (label (gen-label))) + (let ((var (gen-var id))) + (extend-ribcage! ribcage id label) + (extend-store! r label (make-binding 'lexical var)) + (parse + (cdr body) + (cons id ids) + (cons var vars) + (cons (cons er (wrap rhs w)) vals) + inits)))))) + ((define-syntax-form) + (parse-define-syntax e w s + (lambda (id rhs w) + (let ((id (wrap id w)) + (label (gen-label)) + (exp (chi rhs (transformer-env er) w))) + (extend-ribcage! ribcage id label) + (extend-store! r label (make-binding 'deferred exp)) + (parse (cdr body) (cons id ids) vars vals inits))))) + ((module-form) + (let* ((*ribcage (make-empty-ribcage)) + (*w (make-wrap (wrap-marks w) (cons *ribcage (wrap-subst w))))) + (parse-module e w s *w + (lambda (id exports forms) + (chi-internal *ribcage (source-wrap e w s) + (map (lambda (d) (cons er d)) forms) r + (lambda (*body *ids *vars *vals *inits) + ; valid bound ids checked already by chi-internal + (check-module-exports source-exp (flatten-exports exports) *ids) + (let ((iface (make-trimmed-interface exports)) + (vars (append *vars vars)) + (vals (append *vals vals)) + (inits (append inits *inits *body))) + (if id + (let ((label (gen-label))) + (extend-ribcage! ribcage id label) + (extend-store! r label (make-binding 'module iface)) + (parse (cdr body) (cons id ids) vars vals inits)) + (let () + (do-import! iface ribcage) + (parse (cdr body) (cons iface ids) vars vals inits)))))))))) + ((import-form) + (parse-import e w s + (lambda (mid) + (let ((mlabel (id-var-name mid empty-wrap))) + (let ((binding (lookup mlabel r))) + (case (car binding) + ((module) + (let ((iface (cdr binding))) + (when value (extend-ribcage-barrier! ribcage value)) + (do-import! iface ribcage) + (parse (cdr body) (cons iface ids) vars vals inits))) + ((displaced-lexical) (displaced-lexical-error mid)) + (else (syntax-error mid "import from unknown module")))))))) + ((begin-form) + (syntax-case e () + ((_ e1 ...) + (parse (let f ((forms (syntax (e1 ...)))) + (if (null? forms) + (cdr body) + (cons (cons er (wrap (car forms) w)) + (f (cdr forms))))) + ids vars vals inits)))) + ((local-syntax-form) + (chi-local-syntax value e er w s + (lambda (forms er w s) + (parse (let f ((forms forms)) + (if (null? forms) + (cdr body) + (cons (cons er (wrap (car forms) w)) + (f (cdr forms))))) + ids vars vals inits)))) + (else ; found a non-definition + (return (cons (cons er (source-wrap e w s)) (cdr body)) + ids vars vals inits)))))))))) + +(define do-import! + (lambda (interface ribcage) + (let ((token (interface-token interface))) + (if token + (extend-ribcage-subst! ribcage token) + (vfor-each + (lambda (id) + (let ((label1 (id-var-name-loc id empty-wrap))) + (unless label1 + (syntax-error id "exported identifier not visible")) + (extend-ribcage! ribcage id label1))) + (interface-exports interface)))))) + +(define parse-module + (lambda (e w s *w k) + (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 s) + "invalid exports list in")))) + (listify (cdr exports)))))) + (define return + (lambda (id exports forms) + (k id (listify exports) (map (lambda (x) (wrap x *w)) forms)))) + (syntax-case e () + ((_ (ex ...) form ...) + (return #f (syntax (ex ...)) (syntax (form ...)))) + ((_ 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 + (return (wrap (syntax mid) w) (syntax (ex ...)) (syntax (form ...)))) + (_ (syntax-error (source-wrap e w s)))))) + +(define parse-import + (lambda (e w s k) + (syntax-case e () + ((_ mid) + (id? (syntax mid)) + (k (wrap (syntax mid) w))) + (_ (syntax-error (source-wrap e w s)))))) + +(define parse-define + (lambda (e w s k) + (syntax-case e () + ((_ name val) + (id? (syntax name)) + (k (syntax name) (syntax val) w)) + ((_ (name . args) e1 e2 ...) + (and (id? (syntax name)) + (valid-bound-ids? (lambda-var-list (syntax args)))) + (k (wrap (syntax name) w) + (cons (syntax lambda) (wrap (syntax (args e1 e2 ...)) w)) + empty-wrap)) + ((_ name) + (id? (syntax name)) + (k (wrap (syntax name) w) (syntax (void)) empty-wrap)) + (_ (syntax-error (source-wrap e w s)))))) + +(define parse-define-syntax + (lambda (e w s k) + (syntax-case e () + ((_ name val) + (id? (syntax name)) + (k (syntax name) (syntax val) w)) + (_ (syntax-error (source-wrap e w s)))))) + +(define chi-lambda-clause + (lambda (e c r w k) + (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))) + (k new-vars + (chi-body (syntax (e1 e2 ...)) + e + (extend-var-env* labels new-vars r) + (make-binding-wrap ids labels w))))))) + ((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))) + (k (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) + (make-binding-wrap old-ids labels w))))))) + (_ (syntax-error e))))) + +(define chi-local-syntax + (lambda (rec? e r w s k) + (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 s) + "keyword") + (let ((labels (gen-labels ids))) + (let ((new-w (make-binding-wrap ids labels w))) + (k (syntax (e1 e2 ...)) + (extend-env* + labels + (let ((w (if rec? new-w w)) + (trans-r (transformer-env r))) + (map (lambda (x) (make-binding 'deferred (chi x trans-r w))) (syntax (val ...)))) + r) + new-w + s)))))) + (_ (syntax-error (source-wrap e w s)))))) + +(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 parent) + (cond + ((pair? x) + (let ((new (cons #f #f))) + (when parent (set-annotation-stripped! parent new)) + (set-car! new (strip-annotation (car x) #f)) + (set-cdr! new (strip-annotation (cdr x) #f)) + new)) + ((annotation? x) + (or (annotation-stripped x) + (strip-annotation (annotation-expression x) x))) + ((vector? x) + (let ((new (make-vector (vector-length x)))) + (when parent (set-annotation-stripped! parent new)) + (let loop ((i (- (vector-length x) 1))) + (unless (fx< i 0) + (vector-set! new i (strip-annotation (vector-ref x i) #f)) + (loop (fx- i 1)))) + new)) + (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 #f) + 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 (annotation-source id) (annotation-expression id)) + (build-lexical-var no-source 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) + (define put-token + (lambda (id token) + (define cons-id + (lambda (id x) + (if (not x) id (cons id x)))) + (define weed + (lambda (id x) + (if (pair? x) + (if (bound-id=? (car x) id) ; could just check same-marks + (weed id (cdr x)) + (cons-id (car x) (weed id (cdr x)))) + (if (or (not x) (bound-id=? x id)) + #f + x)))) + (let ((sym (id-sym-name id))) + (let ((x (weed id (getprop sym token)))) + (if (and (not x) (symbol? id)) + ; don't pollute property list when all we have is a plain + ; top-level binding, since that's what's assumed anyway + (remprop sym token) + (putprop sym token (cons-id id x))))))) + (define sc-put-module + (lambda (exports token) + (vfor-each + (lambda (id) (put-token id token)) + exports))) + (define (put-cte id binding) + ;; making assumption here that all macros should be visible to the user and that system + ;; globals don't come through here (primvars.ss sets up their properties) + (let ((sym (if (symbol? id) id (id-var-name id empty-wrap)))) + (putprop sym '*sc-expander* binding))) + (let ((binding (or (sanitize-binding b) (error 'define-syntax "invalid transformer ~s" b)))) + (case (binding-type binding) + ((module) + (let ((iface (binding-value binding))) + (sc-put-module (interface-exports iface) (interface-token iface))) + (put-cte id binding)) + ((do-import) ; fake binding: id is module id, binding-value is import 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))) + (unless (eq? (interface-token iface) token) + (syntax-error id "import mismatch for module")) + (sc-put-module (interface-exports iface) '*top*))) + (else (syntax-error id "import from unknown module")))))) + (else (put-cte id binding)))))) + + +;;; 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 w s) + (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) + (chi-body + (syntax (e1 e2 ...)) + (source-wrap e w s) + (extend-env* + names + (let ((trans-r (transformer-env r))) + (map (lambda (x) (make-binding 'deferred (chi x trans-r w))) (syntax (val ...)))) + r) + w))) + (_ (syntax-error (source-wrap e w s)))))) + +(global-extend 'core 'quote + (lambda (e r w s) + (syntax-case e () + ((_ e) (build-data s (strip (syntax e) w))) + (_ (syntax-error (source-wrap e w s)))))) + +(global-extend 'core 'syntax + (let () + (define gen-syntax + (lambda (src e r maps ellipsis?) + (if (id? e) + (let ((label (id-var-name e empty-wrap))) + (let ((b (lookup label r))) + (if (eq? (binding-type b) 'syntax) + (call-with-values + (lambda () + (let ((var.lev (binding-value b))) + (gen-ref src (car var.lev) (cdr var.lev) maps))) + (lambda (var 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)) + (gen-syntax src (syntax e) r maps (lambda (x) #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) + (call-with-values + (lambda () + (gen-syntax src (syntax x) r + (cons '() maps) ellipsis?)) + (lambda (x maps) + (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) + (call-with-values + (lambda () (k (cons '() maps))) + (lambda (x maps) + (if (null? (car maps)) + (syntax-error src + "extra ellipsis in syntax form") + (values (gen-mappend x (car maps)) + (cdr maps)))))))) + (_ (call-with-values + (lambda () (gen-syntax src y r maps ellipsis?)) + (lambda (y maps) + (call-with-values + (lambda () (k maps)) + (lambda (x maps) + (values (gen-append x y) maps))))))))) + ((x . y) + (call-with-values + (lambda () (gen-syntax src (syntax x) r maps ellipsis?)) + (lambda (x maps) + (call-with-values + (lambda () (gen-syntax src (syntax y) r maps ellipsis?)) + (lambda (y maps) (values (gen-cons x y) maps)))))) + (#(e1 e2 ...) + (call-with-values + (lambda () + (gen-syntax src (syntax (e1 e2 ...)) r maps ellipsis?)) + (lambda (e maps) (values (gen-vector e) 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") + (call-with-values + (lambda () (gen-ref src var (fx- level 1) (cdr maps))) + (lambda (outer-var outer-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-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)))))) + + (define gen-cons + (lambda (x y) + (case (car y) + ((quote) + (if (eq? (car x) 'quote) + `(quote (,(cadr x) . ,(cadr y))) + (if (eq? (cadr y) '()) + `(list ,x) + `(cons ,x ,y)))) + ((list) `(list ,x ,@(cdr y))) + (else `(cons ,x ,y))))) + + (define gen-append + (lambda (x y) + (if (equal? y '(quote ())) + x + `(append ,x ,y)))) + + (define gen-vector + (lambda (x) + (cond + ((eq? (car x) 'list) `(vector ,@(cdr x))) + ((eq? (car x) 'quote) `(quote #(,@(cadr x)))) + (else `(list->vector ,x))))) + + + (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 w s) + (let ((e (source-wrap e w s))) + (syntax-case e () + ((_ x) + (call-with-values + (lambda () (gen-syntax e (syntax x) r '() ellipsis?)) + (lambda (e maps) (regen e)))) + (_ (syntax-error e))))))) + + +(global-extend 'core 'lambda + (lambda (e r w s) + (syntax-case e () + ((_ . c) + (chi-lambda-clause (source-wrap e w s) (syntax c) r w + (lambda (vars body) (build-lambda s vars body))))))) + + +(global-extend 'core 'letrec + (lambda (e r w s) + (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 s) "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 s + new-vars + (map (lambda (x) (chi x r w)) (syntax (val ...))) + (chi-body (syntax (e1 e2 ...)) (source-wrap e w s) r w))))))) + (_ (syntax-error (source-wrap e w s)))))) + +(global-extend 'core 'if + (lambda (e r w s) + (syntax-case e () + ((_ test then) + (build-conditional s + (chi (syntax test) r w) + (chi (syntax then) r w) + (chi-void))) + ((_ test then else) + (build-conditional s + (chi (syntax test) r w) + (chi (syntax then) r w) + (chi (syntax else) r w))) + (_ (syntax-error (source-wrap e w s)))))) + + + +(global-extend 'set! 'set! '()) + +(global-extend 'begin 'begin '()) + +(global-extend 'module-key 'module '()) +(global-extend 'import 'import #f) +(global-extend 'import 'import-only #t) + +(global-extend 'define 'define '()) + +(global-extend 'define-syntax 'define-syntax '()) + +(global-extend 'eval-when 'eval-when '()) + +(global-extend 'core 'syntax-case + (let () + (define convert-pattern + ; accepts pattern & keys + ; returns syntax-dispatch pattern & ids + (lambda (pattern keys) + (let cvt ((p pattern) (n 0) (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)) + (call-with-values + (lambda () (cvt (syntax x) (fx+ n 1) ids)) + (lambda (p ids) + (values (if (eq? p 'any) 'each-any (vector 'each p)) + ids)))) + ((x . y) + (call-with-values + (lambda () (cvt (syntax y) n ids)) + (lambda (y ids) + (call-with-values + (lambda () (cvt (syntax x) n ids)) + (lambda (x ids) + (values (cons x y) ids)))))) + (() (values '() ids)) + (#(x ...) + (call-with-values + (lambda () (cvt (syntax (x ...)) n ids)) + (lambda (p ids) (values (vector 'vector p) ids)))) + (x (values (vector 'atom (strip p empty-wrap)) ids))))))) + + (define build-dispatch-call + (lambda (pvars exp y r) + (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) + (make-binding-wrap ids labels empty-wrap))) + y)))))) + + (define gen-clause + (lambda (x keys clauses r pat fender exp) + (call-with-values + (lambda () (convert-pattern pat keys)) + (lambda (p pvars) + (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) + (build-data no-source #f)))) + (build-dispatch-call pvars exp y r) + (gen-syntax-case x keys clauses r)))) + (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) + (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) + (make-binding-wrap (syntax (pat)) + (list label) empty-wrap))) + (list (build-lexical-reference 'value no-source x)))) + (gen-clause x keys (cdr clauses) r + (syntax pat) #t (syntax exp)))) + ((pat fender exp) + (gen-clause x keys (cdr clauses) r + (syntax pat) (syntax fender) (syntax exp))) + (_ (syntax-error (car clauses) "invalid syntax-case clause")))))) + + (lambda (e r w s) + (let ((e (source-wrap e w s))) + (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 s + (build-lambda no-source (list x) + (gen-syntax-case x + (syntax (key ...)) (syntax (m ...)) + r)) + (list (chi (syntax val) r empty-wrap)))) + (syntax-error e "invalid literals list in")))))))) + +;;; The portable sc-expand seeds chi-top's mode m with 'e (for +;;; evaluating) and esew (which stands for "eval syntax expanders +;;; when") with '(eval). In Chez Scheme, m is set to 'c instead of e +;;; if we are compiling a file, and esew is set to +;;; (eval-syntactic-expanders-when), which defaults to the list +;;; '(compile load eval). This means that, by default, 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 ((m 'e) (esew '(eval)) + (user-ribcage + (let ((ribcage (make-empty-ribcage))) + (extend-ribcage-subst! ribcage '*top*) + ribcage))) + (let ((user-top-wrap + (make-wrap (wrap-marks top-wrap) + (cons user-ribcage (wrap-subst top-wrap))))) + (lambda (x) + (if (and (pair? x) (equal? (car x) noexpand)) + (cadr x) + (chi-top x null-env user-top-wrap m esew user-ribcage)))))) + +(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-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 + (lambda (ls) + (arg-check list? ls 'generate-temporaries) + (map (lambda (x) (wrap (gensym-hook) top-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! 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: + +;;; pattern: matches: +;;; () empty list +;;; any anything +;;; (<pattern>1 . <pattern>2) (<pattern>1 . <pattern>2) +;;; each-any (any*) +;;; #(free-id <key>) <key> with free-identifier=? +;;; #(each <pattern>) (<pattern>*) +;;; #(vector <pattern>) (list->vector <pattern>) +;;; #(atom <object>) <object> with "equal?" + +;;; Vector cops out to pair under assumption that vectors are rare. If +;;; not, should convert to: +;;; #(vector <pattern>*) #(<pattern>*) + +(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-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)) + ((free-id atom) r) + ((vector) (match-empty (vector-ref p 1) 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 ((l (match-each e (vector-ref p 1) w))) + (and l + (let collect ((l l)) + (if (null? (car l)) + r + (cons (map car l) (collect (map cdr l))))))))) + ((free-id) (and (id? e) (literal-id=? (wrap e w) (vector-ref p 1)) 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 syntax-rules + (lambda (x) + (syntax-case x () + ((_ (k ...) ((keyword . pattern) template) ...) + (syntax (lambda (x) + (syntax-case x (k ...) + ((dummy . pattern) (syntax template)) + ...))))))) + +(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 doloop ((var init) ...) + (if (not e0) + (begin c ... (doloop step ...)))))) + ((e1 e2 ...) + (syntax (let doloop ((var init) ...) + (if e0 + (begin e1 e2 ...) + (begin c ... (doloop step ...)))))))))))) + +(define-syntax quasiquote + (letrec + ; these are here because syntax-case uses literal-identifier=?, + ; and we want the more precise free-identifier=? + ((isquote? (lambda (x) + (and (identifier? x) + (free-identifier=? x (syntax quote))))) + (islist? (lambda (x) + (and (identifier? x) + (free-identifier=? x (syntax list))))) + (iscons? (lambda (x) + (and (identifier? x) + (free-identifier=? x (syntax cons))))) + (quote-nil? (lambda (x) + (syntax-case x () + ((quote? ()) (isquote? (syntax quote?))) + (_ #f)))) + (quasilist* + (lambda (x y) + (let f ((x x)) + (if (null? x) + y + (quasicons (car x) (f (cdr x))))))) + (quasicons + (lambda (x y) + (with-syntax ((x x) (y y)) + (syntax-case (syntax y) () + ((quote? dy) + (isquote? (syntax quote?)) + (syntax-case (syntax x) () + ((quote? dx) + (isquote? (syntax quote?)) + (syntax (quote (dx . dy)))) + (_ (if (null? (syntax dy)) + (syntax (list x)) + (syntax (cons x y)))))) + ((listp . stuff) + (islist? (syntax listp)) + (syntax (list x . stuff))) + (else (syntax (cons x y))))))) + (quasiappend + (lambda (x y) + (let ((ls (let f ((x x)) + (if (null? x) + (if (quote-nil? y) + '() + (list y)) + (if (quote-nil? (car x)) + (f (cdr x)) + (cons (car x) (f (cdr x)))))))) + (cond + ((null? ls) (syntax (quote ()))) + ((null? (cdr ls)) (car ls)) + (else (with-syntax (((p ...) ls)) + (syntax (append p ...)))))))) + (quasivector + (lambda (x) + (with-syntax ((pat-x x)) + (syntax-case (syntax pat-x) () + ((quote? (x ...)) + (isquote? (syntax quote?)) + (syntax (quote #(x ...)))) + (_ (let f ((x x) (k (lambda (ls) `(,(syntax vector) ,@ls)))) + (syntax-case x () + ((quote? (x ...)) + (isquote? (syntax quote?)) + (k (syntax ((quote x) ...)))) + ((listp x ...) + (islist? (syntax listp)) + (k (syntax (x ...)))) + ((cons? x y) + (iscons? (syntax cons?)) + (f (syntax y) (lambda (ls) (k (cons (syntax x) ls))))) + (else + (syntax (list->vector pat-x)))))))))) + (quasi + (lambda (p lev) + (syntax-case p (unquote unquote-splicing quasiquote) + ((unquote p) + (if (= lev 0) + (syntax p) + (quasicons (syntax (quote unquote)) + (quasi (syntax (p)) (- lev 1))))) + (((unquote p ...) . q) + (if (= lev 0) + (quasilist* (syntax (p ...)) (quasi (syntax q) lev)) + (quasicons (quasicons (syntax (quote unquote)) + (quasi (syntax (p ...)) (- lev 1))) + (quasi (syntax q) lev)))) + (((unquote-splicing p ...) . q) + (if (= lev 0) + (quasiappend (syntax (p ...)) (quasi (syntax q) lev)) + (quasicons (quasicons (syntax (quote unquote-splicing)) + (quasi (syntax (p ...)) (- lev 1))) + (quasi (syntax q) lev)))) + ((quasiquote p) + (quasicons (syntax (quote quasiquote)) + (quasi (syntax (p)) (+ lev 1)))) + ((p . q) + (quasicons (quasi (syntax p) lev) (quasi (syntax q) lev))) + (#(x ...) (quasivector (quasi (syntax (x ...)) lev))) + (p (syntax (quote p))))))) + (lambda (x) + (syntax-case x () + ((_ e) (quasi (syntax e) 0)))))) + +(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 unquote + (lambda (x) + (syntax-case x () + ((_ e ...) + (syntax-error x + "expression not valid outside of quasiquote"))))) + +(define-syntax unquote-splicing + (lambda (x) + (syntax-case x () + ((_ e ...) + (syntax-error x + "expression not valid outside of quasiquote"))))) + +(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 + (lambda (x) + (syntax-case x (set!) + ((_ e) + (syntax + (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))) + (syntax + (cons 'macro! + (lambda (x) + (syntax-case x (set!) + ((set! var val) (syntax exp2)) + ((id x (... ...)) (syntax (exp1 x (... ...)))) + (id (identifier? (syntax id)) (syntax exp1)))))))))) + |