;; alexpander.scm: a macro expander for scheme. ;; [RCS tag expunged: this is alexpander 1.58 from petrofsky.org/src] ;; Copyright 2002-2004 Al Petrofsky ;; You may redistribute and/or modify this software under the terms of ;; the GNU General Public License as published by the Free Software ;; Foundation (fsf.org); either version 2, or (at your option) any ;; later version. ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, see ;; http://www.fsf.org/licenses/gpl.txt, or write to the Free Software ;; Foundation, 59 Temple Pl Ste 330, Boston MA 02111, U.S.A.. ;; Feel free to ask me for different licensing terms. ;; INTRO: ;; This file implements a macro-expander for r5rs scheme (plus some ;; interesting extensions). There is no magic here to hook this into ;; your native eval system: this is a simple data-in, data-out program ;; that takes a macro-using program represented as scheme data and ;; produces an equivalent macro-free program represented as scheme ;; data. ;; This is mostly intended as a demonstration. Although it certainly ;; could be useful for adding macros to a simple scheme system that ;; lacks any macros, it may not be feasible to get it to interact ;; properly with a low-level macro system or a module system. ;; The expander is written in portable r5rs scheme, except for one use ;; of the pretty-print procedure which you can easily comment out. ;; To try it out, just load the file and execute (alexpander-repl). ;; Skip to the "BASIC USAGE" section for more information. ;; EXTENSIONS: ;; The expander supports all the features of the r5rs macro system, ;; plus several extensions in the way syntaxes can be specified and ;; used, which are best summarized in BNF: ;; Modified r5rs productions: ;; ---> | | ;; | | | ;; | | | ;; | ;; ---> (define-syntax ) ;; | (begin *) ;; | ;; --> ( ) ;; --> | ;; ---> ( *) ;; ---> (define ) ;; | (define ( ) ) ;; | (define ) ;; | (begin *) ;; | ;; | ;; ---> | ;; | (begin *) ;; | ;; | ;; New productions: ;; --> ;; | ;; | ;; | ;; --> ( ) ;; ;; --> ( ) ;; ;; ---> (*) * ;; ---> let-syntax | letrec-syntax ;; These extensions all have the obvious meaning. ;; Okay, I'll elaborate on that a little bit. Consider the intializer ;; position of a syntax definition and the head position of a ;; list-format expression: ;; (define-syntax ) ;; ( *) ;; In r5rs, must be a transformer. may be an expression, ;; in which case the enclosing expression is taken to be a procedure ;; call and the s are the expressions for the operands, or ;; may be a keyword bound to a syntax (a builtin or transformer), in ;; which case the s are processed according to that syntax. ;; The core generalization in our system is that both and ;; may be any type of expression or syntax. The four forms of syntax ;; allowed are: a transformer (as allowed in the position in ;; r5rs), a keyword (as allowed in the position in r5rs), a ;; macro use that expands into a syntax, and a macro block (let-syntax ;; or letrec-syntax) whose body is a syntax. ;; Some examples: ;; ;; ;; a macro with a local macro ;; (let-syntax ((foo (let-syntax ((bar (syntax-rules () ((bar x) (- x))))) ;; (syntax-rules () ((foo) (bar 2)))))) ;; (foo)) ;; => -2 ;; ;; ;; an anonymous let transformer, used directly in a macro call. ;; ((syntax-rules () ;; ((_ ((var init) ...) . body) ;; ((lambda (var ...) . body) init ...))) ;; ((x 1) (y 2)) ;; (+ x y)) ;; => 3 ;; ;; ;; a keyword used to initialize a keyword ;; (let-syntax ((q quote)) (q x)) => x ;; ;; ;; Binding a keyword to an expression (which could also be thought ;; ;; of as creating a macro that is called without arguments). ;; (let ((n 0)) ;; (let-syntax ((x (set! n (+ n 1)))) ;; (begin x x x n))) ;; => 3 ;; ;; (let-syntax ((x append)) ((x x))) => () ;; Top-level macro blocks. ;; At top level, if a macro block (a let-syntax or letrec-syntax form) ;; has only one body element, that element need not be an expression ;; (as would be required in r5rs). Instead, it may be anything ;; allowed at top level: an expression, a definition, a begin sequence ;; of top-level forms, or another macro block containing a top-level ;; form. ;; (let-syntax ((- quote)) ;; (define x (- 1))) ;; (list x (- 1)) => (1 -1) ;; Note that, unlike the similar extension in Chez scheme 6.0, this is ;; still r5rs-compatible, because we only treat definitions within the ;; last body element as top-level definitions (and r5rs does not allow ;; internal definitions within a body's last element, even if it is a ;; begin form): ;; (begin ;; (define x 1) ;; (let-syntax () ;; (define x 2) ;; 'blah) ;; x) ;; => 1, in r5rs and alexpander, but 2 in Chez scheme ;; (begin ;; (define x 1) ;; (let-syntax () ;; (begin (define x 2) ;; 'blah)) ;; x) ;; => 2, in alexpander and in Chez scheme, but an error in r5rs. ;; Expressions among internal definitions. ;; A definition of the form (define ) causes the ;; expression to be evaluated at the conclusion of any enclosing set ;; of internal definitons. That is, at top level, (define ;; ) is equivalent to just plain . As for ;; internal definitions, the following are equivalent: ;; (let () ;; (define v1 ) ;; (define ) ;; (define ) ;; (define v2 ) ;; (define ) ;; (begin ;; ;; )) ;; ;; (let () ;; (define v1 ) ;; (define v2 ) ;; (begin ;; ;; ;; ;; ;; )) ;; (Yes, it would probably be better to have a separate builtin for ;; this rather than to overload define.) ;; This feature makes it possible to implement a define-values that ;; works properly both at top-level and among internal definitions: ;; (define define-values-temp #f) ;; ;; (define-syntax define-values ;; (syntax-rules () ;; ((define-values (var ...) init) ;; (begin ;; (define define-values-temp (call-with-values (lambda () init) list)) ;; (define var #f) ... ;; (define ;; (set!-values (var ...) (apply values define-values-temp))))))) ;; (Set!-values is implementable using just r5rs features and is left ;; as an exercise.) ;; When used among internal definitions, the definition of ;; define-values-temp in define-values's output creates a local ;; binding, and thus the top-level binding of define-values-temp is ;; irrelevant. When used at top-level, the definition of ;; define-values-temp in the output does not create a binding, it ;; mutates the top-level binding of define-values-temp. Thus, all ;; top-level uses of define-values share a single temp variable. For ;; internal-definition-level uses of define-values, a single shared ;; temp would not be sufficient, but things work out okay because ;; hygienic renaming causes each such use to create a distinct temp ;; variable. ;; The version below works the same way, but hides from the top-level ;; environment the temp that is shared by top-level uses of ;; define-values. For a bit of tutorial and rationale about this ;; technique, see usenet article ;; <8765tos2y9.fsf@radish.petrofsky.org>: ;; (define-syntax define-values ;; (let-syntax ((temp (syntax-rules ()))) ;; (syntax-rules () ;; ((define-values (var ...) init) ;; (begin ;; (define temp (call-with-values (lambda () init) list)) ;; (define var #f) ... ;; (define (set!-values (var ...) (apply values temp)))))))) ;; Internal syntax definitions. ;; Internal syntax definitions are supported wherever they would make ;; sense (see the BNF) and have the letrec-syntax semantics you would ;; expect. It is legal for the initializer of an internal variable ;; definition to use one of the internal syntax definitions in the ;; same body: ;; (let () ;; (define x (y)) ;; (define-syntax y (syntax-rules () ((y) 1))) ;; x) ;; => 1 ;; It's also legal for internal syntax definitions to be mutually ;; recursive transformers, but it is an error for the expansion of a ;; syntax definition's initializer to require the result of another ;; initializer: ;; (let () ;; (define-syntax m1 (syntax-rules () ((m1) #f) ((m1 . args) (m2 . args)))) ;; (define-syntax m2 (syntax-rules () ((m2 arg . args) (m1 . args)))) ;; (m1 foo bar baz)) ;; => #f ;; (let () ;; (define-syntax simple-transformer ;; (syntax-rules () ;; ((simple-transformer pattern template) ;; (syntax-rules () (pattern template))))) ;; (define-syntax m (simple-transformer (m x) (- x))) ;; (m 1)) ;; => error ("Premature use of keyword bound by an internal define-syntax") ;; (let () ;; (define-syntax simple-transformer ;; (syntax-rules () ;; ((simple-transformer pattern template) ;; (syntax-rules () (pattern template))))) ;; (let () ;; (define-syntax m (simple-transformer (m x) (- x))) ;; (m 1))) ;; => -1 ;; Syntax-rules ellipsis ;; Per draft SRFI-46, syntax-rules transformers can specify the ;; identifier to be used as the ellipsis (such a specification is ;; treated as a hygienic binding), and a list pattern may contain ;; subpatterns after an ellipsis as well as before it: ;; ---> (syntax-rules (*) *) ;; | (syntax-rules (*) *) ;; ;; ---> (