Beautiful Code [287]
(define bound-identifier=?
(lambda (x y)
(and (eq? (syntax-object-expr x) (syntax-object-expr y))
(same-marks?
(wrap-marks (syntax-object-wrap x))
(wrap-marks (syntax-object-wrap y))))))
The bound-identifier=? predicate is often used to check for duplicate identifier errors in a binding form, such as lambda or let.
25.2.13. Conversions
The conversion from s-expression to syntax object performed by datum->syntax requires only that the wrap be transferred from the template identifier to the s-expression:
(define datum->syntax
(lambda (template-id x)
(make-syntax-object x (syntax-object-wrap template-id))))
The opposite conversion involves stripping the wrap away from a syntax object, so syntax->datum is just strip:
(define syntax->datum strip)
25.2.14. Starting Expansion
All of the pieces are now in place to expand Scheme expressions containing macros into expressions in the core language. The main expander merely supplies an initial wrap and environment that include names and bindings for the core forms and primitives:
(define expand
(lambda (x)
(let-values ([(wrap env) (initial-wrap-and-env)])
(exp (make-syntax-object x wrap) env env))))
The initial wrap consists of a set of substitutions mapping each predefined identifier to a fresh label, and the initial environment associates each of these labels with the corresponding binding:
Code View: Scroll / Show All
(define initial-wrap-and-env
(lambda ()
(define id-binding*
`((quote . ,(make-binding 'core exp-quote))
(if . ,(make-binding 'core exp-if))
(lambda . ,(make-binding 'core exp-lambda))
(let . ,(make-binding 'core exp-let))
(letrec-syntax . ,(make-binding 'core exp-letrec-syntax))
(identifier? . ,(make-binding 'lexical 'identifier?))
(free-identifier=? . ,(make-binding 'lexical 'free-identifier=?))
(bound-identifier=? . ,(make-binding 'lexical 'bound-identifier=?))
(datum->syntax . ,(make-binding 'lexical 'datum->syntax))
(syntax->datum . ,(make-binding 'lexical 'syntax->datum))
(syntax-error . ,(make-binding 'lexical 'syntax-error))
(syntax-pair? . ,(make-binding 'lexical 'syntax-pair?))
(syntax-car . ,(make-binding 'lexical 'syntax-car))
(syntax-cdr . ,(make-binding 'lexical 'syntax-cdr))
(syntax . ,(make-binding 'core exp-syntax))
(list . ,(make-binding 'core 'list))))
(let ([label* (map (lambda (x) (make-label)) id-binding*)])
(values
`(,@(map (lambda (sym label)
(make-subst sym (list top-mark) label))
(map car id-binding*)
label*)
,top-mark)
(map cons label* (map cdr id-binding*))))))
In addition to the entries listed, the initial environment should also include bindings for the built-in syntactic forms we have not implemented (e.g., letrec and let-syntax), as well as for all built-in Scheme procedures. It should also include a full version of syntax and, in place of syntax-pair?, syntax-car, and syntax-cdr, it should include syntax-case.
Syntactic Abstraction: The syntax-case Expander > Example
25.3. Example
We now trace through the following example from the beginning of this chapter:
(let ([t #t]) (or #f t))
We assume that or has been defined to do the transformation given at the beginning of the chapter, using the equivalent of the following definition of or from the section "Brief Introduction to syntax-case":
(define-syntax or
(lambda (x)
(syntax-case x ()
[(_ e1 e2) #'(let ([t e1]) (if t t e2))])))
At the outset, the expander is presented with a syntax object whose expression is(let ([t #t]) (or #f t)), and the wrap is empty, except for the contents of the initial wrap, which we suppress for brevity. (We identify syntax objects by enclosing the expression and wrap entries, if any, in angle brackets.)
<(let ((t #t)) (or #f t))>
The expander is also presented with the initial environment, which we assume contains a binding for the macro or as well as for the core forms and built-in procedures. Again, these environment entries