;;;; -*-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 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. ;;; Modified by Mark H Weaver 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. ;;; ;;; 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)) (and (not (equal? module '(primitive))) (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-call (lambda (source fun-exp arg-exps) (make-call 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-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))) ((primitive) (syntax-violation #f "primitive not in operator position" 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-primcall (lambda (src name args) (make-primcall src name args))) (define build-primref (lambda (src name) (make-primitive-ref src name))) (define (build-data src exp) (make-const src exp)) (define build-sequence (lambda (src exps) (if (null? (cdr exps)) (car exps) (make-seq src (car exps) (build-sequence #f (cdr exps)))))) (define build-let (lambda (src ids vars val-exps body-exp) (for-each maybe-name-value! ids val-exps) (if (null? vars) body-exp (make-let src ids vars val-exps body-exp)))) (define build-named-let (lambda (src ids vars val-exps body-exp) (let ((f (car vars)) (f-name (car ids)) (vars (cdr vars)) (ids (cdr ids))) (let ((proc (build-simple-lambda src ids #f vars '() body-exp))) (maybe-name-value! f-name proc) (for-each maybe-name-value! ids val-exps) (make-letrec src #f (list f-name) (list f) (list proc) (build-call src (build-lexical-reference 'fun src f-name f) val-exps)))))) (define build-letrec (lambda (src in-order? ids vars val-exps body-exp) (if (null? vars) body-exp (begin (for-each maybe-name-value! ids val-exps) (make-letrec src in-order? ids vars val-exps body-exp))))) ;; 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 --> ;; 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 ;; resolve-identifier when it finds no other bindings. ;; ::= ((