Beautiful Code [284]
(define id-label
(lambda (id)
(let ([sym (syntax-object-expr id)]
[wrap (syntax-object-wrap id)])
(let search ([wrap wrap] [mark* (wrap-marks wrap)])
(if (null? wrap)
(syntax-error id "undefined identifier")
(let ([w0 (car wrap)])
(if (mark? w0)
(search (cdr wrap) (cdr mark*))
(if (and (eq? (subst-sym w0) sym)
(same-marks? (subst-mark* w0) mark*))
(subst-label w0)
(search (cdr wrap) mark*)))))))))
If no matching substitution exists in the wrap, the identifier is undefined, and a syntax error is signaled. It would be possible instead to treat all such identifier references as global variable references.
The id-label procedure obtains the starting list of marks via wrap-marks and uses the same-marks? predicate to compare lists of marks:
(define wrap-marks
(lambda (wrap)
(if (null? wrap)
'()
(let ([w0 (car wrap)])
(if (mark? w0)
(cons w0 (wrap-marks (cdr wrap)))
(wrap-marks (cdr wrap)))))))
(define same-marks?
(lambda (m1* m2*)
(if (null? m1*)
(null? m2*)
(and (not (null? m2*))
(eq? (car m1*) (car m2*))
(same-marks? (cdr m1*) (cdr m2*))))))
Once a label has been found, id-binding is used to find the associated binding, if any, using the assq procedure for performing association-list lookups. If an association is found, the binding in the cdr of the association is returned:
(define label-binding
(lambda (id label r)
(let ([a (assq label r)])
(if a
(cdr a)
(syntax-error id "displaced lexical")))))
If no binding is found, the identifier is a "displaced lexical." This occurs when a macro improperly inserts into its output a reference to an identifier that is not visible in the context of the macro output.
25.2.9. The Expander
With the mechanisms for handling wraps and environments in place, the expander is straightforward. The expression expander, exp, handles macro calls, lexical variable references, applications, core forms, and constants. Macro calls come in two forms: singleton macro-keyword references and structured forms with a macro keyword in the first position.
The exp procedure takes three arguments: a syntax object x, a runtime environment r, and a meta environment mr. The runtime environment is used to process ordinary expressions whose code will appear in the expander's output, while the meta environment is used to process transformer expressions (e.g., on the righthand sides of letrec-syntax bindings), which are evaluated and used at expansion time. The difference between the runtime and meta environments is that the meta environment does not contain lexical variable bindings, because these bindings are not available when the transformer is evaluated and used:
Code View: Scroll / Show All
(define exp
(lambda (x r mr)
(syntax-case x ()
[id
(identifier? #'id)
(let ([b (id-binding #'id r)])
(case (binding-type b)
[(macro) (exp (exp-macro (binding-value b) x) r mr)]
[(lexical) (binding-value b)]
[else (syntax-error x "invalid syntax")]))]
[(e0 e1 ...)
(identifier? #'e0)
(let ([b (id-binding #'e0 r)])
(case (binding-type b)
[(macro) (exp (exp-macro (binding-value b) x) r mr)]
[(lexical)
`(,(binding-value b) ,@(exp-exprs #'(e1 ...) r mr))]
[(core) (exp-core (binding-value b) x r mr)]
[else (syntax-error x "invalid syntax")]))]
[(e0 e1 ...)
`(,(exp #'e0 r mr) ,@(exp-exprs #'(e1 ...) r mr))]
[_
(let ([d (strip x)])
(if (self-evaluating? d)
d
(syntax-error x "invalid syntax")))])))
Macro calls are handled by exp-macro (described shortly) and then re-expanded. Lexical variables are rewritten into the binding value, which is always a generated variable name. Applications are rewritten into lists as in the traditional s-expression syntax for Lisp and Scheme, with the subforms expanded recursively.