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]