Beautiful Code [286]
The body is in the scope of the binding created by let, so it is expanded with the extended wrap and environment. The righthand-side expression, expr, is not within the scope, so it is expanded with the original wrap and environment.
The exp-letrec-syntax procedure handles single-binding letrec-syntax forms. As with lambda and let, a substitution mapping the bound identifier—in this case, a keyword rather than a variable—to a fresh label is added to the wrap on the body, and an association from the label to a binding is added to the environment while the body is recursively processed. The binding is a macro binding rather than a lexical binding, and the binding value is the result of recursively expanding and evaluating the righthand-side expression of the letrec-syntax form.
In contrast with let, the righthand-side expression is also wrapped with a substitution from the keyword to the label and expanded with the extended environment; this allows the macro to be recursive. This would not be done if the form were a let-syntax form instead of a letrec-syntax form. The output produced by expanding a letrec-syntax form consists only of the output of the recursive call to the expander on the body of the form:
(define exp-letrec-syntax
(lambda (x r mr)
(syntax-case x ()
[(_ ((kwd expr)) body)
(let ([label (make-label)])
(let ([b (make-binding 'macro
(eval (exp (add-subst #'kwd label #'expr)
mr mr)))])
(exp (add-subst #'kwd label #'body)
(extend-env label b r)
(extend-env label b mr))))])))
Both the runtime and meta environments are extended in this case, since transformers are available both in runtime and transformer code.
25.2.11. Parsing and Constructing Syntax Objects
Macros are written in a pattern-matching style using syntax-case to match and take apart the input, and syntax to reconstruct the output. The implementation of the pattern matching and reconstruction is outside the scope of this chapter, but the following low-level operators can be used as the basis for the implementation. The syntax-case form can be built from the following set of three operators that treat syntax objects as abstract s-expressions:
(define syntax-pair?
(lambda (x)
(pair? (syntax-object-expr x))))
(define syntax-car
(lambda (x)
(extend-wrap
(syntax-object-wrap x)
(car (syntax-object-expr x)))))
(define syntax-cdr
(lambda (x)
(extend-wrap
(syntax-object-wrap x)
(cdr (syntax-object-expr x)))))
The definitions of syntax-car and syntax-cdr employ the extend-wrap helper defined in the earlier section "Creating Wraps" to push the wrap on the pair onto the car and cdr.
Similarly, syntax can be built from the following more basic version of syntax that handles constant input, but not pattern variables and ellipses:
(define exp-syntax
(lambda (x r mr)
(syntax-case x ()
[(_ t) `(quote ,#'t)])))
In essence, the simplified version of syntax is just like quote except that syntax does not strip the encapsulated value but rather leaves the syntax wrappers intact.
25.2.12. Comparing Identifiers
Identifiers are compared based on their intended use. They may be compared as symbols by using the pointer equivalence operator eq? on the symbolic names of the identifiers. They may also be compared according to their intended use as free or bound identifiers in the output of a macro.
Two identifiers are considered equivalent by free-identifier=? if they would resolve to the same binding if introduced into the output of a macro outside of any binding introduced by the macro. Equivalency is tested by comparing the labels to which the identifiers resolve, as described previously in the section "Identifier Resolution":
(define free-identifier=?
(lambda (x y)
(eq? (id-label x) (id-label y))))
The free-identifier=? predicate is often used to check for auxiliary keywords, such as else in cond or case.
Two identifiers are considered equivalent by bound-identifier=? if a reference to one would be captured by an enclosing binding for another. This is accomplished