shithub: femtolisp

ref: 36b24a92d94b1d1f100746d27d13fc77f1599201
dir: /lib/psyntax.ss/

View raw version
;;; Portable implementation of syntax-case
;;; Extracted from Chez Scheme Version 7.3 (Feb 26, 2007)
;;; Authors: R. Kent Dybvig, Oscar Waddell, Bob Hieb, Carl Bruggeman

;;; Copyright (c) 1992-2002 Cadence Research Systems
;;; Permission to copy this software, in whole or in part, to use this
;;; software for any lawful purpose, and to redistribute this software
;;; is granted subject to the restriction that all copies made of this
;;; software must include this copyright notice in full.  This software
;;; is provided AS IS, with NO WARRANTY, EITHER EXPRESS OR IMPLIED,
;;; INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY
;;; OR FITNESS FOR ANY PARTICULAR PURPOSE.  IN NO EVENT SHALL THE
;;; AUTHORS BE LIABLE FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES OF ANY
;;; NATURE WHATSOEVER.

;;; Before attempting to port this code to a new implementation of
;;; Scheme, please read the notes below carefully.

;;; This file defines the syntax-case expander, sc-expand, and a set
;;; of associated syntactic forms and procedures.  Of these, the
;;; following are documented in The Scheme Programming Language,
;;; Third Edition (R. Kent Dybvig, MIT Press, 2003), which can be
;;; found online at http://www.scheme.com/tspl3/.  Most are also documented
;;; in the R4RS and draft R5RS.
;;;
;;;   bound-identifier=?
;;;   datum->syntax-object
;;;   define-syntax
;;;   fluid-let-syntax
;;;   free-identifier=?
;;;   generate-temporaries
;;;   identifier?
;;;   identifier-syntax
;;;   let-syntax
;;;   letrec-syntax
;;;   syntax
;;;   syntax-case
;;;   syntax-object->datum
;;;   syntax-rules
;;;   with-syntax
;;;
;;; All standard Scheme syntactic forms are supported by the expander
;;; or syntactic abstractions defined in this file.  Only the R4RS
;;; delay is omitted, since its expansion is implementation-dependent.

;;; Also defined are three forms that support modules: module, import,
;;; and import-only.  These are documented in the Chez Scheme User's
;;; Guide (R. Kent Dybvig, Cadence Research Systems, 1998), which can
;;; also be found online at http://www.scheme.com/csug/.  They are
;;; described briefly here as well.

;;; All are definitions and may appear where and only where other
;;; definitions may appear.  modules may be named:
;;;
;;;   (module id (ex ...) defn ... init ...)
;;;
;;; or anonymous:
;;;
;;;   (module (ex ...) defn ... init ...)
;;;
;;; The latter form is semantically equivalent to:
;;;
;;;   (module T (ex ...) defn ... init ...)
;;;   (import T)
;;;
;;; where T is a fresh identifier.
;;;
;;; In either form, each of the exports in (ex ...) is either an
;;; identifier or of the form (id ex ...).  In the former case, the
;;; single identifier ex is exported.  In the latter, the identifier
;;; id is exported and the exports ex ... are "implicitly" exported.
;;; This listing of implicit exports is useful only when id is a
;;; keyword bound to a transformer that expands into references to
;;; the listed implicit exports.  In the present implementation,
;;; listing of implicit exports is necessary only for top-level
;;; modules and allows the implementation to avoid placing all
;;; identifiers into the top-level environment where subsequent passes
;;; of the compiler will be unable to deal effectively with them.
;;;
;;; Named modules may be referenced in import statements, which
;;; always take one of the forms:
;;;
;;;   (import id)
;;;   (import-only id)
;;;
;;; id must name a module.  Each exported identifier becomes visible
;;; within the scope of the import form.  In the case of import-only,
;;; all other identifiers become invisible in the scope of the
;;; import-only form, except for those established by definitions
;;; that appear textually after the import-only form.

;;; import and import-only also support a variety of identifier
;;; selection and renaming forms: only, except, add-prefix,
;;; drop-prefix, rename, and alias.
;;;
;;;   (import (only m x y))
;;;
;;; imports x and y (and nothing else) from m.
;;;
;;;   (import (except m x y))
;;;
;;; imports all of m's imports except for x and y.
;;;
;;;   (import (add-prefix (only m x y) m:))
;;;
;;; imports x and y as m:x and m:y.
;;;
;;;   (import (drop-prefix m foo:))
;;;
;;; imports all of m's imports, dropping the common foo: prefix
;;; (which must appear on all of m's exports).
;;;
;;;   (import (rename (except m a b) (m-c c) (m-d d)))
;;;
;;; imports all of m's imports except for x and y, renaming c
;;; m-c and d m-d.
;;;
;;;   (import (alias (except m a b) (m-c c) (m-d d)))
;;;
;;; imports all of m's imports except for x and y, with additional
;;; aliases m-c for c and m-d for d.
;;;
;;; multiple imports may be specified with one import form:
;;;
;;;   (import (except m1 x) (only m2 x))
;;;
;;; imports all of m1's exports except for x plus x from m2.

;;; Another form, meta, may be used as a prefix for any definition and
;;; causes any resulting variable bindings to be created at expansion
;;; time.  Meta variables (variables defined using meta) are available
;;; only at expansion time.  Meta definitions are often used to create
;;; data and helpers that can be shared by multiple macros, for example:

;;; (module (alpha beta)
;;;   (meta define key-error
;;;     (lambda (key)
;;;       (syntax-error key "invalid key")))
;;;   (meta define parse-keys
;;;     (lambda (keys)
;;;       (let f ((keys keys) (c #'white) (s 10))
;;;         (syntax-case keys (color size)
;;;           (() (list c s))
;;;           (((color c) . keys) (f #'keys #'c s))
;;;           (((size s) . keys) (f #'keys c #'s))
;;;           ((k . keys) (key-error #'k))))))
;;;   (define-syntax alpha
;;;     (lambda (x)
;;;       (syntax-case x ()
;;;         ((_ (k ...) <other stuff>)
;;;          (with-syntax (((c s) (parse-keys (syntax (k ...)))))
;;;            ---)))))
;;;   (define-syntax beta
;;;     (lambda (x)
;;;       (syntax-case x ()
;;;         ((_ (k ...) <other stuff>)
;;;          (with-syntax (((c s) (parse-keys (syntax (k ...)))))
;;;            ---))))))

;;; As with define-syntax rhs expressions, meta expressions can evaluate
;;; references only to identifiers whose values are (already) available
;;; in the compile-time environment, e.g., macros and meta variables.
;;; They can, however, like define-syntax rhs expressions, build syntax
;;; objects containing occurrences of any identifiers in their scope.

;;; meta definitions propagate through macro expansion, so one can write,
;;; for example:
;;;
;;;   (module (a)
;;;     (meta define-structure (foo x))
;;;     (define-syntax a
;;;       (let ((q (make-foo (syntax 'q))))
;;;         (lambda (x)
;;;           (foo-x q)))))
;;;   a -> q
;;;
;;; where define-record is a macro that expands into a set of defines.
;;;
;;; It is also sometimes convenient to write
;;;
;;;   (meta begin defn ...)
;;;
;;; or
;;;
;;;   (meta module {exports} defn ...)
;;;
;;; to create groups of meta bindings.

;;; Another form, alias, is used to create aliases from one identifier
;;; to another.  This is used primarily to support the extended import
;;; syntaxes (add-prefix, drop-prefix, rename, and alias).

;;; (let ((x 3)) (alias y x) y) -> 3

;;; The remaining exports are listed below.  sc-expand, eval-when, and
;;; syntax-error are described in the Chez Scheme User's Guide.
;;;
;;;   (sc-expand datum)
;;;      if datum represents a valid expression, sc-expand returns an
;;;      expanded version of datum in a core language that includes no
;;;      syntactic abstractions.  The core language includes begin,
;;;      define, if, lambda, letrec, quote, and set!.
;;;   (eval-when situations expr ...)
;;;      conditionally evaluates expr ... at compile-time or run-time
;;;      depending upon situations
;;;   (syntax-error object message)
;;;      used to report errors found during expansion
;;;   ($syntax-dispatch e p)
;;;      used by expanded code to handle syntax-case matching
;;;   ($sc-put-cte symbol val top-token)
;;;      used to establish top-level compile-time (expand-time) bindings.

;;; The following nonstandard procedures must be provided by the
;;; implementation for this code to run.
;;;
;;; (void)
;;; returns the implementation's cannonical "unspecified value".  The
;;; following usually works:
;;;
;;; (define void (lambda () (if #f #f))).
;;;
;;; (andmap proc list1 list2 ...)
;;; returns true if proc returns true when applied to each element of list1
;;; along with the corresponding elements of list2 ....  The following
;;; definition works but does no error checking:
;;;
;;; (define andmap
;;;   (lambda (f first . rest)
;;;     (or (null? first)
;;;         (if (null? rest)
;;;             (let andmap ((first first))
;;;               (let ((x (car first)) (first (cdr first)))
;;;                 (if (null? first)
;;;                     (f x)
;;;                     (and (f x) (andmap first)))))
;;;             (let andmap ((first first) (rest rest))
;;;               (let ((x (car first))
;;;                     (xr (map car rest))
;;;                     (first (cdr first))
;;;                     (rest (map cdr rest)))
;;;                 (if (null? first)
;;;                     (apply f (cons x xr))
;;;                     (and (apply f (cons x xr)) (andmap first rest)))))))))
;;;
;;; (ormap proc list1)
;;; returns the first non-false return result of proc applied to
;;; the elements of list1 or false if none.  The following definition
;;; works but does no error checking:
;;;
;;; (define ormap
;;;   (lambda (proc list1)
;;;     (and (not (null? list1))
;;;          (or (proc (car list1)) (ormap proc (cdr list1))))))
;;;
;;; The following nonstandard procedures must also be provided by the
;;; implementation for this code to run using the standard portable
;;; hooks and output constructors.  They are not used by expanded code,
;;; and so need be present only at expansion time.
;;;
;;; (eval x)
;;; where x is always in the form ("noexpand" expr).
;;; returns the value of expr.  the "noexpand" flag is used to tell the
;;; evaluator/expander that no expansion is necessary, since expr has
;;; already been fully expanded to core forms.
;;;
;;; eval will not be invoked during the loading of psyntax.pp.  After
;;; psyntax.pp has been loaded, the expansion of any macro definition,
;;; whether local or global, results in a call to eval.  If, however,
;;; sc-expand has already been registered as the expander to be used
;;; by eval, and eval accepts one argument, nothing special must be done
;;; to support the "noexpand" flag, since it is handled by sc-expand.
;;;
;;; (error who format-string why what)
;;; where who is either a symbol or #f, format-string is always "~a ~s",
;;; why is always a string, and what may be any object.  error should
;;; signal an error with a message something like
;;;
;;;    "error in <who>: <why> <what>"
;;;
;;; (gensym)
;;; returns a unique symbol each time it's called.  In Chez Scheme, gensym
;;; returns a symbol with a "globally" unique name so that gensyms that
;;; end up in the object code of separately compiled files cannot conflict.
;;; This is necessary only if you intend to support compiled files.
;;;
;;; (gensym? x)
;;; returns #t if x is a gensym, otherwise false.
;;;
;;; (putprop symbol key value)
;;; (getprop symbol key)
;;; (remprop symbol key)
;;; key is always a symbol; value may be any object.  putprop should
;;; associate the given value with the given symbol and key in some way
;;; that it can be retrieved later with getprop.  getprop should return
;;; #f if no value is associated with the given symbol and key.  remprop
;;; should remove the association between the given symbol and key.

;;; When porting to a new Scheme implementation, you should define the
;;; procedures listed above, load the expanded version of psyntax.ss
;;; (psyntax.pp, which should be available whereever you found
;;; psyntax.ss), and register sc-expand as the current expander (how
;;; you do this depends upon your implementation of Scheme).  You may
;;; change the hooks and constructors defined toward the beginning of
;;; the code below, but to avoid bootstrapping problems, do so only
;;; after you have a working version of the expander.

;;; Chez Scheme allows the syntactic form (syntax <template>) to be
;;; abbreviated to #'<template>, just as (quote <datum>) may be
;;; abbreviated to '<datum>.  The #' syntax makes programs written
;;; using syntax-case shorter and more readable and draws out the
;;; intuitive connection between syntax and quote.  If you have access
;;; to the source code of your Scheme system's reader, you might want
;;; to implement this extension.

;;; If you find that this code loads or runs slowly, consider
;;; switching to faster hardware or a faster implementation of
;;; Scheme.  In Chez Scheme on a 200Mhz Pentium Pro, expanding,
;;; compiling (with full optimization), and loading this file takes
;;; between one and two seconds.

;;; In the expander implementation, we sometimes use syntactic abstractions
;;; when procedural abstractions would suffice.  For example, we define
;;; top-wrap and top-marked? as
;;;   (define-syntax top-wrap (identifier-syntax '((top))))
;;;   (define-syntax top-marked?
;;;     (syntax-rules ()
;;;       ((_ w) (memq 'top (wrap-marks w)))))
;;; rather than
;;;   (define top-wrap '((top)))
;;;   (define top-marked?
;;;     (lambda (w) (memq 'top (wrap-marks w))))
;;; On ther other hand, we don't do this consistently; we define make-wrap,
;;; wrap-marks, and wrap-subst simply as
;;;   (define make-wrap cons)
;;;   (define wrap-marks car)
;;;   (define wrap-subst cdr)
;;; In Chez Scheme, the syntactic and procedural forms of these
;;; abstractions are equivalent, since the optimizer consistently
;;; integrates constants and small procedures.  Some Scheme
;;; implementations, however, may benefit from more consistent use
;;; of one form or the other.


;;; Implementation notes:

;;; "begin" is treated as a splicing construct at top level and at
;;; the beginning of bodies.  Any sequence of expressions that would
;;; be allowed where the "begin" occurs is allowed.

;;; "let-syntax" and "letrec-syntax" are also treated as splicing
;;; constructs, in violation of the R5RS.  A consequence is that let-syntax
;;; and letrec-syntax do not create local contours, as do let and letrec.
;;; Although the functionality is greater as it is presently implemented,
;;; we will probably change it to conform to the R5RS.  modules provide
;;; similar functionality to nonsplicing letrec-syntax when the latter is
;;; used as a definition.

;;; Objects with no standard print syntax, including objects containing
;;; cycles and syntax objects, are allowed in quoted data as long as they
;;; are contained within a syntax form or produced by datum->syntax-object.
;;; Such objects are never copied.

;;; When the expander encounters a reference to an identifier that has
;;; no global or lexical binding, it treats it as a global-variable
;;; reference.  This allows one to write mutually recursive top-level
;;; definitions, e.g.:
;;;
;;;   (define f (lambda (x) (g x)))
;;;   (define g (lambda (x) (f x)))
;;;
;;; but may not always yield the intended when the variable in question
;;; is later defined as a keyword.

;;; Top-level variable definitions of syntax keywords are permitted.
;;; In order to make this work, top-level define not only produces a
;;; top-level definition in the core language, but also modifies the
;;; compile-time environment (using $sc-put-cte) to record the fact
;;; that the identifier is a variable.

;;; Top-level definitions of macro-introduced identifiers are visible
;;; only in code produced by the macro.  That is, a binding for a
;;; hidden (generated) identifier is created instead, and subsequent
;;; references within the macro output are renamed accordingly.  For
;;; example:
;;;
;;; (define-syntax a
;;;   (syntax-rules ()
;;;     ((_ var exp)
;;;      (begin
;;;        (define secret exp)
;;;        (define var
;;;          (lambda ()
;;;            (set! secret (+ secret 17))
;;;            secret))))))
;;; (a x 0)
;;; (x) => 17
;;; (x) => 34
;;; secret => Error: variable secret is not bound
;;;
;;; The definition above would fail if the definition for secret
;;; were placed after the definition for var, since the expander would
;;; encounter the references to secret before the definition that
;;; establishes the compile-time map from the identifier secret to
;;; the generated identifier.

;;; Identifiers and syntax objects are implemented as vectors for
;;; portability.  As a result, it is possible to "forge" syntax
;;; objects.

;;; The input to sc-expand may contain "annotations" describing, e.g., the
;;; source file and character position from where each object was read if
;;; it was read from a file.  These annotations are handled properly by
;;; sc-expand only if the annotation? hook (see hooks below) is implemented
;;; properly and the operators annotation-expression and annotation-stripped
;;; are supplied.  If annotations are supplied, the proper annotated
;;; expression is passed to the various output constructors, allowing
;;; implementations to accurately correlate source and expanded code.
;;; Contact one of the authors for details if you wish to make use of
;;; this feature.

;;; Implementation of modules:
;;;
;;; The implementation of modules requires that implicit top-level exports
;;; be listed with the exported macro at some level where both are visible,
;;; e.g.,
;;;
;;;   (module M (alpha (beta b))
;;;     (module ((alpha a) b)
;;;       (define-syntax alpha (identifier-syntax a))
;;;       (define a 'a)
;;;       (define b 'b))
;;;     (define-syntax beta (identifier-syntax b)))
;;;
;;; Listing of implicit imports is not needed for macros that do not make
;;; it out to top level, including all macros that are local to a "body".
;;; (They may be listed in this case, however.)  We need this information
;;; for top-level modules since a top-level module expands into a letrec
;;; for non-top-level variables and top-level definitions (assignments) for
;;; top-level variables.  Because of the general nature of macro
;;; transformers, we cannot determine the set of implicit exports from the
;;; transformer code, so without the user's help, we'd have to put all
;;; variables at top level.
;;;
;;; Each such top-level identifier is given a generated name (gensym).
;;; When a top-level module is imported at top level, a compile-time
;;; alias is established from the top-level name to the generated name.
;;; The expander follows these aliases transparently.  When any module is
;;; imported anywhere other than at top level, the id-var-name of the
;;; import identifier is set to the id-var-name of the export identifier.
;;; Since we can't determine the actual labels for identifiers defined in
;;; top-level modules until we determine which are placed in the letrec
;;; and which make it to top level, we give each an "indirect" label---a
;;; pair whose car will eventually contain the actual label.  Import does
;;; not follow the indirect, but id-var-name does.
;;;
;;; All identifiers defined within a local module are folded into the
;;; letrec created for the enclosing body.  Visibility is controlled in
;;; this case and for nested top-level modules by introducing a new wrap
;;; for each module.


;;; Bootstrapping:

;;; When changing syntax-object representations, it is necessary to support
;;; both old and new syntax-object representations in id-var-name.  It
;;; should be sufficient to redefine syntax-object-expression to work for
;;; both old and new representations and syntax-object-wrap to return the
;;; empty-wrap for old representations.


;;; The following set of definitions establishes bindings for the
;;; top-level variables assigned values in the let expression below.
;;; Uncomment them here and copy them to the front of psyntax.pp if
;;; required by your system.

; (define $sc-put-cte #f)
; (define sc-expand #f)
; (define $make-environment #f)
; (define environment? #f)
; (define interaction-environment #f)
; (define identifier? #f)
; (define syntax->list #f)
; (define syntax-object->datum #f)
; (define datum->syntax-object #f)
; (define generate-temporaries #f)
; (define free-identifier=? #f)
; (define bound-identifier=? #f)
; (define literal-identifier=? #f)
; (define syntax-error #f)
; (define $syntax-dispatch #f)

(let ()

(define-syntax when
  (syntax-rules ()
    ((_ test e1 e2 ...) (if test (begin e1 e2 ...)))))
(define-syntax unless
  (syntax-rules ()
    ((_ test e1 e2 ...) (when (not test) (begin e1 e2 ...)))))
(define-syntax define-structure
  (lambda (x)
    (define construct-name
      (lambda (template-identifier . args)
        (datum->syntax-object
          template-identifier
          (string->symbol
            (apply string-append
                   (map (lambda (x)
                          (if (string? x)
                              x
                              (symbol->string (syntax-object->datum x))))
                        args))))))
    (syntax-case x ()
      ((_ (name id1 ...))
       (andmap identifier? (syntax (name id1 ...)))
       (with-syntax
         ((constructor (construct-name (syntax name) "make-" (syntax name)))
          (predicate (construct-name (syntax name) (syntax name) "?"))
          ((access ...)
           (map (lambda (x) (construct-name x (syntax name) "-" x))
                (syntax (id1 ...))))
          ((assign ...)
           (map (lambda (x)
                  (construct-name x "set-" (syntax name) "-" x "!"))
                (syntax (id1 ...))))
          (structure-length
           (+ (length (syntax (id1 ...))) 1))
          ((index ...)
           (let f ((i 1) (ids (syntax (id1 ...))))
              (if (null? ids)
                  '()
                  (cons i (f (+ i 1) (cdr ids)))))))
         (syntax (begin
                   (define constructor
                     (lambda (id1 ...)
                       (vector 'name id1 ... )))
                   (define predicate
                     (lambda (x)
                       (and (vector? x)
                            (= (vector-length x) structure-length)
                            (eq? (vector-ref x 0) 'name))))
                   (define access
                     (lambda (x)
                       (vector-ref x index)))
                   ...
                   (define assign
                     (lambda (x update)
                       (vector-set! x index update)))
                   ...)))))))

(define-syntax let-values ; impoverished one-clause version
  (syntax-rules ()
    ((_ ((formals expr)) form1 form2 ...)
     (call-with-values (lambda () expr) (lambda formals form1 form2 ...)))))

(define noexpand "noexpand")

(define-structure (syntax-object expression wrap))

;;; hooks to nonportable run-time helpers
(begin
(define-syntax fx+ (identifier-syntax +))
(define-syntax fx- (identifier-syntax -))
(define-syntax fx= (identifier-syntax =))
(define-syntax fx< (identifier-syntax <))
(define-syntax fx> (identifier-syntax >))
(define-syntax fx<= (identifier-syntax <=))
(define-syntax fx>= (identifier-syntax >=))

(define annotation? (lambda (x) #f))

; top-level-eval-hook is used to create "permanent" code (e.g., top-level
; transformers), so it might be a good idea to compile it
(define top-level-eval-hook
  (lambda (x)
    (eval `(,noexpand ,x))))

; local-eval-hook is used to create "temporary" code (e.g., local
; transformers), so it might be a good idea to interpret it
(define local-eval-hook
  (lambda (x)
    (eval `(,noexpand ,x))))

(define define-top-level-value-hook
  (lambda (sym val)
    (top-level-eval-hook
      (build-global-definition no-source sym
        (build-data no-source val)))))

(define error-hook
  (lambda (who why what)
    (error who "~a ~s" why what)))

(define put-cte-hook
  (lambda (symbol val)
    ($sc-put-cte symbol val '*top*)))

(define get-global-definition-hook
  (lambda (symbol)
    (getprop symbol '*sc-expander*)))

(define put-global-definition-hook
  (lambda (symbol x)
    (if (not x)
        (remprop symbol '*sc-expander*)
        (putprop symbol '*sc-expander* x))))

; if you treat certain bindings (say from environments like ieee or r5rs)
; read-only, this should return #t for those bindings
(define read-only-binding?
  (lambda (symbol)
    #f))

; should return #f if symbol has no binding for token
(define get-import-binding
  (lambda (symbol token)
    (getprop symbol token)))

; remove binding if x is false
(define update-import-binding!
  (lambda (symbol token p)
    (let ((x (p (get-import-binding symbol token))))
      (if (not x)
          (remprop symbol token)
          (putprop symbol token x)))))

;;; generate-id ideally produces globally unique symbols, i.e., symbols
;;; unique across system runs, to support separate compilation/expansion.
;;; Use gensyms if you do not need to support separate compilation/
;;; expansion or if your system's gensym creates globally unique
;;; symbols (as in Chez Scheme).  Otherwise, use the following code
;;; as a starting point.  session-key should be a unique string for each
;;; system run to support separate compilation; the default value given
;;; is satisfactory during initial development only.
(define generate-id
  (let ((digits "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!$%&*/:<=>?~_^.+-"))
    (let ((base (string-length digits)) (session-key "_"))
      (define make-digit (lambda (x) (string-ref digits x)))
      (define fmt
        (lambda (n)
          (let fmt ((n n) (a '()))
            (if (< n base)
                (list->string (cons (make-digit n) a))
                (let ((r (modulo n base)) (rest (quotient n base)))
                  (fmt rest (cons (make-digit r) a)))))))
      (let ((n -1))
        (lambda (name) ; name is #f or a symbol
          (set! n (+ n 1))
          (string->symbol (string-append session-key (fmt n))))))))
)



;;; output constructors
(begin
(define-syntax build-application
  (syntax-rules ()
    ((_ ae fun-exp arg-exps)
     `(,fun-exp . ,arg-exps))))

(define-syntax build-conditional
  (syntax-rules ()
    ((_ ae test-exp then-exp else-exp)
     `(if ,test-exp ,then-exp ,else-exp))))

(define-syntax build-lexical-reference
  (syntax-rules ()
    ((_ type ae var)
     var)))

(define-syntax build-lexical-assignment
  (syntax-rules ()
    ((_ ae var exp)
     `(set! ,var ,exp))))

(define-syntax build-global-reference
  (syntax-rules ()
    ((_ ae var)
     var)))

(define-syntax build-global-assignment
  (syntax-rules ()
    ((_ ae var exp)
     `(set! ,var ,exp))))

(define-syntax build-global-definition
  (syntax-rules ()
    ((_ ae var exp)
     `(define ,var ,exp))))

(define-syntax build-cte-install
 ; should build a call that has the same effect as calling put-cte-hook
  (syntax-rules ()
    ((_ sym exp token) `($sc-put-cte ',sym ,exp ',token))))

(define-syntax build-visit-only
 ; should mark the result as "visit only" for compile-file
 ; in implementations that support visit/revisit
  (syntax-rules ()
    ((_ exp) exp)))

(define-syntax build-revisit-only
 ; should mark the result as "revisit only" for compile-file,
 ; in implementations that support visit/revisit
  (syntax-rules ()
    ((_ exp) exp)))

(define-syntax build-lambda
  (syntax-rules ()
    ((_ ae vars exp)
     `(lambda ,vars ,exp))))

(define built-lambda?
  (lambda (x)
    (and (pair? x) (eq? (car x) 'lambda))))

(define-syntax build-primref
  (syntax-rules ()
    ((_ ae name) name)
    ((_ ae level name) name)))

(define-syntax build-data
  (syntax-rules ()
    ((_ ae exp) `',exp)))

(define build-sequence
  (lambda (ae exps)
    (let loop ((exps exps))
      (if (null? (cdr exps))
          (car exps)
         ; weed out leading void calls, assuming ordinary list representation
          (if (equal? (car exps) '(void))
              (loop (cdr exps))
              `(begin ,@exps))))))

(define build-letrec
  (lambda (ae vars val-exps body-exp)
    (if (null? vars)
        body-exp
        `(letrec ,(map list vars val-exps) ,body-exp))))

(define build-body
  (lambda (ae vars val-exps body-exp)
    (build-letrec ae vars val-exps body-exp)))

(define build-top-module
 ; each type is either global (exported) or local (not exported)
 ; we produce global definitions and assignments for globals and
 ; letrec bindings for locals.  if you don't need the definitions,
 ; (just assignments) you can eliminate them.  if you wish to
 ; have your module definitions ordered from left-to-right (ala
 ; letrec*), you can replace the global var-exps with dummy vars
 ; and global val-exps with global assignments, and produce a letrec*
 ; in place of a letrec.
  (lambda (ae types vars val-exps body-exp)
    (let-values (((vars defns sets)
                  (let f ((types types) (vars vars))
                    (if (null? types)
                        (values '() '() '())
                        (let ((var (car vars)))
                          (let-values (((vars defns sets) (f (cdr types) (cdr vars))))
                            (if (eq? (car types) 'global)
                                (let ((x (build-lexical-var no-source var)))
                                  (values
                                    (cons x vars)
                                    (cons (build-global-definition no-source var (chi-void)) defns)
                                    (cons (build-global-assignment no-source var (build-lexical-reference 'value no-source x)) sets)))
                                (values (cons var vars) defns sets))))))))
      (if (null? defns)
          (build-letrec ae vars val-exps body-exp)
          (build-sequence no-source
            (append defns
              (list
                (build-letrec ae vars val-exps
                  (build-sequence no-source (append sets (list body-exp)))))))))))

(define-syntax build-lexical-var
  (syntax-rules ()
    ((_ ae id) (gensym))))

(define-syntax lexical-var? gensym?)

(define-syntax self-evaluating?
  (syntax-rules ()
    ((_ e)
     (let ((x e))
       (or (boolean? x) (number? x) (string? x) (char? x) (null? x))))))
)

(define-syntax unannotate
  (syntax-rules ()
    ((_ x)
     (let ((e x))
       (if (annotation? e)
           (annotation-expression e)
           e)))))

(define-syntax no-source (identifier-syntax #f))

(define-syntax arg-check
  (syntax-rules ()
    ((_ pred? e who)
     (let ((x e))
       (if (not (pred? x)) (error-hook who "invalid argument" x))))))

;;; compile-time environments

;;; wrap and environment comprise two level mapping.
;;;   wrap : id --> label
;;;   env : label --> <element>

;;; environments are represented in two parts: a lexical part and a global
;;; part.  The lexical part is a simple list of associations from labels
;;; to bindings.  The global part is implemented by
;;; {put,get}-global-definition-hook and associates symbols with
;;; bindings.

;;; global (assumed global variable) and displaced-lexical (see below)
;;; do not show up in any environment; instead, they are fabricated by
;;; lookup when it finds no other bindings.

;;; <environment>              ::= ((<label> . <binding>)*)

;;; identifier bindings include a type and a value

;;; <binding> ::= <procedure>                     macro keyword
;;;               (macro . <procedure>)           macro keyword
;;;               (deferred . <thunk>)            macro keyword w/lazily evaluated transformer
;;;               (macro! . <procedure>)          extended identifier macro keyword
;;;               (core . <procedure>)            core keyword
;;;               (begin)                         begin keyword
;;;               (define)                        define keyword
;;;               (define-syntax)                 define-syntax keyword
;;;               (local-syntax . <boolean>)      let-syntax (#f)/letrec-syntax (#t) keyword
;;;               (eval-when)                     eval-when keyword
;;;               (set!)                          set! keyword
;;;               (meta)                          meta keyword
;;;               ($module-key)                   $module keyword
;;;               ($import)                       $import keyword
;;;               ($module . <interface>)         modules
;;;               (syntax . (<var> . <level>))    pattern variables
;;;               (global . <symbol>)             assumed global variable
;;;               (meta-variable . <symbol>)      meta variable
;;;               (lexical . <var>)               lexical variables
;;;               (displaced-lexical . #f)        id-var-name not found in store
;;; <level>   ::= <nonnegative integer>
;;; <var>     ::= variable returned by build-lexical-var

;;; a macro is a user-defined syntactic-form.  a core is a system-defined
;;; syntactic form.  begin, define, define-syntax, let-syntax, letrec-syntax,
;;; eval-when, and meta are treated specially since they are sensitive to
;;; whether the form is at top-level and can denote valid internal
;;; definitions.

;;; a pattern variable is a variable introduced by syntax-case and can
;;; be referenced only within a syntax form.

;;; any identifier for which no top-level syntax definition or local
;;; binding of any kind has been seen is assumed to be a global
;;; variable.

;;; a lexical variable is a lambda- or letrec-bound variable.

;;; a displaced-lexical identifier is a lexical identifier removed from
;;; it's scope by the return of a syntax object containing the identifier.
;;; a displaced lexical can also appear when a letrec-syntax-bound
;;; keyword is referenced on the rhs of one of the letrec-syntax clauses.
;;; a displaced lexical should never occur with properly written macros.

(define sanitize-binding
  (lambda (b)
    (cond
      ((procedure? b) (make-binding 'macro b))
      ((binding? b)
       (and (case (binding-type b)
              ((core macro macro! deferred) (and (procedure? (binding-value b))))
              (($module) (interface? (binding-value b)))
              ((lexical) (lexical-var? (binding-value b)))
             ((global meta-variable) (symbol? (binding-value b)))
              ((syntax) (let ((x (binding-value b)))
                          (and (pair? x)
                               (lexical-var? (car x))
                               (let ((n (cdr x)))
                                 (and (integer? n) (exact? n) (>= n 0))))))
              ((begin define define-syntax set! $module-key $import eval-when meta) (null? (binding-value b)))
              ((local-syntax) (boolean? (binding-value b)))
              ((displaced-lexical) (eq? (binding-value b) #f))
              (else #t))
            b))
      (else #f))))

(define-syntax make-binding
  (syntax-rules (quote)
    ((_ 'type #f) '(type . #f))
    ((_ type value) (cons type value))))
(define binding-type car)
(define binding-value cdr)
(define set-binding-type! set-car!)
(define set-binding-value! set-cdr!)
(define binding? (lambda (x) (and (pair? x) (symbol? (car x)))))

(define-syntax null-env (identifier-syntax '()))

(define extend-env
  (lambda (label binding r)
    (cons (cons label binding) r)))

(define extend-env*
  (lambda (labels bindings r)
    (if (null? labels)
        r
        (extend-env* (cdr labels) (cdr bindings)
          (extend-env (car labels) (car bindings) r)))))

(define extend-var-env*
  ; variant of extend-env* that forms "lexical" binding
  (lambda (labels vars r)
    (if (null? labels)
        r
        (extend-var-env* (cdr labels) (cdr vars)
          (extend-env (car labels) (make-binding 'lexical (car vars)) r)))))

(define (displaced-lexical? id r)
  (let ((n (id-var-name id empty-wrap)))
    (and n
         (let ((b (lookup n r)))
           (eq? (binding-type b) 'displaced-lexical)))))

(define displaced-lexical-error
  (lambda (id)
    (syntax-error id
      (if (id-var-name id empty-wrap)
          "identifier out of context"
          "identifier not visible"))))

(define lookup*
  ; x may be a label or a symbol
  ; although symbols are usually global, we check the environment first
  ; anyway because a temporary binding may have been established by
  ; fluid-let-syntax
  (lambda (x r)
    (cond
      ((assq x r) => cdr)
      ((symbol? x)
       (or (get-global-definition-hook x) (make-binding 'global x)))
      (else (make-binding 'displaced-lexical #f)))))

(define lookup
  (lambda (x r)
    (define whack-binding!
      (lambda (b *b)
        (set-binding-type! b (binding-type *b))
        (set-binding-value! b (binding-value *b))))
    (let ((b (lookup* x r)))
      (when (eq? (binding-type b) 'deferred)
        (whack-binding! b (make-transformer-binding ((binding-value b)))))
      b)))

(define make-transformer-binding
  (lambda (b)
    (or (sanitize-binding b)
        (syntax-error b "invalid transformer"))))

(define defer-or-eval-transformer
  (lambda (eval x)
    (if (built-lambda? x)
        (make-binding 'deferred (lambda () (eval x)))
        (make-transformer-binding (eval x)))))

(define global-extend
  (lambda (type sym val)
    (put-cte-hook sym (make-binding type val))))


;;; Conceptually, identifiers are always syntax objects.  Internally,
;;; however, the wrap is sometimes maintained separately (a source of
;;; efficiency and confusion), so that symbols are also considered
;;; identifiers by id?.  Externally, they are always wrapped.

(define nonsymbol-id?
  (lambda (x)
    (and (syntax-object? x)
         (symbol? (unannotate (syntax-object-expression x))))))

(define id?
  (lambda (x)
    (cond
      ((symbol? x) #t)
      ((syntax-object? x) (symbol? (unannotate (syntax-object-expression x))))
      ((annotation? x) (symbol? (annotation-expression x)))
      (else #f))))

(define-syntax id-sym-name
  (syntax-rules ()
    ((_ e)
     (let ((x e))
       (unannotate (if (syntax-object? x) (syntax-object-expression x) x))))))

(define id-marks
  (lambda (id)
    (if (syntax-object? id)
        (wrap-marks (syntax-object-wrap id))
        (wrap-marks top-wrap))))

(define id-subst
  (lambda (id)
    (if (syntax-object? id)
        (wrap-subst (syntax-object-wrap id))
        (wrap-marks top-wrap))))

(define id-sym-name&marks
  (lambda (x w)
    (if (syntax-object? x)
        (values
          (unannotate (syntax-object-expression x))
          (join-marks (wrap-marks w) (wrap-marks (syntax-object-wrap x))))
        (values (unannotate x) (wrap-marks w)))))

;;; syntax object wraps

;;;         <wrap>     ::= ((<mark> ...) . (<subst> ...))
;;;        <subst>     ::= <ribcage> | <shift>
;;;      <ribcage>     ::= #((<ex-symname> ...) (<mark> ...) (<label> ...)) ; extensible, for chi-internal/external
;;;                      | #(#(<symname> ...) #(<mark> ...) #(<label> ...)) ; nonextensible
;;;   <ex-symname>     ::= <symname> | <import token> | <barrier>
;;;        <shift>     ::= shift
;;;      <barrier>     ::= #f                                               ; inserted by import-only
;;; <import interface> ::= #<import-interface interface new-marks>
;;;        <token>     ::= <generated id>

(define make-wrap cons)
(define wrap-marks car)
(define wrap-subst cdr)


(define-syntax empty-wrap (identifier-syntax '(())))

(define-syntax top-wrap (identifier-syntax '((top))))

(define-syntax tmp-wrap (identifier-syntax '((tmp)))) ; for generate-temporaries

(define-syntax top-marked?
  (syntax-rules ()
    ((_ w) (memq 'top (wrap-marks w)))))

(define-syntax only-top-marked?
  (syntax-rules ()
    ((_ id) (same-marks? (wrap-marks (syntax-object-wrap id)) (wrap-marks top-wrap)))))

;;; labels

;;; simple labels must be comparable with "eq?" and distinct from symbols
;;; and pairs.

;;; indirect labels, which are implemented as pairs, are used to support
;;; import aliasing for identifiers exported (explictly or implicitly) from
;;; top-level modules.  chi-external creates an indirect label for each
;;; defined identifier, import causes the pair to be shared with aliases it
;;; establishes, and chi-top-module whacks the pair to hold the top-level
;;; identifier name (symbol) if the id is to be placed at top level, before
;;; expanding the right-hand sides of the definitions in the module.

(module (gen-indirect-label indirect-label? get-indirect-label set-indirect-label!)
  (define-structure (indirect-label label))
  (define gen-indirect-label
    (lambda ()
      (make-indirect-label (gen-label))))
  (define get-indirect-label (lambda (x) (indirect-label-label x)))
  (define set-indirect-label! (lambda (x v) (set-indirect-label-label! x v))))

(define gen-label
  (lambda () (string #\i)))
(define label?
  (lambda (x)
    (or (string? x) ; normal lexical labels
        (symbol? x) ; global labels (symbolic names)
        (indirect-label? x))))

(define gen-labels
  (lambda (ls)
    (if (null? ls)
        '()
        (cons (gen-label) (gen-labels (cdr ls))))))

(define-structure (ribcage symnames marks labels))
(define-structure (top-ribcage key mutable?))
(define-structure (import-interface interface new-marks))
(define-structure (env top-ribcage wrap))

;;; Marks must be comparable with "eq?" and distinct from pairs and
;;; the symbol top.  We do not use integers so that marks will remain
;;; unique even across file compiles.

(define-syntax the-anti-mark (identifier-syntax #f))

(define anti-mark
  (lambda (w)
    (make-wrap (cons the-anti-mark (wrap-marks w))
               (cons 'shift (wrap-subst w)))))

(define-syntax new-mark
  (syntax-rules ()
    ((_) (string #\m))))

(define barrier-marker #f)

;;; make-empty-ribcage and extend-ribcage maintain list-based ribcages for
;;; internal definitions, in which the ribcages are built incrementally
(define-syntax make-empty-ribcage
  (syntax-rules ()
    ((_) (make-ribcage '() '() '()))))

(define extend-ribcage!
 ; must receive ids with complete wraps
 ; ribcage guaranteed to be list-based
  (lambda (ribcage id label)
    (set-ribcage-symnames! ribcage
      (cons (unannotate (syntax-object-expression id))
            (ribcage-symnames ribcage)))
    (set-ribcage-marks! ribcage
      (cons (wrap-marks (syntax-object-wrap id))
            (ribcage-marks ribcage)))
    (set-ribcage-labels! ribcage
      (cons label (ribcage-labels ribcage)))))

(define import-extend-ribcage!
 ; must receive ids with complete wraps
 ; ribcage guaranteed to be list-based
  (lambda (ribcage new-marks id label)
    (set-ribcage-symnames! ribcage
      (cons (unannotate (syntax-object-expression id))
            (ribcage-symnames ribcage)))
    (set-ribcage-marks! ribcage
      (cons (join-marks new-marks (wrap-marks (syntax-object-wrap id)))
            (ribcage-marks ribcage)))
    (set-ribcage-labels! ribcage
      (cons label (ribcage-labels ribcage)))))

(define extend-ribcage-barrier!
 ; must receive ids with complete wraps
 ; ribcage guaranteed to be list-based
  (lambda (ribcage killer-id)
    (extend-ribcage-barrier-help! ribcage (syntax-object-wrap killer-id))))

(define extend-ribcage-barrier-help!
  (lambda (ribcage wrap)
    (set-ribcage-symnames! ribcage
      (cons barrier-marker (ribcage-symnames ribcage)))
    (set-ribcage-marks! ribcage
      (cons (wrap-marks wrap) (ribcage-marks ribcage)))))

(define extend-ribcage-subst!
 ; ribcage guaranteed to be list-based
  (lambda (ribcage import-iface)
    (set-ribcage-symnames! ribcage
      (cons import-iface (ribcage-symnames ribcage)))))

(define lookup-import-binding-name
  (lambda (sym marks token new-marks)
    (let ((new (get-import-binding sym token)))
      (and new
           (let f ((new new))
             (cond
               ((pair? new) (or (f (car new)) (f (cdr new))))
               ((symbol? new)
                (and (same-marks? marks (join-marks new-marks (wrap-marks top-wrap))) new))
               ((same-marks? marks (join-marks new-marks (wrap-marks (syntax-object-wrap new)))) new)
               (else #f)))))))

(define store-import-binding
  (lambda (id token new-marks)
    (define cons-id
      (lambda (id x)
        (if (not x) id (cons id x))))
    (define weed ; remove existing binding for id, if any
      (lambda (marks x)
        (if (pair? x)
            (if (same-marks? (id-marks (car x)) marks)
                (weed marks (cdr x))
                (cons-id (car x) (weed marks (cdr x))))
            (and x (not (same-marks? (id-marks x) marks)) x))))
    (let ((id (if (null? new-marks)
                  id
                  (make-syntax-object (id-sym-name id)
                    (make-wrap
                      (join-marks new-marks (id-marks id))
                      (id-subst id))))))
      (let ((sym (id-sym-name id)))
       ; no need to record bindings mapping symbol to self, since this
       ; assumed by default.
        (unless (eq? id sym)
          (let ((marks (id-marks id)))
            (update-import-binding! sym token
              (lambda (old-binding)
                (let ((x (weed marks old-binding)))
                  (cons-id
                    (if (same-marks? marks (wrap-marks top-wrap))
                       ; need full id only if more than top-marked.
                        (resolved-id-var-name id)
                        id)
                    x))))))))))

;;; make-binding-wrap creates vector-based ribcages
(define make-binding-wrap
  (lambda (ids labels w)
    (if (null? ids)
        w
        (make-wrap
          (wrap-marks w)
          (cons
            (let ((labelvec (list->vector labels)))
              (let ((n (vector-length labelvec)))
                (let ((symnamevec (make-vector n)) (marksvec (make-vector n)))
                  (let f ((ids ids) (i 0))
                    (unless (null? ids)
                      (let-values (((symname marks) (id-sym-name&marks (car ids) w)))
                        (vector-set! symnamevec i symname)
                        (vector-set! marksvec i marks)
                        (f (cdr ids) (fx+ i 1)))))
                  (make-ribcage symnamevec marksvec labelvec))))
            (wrap-subst w))))))

;;; resolved ids contain no unnecessary substitutions or marks.  they are
;;; used essentially as indirects or aliases in modules interfaces.
(define make-resolved-id
  (lambda (fromsym marks tosym)
    (make-syntax-object fromsym
      (make-wrap marks
        (list (make-ribcage (vector fromsym) (vector marks) (vector tosym)))))))

(define id->resolved-id
  (lambda (id)
    (let-values (((tosym marks) (id-var-name&marks id empty-wrap)))
      (unless tosym
        (syntax-error id "identifier not visible for export"))
      (make-resolved-id (id-sym-name id) marks tosym))))

(define resolved-id-var-name
  (lambda (id)
    (vector-ref
      (ribcage-labels (car (wrap-subst (syntax-object-wrap id))))
      0)))

;;; Scheme's append should not copy the first argument if the second is
;;; nil, but it does, so we define a smart version here.
(define smart-append
  (lambda (m1 m2)
    (if (null? m2)
        m1
        (append m1 m2))))

(define join-wraps
  (lambda (w1 w2)
    (let ((m1 (wrap-marks w1)) (s1 (wrap-subst w1)))
      (if (null? m1)
          (if (null? s1)
              w2
              (make-wrap
                (wrap-marks w2)
                (join-subst s1 (wrap-subst w2))))
          (make-wrap
            (join-marks m1 (wrap-marks w2))
            (join-subst s1 (wrap-subst w2)))))))

(define join-marks
  (lambda (m1 m2)
    (smart-append m1 m2)))

(define join-subst
  (lambda (s1 s2)
    (smart-append s1 s2)))

(define same-marks?
  (lambda (x y)
    (or (eq? x y)
        (and (not (null? x))
             (not (null? y))
             (eq? (car x) (car y))
             (same-marks? (cdr x) (cdr y))))))

(define diff-marks
  (lambda (m1 m2)
    (let ((n1 (length m1)) (n2 (length m2)))
      (let f ((n1 n1) (m1 m1))
        (cond
          ((> n1 n2) (cons (car m1) (f (- n1 1) (cdr m1))))
          ((equal? m1 m2) '())
          (else (error 'sc-expand
                  "internal error in diff-marks: ~s is not a tail of ~s"
                  m1 m2)))))))

(module (top-id-bound-var-name top-id-free-var-name)
  ;; top-id-bound-var-name is used to look up or establish new top-level
  ;; substitutions, while top-id-free-var-name is used to look up existing
  ;; (possibly implicit) substitutions.  Implicit substitutions exist
  ;; for top-marked names in all environments, but we represent them
  ;; explicitly only on demand.
  ;;
  ;; In both cases, we first look for an existing substitution for sym
  ;; and the given marks.  If we find one, we return it.  Otherwise, we
  ;; extend the appropriate top-level environment
  ;;
  ;; For top-id-bound-var-name, we extend the environment with a substition
  ;; keyed by the given marks, so that top-level definitions introduced by
  ;; a macro are distinct from other top-level definitions for the same
  ;; name.  For example, if macros a and b both introduce definitions and
  ;; bound references to identifier x, the two x's should be different,
  ;; i.e., keyed by their own marks.
  ;;
  ;; For top-id-free-var-name, we extend the environment with a substition
  ;; keyed by the top marks, since top-level free identifier references
  ;; should refer to the existing implicit (top-marked) substitution.  For
  ;; example, if macros a and b both introduce free references to identifier
  ;; x, they should both refer to the same (global, unmarked) x.
  ;;
 ;; If the environment is *top*, we map a symbol to itself

 (define leave-implicit? (lambda (token) (eq? token '*top*)))

  (define new-binding
    (lambda (sym marks token)
      (let ((loc (if (and (leave-implicit? token)
                          (same-marks? marks (wrap-marks top-wrap)))
                     sym
                     (generate-id sym))))
        (let ((id (make-resolved-id sym marks loc)))
          (store-import-binding id token '())
          (values loc id)))))

  (define top-id-bound-var-name
   ; should be called only when top-ribcage is mutable
    (lambda (sym marks top-ribcage)
      (let ((token (top-ribcage-key top-ribcage)))
        (cond
          ((lookup-import-binding-name sym marks token '()) =>
           (lambda (id)
             (if (symbol? id) ; symbol iff marks == (wrap-marks top-wrap)
                 (if (read-only-binding? id)
                     (new-binding sym marks token)
                     (values id (make-resolved-id sym marks id)))
                 (values (resolved-id-var-name id) id))))
          (else (new-binding sym marks token))))))

  (define top-id-free-var-name
    (lambda (sym marks top-ribcage)
      (let ((token (top-ribcage-key top-ribcage)))
        (cond
          ((lookup-import-binding-name sym marks token '()) =>
           (lambda (id) (if (symbol? id) id (resolved-id-var-name id))))
          ((and (top-ribcage-mutable? top-ribcage)
                (same-marks? marks (wrap-marks top-wrap)))
           (let-values (((sym id) (new-binding sym (wrap-marks top-wrap) token)))
             sym))
          (else #f))))))

(define id-var-name-loc&marks
  (lambda (id w)
    (define search
      (lambda (sym subst marks)
        (if (null? subst)
            (values #f marks)
            (let ((fst (car subst)))
               (cond
                 ((eq? fst 'shift) (search sym (cdr subst) (cdr marks)))
                 ((ribcage? fst)
                  (let ((symnames (ribcage-symnames fst)))
                    (if (vector? symnames)
                        (search-vector-rib sym subst marks symnames fst)
                        (search-list-rib sym subst marks symnames fst))))
                 ((top-ribcage? fst)
                  (cond
                    ((top-id-free-var-name sym marks fst) =>
                     (lambda (var-name) (values var-name marks)))
                    (else (search sym (cdr subst) marks))))
                 (else
                  (error 'sc-expand
                    "internal error in id-var-name-loc&marks: improper subst ~s"
                    subst)))))))
    (define search-list-rib
      (lambda (sym subst marks symnames ribcage)
        (let f ((symnames symnames) (i 0))
          (if (null? symnames)
              (search sym (cdr subst) marks)
              (let ((x (car symnames)))
                (cond
                  ((and (eq? x sym)
                        (same-marks? marks (list-ref (ribcage-marks ribcage) i)))
                   (values (list-ref (ribcage-labels ribcage) i) marks))
                  ((import-interface? x)
                   (let ((iface (import-interface-interface x))
                         (new-marks (import-interface-new-marks x)))
                     (cond
                       ((interface-token iface) =>
                        (lambda (token)
                          (cond
                            ((lookup-import-binding-name sym marks token new-marks) =>
                             (lambda (id)
                               (values
                                 (if (symbol? id) id (resolved-id-var-name id))
                                 marks)))
                            (else (f (cdr symnames) i)))))
                       (else
                        (let* ((ie (interface-exports iface))
                               (n (vector-length ie)))
                          (let g ((j 0))
                            (if (fx= j n)
                                (f (cdr symnames) i)
                                (let ((id (vector-ref ie j)))
                                  (let ((id.sym (id-sym-name id))
                                        (id.marks (join-marks new-marks (id-marks id))))
                                    (if (help-bound-id=? id.sym id.marks sym marks)
                                        (values (lookup-import-label id) marks)
                                        (g (fx+ j 1))))))))))))
                  ((and (eq? x barrier-marker)
                        (same-marks? marks (list-ref (ribcage-marks ribcage) i)))
                   (values #f marks))
                  (else (f (cdr symnames) (fx+ i 1)))))))))
    (define search-vector-rib
      (lambda (sym subst marks symnames ribcage)
        (let ((n (vector-length symnames)))
          (let f ((i 0))
            (cond
              ((fx= i n) (search sym (cdr subst) marks))
              ((and (eq? (vector-ref symnames i) sym)
                    (same-marks? marks (vector-ref (ribcage-marks ribcage) i)))
               (values (vector-ref (ribcage-labels ribcage) i) marks))
              (else (f (fx+ i 1))))))))
    (cond
      ((symbol? id) (search id (wrap-subst w) (wrap-marks w)))
      ((syntax-object? id)
       (let ((sym (unannotate (syntax-object-expression id)))
             (w1 (syntax-object-wrap id)))
         (let-values (((name marks) (search sym (wrap-subst w)
                                      (join-marks
                                        (wrap-marks w)
                                        (wrap-marks w1)))))
           (if name
               (values name marks)
               (search sym (wrap-subst w1) marks)))))
      ((annotation? id) (search (unannotate id) (wrap-subst w) (wrap-marks w)))
      (else (error-hook 'id-var-name "invalid id" id)))))

(define id-var-name&marks
 ; this version follows indirect labels
  (lambda (id w)
    (let-values (((label marks) (id-var-name-loc&marks id w)))
      (values (if (indirect-label? label) (get-indirect-label label) label) marks))))

(define id-var-name-loc
 ; this version doesn't follow indirect labels
  (lambda (id w)
    (let-values (((label marks) (id-var-name-loc&marks id w)))
      label)))

(define id-var-name
 ; this version follows indirect labels
  (lambda (id w)
    (let-values (((label marks) (id-var-name-loc&marks id w)))
      (if (indirect-label? label) (get-indirect-label label) label))))

;;; free-id=? must be passed fully wrapped ids since (free-id=? x y)
;;; may be true even if (free-id=? (wrap x w) (wrap y w)) is not.

(define free-id=?
  (lambda (i j)
    (and (eq? (id-sym-name i) (id-sym-name j)) ; accelerator
         (eq? (id-var-name i empty-wrap) (id-var-name j empty-wrap)))))

(define literal-id=?
  (lambda (id literal)
    (and (eq? (id-sym-name id) (id-sym-name literal))
         (let ((n-id (id-var-name id empty-wrap))
               (n-literal (id-var-name literal empty-wrap)))
           (or (eq? n-id n-literal)
               (and (or (not n-id) (symbol? n-id))
                    (or (not n-literal) (symbol? n-literal))))))))

;;; bound-id=? may be passed unwrapped (or partially wrapped) ids as
;;; long as the missing portion of the wrap is common to both of the ids
;;; since (bound-id=? x y) iff (bound-id=? (wrap x w) (wrap y w))

(define help-bound-id=?
  (lambda (i.sym i.marks j.sym j.marks)
    (and (eq? i.sym j.sym)
         (same-marks? i.marks j.marks))))

(define bound-id=?
  (lambda (i j)
    (help-bound-id=? (id-sym-name i) (id-marks i) (id-sym-name j) (id-marks j))))

;;; "valid-bound-ids?" returns #t if it receives a list of distinct ids.
;;; valid-bound-ids? may be passed unwrapped (or partially wrapped) ids
;;; as long as the missing portion of the wrap is common to all of the
;;; ids.

(define valid-bound-ids?
  (lambda (ids)
     (and (let all-ids? ((ids ids))
            (or (null? ids)
                (and (id? (car ids))
                     (all-ids? (cdr ids)))))
          (distinct-bound-ids? ids))))

;;; distinct-bound-ids? expects a list of ids and returns #t if there are
;;; no duplicates.  It is quadratic on the length of the id list; long
;;; lists could be sorted to make it more efficient.  distinct-bound-ids?
;;; may be passed unwrapped (or partially wrapped) ids as long as the
;;; missing portion of the wrap is common to all of the ids.

(define distinct-bound-ids?
  (lambda (ids)
    (let distinct? ((ids ids))
      (or (null? ids)
          (and (not (bound-id-member? (car ids) (cdr ids)))
               (distinct? (cdr ids)))))))

(define invalid-ids-error
 ; find first bad one and complain about it
  (lambda (ids exp class)
    (let find ((ids ids) (gooduns '()))
      (if (null? ids)
          (syntax-error exp) ; shouldn't happen
          (if (id? (car ids))
              (if (bound-id-member? (car ids) gooduns)
                  (syntax-error (car ids) "duplicate " class)
                  (find (cdr ids) (cons (car ids) gooduns)))
              (syntax-error (car ids) "invalid " class))))))

(define bound-id-member?
   (lambda (x list)
      (and (not (null? list))
           (or (bound-id=? x (car list))
               (bound-id-member? x (cdr list))))))

;;; wrapping expressions and identifiers

(define wrap
  (lambda (x w)
    (cond
      ((and (null? (wrap-marks w)) (null? (wrap-subst w))) x)
      ((syntax-object? x)
       (make-syntax-object
         (syntax-object-expression x)
         (join-wraps w (syntax-object-wrap x))))
      ((null? x) x)
      (else (make-syntax-object x w)))))

(define source-wrap
  (lambda (x w ae)
    (wrap (if (annotation? ae)
              (begin
                (unless (eq? (annotation-expression ae) x)
                  (error 'sc-expand "internal error in source-wrap: ae/x mismatch"))
                ae)
              x)
          w)))

;;; expanding

(define chi-when-list
  (lambda (when-list w)
    ; when-list is syntax'd version of list of situations
    (map (lambda (x)
           (cond
             ((literal-id=? x (syntax compile)) 'compile)
             ((literal-id=? x (syntax load)) 'load)
             ((literal-id=? x (syntax visit)) 'visit)
             ((literal-id=? x (syntax revisit)) 'revisit)
             ((literal-id=? x (syntax eval)) 'eval)
             (else (syntax-error (wrap x w) "invalid eval-when situation"))))
         when-list)))

;;; syntax-type returns five values: type, value, e, w, and ae.  The first
;;; two are described in the table below.
;;;
;;;    type                   value         explanation
;;;    -------------------------------------------------------------------
;;;    alias                  none          alias keyword
;;;    alias-form             none          alias expression
;;;    begin                  none          begin keyword
;;;    begin-form             none          begin expression
;;;    call                   none          any other call
;;;    constant               none          self-evaluating datum
;;;    core                   procedure     core form (including singleton)
;;;    define                 none          define keyword
;;;    define-form            none          variable definition
;;;    define-syntax          none          define-syntax keyword
;;;    define-syntax-form     none          syntax definition
;;;    displaced-lexical      none          displaced lexical identifier
;;;    eval-when              none          eval-when keyword
;;;    eval-when-form         none          eval-when form
;;;    global                 name          global variable reference
;;;    $import                none          $import keyword
;;;    $import-form           none          $import form
;;;    lexical                name          lexical variable reference
;;;    lexical-call           name          call to lexical variable
;;;    local-syntax           rec?          letrec-syntax/let-syntax keyword
;;;    local-syntax-form      rec?          syntax definition
;;;    meta                   none          meta keyword
;;;    meta-form              none          meta form
;;;    meta-variable          name          meta variable
;;;    $module                none          $module keyword
;;;    $module-form           none          $module definition
;;;    syntax                 level         pattern variable
;;;    other                  none          anything else
;;;
;;; For all forms, e is the form, w is the wrap for e. and ae is the
;;; (possibly) source-annotated form.
;;;
;;; syntax-type expands macros and unwraps as necessary to get to
;;; one of the forms above.

(define syntax-type
  (lambda (e r w ae rib)
    (cond
      ((symbol? e)
       (let* ((n (id-var-name e w))
              (b (lookup n r))
              (type (binding-type b)))
         (case type
           ((macro macro!) (syntax-type (chi-macro (binding-value b) e r w ae rib) r empty-wrap #f rib))
           (else (values type (binding-value b) e w ae)))))
      ((pair? e)
       (let ((first (car e)))
         (if (id? first)
             (let* ((n (id-var-name first w))
                    (b (lookup n r))
                    (type (binding-type b)))
               (case type
                 ((lexical) (values 'lexical-call (binding-value b) e w ae))
                 ((macro macro!)
                  (syntax-type (chi-macro (binding-value b) e r w ae rib)
                    r empty-wrap #f rib))
                 ((core) (values type (binding-value b) e w ae))
                 ((begin) (values 'begin-form #f e w ae))
                 ((alias) (values 'alias-form #f e w ae))
                 ((define) (values 'define-form #f e w ae))
                 ((define-syntax) (values 'define-syntax-form #f e w ae))
                 ((set!) (chi-set! e r w ae rib))
                 (($module-key) (values '$module-form #f e w ae))
                 (($import) (values '$import-form #f e w ae))
                 ((eval-when) (values 'eval-when-form #f e w ae))
                 ((meta) (values 'meta-form #f e w ae))
                 ((local-syntax)
                  (values 'local-syntax-form (binding-value b) e w ae))
                 (else (values 'call #f e w ae))))
             (values 'call #f e w ae))))
      ((syntax-object? e)
       (syntax-type (syntax-object-expression e)
                    r
                    (join-wraps w (syntax-object-wrap e))
                    #f rib))
      ((annotation? e)
       (syntax-type (annotation-expression e) r w e rib))
      ((self-evaluating? e) (values 'constant #f e w ae))
      (else (values 'other #f e w ae)))))

(define chi-top*
  (lambda (e r w ctem rtem meta? top-ribcage)
    (let ((meta-residuals '()))
      (define meta-residualize!
        (lambda (x)
          (set! meta-residuals
            (cons x meta-residuals))))
      (let ((e (chi-top e r w ctem rtem meta? top-ribcage meta-residualize! #f)))
        (build-sequence no-source
          (reverse (cons e meta-residuals)))))))

(define chi-top-sequence
  (lambda (body r w ae ctem rtem meta? ribcage meta-residualize!)
    (build-sequence ae
      (let dobody ((body body))
        (if (null? body)
            '()
            (let ((first (chi-top (car body) r w ctem rtem meta? ribcage meta-residualize! #f)))
              (cons first (dobody (cdr body)))))))))

(define chi-top
  (lambda (e r w ctem rtem meta? top-ribcage meta-residualize! meta-seen?)
    (let-values (((type value e w ae) (syntax-type e r w no-source top-ribcage)))
      (case type
        ((begin-form)
         (let ((forms (parse-begin e w ae #t)))
           (if (null? forms)
               (chi-void)
               (chi-top-sequence forms r w ae ctem rtem meta? top-ribcage meta-residualize!))))
        ((local-syntax-form)
         (let-values (((forms r mr w ae) (chi-local-syntax value e r r w ae)))
          ; mr should be same as r here
           (chi-top-sequence forms r w ae ctem rtem meta? top-ribcage meta-residualize!)))
        ((eval-when-form)
         (let-values (((when-list forms) (parse-eval-when e w ae)))
           (let ((ctem (update-mode-set when-list ctem))
                 (rtem (update-mode-set when-list rtem)))
             (if (and (null? ctem) (null? rtem))
                 (chi-void)
                 (chi-top-sequence forms r w ae ctem rtem meta? top-ribcage meta-residualize!)))))
        ((meta-form) (chi-top (parse-meta e w ae) r w ctem rtem #t top-ribcage meta-residualize! #t))
        ((define-syntax-form)
         (let-values (((id rhs w) (parse-define-syntax e w ae)))
           (let ((id (wrap id w)))
             (when (displaced-lexical? id r) (displaced-lexical-error id))
             (unless (top-ribcage-mutable? top-ribcage)
               (syntax-error (source-wrap e w ae)
                 "invalid definition in read-only environment"))
             (let ((sym (id-sym-name id)))
               (let-values (((valsym bound-id) (top-id-bound-var-name sym (wrap-marks (syntax-object-wrap id)) top-ribcage)))
                 (unless (eq? (id-var-name id empty-wrap) valsym)
                   (syntax-error (source-wrap e w ae)
                     "definition not permitted"))
                 (when (read-only-binding? valsym)
                   (syntax-error (source-wrap e w ae)
                     "invalid definition of read-only identifier"))
                 (ct-eval/residualize2 ctem
                   (lambda ()
                     (build-cte-install
                       bound-id
                       (chi rhs r r w #t)
                       (top-ribcage-key top-ribcage)))))))))
        ((define-form)
         (let-values (((id rhs w) (parse-define e w ae)))
           (let ((id (wrap id w)))
             (when (displaced-lexical? id r) (displaced-lexical-error id))
             (unless (top-ribcage-mutable? top-ribcage)
               (syntax-error (source-wrap e w ae)
                 "invalid definition in read-only environment"))
             (let ((sym (id-sym-name id)))
               (let-values (((valsym bound-id) (top-id-bound-var-name sym (wrap-marks (syntax-object-wrap id)) top-ribcage)))
                 (unless (eq? (id-var-name id empty-wrap) valsym)
                   (syntax-error (source-wrap e w ae)
                     "definition not permitted"))
                 (when (read-only-binding? valsym)
                   (syntax-error (source-wrap e w ae)
                     "invalid definition of read-only identifier"))
                 (if meta?
                     (ct-eval/residualize2 ctem
                       (lambda ()
                         (build-sequence no-source
                           (list
                             (build-cte-install bound-id
                               (build-data no-source (make-binding 'meta-variable valsym))
                               (top-ribcage-key top-ribcage))
                             (build-global-definition ae valsym (chi rhs r r w #t))))))
                    ; make sure compile-time definitions occur before we
                    ; expand the run-time code
                     (let ((x (ct-eval/residualize2 ctem
                                (lambda ()
                                  (build-cte-install
                                    bound-id
                                    (build-data no-source (make-binding 'global valsym))
                                    (top-ribcage-key top-ribcage))))))
                       (build-sequence no-source
                         (list
                           x
                           (rt-eval/residualize rtem
                             (lambda ()
                               (build-global-definition ae valsym (chi rhs r r w #f)))))))))
             ))))
        (($module-form)
         (let ((ribcage (make-empty-ribcage)))
           (let-values (((orig id exports forms)
                         (parse-module e w ae
                           (make-wrap (wrap-marks w) (cons ribcage (wrap-subst w))))))
             (when (displaced-lexical? id r) (displaced-lexical-error (wrap id w)))
             (unless (top-ribcage-mutable? top-ribcage)
               (syntax-error orig
                 "invalid definition in read-only environment"))
             (chi-top-module orig r r top-ribcage ribcage ctem rtem meta? id exports forms meta-residualize!))))
        (($import-form)
         (let-values (((orig only? mid) (parse-import e w ae)))
           (unless (top-ribcage-mutable? top-ribcage)
             (syntax-error orig
               "invalid definition in read-only environment"))
           (ct-eval/residualize2 ctem
             (lambda ()
               (let ((binding (lookup (id-var-name mid empty-wrap) null-env)))
                 (case (binding-type binding)
                   (($module) (do-top-import only? top-ribcage mid (interface-token (binding-value binding))))
                   ((displaced-lexical) (displaced-lexical-error mid))
                   (else (syntax-error mid "unknown module"))))))))
        ((alias-form)
         (let-values (((new-id old-id) (parse-alias e w ae)))
           (let ((new-id (wrap new-id w)))
             (when (displaced-lexical? new-id r) (displaced-lexical-error new-id))
             (unless (top-ribcage-mutable? top-ribcage)
               (syntax-error (source-wrap e w ae)
                 "invalid definition in read-only environment"))
             (let ((sym (id-sym-name new-id)))
               (let-values (((valsym bound-id) (top-id-bound-var-name sym (wrap-marks (syntax-object-wrap new-id)) top-ribcage)))
                 (unless (eq? (id-var-name new-id empty-wrap) valsym)
                   (syntax-error (source-wrap e w ae)
                     "definition not permitted"))
                 (when (read-only-binding? valsym)
                   (syntax-error (source-wrap e w ae)
                     "invalid definition of read-only identifier"))
                 (ct-eval/residualize2 ctem
                   (lambda ()
                     (build-cte-install
                       (make-resolved-id sym (wrap-marks (syntax-object-wrap new-id)) (id-var-name old-id w))
                       (build-data no-source (make-binding 'do-alias #f))
                       (top-ribcage-key top-ribcage)))))))))
        (else
         (when meta-seen? (syntax-error (source-wrap e w ae) "invalid meta definition"))
         (if meta?
             (let ((x (chi-expr type value e r r w ae #t)))
               (top-level-eval-hook x)
               (ct-eval/residualize3 ctem void (lambda () x)))
             (rt-eval/residualize rtem
               (lambda ()
                 (chi-expr type value e r r w ae #f)))))))))

(define flatten-exports
  (lambda (exports)
    (let loop ((exports exports) (ls '()))
      (if (null? exports)
          ls
          (loop (cdr exports)
                (if (pair? (car exports))
                    (loop (car exports) ls)
                    (cons (car exports) ls)))))))


(define-structure (interface marks exports token))

;; leaves interfaces unresolved so that indirect labels can be followed.
;; (can't resolve until indirect labels have their final value)
(define make-unresolved-interface
 ; trim out implicit exports
  (lambda (mid exports)
    (make-interface
      (wrap-marks (syntax-object-wrap mid))
      (list->vector (map (lambda (x) (if (pair? x) (car x) x)) exports))
      #f)))

(define make-resolved-interface
 ; trim out implicit exports & resolve others to actual top-level symbol
  (lambda (mid exports token)
    (make-interface
      (wrap-marks (syntax-object-wrap mid))
      (list->vector (map (lambda (x) (id->resolved-id (if (pair? x) (car x) x))) exports))
      token)))

(define-structure (module-binding type id label imps val exported))
(define create-module-binding
  (lambda (type id label imps val)
    (make-module-binding type id label imps val #f)))

;;; frobs represent body forms
(define-structure (frob e meta?))

(define chi-top-module
  (lambda (orig r mr top-ribcage ribcage ctem rtem meta? id exports forms meta-residualize!)
    (let ((fexports (flatten-exports exports)))
      (let-values (((r mr bindings inits)
                    (chi-external ribcage orig
                      (map (lambda (d) (make-frob d meta?)) forms) r mr ctem exports fexports
                      meta-residualize!)))
       ; identify exported identifiers, create ctdefs
        (let process-exports ((fexports fexports) (ctdefs (lambda () '())))
          (if (null? fexports)
             ; remaining bindings are either identified global vars,
             ; local vars, or local compile-time entities
             ; dts: type (local/global)
             ; dvs & des: define lhs & rhs
              (let process-locals ((bs bindings) (r r) (dts '()) (dvs '()) (des '()))
                (if (null? bs)
                    (let ((des (chi-frobs des r mr #f))
                          (inits (chi-frobs inits r mr #f)))
                      (build-sequence no-source
                        (append
                         ; we wait to establish global compile-time definitions so that
                         ; expansion of des use local versions of modules and macros
                         ; in case ctem tells us not to eval ctdefs now.  this means that
                         ; local code can use exported compile-time values (modules, macros,
                         ; meta variables) just as it can unexported ones.
                          (ctdefs)
                          (list
                            (ct-eval/residualize2 ctem
                              (lambda ()
                                (let ((sym (id-sym-name id)))
                                  (let* ((token (generate-id sym))
                                         (b (build-data no-source
                                              (make-binding '$module
                                                (make-resolved-interface id exports token)))))
                                    (let-values (((valsym bound-id)
                                                  (top-id-bound-var-name sym
                                                    (wrap-marks (syntax-object-wrap id))
                                                    top-ribcage)))
                                      (unless (eq? (id-var-name id empty-wrap) valsym)
                                        (syntax-error orig
                                          "definition not permitted"))
                                      (when (read-only-binding? valsym)
                                        (syntax-error orig
                                          "invalid definition of read-only identifier"))
                                      (build-cte-install bound-id b
                                        (top-ribcage-key top-ribcage)))))))
                            (rt-eval/residualize rtem
                              (lambda ()
                                (build-top-module no-source dts dvs des
                                  (if (null? inits)
                                      (chi-void)
                                      (build-sequence no-source
                                        (append inits (list (chi-void))))))))))))
                    (let ((b (car bs)) (bs (cdr bs)))
                      (let ((t (module-binding-type b)))
                        (case (module-binding-type b)
                          ((define-form)
                           (let ((label (get-indirect-label (module-binding-label b))))
                             (if (module-binding-exported b)
                                 (let ((var (module-binding-id b)))
                                   (process-locals bs r (cons 'global dts) (cons label dvs)
                                     (cons (module-binding-val b) des)))
                                 (let ((var (gen-var (module-binding-id b))))
                                   (process-locals bs
                                    ; add lexical bindings only to run-time environment
                                     (extend-env label (make-binding 'lexical var) r)
                                     (cons 'local dts) (cons var dvs)
                                     (cons (module-binding-val b) des))))))
                          ((ctdefine-form define-syntax-form $module-form alias-form) (process-locals bs r dts dvs des))
                          (else (error 'sc-expand-internal "unexpected module binding type ~s" t)))))))
              (let ((id (car fexports)) (fexports (cdr fexports)))
                (let loop ((bs bindings))
                  (if (null? bs)
                     ; must be rexport from an imported module
                      (process-exports fexports ctdefs)
                      (let ((b (car bs)) (bs (cdr bs)))
                       ; following formerly used bound-id=?, but free-id=? can prevent false positives
                       ; and is okay since the substitutions have already been applied
                        (if (free-id=? (module-binding-id b) id)
                            (if (module-binding-exported b)
                                (process-exports fexports ctdefs)
                                (let* ((t (module-binding-type b))
                                       (label (module-binding-label b))
                                       (imps (module-binding-imps b))
                                       (fexports (append imps fexports)))
                                  (set-module-binding-exported! b #t)
                                  (case t
                                    ((define-form)
                                     (let ((sym (generate-id (id-sym-name id))))
                                       (set-indirect-label! label sym)
                                       (process-exports fexports ctdefs)))
                                    ((ctdefine-form)
                                     (let ((b (module-binding-val b)))
                                       (process-exports fexports
                                         (lambda ()
                                           (let ((sym (binding-value b)))
                                             (set-indirect-label! label sym)
                                             (cons (ct-eval/residualize3 ctem
                                                     (lambda () (put-cte-hook sym b))
                                                     (lambda () (build-cte-install sym (build-data no-source b) #f)))
                                                   (ctdefs)))))))
                                    ((define-syntax-form)
                                     (let ((sym (generate-id (id-sym-name id))))
                                       (process-exports fexports
                                         (lambda ()
                                           (let ((local-label (get-indirect-label label)))
                                             (set-indirect-label! label sym)
                                             (cons
                                               (ct-eval/residualize3 ctem
                                                 (lambda () (put-cte-hook sym (car (module-binding-val b))))
                                                 (lambda () (build-cte-install sym (cdr (module-binding-val b)) #f)))
                                               (ctdefs)))))))
                                    (($module-form)
                                     (let ((sym (generate-id (id-sym-name id)))
                                           (exports (module-binding-val b)))
                                       (process-exports (append (flatten-exports exports) fexports)
                                         (lambda ()
                                           (set-indirect-label! label sym)
                                           (let ((rest (ctdefs))) ; set indirect labels before resolving
                                             (let ((x (make-binding '$module (make-resolved-interface id exports sym))))
                                               (cons (ct-eval/residualize3 ctem
                                                       (lambda () (put-cte-hook sym x))
                                                       (lambda () (build-cte-install sym (build-data no-source x) #f)))
                                                     rest)))))))
                                    ((alias-form)
                                     (process-exports
                                       fexports
                                       (lambda ()
                                         (let ((rest (ctdefs))) ; set indirect labels before resolving
                                           (when (indirect-label? label)
                                             (unless (symbol? (get-indirect-label label))
                                               (syntax-error (module-binding-id b) "unexported target of alias")))
                                           rest))))
                                    (else (error 'sc-expand-internal "unexpected module binding type ~s" t)))))
                            (loop bs))))))))))))

(define id-set-diff
  (lambda (exports defs)
    (cond
      ((null? exports) '())
      ((bound-id-member? (car exports) defs) (id-set-diff (cdr exports) defs))
      (else (cons (car exports) (id-set-diff (cdr exports) defs))))))

(define check-module-exports
  ; After processing the definitions of a module this is called to verify that the
  ; module has defined or imported each exported identifier.  Because ids in fexports are
  ; wrapped with the given ribcage, they will contain substitutions for anything defined
  ; or imported here.  These subsitutions can be used by do-import! and do-import-top! to
  ; provide access to reexported bindings, for example.
  (lambda (source-exp fexports ids)
    (define defined?
      (lambda (e ids)
        (ormap (lambda (x)
                 (if (import-interface? x)
                     (let ((x.iface (import-interface-interface x))
                           (x.new-marks (import-interface-new-marks x)))
                       (cond
                         ((interface-token x.iface) =>
                          (lambda (token)
                            (lookup-import-binding-name (id-sym-name e) (id-marks e) token x.new-marks)))
                         (else
                          (let ((v (interface-exports x.iface)))
                            (let lp ((i (fx- (vector-length v) 1)))
                              (and (fx>= i 0)
                                   (or (let ((id (vector-ref v i)))
                                         (help-bound-id=?
                                           (id-sym-name id)
                                           (join-marks x.new-marks (id-marks id))
                                           (id-sym-name e) (id-marks e)))
                                       (lp (fx- i 1)))))))))
                     (bound-id=? e x)))
               ids)))
    (let loop ((fexports fexports) (missing '()))
      (if (null? fexports)
          (unless (null? missing)
            (syntax-error (car missing)
              (if (= (length missing) 1)
                  "missing definition for export"
                  "missing definition for multiple exports, including")))
          (let ((e (car fexports)) (fexports (cdr fexports)))
            (if (defined? e ids)
                (loop fexports missing)
                (loop fexports (cons e missing))))))))

(define check-defined-ids
  (lambda (source-exp ls)
    (define vfold
      (lambda (v p cls)
        (let ((len (vector-length v)))
          (let lp ((i 0) (cls cls))
            (if (fx= i len)
                cls
                (lp (fx+ i 1) (p (vector-ref v i) cls)))))))
    (define conflicts
      (lambda (x y cls)
        (if (import-interface? x)
            (let ((x.iface (import-interface-interface x))
                  (x.new-marks (import-interface-new-marks x)))
              (if (import-interface? y)
                  (let ((y.iface (import-interface-interface y))
                        (y.new-marks (import-interface-new-marks y)))
                    (let ((xe (interface-exports x.iface)) (ye (interface-exports y.iface)))
                      (if (fx> (vector-length xe) (vector-length ye))
                          (vfold ye
                            (lambda (id cls)
                              (id-iface-conflicts id y.new-marks x.iface x.new-marks cls)) cls)
                          (vfold xe
                            (lambda (id cls)
                              (id-iface-conflicts id x.new-marks y.iface y.new-marks cls)) cls))))
                  (id-iface-conflicts y '() x.iface x.new-marks cls)))
            (if (import-interface? y)
                (let ((y.iface (import-interface-interface y))
                      (y.new-marks (import-interface-new-marks y)))
                  (id-iface-conflicts x '() y.iface y.new-marks cls))
                (if (bound-id=? x y) (cons x cls) cls)))))
     (define id-iface-conflicts
       (lambda (id id.new-marks iface iface.new-marks cls)
         (let ((id.sym (id-sym-name id))
               (id.marks (join-marks id.new-marks (id-marks id))))
           (cond
             ((interface-token iface) =>
              (lambda (token)
                (if (lookup-import-binding-name id.sym id.marks token iface.new-marks)
                    (cons id cls)
                    cls)))
             (else
              (vfold (interface-exports iface)
                     (lambda (*id cls)
                       (let ((*id.sym (id-sym-name *id))
                             (*id.marks (join-marks iface.new-marks (id-marks *id))))
                         (if (help-bound-id=? *id.sym *id.marks id.sym id.marks)
                             (cons *id cls)
                             cls)))
                     cls))))))
     (unless (null? ls)
       (let lp ((x (car ls)) (ls (cdr ls)) (cls '()))
         (if (null? ls)
             (unless (null? cls)
               (let ((cls (syntax-object->datum cls)))
                 (syntax-error source-exp "duplicate definition for "
                  (symbol->string (car cls))
                   " in")))
             (let lp2 ((ls2 ls) (cls cls))
               (if (null? ls2)
                   (lp (car ls) (cdr ls) cls)
                   (lp2 (cdr ls2) (conflicts x (car ls2) cls)))))))))

(define chi-external
  (lambda (ribcage source-exp body r mr ctem exports fexports meta-residualize!)
    (define return
      (lambda (r mr bindings ids inits)
        (check-defined-ids source-exp ids)
        (check-module-exports source-exp fexports ids)
        (values r mr bindings inits)))
    (define get-implicit-exports
      (lambda (id)
        (let f ((exports exports))
          (if (null? exports)
              '()
              (if (and (pair? (car exports)) (bound-id=? id (caar exports)))
                  (flatten-exports (cdar exports))
                  (f (cdr exports)))))))
    (define update-imp-exports
      (lambda (bindings exports)
        (let ((exports (map (lambda (x) (if (pair? x) (car x) x)) exports)))
          (map (lambda (b)
                 (let ((id (module-binding-id b)))
                   (if (not (bound-id-member? id exports))
                       b
                       (create-module-binding
                         (module-binding-type b)
                         id
                         (module-binding-label b)
                         (append (get-implicit-exports id) (module-binding-imps b))
                         (module-binding-val b)))))
               bindings))))
    (let parse ((body body) (r r) (mr mr) (ids '()) (bindings '()) (inits '()) (meta-seen? #f))
      (if (null? body)
          (return r mr bindings ids inits)
          (let* ((fr (car body)) (e (frob-e fr)) (meta? (frob-meta? fr)))
            (let-values (((type value e w ae) (syntax-type e r empty-wrap no-source ribcage)))
              (case type
                ((define-form)
                 (let-values (((id rhs w) (parse-define e w ae)))
                   (let* ((id (wrap id w))
                          (label (gen-indirect-label))
                          (imps (get-implicit-exports id)))
                     (extend-ribcage! ribcage id label)
                     (cond
                       (meta?
                        (let* ((sym (generate-id (id-sym-name id)))
                               (b (make-binding 'meta-variable sym)))
                         ; add meta bindings only to meta environment
                          (let ((mr (extend-env (get-indirect-label label) b mr)))
                            (let ((exp (chi rhs mr mr w #t)))
                              (define-top-level-value-hook sym (top-level-eval-hook exp))
                              (meta-residualize!
                                (ct-eval/residualize3 ctem
                                  void
                                  (lambda () (build-global-definition no-source sym exp))))
                              (parse (cdr body) r mr
                                (cons id ids)
                                (cons (create-module-binding 'ctdefine-form id label imps b) bindings)
                                inits
                                #f)))))
                       (else
                        (parse (cdr body) r mr
                          (cons id ids)
                          (cons (create-module-binding type id label
                                  imps (make-frob (wrap rhs w) meta?))
                                bindings)
                          inits
                          #f))))))
                ((define-syntax-form)
                 (let-values (((id rhs w) (parse-define-syntax e w ae)))
                   (let* ((id (wrap id w))
                          (label (gen-indirect-label))
                          (imps (get-implicit-exports id))
                          (exp (chi rhs mr mr w #t)))
                     (extend-ribcage! ribcage id label)
                     (let ((l (get-indirect-label label)) (b (defer-or-eval-transformer top-level-eval-hook exp)))
                       (parse (cdr body)
                         (extend-env l b r)
                         (extend-env l b mr)
                         (cons id ids)
                         (cons (create-module-binding type id label imps (cons b exp))
                               bindings)
                         inits
                         #f)))))
                (($module-form)
                 (let* ((*ribcage (make-empty-ribcage))
                        (*w (make-wrap (wrap-marks w) (cons *ribcage (wrap-subst w)))))
                   (let-values (((orig id *exports forms) (parse-module e w ae *w)))
                     (let-values (((r mr *bindings *inits)
                                   (chi-external *ribcage orig
                                     (map (lambda (d) (make-frob d meta?)) forms)
                                     r mr ctem *exports (flatten-exports *exports) meta-residualize!)))
                       (let ((iface (make-unresolved-interface id *exports))
                             (bindings (append *bindings bindings))
                             (inits (append inits *inits))
                             (label (gen-indirect-label))
                             (imps (get-implicit-exports id)))
                         (extend-ribcage! ribcage id label)
                         (let ((l (get-indirect-label label)) (b (make-binding '$module iface)))
                           (parse (cdr body)
                             (extend-env l b r)
                             (extend-env l b mr)
                             (cons id ids)
                             (cons (create-module-binding type id label imps *exports) bindings)
                             inits
                             #f)))))))
               (($import-form)
                (let-values (((orig only? mid) (parse-import e w ae)))
                  (let ((mlabel (id-var-name mid empty-wrap)))
                    (let ((binding (lookup mlabel r)))
                      (case (binding-type binding)
                        (($module)
                         (let* ((iface (binding-value binding))
                                (import-iface (make-import-interface iface (import-mark-delta mid iface))))
                           (when only? (extend-ribcage-barrier! ribcage mid))
                           (do-import! import-iface ribcage)
                           (parse (cdr body) r mr
                             (cons import-iface ids)
                             (update-imp-exports bindings (vector->list (interface-exports iface)))
                             inits
                             #f)))
                        ((displaced-lexical) (displaced-lexical-error mid))
                        (else (syntax-error mid "unknown module")))))))
               ((alias-form)
                (let-values (((new-id old-id) (parse-alias e w ae)))
                  (let* ((new-id (wrap new-id w))
                         (label (id-var-name-loc old-id w))
                         (imps (get-implicit-exports new-id)))
                    (extend-ribcage! ribcage new-id label)
                    (parse (cdr body) r mr
                      (cons new-id ids)
                      (cons (create-module-binding type new-id label imps #f)
                            bindings)
                      inits
                      #f))))
                ((begin-form)
                 (parse (let f ((forms (parse-begin e w ae #t)))
                          (if (null? forms)
                              (cdr body)
                              (cons (make-frob (wrap (car forms) w) meta?)
                                    (f (cdr forms)))))
                   r mr ids bindings inits #f))
                ((eval-when-form)
                 (let-values (((when-list forms) (parse-eval-when e w ae)))
                    (parse (if (memq 'eval when-list) ; mode set is implicitly (E)
                               (let f ((forms forms))
                                 (if (null? forms)
                                     (cdr body)
                                     (cons (make-frob (wrap (car forms) w) meta?)
                                           (f (cdr forms)))))
                               (cdr body))
                      r mr ids bindings inits #f)))
                ((meta-form)
                 (parse (cons (make-frob (wrap (parse-meta e w ae) w) #t)
                              (cdr body))
                   r mr ids bindings inits #t))
                ((local-syntax-form)
                 (let-values (((forms r mr w ae) (chi-local-syntax value e r mr w ae)))
                   (parse (let f ((forms forms))
                            (if (null? forms)
                                (cdr body)
                                (cons (make-frob (wrap (car forms) w) meta?)
                                      (f (cdr forms)))))
                     r mr ids bindings inits #f)))
                (else ; found an init expression
                 (when meta-seen? (syntax-error (source-wrap e w ae) "invalid meta definition"))
                 (let f ((body (cons (make-frob (source-wrap e w ae) meta?) (cdr body))))
                   (if (or (null? body) (not (frob-meta? (car body))))
                       (return r mr bindings ids (append inits body))
                       (begin
                        ; expand and eval meta inits for effect only
                         (let ((x (chi-meta-frob (car body) mr)))
                           (top-level-eval-hook x)
                           (meta-residualize! (ct-eval/residualize3 ctem void (lambda () x))))
                         (f (cdr body)))))))))))))

(define vmap
  (lambda (fn v)
    (do ((i (fx- (vector-length v) 1) (fx- i 1))
         (ls '() (cons (fn (vector-ref v i)) ls)))
        ((fx< i 0) ls))))

(define vfor-each
  (lambda (fn v)
    (let ((len (vector-length v)))
      (do ((i 0 (fx+ i 1)))
          ((fx= i len))
        (fn (vector-ref v i))))))

(define do-top-import
  (lambda (import-only? top-ribcage mid token)
   ; silently treat import-only like regular import at top level
    (build-cte-install mid
      (build-data no-source
        (make-binding 'do-import token))
      (top-ribcage-key top-ribcage))))

(define update-mode-set
  (let ((table
         '((L (load . L) (compile . C) (visit . V) (revisit . R) (eval . -))
           (C (load . -) (compile . -) (visit . -) (revisit . -) (eval . C))
           (V (load . V) (compile . C) (visit . V) (revisit . -) (eval . -))
           (R (load . R) (compile . C) (visit . -) (revisit . R) (eval . -))
           (E (load . -) (compile . -) (visit . -) (revisit . -) (eval . E)))))
    (lambda (when-list mode-set)
      (define remq
        (lambda (x ls)
          (if (null? ls)
              '()
              (if (eq? (car ls) x)
                  (remq x (cdr ls))
                  (cons (car ls) (remq x (cdr ls)))))))
      (remq '-
        (apply append
          (map (lambda (m)
                 (let ((row (cdr (assq m table))))
                   (map (lambda (s) (cdr (assq s row)))
                        when-list)))
               mode-set))))))

(define initial-mode-set
  (lambda (when-list compiling-a-file)
    (apply append
      (map (lambda (s)
             (if compiling-a-file
                 (case s
                   ((compile) '(C))
                   ((load) '(L))
                   ((visit) '(V))
                   ((revisit) '(R))
                   (else '()))
                 (case s
                   ((eval) '(E))
                   (else '()))))
           when-list))))

(define rt-eval/residualize
  (lambda (rtem thunk)
    (if (memq 'E rtem)
        (thunk)
        (let ((thunk (if (memq 'C rtem)
                         (let ((x (thunk)))
                           (top-level-eval-hook x)
                           (lambda () x))
                         thunk)))
          (if (memq 'V rtem)
              (if (or (memq 'L rtem) (memq 'R rtem))
                  (thunk) ; visit-revisit
                  (build-visit-only (thunk)))
              (if (or (memq 'L rtem) (memq 'R rtem))
                  (build-revisit-only (thunk))
                  (chi-void)))))))

(define ct-eval/residualize2
  (lambda (ctem thunk)
    (let ((t #f))
      (ct-eval/residualize3 ctem
        (lambda ()
          (unless t (set! t (thunk)))
          (top-level-eval-hook t))
        (lambda () (or t (thunk)))))))
(define ct-eval/residualize3
  (lambda (ctem eval-thunk residualize-thunk)
    (if (memq 'E ctem)
        (begin (eval-thunk) (chi-void))
        (begin
          (when (memq 'C ctem) (eval-thunk))
          (if (memq 'R ctem)
              (if (or (memq 'L ctem) (memq 'V ctem))
                  (residualize-thunk) ; visit-revisit
                  (build-revisit-only (residualize-thunk)))
              (if (or (memq 'L ctem) (memq 'V ctem))
                  (build-visit-only (residualize-thunk))
                  (chi-void)))))))

(define chi-frobs
  (lambda (frob* r mr m?)
    (map (lambda (x) (chi (frob-e x) r mr empty-wrap m?)) frob*)))

(define chi-meta-frob
  (lambda (x mr)
    (chi (frob-e x) mr mr empty-wrap #t)))

(define chi-sequence
  (lambda (body r mr w ae m?)
    (build-sequence ae
      (let dobody ((body body))
        (if (null? body)
            '()
            (let ((first (chi (car body) r mr w m?)))
              (cons first (dobody (cdr body)))))))))

(define chi
  (lambda (e r mr w m?)
    (let-values (((type value e w ae) (syntax-type e r w no-source #f)))
      (chi-expr type value e r mr w ae m?))))

(define chi-expr
  (lambda (type value e r mr w ae m?)
    (case type
      ((lexical)
       (build-lexical-reference 'value ae value))
      ((core) (value e r mr w ae m?))
      ((lexical-call)
       (chi-application
         (build-lexical-reference 'fun
           (let ((x (car e)))
             (if (syntax-object? x) (syntax-object-expression x) x))
           value)
         e r mr w ae m?))
      ((constant) (build-data ae (strip (source-wrap e w ae) empty-wrap)))
      ((global) (build-global-reference ae value))
      ((meta-variable)
       (if m?
           (build-global-reference ae value)
           (displaced-lexical-error (source-wrap e w ae))))
      ((call) (chi-application (chi (car e) r mr w m?) e r mr w ae m?))
      ((begin-form) (chi-sequence (parse-begin e w ae #f) r mr w ae m?))
      ((local-syntax-form)
       (let-values (((forms r mr w ae) (chi-local-syntax value e r mr w ae)))
         (chi-sequence forms r mr w ae m?)))
      ((eval-when-form)
       (let-values (((when-list forms) (parse-eval-when e w ae)))
         (if (memq 'eval when-list) ; mode set is implicitly (E)
             (chi-sequence forms r mr w ae m?)
             (chi-void))))
      ((meta-form)
       (syntax-error (source-wrap e w ae) "invalid context for meta definition"))
      ((define-form)
       (parse-define e w ae)
       (syntax-error (source-wrap e w ae) "invalid context for definition"))
      ((define-syntax-form)
       (parse-define-syntax e w ae)
       (syntax-error (source-wrap e w ae) "invalid context for definition"))
      (($module-form)
       (let-values (((orig id exports forms) (parse-module e w ae w)))
         (syntax-error orig "invalid context for definition")))
      (($import-form)
       (let-values (((orig only? mid) (parse-import e w ae)))
         (syntax-error orig "invalid context for definition")))
      ((alias-form)
       (parse-alias e w ae)
       (syntax-error (source-wrap e w ae) "invalid context for definition"))
      ((syntax)
       (syntax-error (source-wrap e w ae)
         "reference to pattern variable outside syntax form"))
      ((displaced-lexical) (displaced-lexical-error (source-wrap e w ae)))
      (else (syntax-error (source-wrap e w ae))))))

(define chi-application
  (lambda (x e r mr w ae m?)
    (syntax-case e ()
      ((e0 e1 ...)
       (build-application ae x
         (map (lambda (e) (chi e r mr w m?)) (syntax (e1 ...)))))
      (_ (syntax-error (source-wrap e w ae))))))

(define chi-set!
  (lambda (e r w ae rib)
    (syntax-case e ()
      ((_ id val)
       (id? (syntax id))
       (let ((n (id-var-name (syntax id) w)))
         (let ((b (lookup n r)))
           (case (binding-type b)
             ((macro!)
              (let ((id (wrap (syntax id) w)) (val (wrap (syntax val) w)))
                (syntax-type (chi-macro (binding-value b)
                               `(,(syntax set!) ,id ,val)
                               r empty-wrap #f rib) r empty-wrap #f rib)))
             (else
              (values 'core
                (lambda (e r mr w ae m?)
                 ; repeat lookup in case we were first expression (init) in
                 ; module or lambda body.  we repeat id-var-name as well,
                 ; although this is only necessary if we allow inits to
                 ; preced definitions
                  (let ((val (chi (syntax val) r mr w m?))
                        (n (id-var-name (syntax id) w)))
                    (let ((b (lookup n r)))
                      (case (binding-type b)
                        ((lexical) (build-lexical-assignment ae (binding-value b) val))
                        ((global)
                         (let ((sym (binding-value b)))
                           (when (read-only-binding? n)
                             (syntax-error (source-wrap e w ae)
                               "invalid assignment to read-only variable"))
                           (build-global-assignment ae sym val)))
                        ((meta-variable)
                         (if m?
                             (build-global-assignment ae (binding-value b) val)
                             (displaced-lexical-error (wrap (syntax id) w))))
                        ((displaced-lexical)
                         (displaced-lexical-error (wrap (syntax id) w)))
                        (else (syntax-error (source-wrap e w ae)))))))
                e w ae))))))
      (_ (syntax-error (source-wrap e w ae))))))

(define chi-macro
  (lambda (p e r w ae rib)
    (define rebuild-macro-output
      (lambda (x m)
        (cond ((pair? x)
               (cons (rebuild-macro-output (car x) m)
                     (rebuild-macro-output (cdr x) m)))
              ((syntax-object? x)
               (let ((w (syntax-object-wrap x)))
                 (let ((ms (wrap-marks w)) (s (wrap-subst w)))
                   (make-syntax-object (syntax-object-expression x)
                     (if (and (pair? ms) (eq? (car ms) the-anti-mark))
                         (make-wrap (cdr ms) (cdr s))
                         (make-wrap (cons m ms)
                           (if rib
                               (cons rib (cons 'shift s))
                               (cons 'shift s))))))))
              ((vector? x)
               (let* ((n (vector-length x)) (v (make-vector n)))
                 (do ((i 0 (fx+ i 1)))
                     ((fx= i n) v)
                     (vector-set! v i
                       (rebuild-macro-output (vector-ref x i) m)))))
              ((symbol? x)
               (syntax-error (source-wrap e w ae)
                 "encountered raw symbol "
                (symbol->string x)
                 " in output of macro"))
              (else x))))
    (rebuild-macro-output
      (let ((out (p (source-wrap e (anti-mark w) ae))))
        (if (procedure? out)
            (out (lambda (id)
                   (unless (identifier? id)
                     (syntax-error id
                       "environment argument is not an identifier"))
                   (lookup (id-var-name id empty-wrap) r)))
            out))
      (new-mark))))

(define chi-body
  (lambda (body outer-form r mr w m?)
    (let* ((ribcage (make-empty-ribcage))
           (w (make-wrap (wrap-marks w) (cons ribcage (wrap-subst w))))
           (body (map (lambda (x) (make-frob (wrap x w) #f)) body)))
      (let-values (((r mr exprs ids vars vals inits)
                    (chi-internal ribcage outer-form body r mr m?)))
        (when (null? exprs) (syntax-error outer-form "no expressions in body"))
        (build-body no-source
          (reverse vars) (chi-frobs (reverse vals) r mr m?)
          (build-sequence no-source
            (chi-frobs (append inits exprs) r mr m?)))))))

(define chi-internal
  ;; In processing the forms of the body, we create a new, empty wrap.
  ;; This wrap is augmented (destructively) each time we discover that
  ;; the next form is a definition.  This is done:
  ;;
  ;;   (1) to allow the first nondefinition form to be a call to
  ;;       one of the defined ids even if the id previously denoted a
  ;;       definition keyword or keyword for a macro expanding into a
  ;;       definition;
  ;;   (2) to prevent subsequent definition forms (but unfortunately
  ;;       not earlier ones) and the first nondefinition form from
  ;;       confusing one of the bound identifiers for an auxiliary
  ;;       keyword; and
  ;;   (3) so that we do not need to restart the expansion of the
  ;;       first nondefinition form, which is problematic anyway
  ;;       since it might be the first element of a begin that we
  ;;       have just spliced into the body (meaning if we restarted,
  ;;       we'd really need to restart with the begin or the macro
  ;;       call that expanded into the begin, and we'd have to give
  ;;       up allowing (begin <defn>+ <expr>+), which is itself
  ;;       problematic since we don't know if a begin contains only
  ;;       definitions until we've expanded it).
  ;;
  ;; Subforms of a begin, let-syntax, or letrec-syntax are spliced
  ;; into the body.
  ;;
  ;; outer-form is fully wrapped w/source
  (lambda (ribcage source-exp body r mr m?)
    (define return
      (lambda (r mr exprs ids vars vals inits)
        (check-defined-ids source-exp ids)
        (values r mr exprs ids vars vals inits)))
    (let parse ((body body) (r r) (mr mr) (ids '()) (vars '()) (vals '()) (inits '()) (meta-seen? #f))
      (if (null? body)
          (return r mr body ids vars vals inits)
          (let* ((fr (car body)) (e (frob-e fr)) (meta? (frob-meta? fr)))
            (let-values (((type value e w ae) (syntax-type e r empty-wrap no-source ribcage)))
              (case type
                ((define-form)
                 (let-values (((id rhs w) (parse-define e w ae)))
                   (let ((id (wrap id w)) (label (gen-label)))
                     (cond
                       (meta?
                        (let ((sym (generate-id (id-sym-name id))))
                          (extend-ribcage! ribcage id label)
                         ; add meta bindings only to meta environment
                         ; so visible only to next higher level and beyond
                          (let ((mr (extend-env label (make-binding 'meta-variable sym) mr)))
                            (define-top-level-value-hook sym
                              (top-level-eval-hook (chi rhs mr mr w #t)))
                            (parse (cdr body) r mr (cons id ids) vars vals inits #f))))
                       (else
                        (let ((var (gen-var id)))
                          (extend-ribcage! ribcage id label)
                         ; add lexical bindings only to run-time environment
                          (parse (cdr body)
                            (extend-env label (make-binding 'lexical var) r)
                            mr
                            (cons id ids)
                            (cons var vars)
                            (cons (make-frob (wrap rhs w) meta?) vals)
                            inits
                            #f)))))))
                ((define-syntax-form)
                 (let-values (((id rhs w) (parse-define-syntax e w ae)))
                   (let ((id (wrap id w))
                         (label (gen-label))
                         (exp (chi rhs mr mr w #t)))
                     (extend-ribcage! ribcage id label)
                     (let ((b (defer-or-eval-transformer local-eval-hook exp)))
                       (parse (cdr body)
                         (extend-env label b r) (extend-env label b mr)
                         (cons id ids) vars vals inits #f)))))
                (($module-form)
                 (let* ((*ribcage (make-empty-ribcage))
                        (*w (make-wrap (wrap-marks w) (cons *ribcage (wrap-subst w)))))
                   (let-values (((orig id exports forms) (parse-module e w ae *w)))
                     (let-values (((r mr *body *ids *vars *vals *inits)
                                   (chi-internal *ribcage orig
                                     (map (lambda (d) (make-frob d meta?)) forms)
                                     r mr m?)))
                      ; valid bound ids checked already by chi-internal
                       (check-module-exports source-exp (flatten-exports exports) *ids)
                       (let ((iface (make-resolved-interface id exports #f))
                             (vars (append *vars vars))
                             (vals (append *vals vals))
                             (inits (append inits *inits *body))
                             (label (gen-label)))
                         (extend-ribcage! ribcage id label)
                         (let ((b (make-binding '$module iface)))
                           (parse (cdr body)
                             (extend-env label b r) (extend-env label b mr)
                             (cons id ids) vars vals inits #f)))))))
               (($import-form)
                (let-values (((orig only? mid) (parse-import e w ae)))
                  (let ((mlabel (id-var-name mid empty-wrap)))
                    (let ((binding (lookup mlabel r)))
                      (case (binding-type binding)
                        (($module)
                         (let* ((iface (binding-value binding))
                                (import-iface (make-import-interface iface (import-mark-delta mid iface))))
                           (when only? (extend-ribcage-barrier! ribcage mid))
                           (do-import! import-iface ribcage)
                           (parse (cdr body) r mr (cons import-iface ids) vars vals inits #f)))
                        ((displaced-lexical) (displaced-lexical-error mid))
                        (else (syntax-error mid "unknown module")))))))
                ((alias-form)
                 (let-values (((new-id old-id) (parse-alias e w ae)))
                   (let ((new-id (wrap new-id w)))
                     (extend-ribcage! ribcage new-id (id-var-name-loc old-id w))
                     (parse (cdr body) r mr
                       (cons new-id ids)
                       vars
                       vals
                       inits
                       #f))))
                ((begin-form)
                 (parse (let f ((forms (parse-begin e w ae #t)))
                          (if (null? forms)
                              (cdr body)
                              (cons (make-frob (wrap (car forms) w) meta?)
                                    (f (cdr forms)))))
                   r mr ids vars vals inits #f))
                ((eval-when-form)
                 (let-values (((when-list forms) (parse-eval-when e w ae)))
                   (parse (if (memq 'eval when-list) ; mode set is implicitly (E)
                              (let f ((forms forms))
                                (if (null? forms)
                                    (cdr body)
                                    (cons (make-frob (wrap (car forms) w) meta?)
                                          (f (cdr forms)))))
                              (cdr body))
                     r mr ids vars vals inits #f)))
                ((meta-form)
                 (parse (cons (make-frob (wrap (parse-meta e w ae) w) #t)
                              (cdr body))
                   r mr ids vars vals inits #t))
                ((local-syntax-form)
                 (let-values (((forms r mr w ae) (chi-local-syntax value e r mr w ae)))
                   (parse (let f ((forms forms))
                            (if (null? forms)
                                (cdr body)
                                (cons (make-frob (wrap (car forms) w) meta?)
                                      (f (cdr forms)))))
                     r mr ids vars vals inits #f)))
                (else ; found a non-definition
                 (when meta-seen? (syntax-error (source-wrap e w ae) "invalid meta definition"))
                 (let f ((body (cons (make-frob (source-wrap e w ae) meta?) (cdr body))))
                   (if (or (null? body) (not (frob-meta? (car body))))
                       (return r mr body ids vars vals inits)
                       (begin
                        ; expand meta inits for effect only
                         (top-level-eval-hook (chi-meta-frob (car body) mr))
                         (f (cdr body)))))))))))))

(define import-mark-delta
 ; returns list of marks layered on top of module id beyond those
 ; cached in the interface
  (lambda (mid iface)
    (diff-marks (id-marks mid) (interface-marks iface))))

(define lookup-import-label
  (lambda (id)
    (let ((label (id-var-name-loc id empty-wrap)))
      (unless label
        (syntax-error id "exported identifier not visible"))
      label)))

(define do-import!
  (lambda (import-iface ribcage)
    (let ((ie (interface-exports (import-interface-interface import-iface))))
      (if (<= (vector-length ie) 20)
          (let ((new-marks (import-interface-new-marks import-iface)))
            (vfor-each
              (lambda (id)
                (import-extend-ribcage! ribcage new-marks id
                  (lookup-import-label id)))
              ie))
          (extend-ribcage-subst! ribcage import-iface)))))

(define parse-module
  (lambda (e w ae *w)
    (define listify
      (lambda (exports)
        (if (null? exports)
            '()
            (cons (syntax-case (car exports) ()
                    ((ex ...) (listify (syntax (ex ...))))
                    (x (if (id? (syntax x))
                           (wrap (syntax x) *w)
                           (syntax-error (source-wrap e w ae)
                             "invalid exports list in"))))
                  (listify (cdr exports))))))
    (syntax-case e ()
      ((_ orig mid (ex ...) form ...)
       (id? (syntax mid))
      ; id receives old wrap so it won't be confused with id of same name
      ; defined within the module
       (values (syntax orig) (wrap (syntax mid) w) (listify (syntax (ex ...))) (map (lambda (x) (wrap x *w)) (syntax (form ...)))))
      (_ (syntax-error (source-wrap e w ae))))))

(define parse-import
  (lambda (e w ae)
    (syntax-case e ()
      ((_ orig #t mid)
       (id? (syntax mid))
       (values (syntax orig) #t (wrap (syntax mid) w)))
      ((_ orig #f mid)
       (id? (syntax mid))
       (values (syntax orig) #f (wrap (syntax mid) w)))
      (_ (syntax-error (source-wrap e w ae))))))

(define parse-define
  (lambda (e w ae)
    (syntax-case e ()
      ((_ name val)
       (id? (syntax name))
       (values (syntax name) (syntax val) w))
      ((_ (name . args) e1 e2 ...)
       (and (id? (syntax name))
            (valid-bound-ids? (lambda-var-list (syntax args))))
       (values (wrap (syntax name) w)
               (cons (syntax lambda) (wrap (syntax (args e1 e2 ...)) w))
               empty-wrap))
      ((_ name)
       (id? (syntax name))
       (values (wrap (syntax name) w) (syntax (void)) empty-wrap))
      (_ (syntax-error (source-wrap e w ae))))))

(define parse-define-syntax
  (lambda (e w ae)
    (syntax-case e ()
      ((_ (name id) e1 e2 ...)
       (and (id? (syntax name)) (id? (syntax id)))
       (values (wrap (syntax name) w)
               `(,(syntax lambda) ,(wrap (syntax (id)) w)
                   ,@(wrap (syntax (e1 e2 ...)) w))
               empty-wrap))
      ((_ name val)
       (id? (syntax name))
       (values (syntax name) (syntax val) w))
      (_ (syntax-error (source-wrap e w ae))))))

(define parse-meta
  (lambda (e w ae)
    (syntax-case e ()
      ((_ . form) (syntax form))
      (_ (syntax-error (source-wrap e w ae))))))

(define parse-eval-when
  (lambda (e w ae)
    (syntax-case e ()
      ((_ (x ...) e1 e2 ...)
       (values (chi-when-list (syntax (x ...)) w) (syntax (e1 e2 ...))))
      (_ (syntax-error (source-wrap e w ae))))))

(define parse-alias
  (lambda (e w ae)
    (syntax-case e ()
      ((_ new-id old-id)
       (and (id? (syntax new-id)) (id? (syntax old-id)))
       (values (syntax new-id) (syntax old-id)))
      (_ (syntax-error (source-wrap e w ae))))))

(define parse-begin
  (lambda (e w ae empty-okay?)
    (syntax-case e ()
      ((_) empty-okay? '())
      ((_ e1 e2 ...) (syntax (e1 e2 ...)))
      (_ (syntax-error (source-wrap e w ae))))))

(define chi-lambda-clause
  (lambda (e c r mr w m?)
    (syntax-case c ()
      (((id ...) e1 e2 ...)
       (let ((ids (syntax (id ...))))
         (if (not (valid-bound-ids? ids))
             (syntax-error e "invalid parameter list in")
             (let ((labels (gen-labels ids))
                   (new-vars (map gen-var ids)))
               (values
                 new-vars
                 (chi-body (syntax (e1 e2 ...))
                           e
                           (extend-var-env* labels new-vars r)
                           mr
                           (make-binding-wrap ids labels w)
                           m?))))))
      ((ids e1 e2 ...)
       (let ((old-ids (lambda-var-list (syntax ids))))
         (if (not (valid-bound-ids? old-ids))
             (syntax-error e "invalid parameter list in")
             (let ((labels (gen-labels old-ids))
                   (new-vars (map gen-var old-ids)))
               (values
                 (let f ((ls1 (cdr new-vars)) (ls2 (car new-vars)))
                   (if (null? ls1)
                       ls2
                       (f (cdr ls1) (cons (car ls1) ls2))))
                 (chi-body (syntax (e1 e2 ...))
                           e
                           (extend-var-env* labels new-vars r)
                           mr
                           (make-binding-wrap old-ids labels w)
                           m?))))))
      (_ (syntax-error e)))))

(define chi-local-syntax
  (lambda (rec? e r mr w ae)
    (syntax-case e ()
      ((_ ((id val) ...) e1 e2 ...)
       (let ((ids (syntax (id ...))))
         (if (not (valid-bound-ids? ids))
             (invalid-ids-error (map (lambda (x) (wrap x w)) ids)
               (source-wrap e w ae)
               "keyword")
             (let ((labels (gen-labels ids)))
               (let ((new-w (make-binding-wrap ids labels w)))
                 (let ((b* (let ((w (if rec? new-w w)))
                             (map (lambda (x)
                                    (defer-or-eval-transformer
                                      local-eval-hook
                                      (chi x mr mr w #t)))
                                  (syntax (val ...))))))
                   (values
                     (syntax (e1 e2 ...))
                     (extend-env* labels b* r)
                     (extend-env* labels b* mr)
                     new-w
                     ae)))))))
      (_ (syntax-error (source-wrap e w ae))))))

(define chi-void
  (lambda ()
    (build-application no-source (build-primref no-source 'void) '())))

(define ellipsis?
  (lambda (x)
    (and (nonsymbol-id? x)
         (literal-id=? x (syntax (... ...))))))

;;; data

;;; strips all annotations from potentially circular reader output.

(define strip-annotation
  (lambda (x)
    (cond
      ((pair? x)
       (cons (strip-annotation (car x))
             (strip-annotation (cdr x))))
      ((annotation? x) (annotation-stripped x))
      (else x))))

;;; strips syntax-objects down to top-wrap; if top-wrap is layered directly
;;; on an annotation, strips the annotation as well.
;;; since only the head of a list is annotated by the reader, not each pair
;;; in the spine, we also check for pairs whose cars are annotated in case
;;; we've been passed the cdr of an annotated list

(define strip*
  (lambda (x w fn)
    (if (top-marked? w)
        (fn x)
        (let f ((x x))
          (cond
            ((syntax-object? x)
             (strip* (syntax-object-expression x) (syntax-object-wrap x) fn))
            ((pair? x)
             (let ((a (f (car x))) (d (f (cdr x))))
               (if (and (eq? a (car x)) (eq? d (cdr x)))
                   x
                   (cons a d))))
            ((vector? x)
             (let ((old (vector->list x)))
                (let ((new (map f old)))
                   (if (andmap eq? old new) x (list->vector new)))))
            (else x))))))

(define strip
  (lambda (x w)
    (strip* x w
      (lambda (x)
        (if (or (annotation? x) (and (pair? x) (annotation? (car x))))
            (strip-annotation x)
            x)))))

;;; lexical variables

(define gen-var
  (lambda (id)
    (let ((id (if (syntax-object? id) (syntax-object-expression id) id)))
      (if (annotation? id)
          (build-lexical-var id (annotation-expression id))
          (build-lexical-var id id)))))

(define lambda-var-list
  (lambda (vars)
    (let lvl ((vars vars) (ls '()) (w empty-wrap))
       (cond
         ((pair? vars) (lvl (cdr vars) (cons (wrap (car vars) w) ls) w))
         ((id? vars) (cons (wrap vars w) ls))
         ((null? vars) ls)
         ((syntax-object? vars)
          (lvl (syntax-object-expression vars)
               ls
               (join-wraps w (syntax-object-wrap vars))))
         ((annotation? vars)
          (lvl (annotation-expression vars) ls w))
       ; include anything else to be caught by subsequent error
       ; checking
         (else (cons vars ls))))))


; must precede global-extends

(set! $sc-put-cte
  (lambda (id b top-token)
     (define sc-put-module
       (lambda (exports token new-marks)
         (vfor-each
           (lambda (id) (store-import-binding id token new-marks))
           exports)))
     (define (put-cte id binding token)
       (let ((sym (if (symbol? id) id (id-var-name id empty-wrap))))
        (store-import-binding id token '())
         (put-global-definition-hook sym
          ; global binding is assumed; if global pass #f to remove existing binding, if any
           (if (and (eq? (binding-type binding) 'global)
                    (eq? (binding-value binding) sym))
               #f
               binding))))
     (let ((binding (make-transformer-binding b)))
       (case (binding-type binding)
         (($module)
          (let ((iface (binding-value binding)))
            (sc-put-module (interface-exports iface) (interface-token iface) '()))
          (put-cte id binding top-token))
         ((do-alias) (store-import-binding id top-token '()))
         ((do-import)
          ; fake binding: id is module id binding-value is token
          (let ((token (binding-value b)))
             (let ((b (lookup (id-var-name id empty-wrap) null-env)))
               (case (binding-type b)
                 (($module)
                  (let* ((iface (binding-value b))
                         (exports (interface-exports iface)))
                    (unless (eq? (interface-token iface) token)
                      (syntax-error id "import mismatch for module"))
                    (sc-put-module (interface-exports iface) top-token
                      (import-mark-delta id iface))))
                 (else (syntax-error id "unknown module"))))))
         (else (put-cte id binding top-token))))
     ))


;;; core transformers

(global-extend 'local-syntax 'letrec-syntax #t)
(global-extend 'local-syntax 'let-syntax #f)


(global-extend 'core 'fluid-let-syntax
  (lambda (e r mr w ae m?)
    (syntax-case e ()
      ((_ ((var val) ...) e1 e2 ...)
       (valid-bound-ids? (syntax (var ...)))
       (let ((names (map (lambda (x) (id-var-name x w)) (syntax (var ...)))))
         (for-each
           (lambda (id n)
             (case (binding-type (lookup n r))
               ((displaced-lexical) (displaced-lexical-error (wrap id w)))))
           (syntax (var ...))
           names)
         (let ((b* (map (lambda (x)
                          (defer-or-eval-transformer
                            local-eval-hook
                            (chi x mr mr w #t)))
                        (syntax (val ...)))))
           (chi-body
             (syntax (e1 e2 ...))
             (source-wrap e w ae)
             (extend-env* names b* r)
             (extend-env* names b* mr)
             w
             m?))))
      (_ (syntax-error (source-wrap e w ae))))))

(global-extend 'core 'quote
   (lambda (e r mr w ae m?)
      (syntax-case e ()
         ((_ e) (build-data ae (strip (syntax e) w)))
         (_ (syntax-error (source-wrap e w ae))))))

(global-extend 'core 'syntax
  (let ()
    (define gen-syntax
      (lambda (src e r maps ellipsis? vec?)
        (if (id? e)
            (let ((label (id-var-name e empty-wrap)))
              (let ((b (lookup label r)))
                (if (eq? (binding-type b) 'syntax)
                    (let-values (((var maps)
                                  (let ((var.lev (binding-value b)))
                                    (gen-ref src (car var.lev) (cdr var.lev) maps))))
                      (values `(ref ,var) maps))
                    (if (ellipsis? e)
                        (syntax-error src "misplaced ellipsis in syntax form")
                        (values `(quote ,e) maps)))))
            (syntax-case e ()
              ((dots e)
               (ellipsis? (syntax dots))
               (if vec?
                   (syntax-error src "misplaced ellipsis in syntax template")
                   (gen-syntax src (syntax e) r maps (lambda (x) #f) #f)))
              ((x dots . y)
               ; this could be about a dozen lines of code, except that we
               ; choose to handle (syntax (x ... ...)) forms
               (ellipsis? (syntax dots))
               (let f ((y (syntax y))
                       (k (lambda (maps)
                            (let-values (((x maps)
                                          (gen-syntax src (syntax x) r
                                            (cons '() maps) ellipsis? #f)))
                              (if (null? (car maps))
                                  (syntax-error src
                                    "extra ellipsis in syntax form")
                                  (values (gen-map x (car maps))
                                          (cdr maps)))))))
                 (syntax-case y ()
                   ((dots . y)
                    (ellipsis? (syntax dots))
                    (f (syntax y)
                       (lambda (maps)
                         (let-values (((x maps) (k (cons '() maps))))
                           (if (null? (car maps))
                               (syntax-error src
                                 "extra ellipsis in syntax form")
                               (values (gen-mappend x (car maps))
                                       (cdr maps)))))))
                   (_ (let-values (((y maps) (gen-syntax src y r maps ellipsis? vec?)))
                        (let-values (((x maps) (k maps)))
                          (values (gen-append x y) maps)))))))
              ((x . y)
               (let-values (((xnew maps) (gen-syntax src (syntax x) r maps ellipsis? #f)))
                 (let-values (((ynew maps) (gen-syntax src (syntax y) r maps ellipsis? vec?)))
                   (values (gen-cons e (syntax x) (syntax y) xnew ynew)
                           maps))))
              (#(x1 x2 ...)
               (let ((ls (syntax (x1 x2 ...))))
                 (let-values (((lsnew maps) (gen-syntax src ls r maps ellipsis? #t)))
                   (values (gen-vector e ls lsnew) maps))))
              (_ (values `(quote ,e) maps))))))

    (define gen-ref
      (lambda (src var level maps)
        (if (fx= level 0)
            (values var maps)
            (if (null? maps)
                (syntax-error src "missing ellipsis in syntax form")
                (let-values (((outer-var outer-maps) (gen-ref src var (fx- level 1) (cdr maps))))
                  (let ((b (assq outer-var (car maps))))
                    (if b
                        (values (cdr b) maps)
                        (let ((inner-var (gen-var 'tmp)))
                          (values inner-var
                                  (cons (cons (cons outer-var inner-var)
                                              (car maps))
                                        outer-maps))))))))))

    (define gen-append
      (lambda (x y)
        (if (equal? y '(quote ()))
            x
            `(append ,x ,y))))

    (define gen-mappend
      (lambda (e map-env)
        `(apply (primitive append) ,(gen-map e map-env))))

    (define gen-map
      (lambda (e map-env)
        (let ((formals (map cdr map-env))
              (actuals (map (lambda (x) `(ref ,(car x))) map-env)))
          (cond
            ((eq? (car e) 'ref)
             ; identity map equivalence:
             ; (map (lambda (x) x) y) == y
             (car actuals))
            ((andmap
                (lambda (x) (and (eq? (car x) 'ref) (memq (cadr x) formals)))
                (cdr e))
             ; eta map equivalence:
             ; (map (lambda (x ...) (f x ...)) y ...) == (map f y ...)
             `(map (primitive ,(car e))
                   ,@(map (let ((r (map cons formals actuals)))
                            (lambda (x) (cdr (assq (cadr x) r))))
                          (cdr e))))
            (else `(map (lambda ,formals ,e) ,@actuals))))))

   ; 12/12/00: semantic change: we now return original syntax object (e)
   ; if no pattern variables were found within, to avoid dropping
   ; source annotations prematurely.  the "syntax returns lists" for
   ; lists in its input guarantee counts only for substructure that
   ; contains pattern variables
    (define gen-cons
      (lambda (e x y xnew ynew)
        (case (car ynew)
          ((quote)
           (if (eq? (car xnew) 'quote)
               (let ((xnew (cadr xnew)) (ynew (cadr ynew)))
                 (if (and (eq? xnew x) (eq? ynew y))
                     `',e
                     `'(,xnew . ,ynew)))
               (if (eq? (cadr ynew) '()) `(list ,xnew) `(cons ,xnew ,ynew))))
          ((list) `(list ,xnew ,@(cdr ynew)))
          (else `(cons ,xnew ,ynew)))))

    (define gen-vector
      (lambda (e ls lsnew)
        (cond
          ((eq? (car lsnew) 'quote)
           (if (eq? (cadr lsnew) ls)
               `',e
               `(quote #(,@(cadr lsnew)))))
          ((eq? (car lsnew) 'list) `(vector ,@(cdr lsnew)))
          (else `(list->vector ,lsnew)))))


    (define regen
      (lambda (x)
        (case (car x)
          ((ref) (build-lexical-reference 'value no-source (cadr x)))
          ((primitive) (build-primref no-source (cadr x)))
          ((quote) (build-data no-source (cadr x)))
          ((lambda) (build-lambda no-source (cadr x) (regen (caddr x))))
          ((map) (let ((ls (map regen (cdr x))))
                   (build-application no-source
                     (if (fx= (length ls) 2)
                         (build-primref no-source 'map)
                        ; really need to do our own checking here
                         (build-primref no-source 2 'map)) ; require error check
                     ls)))
          (else (build-application no-source
                  (build-primref no-source (car x))
                  (map regen (cdr x)))))))

    (lambda (e r mr w ae m?)
      (let ((e (source-wrap e w ae)))
        (syntax-case e ()
          ((_ x)
           (let-values (((e maps) (gen-syntax e (syntax x) r '() ellipsis? #f)))
             (regen e)))
          (_ (syntax-error e)))))))


(global-extend 'core 'lambda
  (lambda (e r mr w ae m?)
    (syntax-case e ()
      ((_ . c)
       (let-values (((vars body) (chi-lambda-clause (source-wrap e w ae) (syntax c) r mr w m?)))
         (build-lambda ae vars body))))))


(global-extend 'core 'letrec
  (lambda (e r mr w ae m?)
    (syntax-case e ()
      ((_ ((id val) ...) e1 e2 ...)
       (let ((ids (syntax (id ...))))
         (if (not (valid-bound-ids? ids))
             (invalid-ids-error (map (lambda (x) (wrap x w)) ids)
               (source-wrap e w ae) "bound variable")
             (let ((labels (gen-labels ids))
                   (new-vars (map gen-var ids)))
               (let ((w (make-binding-wrap ids labels w))
                    (r (extend-var-env* labels new-vars r)))
                 (build-letrec ae
                   new-vars
                   (map (lambda (x) (chi x r mr w m?)) (syntax (val ...)))
                   (chi-body (syntax (e1 e2 ...)) (source-wrap e w ae) r mr w m?)))))))
      (_ (syntax-error (source-wrap e w ae))))))


(global-extend 'core 'if
   (lambda (e r mr w ae m?)
      (syntax-case e ()
         ((_ test then)
          (build-conditional ae
             (chi (syntax test) r mr w m?)
             (chi (syntax then) r mr w m?)
             (chi-void)))
         ((_ test then else)
          (build-conditional ae
             (chi (syntax test) r mr w m?)
             (chi (syntax then) r mr w m?)
             (chi (syntax else) r mr w m?)))
         (_ (syntax-error (source-wrap e w ae))))))



(global-extend 'set! 'set! '())

(global-extend 'alias 'alias '())
(global-extend 'begin 'begin '())

(global-extend '$module-key '$module '())
(global-extend '$import '$import '())

(global-extend 'define 'define '())

(global-extend 'define-syntax 'define-syntax '())

(global-extend 'eval-when 'eval-when '())

(global-extend 'meta 'meta '())

(global-extend 'core 'syntax-case
  (let ()
    (define convert-pattern
      ; accepts pattern & keys
      ; returns syntax-dispatch pattern & ids
      (lambda (pattern keys)
        (define cvt*
          (lambda (p* n ids)
            (if (null? p*)
                (values '() ids)
                (let-values (((y ids) (cvt* (cdr p*) n ids)))
                  (let-values (((x ids) (cvt (car p*) n ids)))
                    (values (cons x y) ids))))))
        (define cvt
          (lambda (p n ids)
            (if (id? p)
                (if (bound-id-member? p keys)
                    (values (vector 'free-id p) ids)
                    (values 'any (cons (cons p n) ids)))
                (syntax-case p ()
                  ((x dots)
                   (ellipsis? (syntax dots))
                   (let-values (((p ids) (cvt (syntax x) (fx+ n 1) ids)))
                     (values (if (eq? p 'any) 'each-any (vector 'each p))
                             ids)))
                  ((x dots y ... . z)
                   (ellipsis? (syntax dots))
                   (let-values (((z ids) (cvt (syntax z) n ids)))
                     (let-values (((y ids) (cvt* (syntax (y ...)) n ids)))
                       (let-values (((x ids) (cvt (syntax x) (fx+ n 1) ids)))
                         (values `#(each+ ,x ,(reverse y) ,z) ids)))))
                  ((x . y)
                   (let-values (((y ids) (cvt (syntax y) n ids)))
                     (let-values (((x ids) (cvt (syntax x) n ids)))
                       (values (cons x y) ids))))
                  (() (values '() ids))
                  (#(x ...)
                   (let-values (((p ids) (cvt (syntax (x ...)) n ids)))
                     (values (vector 'vector p) ids)))
                  (x (values (vector 'atom (strip p empty-wrap)) ids))))))
        (cvt pattern 0 '())))

    (define build-dispatch-call
      (lambda (pvars exp y r mr m?)
        (let ((ids (map car pvars)) (levels (map cdr pvars)))
          (let ((labels (gen-labels ids)) (new-vars (map gen-var ids)))
            (build-application no-source
              (build-primref no-source 'apply)
              (list (build-lambda no-source new-vars
                      (chi exp
                         (extend-env*
                             labels
                             (map (lambda (var level)
                                    (make-binding 'syntax `(,var . ,level)))
                                  new-vars
                                  (map cdr pvars))
                             r)
                         mr
                         (make-binding-wrap ids labels empty-wrap)
                         m?))
                    y))))))

    (define gen-clause
      (lambda (x keys clauses r mr m? pat fender exp)
        (let-values (((p pvars) (convert-pattern pat keys)))
          (cond
            ((not (distinct-bound-ids? (map car pvars)))
             (invalid-ids-error (map car pvars) pat "pattern variable"))
            ((not (andmap (lambda (x) (not (ellipsis? (car x)))) pvars))
             (syntax-error pat
               "misplaced ellipsis in syntax-case pattern"))
            (else
             (let ((y (gen-var 'tmp)))
               ; fat finger binding and references to temp variable y
               (build-application no-source
                 (build-lambda no-source (list y)
                   (let-syntax ((y (identifier-syntax
                                     (build-lexical-reference 'value no-source y))))
                     (build-conditional no-source
                       (syntax-case fender ()
                         (#t y)
                         (_ (build-conditional no-source
                              y
                              (build-dispatch-call pvars fender y r mr m?)
                              (build-data no-source #f))))
                       (build-dispatch-call pvars exp y r mr m?)
                       (gen-syntax-case x keys clauses r mr m?))))
                 (list (if (eq? p 'any)
                           (build-application no-source
                             (build-primref no-source 'list)
                             (list (build-lexical-reference no-source 'value x)))
                           (build-application no-source
                             (build-primref no-source '$syntax-dispatch)
                             (list (build-lexical-reference no-source 'value x)
                                   (build-data no-source p))))))))))))

    (define gen-syntax-case
      (lambda (x keys clauses r mr m?)
        (if (null? clauses)
            (build-application no-source
              (build-primref no-source 'syntax-error)
              (list (build-lexical-reference 'value no-source x)))
            (syntax-case (car clauses) ()
              ((pat exp)
               (if (and (id? (syntax pat))
                        (not (bound-id-member? (syntax pat) keys))
                        (not (ellipsis? (syntax pat))))
                   (let ((label (gen-label))
                         (var (gen-var (syntax pat))))
                     (build-application no-source
                       (build-lambda no-source (list var)
                         (chi (syntax exp)
                              (extend-env label (make-binding 'syntax `(,var . 0)) r)
                              mr
                              (make-binding-wrap (syntax (pat))
                                (list label) empty-wrap)
                              m?))
                       (list (build-lexical-reference 'value no-source x))))
                   (gen-clause x keys (cdr clauses) r mr m?
                     (syntax pat) #t (syntax exp))))
              ((pat fender exp)
               (gen-clause x keys (cdr clauses) r mr m?
                 (syntax pat) (syntax fender) (syntax exp)))
              (_ (syntax-error (car clauses) "invalid syntax-case clause"))))))

    (lambda (e r mr w ae m?)
      (let ((e (source-wrap e w ae)))
        (syntax-case e ()
          ((_ val (key ...) m ...)
           (if (andmap (lambda (x) (and (id? x) (not (ellipsis? x))))
                       (syntax (key ...)))
               (let ((x (gen-var 'tmp)))
                 ; fat finger binding and references to temp variable x
                 (build-application ae
                   (build-lambda no-source (list x)
                     (gen-syntax-case x
                       (syntax (key ...)) (syntax (m ...))
                       r mr m?))
                   (list (chi (syntax val) r mr empty-wrap m?))))
               (syntax-error e "invalid literals list in"))))))))

(put-cte-hook 'module
  (lambda (x)
    (define proper-export?
      (lambda (e)
        (syntax-case e ()
          ((id e ...)
           (and (identifier? (syntax id))
                (andmap proper-export? (syntax (e ...)))))
          (id (identifier? (syntax id))))))
    (with-syntax ((orig x))
      (syntax-case x ()
        ((_ (e ...) d ...)
         (if (andmap proper-export? (syntax (e ...)))
             (syntax (begin ($module orig anon (e ...) d ...) ($import orig #f anon)))
             (syntax-error x "invalid exports list in")))
        ((_ m (e ...) d ...)
         (identifier? (syntax m))
         (if (andmap proper-export? (syntax (e ...)))
             (syntax ($module orig m (e ...) d ...))
             (syntax-error x "invalid exports list in")))))))

(let ()
  (define $module-exports
    (lambda (m r)
      (let ((b (r m)))
        (case (binding-type b)
          (($module)
           (let* ((interface (binding-value b))
                  (new-marks (import-mark-delta m interface)))
             (vmap (lambda (x)
                     (let ((id (if (pair? x) (car x) x)))
                       (make-syntax-object
                         (syntax-object->datum id)
                         (let ((marks (join-marks new-marks (wrap-marks (syntax-object-wrap id)))))
                           (make-wrap marks
                                     ; the anti mark should always be present at the head
                                     ; of new-marks, but we paranoically check anyway
                                      (if (eq? (car marks) the-anti-mark)
                                          (cons 'shift (wrap-subst top-wrap))
                                          (wrap-subst top-wrap)))))))
                   (interface-exports interface))))
          ((displaced-lexical) (displaced-lexical-error m))
          (else (syntax-error m "unknown module"))))))
  (define $import-help
    (lambda (orig import-only?)
      (lambda (r)
        (define difference
          (lambda (ls1 ls2)
            (if (null? ls1)
                ls1
                (if (bound-id-member? (car ls1) ls2)
                    (difference (cdr ls1) ls2)
                    (cons (car ls1) (difference (cdr ls1) ls2))))))
        (define prefix-add
          (lambda (prefix-id)
            (let ((prefix (symbol->string (syntax-object->datum prefix-id))))
              (lambda (id)
                (datum->syntax-object id
                  (string->symbol
                    (string-append prefix
                      (symbol->string (syntax-object->datum id)))))))))
        (define prefix-drop
          (lambda (prefix-id)
            (let ((prefix (symbol->string (syntax-object->datum prefix-id))))
              (lambda (id)
                (let ((s (symbol->string (syntax-object->datum id))))
                  (let ((np (string-length prefix)) (ns (string-length s)))
                    (unless (and (>= ns np) (string=? (substring s 0 np) prefix))
                      (syntax-error id (string-append "missing expected prefix " prefix)))
                    (datum->syntax-object id
                      (string->symbol (substring s np ns)))))))))
        (define gen-mid
          (lambda (mid)
           ; introduced module ids must have same marks as original
           ; for import-only, since the barrier carries the marks of
           ; the module id
            (datum->syntax-object mid (generate-id (id-sym-name mid)))))
        (define (modspec m exports?)
          (with-syntax ((orig orig) (import-only? import-only?))
            (syntax-case m (only-for-syntax also-for-syntax
                            only except
                            add-prefix drop-prefix rename alias)
              ((only m id ...)
               (andmap identifier? (syntax (id ...)))
               (let-values (((mid d exports) (modspec (syntax m) #f)))
                 (with-syntax ((d d) (tmid (gen-mid mid)))
                   (values mid
                           (syntax (begin ($module orig tmid (id ...) d) ($import orig import-only? tmid)))
                           (and exports? (syntax (id ...)))))))
              ((except m id ...)
               (andmap identifier? (syntax (id ...)))
               (let-values (((mid d exports) (modspec (syntax m) #t)))
                 (with-syntax ((d d)
                               (tmid (gen-mid mid))
                               ((id ...) (difference exports (syntax (id ...)))))
                   (values mid
                           (syntax (begin ($module orig tmid (id ...) d) ($import orig import-only? tmid)))
                           (and exports? (syntax (id ...)))))))
              ((add-prefix m prefix-id)
               (identifier? (syntax prefix-id))
               (let-values (((mid d exports) (modspec (syntax m) #t)))
                 (with-syntax ((d d)
                               (tmid (gen-mid mid))
                               ((old-id ...) exports)
                               ((tmp ...) (generate-temporaries exports))
                               ((id ...) (map (prefix-add (syntax prefix-id)) exports)))
                   (values mid
                           (syntax (begin ($module orig tmid ((id tmp) ...)
                                            ($module orig tmid ((tmp old-id) ...) d (alias tmp old-id) ...)
                                            ($import orig import-only? tmid)
                                            (alias id tmp) ...)
                                          ($import orig import-only? tmid)))
                           (and exports? (syntax (id ...)))))))
              ((drop-prefix m prefix-id)
               (identifier? (syntax prefix-id))
               (let-values (((mid d exports) (modspec (syntax m) #t)))
                 (with-syntax ((d d)
                               (tmid (gen-mid mid))
                               ((old-id ...) exports)
                               ((tmp ...) (generate-temporaries exports))
                               ((id ...) (map (prefix-drop (syntax prefix-id)) exports)))
                   (values mid
                           (syntax (begin ($module orig tmid ((id tmp) ...)
                                            ($module orig tmid ((tmp old-id) ...) d (alias tmp old-id) ...)
                                            ($import orig import-only? tmid)
                                            (alias id tmp) ...)
                                          ($import orig import-only? tmid)))
                           (and exports? (syntax (id ...)))))))
              ((rename m (new-id old-id) ...)
               (and (andmap identifier? (syntax (new-id ...)))
                    (andmap identifier? (syntax (old-id ...))))
               (let-values (((mid d exports) (modspec (syntax m) #t)))
                 (with-syntax ((d d)
                               (tmid (gen-mid mid))
                               ((tmp ...) (generate-temporaries (syntax (old-id ...))))
                               ((other-id ...) (difference exports (syntax (old-id ...)))))
                   (values mid
                           (syntax (begin ($module orig tmid ((new-id tmp) ... other-id ...)
                                            ($module orig tmid (other-id ... (tmp old-id) ...) d (alias tmp old-id) ...)
                                            ($import orig import-only? tmid)
                                            (alias new-id tmp) ...)
                                          ($import orig import-only? tmid)))
                           (and exports? (syntax (new-id ... other-id ...)))))))
              ((alias m (new-id old-id) ...)
               (and (andmap identifier? (syntax (new-id ...)))
                    (andmap identifier? (syntax (old-id ...))))
               (let-values (((mid d exports) (modspec (syntax m) #t)))
                 (with-syntax ((d d)
                               (tmid (gen-mid mid))
                               ((other-id ...) exports))
                   (values mid
                           (syntax (begin ($module orig tmid ((new-id old-id) ... other-id ...) d (alias new-id old-id) ...)
                                          ($import orig import-only? tmid)))
                           (and exports? (syntax (new-id ... other-id ...)))))))
             ; base cases
              (mid
               (identifier? (syntax mid))
               (values (syntax mid)
                       (syntax ($import orig import-only? mid))
                       (and exports? ($module-exports (syntax mid) r))))
              ((mid)
               (identifier? (syntax mid))
               (values (syntax mid)
                       (syntax ($import orig import-only? mid))
                       (and exports? ($module-exports (syntax mid) r))))
            (_ (syntax-error m "invalid module specifier")))))
        (define modspec*
          (lambda (m)
            (let-values (((mid d exports) (modspec m #f))) d)))
        (syntax-case orig ()
          ((_ m ...)
           (with-syntax (((d ...) (map modspec* (syntax (m ...)))))
             (syntax (begin d ...))))))))

  (put-cte-hook 'import
    (lambda (orig)
      ($import-help orig #f)))

  (put-cte-hook 'import-only
    (lambda (orig)
      ($import-help orig #t)))
)

;;; To support eval-when, we maintain two mode sets:
;;;
;;; ctem (compile-time-expression mode)
;;;   determines whether/when to evaluate compile-time expressions such
;;;   as macro definitions, module definitions, and compile-time
;;;   registration of variable definitions
;;;
;;; rtem (run-time-expression mode)
;;;   determines whether/when to evaluate run-time expressions such
;;;   as the actual assignment performed by a variable definition or
;;;   arbitrary top-level expressions

;;; Possible modes in the mode set are:
;;;
;;; L (load): evaluate at load time.  implies V for compile-time
;;;     expressions and R for run-time expressions.
;;;
;;; C (compile): evaluate at compile (file) time
;;;
;;; E (eval): evaluate at evaluation (compile or interpret) time
;;;
;;; V (visit): evaluate at visit time
;;;
;;; R (revisit): evaluate at revisit time

;;; The mode set for the body of an eval-when is determined by
;;; translating each mode in the old mode set based on the situations
;;; present in the eval-when form and combining these into a set,
;;; using the following table.  See also update-mode-set.

;;;      load  compile  visit  revisit  eval
;;;
;;; L     L      C        V       R      -
;;;
;;; C     -      -        -       -      C
;;;
;;; V     V      C        V       -      -
;;;
;;; R     R      C        -       R      -
;;;
;;; E     -      -        -       -      E

;;; When we complete the expansion of a compile or run-time expression,
;;; the current ctem or rtem determines how the expression will be
;;; treated.  See ct-eval/residualize and rt-eval/residualize.

;;; Initial mode sets
;;;
;;; when compiling a file:
;;;
;;;     initial ctem: (L C)
;;;
;;;     initial rtem: (L)
;;;
;;; when not compiling a file:
;;;
;;;     initial ctem: (E)
;;;
;;;     initial rtem: (E)
;;;
;;;
;;; This means that top-level syntactic definitions are evaluated
;;; immediately after they are expanded, and the expanded definitions
;;; are also residualized into the object file if we are compiling
;;; a file.

(set! sc-expand
  (let ((ctem '(E)) (rtem '(E)))
    (lambda (x)
      (let ((env (interaction-environment)))
        (if (and (pair? x) (equal? (car x) noexpand))
            (cadr x)
            (chi-top* x null-env
              (env-wrap env)
              ctem rtem #f
              (env-top-ribcage env)))))))



(set! $make-environment
  (lambda (token mutable?)
    (let ((top-ribcage (make-top-ribcage token mutable?)))
      (make-env
        top-ribcage
        (make-wrap
          (wrap-marks top-wrap)
          (cons top-ribcage (wrap-subst top-wrap)))))))

(set! environment?
  (lambda (x)
    (env? x)))



(set! interaction-environment
  (let ((e ($make-environment '*top* #t)))
    (lambda () e)))

(set! identifier?
  (lambda (x)
    (nonsymbol-id? x)))

(set! datum->syntax-object
  (lambda (id datum)
    (arg-check nonsymbol-id? id 'datum->syntax-object)
    (make-syntax-object
      datum
      (syntax-object-wrap id))))

(set! syntax->list
  (lambda (orig-ls)
    (let f ((ls orig-ls))
      (syntax-case ls ()
        (() '())
        ((x . r) (cons #'x (f #'r)))
        (_ (error 'syntax->list "invalid argument ~s" orig-ls))))))

(set! syntax->vector
  (lambda (v)
    (syntax-case v ()
      (#(x ...) (apply vector (syntax->list #'(x ...))))
      (_ (error 'syntax->vector "invalid argument ~s" v)))))

(set! syntax-object->datum
  ; accepts any object, since syntax objects may consist partially
  ; or entirely of unwrapped, nonsymbolic data
  (lambda (x)
    (strip x empty-wrap)))

(set! generate-temporaries
  (let ((n 0))
    (lambda (ls)
      (arg-check list? ls 'generate-temporaries)
      (map (lambda (x)
             (set! n (+ n 1))
             (wrap
              ; unique name to distinguish from other temporaries
               (string->symbol (string-append "t" (number->string n)))
              ; unique mark (in tmp-wrap) to distinguish from non-temporaries
               tmp-wrap))
           ls))))

(set! free-identifier=?
   (lambda (x y)
      (arg-check nonsymbol-id? x 'free-identifier=?)
      (arg-check nonsymbol-id? y 'free-identifier=?)
      (free-id=? x y)))

(set! bound-identifier=?
   (lambda (x y)
      (arg-check nonsymbol-id? x 'bound-identifier=?)
      (arg-check nonsymbol-id? y 'bound-identifier=?)
      (bound-id=? x y)))

(set! literal-identifier=?
  (lambda (x y)
    (arg-check nonsymbol-id? x 'literal-identifier=?)
    (arg-check nonsymbol-id? y 'literal-identifier=?)
    (literal-id=? x y)))

(set! syntax-error
  (lambda (object . messages)
    (for-each (lambda (x) (arg-check string? x 'syntax-error)) messages)
    (let ((message (if (null? messages)
                       "invalid syntax"
                       (apply string-append messages))))
      (error-hook #f message (strip object empty-wrap)))))

;;; syntax-dispatch expects an expression and a pattern.  If the expression
;;; matches the pattern a list of the matching expressions for each
;;; "any" is returned.  Otherwise, #f is returned.  (This use of #f will
;;; not work on r4rs implementations that violate the ieee requirement
;;; that #f and () be distinct.)

;;; The expression is matched with the pattern as follows:

;;; p in pattern:                        matches:
;;;   ()                                 empty list
;;;   any                                anything
;;;   (p1 . p2)                          pair (list)
;;;   #(free-id <key>)                   <key> with literal-identifier=?
;;;   each-any                           any proper list
;;;   #(each p)                          (p*)
;;;   #(each+ p1 (p2_1 ...p2_n) p3)      (p1* (p2_n ... p2_1) . p3)
;;;   #(vector p)                        (list->vector p)
;;;   #(atom <object>)                   <object> with "equal?"

;;; Vector cops out to pair under assumption that vectors are rare.  If
;;; not, should convert to:
;;;   #(vector p)                        #(p*)

(let ()

(define match-each
  (lambda (e p w)
    (cond
      ((annotation? e)
       (match-each (annotation-expression e) p w))
      ((pair? e)
       (let ((first (match (car e) p w '())))
         (and first
              (let ((rest (match-each (cdr e) p w)))
                 (and rest (cons first rest))))))
      ((null? e) '())
      ((syntax-object? e)
       (match-each (syntax-object-expression e)
                   p
                   (join-wraps w (syntax-object-wrap e))))
      (else #f))))

(define match-each+
  (lambda (e x-pat y-pat z-pat w r)
    (let f ((e e) (w w))
      (cond
        ((pair? e)
         (let-values (((xr* y-pat r) (f (cdr e) w)))
           (if r
               (if (null? y-pat)
                   (let ((xr (match (car e) x-pat w '())))
                     (if xr
                         (values (cons xr xr*) y-pat r)
                         (values #f #f #f)))
                   (values '() (cdr y-pat) (match (car e) (car y-pat) w r)))
               (values #f #f #f))))
        ((annotation? e) (f (annotation-expression e) w))
        ((syntax-object? e) (f (syntax-object-expression e)
                               (join-wraps w (syntax-object-wrap e))))
        (else (values '() y-pat (match e z-pat w r)))))))

(define match-each-any
  (lambda (e w)
    (cond
      ((annotation? e)
       (match-each-any (annotation-expression e) w))
      ((pair? e)
       (let ((l (match-each-any (cdr e) w)))
         (and l (cons (wrap (car e) w) l))))
      ((null? e) '())
      ((syntax-object? e)
       (match-each-any (syntax-object-expression e)
                       (join-wraps w (syntax-object-wrap e))))
      (else #f))))

(define match-empty
  (lambda (p r)
    (cond
      ((null? p) r)
      ((eq? p 'any) (cons '() r))
      ((pair? p) (match-empty (car p) (match-empty (cdr p) r)))
      ((eq? p 'each-any) (cons '() r))
      (else
       (case (vector-ref p 0)
         ((each) (match-empty (vector-ref p 1) r))
         ((each+) (match-empty (vector-ref p 1)
                    (match-empty (reverse (vector-ref p 2))
                      (match-empty (vector-ref p 3) r))))
         ((free-id atom) r)
         ((vector) (match-empty (vector-ref p 1) r)))))))

(define combine
  (lambda (r* r)
    (if (null? (car r*))
        r
        (cons (map car r*) (combine (map cdr r*) r)))))

(define match*
  (lambda (e p w r)
    (cond
      ((null? p) (and (null? e) r))
      ((pair? p)
       (and (pair? e) (match (car e) (car p) w
                        (match (cdr e) (cdr p) w r))))
      ((eq? p 'each-any)
       (let ((l (match-each-any e w))) (and l (cons l r))))
      (else
       (case (vector-ref p 0)
         ((each)
          (if (null? e)
              (match-empty (vector-ref p 1) r)
              (let ((r* (match-each e (vector-ref p 1) w)))
                (and r* (combine r* r)))))
         ((free-id) (and (id? e) (literal-id=? (wrap e w) (vector-ref p 1)) r))
         ((each+)
          (let-values (((xr* y-pat r)
                        (match-each+ e (vector-ref p 1) (vector-ref p 2)
                          (vector-ref p 3) w r)))
            (and r (null? y-pat)
              (if (null? xr*)
                  (match-empty (vector-ref p 1) r)
                  (combine xr* r)))))
         ((atom) (and (equal? (vector-ref p 1) (strip e w)) r))
         ((vector)
          (and (vector? e)
               (match (vector->list e) (vector-ref p 1) w r))))))))

(define match
  (lambda (e p w r)
    (cond
      ((not r) #f)
      ((eq? p 'any) (cons (wrap e w) r))
      ((syntax-object? e)
       (match*
         (unannotate (syntax-object-expression e))
         p
         (join-wraps w (syntax-object-wrap e))
         r))
      (else (match* (unannotate e) p w r)))))

(set! $syntax-dispatch
  (lambda (e p)
    (cond
      ((eq? p 'any) (list e))
      ((syntax-object? e)
       (match* (unannotate (syntax-object-expression e))
         p (syntax-object-wrap e) '()))
      (else (match* (unannotate e) p empty-wrap '())))))
))


(define-syntax with-syntax
   (lambda (x)
      (syntax-case x ()
         ((_ () e1 e2 ...)
          (syntax (begin e1 e2 ...)))
         ((_ ((out in)) e1 e2 ...)
          (syntax (syntax-case in () (out (begin e1 e2 ...)))))
         ((_ ((out in) ...) e1 e2 ...)
          (syntax (syntax-case (list in ...) ()
                     ((out ...) (begin e1 e2 ...))))))))

(define-syntax with-implicit
  (syntax-rules ()
    ((_ (tid id ...) e1 e2 ...)
     (andmap identifier? (syntax (tid id ...)))
     (begin
       (unless (identifier? (syntax tid))
         (syntax-error (syntax tid) "non-identifier with-implicit template"))
       (with-syntax ((id (datum->syntax-object (syntax tid) 'id)) ...)
         e1 e2 ...)))))

(define-syntax datum
  (syntax-rules ()
    ((_ x) (syntax-object->datum (syntax x)))))

(define-syntax syntax-rules
  (lambda (x)
    (define clause
      (lambda (y)
        (syntax-case y ()
          (((keyword . pattern) template)
           (syntax ((dummy . pattern) (syntax template))))
          (((keyword . pattern) fender template)
           (syntax ((dummy . pattern) fender (syntax template))))
          (_ (syntax-error x)))))
    (syntax-case x ()
      ((_ (k ...) cl ...)
       (andmap identifier? (syntax (k ...)))
       (with-syntax (((cl ...) (map clause (syntax (cl ...)))))
         (syntax (lambda (x) (syntax-case x (k ...) cl ...))))))))

(define-syntax or
   (lambda (x)
      (syntax-case x ()
         ((_) (syntax #f))
         ((_ e) (syntax e))
         ((_ e1 e2 e3 ...)
          (syntax (let ((t e1)) (if t t (or e2 e3 ...))))))))

(define-syntax and
   (lambda (x)
      (syntax-case x ()
         ((_ e1 e2 e3 ...) (syntax (if e1 (and e2 e3 ...) #f)))
         ((_ e) (syntax e))
         ((_) (syntax #t)))))

(define-syntax let
   (lambda (x)
      (syntax-case x ()
         ((_ ((x v) ...) e1 e2 ...)
          (andmap identifier? (syntax (x ...)))
          (syntax ((lambda (x ...) e1 e2 ...) v ...)))
         ((_ f ((x v) ...) e1 e2 ...)
          (andmap identifier? (syntax (f x ...)))
          (syntax ((letrec ((f (lambda (x ...) e1 e2 ...))) f)
                    v ...))))))

(define-syntax let*
  (lambda (x)
    (syntax-case x ()
      ((let* ((x v) ...) e1 e2 ...)
       (andmap identifier? (syntax (x ...)))
       (let f ((bindings (syntax ((x v)  ...))))
         (if (null? bindings)
             (syntax (let () e1 e2 ...))
             (with-syntax ((body (f (cdr bindings)))
                           (binding (car bindings)))
               (syntax (let (binding) body)))))))))

(define-syntax cond
  (lambda (x)
    (syntax-case x ()
      ((_ m1 m2 ...)
       (let f ((clause (syntax m1)) (clauses (syntax (m2 ...))))
         (if (null? clauses)
             (syntax-case clause (else =>)
               ((else e1 e2 ...) (syntax (begin e1 e2 ...)))
               ((e0) (syntax (let ((t e0)) (if t t))))
               ((e0 => e1) (syntax (let ((t e0)) (if t (e1 t)))))
               ((e0 e1 e2 ...) (syntax (if e0 (begin e1 e2 ...))))
               (_ (syntax-error x)))
             (with-syntax ((rest (f (car clauses) (cdr clauses))))
               (syntax-case clause (else =>)
                 ((e0) (syntax (let ((t e0)) (if t t rest))))
                 ((e0 => e1) (syntax (let ((t e0)) (if t (e1 t) rest))))
                 ((e0 e1 e2 ...) (syntax (if e0 (begin e1 e2 ...) rest)))
                 (_ (syntax-error x))))))))))

(define-syntax do
   (lambda (orig-x)
      (syntax-case orig-x ()
         ((_ ((var init . step) ...) (e0 e1 ...) c ...)
          (with-syntax (((step ...)
                         (map (lambda (v s)
                                 (syntax-case s ()
                                    (() v)
                                    ((e) (syntax e))
                                    (_ (syntax-error orig-x))))
                              (syntax (var ...))
                              (syntax (step ...)))))
             (syntax-case (syntax (e1 ...)) ()
                (() (syntax (let do ((var init) ...)
                               (if (not e0)
                                   (begin c ... (do step ...))))))
                ((e1 e2 ...)
                 (syntax (let do ((var init) ...)
                            (if e0
                                (begin e1 e2 ...)
                                (begin c ... (do step ...))))))))))))

(define-syntax quasiquote
  (let ()
    (define (quasi p lev)
      (syntax-case p (unquote quasiquote)
        ((unquote p)
         (if (= lev 0)
             #'("value" p)
             (quasicons #'("quote" unquote) (quasi #'(p) (- lev 1)))))
        ((quasiquote p) (quasicons #'("quote" quasiquote) (quasi #'(p) (+ lev 1))))
        ((p . q)
         (syntax-case #'p (unquote unquote-splicing)
           ((unquote p ...)
            (if (= lev 0)
                (quasilist* #'(("value" p) ...) (quasi #'q lev))
                (quasicons
                  (quasicons #'("quote" unquote) (quasi #'(p ...) (- lev 1)))
                  (quasi #'q lev))))
           ((unquote-splicing p ...)
            (if (= lev 0)
                (quasiappend #'(("value" p) ...) (quasi #'q lev))
                (quasicons
                  (quasicons #'("quote" unquote-splicing) (quasi #'(p ...) (- lev 1)))
                  (quasi #'q lev))))
           (_ (quasicons (quasi #'p lev) (quasi #'q lev)))))
        (#(x ...) (quasivector (vquasi #'(x ...) lev)))
        (p #'("quote" p))))
    (define (vquasi p lev)
      (syntax-case p ()
        ((p . q)
         (syntax-case #'p (unquote unquote-splicing)
           ((unquote p ...)
            (if (= lev 0)
                (quasilist* #'(("value" p) ...) (vquasi #'q lev))
                (quasicons
                  (quasicons #'("quote" unquote) (quasi #'(p ...) (- lev 1)))
                  (vquasi #'q lev))))
           ((unquote-splicing p ...)
            (if (= lev 0)
                (quasiappend #'(("value" p) ...) (vquasi #'q lev))
                (quasicons
                  (quasicons
                    #'("quote" unquote-splicing)
                    (quasi #'(p ...) (- lev 1)))
                  (vquasi #'q lev))))
           (_ (quasicons (quasi #'p lev) (vquasi #'q lev)))))
        (() #'("quote" ()))))
    (define (quasicons x y)
      (with-syntax ((x x) (y y))
        (syntax-case #'y ()
          (("quote" dy)
           (syntax-case #'x ()
             (("quote" dx) #'("quote" (dx . dy)))
             (_ (if (null? #'dy) #'("list" x) #'("list*" x y)))))
          (("list" . stuff) #'("list" x . stuff))
          (("list*" . stuff) #'("list*" x . stuff))
          (_ #'("list*" x y)))))
    (define (quasiappend x y)
      (syntax-case y ()
        (("quote" ())
         (cond
           ((null? x) #'("quote" ()))
           ((null? (cdr x)) (car x))
           (else (with-syntax (((p ...) x)) #'("append" p ...)))))
        (_
         (cond
           ((null? x) y)
           (else (with-syntax (((p ...) x) (y y)) #'("append" p ... y)))))))
    (define (quasilist* x y)
      (let f ((x x))
        (if (null? x)
            y
            (quasicons (car x) (f (cdr x))))))
    (define (quasivector x)
      (syntax-case x ()
        (("quote" (x ...)) #'("quote" #(x ...)))
        (_
         (let f ((y x) (k (lambda (ls) #`("vector" #,@ls))))
           (syntax-case y ()
             (("quote" (y ...)) (k #'(("quote" y) ...)))
             (("list" y ...) (k #'(y ...)))
             (("list*" y ... z) (f #'z (lambda (ls) (k (append #'(y ...) ls)))))
             (else #`("list->vector" #,x)))))))
    (define (emit x)
      (syntax-case x ()
        (("quote" x) #''x)
        (("list" x ...) #`(list #,@(map emit #'(x ...))))
      ; could emit list* for 3+ arguments if implementation supports list*
       (("list*" x ... y)
        (let f ((x* #'(x ...)))
          (if (null? x*)
              (emit #'y)
              #`(cons #,(emit (car x*)) #,(f (cdr x*))))))
        (("append" x ...) #`(append #,@(map emit #'(x ...))))
        (("vector" x ...) #`(vector #,@(map emit #'(x ...))))
        (("list->vector" x) #`(list->vector #,(emit #'x)))
        (("value" x) #'x)))
    (lambda (x)
      (syntax-case x ()
       ; convert to intermediate language, combining introduced (but not
       ; unquoted source) quote expressions where possible and choosing
       ; optimal construction code otherwise, then emit Scheme code
       ; corresponding to the intermediate language forms.
        ((_ e) (emit (quasi #'e 0)))))))

(define-syntax unquote
  (lambda (x)
    (syntax-error x "misplaced")))

(define-syntax unquote-splicing
  (lambda (x)
    (syntax-error x "misplaced")))

(define-syntax quasisyntax
  (lambda (x)
    (define (qs q n b* k)
      (syntax-case q (quasisyntax unsyntax unsyntax-splicing)
        ((quasisyntax . d)
         (qs #'d (+ n 1) b*
           (lambda (b* dnew)
             (k b*
                (if (eq? dnew #'d)
                    q
                    (with-syntax ((d dnew)) #'(quasisyntax . d)))))))
        ((unsyntax . d)
         (not (= n 0))
         (qs #'d (- n 1) b*
           (lambda (b* dnew)
             (k b*
                (if (eq? dnew #'d)
                    q
                    (with-syntax ((d dnew)) #'(unsyntax . d)))))))
        ((unsyntax-splicing . d)
         (not (= n 0))
         (qs #'d (- n 1) b*
           (lambda (b* dnew)
             (k b*
                (if (eq? dnew #'d)
                    q
                    (with-syntax ((d dnew)) #'(unsyntax-splicing . d)))))))
        ((unsyntax q)
         (= n 0)
         (with-syntax (((t) (generate-temporaries #'(q))))
           (k (cons #'(t q) b*) #'t)))
        (((unsyntax q ...) . d)
         (= n 0)
         (qs #'d n b*
           (lambda (b* dnew)
             (with-syntax (((t ...) (generate-temporaries #'(q ...))))
               (k (append #'((t q) ...) b*)
                  (with-syntax ((d dnew)) #'(t ... . d)))))))
        (((unsyntax-splicing q ...) . d)
         (= n 0)
         (qs #'d n b*
           (lambda (b* dnew)
             (with-syntax (((t ...) (generate-temporaries #'(q ...))))
               (k (append #'(((t (... ...)) q) ...) b*)
                  (with-syntax ((((m ...) ...) #'((t (... ...)) ...)))
                    (with-syntax ((d dnew)) #'(m ... ... . d))))))))
        ((a . d)
         (qs #'a n b*
           (lambda (b* anew)
             (qs #'d n b*
               (lambda (b* dnew)
                 (k b*
                    (if (and (eq? anew #'a) (eq? dnew #'d))
                        q
                        (with-syntax ((a anew) (d dnew)) #'(a . d)))))))))
        (#(x ...)
         (vqs #'(x ...) n b*
           (lambda (b* xnew*)
             (k b*
                (if (let same? ((x* #'(x ...)) (xnew* xnew*))
                      (if (null? x*)
                          (null? xnew*)
                          (and (not (null? xnew*))
                               (eq? (car x*) (car xnew*))
                               (same? (cdr x*) (cdr xnew*)))))
                    q
                    (with-syntax (((x ...) xnew*)) #'#(x ...)))))))
        (_ (k b* q))))
    (define (vqs x* n b* k)
      (if (null? x*)
          (k b* '())
          (vqs (cdr x*) n b*
            (lambda (b* xnew*)
              (syntax-case (car x*) (unsyntax unsyntax-splicing)
                ((unsyntax q ...)
                 (= n 0)
                 (with-syntax (((t ...) (generate-temporaries #'(q ...))))
                   (k (append #'((t q) ...) b*)
                      (append #'(t ...) xnew*))))
                ((unsyntax-splicing q ...)
                 (= n 0)
                 (with-syntax (((t ...) (generate-temporaries #'(q ...))))
                   (k (append #'(((t (... ...)) q) ...) b*)
                      (with-syntax ((((m ...) ...) #'((t (... ...)) ...)))
                        (append #'(m ... ...) xnew*)))))
                (_ (qs (car x*) n b*
                     (lambda (b* xnew)
                       (k b* (cons xnew xnew*))))))))))
    (syntax-case x ()
      ((_ x)
       (qs #'x 0 '()
         (lambda (b* xnew)
           (if (eq? xnew #'x)
               #'(syntax x)
               (with-syntax (((b ...) b*) (x xnew))
                 #'(with-syntax (b ...) (syntax x))))))))))

(define-syntax unsyntax
  (lambda (x)
    (syntax-error x "misplaced")))

(define-syntax unsyntax-splicing
  (lambda (x)
    (syntax-error x "misplaced")))

(define-syntax include
  (lambda (x)
    (define read-file
      (lambda (fn k)
        (let ((p (open-input-file fn)))
          (let f ()
            (let ((x (read p)))
              (if (eof-object? x)
                  (begin (close-input-port p) '())
                  (cons (datum->syntax-object k x) (f))))))))
    (syntax-case x ()
      ((k filename)
       (let ((fn (syntax-object->datum (syntax filename))))
         (with-syntax (((exp ...) (read-file fn (syntax k))))
           (syntax (begin exp ...))))))))

(define-syntax case
  (lambda (x)
    (syntax-case x ()
      ((_ e m1 m2 ...)
       (with-syntax
         ((body (let f ((clause (syntax m1)) (clauses (syntax (m2 ...))))
                  (if (null? clauses)
                      (syntax-case clause (else)
                        ((else e1 e2 ...) (syntax (begin e1 e2 ...)))
                        (((k ...) e1 e2 ...)
                         (syntax (if (memv t '(k ...)) (begin e1 e2 ...))))
                        (_ (syntax-error x)))
                      (with-syntax ((rest (f (car clauses) (cdr clauses))))
                        (syntax-case clause (else)
                          (((k ...) e1 e2 ...)
                           (syntax (if (memv t '(k ...))
                                       (begin e1 e2 ...)
                                       rest)))
                          (_ (syntax-error x))))))))
         (syntax (let ((t e)) body)))))))

(define-syntax identifier-syntax
  (syntax-rules (set!)
    ((_ e)
     (lambda (x)
       (syntax-case x ()
         (id (identifier? (syntax id)) (syntax e))
         ((_ x (... ...)) (syntax (e x (... ...)))))))
    ((_ (id exp1) ((set! var val) exp2))
     (and (identifier? (syntax id)) (identifier? (syntax var)))
     (cons 'macro!
       (lambda (x)
         (syntax-case x (set!)
           ((set! var val) (syntax exp2))
           ((id x (... ...)) (syntax (exp1 x (... ...))))
           (id (identifier? (syntax id)) (syntax exp1))))))))