;; alexpander.scm: a macro expander for scheme. ;; based on 1.65 (module crunch-expander (crunch-expand) (import scheme chicken) (use extras) ;; Copyright 2002-2004,2006,2007 Al Petrofsky ;; LICENSING (3-clause BSD or GNU GPL 2 and up) ;; Redistribution and use in source and binary forms, with or without ;; modification, are permitted provided that the following conditions ;; are met: ;; ;; Redistributions of source code must retain the above copyright ;; notice, this list of conditions and the following disclaimer. ;; ;; Redistributions in binary form must reproduce the above copyright ;; notice, this list of conditions and the following disclaimer in ;; the documentation and/or other materials provided with the ;; distribution. ;; ;; Neither the name of the author nor the names of its contributors ;; may be used to endorse or promote products derived from this ;; software without specific prior written permission. ;; ;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ;; HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, ;; BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS ;; OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED ;; AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY ;; WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ;; POSSIBILITY OF SUCH DAMAGE. ;; Alternatively, you may redistribute, use, or modify this software ;; according to 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. ;; INTRODUCTION: ;; 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. ;; To find the latest version of this program, try here: ;; http://petrofsky.org/src/alexpander.scm ;; ;; To find older versions or the log messages between versions, try here: ;; http://petrofsky.org/src/RCS/alexpander.scm,v ;; If you are wondering what "r5rs scheme" is, see: ;; Richard Kelsey, William Clinger, and Jonathan Rees, "Revised^5 ;; report on the algorithmic language Scheme", Higher-Order and ;; Symbolic Computation, 11(1):7-105, 1998. Available at: ;; PDF: http://www-swiss.ai.mit.edu/~jaffer/r5rs.pdf ;; LaTeX source: ftp://swiss.csail.mit.edu/pub/scheme-reports/r5rs.tar.gz ;; 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 () ;; ((let ((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))) => () ;; Internal syntax definitions. ;; Internal syntax definitions are supported wherever they would make ;; sense (see the BNF), and they 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 ;; Top-level macro blocks. ;; At the top level, if a macro block (i.e., a let-syntax or ;; letrec-syntax form) has only one body element, or if all of the ;; body elements before the last one are internal syntax definitions, ;; then the last body 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): ;; (define x 1) ;; (define (f) x) ;; (let-syntax () ;; (define x 2) ;; (f)) ;; => 1, in r5rs and alexpander, but 2 in Chez scheme ;; (define x 1) ;; (define (f) x) ;; (let-syntax () ;; (begin ;; (define x 2) ;; (f))) ;; => 2, in alexpander and in Chez scheme, but an error in r5rs. ;; Syntax-rules ellipsis ;; Per 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 (*) *) ;; ;; ---> (