Next: stdlib syntax-case temporaries, Previous: stdlib syntax-case identifier, Up: stdlib syntax-case [Index]
Strip all syntactic information from a syntax object and returns the corresponding Scheme datum.
Identifiers stripped in this manner are converted to their symbolic
names, which can then be compared with eq?. Thus, a predicate
symbolic-identifier=? might be defined as follows.
(define (symbolic-identifier=? x y)
(eq? (syntax->datum x)
(syntax->datum y)))
template-id must be a template identifier and datum should be a datum value.
The datum->syntax procedure returns a syntax-object
representation of datum that contains the same contextual
information as template-id, with the effect that the syntax object
behaves as if it were introduced into the code when template-id
was introduced.
The datum->syntax procedure allows a transformer to “bend”
lexical scoping rules by creating implicit identifiers that
behave as if they were present in the input form, thus permitting the
definition of macros that introduce visible bindings for, or references
to, identifiers that do not appear explicitly in the input form.
In the following example, green and red have the role of
template-id in the alpha and beta macro uses at the
end of the (test) library:
(library (lib)
(export beta red)
(import (rnrs))
(define red #t)
(define data 'in-lib)
(define-syntax beta
(lambda (stx)
(syntax-case stx ()
((_ ?context)
#`(begin #,(datum->syntax #'?context 'data)))))))
(library (test)
(export)
(import (rnrs) (lib))
(define green #t)
(define data 'in-test)
(define-syntax alpha
(lambda (stx)
(syntax-case stx ()
((_ ?context)
#`(begin #,(datum->syntax #'?context 'data))))))
(display (alpha green)) -| in-test
(display (alpha red)) -| in-test
(display (beta green)) -| in-test
(display (beta red))) -| in-test
in both the macro uses: the identifier syntax objects representing
‘green’ and ‘red’ are created in the (test) library,
so both the macros reach for data in the context of the
(test) library.
The following example demonstrates why, when macro uses are nested, we cannot rely on the macro keyword syntax object to carry the context of the “outer” macro use:
(library (sublib)
(export beta)
(import (rnrs))
(define data 33)
(define-syntax beta
(lambda (stx)
(syntax-case stx ()
((?context)
#`(begin #,(datum->syntax #'?context 'data)))))))
(library (lib)
(export alpha)
(import (rnrs) (sublib))
(define data 22)
(define-syntax alpha
(lambda (stx)
(syntax-case stx ()
((_)
#'(beta))))))
(library (test)
(export)
(import (rnrs) (lib))
(define data 11)
(display (alpha))) -| 22
in the body of the beta macro: ?context is bound to a
syntax object carrying reference to the lexical context of the body of
alpha; from the body of beta it is impossible to reach the
lexical context of the (test) library, where alpha is
used.
The following example defines a loop expression that uses this
controlled form of identifier capture to bind the variable ‘break’
to an escape procedure within the loop body. (The derived
with-syntax form is like let but binds pattern variables.)
(import (rnrs))
(define-syntax loop
(lambda (x)
(syntax-case x ()
((k e ...)
(with-syntax ((break (datum->syntax #'k 'break)))
#'(call-with-current-continuation
(lambda (break)
(let f () e ... (f)))))))))
(let ((n 3) (ls '()))
(loop
(when (= n 0)
(break ls))
(set! ls (cons 'a ls))
(set! n (- n 1))))
⇒ (a a a)
Were loop to be defined as:
(define-syntax loop
(lambda (x)
(syntax-case x ()
((_ e ...)
#'(call-with-current-continuation
(lambda (break)
(let f () e ... (f))))))))
the variable ‘break’ would not be visible in ‘e ...’.
The datum argument datum may also represent an arbitrary Scheme
form, as demonstrated by the following definition of include.
(define-syntax include
(lambda (x)
(define (read-file fn k)
(let ((p (open-file-input-port fn)))
(let loop ((x (get-datum p)))
(if (eof-object? x)
(begin
(close-port p)
'())
(cons (datum->syntax k x)
(loop (get-datum p)))))))
(syntax-case x ()
((k filename)
(let ((fn (syntax->datum #'filename)))
(with-syntax (((exp ...) (read-file fn #'k)))
#'(begin exp ...)))))))
(include "filename") expands into a begin expression
containing the forms found in the file named by ‘"filename"’. For
example, if the file flib.ss contains:
(define f (lambda (x) (g (* x x))))
and the file glib.ss contains:
(define g (lambda (x) (+ x x)))
the expression:
(let () (include "flib.ss") (include "glib.ss") (f 5))
evaluates to 50.
The definition of include uses datum->syntax to convert
the objects read from the file into syntax objects in the proper lexical
context, so that identifier references and definitions within those
expressions are scoped where the include form appears.
Next: stdlib syntax-case temporaries, Previous: stdlib syntax-case identifier, Up: stdlib syntax-case [Index]