Spaces:
Sleeping
Sleeping
;;;; -*-scheme-*- | |
;;;; | |
;;;; Copyright (C) 2001, 2003, 2006, 2009, 2010, 2011, | |
;;;; 2012, 2013 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. | |
;;; This implementation of the expander sometimes uses syntactic | |
;;; abstractions when procedural abstractions would suffice. For | |
;;; example, we define top-wrap and top-marked? as | |
;;; | |
;;; (define-syntax top-wrap (identifier-syntax '((top)))) | |
;;; (define-syntax top-marked? | |
;;; (syntax-rules () | |
;;; ((_ w) (memq 'top (wrap-marks w))))) | |
;;; | |
;;; rather than | |
;;; | |
;;; (define top-wrap '((top))) | |
;;; (define top-marked? | |
;;; (lambda (w) (memq 'top (wrap-marks w)))) | |
;;; | |
;;; On the other hand, we don't do this consistently; we define | |
;;; make-wrap, wrap-marks, and wrap-subst simply as | |
;;; | |
;;; (define make-wrap cons) | |
;;; (define wrap-marks car) | |
;;; (define wrap-subst cdr) | |
;;; | |
;;; In Chez Scheme, the syntactic and procedural forms of these | |
;;; abstractions are equivalent, since the optimizer consistently | |
;;; integrates constants and small procedures. This will be true of | |
;;; Guile as well, once we implement a proper inliner. | |
;;; 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-object representations, it is necessary to support | |
;;; both old and new syntax-object representations in id-var-name. It | |
;;; should be sufficient to recognize old representations and treat | |
;;; them as not lexically bound. | |
(eval-when (compile) | |
(set-current-module (resolve-module '(guile)))) | |
(let () | |
(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 (vector-ref %expanded-vtables #,n) 0 | |
#,@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 put-global-definition-hook | |
(lambda (symbol type val) | |
(module-define! (current-module) | |
symbol | |
(make-syntax-transformer symbol type val)))) | |
(define get-global-definition-hook | |
(lambda (symbol module) | |
(if (and (not module) (current-module)) | |
(warn "module system is booted, we should have a module" symbol)) | |
(let ((v (module-variable (if module | |
(resolve-module (cdr module)) | |
(current-module)) | |
symbol))) | |
(and v (variable-bound? v) | |
(let ((val (variable-ref v))) | |
(and (macro? val) (macro-type val) | |
(cons (macro-type val) | |
(macro-binding val))))))))) | |
(define (decorate-source e s) | |
(if (and s (supports-source-properties? e)) | |
(set-source-properties! e s)) | |
e) | |
(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 (source) | |
(make-void source))) | |
(define build-application | |
(lambda (source fun-exp arg-exps) | |
(make-application source fun-exp arg-exps))) | |
(define build-conditional | |
(lambda (source test-exp then-exp else-exp) | |
(make-conditional source test-exp then-exp else-exp))) | |
(define build-dynlet | |
(lambda (source fluids vals body) | |
(make-dynlet source fluids vals body))) | |
(define build-lexical-reference | |
(lambda (type source name var) | |
(make-lexical-ref source name var))) | |
(define build-lexical-assignment | |
(lambda (source name var exp) | |
(maybe-name-value! name exp) | |
(make-lexical-set source name var exp))) | |
(define (analyze-variable mod var modref-cont bare-cont) | |
(if (not mod) | |
(bare-cont var) | |
(let ((kind (car mod)) | |
(mod (cdr mod))) | |
(case kind | |
((public) (modref-cont mod var #t)) | |
((private) (if (not (equal? mod (module-name (current-module)))) | |
(modref-cont mod var #f) | |
(bare-cont var))) | |
((bare) (bare-cont var)) | |
((hygiene) (if (and (not (equal? mod (module-name (current-module)))) | |
(module-variable (resolve-module mod) var)) | |
(modref-cont mod var #f) | |
(bare-cont var))) | |
(else (syntax-violation #f "bad module kind" var mod)))))) | |
(define build-global-reference | |
(lambda (source var mod) | |
(analyze-variable | |
mod var | |
(lambda (mod var public?) | |
(make-module-ref source mod var public?)) | |
(lambda (var) | |
(make-toplevel-ref source var))))) | |
(define build-global-assignment | |
(lambda (source var exp mod) | |
(maybe-name-value! var exp) | |
(analyze-variable | |
mod var | |
(lambda (mod var public?) | |
(make-module-set source mod var public? exp)) | |
(lambda (var) | |
(make-toplevel-set source var exp))))) | |
(define build-global-definition | |
(lambda (source var exp) | |
(maybe-name-value! var exp) | |
(make-toplevel-define source 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-primref | |
(lambda (src name) | |
(if (equal? (module-name (current-module)) '(guile)) | |
(make-toplevel-ref src name) | |
(make-module-ref src '(guile) name #f)))) | |
(define (build-data src exp) | |
(make-const src exp)) | |
(define build-sequence | |
(lambda (src exps) | |
(if (null? (cdr exps)) | |
(car exps) | |
(make-sequence src 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-application 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))))) | |
;; FIXME: use a faster gensym | |
(define-syntax-rule (build-lexical-var src id) | |
(gensym (string-append (symbol->string id) "-"))) | |
(define-structure (syntax-object expression wrap module)) | |
(define-syntax no-source (identifier-syntax #f)) | |
(define source-annotation | |
(lambda (x) | |
(let ((props (source-properties | |
(if (syntax-object? x) | |
(syntax-object-expression x) | |
x)))) | |
(and (pair? props) props)))) | |
(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 | |
;; {put,get}-global-definition-hook and associates symbols with | |
;; bindings. | |
;; global (assumed global variable) and displaced-lexical (see below) | |
;; do not show up in any environment; instead, they are fabricated by | |
;; lookup when it finds no other bindings. | |
;; <environment> ::= ((<label> . <binding>)*) | |
;; identifier bindings include a type and a value | |
;; <binding> ::= (macro . <procedure>) macros | |
;; (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> ::= <nonnegative integer> | |
;; <var> ::= variable returned by build-lexical-var | |
;; a macro is a user-defined syntactic-form. a core is a | |
;; system-defined syntactic form. begin, define, define-syntax, | |
;; 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 ellipsis)) | |
(cons a (macros-only-env (cdr r))) | |
(macros-only-env (cdr r))))))) | |
(define lookup | |
;; x may be a label or a symbol | |
;; although symbols are usually global, we check the environment first | |
;; anyway because a temporary binding may have been established by | |
;; fluid-let-syntax | |
(lambda (x r mod) | |
(cond | |
((assq x r) => cdr) | |
((symbol? x) | |
(or (get-global-definition-hook x mod) (make-binding 'global))) | |
(else (make-binding 'displaced-lexical))))) | |
(define global-extend | |
(lambda (type sym val) | |
(put-global-definition-hook 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-object? x) | |
(symbol? (syntax-object-expression x))))) | |
(define id? | |
(lambda (x) | |
(cond | |
((symbol? x) #t) | |
((syntax-object? x) (symbol? (syntax-object-expression x))) | |
(else #f)))) | |
(define-syntax-rule (id-sym-name e) | |
(let ((x e)) | |
(if (syntax-object? x) | |
(syntax-object-expression x) | |
x))) | |
(define id-sym-name&marks | |
(lambda (x w) | |
(if (syntax-object? x) | |
(values | |
(syntax-object-expression x) | |
(join-marks (wrap-marks w) (wrap-marks (syntax-object-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) | |
(string-append "l-" (session-id) (symbol->string (gensym "-")))) | |
(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)))) | |
(define-syntax-rule (top-marked? w) | |
(memq 'top (wrap-marks w))) | |
;; 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) | |
(gensym (string-append "m-" (session-id) "-"))) | |
;; 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-object-expression id) | |
(ribcage-symnames ribcage))) | |
(set-ribcage-marks! ribcage | |
(cons (wrap-marks (syntax-object-wrap id)) | |
(ribcage-marks ribcage))) | |
(set-ribcage-labels! ribcage | |
(cons label (ribcage-labels ribcage))))) | |
;; 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 | |
(lambda (id w) | |
(define-syntax-rule (first e) | |
;; Rely on Guile's multiple-values truncation. | |
e) | |
(define search | |
(lambda (sym subst marks) | |
(if (null? subst) | |
(values #f marks) | |
(let ((fst (car subst))) | |
(if (eq? fst 'shift) | |
(search sym (cdr subst) (cdr marks)) | |
(let ((symnames (ribcage-symnames fst))) | |
(if (vector? symnames) | |
(search-vector-rib sym subst marks symnames fst) | |
(search-list-rib sym subst marks symnames fst)))))))) | |
(define search-list-rib | |
(lambda (sym subst marks symnames ribcage) | |
(let f ((symnames symnames) (i 0)) | |
(cond | |
((null? symnames) (search sym (cdr subst) marks)) | |
((and (eq? (car symnames) sym) | |
(same-marks? marks (list-ref (ribcage-marks ribcage) i))) | |
(values (list-ref (ribcage-labels ribcage) i) marks)) | |
(else (f (cdr symnames) (fx+ i 1))))))) | |
(define search-vector-rib | |
(lambda (sym subst marks symnames ribcage) | |
(let ((n (vector-length symnames))) | |
(let f ((i 0)) | |
(cond | |
((fx= i n) (search sym (cdr subst) marks)) | |
((and (eq? (vector-ref symnames i) sym) | |
(same-marks? marks (vector-ref (ribcage-marks ribcage) i))) | |
(values (vector-ref (ribcage-labels ribcage) i) marks)) | |
(else (f (fx+ i 1)))))))) | |
(cond | |
((symbol? id) | |
(or (first (search id (wrap-subst w) (wrap-marks w))) id)) | |
((syntax-object? id) | |
(let ((id (syntax-object-expression id)) | |
(w1 (syntax-object-wrap id))) | |
(let ((marks (join-marks (wrap-marks w) (wrap-marks w1)))) | |
(call-with-values (lambda () (search id (wrap-subst w) marks)) | |
(lambda (new-id marks) | |
(or new-id | |
(first (search id (wrap-subst w1) marks)) | |
id)))))) | |
(else (syntax-violation 'id-var-name "invalid id" id))))) | |
;; A helper procedure for syntax-locally-bound-identifiers, which | |
;; itself is a helper for transformer procedures. | |
;; `locally-bound-identifiers' returns a list of all bindings | |
;; visible to a syntax object with the given wrap. They are in | |
;; order from outer to inner. | |
;; | |
;; The purpose of this procedure is to give a transformer procedure | |
;; references on bound identifiers, that the transformer can then | |
;; introduce some of them in its output. As such, the identifiers | |
;; are anti-marked, so that rebuild-macro-output doesn't apply new | |
;; marks to them. | |
;; | |
(define locally-bound-identifiers | |
(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, the module (for | |
;; resolving toplevel vars). | |
(define (resolve-identifier id w r mod) | |
(define (resolve-global var mod) | |
(let ((b (or (get-global-definition-hook var mod) | |
(make-binding 'global)))) | |
(if (eq? (binding-type b) 'global) | |
(values 'global var mod) | |
(values (binding-type b) (binding-value b) mod)))) | |
(define (resolve-lexical label mod) | |
(let ((b (or (assq-ref r label) | |
(make-binding 'displaced-lexical)))) | |
(values (binding-type b) (binding-value b) mod))) | |
(let ((n (id-var-name id w))) | |
(cond | |
((symbol? n) | |
(resolve-global n (if (syntax-object? id) | |
(syntax-object-module id) | |
mod))) | |
((string? n) | |
(resolve-lexical n (if (syntax-object? id) | |
(syntax-object-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) | |
(and (eq? (id-sym-name i) (id-sym-name j)) ; accelerator | |
(eq? (id-var-name i empty-wrap) (id-var-name j empty-wrap))))) | |
;; bound-id=? may be passed unwrapped (or partially wrapped) ids as | |
;; long as the missing portion of the wrap is common to both of the ids | |
;; since (bound-id=? x y) iff (bound-id=? (wrap x w) (wrap y w)) | |
(define bound-id=? | |
(lambda (i j) | |
(if (and (syntax-object? i) (syntax-object? j)) | |
(and (eq? (syntax-object-expression i) | |
(syntax-object-expression j)) | |
(same-marks? (wrap-marks (syntax-object-wrap i)) | |
(wrap-marks (syntax-object-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) | |
(cond | |
((and (null? (wrap-marks w)) (null? (wrap-subst w))) x) | |
((syntax-object? x) | |
(make-syntax-object | |
(syntax-object-expression x) | |
(join-wraps w (syntax-object-wrap x)) | |
(syntax-object-module x))) | |
((null? x) x) | |
(else (make-syntax-object x w defmod))))) | |
(define source-wrap | |
(lambda (x w s defmod) | |
(wrap (decorate-source x s) w defmod))) | |
;; 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) | |
(define (scan body r w s m esew mod exps) | |
(cond | |
((null? body) | |
;; in reversed order | |
exps) | |
(else | |
(call-with-values | |
(lambda () | |
(call-with-values | |
(lambda () | |
(let ((e (car body))) | |
(syntax-type e r w (or (source-annotation e) s) #f mod #f))) | |
(lambda (type value form e w s mod) | |
(case type | |
((begin-form) | |
(syntax-case e () | |
((_) exps) | |
((_ e1 e2 ...) | |
(scan #'(e1 e2 ...) r w s m esew mod exps)))) | |
((local-syntax-form) | |
(expand-local-syntax value e r w s mod | |
(lambda (body r w s mod) | |
(scan body r w s m esew mod exps)))) | |
((eval-when-form) | |
(syntax-case e () | |
((_ (x ...) e1 e2 ...) | |
(let ((when-list (parse-when-list e #'(x ...))) | |
(body #'(e1 e2 ...))) | |
(cond | |
((eq? m 'e) | |
(if (memq 'eval when-list) | |
(scan body r w s | |
(if (memq 'expand when-list) 'c&e 'e) | |
'(eval) | |
mod exps) | |
(begin | |
(if (memq 'expand when-list) | |
(top-level-eval-hook | |
(expand-top-sequence body r w s 'e '(eval) mod) | |
mod)) | |
(values exps)))) | |
((memq 'load when-list) | |
(if (or (memq 'compile when-list) | |
(memq 'expand when-list) | |
(and (eq? m 'c&e) (memq 'eval when-list))) | |
(scan body r w s 'c&e '(compile load) mod exps) | |
(if (memq m '(c c&e)) | |
(scan body r w s 'c '(load) mod exps) | |
(values exps)))) | |
((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) | |
(values exps)) | |
(else | |
(values exps))))))) | |
((define-syntax-form define-syntax-parameter-form) | |
(let ((n (id-var-name value w)) (r (macros-only-env r))) | |
(case m | |
((c) | |
(if (memq 'compile esew) | |
(let ((e (expand-install-global n (expand e r w mod)))) | |
(top-level-eval-hook e mod) | |
(if (memq 'load esew) | |
(values (cons e exps)) | |
(values exps))) | |
(if (memq 'load esew) | |
(values (cons (expand-install-global n (expand e r w mod)) | |
exps)) | |
(values exps)))) | |
((c&e) | |
(let ((e (expand-install-global n (expand e r w mod)))) | |
(top-level-eval-hook e mod) | |
(values (cons e exps)))) | |
(else | |
(if (memq 'eval esew) | |
(top-level-eval-hook | |
(expand-install-global n (expand e r w mod)) | |
mod)) | |
(values exps))))) | |
((define-form) | |
(let* ((n (id-var-name value w)) | |
;; Lookup the name in the module of the define form. | |
(type (binding-type (lookup n r mod)))) | |
(case type | |
((global core macro module-ref) | |
;; affect compile-time environment (once we have booted) | |
(if (and (memq m '(c c&e)) | |
(not (module-local-variable (current-module) n)) | |
(current-module)) | |
(let ((old (module-variable (current-module) n))) | |
;; use value of the same-named imported variable, if | |
;; any | |
(if (and (variable? old) | |
(variable-bound? old) | |
(not (macro? (variable-ref old)))) | |
(module-define! (current-module) n (variable-ref old)) | |
(module-add! (current-module) n (make-undefined-variable))))) | |
(values | |
(cons | |
(if (eq? m 'c&e) | |
(let ((x (build-global-definition s n (expand e r w mod)))) | |
(top-level-eval-hook x mod) | |
x) | |
(lambda () | |
(build-global-definition s n (expand e r w mod)))) | |
exps))) | |
((displaced-lexical) | |
(syntax-violation #f "identifier out of context" | |
(source-wrap form w s mod) | |
(wrap value w mod))) | |
(else | |
(syntax-violation #f "cannot define keyword at top level" | |
(source-wrap form w s mod) | |
(wrap value w mod)))))) | |
(else | |
(values (cons | |
(if (eq? m 'c&e) | |
(let ((x (expand-expr type value form e r w s mod))) | |
(top-level-eval-hook x mod) | |
x) | |
(lambda () | |
(expand-expr type value form e r w s mod))) | |
exps))))))) | |
(lambda (exps) | |
(scan (cdr body) r w s m esew mod exps)))))) | |
(call-with-values (lambda () | |
(scan body r w s m esew mod '())) | |
(lambda (exps) | |
(if (null? exps) | |
(build-void s) | |
(build-sequence | |
s | |
(let lp ((in exps) (out '())) | |
(if (null? in) out | |
(let ((e (car in))) | |
(lp (cdr in) | |
(cons (if (procedure? e) (e) e) out))))))))))) | |
(define expand-install-global | |
(lambda (name e) | |
(build-global-definition | |
no-source | |
name | |
(build-application | |
no-source | |
(build-primref no-source 'make-syntax-transformer) | |
(list (build-data no-source name) | |
(build-data no-source 'macro) | |
e))))) | |
(define parse-when-list | |
(lambda (e when-list) | |
;; when-list is syntax'd version of list of situations | |
(let ((result (strip when-list empty-wrap))) | |
(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 | |
;; 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) | |
(let* ((n (id-var-name e w)) | |
(b (lookup n r mod)) | |
(type (binding-type b))) | |
(case type | |
((lexical) (values type (binding-value b) e e w s mod)) | |
((global) (values type n e e w s mod)) | |
((macro) | |
(if for-car? | |
(values type (binding-value b) e e w s mod) | |
(syntax-type (expand-macro (binding-value b) e r w s rib mod) | |
r empty-wrap s rib mod #f))) | |
(else (values type (binding-value b) 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 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-object fval w fmod) | |
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)) | |
(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) | |
(decorate-source | |
(cons #'lambda (wrap #'(args e1 e2 ...) w mod)) | |
s) | |
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-object? e) | |
(syntax-type (syntax-object-expression e) | |
r | |
(join-wraps w (syntax-object-wrap e)) | |
(or (source-annotation e) s) rib | |
(or (syntax-object-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)) | |
(lambda (e r w s mod) | |
(expand e r w mod)))) | |
((lexical-call) | |
(expand-application | |
(let ((id (car e))) | |
(build-lexical-reference 'fun (source-annotation id) | |
(if (syntax-object? id) | |
(syntax->datum id) | |
id) | |
value)) | |
e r w s mod)) | |
((global-call) | |
(expand-application | |
(build-global-reference (source-annotation (car e)) | |
(if (syntax-object? value) | |
(syntax-object-expression value) | |
value) | |
(if (syntax-object? value) | |
(syntax-object-module value) | |
mod)) | |
e r w s mod)) | |
((constant) (build-data s (strip (source-wrap e w s mod) empty-wrap))) | |
((global) (build-global-reference s value mod)) | |
((call) (expand-application (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)) | |
((_) | |
(if (include-deprecated-features) | |
(begin | |
(issue-deprecation-warning | |
"Sequences of zero expressions are deprecated. Use *unspecified*.") | |
(expand-void)) | |
(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-application | |
(lambda (x e r w s mod) | |
(syntax-case e () | |
((e0 e1 ...) | |
(build-application 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 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 rebuild-macro-output | |
(lambda (x m) | |
(cond ((pair? x) | |
(decorate-source | |
(cons (rebuild-macro-output (car x) m) | |
(rebuild-macro-output (cdr x) m)) | |
s)) | |
((syntax-object? x) | |
(let ((w (syntax-object-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 | |
(make-syntax-object | |
(syntax-object-expression x) | |
(make-wrap (cdr ms) (if rib (cons rib (cdr ss)) (cdr ss))) | |
(syntax-object-module x)) | |
;; output introduced by macro | |
(make-syntax-object | |
(decorate-source (syntax-object-expression x) s) | |
(make-wrap (cons m ms) | |
(if rib | |
(cons rib (cons 'shift ss)) | |
(cons 'shift ss))) | |
(syntax-object-module x)))))) | |
((vector? x) | |
(let* ((n (vector-length x)) | |
(v (decorate-source (make-vector n) s))) | |
(do ((i 0 (fx+ i 1))) | |
((fx= i n) v) | |
(vector-set! v i | |
(rebuild-macro-output (vector-ref x i) m))))) | |
((symbol? x) | |
(syntax-violation #f "encountered raw symbol in macro output" | |
(source-wrap e w (wrap-subst w) mod) x)) | |
(else (decorate-source x s))))) | |
(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 '())) | |
(if (null? body) | |
(syntax-violation #f "no expressions in body" outer-form) | |
(let ((e (cdar body)) (er (caar 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 (cdr body) | |
(cons id ids) (cons label labels) | |
(cons id var-ids) | |
(cons var vars) (cons (cons er (wrap e w mod)) vals) | |
(cons (make-binding 'lexical var) bindings))))) | |
((define-syntax-form define-syntax-parameter-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 (cdr body) (cons id ids) labels var-ids vars vals bindings))) | |
((begin-form) | |
(syntax-case e () | |
((_ e1 ...) | |
(parse (let f ((forms #'(e1 ...))) | |
(if (null? forms) | |
(cdr body) | |
(cons (cons er (wrap (car forms) w mod)) | |
(f (cdr forms))))) | |
ids labels var-ids vars vals bindings)))) | |
((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) | |
(cdr body) | |
(cons (cons er (wrap (car forms) w mod)) | |
(f (cdr forms))))) | |
ids labels var-ids vars vals bindings)))) | |
(else ; found a non-definition | |
(if (null? ids) | |
(build-sequence no-source | |
(map (lambda (x) | |
(expand (cdr x) (car x) empty-wrap mod)) | |
(cons (cons er (source-wrap e w s mod)) | |
(cdr body)))) | |
(begin | |
(if (not (valid-bound-ids? ids)) | |
(syntax-violation | |
#f "invalid or duplicate identifier in definition" | |
outer-form)) | |
(set-cdr! r (extend-env labels bindings (cdr r))) | |
(build-letrec no-source #t | |
(reverse (map syntax->datum var-ids)) | |
(reverse vars) | |
(map (lambda (x) | |
(expand (cdr x) (car x) empty-wrap mod)) | |
(reverse vals)) | |
(build-sequence no-source | |
(map (lambda (x) | |
(expand (cdr x) (car x) empty-wrap mod)) | |
(cons (cons er (source-wrap e w s mod)) | |
(cdr body))))))))))))))))) | |
(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=?'. | |
(let* ((id (make-syntax-object '#{ $sc-ellipsis }# | |
(syntax-object-wrap e) | |
(syntax-object-module e))) | |
(n (id-var-name id empty-wrap)) | |
(b (lookup n r mod))) | |
(if (eq? (binding-type b) 'ellipsis) | |
(bound-id=? e (binding-value b)) | |
(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 down to top-wrap | |
;; | |
;; since only the head of a list is annotated by the reader, not each pair | |
;; in the spine, we also check for pairs whose cars are annotated in case | |
;; we've been passed the cdr of an annotated list | |
(define strip | |
(lambda (x w) | |
(if (top-marked? w) | |
x | |
(let f ((x x)) | |
(cond | |
((syntax-object? x) | |
(strip (syntax-object-expression x) (syntax-object-wrap x))) | |
((pair? x) | |
(let ((a (f (car x))) (d (f (cdr x)))) | |
(if (and (eq? a (car x)) (eq? d (cdr x))) | |
x | |
(cons a d)))) | |
((vector? x) | |
(let ((old (vector->list x))) | |
(let ((new (map f old))) | |
;; inlined and-map with two args | |
(let lp ((l1 old) (l2 new)) | |
(if (null? l1) | |
x | |
(if (eq? (car l1) (car l2)) | |
(lp (cdr l1) (cdr l2)) | |
(list->vector new))))))) | |
(else x)))))) | |
;; lexical variables | |
(define gen-var | |
(lambda (id) | |
(let ((id (if (syntax-object? id) (syntax-object-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-object? vars) | |
(lvl (syntax-object-expression vars) | |
ls | |
(join-wraps w (syntax-object-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) (id-var-name x w)) #'(var ...)))) | |
(for-each | |
(lambda (id n) | |
(case (binding-type (lookup n r mod)) | |
((displaced-lexical) | |
(syntax-violation 'syntax-parameterize | |
"identifier out of context" | |
e | |
(source-wrap id w s mod))))) | |
#'(var ...) | |
names) | |
(expand-body | |
#'(e1 e2 ...) | |
(source-wrap e w s mod) | |
(extend-env | |
names | |
(let ((trans-r (macros-only-env r))) | |
(map (lambda (x) | |
(make-binding 'macro | |
(eval-local-transformer (expand x trans-r w mod) | |
mod))) | |
#'(val ...))) | |
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 w))) | |
(_ (syntax-violation 'quote "bad syntax" | |
(source-wrap e w s mod)))))) | |
(global-extend 'core 'syntax | |
(let () | |
(define gen-syntax | |
(lambda (src e r maps ellipsis? mod) | |
(if (id? e) | |
(let ((label (id-var-name e empty-wrap))) | |
;; Mod does not matter, we are looking to see if | |
;; the id is lexical syntax. | |
(let ((b (lookup label r mod))) | |
(if (eq? (binding-type b) 'syntax) | |
(call-with-values | |
(lambda () | |
(let ((var.lev (binding-value b))) | |
(gen-ref src (car var.lev) (cdr var.lev) maps))) | |
(lambda (var maps) (values `(ref ,var) maps))) | |
(if (ellipsis? e 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)))) | |
(_ (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-application no-source | |
(build-primref 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-object '#{ $sc-ellipsis }# | |
(syntax-object-wrap #'dots) | |
(syntax-object-module #'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) | |
(let ((n (id-var-name #'id w)) | |
;; Lookup id in its module | |
(id-mod (if (syntax-object? #'id) | |
(syntax-object-module #'id) | |
mod))) | |
(let ((b (lookup n r id-mod))) | |
(case (binding-type b) | |
((lexical) | |
(build-lexical-assignment s | |
(syntax->datum #'id) | |
(binding-value b) | |
(expand #'val r w mod))) | |
((global) | |
(build-global-assignment s n (expand #'val r w mod) id-mod)) | |
((macro) | |
(let ((p (binding-value b))) | |
(if (procedure-property p 'variable-transformer) | |
;; As syntax-type does, call expand-macro with | |
;; the mod of the expression. Hmm. | |
(expand (expand-macro p 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 formform ee ww ss modmod) | |
(case type | |
((module-ref) | |
(let ((val (expand #'val r w mod))) | |
(call-with-values (lambda () (value #'(head tail ...) r w)) | |
(lambda (e r w s* mod) | |
(syntax-case e () | |
(e (id? #'e) | |
(build-global-assignment s (syntax->datum #'e) | |
val mod))))))) | |
(else | |
(build-application 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) | |
(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) | |
(define remodulate | |
(lambda (x mod) | |
(cond ((pair? x) | |
(cons (remodulate (car x) mod) | |
(remodulate (cdr x) mod))) | |
((syntax-object? x) | |
(make-syntax-object | |
(remodulate (syntax-object-expression x) mod) | |
(syntax-object-wrap x) | |
;; hither the remodulation | |
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 (remodulate (vector-ref x i) mod))))) | |
(else x)))) | |
(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 | |
#'(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 'core 'with-fluids | |
(lambda (e r w s mod) | |
(syntax-case e () | |
((_ ((fluid val) ...) b b* ...) | |
(build-dynlet | |
s | |
(map (lambda (x) (expand x r w mod)) #'(fluid ...)) | |
(map (lambda (x) (expand x r w mod)) #'(val ...)) | |
(expand-body #'(b b* ...) | |
(source-wrap e w s mod) 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 empty-wrap)) 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-application no-source | |
(build-primref 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-application 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-application no-source | |
(build-primref no-source 'list) | |
(list x)) | |
(build-application no-source | |
(build-primref 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-application no-source | |
(build-primref 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-application 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-application 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))) | |
(expand-top-sequence (list 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) | |
(make-syntax-object datum (syntax-object-wrap id) | |
(syntax-object-module id)))) | |
(set! syntax->datum | |
;; accepts any object, since syntax objects may consist partially | |
;; or entirely of unwrapped, nonsymbolic data | |
(lambda (x) | |
(strip x empty-wrap))) | |
(set! syntax-source | |
(lambda (x) (source-annotation x))) | |
(set! generate-temporaries | |
(lambda (ls) | |
(arg-check list? ls 'generate-temporaries) | |
(let ((mod (cons 'hygiene (module-name (current-module))))) | |
(map (lambda (x) (wrap (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 | |
(or (source-annotation subform) | |
(source-annotation form)) | |
(strip form empty-wrap) | |
(and subform (strip subform empty-wrap))))) | |
(let () | |
(define (syntax-module id) | |
(arg-check nonsymbol-id? id 'syntax-module) | |
(cdr (syntax-object-module id))) | |
(define (syntax-local-binding id) | |
(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-object-expression id) | |
(strip-anti-mark (syntax-object-wrap id)) | |
r | |
(syntax-object-module id))) | |
(lambda (type value mod) | |
(case type | |
((lexical) (values 'lexical value)) | |
((macro) (values 'macro value)) | |
((syntax) (values 'pattern-variable value)) | |
((displaced-lexical) (values 'displaced-lexical #f)) | |
((global) (values 'global (cons value (cdr mod)))) | |
((ellipsis) | |
(values 'ellipsis | |
(make-syntax-object (syntax-object-expression value) | |
(anti-mark (syntax-object-wrap value)) | |
(syntax-object-module value)))) | |
(else (values 'other #f)))))))) | |
(define (syntax-locally-bound-identifiers id) | |
(arg-check nonsymbol-id? id 'syntax-locally-bound-identifiers) | |
(locally-bound-identifiers (syntax-object-wrap id) | |
(syntax-object-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-object? e) | |
(match-each (syntax-object-expression e) | |
p | |
(join-wraps w (syntax-object-wrap e)) | |
(syntax-object-module e))) | |
(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-object? e) | |
(f (syntax-object-expression e) | |
(join-wraps w (syntax-object-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-object? e) | |
(match-each-any (syntax-object-expression e) | |
(join-wraps w (syntax-object-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 w)) 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-object? e) | |
(match* | |
(syntax-object-expression e) | |
p | |
(join-wraps w (syntax-object-wrap e)) | |
r | |
(syntax-object-module e))) | |
(else (match* e p w r mod))))) | |
(set! $sc-dispatch | |
(lambda (e p) | |
(cond | |
((eq? p 'any) (list e)) | |
((eq? p '_) '()) | |
((syntax-object? e) | |
(match* (syntax-object-expression e) | |
p (syntax-object-wrap e) '() (syntax-object-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-syntax include | |
(lambda (x) | |
(define read-file | |
(lambda (fn dir k) | |
(let* ((p (open-input-file | |
(cond ((absolute-file-name? fn) | |
fn) | |
(dir | |
(in-vicinity dir fn)) | |
(else | |
(syntax-violation | |
'include | |
"relative file name only allowed when the include form is in a file" | |
x))))) | |
(enc (file-encoding p))) | |
;; Choose the input encoding deterministically. | |
(set-port-encoding! p (or enc "UTF-8")) | |
(let f ((x (read p)) | |
(result '())) | |
(if (eof-object? x) | |
(begin | |
(close-input-port p) | |
(reverse result)) | |
(f (read p) | |
(cons (datum->syntax k x) result))))))) | |
(let* ((src (syntax-source x)) | |
(file (and src (assq-ref src 'filename))) | |
(dir (and (string? file) (dirname file)))) | |
(syntax-case x () | |
((k filename) | |
(let ((fn (syntax->datum #'filename))) | |
(with-syntax (((exp ...) (read-file fn dir #'filename))) | |
#'(begin exp ...)))))))) | |
(define-syntax include-from-path | |
(lambda (x) | |
(syntax-case x () | |
((k filename) | |
(let ((fn (syntax->datum #'filename))) | |
(with-syntax ((fn (datum->syntax | |
#'filename | |
(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))))) | |