Beautiful Code [282]
25.2.1. Representations
The most important aspect of the syntax-case mechanism is its abstract representation of program source code as syntax objects. As described above, a syntax object encapsulates not only a representation of the program source code but also a wrap that provides sufficient information about the identifiers contained within the code to implement hygiene:
(define-record syntax-object (expr wrap))
The define-record form creates a new type of value with the specified name (in this case, syntax-object) and fields (in this case, expr and wrap), along with a set of procedures to manipulate it. The procedures in this case are:
make-syntax-object
Returns a new syntax object with the expr and wrap fields initialized to the values of its arguments
syntax-object?
Returns true if and only if its argument is a syntax object
syntax-object-expr
Returns the value of the expr field of a syntax-object
syntax-object-wrap
Returns the value of the wrap field of a syntax object
A complete implementation of syntax-case might also include, within each syntax object, source information to be tracked through the expansion process.
Each wrap, as explained previously, consists of a list of marks and substitutions. Marks are distinguished by their object identity and do not require any fields:
(define-record mark ())
A substitution maps a symbolic name and list of marks to a label:
(define-record subst (sym mark* label))
Labels, like marks, are distinguished by their identity and require no fields:
(define-record label ())
The expand-time environment maintained by the expander maps labels to bindings. The environment is structured as a traditional association list—i.e., a list of pairs, each car of which contains a label and each cdr of which contains a binding. Bindings consist of a type (represented as a symbol) and a value:
(define-record binding (type value))
The type identifies the nature of the binding: macro for keyword bindings and lexical for lexical variable bindings. The value is any additional information required to specify the binding, such as the transformation procedure when the binding is a keyword binding.
25.2.2. Producing Expander Output
The expander's output is a simple s-expression in the core language and is thus constructed for the most part using Scheme's quasiquote syntax for creating list structure. For example, a lambda expression may be created with formal parameter var and body body as follows:
`(lambda (,var) ,body)
The expander does need to create fresh names, however, and does so via the gen-var helper, which makes use of the Scheme primitives for converting strings to symbols and vice versa, along with a local sequence counter:
(define gen-var
(let ([n 0])
(lambda (id)
(set! n (+ n 1))
(let ([name (syntax-object-expr id)])
(string->symbol (format "~s.~s" name n))))))
25.2.3. Stripping Syntax Objects
Whenever a quote form is encountered in the input, the expander must return a representation of the constant contents appearing within the quote form. To do this, it must strip off any embedded syntax objects and wraps using the strip procedure, which traverses the syntax object and list structure of its input and recreates an s-expression representation of its input:
(define strip
(lambda (x)
(cond
[(syntax-object? x)
(if (top-marked? (syntax-object-wrap x))
(syntax-object-expr x)
(strip (syntax-object-expr x)))]
[(pair? x)
(let ([a (strip (car x))] [d (strip (cdr x))])
(if (and (eq? a (car x)) (eq? d (cdr x)))
x
(cons a d)))]
[else x])))
Traversal terminates along any branch of the input expression when something other than a syntax object or pair is found—i.e., when a symbol or immediate value is found. It also terminates when a syntax object is found to be "top marked"—i.e., when