;;; Portable implementation of syntax-case ;;; 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. ;;; Before attempting to port this code to a new implementation of ;;; Scheme, please read the notes below carefully. ;;; This file defines the syntax-case expander, sc-expand, and a set ;;; of associated syntactic forms and procedures. Of these, the ;;; following are documented in The Scheme Programming Language, ;;; Second Edition (R. Kent Dybvig, Prentice Hall, 1996). Most are ;;; also documented in the R4RS and draft R5RS. ;;; ;;; bound-identifier=? ;;; datum->syntax-object ;;; define-syntax ;;; fluid-let-syntax ;;; free-identifier=? ;;; generate-temporaries ;;; identifier? ;;; identifier-syntax ;;; let-syntax ;;; letrec-syntax ;;; syntax ;;; syntax-case ;;; syntax-object->datum ;;; syntax-rules ;;; with-syntax ;;; ;;; All standard Scheme syntactic forms are supported by the expander ;;; or syntactic abstractions defined in this file. Only the R4RS ;;; delay is omitted, since its expansion is implementation-dependent. ;;; The remaining exports are listed below: ;;; ;;; (sc-expand datum) ;;; if datum represents a valid expression, sc-expand returns an ;;; expanded version of datum in a core language that includes no ;;; syntactic abstractions. The core language includes begin, ;;; define, if, lambda, letrec, quote, and set!. ;;; (eval-when situations expr ...) ;;; conditionally evaluates expr ... at compile-time or run-time ;;; depending upon situations (see the Chez Scheme System Manual, ;;; Revision 3, for a complete description) ;;; (syntax-error object message) ;;; used to report errors found during expansion ;;; (install-global-transformer symbol value) ;;; used by expanded code to install top-level syntactic abstractions ;;; (syntax-dispatch e p) ;;; used by expanded code to handle syntax-case matching ;;; The following nonstandard procedures must be provided by the ;;; implementation for this code to run. ;;; ;;; (void) ;;; returns the implementation's cannonical "unspecified value". This ;;; usually works: (define void (lambda () (if #f #f))). ;;; ;;; (andmap proc list1 list2 ...) ;;; returns true if proc returns true when applied to each element of list1 ;;; along with the corresponding elements of list2 .... ;;; The following definition works but does no error checking: ;;; ;;; (define andmap ;;; (lambda (f first . rest) ;;; (or (null? first) ;;; (if (null? rest) ;;; (let andmap ((first first)) ;;; (let ((x (car first)) (first (cdr first))) ;;; (if (null? first) ;;; (f x) ;;; (and (f x) (andmap first))))) ;;; (let andmap ((first first) (rest rest)) ;;; (let ((x (car first)) ;;; (xr (map car rest)) ;;; (first (cdr first)) ;;; (rest (map cdr rest))) ;;; (if (null? first) ;;; (apply f (cons x xr)) ;;; (and (apply f (cons x xr)) (andmap first rest))))))))) ;;; ;;; The following nonstandard procedures must also be provided by the ;;; implementation for this code to run using the standard portable ;;; hooks and output constructors. They are not used by expanded code, ;;; and so need be present only at expansion time. ;;; ;;; (eval x) ;;; where x is always in the form ("noexpand" expr). ;;; returns the value of expr. the "noexpand" flag is used to tell the ;;; evaluator/expander that no expansion is necessary, since expr has ;;; already been fully expanded to core forms. ;;; ;;; (error who format-string why what) ;;; where who is either a symbol or #f, format-string is always "~a ~s", ;;; why is always a string, and what may be any object. error should ;;; signal an error with a message something like ;;; ;;; "error in : " ;;; ;;; (gensym) ;;; returns a unique symbol each time it's called ;;; ;;; (putprop symbol key value) ;;; (getprop symbol key) ;;; key is always the symbol *sc-expander*; value may be any object. ;;; putprop should associate the given value with the given symbol in ;;; some way that it can be retrieved later with getprop. ;;; When porting to a new Scheme implementation, you should define the ;;; procedures listed above, load the expanded version of psyntax.ss ;;; (psyntax.pp, which should be available whereever you found ;;; psyntax.ss), and register sc-expand as the current expander (how ;;; you do this depends upon your implementation of Scheme). You may ;;; change the hooks and constructors defined toward the beginning of ;;; the code below, but to avoid bootstrapping problems, do so only ;;; after you have a working version of the expander. ;;; If you find that this code loads or runs slowly, consider ;;; switching to faster hardware or a faster implementation of ;;; Scheme. In Chez Scheme on a 200Mhz Pentium Pro, expanding, ;;; compiling (with full optimization), and loading this file takes ;;; between one and two seconds. ;;; In the expander implementation, we sometimes use syntactic abstractions ;;; when procedural abstractions would suffice. For example, we define ;;; top-wrap and top-marked? as ;;; (define-syntax top-wrap (identifier-syntax '((top)))) ;;; (define-syntax top-marked? ;;; (syntax-rules () ;;; ((_ w) (memq 'top (wrap-marks w))))) ;;; rather than ;;; (define top-wrap '((top))) ;;; (define top-marked? ;;; (lambda (w) (memq 'top (wrap-marks w)))) ;;; On ther other hand, we don't do this consistently; we define make-wrap, ;;; wrap-marks, and wrap-subst simply as ;;; (define make-wrap cons) ;;; (define wrap-marks car) ;;; (define wrap-subst cdr) ;;; In Chez Scheme, the syntactic and procedural forms of these ;;; abstractions are equivalent, since the optimizer consistently ;;; integrates constants and small procedures. Some Scheme ;;; implementations, however, may benefit from more consistent use ;;; of one form or the other. ;;; implementation information: ;;; "begin" is treated as a splicing construct at top level and at ;;; the beginning of bodies. Any sequence of expressions that would ;;; be allowed where the "begin" occurs is allowed. ;;; "let-syntax" and "letrec-syntax" are also treated as splicing ;;; constructs, in violation of the R4RS appendix and probably the R5RS ;;; when it comes out. A consequence, let-syntax and letrec-syntax do ;;; not create local contours, as do let and letrec. Although the ;;; functionality is greater as it is presently implemented, we will ;;; probably change it to conform to the R4RS/expected R5RS. ;;; 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-object. ;;; 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. ;;; Top-level variable definitions of syntax keywords is not permitted. ;;; Any solution allowing this would be kludgey and would yield ;;; surprising results in some cases. We can provide an undefine-syntax ;;; form. The questions is, should define be an implicit undefine-syntax? ;;; We've decided no for now. ;;; 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 input to sc-expand may contain "annotations" describing, e.g., the ;;; source file and character position from where each object was read if ;;; it was read from a file. These annotations are handled properly by ;;; sc-expand only if the annotation? hook (see hooks below) is implemented ;;; properly and the operators make-annotation, annotation-expression, ;;; annotation-source, annotation-stripped, and set-annotation-stripped! ;;; are supplied. If annotations are supplied, the proper annotation ;;; source is passed to the various output constructors, allowing ;;; implementations to accurately correlate source and expanded code. ;;; Contact one of the authors for details if you wish to make use of ;;; this feature. ;;; Bootstrapping: ;;; When changing syntax-object representations, it is necessary to support ;;; both old and new syntax-object representations in id-var-name. It ;;; should be sufficient to recognize old representations and treat ;;; them as not lexically bound. (let () (define-syntax define-structure (lambda (x) (define construct-name (lambda (template-identifier . args) (datum->syntax-object template-identifier (string->symbol (apply string-append (map (lambda (x) (if (string? x) x (symbol->string (syntax-object->datum x)))) args)))))) (syntax-case x () ((_ (name id1 ...)) (andmap identifier? (syntax (name id1 ...))) (with-syntax ((constructor (construct-name (syntax name) "make-" (syntax name))) (predicate (construct-name (syntax name) (syntax name) "?")) ((access ...) (map (lambda (x) (construct-name x (syntax name) "-" x)) (syntax (id1 ...)))) ((assign ...) (map (lambda (x) (construct-name x "set-" (syntax name) "-" x "!")) (syntax (id1 ...)))) (structure-length (+ (length (syntax (id1 ...))) 1)) ((index ...) (let f ((i 1) (ids (syntax (id1 ...)))) (if (null? ids) '() (cons i (f (+ i 1) (cdr ids))))))) (syntax (begin (define constructor (lambda (id1 ...) (vector 'name id1 ... ))) (define predicate (lambda (x) (and (vector? x) (= (vector-length x) structure-length) (eq? (vector-ref x 0) 'name)))) (define access (lambda (x) (vector-ref x index))) ... (define assign (lambda (x update) (vector-set! x index update))) ...))))))) (let () (define noexpand "noexpand") ;;; hooks to nonportable run-time helpers (begin (define fx+ +) (define fx- -) (define fx= =) (define fx< <) (define annotation? (lambda (x) #f)) (define top-level-eval-hook (lambda (x) (eval `(,noexpand ,x)))) (define local-eval-hook (lambda (x) (eval `(,noexpand ,x)))) (define error-hook (lambda (who why what) (error who "~a ~s" why what))) (define-syntax gensym-hook (syntax-rules () ((_) (gensym)))) (define put-global-definition-hook (lambda (symbol binding) (putprop symbol '*sc-expander* binding))) (define get-global-definition-hook (lambda (symbol) (getprop symbol '*sc-expander*))) ) ;;; output constructors (begin (define-syntax build-application (syntax-rules () ((_ source fun-exp arg-exps) `(,fun-exp . ,arg-exps)))) (define-syntax build-conditional (syntax-rules () ((_ source test-exp then-exp else-exp) `(if ,test-exp ,then-exp ,else-exp)))) (define-syntax build-lexical-reference (syntax-rules () ((_ type source var) var))) (define-syntax build-lexical-assignment (syntax-rules () ((_ source var exp) `(set! ,var ,exp)))) (define-syntax build-global-reference (syntax-rules () ((_ source var) var))) (define-syntax build-global-assignment (syntax-rules () ((_ source var exp) `(set! ,var ,exp)))) (define-syntax build-global-definition (syntax-rules () ((_ source var exp) `(define ,var ,exp)))) (define-syntax build-lambda (syntax-rules () ((_ src vars exp) `(lambda ,vars ,exp)))) (define-syntax build-primref (syntax-rules () ((_ src name) name) ((_ src level name) name))) (define-syntax build-data (syntax-rules () ((_ src exp) `',exp))) (define build-sequence (lambda (src exps) (if (null? (cdr exps)) (car exps) `(begin ,@exps)))) (define build-letrec (lambda (src vars val-exps body-exp) (if (null? vars) body-exp `(letrec ,(map list vars val-exps) ,body-exp)))) (define-syntax build-lexical-var (syntax-rules () ((_ src id) (gensym)))) (define-syntax self-evaluating? (syntax-rules () ((_ e) (let ((x e)) (or (boolean? x) (number? x) (string? x) (char? x) (null? x)))))) ) (define-structure (syntax-object expression wrap)) (define-syntax unannotate (syntax-rules () ((_ x) (let ((e x)) (if (annotation? e) (annotation-expression e) e))))) (define-syntax no-source (identifier-syntax #f)) (define source-annotation (lambda (x) (cond ((annotation? x) (annotation-source x)) ((syntax-object? x) (source-annotation (syntax-object-expression x))) (else no-source)))) (define-syntax arg-check (syntax-rules () ((_ pred? e who) (let ((x e)) (if (not (pred? x)) (error-hook who "invalid argument" x)))))) ;;; compile-time environments ;;; wrap and environment comprise two level mapping. ;;; wrap : id --> label ;;; env : label --> ;;; 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. ;;; ::= ((