mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-29 19:30:36 +02:00
* NEWS: * doc/ref/api-control.texi: * doc/ref/api-data.texi: * doc/ref/api-debug.texi: * doc/ref/api-deprecated.texi: * doc/ref/api-evaluation.texi: * doc/ref/api-foreign.texi: * doc/ref/api-i18n.texi: * doc/ref/api-io.texi: * doc/ref/api-languages.texi: * doc/ref/api-macros.texi: * doc/ref/api-memory.texi: * doc/ref/api-modules.texi: * doc/ref/api-options.texi: * doc/ref/api-peg.texi: * doc/ref/api-procedures.texi: * doc/ref/api-scheduling.texi: * doc/ref/api-undocumented.texi: * doc/ref/api-utility.texi: * doc/ref/expect.texi: * doc/ref/goops.texi: * doc/ref/misc-modules.texi: * doc/ref/posix.texi: * doc/ref/repl-modules.texi: * doc/ref/scheme-ideas.texi: * doc/ref/scheme-scripts.texi: * doc/ref/srfi-modules.texi: * gc-benchmarks/larceny/dynamic.sch: * gc-benchmarks/larceny/twobit-input-long.sch: * gc-benchmarks/larceny/twobit.sch: * libguile/gc.h: * libguile/ioext.c: * libguile/list.c: * libguile/options.c: * libguile/posix.c: * libguile/threads.c: * module/ice-9/boot-9.scm: * module/ice-9/optargs.scm: * module/ice-9/ports.scm: * module/ice-9/pretty-print.scm: * module/ice-9/psyntax.scm: * module/language/elisp/parser.scm: * module/language/tree-il/compile-bytecode.scm: * module/srfi/srfi-37.scm: * module/srfi/srfi-43.scm: * module/statprof.scm: * module/texinfo/reflection.scm: * test-suite/tests/eval.test: * test-suite/tests/fluids.test: Fix typos. Signed-off-by: Ludovic Courtès <ludo@gnu.org>
3383 lines
150 KiB
Scheme
3383 lines
150 KiB
Scheme
;;;; -*-scheme-*-
|
||
;;;;
|
||
;;;; Copyright (C) 2001, 2003, 2006, 2009, 2010-2022
|
||
;;;; 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 library; if not, write to the Free Software
|
||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
;;;;
|
||
|
||
|
||
;;; Portable implementation of syntax-case
|
||
;;; 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.
|
||
|
||
;;; Modified by Mikael Djurfeldt <djurfeldt@nada.kth.se> according
|
||
;;; to the ChangeLog distributed in the same directory as this file:
|
||
;;; 1997-08-19, 1997-09-03, 1997-09-10, 2000-08-13, 2000-08-24,
|
||
;;; 2000-09-12, 2001-03-08
|
||
|
||
;;; Modified by Andy Wingo <wingo@pobox.com> according to the Git
|
||
;;; revision control logs corresponding to this file: 2009, 2010.
|
||
|
||
;;; Modified by Mark H Weaver <mhw@netris.org> according to the Git
|
||
;;; revision control logs corresponding to this file: 2012, 2013.
|
||
|
||
|
||
;;; 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 the syntax-case expander, macroexpand, and a set
|
||
;;; of associated syntactic forms and procedures. Of these, the
|
||
;;; following are documented in The Scheme Programming Language,
|
||
;;; Fourth Edition (R. Kent Dybvig, MIT Press, 2009), and in the
|
||
;;; R6RS:
|
||
;;;
|
||
;;; bound-identifier=?
|
||
;;; datum->syntax
|
||
;;; define-syntax
|
||
;;; syntax-parameterize
|
||
;;; free-identifier=?
|
||
;;; generate-temporaries
|
||
;;; identifier?
|
||
;;; identifier-syntax
|
||
;;; let-syntax
|
||
;;; letrec-syntax
|
||
;;; syntax
|
||
;;; syntax-case
|
||
;;; syntax->datum
|
||
;;; syntax-rules
|
||
;;; with-syntax
|
||
;;;
|
||
;;; Additionally, the expander provides definitions for a number of core
|
||
;;; Scheme syntactic bindings, such as `let', `lambda', and the like.
|
||
|
||
;;; The remaining exports are listed below:
|
||
;;;
|
||
;;; (macroexpand datum)
|
||
;;; if datum represents a valid expression, macroexpand returns an
|
||
;;; expanded version of datum in a core language that includes no
|
||
;;; syntactic abstractions. The core language includes begin,
|
||
;;; define, if, lambda, letrec, quote, and set!.
|
||
;;; (eval-when situations expr ...)
|
||
;;; conditionally evaluates expr ... at compile-time or run-time
|
||
;;; depending upon situations (see the Chez Scheme System Manual,
|
||
;;; Revision 3, for a complete description)
|
||
;;; (syntax-violation who message form [subform])
|
||
;;; used to report errors found during expansion
|
||
;;; ($sc-dispatch e p)
|
||
;;; used by expanded code to handle syntax-case matching
|
||
|
||
;;; 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.
|
||
;;; This may not be appropriate for implementations in which the
|
||
;;; model is that bindings are created by definitions, as opposed to
|
||
;;; one in which initial values are assigned by definitions.
|
||
|
||
;;; Identifiers and syntax objects are implemented as vectors for
|
||
;;; portability. As a result, it is possible to "forge" syntax objects.
|
||
|
||
;;; The implementation of generate-temporaries assumes that it is
|
||
;;; possible to generate globally unique symbols (gensyms).
|
||
|
||
;;; The source location associated with incoming expressions is tracked
|
||
;;; via the source-properties mechanism, a weak map from expression to
|
||
;;; source information. At times the source is separated from the
|
||
;;; expression; see the note below about "efficiency and confusion".
|
||
|
||
|
||
;;; Bootstrapping:
|
||
|
||
;;; 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 lp ((n 0))
|
||
(let ((vtable (vector-ref %expanded-vtables n))
|
||
(stem (syntax->datum #'stem)))
|
||
(if (eq? (struct-ref vtable (+ vtable-offset-user 0)) stem)
|
||
#`(begin
|
||
(define (#,(datum->syntax x (symbol-append stem '?)) x)
|
||
(and (struct? x)
|
||
(eq? (struct-vtable x)
|
||
(vector-ref %expanded-vtables #,n))))
|
||
#,@(map
|
||
(lambda (f)
|
||
(let ((get (datum->syntax x (symbol-append stem '- f)))
|
||
(set (datum->syntax x (symbol-append 'set- stem '- f '!)))
|
||
(idx (list-index (struct-ref vtable
|
||
(+ vtable-offset-user 2))
|
||
f)))
|
||
#`(begin
|
||
(define (#,get x)
|
||
(struct-ref x #,idx))
|
||
(define (#,set x v)
|
||
(struct-set! x #,idx v)))))
|
||
(syntax->datum #'(field ...))))
|
||
(lp (1+ n)))))))))
|
||
|
||
(define-syntax define-structure
|
||
(lambda (x)
|
||
(define construct-name
|
||
(lambda (template-identifier . args)
|
||
(datum->syntax
|
||
template-identifier
|
||
(string->symbol
|
||
(apply string-append
|
||
(map (lambda (x)
|
||
(if (string? x)
|
||
x
|
||
(symbol->string (syntax->datum x))))
|
||
args))))))
|
||
(syntax-case x ()
|
||
((_ (name id1 ...))
|
||
(and-map identifier? #'(name id1 ...))
|
||
(with-syntax
|
||
((constructor (construct-name #'name "make-" #'name))
|
||
(predicate (construct-name #'name #'name "?"))
|
||
((access ...)
|
||
(map (lambda (x) (construct-name x #'name "-" x))
|
||
#'(id1 ...)))
|
||
((assign ...)
|
||
(map (lambda (x)
|
||
(construct-name x "set-" #'name "-" x "!"))
|
||
#'(id1 ...)))
|
||
(structure-length
|
||
(+ (length #'(id1 ...)) 1))
|
||
((index ...)
|
||
(let f ((i 1) (ids #'(id1 ...)))
|
||
(if (null? ids)
|
||
'()
|
||
(cons i (f (+ i 1) (cdr ids)))))))
|
||
#'(begin
|
||
(define constructor
|
||
(lambda (id1 ...)
|
||
(vector 'name id1 ... )))
|
||
(define predicate
|
||
(lambda (x)
|
||
(and (vector? x)
|
||
(= (vector-length x) structure-length)
|
||
(eq? (vector-ref x 0) 'name))))
|
||
(define access
|
||
(lambda (x)
|
||
(vector-ref x index)))
|
||
...
|
||
(define assign
|
||
(lambda (x update)
|
||
(vector-set! x index update)))
|
||
...))))))
|
||
|
||
(let ()
|
||
(define-expansion-constructors)
|
||
(define-expansion-accessors lambda meta)
|
||
|
||
;; hooks to nonportable run-time helpers
|
||
(begin
|
||
(define-syntax fx+ (identifier-syntax +))
|
||
(define-syntax fx- (identifier-syntax -))
|
||
(define-syntax fx= (identifier-syntax =))
|
||
(define-syntax fx< (identifier-syntax <))
|
||
|
||
(define top-level-eval-hook
|
||
(lambda (x mod)
|
||
(primitive-eval x)))
|
||
|
||
(define local-eval-hook
|
||
(lambda (x mod)
|
||
(primitive-eval x)))
|
||
|
||
;; Capture syntax-session-id before we shove it off into a module.
|
||
(define session-id
|
||
(let ((v (module-variable (current-module) 'syntax-session-id)))
|
||
(lambda ()
|
||
((variable-ref v))))))
|
||
|
||
(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 (not (assq 'name meta))
|
||
(set-lambda-meta! val (acons 'name name meta))))))
|
||
|
||
;; output constructors
|
||
(define build-void
|
||
(lambda (sourcev)
|
||
(make-void sourcev)))
|
||
|
||
(define build-call
|
||
(lambda (sourcev fun-exp arg-exps)
|
||
(make-call sourcev fun-exp arg-exps)))
|
||
|
||
(define build-conditional
|
||
(lambda (sourcev test-exp then-exp else-exp)
|
||
(make-conditional sourcev test-exp then-exp else-exp)))
|
||
|
||
(define build-lexical-reference
|
||
(lambda (type sourcev name var)
|
||
(make-lexical-ref sourcev name var)))
|
||
|
||
(define build-lexical-assignment
|
||
(lambda (sourcev name var exp)
|
||
(maybe-name-value! name exp)
|
||
(make-lexical-set sourcev name var exp)))
|
||
|
||
(define (analyze-variable mod var modref-cont bare-cont)
|
||
(if (not mod)
|
||
(bare-cont #f var)
|
||
(let ((kind (car mod))
|
||
(mod (cdr mod)))
|
||
(case kind
|
||
((public) (modref-cont mod var #t))
|
||
((private hygiene) (if (equal? mod (module-name (current-module)))
|
||
(bare-cont mod var)
|
||
(modref-cont mod var #f)))
|
||
((bare) (bare-cont var))
|
||
((primitive)
|
||
(syntax-violation #f "primitive not in operator position" var))
|
||
(else (syntax-violation #f "bad module kind" var mod))))))
|
||
|
||
(define build-global-reference
|
||
(lambda (sourcev var mod)
|
||
(analyze-variable
|
||
mod var
|
||
(lambda (mod var public?)
|
||
(make-module-ref sourcev mod var public?))
|
||
(lambda (mod var)
|
||
(make-toplevel-ref sourcev mod var)))))
|
||
|
||
(define build-global-assignment
|
||
(lambda (sourcev var exp mod)
|
||
(maybe-name-value! var exp)
|
||
(analyze-variable
|
||
mod var
|
||
(lambda (mod var public?)
|
||
(make-module-set sourcev mod var public? exp))
|
||
(lambda (mod var)
|
||
(make-toplevel-set sourcev mod var exp)))))
|
||
|
||
(define build-global-definition
|
||
(lambda (sourcev mod var exp)
|
||
(maybe-name-value! var exp)
|
||
(make-toplevel-define sourcev (and mod (cdr mod)) var exp)))
|
||
|
||
(define build-simple-lambda
|
||
(lambda (src req rest vars meta exp)
|
||
(make-lambda src
|
||
meta
|
||
;; hah, a case in which kwargs would be nice.
|
||
(make-lambda-case
|
||
;; src req opt rest kw inits vars body else
|
||
src req #f rest #f '() vars exp #f))))
|
||
|
||
(define build-case-lambda
|
||
(lambda (src meta body)
|
||
(make-lambda src meta body)))
|
||
|
||
(define build-lambda-case
|
||
;; req := (name ...)
|
||
;; opt := (name ...) | #f
|
||
;; rest := name | #f
|
||
;; kw := (allow-other-keys? (keyword name var) ...) | #f
|
||
;; inits: (init ...)
|
||
;; vars: (sym ...)
|
||
;; vars map to named arguments in the following order:
|
||
;; required, optional (positional), rest, keyword.
|
||
;; the body of a lambda: anything, already expanded
|
||
;; else: lambda-case | #f
|
||
(lambda (src req opt rest kw inits vars body else-case)
|
||
(make-lambda-case src req opt rest kw inits vars body else-case)))
|
||
|
||
(define build-primcall
|
||
(lambda (src name args)
|
||
(make-primcall src name args)))
|
||
|
||
(define build-primref
|
||
(lambda (src name)
|
||
(make-primitive-ref src name)))
|
||
|
||
(define (build-data src exp)
|
||
(make-const src exp))
|
||
|
||
(define build-sequence
|
||
(lambda (src exps)
|
||
(if (null? (cdr exps))
|
||
(car exps)
|
||
(make-seq src (car exps) (build-sequence #f (cdr exps))))))
|
||
|
||
(define build-let
|
||
(lambda (src ids vars val-exps body-exp)
|
||
(for-each maybe-name-value! ids val-exps)
|
||
(if (null? vars)
|
||
body-exp
|
||
(make-let src ids vars val-exps body-exp))))
|
||
|
||
(define build-named-let
|
||
(lambda (src ids vars val-exps body-exp)
|
||
(let ((f (car vars))
|
||
(f-name (car ids))
|
||
(vars (cdr vars))
|
||
(ids (cdr ids)))
|
||
(let ((proc (build-simple-lambda src ids #f vars '() body-exp)))
|
||
(maybe-name-value! f-name proc)
|
||
(for-each maybe-name-value! ids val-exps)
|
||
(make-letrec
|
||
src #f
|
||
(list f-name) (list f) (list proc)
|
||
(build-call src (build-lexical-reference 'fun src f-name f)
|
||
val-exps))))))
|
||
|
||
(define build-letrec
|
||
(lambda (src in-order? ids vars val-exps body-exp)
|
||
(if (null? vars)
|
||
body-exp
|
||
(begin
|
||
(for-each maybe-name-value! ids val-exps)
|
||
(make-letrec src in-order? ids vars val-exps body-exp)))))
|
||
|
||
|
||
(define-syntax-rule (build-lexical-var src id)
|
||
;; Use a per-module counter instead of the global counter of
|
||
;; 'gensym' so that the generated identifier is reproducible.
|
||
(module-gensym (symbol->string id)))
|
||
|
||
(define-syntax no-source (identifier-syntax #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
|
||
(lambda (x)
|
||
;; Normally X is a syntax object. However, if it comes from a
|
||
;; read hash extension, X might be a plain sexp with source
|
||
;; properties.
|
||
(if (syntax? x)
|
||
(syntax-sourcev x)
|
||
(datum-sourcev x))))
|
||
|
||
(define-syntax-rule (arg-check pred? e who)
|
||
(let ((x e))
|
||
(if (not (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> ::= variable returned by build-lexical-var
|
||
|
||
;; a macro is a user-defined syntactic-form. a core is a
|
||
;; system-defined syntactic form. begin, define, define-syntax,
|
||
;; 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
|
||
;; it's scope by the return of a syntax object containing the identifier.
|
||
;; a displaced lexical can also appear when a letrec-syntax-bound
|
||
;; keyword is referenced on the rhs of one of the letrec-syntax clauses.
|
||
;; a displaced lexical should never occur with properly written macros.
|
||
|
||
(define-syntax make-binding
|
||
(syntax-rules (quote)
|
||
((_ type value) (cons type value))
|
||
((_ 'type) '(type))
|
||
((_ type) (cons type '()))))
|
||
(define-syntax-rule (binding-type x)
|
||
(car x))
|
||
(define-syntax-rule (binding-value x)
|
||
(cdr x))
|
||
|
||
(define-syntax null-env (identifier-syntax '()))
|
||
|
||
(define extend-env
|
||
(lambda (labels bindings r)
|
||
(if (null? labels)
|
||
r
|
||
(extend-env (cdr labels) (cdr bindings)
|
||
(cons (cons (car labels) (car bindings)) r)))))
|
||
|
||
(define extend-var-env
|
||
;; variant of extend-env that forms "lexical" binding
|
||
(lambda (labels vars r)
|
||
(if (null? labels)
|
||
r
|
||
(extend-var-env (cdr labels) (cdr vars)
|
||
(cons (cons (car labels) (make-binding 'lexical (car vars))) r)))))
|
||
|
||
;; we use a "macros only" environment in expansion of local macro
|
||
;; definitions so that their definitions can use local macros without
|
||
;; attempting to use other lexical identifiers.
|
||
(define macros-only-env
|
||
(lambda (r)
|
||
(if (null? r)
|
||
'()
|
||
(let ((a (car r)))
|
||
(if (memq (cadr a) '(macro syntax-parameter ellipsis))
|
||
(cons a (macros-only-env (cdr r)))
|
||
(macros-only-env (cdr r)))))))
|
||
|
||
(define global-extend
|
||
(lambda (type sym val)
|
||
(module-define! (current-module)
|
||
sym
|
||
(make-syntax-transformer sym type val))))
|
||
|
||
|
||
;; Conceptually, identifiers are always syntax objects. Internally,
|
||
;; however, the wrap is sometimes maintained separately (a source of
|
||
;; efficiency and confusion), so that symbols are also considered
|
||
;; identifiers by id?. Externally, they are always wrapped.
|
||
|
||
(define nonsymbol-id?
|
||
(lambda (x)
|
||
(and (syntax? x)
|
||
(symbol? (syntax-expression x)))))
|
||
|
||
(define id?
|
||
(lambda (x)
|
||
(cond
|
||
((symbol? x) #t)
|
||
((syntax? x) (symbol? (syntax-expression x)))
|
||
(else #f))))
|
||
|
||
(define-syntax-rule (id-sym-name e)
|
||
(let ((x e))
|
||
(if (syntax? x)
|
||
(syntax-expression x)
|
||
x)))
|
||
|
||
(define id-sym-name&marks
|
||
(lambda (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-syntax make-wrap (identifier-syntax cons))
|
||
(define-syntax wrap-marks (identifier-syntax car))
|
||
(define-syntax wrap-subst (identifier-syntax cdr))
|
||
|
||
;; labels must be comparable with "eq?", have read-write invariance,
|
||
;; and distinct from symbols.
|
||
(define (gen-label)
|
||
(symbol->string (module-gensym "l")))
|
||
|
||
(define gen-labels
|
||
(lambda (ls)
|
||
(if (null? ls)
|
||
'()
|
||
(cons (gen-label) (gen-labels (cdr ls))))))
|
||
|
||
(define-structure (ribcage symnames marks labels))
|
||
|
||
(define-syntax empty-wrap (identifier-syntax '(())))
|
||
(define-syntax top-wrap (identifier-syntax '((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-syntax the-anti-mark (identifier-syntax #f))
|
||
|
||
(define anti-mark
|
||
(lambda (w)
|
||
(make-wrap (cons the-anti-mark (wrap-marks w))
|
||
(cons 'shift (wrap-subst w)))))
|
||
|
||
(define-syntax-rule (new-mark)
|
||
(module-gensym "m"))
|
||
|
||
;; make-empty-ribcage and extend-ribcage maintain list-based ribcages for
|
||
;; internal definitions, in which the ribcages are built incrementally
|
||
(define-syntax-rule (make-empty-ribcage)
|
||
(make-ribcage '() '() '()))
|
||
|
||
(define extend-ribcage!
|
||
;; must receive ids with complete wraps
|
||
(lambda (ribcage id label)
|
||
(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
|
||
(lambda (ids labels w)
|
||
(if (null? ids)
|
||
w
|
||
(make-wrap
|
||
(wrap-marks w)
|
||
(cons
|
||
(let ((labelvec (list->vector labels)))
|
||
(let ((n (vector-length labelvec)))
|
||
(let ((symnamevec (make-vector n)) (marksvec (make-vector n)))
|
||
(let f ((ids ids) (i 0))
|
||
(if (not (null? ids))
|
||
(call-with-values
|
||
(lambda () (id-sym-name&marks (car ids) w))
|
||
(lambda (symname marks)
|
||
(vector-set! symnamevec i symname)
|
||
(vector-set! marksvec i marks)
|
||
(f (cdr ids) (fx+ i 1))))))
|
||
(make-ribcage symnamevec marksvec labelvec))))
|
||
(wrap-subst w))))))
|
||
|
||
(define smart-append
|
||
(lambda (m1 m2)
|
||
(if (null? m2)
|
||
m1
|
||
(append m1 m2))))
|
||
|
||
(define join-wraps
|
||
(lambda (w1 w2)
|
||
(let ((m1 (wrap-marks w1)) (s1 (wrap-subst w1)))
|
||
(if (null? m1)
|
||
(if (null? s1)
|
||
w2
|
||
(make-wrap
|
||
(wrap-marks w2)
|
||
(smart-append s1 (wrap-subst w2))))
|
||
(make-wrap
|
||
(smart-append m1 (wrap-marks w2))
|
||
(smart-append s1 (wrap-subst w2)))))))
|
||
|
||
(define join-marks
|
||
(lambda (m1 m2)
|
||
(smart-append m1 m2)))
|
||
|
||
(define same-marks?
|
||
(lambda (x y)
|
||
(or (eq? x y)
|
||
(and (not (null? x))
|
||
(not (null? y))
|
||
(eq? (car x) (car y))
|
||
(same-marks? (cdr x) (cdr y))))))
|
||
|
||
(define id-var-name
|
||
;; 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.
|
||
;;
|
||
(lambda (id w mod)
|
||
(define-syntax-rule (first e)
|
||
;; Rely on Guile's multiple-values truncation.
|
||
e)
|
||
(define search
|
||
(lambda (sym subst marks mod)
|
||
(if (null? subst)
|
||
(values #f marks)
|
||
(let ((fst (car subst)))
|
||
(if (eq? fst 'shift)
|
||
(search sym (cdr subst) (cdr marks) mod)
|
||
(let ((symnames (ribcage-symnames fst)))
|
||
(if (vector? symnames)
|
||
(search-vector-rib sym subst marks symnames fst mod)
|
||
(search-list-rib sym subst marks symnames fst mod))))))))
|
||
(define search-list-rib
|
||
(lambda (sym subst marks symnames ribcage mod)
|
||
(let f ((symnames symnames)
|
||
(rlabels (ribcage-labels ribcage))
|
||
(rmarks (ribcage-marks ribcage)))
|
||
(cond
|
||
((null? symnames) (search sym (cdr subst) marks mod))
|
||
((and (eq? (car symnames) sym) (same-marks? marks (car rmarks)))
|
||
(let ((n (car rlabels)))
|
||
(if (pair? n)
|
||
(if (equal? mod (car n))
|
||
(values (cdr n) marks)
|
||
(f (cdr symnames) (cdr rlabels) (cdr rmarks)))
|
||
(values n marks))))
|
||
(else (f (cdr symnames) (cdr rlabels) (cdr rmarks)))))))
|
||
(define search-vector-rib
|
||
(lambda (sym subst marks symnames ribcage mod)
|
||
(let ((n (vector-length symnames)))
|
||
(let f ((i 0))
|
||
(cond
|
||
((fx= i n) (search sym (cdr subst) marks mod))
|
||
((and (eq? (vector-ref symnames i) sym)
|
||
(same-marks? marks (vector-ref (ribcage-marks ribcage) i)))
|
||
(let ((n (vector-ref (ribcage-labels ribcage) i)))
|
||
(if (pair? n)
|
||
(if (equal? mod (car n))
|
||
(values (cdr n) marks)
|
||
(f (fx+ i 1)))
|
||
(values n marks))))
|
||
(else (f (fx+ i 1))))))))
|
||
(cond
|
||
((symbol? id)
|
||
(or (first (search id (wrap-subst w) (wrap-marks w) mod)) 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))))
|
||
(call-with-values (lambda () (search id (wrap-subst w) marks mod))
|
||
(lambda (new-id marks)
|
||
(or new-id
|
||
(first (search id (wrap-subst w1) marks mod))
|
||
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
|
||
(lambda (w mod)
|
||
(define scan
|
||
(lambda (subst results)
|
||
(if (null? subst)
|
||
results
|
||
(let ((fst (car subst)))
|
||
(if (eq? fst 'shift)
|
||
(scan (cdr subst) results)
|
||
(let ((symnames (ribcage-symnames fst))
|
||
(marks (ribcage-marks fst)))
|
||
(if (vector? symnames)
|
||
(scan-vector-rib subst symnames marks results)
|
||
(scan-list-rib subst symnames marks results))))))))
|
||
(define scan-list-rib
|
||
(lambda (subst symnames marks results)
|
||
(let f ((symnames symnames) (marks marks) (results results))
|
||
(if (null? symnames)
|
||
(scan (cdr subst) results)
|
||
(f (cdr symnames) (cdr marks)
|
||
(cons (wrap (car symnames)
|
||
(anti-mark (make-wrap (car marks) subst))
|
||
mod)
|
||
results))))))
|
||
(define scan-vector-rib
|
||
(lambda (subst symnames marks results)
|
||
(let ((n (vector-length symnames)))
|
||
(let f ((i 0) (results results))
|
||
(if (fx= i n)
|
||
(scan (cdr subst) results)
|
||
(f (fx+ i 1)
|
||
(cons (wrap (vector-ref symnames i)
|
||
(anti-mark (make-wrap (vector-ref marks i) subst))
|
||
mod)
|
||
results)))))))
|
||
(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))
|
||
(trans (if (pair? trans) (car trans) trans)))
|
||
(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)))
|
||
((string? n)
|
||
(resolve-lexical n (or (and (syntax? id)
|
||
(syntax-module id))
|
||
mod)))
|
||
(else
|
||
(error "unexpected id-var-name" id w n)))))
|
||
|
||
(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=?
|
||
(lambda (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)))
|
||
(if bi
|
||
(eq? bi (id-module-binding j mj))
|
||
(and (not (id-module-binding j mj))
|
||
(eq? ni nj))))
|
||
(eq? (id-module-binding i mi) (id-module-binding j mj))))
|
||
(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=?
|
||
(lambda (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?
|
||
(lambda (ids)
|
||
(and (let all-ids? ((ids ids))
|
||
(or (null? ids)
|
||
(and (id? (car ids))
|
||
(all-ids? (cdr ids)))))
|
||
(distinct-bound-ids? ids))))
|
||
|
||
;; distinct-bound-ids? expects a list of ids and returns #t if there are
|
||
;; no duplicates. It is quadratic on the length of the id list; long
|
||
;; lists could be sorted to make it more efficient. distinct-bound-ids?
|
||
;; may be passed unwrapped (or partially wrapped) ids as long as the
|
||
;; missing portion of the wrap is common to all of the ids.
|
||
|
||
(define distinct-bound-ids?
|
||
(lambda (ids)
|
||
(let distinct? ((ids ids))
|
||
(or (null? ids)
|
||
(and (not (bound-id-member? (car ids) (cdr ids)))
|
||
(distinct? (cdr ids)))))))
|
||
|
||
(define bound-id-member?
|
||
(lambda (x list)
|
||
(and (not (null? list))
|
||
(or (bound-id=? x (car list))
|
||
(bound-id-member? x (cdr list))))))
|
||
|
||
;; wrapping expressions and identifiers
|
||
|
||
(define wrap
|
||
(lambda (x w 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
|
||
(lambda (body r w s mod)
|
||
(build-sequence s
|
||
(let dobody ((body body) (r r) (w w) (mod mod))
|
||
(if (null? body)
|
||
'()
|
||
(let ((first (expand (car body) r w mod)))
|
||
(cons first (dobody (cdr body) r w mod))))))))
|
||
|
||
;; 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
|
||
(lambda (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)))
|
||
(and (pair? labels)
|
||
(let ((wrapped (cdar labels)))
|
||
(or (eq? (syntax-expression wrapped) var)
|
||
(lp (cdr 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) (exps '()))
|
||
(if (null? body)
|
||
exps
|
||
(lp (cdr body)
|
||
(append (parse1 (car body) r w s m esew mod)
|
||
exps)))))
|
||
(define (parse1 x r w s m esew mod)
|
||
(define (current-module-for-expansion mod)
|
||
(case (car mod)
|
||
;; 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.
|
||
((hygiene) (cons 'hygiene (module-name (current-module))))
|
||
(else 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))
|
||
(label (gen-label))
|
||
(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-hook 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.
|
||
(if (eq? type* 'macro)
|
||
(top-level-eval-hook (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))
|
||
(label (gen-label))
|
||
(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-hook 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-hook e mod)
|
||
(list (lambda () e))))
|
||
(else
|
||
(if (memq 'eval esew)
|
||
(top-level-eval-hook
|
||
(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
|
||
(if (memq 'expand when-list)
|
||
(top-level-eval-hook
|
||
(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-hook
|
||
(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-hook x mod)
|
||
(lambda () x))
|
||
(lambda ()
|
||
(expand-expr type value form e r w s mod)))))))))
|
||
(let ((exps (map (lambda (x) (x))
|
||
(reverse (parse body r w s m esew mod)))))
|
||
(if (null? exps)
|
||
(build-void s)
|
||
(build-sequence s exps))))))
|
||
|
||
(define expand-install-global
|
||
(lambda (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
|
||
(lambda (e when-list)
|
||
;; `when-list' is syntax'd version of list of situations. We
|
||
;; could match these keywords lexically, via free-id=?, but then
|
||
;; we twingle the definition of eval-when to the bindings of
|
||
;; eval, load, expand, and compile, which is totally unintended.
|
||
;; So do a symbolic match instead.
|
||
(let ((result (strip when-list)))
|
||
(let lp ((l result))
|
||
(if (null? l)
|
||
result
|
||
(if (memq (car l) '(compile load eval expand))
|
||
(lp (cdr l))
|
||
(syntax-violation 'eval-when "invalid situation" e
|
||
(car l))))))))
|
||
|
||
;; 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
|
||
(lambda (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
|
||
(lambda (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
|
||
(lambda (type value form e r w s mod)
|
||
(case type
|
||
((lexical)
|
||
(build-lexical-reference 'value 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 'fun (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
|
||
(lambda (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
|
||
(lambda (p e r w s rib mod)
|
||
(define (decorate-source x)
|
||
(source-wrap x empty-wrap s #f))
|
||
(define (map* f x)
|
||
(cond
|
||
((null? x) x)
|
||
((pair? x) (cons (f (car x)) (map* f (cdr x))))
|
||
(else (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 (fx+ i 1)))
|
||
((fx= 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
|
||
;; In processing the forms of the body, we create a new, empty wrap.
|
||
;; This wrap is augmented (destructively) each time we discover that
|
||
;; the next form is a definition. This is done:
|
||
;;
|
||
;; (1) to allow the first nondefinition form to be a call to
|
||
;; one of the defined ids even if the id previously denoted a
|
||
;; definition keyword or keyword for a macro expanding into a
|
||
;; definition;
|
||
;; (2) to prevent subsequent definition forms (but unfortunately
|
||
;; not earlier ones) and the first nondefinition form from
|
||
;; confusing one of the bound identifiers for an auxiliary
|
||
;; keyword; and
|
||
;; (3) so that we do not need to restart the expansion of the
|
||
;; first nondefinition form, which is problematic anyway
|
||
;; since it might be the first element of a begin that we
|
||
;; have just spliced into the body (meaning if we restarted,
|
||
;; we'd really need to restart with the begin or the macro
|
||
;; call that expanded into the begin, and we'd have to give
|
||
;; up allowing (begin <defn>+ <expr>+), which is itself
|
||
;; problematic since we don't know if a begin contains only
|
||
;; definitions until we've expanded it).
|
||
;;
|
||
;; Before processing the body, we also create a new environment
|
||
;; containing a placeholder for the bindings we will add later and
|
||
;; associate this environment with each form. In processing a
|
||
;; let-syntax or letrec-syntax, the associated environment may be
|
||
;; augmented with local keyword bindings, so the environment may
|
||
;; be different for different forms in the body. Once we have
|
||
;; gathered up all of the definitions, we evaluate the transformer
|
||
;; expressions and splice into r at the placeholder the new variable
|
||
;; and keyword bindings. This allows let-syntax or letrec-syntax
|
||
;; forms local to a portion or all of the body to shadow the
|
||
;; definition bindings.
|
||
;;
|
||
;; Subforms of a begin, let-syntax, or letrec-syntax are spliced
|
||
;; into the body.
|
||
;;
|
||
;; outer-form is fully wrapped w/source
|
||
(lambda (body outer-form r w mod)
|
||
(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-label)))
|
||
(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
|
||
(lambda (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
|
||
(lambda (expanded mod)
|
||
(let ((p (local-eval-hook expanded mod)))
|
||
(if (procedure? p)
|
||
p
|
||
(syntax-violation #f "nonprocedure transformer" p)))))
|
||
|
||
(define expand-void
|
||
(lambda ()
|
||
(build-void no-source)))
|
||
|
||
(define ellipsis?
|
||
(lambda (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
|
||
(lambda (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
|
||
(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
|
||
(lambda (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
|
||
(lambda (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
|
||
(lambda (id)
|
||
(let ((id (if (syntax? id) (syntax-expression id) id)))
|
||
(build-lexical-var no-source id))))
|
||
|
||
;; appears to return a reversed list
|
||
(define lambda-var-list
|
||
(lambda (vars)
|
||
(let lvl ((vars vars) (ls '()) (w empty-wrap))
|
||
(cond
|
||
((pair? vars) (lvl (cdr vars) (cons (wrap (car vars) w #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
|
||
|
||
(global-extend 'local-syntax 'letrec-syntax #t)
|
||
(global-extend 'local-syntax 'let-syntax #f)
|
||
|
||
(global-extend
|
||
'core 'syntax-parameterize
|
||
(lambda (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))))))
|
||
|
||
(global-extend 'core 'quote
|
||
(lambda (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))))))
|
||
|
||
(global-extend 'core 'quote-syntax
|
||
(lambda (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)))))
|
||
|
||
(global-extend
|
||
'core 'syntax
|
||
(let ()
|
||
(define gen-syntax
|
||
(lambda (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
|
||
(lambda (src var level maps)
|
||
(if (fx= level 0)
|
||
(values var maps)
|
||
(if (null? maps)
|
||
(syntax-violation 'syntax "missing ellipsis" src)
|
||
(call-with-values
|
||
(lambda () (gen-ref src var (fx- level 1) (cdr maps)))
|
||
(lambda (outer-var outer-maps)
|
||
(let ((b (assq outer-var (car maps))))
|
||
(if b
|
||
(values (cdr b) maps)
|
||
(let ((inner-var (gen-var 'tmp)))
|
||
(values inner-var
|
||
(cons (cons (cons outer-var inner-var)
|
||
(car maps))
|
||
outer-maps)))))))))))
|
||
|
||
(define gen-mappend
|
||
(lambda (e map-env)
|
||
`(apply (primitive append) ,(gen-map e map-env))))
|
||
|
||
(define gen-map
|
||
(lambda (e map-env)
|
||
(let ((formals (map cdr map-env))
|
||
(actuals (map (lambda (x) `(ref ,(car x))) map-env)))
|
||
(cond
|
||
((eq? (car e) 'ref)
|
||
;; identity map equivalence:
|
||
;; (map (lambda (x) x) y) == y
|
||
(car actuals))
|
||
((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
|
||
(lambda (x y)
|
||
(case (car y)
|
||
((quote)
|
||
(if (eq? (car x) 'quote)
|
||
`(quote (,(cadr x) . ,(cadr y)))
|
||
(if (eq? (cadr y) '())
|
||
`(list ,x)
|
||
`(cons ,x ,y))))
|
||
((list) `(list ,x ,@(cdr y)))
|
||
(else `(cons ,x ,y)))))
|
||
|
||
(define gen-append
|
||
(lambda (x y)
|
||
(if (equal? y '(quote ()))
|
||
x
|
||
`(append ,x ,y))))
|
||
|
||
(define gen-vector
|
||
(lambda (x)
|
||
(cond
|
||
((eq? (car x) 'list) `(vector ,@(cdr x)))
|
||
((eq? (car x) 'quote) `(quote #(,@(cadr x))))
|
||
(else `(list->vector ,x)))))
|
||
|
||
|
||
(define regen
|
||
(lambda (x)
|
||
(case (car x)
|
||
((ref) (build-lexical-reference 'value no-source (cadr x) (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)))))))
|
||
|
||
(global-extend 'core 'lambda
|
||
(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)))))
|
||
|
||
(global-extend 'core 'lambda*
|
||
(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)))))
|
||
|
||
(global-extend 'core 'case-lambda
|
||
(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)))))
|
||
|
||
(global-extend 'core 'case-lambda*
|
||
(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)))))
|
||
|
||
(global-extend 'core 'with-ellipsis
|
||
(lambda (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))))))
|
||
|
||
(global-extend 'core '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)))))))
|
||
|
||
|
||
(global-extend 'core 'letrec
|
||
(lambda (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))))))
|
||
|
||
|
||
(global-extend 'core 'letrec*
|
||
(lambda (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))))))
|
||
|
||
|
||
(global-extend
|
||
'core 'set!
|
||
(lambda (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))))))
|
||
|
||
(global-extend 'module-ref '@
|
||
(lambda (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 ...)))))))
|
||
|
||
(global-extend 'module-ref '@@
|
||
(lambda (e r w mod)
|
||
(define remodulate
|
||
(lambda (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 (fx+ i 1)))
|
||
((fx= 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))))))
|
||
|
||
(global-extend 'core 'if
|
||
(lambda (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))))))
|
||
|
||
(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
|
||
(let ()
|
||
(define convert-pattern
|
||
;; accepts pattern & keys
|
||
;; returns $sc-dispatch pattern & ids
|
||
(lambda (pattern keys ellipsis?)
|
||
(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) (fx+ n 1) 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
|
||
(lambda (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
|
||
(lambda (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 'value 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
|
||
(lambda (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 'value no-source
|
||
'tmp x)
|
||
#'(key ...) #'(m ...)
|
||
r
|
||
mod))
|
||
(list (expand #'val r empty-wrap mod))))
|
||
(syntax-violation 'syntax-case "invalid literals list" e))))))))
|
||
|
||
;; 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.
|
||
(set! macroexpand
|
||
(lambda* (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))))))
|
||
|
||
(set! identifier?
|
||
(lambda (x)
|
||
(nonsymbol-id? x)))
|
||
|
||
(set! datum->syntax
|
||
(lambda* (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))))))
|
||
|
||
(set! syntax->datum
|
||
;; accepts any object, since syntax objects may consist partially
|
||
;; or entirely of unwrapped, nonsymbolic data
|
||
(lambda (x)
|
||
(strip x)))
|
||
|
||
(set! generate-temporaries
|
||
(lambda (ls)
|
||
(arg-check list? ls 'generate-temporaries)
|
||
(let ((mod (cons 'hygiene (module-name (current-module)))))
|
||
(map (lambda (x)
|
||
(wrap (module-gensym "t") top-wrap mod))
|
||
ls))))
|
||
|
||
(set! free-identifier=?
|
||
(lambda (x y)
|
||
(arg-check nonsymbol-id? x 'free-identifier=?)
|
||
(arg-check nonsymbol-id? y 'free-identifier=?)
|
||
(free-id=? x y)))
|
||
|
||
(set! bound-identifier=?
|
||
(lambda (x y)
|
||
(arg-check nonsymbol-id? x 'bound-identifier=?)
|
||
(arg-check nonsymbol-id? y 'bound-identifier=?)
|
||
(bound-id=? x y)))
|
||
|
||
(set! syntax-violation
|
||
(lambda* (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))
|
||
|
||
;; $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. (This use of #f will
|
||
;; not work on r4rs implementations that violate the ieee requirement
|
||
;; that #f and () be distinct.)
|
||
|
||
;; The expression is matched with the pattern as follows:
|
||
|
||
;; pattern: matches:
|
||
;; () empty list
|
||
;; any anything
|
||
;; (<pattern>1 . <pattern>2) (<pattern>1 . <pattern>2)
|
||
;; each-any (any*)
|
||
;; #(free-id <key>) <key> with free-identifier=?
|
||
;; #(each <pattern>) (<pattern>*)
|
||
;; #(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>*)
|
||
|
||
(let ()
|
||
|
||
(define match-each
|
||
(lambda (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+
|
||
(lambda (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
|
||
(lambda (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
|
||
(lambda (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
|
||
(lambda (r* r)
|
||
(if (null? (car r*))
|
||
r
|
||
(cons (map car r*) (combine (map cdr r*) r)))))
|
||
|
||
(define match*
|
||
(lambda (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
|
||
(lambda (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)))))
|
||
|
||
(set! $sc-dispatch
|
||
(lambda (e p)
|
||
(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)))))
|