1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-29 19:30:36 +02:00
guile/module/ice-9/psyntax.scm

3196 lines
132 KiB
Scheme
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;;; -*-scheme-*-
;;;;
;;;; Copyright (C) 1997-1998,2000-2003,2005-2006,2008-2013,2015-2022,2024
;;;; Free Software Foundation, Inc.
;;;;
;;;; This library is free software: you can redistribute it and/or modify
;;;; it under the terms of the GNU Lesser General Public License as
;;;; published by the Free Software Foundation, either version 3 of the
;;;; License, or (at your option) any later version.
;;;;
;;;; This library is distributed in the hope that it will be useful, but
;;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;;; Lesser General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU Lesser General Public
;;;; License along with this program. If not, see
;;;; <http://www.gnu.org/licenses/>.
;;; Originally extracted from Chez Scheme Version 5.9f
;;; Authors: R. Kent Dybvig, Oscar Waddell, Bob Hieb, Carl Bruggeman
;;; Copyright (c) 1992-1997 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.
;;; This code is based on "Syntax Abstraction in Scheme"
;;; by R. Kent Dybvig, Robert Hieb, and Carl Bruggeman.
;;; Lisp and Symbolic Computation 5:4, 295-326, 1992.
;;; <http://www.cs.indiana.edu/~dyb/pubs/LaSC-5-4-pp295-326.pdf>
;;; This file defines Guile's syntax expander and a set of associated
;;; syntactic forms and procedures. For more documentation, see The
;;; Scheme Programming Language, Fourth Edition (R. Kent Dybvig, MIT
;;; Press, 2009), or the R6RS.
;;; This file is shipped along with an expanded version of itself,
;;; psyntax-pp.scm, which is loaded when psyntax.scm has not yet been
;;; compiled. In this way, psyntax bootstraps off of an expanded
;;; version of itself.
;;; Implementation notes:
;;; Objects with no standard print syntax, including objects containing
;;; cycles and syntax object, are allowed in quoted data as long as they
;;; are contained within a syntax form or produced by datum->syntax.
;;; Such objects are never copied.
;;; All identifiers that don't have macro definitions and are not bound
;;; lexically are assumed to be global variables.
;;; Top-level definitions of macro-introduced identifiers are allowed.
;;; When changing syntax representations, it is necessary to support
;;; both old and new syntax representations in id-var-name. It
;;; should be sufficient to recognize old representations and treat
;;; them as not lexically bound.
(eval-when (compile)
(set-current-module (resolve-module '(guile))))
(let ((syntax? (module-ref (current-module) 'syntax?))
(make-syntax (module-ref (current-module) 'make-syntax))
(syntax-expression (module-ref (current-module) 'syntax-expression))
(syntax-wrap (module-ref (current-module) 'syntax-wrap))
(syntax-module (module-ref (current-module) 'syntax-module))
(syntax-sourcev (module-ref (current-module) 'syntax-sourcev)))
(define-syntax define-expansion-constructors
(lambda (x)
(syntax-case x ()
((_)
(let lp ((n 0) (out '()))
(if (< n (vector-length %expanded-vtables))
(lp (1+ n)
(let* ((vtable (vector-ref %expanded-vtables n))
(stem (struct-ref vtable (+ vtable-offset-user 0)))
(fields (struct-ref vtable (+ vtable-offset-user 2)))
(sfields (map (lambda (f) (datum->syntax x f)) fields))
(ctor (datum->syntax x (symbol-append 'make- stem))))
(cons #`(define (#,ctor #,@sfields)
(make-struct/simple
(vector-ref %expanded-vtables #,n)
#,@sfields))
out)))
#`(begin #,@(reverse out))))))))
(define-syntax define-expansion-accessors
(lambda (x)
(syntax-case x ()
((_ stem field ...)
(let ((stem (syntax->datum #'stem))
(fields (map syntax->datum #'(field ...))))
(let lp ((n 0))
(let ((vtable (vector-ref %expanded-vtables n)))
(if (eq? (struct-ref vtable (+ vtable-offset-user 0)) stem)
(let ((pred (datum->syntax x (symbol-append stem '?)))
(all-fields (struct-ref vtable (+ vtable-offset-user 2))))
#`(begin
(define (#,pred x)
(and (struct? x)
(eq? (struct-vtable x)
(vector-ref %expanded-vtables #,n))))
#,@(map
(lambda (f)
(define get
(datum->syntax x (symbol-append stem '- f)))
(define idx
(list-index all-fields f))
#`(define (#,get x)
(struct-ref x #,idx)))
fields)))
(lp (1+ n))))))))))
(define-expansion-constructors)
(define-expansion-accessors lambda src meta body)
;; A simple pattern matcher based on Oleg Kiselyov's pmatch.
(define-syntax-rule (simple-match e cs ...)
(let ((v e)) (simple-match-1 v cs ...)))
(define-syntax simple-match-1
(syntax-rules ()
((_ v) (error "value failed to match" v))
((_ v (pat e0 e ...) cs ...)
(let ((fk (lambda () (simple-match-1 v cs ...))))
(simple-match-pat v pat (let () e0 e ...) (fk))))))
(define-syntax simple-match-patv
(syntax-rules ()
((_ v idx () kt kf) kt)
((_ v idx (x . y) kt kf)
(simple-match-pat (vector-ref v idx) x
(simple-match-patv v (1+ idx) y kt kf)
kf))))
(define-syntax simple-match-pat
(syntax-rules (_ quote unquote ? and or not)
((_ v _ kt kf) kt)
((_ v () kt kf) (if (null? v) kt kf))
((_ v #t kt kf) (if (eq? v #t) kt kf))
((_ v #f kt kf) (if (eq? v #f) kt kf))
((_ v (and) kt kf) kt)
((_ v (and x . y) kt kf)
(simple-match-pat v x (simple-match-pat v (and . y) kt kf) kf))
((_ v (or) kt kf) kf)
((_ v (or x . y) kt kf)
(let ((tk (lambda () kt)))
(simple-match-pat v x (tk) (simple-match-pat v (or . y) (tk) kf))))
((_ v (not pat) kt kf) (simple-match-pat v pat kf kt))
((_ v (quote lit) kt kf)
(if (eq? v (quote lit)) kt kf))
((_ v (? proc) kt kf) (simple-match-pat v (? proc _) kt kf))
((_ v (? proc pat) kt kf)
(if (proc v) (simple-match-pat v pat kt kf) kf))
((_ v (x . y) kt kf)
(if (pair? v)
(let ((vx (car v)) (vy (cdr v)))
(simple-match-pat vx x (simple-match-pat vy y kt kf) kf))
kf))
((_ v #(x ...) kt kf)
(if (and (vector? v)
(eq? (vector-length v) (length '(x ...))))
(simple-match-patv v 0 (x ...) kt kf)
kf))
((_ v var kt kf) (let ((var v)) kt))))
(define-syntax-rule (match e cs ...) (simple-match e cs ...))
(define (top-level-eval x mod)
(primitive-eval x))
(define (local-eval x mod)
(primitive-eval x))
(define (global-extend type sym val)
(module-define! (current-module) sym
(make-syntax-transformer sym type val)))
(define (sourcev-filename s) (vector-ref s 0))
(define (sourcev-line s) (vector-ref s 1))
(define (sourcev-column s) (vector-ref s 2))
(define (sourcev->alist sourcev)
(define (maybe-acons k v tail) (if v (acons k v tail) tail))
(and sourcev
(maybe-acons 'filename (sourcev-filename sourcev)
`((line . ,(sourcev-line sourcev))
(column . ,(sourcev-column sourcev))))))
(define (maybe-name-value name val)
(if (lambda? val)
(let ((meta (lambda-meta val)))
(if (assq 'name meta)
val
(make-lambda (lambda-src val)
(acons 'name name meta)
(lambda-body val))))
val))
;; output constructors
(define build-void make-void)
(define build-call make-call)
(define build-conditional make-conditional)
(define build-lexical-reference make-lexical-ref)
(define (build-lexical-assignment src name var exp)
(make-lexical-set src name var (maybe-name-value name exp)))
(define (analyze-variable mod var modref-cont bare-cont)
(match mod
(#f (bare-cont #f var))
(('public . mod) (modref-cont mod var #t))
(((or 'private 'hygiene) . mod)
(if (equal? mod (module-name (current-module)))
(bare-cont mod var)
(modref-cont mod var #f)))
(('primitive . _)
(syntax-violation #f "primitive not in operator position" var))))
(define (build-global-reference src var mod)
(analyze-variable
mod var
(lambda (mod var public?)
(make-module-ref src mod var public?))
(lambda (mod var)
(make-toplevel-ref src mod var))))
(define (build-global-assignment src var exp mod)
(let ((exp (maybe-name-value var exp)))
(analyze-variable
mod var
(lambda (mod var public?)
(make-module-set src mod var public? exp))
(lambda (mod var)
(make-toplevel-set src mod var exp)))))
(define (build-global-definition src mod var exp)
(make-toplevel-define src (and mod (cdr mod)) var
(maybe-name-value var exp)))
(define (build-simple-lambda src req rest vars meta exp)
(make-lambda src meta
(make-lambda-case
;; src req opt rest kw inits vars body else
src req #f rest #f '() vars exp #f)))
(define build-case-lambda make-lambda)
(define build-lambda-case make-lambda-case)
(define build-primcall make-primcall)
(define build-primref make-primitive-ref)
(define build-data make-const)
(define (build-sequence src exps)
(match exps
((tail) tail)
((head . tail)
(make-seq src head (build-sequence #f tail)))))
(define (build-let src ids vars val-exps body-exp)
(match (map maybe-name-value ids val-exps)
(() body-exp)
(val-exps (make-let src ids vars val-exps body-exp))))
(define (build-named-let src ids vars val-exps body-exp)
(match vars
((f . vars)
(match ids
((f-name . ids)
(let ((proc (build-simple-lambda src ids #f vars '() body-exp)))
(make-letrec
src #f
(list f-name) (list f) (list (maybe-name-value f-name proc))
(build-call src (build-lexical-reference src f-name f)
(map maybe-name-value ids val-exps)))))))))
(define (build-letrec src in-order? ids vars val-exps body-exp)
(match (map maybe-name-value ids val-exps)
(() body-exp)
(val-exps (make-letrec src in-order? ids vars val-exps body-exp))))
(define (gen-lexical id)
;; Generate a unique symbol for a lexical variable. These need to
;; be symbols as they are embedded in Tree-IL. Lexicals from
;; different separately compiled modules can coexist, for example
;; if a macro defined in module A is used in a separately-compiled
;; module B, so they do need to be unique. However we assume that
;; generally a module corresponds to a compilation unit, so there
;; is no need to be unique across separately-compiled instances of
;; the same module, and that therefore we can use a deterministic
;; per-module counter instead of the global counter of 'gensym' so
;; that the generated identifier is reproducible.
(module-gensym (symbol->string id)))
(define no-source #f)
(define (datum-sourcev datum)
(let ((props (source-properties datum)))
(and (pair? props)
(vector (assq-ref props 'filename)
(assq-ref props 'line)
(assq-ref props 'column)))))
(define (source-annotation x)
(if (syntax? x)
(syntax-sourcev x)
(datum-sourcev x)))
(define-syntax-rule (arg-check pred? e who)
(let ((x e))
(unless (pred? x) (syntax-violation 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
;; Guile's module system 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
;; resolve-identifier when it finds no other bindings.
;; <environment> ::= ((<label> . <binding>)*)
;; identifier bindings include a type and a value
;; <binding> ::= (macro . <procedure>) macros
;; (syntax-parameter . <procedure>) syntax parameters
;; (core . <procedure>) core forms
;; (module-ref . <procedure>) @ or @@
;; (begin) begin
;; (define) define
;; (define-syntax) define-syntax
;; (define-syntax-parameter) define-syntax-parameter
;; (local-syntax . rec?) let-syntax/letrec-syntax
;; (eval-when) eval-when
;; (syntax . (<var> . <level>)) pattern variables
;; (global) assumed global variable
;; (lexical . <var>) lexical variables
;; (ellipsis . <identifier>) custom ellipsis
;; (displaced-lexical) displaced lexicals
;; <level> ::= <non-negative integer>
;; <var> ::= symbol returned by gen-lexical
;; a macro is a user-defined syntactic-form. a core is a
;; system-defined syntactic form. begin, define, define-syntax,
;; define-syntax-parameter, and eval-when are treated specially
;; since they are sensitive to whether the form is at top-level and
;; (except for eval-when) can denote valid internal definitions.
;; a pattern variable is a variable introduced by syntax-case and can
;; be referenced only within a syntax form.
;; any identifier for which no top-level syntax definition or local
;; binding of any kind has been seen is assumed to be a global
;; variable.
;; a lexical variable is a lambda- or letrec-bound variable.
;; an ellipsis binding is introduced by the 'with-ellipsis' special
;; form.
;; a displaced-lexical identifier is a lexical identifier removed from
;; its 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-syntax make-binding
(syntax-rules (quote)
((_ type value) (cons type value))
((_ 'type) '(type))
((_ type) (cons type '()))))
(define (binding-type x) (car x))
(define (binding-value x) (cdr x))
(define null-env '())
(define (extend-env labels bindings r)
(match labels
(() r)
((label . labels)
(match bindings
((binding . bindings)
(extend-env labels bindings (acons label binding r)))))))
(define (extend-var-env labels vars r)
;; variant of extend-env that forms "lexical" binding
(match labels
(() r)
((label . labels)
(match vars
((var . vars)
(extend-var-env labels vars
(acons label (make-binding 'lexical var) r)))))))
;; we use a "macros only" environment in expansion of local macro
;; definitions so that their definitions can use local macros without
;; attempting to use other lexical identifiers.
(define (macros-only-env r)
(match r
(() '())
((a . r)
(match a
((k . ((or 'macro 'syntax-parameter 'ellipsis) . _))
(cons a (macros-only-env r)))
(_
(macros-only-env r))))))
;; 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? x)
(and (syntax? x)
(symbol? (syntax-expression x))))
(define (id? x)
(cond
((symbol? x) #t)
((syntax? x) (symbol? (syntax-expression x)))
(else #f)))
(define (id-sym-name x)
(if (syntax? x)
(syntax-expression x)
x))
(define (id-sym-name&marks x w)
(if (syntax? x)
(values
(syntax-expression x)
(join-marks (wrap-marks w) (wrap-marks (syntax-wrap x))))
(values x (wrap-marks w))))
;; syntax object wraps
;; <wrap> ::= ((<mark> ...) . (<subst> ...))
;; <subst> ::= shift | <subs>
;; <subs> ::= #(ribcage #(<sym> ...) #(<mark> ...) #(<label> ...))
;; | #(ribcage (<sym> ...) (<mark> ...) (<label> ...))
(define (make-wrap marks subst) (cons marks subst))
(define (wrap-marks wrap) (car wrap))
(define (wrap-subst wrap) (cdr wrap))
(define* (gen-unique #:optional (module (current-module)))
;; Generate a unique value, used as a mark to identify a scope, or
;; as a label to associate an identifier with a lexical. They
;; need to be readable and writable, and because of they way they
;; are used as labels and marks, distinct from pairs, syntax, and
;; the symbol `top'. Unique values from different separately
;; compiled modules can coexist, for example if a macro defined in
;; module A is used in a separately-compiled module B; however we
;; assume that generally a module corresponds to a compilation
;; unit, so there is no need to be unique across
;; separately-compiled instances of the same module, and that
;; therefore we can use a deterministic per-module counter instead
;; of, say, a random number of a long enough length.
(if module
(vector (module-name module) (module-generate-unique-id! module))
(vector '(guile) (gensym "id"))))
;; labels must be comparable with "eq?", have read-write invariance,
;; and distinct from symbols. Pair labels are used for top-level
;; definition placeholders. These labels are used for proper
;; lexicals.
(define (gen-label)
(gen-unique))
(define (gen-labels ls)
(match ls
(() '())
((_ . ls) (cons (gen-label) (gen-labels ls)))))
(define (make-ribcage symnames marks labels)
(vector 'ribcage symnames marks labels))
(define (ribcage-symnames ribcage) (vector-ref ribcage 1))
(define (ribcage-marks ribcage) (vector-ref ribcage 2))
(define (ribcage-labels ribcage) (vector-ref ribcage 3))
(define (set-ribcage-symnames! ribcage x) (vector-set! ribcage 1 x))
(define (set-ribcage-marks! ribcage x) (vector-set! ribcage 2 x))
(define (set-ribcage-labels! ribcage x) (vector-set! ribcage 3 x))
(define empty-wrap '(()))
(define top-wrap '((top)))
;; 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 the-anti-mark #f)
(define (anti-mark w)
(make-wrap (cons the-anti-mark (wrap-marks w))
(cons 'shift (wrap-subst w))))
(define (new-mark)
(gen-unique))
;; make-empty-ribcage and extend-ribcage maintain list-based ribcages for
;; internal definitions, in which the ribcages are built incrementally
(define (make-empty-ribcage)
(make-ribcage '() '() '()))
(define (extend-ribcage! ribcage id label)
;; must receive ids with complete wraps
(set-ribcage-symnames! ribcage
(cons (syntax-expression id)
(ribcage-symnames ribcage)))
(set-ribcage-marks! ribcage
(cons (wrap-marks (syntax-wrap id))
(ribcage-marks ribcage)))
(set-ribcage-labels! ribcage
(cons label (ribcage-labels ribcage))))
;; make-binding-wrap creates vector-based ribcages
(define (make-binding-wrap ids labels w)
(match ids
(() w)
((_ . _)
(make-wrap
(wrap-marks w)
(cons
(let* ((labelvec (list->vector labels))
(n (vector-length labelvec))
(symnamevec (make-vector n))
(marksvec (make-vector n)))
(let f ((ids ids) (i 0))
(match ids
(()
(make-ribcage symnamevec marksvec labelvec))
((id . ids)
(call-with-values
(lambda () (id-sym-name&marks id w))
(lambda (symname marks)
(vector-set! symnamevec i symname)
(vector-set! marksvec i marks)
(f ids (1+ i))))))))
(wrap-subst w))))))
(define (smart-append m1 m2)
(if (null? m2)
m1
(append m1 m2)))
(define (join-wraps w1 w2)
(let ((m1 (wrap-marks w1)) (s1 (wrap-subst w1)))
(if (null? m1)
(if (null? s1)
w2
(make-wrap
(wrap-marks w2)
(smart-append s1 (wrap-subst w2))))
(make-wrap
(smart-append m1 (wrap-marks w2))
(smart-append s1 (wrap-subst w2))))))
(define (join-marks m1 m2)
(smart-append m1 m2))
(define (same-marks? x y)
(or (eq? x y)
(and (not (null? x))
(not (null? y))
(eq? (car x) (car y))
(same-marks? (cdr x) (cdr y)))))
(define (id-var-name id w mod)
;; Syntax objects use wraps to associate names with marked
;; identifiers. This function returns the name corresponding to
;; the given identifier and wrap, or the original identifier if no
;; corresponding name was found.
;;
;; The name may be a string created by gen-label, indicating a
;; lexical binding, or another syntax object, indicating a
;; reference to a top-level definition created during a previous
;; macroexpansion.
;;
;; For lexical variables, finding a label simply amounts to
;; looking for an entry with the same symbolic name and the same
;; marks. Finding a toplevel definition is the same, except we
;; also have to compare modules, hence the `mod' parameter.
;; Instead of adding a separate entry in the ribcage for modules,
;; which wouldn't be used for lexicals, we arrange for the entry
;; for the name entry to be a pair with the module in its car, and
;; the name itself in the cdr. So if the name that we find is a
;; pair, we have to check modules.
;;
;; The identifer may be passed in wrapped or unwrapped. In any
;; case, this routine returns either a symbol, a syntax object, or
;; a string label.
;;
(define (search sym subst marks)
(match subst
(() #f)
(('shift . subst)
(match marks
((_ . marks)
(search sym subst marks))))
((#('ribcage rsymnames rmarks rlabels) . subst)
(define (search-list-rib)
(let lp ((rsymnames rsymnames)
(rmarks rmarks)
(rlabels rlabels))
(match rsymnames
(() (search sym subst marks))
((rsym . rsymnames)
(match rmarks
((rmarks1 . rmarks)
(match rlabels
((label . rlabels)
(if (and (eq? sym rsym) (same-marks? marks rmarks1))
(match label
((mod* . label)
(if (equal? mod* mod)
label
(lp rsymnames rmarks rlabels)))
(_ label))
(lp rsymnames rmarks rlabels))))))))))
(define (search-vector-rib)
(let ((n (vector-length rsymnames)))
(let lp ((i 0))
(cond
((= i n) (search sym subst marks))
((and (eq? (vector-ref rsymnames i) sym)
(same-marks? marks (vector-ref rmarks i)))
(match (vector-ref rlabels i)
((mod* . label)
(if (equal? mod* mod)
label
(lp (1+ i))))
(label
label)))
(else (lp (1+ i)))))))
(if (vector? rsymnames)
(search-vector-rib)
(search-list-rib)))))
(cond
((symbol? id)
(or (search id (wrap-subst w) (wrap-marks w)) id))
((syntax? id)
(let ((id (syntax-expression id))
(w1 (syntax-wrap id))
(mod (or (syntax-module id) mod)))
(let ((marks (join-marks (wrap-marks w) (wrap-marks w1))))
(or (search id (wrap-subst w) marks)
(search id (wrap-subst w1) marks)
id))))
(else (syntax-violation 'id-var-name "invalid id" id))))
;; A helper procedure for syntax-locally-bound-identifiers, which
;; itself is a helper for transformer procedures.
;; `locally-bound-identifiers' returns a list of all bindings
;; visible to a syntax object with the given wrap. They are in
;; order from outer to inner.
;;
;; The purpose of this procedure is to give a transformer procedure
;; references on bound identifiers, that the transformer can then
;; introduce some of them in its output. As such, the identifiers
;; are anti-marked, so that rebuild-macro-output doesn't apply new
;; marks to them.
;;
(define (locally-bound-identifiers w mod)
(define (scan subst results)
(match subst
(() results)
(('shift . subst) (scan subst results))
((#('ribcage symnames marks labels) . subst*)
(define (scan-list-rib)
(let lp ((symnames symnames) (marks marks) (results results))
(match symnames
(() (scan subst* results))
((sym . symnames)
(match marks
((m . marks)
(lp symnames marks
(cons (wrap sym (anti-mark (make-wrap m subst)) mod)
results))))))))
(define (scan-vector-rib)
(let ((n (vector-length symnames)))
(let lp ((i 0) (results results))
(if (= i n)
(scan subst* results)
(lp (1+ i)
(let ((sym (vector-ref symnames i))
(m (vector-ref marks i)))
(cons (wrap sym (anti-mark (make-wrap m subst)) mod)
results)))))))
(if (vector? symnames)
(scan-vector-rib)
(scan-list-rib)))))
(scan (wrap-subst w) '()))
;; Returns three values: binding type, binding value, and the module
;; (for resolving toplevel vars).
(define (resolve-identifier id w r mod resolve-syntax-parameters?)
(define (resolve-global var mod)
(when (and (not mod) (current-module))
(warn "module system is booted, we should have a module" var))
(let ((v (and (not (equal? mod '(primitive)))
(module-variable (if mod
(resolve-module (cdr mod))
(current-module))
var))))
;; The expander needs to know when a top-level definition from
;; outside the compilation unit is a macro.
;;
;; Additionally if a macro is actually a syntax-parameter, we
;; might need to resolve its current binding. If the syntax
;; parameter is locally bound (via syntax-parameterize), then
;; its variable will be present in `r', the expand-time
;; environment. It's a kind of double lookup: first we see
;; that a name is bound to a syntax parameter, then we look
;; for the current binding of the syntax parameter.
;;
;; We use the variable (box) holding the syntax parameter
;; definition as the key for the second lookup. We use the
;; variable for two reasons:
;;
;; 1. If the syntax parameter is redefined in parallel
;; (perhaps via a parallel module compilation), the
;; redefinition keeps the same variable. We don't want to
;; use a "key" that could change during a redefinition. See
;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27476.
;;
;; 2. Using the variable instead of its (symname, modname)
;; pair allows for syntax parameters to be renamed or
;; aliased while preserving the syntax parameter's identity.
;;
(if (and v (variable-bound? v) (macro? (variable-ref v)))
(let* ((m (variable-ref v))
(type (macro-type m))
(trans (macro-binding m)))
(if (eq? type 'syntax-parameter)
(if resolve-syntax-parameters?
(let ((lexical (assq-ref r v)))
;; A resolved syntax parameter is
;; indistinguishable from a macro.
(values 'macro
(if lexical
(binding-value lexical)
trans)
mod))
;; Return box as value for use in second lookup.
(values type v mod))
(values type trans mod)))
(values 'global var mod))))
(define (resolve-lexical label mod)
(let ((b (assq-ref r label)))
(if b
(let ((type (binding-type b))
(value (binding-value b)))
(if (eq? type 'syntax-parameter)
(if resolve-syntax-parameters?
(values 'macro value mod)
;; If the syntax parameter was defined within
;; this compilation unit, use its label as its
;; lookup key.
(values type label mod))
(values type value mod)))
(values 'displaced-lexical #f #f))))
(let ((n (id-var-name id w mod)))
(cond
((syntax? n)
(cond
((not (eq? n id))
;; This identifier aliased another; recurse to allow
;; syntax-parameterize to override macro-introduced syntax
;; parameters.
(resolve-identifier n w r mod resolve-syntax-parameters?))
(else
;; Resolved to a free variable that was introduced by this
;; macro; continue to resolve this global by name.
(resolve-identifier (syntax-expression n)
(syntax-wrap n)
r
(or (syntax-module n) mod)
resolve-syntax-parameters?))))
((symbol? n)
(resolve-global n (or (and (syntax? id)
(syntax-module id))
mod)))
(else
(resolve-lexical n (or (and (syntax? id)
(syntax-module id))
mod))))))
(define transformer-environment
(make-fluid
(lambda (k)
(error "called outside the dynamic extent of a syntax transformer"))))
(define (with-transformer-environment k)
((fluid-ref transformer-environment) k))
;; 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=? i j)
(let* ((mi (and (syntax? i) (syntax-module i)))
(mj (and (syntax? j) (syntax-module j)))
(ni (id-var-name i empty-wrap mi))
(nj (id-var-name j empty-wrap mj)))
(define (id-module-binding id mod)
(module-variable
(if mod
;; The normal case.
(resolve-module (cdr mod))
;; Either modules have not been booted, or we have a
;; raw symbol coming in, which is possible.
(current-module))
(id-sym-name id)))
(cond
((syntax? ni) (free-id=? ni j))
((syntax? nj) (free-id=? i nj))
((symbol? ni)
;; `i' is not lexically bound. Assert that `j' is free,
;; and if so, compare their bindings, that they are either
;; bound to the same variable, or both unbound and have
;; the same name.
(and (eq? nj (id-sym-name j))
(let ((bi (id-module-binding i mi))
(bj (id-module-binding j mj)))
(and (eq? bi bj)
(or bi (eq? ni nj))))))
(else
;; Otherwise `i' is bound, so check that `j' is bound, and
;; bound to the same thing.
(equal? ni nj)))))
;; bound-id=? may be passed unwrapped (or partially wrapped) ids as
;; long as the missing portion of the wrap is common to both of the ids
;; since (bound-id=? x y) iff (bound-id=? (wrap x w) (wrap y w))
(define (bound-id=? i j)
(if (and (syntax? i) (syntax? j))
(and (eq? (syntax-expression i)
(syntax-expression j))
(same-marks? (wrap-marks (syntax-wrap i))
(wrap-marks (syntax-wrap j))))
(eq? i 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? ids)
(and (let all-ids? ((ids ids))
(match ids
(() #t)
((id . ids)
(and (id? id) (all-ids? 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? ids)
(let distinct? ((ids ids))
(match ids
(() #t)
((id . ids)
(and (not (bound-id-member? id ids))
(distinct? ids))))))
(define (bound-id-member? x ids)
(match ids
(() #f)
((id . ids)
(or (bound-id=? x id)
(bound-id-member? x ids)))))
;; wrapping expressions and identifiers
(define (wrap x w defmod)
(source-wrap x w #f defmod))
(define (wrap-syntax x w defmod)
(make-syntax (syntax-expression x)
w
(or (syntax-module x) defmod)
(syntax-sourcev x)))
(define (source-wrap x w s defmod)
(cond
((and (null? (wrap-marks w))
(null? (wrap-subst w))
(not defmod)
(not s))
x)
((syntax? x) (wrap-syntax x (join-wraps w (syntax-wrap x)) defmod))
((null? x) x)
(else (make-syntax x w defmod s))))
;; expanding
(define (expand-sequence body r w s mod)
(build-sequence s
(let lp ((body body))
(match body
(() '())
((head . tail)
(let ((expr (expand head r w mod)))
(cons expr (lp tail))))))))
;; At top-level, we allow mixed definitions and expressions. Like
;; expand-body we expand in two passes.
;;
;; First, from left to right, we expand just enough to know what
;; expressions are definitions, syntax definitions, and splicing
;; statements (`begin'). If we anything needs evaluating at
;; expansion-time, it is expanded directly.
;;
;; Otherwise we collect expressions to expand, in thunks, and then
;; expand them all at the end. This allows all syntax expanders
;; visible in a toplevel sequence to be visible during the
;; expansions of all normal definitions and expressions in the
;; sequence.
;;
(define (expand-top-sequence body r w s m esew mod)
(let* ((r (cons '("placeholder" . (placeholder)) r))
(ribcage (make-empty-ribcage))
(w (make-wrap (wrap-marks w) (cons ribcage (wrap-subst w)))))
(define (record-definition! id var)
(let ((mod (cons 'hygiene (module-name (current-module)))))
;; Ribcages map symbol+marks to names, mostly for
;; resolving lexicals. Here to add a mapping for toplevel
;; definitions we also need to match the module. So, we
;; put it in the name instead, and make id-var-name handle
;; the special case of names that are pairs. See the
;; comments in id-var-name for more.
(extend-ribcage! ribcage id
(cons (or (syntax-module id) mod)
(wrap var top-wrap mod)))))
(define (macro-introduced-identifier? id)
(not (equal? (wrap-marks (syntax-wrap id)) '(top))))
(define (ensure-fresh-name var)
;; If a macro introduces a top-level identifier, we attempt
;; to give it a fresh name by appending the hash of the
;; expression in which it appears. However, this can fail
;; for hash collisions, which is more common that one might
;; think: Guile's hash function stops descending into cdr's
;; at some point. So, within an expansion unit, fall back
;; to appending a uniquifying integer.
(define (ribcage-has-var? var)
(let lp ((labels (ribcage-labels ribcage)))
(match labels
(() #f)
(((_ . wrapped) . labels)
(or (eq? (syntax-expression wrapped) var)
(lp labels))))))
(let lp ((unique var) (n 1))
(if (ribcage-has-var? unique)
(let ((tail (string->symbol (number->string n))))
(lp (symbol-append var '- tail) (1+ n)))
unique)))
(define (fresh-derived-name id orig-form)
(ensure-fresh-name
(symbol-append
(syntax-expression id)
'-
(string->symbol
;; FIXME: This encodes hash values into the ABI of
;; compiled modules; a problem?
(number->string
(hash (syntax->datum orig-form) most-positive-fixnum)
16)))))
(define (parse body r w s m esew mod)
(let lp ((body body))
(match body
(() '())
((head . tail)
(let ((thunks (parse1 head r w s m esew mod)))
(append thunks (lp tail)))))))
(define (parse1 x r w s m esew mod)
(define (current-module-for-expansion mod)
(match mod
(('hygiene . _)
;; If the module was just put in place for hygiene, in a
;; top-level `begin' always recapture the current
;; module. If a user wants to override, then we need to
;; use @@ or similar.
(cons 'hygiene (module-name (current-module))))
(_ mod)))
(call-with-values
(lambda ()
(let ((mod (current-module-for-expansion mod)))
(syntax-type x r w (source-annotation x) ribcage mod #f)))
(lambda (type value form e w s mod)
(case type
((define-form)
(let* ((id (wrap value w mod))
(var (if (macro-introduced-identifier? id)
(fresh-derived-name id x)
(syntax-expression id))))
(record-definition! id var)
(list
(if (eq? m 'c&e)
(let ((x (build-global-definition s mod var (expand e r w mod))))
(top-level-eval x mod)
(lambda () x))
(call-with-values
(lambda () (resolve-identifier id empty-wrap r mod #t))
(lambda (type* value* mod*)
;; If the identifier to be bound is currently bound to a
;; macro, then immediately discard that binding.
(when (eq? type* 'macro)
(top-level-eval (build-global-definition
s mod var (build-void s))
mod))
(lambda ()
(build-global-definition s mod var (expand e r w mod)))))))))
((define-syntax-form define-syntax-parameter-form)
(let* ((id (wrap value w mod))
(var (if (macro-introduced-identifier? id)
(fresh-derived-name id x)
(syntax-expression id))))
(record-definition! id var)
(case m
((c)
(cond
((memq 'compile esew)
(let ((e (expand-install-global mod var type (expand e r w mod))))
(top-level-eval e mod)
(if (memq 'load esew)
(list (lambda () e))
'())))
((memq 'load esew)
(list (lambda ()
(expand-install-global mod var type (expand e r w mod)))))
(else '())))
((c&e)
(let ((e (expand-install-global mod var type (expand e r w mod))))
(top-level-eval e mod)
(list (lambda () e))))
(else
(when (memq 'eval esew)
(top-level-eval
(expand-install-global mod var type (expand e r w mod))
mod))
'()))))
((begin-form)
(syntax-case e ()
((_ e1 ...)
(parse #'(e1 ...) r w s m esew mod))))
((local-syntax-form)
(expand-local-syntax value e r w s mod
(lambda (forms r w s mod)
(parse forms r w s m esew mod))))
((eval-when-form)
(syntax-case e ()
((_ (x ...) e1 e2 ...)
(let ((when-list (parse-when-list e #'(x ...)))
(body #'(e1 e2 ...)))
(define (recurse m esew)
(parse body r w s m esew mod))
(cond
((eq? m 'e)
(if (memq 'eval when-list)
(recurse (if (memq 'expand when-list) 'c&e 'e)
'(eval))
(begin
(when (memq 'expand when-list)
(top-level-eval
(expand-top-sequence body r w s 'e '(eval) mod)
mod))
'())))
((memq 'load when-list)
(if (or (memq 'compile when-list)
(memq 'expand when-list)
(and (eq? m 'c&e) (memq 'eval when-list)))
(recurse 'c&e '(compile load))
(if (memq m '(c c&e))
(recurse 'c '(load))
'())))
((or (memq 'compile when-list)
(memq 'expand when-list)
(and (eq? m 'c&e) (memq 'eval when-list)))
(top-level-eval
(expand-top-sequence body r w s 'e '(eval) mod)
mod)
'())
(else
'()))))))
(else
(list
(if (eq? m 'c&e)
(let ((x (expand-expr type value form e r w s mod)))
(top-level-eval x mod)
(lambda () x))
(lambda ()
(expand-expr type value form e r w s mod)))))))))
(match (let lp ((thunks (parse body r w s m esew mod)))
(match thunks
(() '())
((thunk . thunks) (cons (thunk) (lp thunks)))))
(() (build-void s))
(exps (build-sequence s exps)))))
(define (expand-install-global mod name type e)
(build-global-definition
no-source
mod
name
(build-primcall
no-source
'make-syntax-transformer
(list (build-data no-source name)
(build-data no-source
(if (eq? type 'define-syntax-parameter-form)
'syntax-parameter
'macro))
e))))
(define (parse-when-list e when-list)
(let ((result (strip when-list)))
(let lp ((l result))
(match l
(() result)
((x . l)
(match x
((or 'compile 'load 'eval 'expand) (lp l))
(_ (syntax-violation 'eval-when "invalid situation" e x))))))))
;; syntax-type returns seven values: type, value, form, e, w, s, and
;; mod. The first two are described in the table below.
;;
;; type value explanation
;; -------------------------------------------------------------------
;; core procedure core singleton
;; core-form procedure core form
;; module-ref procedure @ or @@ singleton
;; lexical name lexical variable reference
;; global name global variable reference
;; begin none begin keyword
;; define none define keyword
;; define-syntax none define-syntax keyword
;; define-syntax-parameter none define-syntax-parameter keyword
;; local-syntax rec? letrec-syntax/let-syntax keyword
;; eval-when none eval-when keyword
;; syntax level pattern variable
;; displaced-lexical none displaced lexical identifier
;; lexical-call name call to lexical variable
;; global-call name call to global variable
;; primitive-call name call to primitive
;; call none any other call
;; begin-form none begin expression
;; define-form id variable definition
;; define-syntax-form id syntax definition
;; define-syntax-parameter-form id syntax parameter definition
;; local-syntax-form rec? syntax definition
;; eval-when-form none eval-when form
;; constant none self-evaluating datum
;; other none anything else
;;
;; form is the entire form. For definition forms (define-form,
;; define-syntax-form, and define-syntax-parameter-form), e is the
;; rhs expression. For all others, e is the entire form. w is the
;; wrap for both form and e. s is the source for the entire form.
;; mod is the module for both form and e.
;;
;; syntax-type expands macros and unwraps as necessary to get to one
;; of the forms above. It also parses definition forms, although
;; perhaps this should be done by the consumer.
(define (syntax-type e r w s rib mod for-car?)
(cond
((symbol? e)
(call-with-values (lambda () (resolve-identifier e w r mod #t))
(lambda (type value mod*)
(case type
((macro)
(if for-car?
(values type value e e w s mod)
(syntax-type (expand-macro value e r w s rib mod)
r empty-wrap s rib mod #f)))
((global)
;; Toplevel definitions may resolve to bindings with
;; different names or in different modules.
(values type value e value w s mod*))
(else (values type value e e w s mod))))))
((pair? e)
(let ((first (car e)))
(call-with-values
(lambda () (syntax-type first r w s rib mod #t))
(lambda (ftype fval fform fe fw fs fmod)
(case ftype
((lexical)
(values 'lexical-call fval e e w s mod))
((global)
(if (equal? fmod '(primitive))
(values 'primitive-call fval e e w s mod)
;; If we got here via an (@@ ...) expansion, we
;; need to make sure the fmod information is
;; propagated back correctly -- hence this
;; consing.
(values 'global-call (make-syntax fval w fmod fs)
e e w s mod)))
((macro)
(syntax-type (expand-macro fval e r w s rib mod)
r empty-wrap s rib mod for-car?))
((module-ref)
(call-with-values (lambda () (fval e r w mod))
(lambda (e r w s mod)
(syntax-type e r w s rib mod for-car?))))
((core)
(values 'core-form fval e e w s mod))
((local-syntax)
(values 'local-syntax-form fval e e w s mod))
((begin)
(values 'begin-form #f e e w s mod))
((eval-when)
(values 'eval-when-form #f e e w s mod))
((define)
(syntax-case e ()
((_ name val)
(id? #'name)
(values 'define-form #'name e #'val w s mod))
((_ (name . args) e1 e2 ...)
(and (id? #'name)
(valid-bound-ids? (lambda-var-list #'args)))
;; need lambda here...
(values 'define-form (wrap #'name w mod)
(wrap e w mod)
(source-wrap
(cons #'lambda (wrap #'(args e1 e2 ...) w mod))
empty-wrap s #f)
empty-wrap s mod))
((_ name)
(id? #'name)
(values 'define-form (wrap #'name w mod)
(wrap e w mod)
#'(if #f #f)
empty-wrap s mod))))
((define-syntax)
(syntax-case e ()
((_ name val)
(id? #'name)
(values 'define-syntax-form #'name e #'val w s mod))))
((define-syntax-parameter)
(syntax-case e ()
((_ name val)
(id? #'name)
(values 'define-syntax-parameter-form #'name e #'val w s mod))))
(else
(values 'call #f e e w s mod)))))))
((syntax? e)
(syntax-type (syntax-expression e)
r
(join-wraps w (syntax-wrap e))
(or (source-annotation e) s) rib
(or (syntax-module e) mod) for-car?))
((self-evaluating? e) (values 'constant #f e e w s mod))
(else (values 'other #f e e w s mod))))
(define (expand e r w mod)
(call-with-values
(lambda () (syntax-type e r w (source-annotation e) #f mod #f))
(lambda (type value form e w s mod)
(expand-expr type value form e r w s mod))))
(define (expand-expr type value form e r w s mod)
(case type
((lexical)
(build-lexical-reference s e value))
((core core-form)
;; apply transformer
(value e r w s mod))
((module-ref)
(call-with-values (lambda () (value e r w mod))
(lambda (e r w s mod)
(expand e r w mod))))
((lexical-call)
(expand-call
(let ((id (car e)))
(build-lexical-reference (source-annotation id)
(if (syntax? id)
(syntax->datum id)
id)
value))
e r w s mod))
((global-call)
(expand-call
(build-global-reference (or (source-annotation (car e)) s)
(if (syntax? value)
(syntax-expression value)
value)
(or (and (syntax? value)
(syntax-module value))
mod))
e r w s mod))
((primitive-call)
(syntax-case e ()
((_ e ...)
(build-primcall s
value
(map (lambda (e) (expand e r w mod))
#'(e ...))))))
((constant) (build-data s (strip e)))
((global) (build-global-reference s value mod))
((call) (expand-call (expand (car e) r w mod) e r w s mod))
((begin-form)
(syntax-case e ()
((_ e1 e2 ...) (expand-sequence #'(e1 e2 ...) r w s mod))
((_)
(syntax-violation #f "sequence of zero expressions"
(source-wrap e w s mod)))))
((local-syntax-form)
(expand-local-syntax value e r w s mod expand-sequence))
((eval-when-form)
(syntax-case e ()
((_ (x ...) e1 e2 ...)
(let ((when-list (parse-when-list e #'(x ...))))
(if (memq 'eval when-list)
(expand-sequence #'(e1 e2 ...) r w s mod)
(expand-void))))))
((define-form define-syntax-form define-syntax-parameter-form)
(syntax-violation #f "definition in expression context, where definitions are not allowed,"
(source-wrap form w s mod)))
((syntax)
(syntax-violation #f "reference to pattern variable outside syntax form"
(source-wrap e w s mod)))
((displaced-lexical)
(syntax-violation #f "reference to identifier outside its scope"
(source-wrap e w s mod)))
(else (syntax-violation #f "unexpected syntax"
(source-wrap e w s mod)))))
(define (expand-call x e r w s mod)
(syntax-case e ()
((e0 e1 ...)
(build-call s x
(map (lambda (e) (expand e r w mod)) #'(e1 ...))))))
;; (What follows is my interpretation of what's going on here -- Andy)
;;
;; A macro takes an expression, a tree, the leaves of which are identifiers
;; and datums. Identifiers are symbols along with a wrap and a module. For
;; efficiency, subtrees that share wraps and modules may be grouped as one
;; syntax object.
;;
;; Going into the expansion, the expression is given an anti-mark, which
;; logically propagates to all leaves. Then, in the new expression returned
;; from the transfomer, if we see an expression with an anti-mark, we know it
;; pertains to the original expression; conversely, expressions without the
;; anti-mark are known to be introduced by the transformer.
;;
;; OK, good until now. We know this algorithm does lexical scoping
;; appropriately because it's widely known in the literature, and psyntax is
;; widely used. But what about modules? Here we're on our own. What we do is
;; to mark the module of expressions produced by a macro as pertaining to the
;; module that was current when the macro was defined -- that is, free
;; identifiers introduced by a macro are scoped in the macro's module, not in
;; the expansion's module. Seems to work well.
;;
;; The only wrinkle is when we want a macro to expand to code in another
;; module, as is the case for the r6rs `library' form -- the body expressions
;; should be scoped relative the the new module, the one defined by the macro.
;; For that, use `(@@ mod-name body)'.
;;
;; Part of the macro output will be from the site of the macro use and part
;; from the macro definition. We allow source information from the macro use
;; to pass through, but we annotate the parts coming from the macro with the
;; source location information corresponding to the macro use. It would be
;; really nice if we could also annotate introduced expressions with the
;; locations corresponding to the macro definition, but that is not yet
;; possible.
(define (expand-macro p e r w s rib mod)
(define (decorate-source x)
(source-wrap x empty-wrap s #f))
(define (map* f x)
(match x
(() '())
((x . x*) (cons (f x) (map* f x*)))
(x (f x))))
(define rebuild-macro-output
(lambda (x m)
(cond ((pair? x)
(decorate-source
(map* (lambda (x) (rebuild-macro-output x m)) x)))
((syntax? x)
(let ((w (syntax-wrap x)))
(let ((ms (wrap-marks w)) (ss (wrap-subst w)))
(if (and (pair? ms) (eq? (car ms) the-anti-mark))
;; output is from original text
(wrap-syntax
x
(make-wrap (cdr ms)
(if rib
(cons rib (cdr ss))
(cdr ss)))
mod)
;; output introduced by macro
(wrap-syntax
x
(make-wrap (cons m ms)
(if rib
(cons rib (cons 'shift ss))
(cons 'shift ss)))
mod)))))
((vector? x)
(let* ((n (vector-length x))
(v (make-vector n)))
(do ((i 0 (1+ i)))
((= i n) v)
(vector-set! v i
(rebuild-macro-output (vector-ref x i) m)))
(decorate-source v)))
((symbol? x)
(syntax-violation #f "encountered raw symbol in macro output"
(source-wrap e w (wrap-subst w) mod) x))
(else (decorate-source x)))))
(with-fluids ((transformer-environment
(lambda (k) (k e r w s rib mod))))
(rebuild-macro-output (p (source-wrap e (anti-mark w) s mod))
(new-mark))))
(define (expand-body body outer-form r w mod)
;; In processing the forms of the body, we create a new, empty wrap.
;; This wrap is augmented (destructively) each time we discover that
;; the next form is a definition. This is done:
;;
;; (1) to allow the first nondefinition form to be a call to
;; one of the defined ids even if the id previously denoted a
;; definition keyword or keyword for a macro expanding into a
;; definition;
;; (2) to prevent subsequent definition forms (but unfortunately
;; not earlier ones) and the first nondefinition form from
;; confusing one of the bound identifiers for an auxiliary
;; keyword; and
;; (3) so that we do not need to restart the expansion of the
;; first nondefinition form, which is problematic anyway
;; since it might be the first element of a begin that we
;; have just spliced into the body (meaning if we restarted,
;; we'd really need to restart with the begin or the macro
;; call that expanded into the begin, and we'd have to give
;; up allowing (begin <defn>+ <expr>+), which is itself
;; problematic since we don't know if a begin contains only
;; definitions until we've expanded it).
;;
;; Before processing the body, we also create a new environment
;; containing a placeholder for the bindings we will add later and
;; associate this environment with each form. In processing a
;; let-syntax or letrec-syntax, the associated environment may be
;; augmented with local keyword bindings, so the environment may
;; be different for different forms in the body. Once we have
;; gathered up all of the definitions, we evaluate the transformer
;; expressions and splice into r at the placeholder the new variable
;; and keyword bindings. This allows let-syntax or letrec-syntax
;; forms local to a portion or all of the body to shadow the
;; definition bindings.
;;
;; Subforms of a begin, let-syntax, or letrec-syntax are spliced
;; into the body.
;;
;; outer-form is fully wrapped w/source
(let* ((r (cons '("placeholder" . (placeholder)) r))
(ribcage (make-empty-ribcage))
(w (make-wrap (wrap-marks w) (cons ribcage (wrap-subst w)))))
(let parse ((body (map (lambda (x) (cons r (wrap x w mod))) body))
(ids '()) (labels '())
(var-ids '()) (vars '()) (vals '()) (bindings '())
(expand-tail-expr #f))
(cond
((null? body)
(unless expand-tail-expr
(when (null? ids)
(syntax-violation #f "empty body" outer-form))
(syntax-violation #f "body should end with an expression" outer-form))
(unless (valid-bound-ids? ids)
(syntax-violation
#f "invalid or duplicate identifier in definition"
outer-form))
(set-cdr! r (extend-env labels bindings (cdr r)))
(let ((src (source-annotation outer-form)))
(let lp ((var-ids var-ids) (vars vars) (vals vals)
(tail (expand-tail-expr)))
(cond
((null? var-ids) tail)
((not (car var-ids))
(lp (cdr var-ids) (cdr vars) (cdr vals)
(make-seq src ((car vals)) tail)))
(else
(let ((var-ids (map (lambda (id)
(if id (syntax->datum id) '_))
(reverse var-ids)))
(vars (map (lambda (var) (or var (gen-lexical '_)))
(reverse vars)))
(vals (map (lambda (expand-expr id)
(if id
(expand-expr)
(make-seq src
(expand-expr)
(build-void src))))
(reverse vals) (reverse var-ids))))
(build-letrec src #t var-ids vars vals tail)))))))
(expand-tail-expr
(parse body ids labels
(cons #f var-ids)
(cons #f vars)
(cons expand-tail-expr vals)
bindings #f))
(else
(let ((e (cdar body)) (er (caar body)) (body (cdr body)))
(call-with-values
(lambda () (syntax-type e er empty-wrap (source-annotation e) ribcage mod #f))
(lambda (type value form e w s mod)
(case type
((define-form)
(let ((id (wrap value w mod)) (label (gen-label)))
(let ((var (gen-var id)))
(extend-ribcage! ribcage id label)
(parse body
(cons id ids) (cons label labels)
(cons id var-ids)
(cons var vars)
(cons (let ((wrapped (source-wrap e w s mod)))
(lambda ()
(expand wrapped er empty-wrap mod)))
vals)
(cons (make-binding 'lexical var) bindings)
#f))))
((define-syntax-form)
(let ((id (wrap value w mod))
(label (gen-label))
(trans-r (macros-only-env er)))
(extend-ribcage! ribcage id label)
;; As required by R6RS, evaluate the right-hand-sides of internal
;; syntax definition forms and add their transformers to the
;; compile-time environment immediately, so that the newly-defined
;; keywords may be used in definition context within the same
;; lexical contour.
(set-cdr! r (extend-env
(list label)
(list (make-binding
'macro
(eval-local-transformer
(expand e trans-r w mod)
mod)))
(cdr r)))
(parse body (cons id ids)
labels var-ids vars vals bindings #f)))
((define-syntax-parameter-form)
;; Same as define-syntax-form, different binding type though.
(let ((id (wrap value w mod))
(label (gen-label))
(trans-r (macros-only-env er)))
(extend-ribcage! ribcage id label)
(set-cdr! r (extend-env
(list label)
(list (make-binding
'syntax-parameter
(eval-local-transformer
(expand e trans-r w mod)
mod)))
(cdr r)))
(parse body (cons id ids)
labels var-ids vars vals bindings #f)))
((begin-form)
(syntax-case e ()
((_ e1 ...)
(parse (let f ((forms #'(e1 ...)))
(if (null? forms)
body
(cons (cons er (wrap (car forms) w mod))
(f (cdr forms)))))
ids labels var-ids vars vals bindings #f))))
((local-syntax-form)
(expand-local-syntax
value e er w s mod
(lambda (forms er w s mod)
(parse (let f ((forms forms))
(if (null? forms)
body
(cons (cons er (wrap (car forms) w mod))
(f (cdr forms)))))
ids labels var-ids vars vals bindings #f))))
(else ; An expression, not a definition.
(let ((wrapped (source-wrap e w s mod)))
(parse body ids labels var-ids vars vals bindings
(lambda ()
(expand wrapped er empty-wrap mod))))))))))))))
(define (expand-local-syntax rec? e r w s mod k)
(syntax-case e ()
((_ ((id val) ...) e1 e2 ...)
(let ((ids #'(id ...)))
(if (not (valid-bound-ids? ids))
(syntax-violation #f "duplicate bound keyword" e)
(let ((labels (gen-labels ids)))
(let ((new-w (make-binding-wrap ids labels w)))
(k #'(e1 e2 ...)
(extend-env
labels
(let ((w (if rec? new-w w))
(trans-r (macros-only-env r)))
(map (lambda (x)
(make-binding 'macro
(eval-local-transformer
(expand x trans-r w mod)
mod)))
#'(val ...)))
r)
new-w
s
mod))))))
(_ (syntax-violation #f "bad local syntax definition"
(source-wrap e w s mod)))))
(define (eval-local-transformer expanded mod)
(let ((p (local-eval expanded mod)))
(unless (procedure? p)
(syntax-violation #f "nonprocedure transformer" p))
p))
(define (expand-void)
(build-void no-source))
(define (ellipsis? e r mod)
(and (nonsymbol-id? e)
;; If there is a binding for the special identifier
;; #{ $sc-ellipsis }# in the lexical environment of E,
;; and if the associated binding type is 'ellipsis',
;; then the binding's value specifies the custom ellipsis
;; identifier within that lexical environment, and the
;; comparison is done using 'bound-id=?'.
(call-with-values
(lambda () (resolve-identifier
(make-syntax '#{ $sc-ellipsis }#
(syntax-wrap e)
(or (syntax-module e) mod)
#f)
empty-wrap r mod #f))
(lambda (type value mod)
(if (eq? type 'ellipsis)
(bound-id=? e value)
(free-id=? e #'(... ...)))))))
(define (lambda-formals orig-args)
(define (req args rreq)
(syntax-case args ()
(()
(check (reverse rreq) #f))
((a . b) (id? #'a)
(req #'b (cons #'a rreq)))
(r (id? #'r)
(check (reverse rreq) #'r))
(else
(syntax-violation 'lambda "invalid argument list" orig-args args))))
(define (check req rest)
(cond
((distinct-bound-ids? (if rest (cons rest req) req))
(values req #f rest #f))
(else
(syntax-violation 'lambda "duplicate identifier in argument list"
orig-args))))
(req orig-args '()))
(define (expand-simple-lambda e r w s mod req rest meta body)
(let* ((ids (if rest (append req (list rest)) req))
(vars (map gen-var ids))
(labels (gen-labels ids)))
(build-simple-lambda
s
(map syntax->datum req) (and rest (syntax->datum rest)) vars
meta
(expand-body body (source-wrap e w s mod)
(extend-var-env labels vars r)
(make-binding-wrap ids labels w)
mod))))
(define (lambda*-formals orig-args)
(define (req args rreq)
(syntax-case args ()
(()
(check (reverse rreq) '() #f '()))
((a . b) (id? #'a)
(req #'b (cons #'a rreq)))
((a . b) (eq? (syntax->datum #'a) #:optional)
(opt #'b (reverse rreq) '()))
((a . b) (eq? (syntax->datum #'a) #:key)
(key #'b (reverse rreq) '() '()))
((a b) (eq? (syntax->datum #'a) #:rest)
(rest #'b (reverse rreq) '() '()))
(r (id? #'r)
(rest #'r (reverse rreq) '() '()))
(else
(syntax-violation 'lambda* "invalid argument list" orig-args args))))
(define (opt args req ropt)
(syntax-case args ()
(()
(check req (reverse ropt) #f '()))
((a . b) (id? #'a)
(opt #'b req (cons #'(a #f) ropt)))
(((a init) . b) (id? #'a)
(opt #'b req (cons #'(a init) ropt)))
((a . b) (eq? (syntax->datum #'a) #:key)
(key #'b req (reverse ropt) '()))
((a b) (eq? (syntax->datum #'a) #:rest)
(rest #'b req (reverse ropt) '()))
(r (id? #'r)
(rest #'r req (reverse ropt) '()))
(else
(syntax-violation 'lambda* "invalid optional argument list"
orig-args args))))
(define (key args req opt rkey)
(syntax-case args ()
(()
(check req opt #f (cons #f (reverse rkey))))
((a . b) (id? #'a)
(with-syntax ((k (symbol->keyword (syntax->datum #'a))))
(key #'b req opt (cons #'(k a #f) rkey))))
(((a init) . b) (id? #'a)
(with-syntax ((k (symbol->keyword (syntax->datum #'a))))
(key #'b req opt (cons #'(k a init) rkey))))
(((a init k) . b) (and (id? #'a)
(keyword? (syntax->datum #'k)))
(key #'b req opt (cons #'(k a init) rkey)))
((aok) (eq? (syntax->datum #'aok) #:allow-other-keys)
(check req opt #f (cons #t (reverse rkey))))
((aok a b) (and (eq? (syntax->datum #'aok) #:allow-other-keys)
(eq? (syntax->datum #'a) #:rest))
(rest #'b req opt (cons #t (reverse rkey))))
((aok . r) (and (eq? (syntax->datum #'aok) #:allow-other-keys)
(id? #'r))
(rest #'r req opt (cons #t (reverse rkey))))
((a b) (eq? (syntax->datum #'a) #:rest)
(rest #'b req opt (cons #f (reverse rkey))))
(r (id? #'r)
(rest #'r req opt (cons #f (reverse rkey))))
(else
(syntax-violation 'lambda* "invalid keyword argument list"
orig-args args))))
(define (rest args req opt kw)
(syntax-case args ()
(r (id? #'r)
(check req opt #'r kw))
(else
(syntax-violation 'lambda* "invalid rest argument"
orig-args args))))
(define (check req opt rest kw)
(cond
((distinct-bound-ids?
(append req (map car opt) (if rest (list rest) '())
(if (pair? kw) (map cadr (cdr kw)) '())))
(values req opt rest kw))
(else
(syntax-violation 'lambda* "duplicate identifier in argument list"
orig-args))))
(req orig-args '()))
(define (expand-lambda-case e r w s mod get-formals clauses)
(define (parse-req req opt rest kw body)
(let ((vars (map gen-var req))
(labels (gen-labels req)))
(let ((r* (extend-var-env labels vars r))
(w* (make-binding-wrap req labels w)))
(parse-opt (map syntax->datum req)
opt rest kw body (reverse vars) r* w* '() '()))))
(define (parse-opt req opt rest kw body vars r* w* out inits)
(cond
((pair? opt)
(syntax-case (car opt) ()
((id i)
(let* ((v (gen-var #'id))
(l (gen-labels (list v)))
(r** (extend-var-env l (list v) r*))
(w** (make-binding-wrap (list #'id) l w*)))
(parse-opt req (cdr opt) rest kw body (cons v vars)
r** w** (cons (syntax->datum #'id) out)
(cons (expand #'i r* w* mod) inits))))))
(rest
(let* ((v (gen-var rest))
(l (gen-labels (list v)))
(r* (extend-var-env l (list v) r*))
(w* (make-binding-wrap (list rest) l w*)))
(parse-kw req (if (pair? out) (reverse out) #f)
(syntax->datum rest)
(if (pair? kw) (cdr kw) kw)
body (cons v vars) r* w*
(if (pair? kw) (car kw) #f)
'() inits)))
(else
(parse-kw req (if (pair? out) (reverse out) #f) #f
(if (pair? kw) (cdr kw) kw)
body vars r* w*
(if (pair? kw) (car kw) #f)
'() inits))))
(define (parse-kw req opt rest kw body vars r* w* aok out inits)
(cond
((pair? kw)
(syntax-case (car kw) ()
((k id i)
(let* ((v (gen-var #'id))
(l (gen-labels (list v)))
(r** (extend-var-env l (list v) r*))
(w** (make-binding-wrap (list #'id) l w*)))
(parse-kw req opt rest (cdr kw) body (cons v vars)
r** w** aok
(cons (list (syntax->datum #'k)
(syntax->datum #'id)
v)
out)
(cons (expand #'i r* w* mod) inits))))))
(else
(parse-body req opt rest
(if (or aok (pair? out)) (cons aok (reverse out)) #f)
body (reverse vars) r* w* (reverse inits) '()))))
(define (parse-body req opt rest kw body vars r* w* inits meta)
(syntax-case body ()
((docstring e1 e2 ...) (string? (syntax->datum #'docstring))
(parse-body req opt rest kw #'(e1 e2 ...) vars r* w* inits
(append meta
`((documentation
. ,(syntax->datum #'docstring))))))
((#((k . v) ...) e1 e2 ...)
(parse-body req opt rest kw #'(e1 e2 ...) vars r* w* inits
(append meta (syntax->datum #'((k . v) ...)))))
((e1 e2 ...)
(values meta req opt rest kw inits vars
(expand-body #'(e1 e2 ...) (source-wrap e w s mod)
r* w* mod)))))
(syntax-case clauses ()
(() (values '() #f))
(((args e1 e2 ...) (args* e1* e2* ...) ...)
(call-with-values (lambda () (get-formals #'args))
(lambda (req opt rest kw)
(call-with-values (lambda ()
(parse-req req opt rest kw #'(e1 e2 ...)))
(lambda (meta req opt rest kw inits vars body)
(call-with-values
(lambda ()
(expand-lambda-case e r w s mod get-formals
#'((args* e1* e2* ...) ...)))
(lambda (meta* else*)
(values
(append meta meta*)
(build-lambda-case s req opt rest kw inits vars
body else*)))))))))))
;; data
;; strips syntax objects, recursively.
(define (strip x)
(define (annotate proc datum)
(let ((s (proc x)))
(when (and s (supports-source-properties? datum))
(set-source-properties! datum (sourcev->alist s)))
datum))
(cond
((syntax? x)
(annotate syntax-sourcev (strip (syntax-expression x))))
((pair? x)
(cons (strip (car x)) (strip (cdr x))))
((vector? x)
(list->vector (strip (vector->list x))))
(else x)))
;; lexical variables
(define (gen-var id)
(let ((id (if (syntax? id) (syntax-expression id) id)))
(gen-lexical id)))
;; appears to return a reversed list
(define (lambda-var-list vars)
(let lvl ((vars vars) (ls '()) (w empty-wrap))
(cond
((pair? vars) (lvl (cdr vars) (cons (wrap (car vars) w #f) ls) w))
((id? vars) (cons (wrap vars w #f) ls))
((null? vars) ls)
((syntax? vars)
(lvl (syntax-expression vars)
ls
(join-wraps w (syntax-wrap vars))))
;; include anything else to be caught by subsequent error
;; checking
(else (cons vars ls)))))
;; core transformers
(define (expand-syntax-parameterize e r w s mod)
(syntax-case e ()
((_ ((var val) ...) e1 e2 ...)
(valid-bound-ids? #'(var ...))
(let ((names
(map (lambda (x)
(call-with-values
(lambda () (resolve-identifier x w r mod #f))
(lambda (type value mod)
(case type
((displaced-lexical)
(syntax-violation 'syntax-parameterize
"identifier out of context"
e
(source-wrap x w s mod)))
((syntax-parameter)
value)
(else
(syntax-violation 'syntax-parameterize
"invalid syntax parameter"
e
(source-wrap x w s mod)))))))
#'(var ...)))
(bindings
(let ((trans-r (macros-only-env r)))
(map (lambda (x)
(make-binding
'syntax-parameter
(eval-local-transformer (expand x trans-r w mod) mod)))
#'(val ...)))))
(expand-body #'(e1 e2 ...)
(source-wrap e w s mod)
(extend-env names bindings r)
w
mod)))
(_ (syntax-violation 'syntax-parameterize "bad syntax"
(source-wrap e w s mod)))))
(define (expand-quote e r w s mod)
(syntax-case e ()
((_ e) (build-data s (strip #'e)))
(_ (syntax-violation 'quote "bad syntax"
(source-wrap e w s mod)))))
(define (expand-quote-syntax e r w s mod)
(syntax-case (source-wrap e w s mod) ()
((_ e) (build-data s #'e))
(e (syntax-violation 'quote "bad syntax" #'e))))
(define expand-syntax
(let ()
(define (gen-syntax src e r maps ellipsis? mod)
(if (id? e)
(call-with-values (lambda ()
(resolve-identifier e empty-wrap r mod #f))
(lambda (type value mod)
(case type
((syntax)
(call-with-values
(lambda () (gen-ref src (car value) (cdr value) maps))
(lambda (var maps)
(values `(ref ,var) maps))))
(else
(if (ellipsis? e r mod)
(syntax-violation 'syntax "misplaced ellipsis" src)
(values `(quote ,e) maps))))))
(syntax-case e ()
((dots e)
(ellipsis? #'dots r mod)
(gen-syntax src #'e r maps (lambda (e r mod) #f) mod))
((x dots . y)
;; this could be about a dozen lines of code, except that we
;; choose to handle #'(x ... ...) forms
(ellipsis? #'dots r mod)
(let f ((y #'y)
(k (lambda (maps)
(call-with-values
(lambda ()
(gen-syntax src #'x r
(cons '() maps) ellipsis? mod))
(lambda (x maps)
(if (null? (car maps))
(syntax-violation 'syntax "extra ellipsis"
src)
(values (gen-map x (car maps))
(cdr maps))))))))
(syntax-case y ()
((dots . y)
(ellipsis? #'dots r mod)
(f #'y
(lambda (maps)
(call-with-values
(lambda () (k (cons '() maps)))
(lambda (x maps)
(if (null? (car maps))
(syntax-violation 'syntax "extra ellipsis" src)
(values (gen-mappend x (car maps))
(cdr maps))))))))
(_ (call-with-values
(lambda () (gen-syntax src y r maps ellipsis? mod))
(lambda (y maps)
(call-with-values
(lambda () (k maps))
(lambda (x maps)
(values (gen-append x y) maps)))))))))
((x . y)
(call-with-values
(lambda () (gen-syntax src #'x r maps ellipsis? mod))
(lambda (x maps)
(call-with-values
(lambda () (gen-syntax src #'y r maps ellipsis? mod))
(lambda (y maps) (values (gen-cons x y) maps))))))
(#(e1 e2 ...)
(call-with-values
(lambda ()
(gen-syntax src #'(e1 e2 ...) r maps ellipsis? mod))
(lambda (e maps) (values (gen-vector e) maps))))
(x (eq? (syntax->datum #'x) #nil) (values '(quote #nil) maps))
(() (values '(quote ()) maps))
(_ (values `(quote ,e) maps)))))
(define (gen-ref src var level maps)
(if (= level 0)
(values var maps)
(if (null? maps)
(syntax-violation 'syntax "missing ellipsis" src)
(call-with-values
(lambda () (gen-ref src var (1- level) (cdr maps)))
(lambda (outer-var outer-maps)
(let ((b (assq outer-var (car maps))))
(if b
(values (cdr b) maps)
(let ((inner-var (gen-var 'tmp)))
(values inner-var
(cons (cons (cons outer-var inner-var)
(car maps))
outer-maps))))))))))
(define (gen-mappend e map-env)
`(apply (primitive append) ,(gen-map e map-env)))
(define (gen-map 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))
((and-map
(lambda (x) (and (eq? (car x) 'ref) (memq (cadr x) formals)))
(cdr e))
;; eta map equivalence:
;; (map (lambda (x ...) (f x ...)) y ...) == (map f y ...)
`(map (primitive ,(car e))
,@(map (let ((r (map cons formals actuals)))
(lambda (x) (cdr (assq (cadr x) r))))
(cdr e))))
(else `(map (lambda ,formals ,e) ,@actuals)))))
(define (gen-cons x y)
(case (car y)
((quote)
(if (eq? (car x) 'quote)
`(quote (,(cadr x) . ,(cadr y)))
(if (eq? (cadr y) '())
`(list ,x)
`(cons ,x ,y))))
((list) `(list ,x ,@(cdr y)))
(else `(cons ,x ,y))))
(define (gen-append x y)
(if (equal? y '(quote ()))
x
`(append ,x ,y)))
(define (gen-vector x)
(cond
((eq? (car x) 'list) `(vector ,@(cdr x)))
((eq? (car x) 'quote) `(quote #(,@(cadr x))))
(else `(list->vector ,x))))
(define (regen x)
(case (car x)
((ref) (build-lexical-reference no-source (cadr x) (cadr x)))
((primitive) (build-primref no-source (cadr x)))
((quote) (build-data no-source (cadr x)))
((lambda)
(if (list? (cadr x))
(build-simple-lambda no-source (cadr x) #f (cadr x) '() (regen (caddr x)))
(error "how did we get here" x)))
(else (build-primcall no-source (car x) (map regen (cdr x))))))
(lambda (e r w s mod)
(let ((e (source-wrap e w s mod)))
(syntax-case e ()
((_ x)
(call-with-values
(lambda () (gen-syntax e #'x r '() ellipsis? mod))
(lambda (e maps) (regen e))))
(_ (syntax-violation 'syntax "bad `syntax' form" e)))))))
(define (expand-lambda e r w s mod)
(syntax-case e ()
((_ args e1 e2 ...)
(call-with-values (lambda () (lambda-formals #'args))
(lambda (req opt rest kw)
(let lp ((body #'(e1 e2 ...)) (meta '()))
(syntax-case body ()
((docstring e1 e2 ...) (string? (syntax->datum #'docstring))
(lp #'(e1 e2 ...)
(append meta
`((documentation
. ,(syntax->datum #'docstring))))))
((#((k . v) ...) e1 e2 ...)
(lp #'(e1 e2 ...)
(append meta (syntax->datum #'((k . v) ...)))))
(_ (expand-simple-lambda e r w s mod req rest meta body)))))))
(_ (syntax-violation 'lambda "bad lambda" e))))
(define (expand-lambda* e r w s mod)
(syntax-case e ()
((_ args e1 e2 ...)
(call-with-values
(lambda ()
(expand-lambda-case e r w s mod
lambda*-formals #'((args e1 e2 ...))))
(lambda (meta lcase)
(build-case-lambda s meta lcase))))
(_ (syntax-violation 'lambda "bad lambda*" e))))
(define (expand-case-lambda e r w s mod)
(define (build-it meta clauses)
(call-with-values
(lambda ()
(expand-lambda-case e r w s mod
lambda-formals
clauses))
(lambda (meta* lcase)
(build-case-lambda s (append meta meta*) lcase))))
(syntax-case e ()
((_ (args e1 e2 ...) ...)
(build-it '() #'((args e1 e2 ...) ...)))
((_ docstring (args e1 e2 ...) ...)
(string? (syntax->datum #'docstring))
(build-it `((documentation
. ,(syntax->datum #'docstring)))
#'((args e1 e2 ...) ...)))
(_ (syntax-violation 'case-lambda "bad case-lambda" e))))
(define (expand-case-lambda* e r w s mod)
(define (build-it meta clauses)
(call-with-values
(lambda ()
(expand-lambda-case e r w s mod
lambda*-formals
clauses))
(lambda (meta* lcase)
(build-case-lambda s (append meta meta*) lcase))))
(syntax-case e ()
((_ (args e1 e2 ...) ...)
(build-it '() #'((args e1 e2 ...) ...)))
((_ docstring (args e1 e2 ...) ...)
(string? (syntax->datum #'docstring))
(build-it `((documentation
. ,(syntax->datum #'docstring)))
#'((args e1 e2 ...) ...)))
(_ (syntax-violation 'case-lambda "bad case-lambda*" e))))
(define (expand-with-ellipsis e r w s mod)
(syntax-case e ()
((_ dots e1 e2 ...)
(id? #'dots)
(let ((id (if (symbol? #'dots)
'#{ $sc-ellipsis }#
(make-syntax '#{ $sc-ellipsis }#
(syntax-wrap #'dots)
(syntax-module #'dots)
(syntax-sourcev #'dots)))))
(let ((ids (list id))
(labels (list (gen-label)))
(bindings (list (make-binding 'ellipsis (source-wrap #'dots w s mod)))))
(let ((nw (make-binding-wrap ids labels w))
(nr (extend-env labels bindings r)))
(expand-body #'(e1 e2 ...) (source-wrap e nw s mod) nr nw mod)))))
(_ (syntax-violation 'with-ellipsis "bad syntax"
(source-wrap e w s mod)))))
(define expand-let
(let ()
(define (expand-let e r w s mod constructor ids vals exps)
(if (not (valid-bound-ids? ids))
(syntax-violation 'let "duplicate bound variable" e)
(let ((labels (gen-labels ids))
(new-vars (map gen-var ids)))
(let ((nw (make-binding-wrap ids labels w))
(nr (extend-var-env labels new-vars r)))
(constructor s
(map syntax->datum ids)
new-vars
(map (lambda (x) (expand x r w mod)) vals)
(expand-body exps (source-wrap e nw s mod)
nr nw mod))))))
(lambda (e r w s mod)
(syntax-case e ()
((_ ((id val) ...) e1 e2 ...)
(and-map id? #'(id ...))
(expand-let e r w s mod
build-let
#'(id ...)
#'(val ...)
#'(e1 e2 ...)))
((_ f ((id val) ...) e1 e2 ...)
(and (id? #'f) (and-map id? #'(id ...)))
(expand-let e r w s mod
build-named-let
#'(f id ...)
#'(val ...)
#'(e1 e2 ...)))
(_ (syntax-violation 'let "bad let" (source-wrap e w s mod)))))))
(define (expand-letrec e r w s mod)
(syntax-case e ()
((_ ((id val) ...) e1 e2 ...)
(and-map id? #'(id ...))
(let ((ids #'(id ...)))
(if (not (valid-bound-ids? ids))
(syntax-violation 'letrec "duplicate bound variable" e)
(let ((labels (gen-labels ids))
(new-vars (map gen-var ids)))
(let ((w (make-binding-wrap ids labels w))
(r (extend-var-env labels new-vars r)))
(build-letrec s #f
(map syntax->datum ids)
new-vars
(map (lambda (x) (expand x r w mod)) #'(val ...))
(expand-body #'(e1 e2 ...)
(source-wrap e w s mod) r w mod)))))))
(_ (syntax-violation 'letrec "bad letrec" (source-wrap e w s mod)))))
(define (expand-letrec* e r w s mod)
(syntax-case e ()
((_ ((id val) ...) e1 e2 ...)
(and-map id? #'(id ...))
(let ((ids #'(id ...)))
(if (not (valid-bound-ids? ids))
(syntax-violation 'letrec* "duplicate bound variable" e)
(let ((labels (gen-labels ids))
(new-vars (map gen-var ids)))
(let ((w (make-binding-wrap ids labels w))
(r (extend-var-env labels new-vars r)))
(build-letrec s #t
(map syntax->datum ids)
new-vars
(map (lambda (x) (expand x r w mod)) #'(val ...))
(expand-body #'(e1 e2 ...)
(source-wrap e w s mod) r w mod)))))))
(_ (syntax-violation 'letrec* "bad letrec*" (source-wrap e w s mod)))))
(define (expand-set! e r w s mod)
(syntax-case e ()
((_ id val)
(id? #'id)
(call-with-values
(lambda () (resolve-identifier #'id w r mod #t))
(lambda (type value id-mod)
(case type
((lexical)
(build-lexical-assignment s (syntax->datum #'id) value
(expand #'val r w mod)))
((global)
(build-global-assignment s value (expand #'val r w mod) id-mod))
((macro)
(if (procedure-property value 'variable-transformer)
;; As syntax-type does, call expand-macro with
;; the mod of the expression. Hmm.
(expand (expand-macro value e r w s #f mod) r empty-wrap mod)
(syntax-violation 'set! "not a variable transformer"
(wrap e w mod)
(wrap #'id w id-mod))))
((displaced-lexical)
(syntax-violation 'set! "identifier out of context"
(wrap #'id w mod)))
(else
(syntax-violation 'set! "bad set!" (source-wrap e w s mod)))))))
((_ (head tail ...) val)
(call-with-values
(lambda () (syntax-type #'head r empty-wrap no-source #f mod #t))
(lambda (type value ee* ee ww ss modmod)
(case type
((module-ref)
(let ((val (expand #'val r w mod)))
(call-with-values (lambda () (value #'(head tail ...) r w mod))
(lambda (e r w s* mod)
(syntax-case e ()
(e (id? #'e)
(build-global-assignment s (syntax->datum #'e)
val mod)))))))
(else
(build-call s
(expand #'(setter head) r w mod)
(map (lambda (e) (expand e r w mod))
#'(tail ... val))))))))
(_ (syntax-violation 'set! "bad set!" (source-wrap e w s mod)))))
(define (expand-public-ref e r w mod)
(syntax-case e ()
((_ (mod ...) id)
(and (and-map id? #'(mod ...)) (id? #'id))
;; Strip the wrap from the identifier and return top-wrap
;; so that the identifier will not be captured by lexicals.
(values (syntax->datum #'id) r top-wrap #f
(syntax->datum
#'(public mod ...))))))
(define (expand-private-ref e r w mod)
(define (remodulate x mod)
(cond ((pair? x)
(cons (remodulate (car x) mod)
(remodulate (cdr x) mod)))
((syntax? x)
(make-syntax
(remodulate (syntax-expression x) mod)
(syntax-wrap x)
;; hither the remodulation
mod
(syntax-sourcev x)))
((vector? x)
(let* ((n (vector-length x)) (v (make-vector n)))
(do ((i 0 (1+ i)))
((= i n) v)
(vector-set! v i (remodulate (vector-ref x i) mod)))))
(else x)))
(syntax-case e (@@ primitive)
((_ primitive id)
(and (id? #'id)
(equal? (cdr (or (and (syntax? #'id)
(syntax-module #'id))
mod))
'(guile)))
;; Strip the wrap from the identifier and return top-wrap
;; so that the identifier will not be captured by lexicals.
(values (syntax->datum #'id) r top-wrap #f '(primitive)))
((_ (mod ...) id)
(and (and-map id? #'(mod ...)) (id? #'id))
;; Strip the wrap from the identifier and return top-wrap
;; so that the identifier will not be captured by lexicals.
(values (syntax->datum #'id) r top-wrap #f
(syntax->datum
#'(private mod ...))))
((_ @@ (mod ...) exp)
(and-map id? #'(mod ...))
;; This is a special syntax used to support R6RS library forms.
;; Unlike the syntax above, the last item is not restricted to
;; be a single identifier, and the syntax objects are kept
;; intact, with only their module changed.
(let ((mod (syntax->datum #'(private mod ...))))
(values (remodulate #'exp mod)
r w (source-annotation #'exp)
mod)))))
(define (expand-if e r w s mod)
(syntax-case e ()
((_ test then)
(build-conditional
s
(expand #'test r w mod)
(expand #'then r w mod)
(build-void no-source)))
((_ test then else)
(build-conditional
s
(expand #'test r w mod)
(expand #'then r w mod)
(expand #'else r w mod)))))
(define expand-syntax-case
(let ()
(define (convert-pattern pattern keys ellipsis?)
;; accepts pattern & keys
;; returns $sc-dispatch pattern & ids
(define cvt*
(lambda (p* n ids)
(syntax-case p* ()
((x . y)
(call-with-values
(lambda () (cvt* #'y n ids))
(lambda (y ids)
(call-with-values
(lambda () (cvt #'x n ids))
(lambda (x ids)
(values (cons x y) ids))))))
(_ (cvt p* n ids)))))
(define (v-reverse x)
(let loop ((r '()) (x x))
(if (not (pair? x))
(values r x)
(loop (cons (car x) r) (cdr x)))))
(define cvt
(lambda (p n ids)
(if (id? p)
(cond
((bound-id-member? p keys)
(values (vector 'free-id p) ids))
((free-id=? p #'_)
(values '_ ids))
(else
(values 'any (cons (cons p n) ids))))
(syntax-case p ()
((x dots)
(ellipsis? (syntax dots))
(call-with-values
(lambda () (cvt (syntax x) (1+ n) ids))
(lambda (p ids)
(values (if (eq? p 'any) 'each-any (vector 'each p))
ids))))
((x dots . ys)
(ellipsis? (syntax dots))
(call-with-values
(lambda () (cvt* (syntax ys) n ids))
(lambda (ys ids)
(call-with-values
(lambda () (cvt (syntax x) (+ n 1) ids))
(lambda (x ids)
(call-with-values
(lambda () (v-reverse ys))
(lambda (ys e)
(values `#(each+ ,x ,ys ,e)
ids))))))))
((x . y)
(call-with-values
(lambda () (cvt (syntax y) n ids))
(lambda (y ids)
(call-with-values
(lambda () (cvt (syntax x) n ids))
(lambda (x ids)
(values (cons x y) ids))))))
(() (values '() ids))
(#(x ...)
(call-with-values
(lambda () (cvt (syntax (x ...)) n ids))
(lambda (p ids) (values (vector 'vector p) ids))))
(x (values (vector 'atom (strip p)) ids))))))
(cvt pattern 0 '()))
(define (build-dispatch-call pvars exp y r mod)
(let ((ids (map car pvars)) (levels (map cdr pvars)))
(let ((labels (gen-labels ids)) (new-vars (map gen-var ids)))
(build-primcall
no-source
'apply
(list (build-simple-lambda no-source (map syntax->datum ids) #f new-vars '()
(expand exp
(extend-env
labels
(map (lambda (var level)
(make-binding 'syntax `(,var . ,level)))
new-vars
(map cdr pvars))
r)
(make-binding-wrap ids labels empty-wrap)
mod))
y)))))
(define (gen-clause x keys clauses r pat fender exp mod)
(call-with-values
(lambda () (convert-pattern pat keys (lambda (e) (ellipsis? e r mod))))
(lambda (p pvars)
(cond
((not (and-map (lambda (x) (not (ellipsis? (car x) r mod))) pvars))
(syntax-violation 'syntax-case "misplaced ellipsis" pat))
((not (distinct-bound-ids? (map car pvars)))
(syntax-violation 'syntax-case "duplicate pattern variable" pat))
(else
(let ((y (gen-var 'tmp)))
;; fat finger binding and references to temp variable y
(build-call no-source
(build-simple-lambda no-source (list 'tmp) #f (list y) '()
(let ((y (build-lexical-reference no-source 'tmp y)))
(build-conditional no-source
(syntax-case fender ()
(#t y)
(_ (build-conditional no-source
y
(build-dispatch-call pvars fender y r mod)
(build-data no-source #f))))
(build-dispatch-call pvars exp y r mod)
(gen-syntax-case x keys clauses r mod))))
(list (if (eq? p 'any)
(build-primcall no-source 'list (list x))
(build-primcall no-source '$sc-dispatch
(list x (build-data no-source p))))))))))))
(define (gen-syntax-case x keys clauses r mod)
(if (null? clauses)
(build-primcall no-source 'syntax-violation
(list (build-data no-source #f)
(build-data no-source
"source expression failed to match any pattern")
x))
(syntax-case (car clauses) ()
((pat exp)
(if (and (id? #'pat)
(and-map (lambda (x) (not (free-id=? #'pat x)))
(cons #'(... ...) keys)))
(if (free-id=? #'pat #'_)
(expand #'exp r empty-wrap mod)
(let ((labels (list (gen-label)))
(var (gen-var #'pat)))
(build-call no-source
(build-simple-lambda
no-source (list (syntax->datum #'pat)) #f (list var)
'()
(expand #'exp
(extend-env labels
(list (make-binding 'syntax `(,var . 0)))
r)
(make-binding-wrap #'(pat)
labels empty-wrap)
mod))
(list x))))
(gen-clause x keys (cdr clauses) r
#'pat #t #'exp mod)))
((pat fender exp)
(gen-clause x keys (cdr clauses) r
#'pat #'fender #'exp mod))
(_ (syntax-violation 'syntax-case "invalid clause"
(car clauses))))))
(lambda (e r w s mod)
(let ((e (source-wrap e w s mod)))
(syntax-case e ()
((_ val (key ...) m ...)
(if (and-map (lambda (x) (and (id? x) (not (ellipsis? x r mod))))
#'(key ...))
(let ((x (gen-var 'tmp)))
;; fat finger binding and references to temp variable x
(build-call s
(build-simple-lambda no-source (list 'tmp) #f (list x) '()
(gen-syntax-case (build-lexical-reference no-source 'tmp x)
#'(key ...) #'(m ...)
r
mod))
(list (expand #'val r empty-wrap mod))))
(syntax-violation 'syntax-case "invalid literals list" e))))))))
(global-extend 'local-syntax 'letrec-syntax #t)
(global-extend 'local-syntax 'let-syntax #f)
(global-extend 'core 'syntax-parameterize expand-syntax-parameterize)
(global-extend 'core 'quote expand-quote)
(global-extend 'core 'quote-syntax expand-quote-syntax)
(global-extend 'core 'syntax expand-syntax)
(global-extend 'core 'lambda expand-lambda)
(global-extend 'core 'lambda* expand-lambda*)
(global-extend 'core 'case-lambda expand-case-lambda)
(global-extend 'core 'case-lambda* expand-case-lambda*)
(global-extend 'core 'with-ellipsis expand-with-ellipsis)
(global-extend 'core 'let expand-let)
(global-extend 'core 'letrec expand-letrec)
(global-extend 'core 'letrec* expand-letrec*)
(global-extend 'core 'set! expand-set!)
(global-extend 'module-ref '@ expand-public-ref)
(global-extend 'module-ref '@@ expand-private-ref)
(global-extend 'core 'if expand-if)
(global-extend 'begin 'begin '())
(global-extend 'define 'define '())
(global-extend 'define-syntax 'define-syntax '())
(global-extend 'define-syntax-parameter 'define-syntax-parameter '())
(global-extend 'eval-when 'eval-when '())
(global-extend 'core 'syntax-case expand-syntax-case)
(define-syntax define/override
(syntax-rules ()
((_ (id . args) . body) (define/override id (lambda args . body)))
((_ id exp) (set! id exp))))
(define-syntax define*/override
(syntax-rules ()
((_ (id . args) . body) (define/override id (lambda* args . body)))))
;; The portable macroexpand seeds expand-top's mode m with 'e (for
;; evaluating) and esew (which stands for "eval syntax expanders
;; when") with '(eval). In Chez Scheme, m is set to 'c instead of e
;; if we are compiling a file, and esew is set to
;; (eval-syntactic-expanders-when), which defaults to the list
;; '(compile load eval). This means that, by default, top-level
;; syntactic definitions are evaluated immediately after they are
;; expanded, and the expanded definitions are also residualized into
;; the object file if we are compiling a file.
(define*/override (macroexpand x #:optional (m 'e) (esew '(eval)))
(define (unstrip x)
(define (annotate result)
(let ((props (source-properties x)))
(if (pair? props)
(datum->syntax #f result #:source props)
result)))
(cond
((pair? x)
(annotate (cons (unstrip (car x)) (unstrip (cdr x)))))
((vector? x)
(let ((v (make-vector (vector-length x))))
(annotate (list->vector (map unstrip (vector->list x))))))
((syntax? x) x)
(else (annotate x))))
(expand-top-sequence (list (unstrip x)) null-env top-wrap #f m esew
(cons 'hygiene (module-name (current-module)))))
(define/override (identifier? x)
(nonsymbol-id? x))
(define*/override (datum->syntax id datum #:key source)
(define (props->sourcev alist)
(and (pair? alist)
(vector (assq-ref alist 'filename)
(assq-ref alist 'line)
(assq-ref alist 'column))))
(make-syntax datum
(if id
(syntax-wrap id)
empty-wrap)
(if id
(syntax-module id)
#f)
(cond
((not source)
(props->sourcev (source-properties datum)))
((and (list? source) (and-map pair? source))
(props->sourcev source))
((and (vector? source) (= 3 (vector-length source)))
source)
(else (syntax-sourcev source)))))
(define/override (syntax->datum x)
;; accepts any object, since syntax objects may consist partially
;; or entirely of unwrapped, nonsymbolic data
(strip x))
(define/override (generate-temporaries ls)
(arg-check list? ls 'generate-temporaries)
(let ((mod (cons 'hygiene (module-name (current-module)))))
(map (lambda (x)
(wrap (gen-var 't) top-wrap mod))
ls)))
(define/override (free-identifier=? x y)
(arg-check nonsymbol-id? x 'free-identifier=?)
(arg-check nonsymbol-id? y 'free-identifier=?)
(free-id=? x y))
(define/override (bound-identifier=? x y)
(arg-check nonsymbol-id? x 'bound-identifier=?)
(arg-check nonsymbol-id? y 'bound-identifier=?)
(bound-id=? x y))
(define*/override (syntax-violation who message form #:optional subform)
(arg-check (lambda (x) (or (not x) (string? x) (symbol? x)))
who 'syntax-violation)
(arg-check string? message 'syntax-violation)
(throw 'syntax-error who message
(sourcev->alist
(or (source-annotation subform)
(source-annotation form)))
(strip form)
(strip subform)))
(let ()
(define (%syntax-module id)
(arg-check nonsymbol-id? id 'syntax-module)
(let ((mod (syntax-module id)))
(and mod
(not (equal? mod '(primitive)))
(cdr mod))))
(define* (syntax-local-binding id #:key (resolve-syntax-parameters? #t))
(arg-check nonsymbol-id? id 'syntax-local-binding)
(with-transformer-environment
(lambda (e r w s rib mod)
(define (strip-anti-mark w)
(let ((ms (wrap-marks w)) (s (wrap-subst w)))
(if (and (pair? ms) (eq? (car ms) the-anti-mark))
;; output is from original text
(make-wrap (cdr ms) (if rib (cons rib (cdr s)) (cdr s)))
;; output introduced by macro
(make-wrap ms (if rib (cons rib s) s)))))
(call-with-values (lambda ()
(resolve-identifier
(syntax-expression id)
(strip-anti-mark (syntax-wrap id))
r
(or (syntax-module id) mod)
resolve-syntax-parameters?))
(lambda (type value mod)
(case type
((lexical) (values 'lexical value))
((macro) (values 'macro value))
((syntax-parameter) (values 'syntax-parameter value))
((syntax) (values 'pattern-variable value))
((displaced-lexical) (values 'displaced-lexical #f))
((global)
(if (equal? mod '(primitive))
(values 'primitive value)
(values 'global (cons value (cdr mod)))))
((ellipsis)
(values 'ellipsis
(wrap-syntax value (anti-mark (syntax-wrap value))
mod)))
(else (values 'other #f))))))))
(define (syntax-locally-bound-identifiers id)
(arg-check nonsymbol-id? id 'syntax-locally-bound-identifiers)
(locally-bound-identifiers (syntax-wrap id)
(syntax-module id)))
;; Using define! instead of set! to avoid warnings at
;; compile-time, after the variables are stolen away into (system
;; syntax). See the end of boot-9.scm.
;;
(define! '%syntax-module %syntax-module)
(define! 'syntax-local-binding syntax-local-binding)
(define! 'syntax-locally-bound-identifiers syntax-locally-bound-identifiers))
(define/override ($sc-dispatch e p)
;; $sc-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.
;; The expression is matched with the pattern as follows:
;; pattern: matches:
;; () empty list
;; any anything
;; (<pattern>1 . <pattern>2) (<pattern>1 . <pattern>2)
;; each-any (any*)
;; #(free-id <key>) <key> with free-identifier=?
;; #(each <pattern>) (<pattern>*)
;; #(each+ p1 (p2_1 ... p2_n) p3) (p1* (p2_n ... p2_1) . p3)
;; #(vector <pattern>) (list->vector <pattern>)
;; #(atom <object>) <object> with "equal?"
;; Vector cops out to pair under assumption that vectors are rare. If
;; not, should convert to:
;; #(vector <pattern>*) #(<pattern>*)
(define (match-each e p w mod)
(cond
((pair? e)
(let ((first (match (car e) p w '() mod)))
(and first
(let ((rest (match-each (cdr e) p w mod)))
(and rest (cons first rest))))))
((null? e) '())
((syntax? e)
(match-each (syntax-expression e)
p
(join-wraps w (syntax-wrap e))
(or (syntax-module e) mod)))
(else #f)))
(define (match-each+ e x-pat y-pat z-pat w r mod)
(let f ((e e) (w w))
(cond
((pair? e)
(call-with-values (lambda () (f (cdr e) w))
(lambda (xr* y-pat r)
(if r
(if (null? y-pat)
(let ((xr (match (car e) x-pat w '() mod)))
(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 mod)))
(values #f #f #f)))))
((syntax? e)
(f (syntax-expression e)
(join-wraps w (syntax-wrap e))))
(else
(values '() y-pat (match e z-pat w r mod))))))
(define (match-each-any e w mod)
(cond
((pair? e)
(let ((l (match-each-any (cdr e) w mod)))
(and l (cons (wrap (car e) w mod) l))))
((null? e) '())
((syntax? e)
(match-each-any (syntax-expression e)
(join-wraps w (syntax-wrap e))
mod))
(else #f)))
(define (match-empty p r)
(cond
((null? p) r)
((eq? 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 r* r)
(if (null? (car r*))
r
(cons (map car r*) (combine (map cdr r*) r))))
(define (match* e p w r mod)
(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 mod)
mod)))
((eq? p 'each-any)
(let ((l (match-each-any e w mod))) (and l (cons l r))))
(else
(case (vector-ref p 0)
((each)
(if (null? e)
(match-empty (vector-ref p 1) r)
(let ((l (match-each e (vector-ref p 1) w mod)))
(and l
(let collect ((l l))
(if (null? (car l))
r
(cons (map car l) (collect (map cdr l)))))))))
((each+)
(call-with-values
(lambda ()
(match-each+ e (vector-ref p 1) (vector-ref p 2) (vector-ref p 3) w r mod))
(lambda (xr* y-pat r)
(and r
(null? y-pat)
(if (null? xr*)
(match-empty (vector-ref p 1) r)
(combine xr* r))))))
((free-id) (and (id? e) (free-id=? (wrap e w mod) (vector-ref p 1)) r))
((atom) (and (equal? (vector-ref p 1) (strip e)) r))
((vector)
(and (vector? e)
(match (vector->list e) (vector-ref p 1) w r mod)))))))
(define (match e p w r mod)
(cond
((not r) #f)
((eq? p '_) r)
((eq? p 'any) (cons (wrap e w mod) r))
((syntax? e)
(match*
(syntax-expression e)
p
(join-wraps w (syntax-wrap e))
r
(or (syntax-module e) mod)))
(else (match* e p w r mod))))
(cond
((eq? p 'any) (list e))
((eq? p '_) '())
((syntax? e)
(match* (syntax-expression e)
p (syntax-wrap e) '() (syntax-module e)))
(else (match* e p empty-wrap '() #f)))))
(define-syntax with-syntax
(lambda (x)
(syntax-case x ()
((_ () e1 e2 ...)
#'(let () e1 e2 ...))
((_ ((out in)) e1 e2 ...)
#'(syntax-case in ()
(out (let () e1 e2 ...))))
((_ ((out in) ...) e1 e2 ...)
#'(syntax-case (list in ...) ()
((out ...) (let () e1 e2 ...)))))))
(define-syntax syntax-error
(lambda (x)
(syntax-case x ()
;; Extended internal syntax which provides the original form
;; as the first operand, for improved error reporting.
((_ (keyword . operands) message arg ...)
(string? (syntax->datum #'message))
(syntax-violation (syntax->datum #'keyword)
(string-join (cons (syntax->datum #'message)
(map (lambda (x)
(object->string
(syntax->datum x)))
#'(arg ...))))
(and (syntax->datum #'keyword)
#'(keyword . operands))))
;; Standard R7RS syntax
((_ message arg ...)
(string? (syntax->datum #'message))
#'(syntax-error (#f) message arg ...)))))
(define-syntax syntax-rules
(lambda (xx)
(define (expand-clause clause)
;; Convert a 'syntax-rules' clause into a 'syntax-case' clause.
(syntax-case clause (syntax-error)
;; If the template is a 'syntax-error' form, use the extended
;; internal syntax, which adds the original form as the first
;; operand for improved error reporting.
(((keyword . pattern) (syntax-error message arg ...))
(string? (syntax->datum #'message))
#'((dummy . pattern) #'(syntax-error (dummy . pattern) message arg ...)))
;; Normal case
(((keyword . pattern) template)
#'((dummy . pattern) #'template))))
(define (expand-syntax-rules dots keys docstrings clauses)
(with-syntax
(((k ...) keys)
((docstring ...) docstrings)
((((keyword . pattern) template) ...) clauses)
((clause ...) (map expand-clause clauses)))
(with-syntax
((form #'(lambda (x)
docstring ... ; optional docstring
#((macro-type . syntax-rules)
(patterns pattern ...)) ; embed patterns as procedure metadata
(syntax-case x (k ...)
clause ...))))
(if dots
(with-syntax ((dots dots))
#'(with-ellipsis dots form))
#'form))))
(syntax-case xx ()
((_ (k ...) ((keyword . pattern) template) ...)
(expand-syntax-rules #f #'(k ...) #'() #'(((keyword . pattern) template) ...)))
((_ (k ...) docstring ((keyword . pattern) template) ...)
(string? (syntax->datum #'docstring))
(expand-syntax-rules #f #'(k ...) #'(docstring) #'(((keyword . pattern) template) ...)))
((_ dots (k ...) ((keyword . pattern) template) ...)
(identifier? #'dots)
(expand-syntax-rules #'dots #'(k ...) #'() #'(((keyword . pattern) template) ...)))
((_ dots (k ...) docstring ((keyword . pattern) template) ...)
(and (identifier? #'dots) (string? (syntax->datum #'docstring)))
(expand-syntax-rules #'dots #'(k ...) #'(docstring) #'(((keyword . pattern) template) ...))))))
(define-syntax define-syntax-rule
(lambda (x)
(syntax-case x ()
((_ (name . pattern) template)
#'(define-syntax name
(syntax-rules ()
((_ . pattern) template))))
((_ (name . pattern) docstring template)
(string? (syntax->datum #'docstring))
#'(define-syntax name
(syntax-rules ()
docstring
((_ . pattern) template)))))))
(define-syntax let*
(lambda (x)
(syntax-case x ()
((let* ((x v) ...) e1 e2 ...)
(and-map identifier? #'(x ...))
(let f ((bindings #'((x v) ...)))
(if (null? bindings)
#'(let () e1 e2 ...)
(with-syntax ((body (f (cdr bindings)))
(binding (car bindings)))
#'(let (binding) body))))))))
(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 call-with-include-port
(let ((syntax-dirname (lambda (stx)
(define src (syntax-source stx))
(define filename (and src (assq-ref src 'filename)))
(and (string? filename)
(dirname filename)))))
(lambda* (filename proc #:key (dirname (syntax-dirname filename)))
"Like @code{call-with-input-file}, except relative paths are
searched relative to the @var{dirname} instead of the current working
directory. Also, @var{filename} can be a syntax object; in that case,
and if @var{dirname} is not specified, the @code{syntax-source} of
@var{filename} is used to obtain a base directory for relative file
names."
(let* ((filename (syntax->datum filename))
(p (open-input-file
(cond ((absolute-file-name? filename)
filename)
(dirname
(in-vicinity dirname filename))
(else
(error
"attempt to include relative file name but could not determine base dir")))))
(enc (file-encoding p)))
;; Choose the input encoding deterministically.
(set-port-encoding! p (or enc "UTF-8"))
(call-with-values (lambda () (proc p))
(lambda results
(close-port p)
(apply values results)))))))
(define-syntax include
(lambda (stx)
(syntax-case stx ()
((_ filename)
(call-with-include-port
#'filename
(lambda (p)
;; In Guile, (cons #'a #'b) is the same as #'(a . b).
(cons #'begin
(let lp ()
(let ((x (read-syntax p)))
(if (eof-object? x)
#'()
(cons (datum->syntax #'filename x) (lp))))))))))))
(define-syntax include-from-path
(lambda (x)
(syntax-case x ()
((k filename)
(let ((fn (syntax->datum #'filename)))
(with-syntax ((fn (datum->syntax
#'filename
(canonicalize-path
(or (%search-load-path fn)
(syntax-violation 'include-from-path
"file not found in path"
x #'filename))))))
#'(include fn)))))))
(define-syntax unquote
(lambda (x)
(syntax-violation 'unquote
"expression not valid outside of quasiquote"
x)))
(define-syntax unquote-splicing
(lambda (x)
(syntax-violation 'unquote-splicing
"expression not valid outside of quasiquote"
x)))
(define (make-variable-transformer proc)
(if (procedure? proc)
(let ((trans (lambda (x)
#((macro-type . variable-transformer))
(proc x))))
(set-procedure-property! trans 'variable-transformer #t)
trans)
(error "variable transformer not a procedure" proc)))
(define-syntax identifier-syntax
(lambda (xx)
(syntax-case xx (set!)
((_ e)
#'(lambda (x)
#((macro-type . identifier-syntax))
(syntax-case x ()
(id
(identifier? #'id)
#'e)
((_ x (... ...))
#'(e x (... ...))))))
((_ (id exp1) ((set! var val) exp2))
(and (identifier? #'id) (identifier? #'var))
#'(make-variable-transformer
(lambda (x)
#((macro-type . variable-transformer))
(syntax-case x (set!)
((set! var val) #'exp2)
((id x (... ...)) #'(exp1 x (... ...)))
(id (identifier? #'id) #'exp1))))))))
(define-syntax define*
(lambda (x)
(syntax-case x ()
((_ (id . args) b0 b1 ...)
#'(define id (lambda* args b0 b1 ...)))
((_ id val) (identifier? #'id)
#'(define id val)))))