;;;; -*-scheme-*- ;;;; ;;;; Copyright (C) 2001, 2003, 2006, 2009, 2010 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 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 according to the Git ;;; revision control logs corresponding to this file: 2009, 2010. ;;; 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 ;;; fluid-let-syntax ;;; 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 () ;; Private version of and-map that handles multiple lists. (define and-map* (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 x xr) (and (apply f x xr) (andmap first rest))))))))) (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 fx+ +) (define fx- -) (define fx= =) (define fx< <) (define top-level-eval-hook (lambda (x mod) (primitive-eval x))) (define local-eval-hook (lambda (x mod) (primitive-eval x))) (define-syntax gensym-hook (syntax-rules () ((_) (gensym)))) (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 (pair? e) s) (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 build-lexical-var (syntax-rules () ((_ 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) (cond ((syntax-object? x) (source-annotation (syntax-object-expression x))) ((pair? x) (let ((props (source-properties x))) (if (pair? props) props #f))) (else #f)))) (define-syntax arg-check (syntax-rules () ((_ 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 --> ;; 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. ;; ::= ((