![]()
|
PLT MzScheme: Language Manual
|
|
(syntax->list
stx
)
returns an immutable list of syntax objects or
#f
. The result is a list of syntax objects when
(
would produce a list. In other
words, syntax pairs in syntax-object->datum
stx
)(syntax-e
are
flattened.stx
)
(syntax-object->datum
stx
)
returns an S-expression by
stripping the syntactic information from stx
. Graph
structure is preserved by the conversion.
(datum->syntax-object
ctxt-stx v
[src-stx-or-list prop-stx
])
converts the S-expression v
to a syntax object, using
syntax objects already in v
in the result. Converted
objects in v
are given the lexical context information of
ctxt-stx
and the source-location information of
src-stx-or-list
; if the resulting syntax object has no
properties, then it is given the properties of
prop-stx
. Any of ctxt-stx
, src-stx-or-list
,
or prop-stx
can be #f
, in which case the
resulting syntax has no lexical context, source information,
and/or new properties. If src-stx-or-list
is not #f
or a syntax object, it must be a list of five elements:
(list
source-name-v line-k column-k position-k span-k)
where source-name-v
is an arbitrary value for the source
name; line-k
is a positive, exact integer for the source
line, or #f
; and column-k
is a positive, exact
integer for the source column, or #f
; position-k
is a positive, exact integer for the source position, or
#f
; and span-k
is a non-negative, exact integer
for the source span, or #f
. The line-k
and
column-k
values must both be numbers or both be
#f
, otherwise the
exn:application;mismatch
exception is raised.
Graph structure is preserved by the conversion, but graph
structure that is distributed among distinct syntax objects in
v
may be hidden from future applications of
and syntax-object->datum
to the
new syntax object.syntax-graph?
(syntax-graph?
stx
)
returns #t
if stx
might be
preservably shared within a syntax object created by
or read-syntax
. In
general, sharing detection is
approximate -- datum->syntax-object
can construct
syntax objects with sharing that is hidden from
datum->syntax-object
-- but syntax-graph?
reliably
returns syntax-graph?
#t
for at least one syntax object in a cyclic
structure. Meanwhile, deconstructing a syntax object with
procedures such as
and comparing the results
with syntax-e
can also fail to detect sharing (even cycles),
due to the way lexical information is lazily propagated; only
eq?
reliably exposes sharing in a way
that can be detected with syntax-object->datum
.eq?
(identifier?
v
)
returns #t
if v
is a syntax
object and (syntax-e
produces a symbol.stx
)
(generate-temporaries
stx-pair
)
returns a list of identifiers
that are distinct from all other identifiers. The list contains
as many identifiers as stx-pair
contains elements. The
stx-pair
argument must be a syntax pair that can be
flattened into a list. The elements of stx-pair
can be
anything, but string, symbol, and identifier elements will be
embedded in the corresponding generated name (useful for
debugging purposes). Generated identifiers can be used for
definitions in a module top level, but section 14.6
describes some limitations with compiled modules.
Hygienic macro expansion depends on information associated with each
syntax object that records the lexical context of the site where the
syntax object is introduced. This information includes the variables
that are bound by lambda
, let
, letrec
,
etc., at the syntax object's introduction site, the require
d
variables at the introduction site, and the macro expansion that
introduces the object.
Based on this information, a particular identifier syntax object falls into one of three classifications:
lexical -- the identifier is bound by
lambda
, let
, letrec
, or some other form
besides module
or a top-level definition.
module-imported -- the identifier is bound through a
require
declaration or a top-level definition within
module
.
free -- the identifier is not bound (and therefore refers to a top-level variable, if the identifier is not within a module).
The identifier-binding
procedure (described in
section 12.3.2) reports an identifiers classification. Further
information about a lexical identifier is available only in relative
terms, such as whether two identifiers refer to the same binding (see
in section 12.3.1). For module-imported
identifiers, information about the module source is available.bound-identifier=?
In a freshly read syntax object, identifiers have no lexical information, so they are all classified as free. During expansion, some identifiers acquire lexical or module-import classifications. An identifier that becomes classified as lexical will remain so classified, though its binding might shift as expansion proceeds (i.e., as nested binding expressions are parsed, and as macro introductions are tracked). An identifier classified as module-imported might similarly shift to the lexical classification, but if it remains module-imported, its source-module designation will never change.
Lexical information is used to expand and parse syntax in a way that it obeys lexical and module scopes. In addition, an identifier's lexical information encompasses a second dimension, which distinguishes the environment of normal expressions from the environment of transformer expressions. The module bindings of each environment can be different, so an identifier may be classified differently depending on whether it is ultimately used in a normal expression or in a transformer expression. See section 12.3.3 and section 12.3.4 for more information on the two environments.
(bound-identifier=?
a-id-stx b-id-stx
)
returns #t
if
the identifier a-id-stx
would bind b-id-stx
(or
vice-versa) if the identifiers were substituted in a suitable
expression context, #f
otherwise.
(free-identifier=?
a-id-stx b-id-stx
)
returns #t
if
a-id-stx
and b-id-stx
access the same lexical,
module, or top-level binding and return the same result for
, syntax-e
#f
otherwise.
(module-identifier=?
a-id-stx b-id-stx
)
returns #t
if
a-id-stx
and b-id-stx
access the same lexical,
module, or top-level binding in the normal environment. Due to
renaming in require
and provide
, the
identifiers may return distinct results with
.syntax-e
(module-transformer-identifier=?
a-id-stx b-id-stx
)
returns
#t
if a-id-stx
and b-id-stx
access the
same lexical, module, or top-level binding in the identifiers'
transformer environments (see section 12.3.3).
(check-duplicate-identifier
id-stx-list
)
compares each
identifier in id-stx-list
with every other identifier in
the list with
. If any comparison
returns bound-identifier=?
#t
, one of the duplicate identifiers is
returned (the first one in id-stx-list
that is a
duplicate), otherwise the result is #f
.
(identifier-binding
id-stx
)
returns one of three kinds of
values, depending on the binding of id-stx
in its normal
environment:
The result is 'lexical
if id-stx
is
bound in its context to anything other than a top-level variable
or a module variable.
The result is a list of four items when id-stx
is
bound in its context to a module-defined variable: (
.list
source-mod source-id nominal-source-mod nominal-source-id)
source-mod
is a module path index or symbol (see
section 12.6.4) that indicates the defining module.
source-id
is a symbol for the variable's name at
its definition site in the source module (as opposed to the
local name returned by
).syntax-object->datum
nominal-source-mod
is a module path index or
symbol (see section 12.6.4) that indicates the module
require
d into the context of id-stx
to provide
its binding. It can be different from source-mod
due to
a re-export in nominal-source-mod
of some imported
identifier.
nominal-source-id
is a symbol for the variable's
name as exported by nominal-source-mod
. It can be
different from source-id
due to a renaming
provide
, even if source-mod
and
nominal-source-mod
are the same.
The result is #f
if id-stx
is not bound
(or bound only to a top-level variable) in its lexical context.
(identifier-transformer-binding
id-stx
)
is like
, except that the reported
information is for the identifier's bindings in the transformer
environment (see section 12.3.3), instead of the normal
environment. If the result is identifier-binding
'lexical
for either of
or
identifier-binding
, then the result is
always identifier-transformer-binding
'lexical
for both.
(identifier-binding-export-position
id-stx
)
returns either
#f
or an exact non-negative integer. It returns an
integer only when identifier-binding
returns a list, when
id-stx
represents an imported binding, and when the source
module assigns internal positions for its definitions. This
function is intended for use by mzc.
(identifier-transformer-binding-export-position
id-stx
)
is
like identifier-binding-export-position
, except that
the reported information is for the transformer
environment. This function is intended for use by mzc.
The top-level environment for transformer expressions is separate from the normal top-level environment. Consequently, top-level definitions are not available for use in top-level transformer definitions. For example, the following program does not work:
(define count 0) (define-syntax (let1 stx) (syntax-case stx () [(_ x v b) (begin (set! count (add1
count)) ; DOESN'T WORK (syntax (let ([x v]) b)))])) (let1 x 2 (add1
x))
The variable count
is bound in the normal top-level
environment, but it is not bound in the transformer environment, so
the attempt to expand (let1 x 2 (
will result in an
undefined-variable error.add1
x))
The initial namespace created by the stand-alone MzScheme application
imports all of MzScheme's built-in syntax, procedures, and constants
into the transformer environment.30 To extend
this environment, a programmer must place definitions into a module,
and then use require-for-syntax
to import the definitions
into the top-level transformer environment.
Like a top-level definition, a top-level require
expression
imports into the normal environment, and the imported bindings are
not made visible in the transformer environment. A top-level
require-for-syntax
imports into the transformer environment
without affecting the normal environment. The require
and
require-for-syntax
forms create separate instantiations of
any module that is imported into both environments, in keeping with
the separation of the environments.
When a lexical variable is introduced by a form other than
module
or a top-level definition, it extends the environment
for both normal and transformer expressions within its scope, but the
binding is only accessible by expressions resolved in the proper
environment (i.e., the one in which it was introduced). In
particular, a transformer expression in a let-syntax
or
letrec-syntax
expression cannot access identifiers bound by
enclosing forms, and an identifier bound in a transformer expression
should not appear as an expression in the result of the
transformer. Such out-of-context uses of a variable are flagged as
syntax errors when attempting to resolve the identifier.
A let-syntax
or letrec-syntax
expression can never
usefully appear as a transformer expression, because MzScheme
provides no mechanism for importing into the meta-transformer
environment that would be used by meta-transformer expressions to
operate on transformer expressions. In other words, an expression of
the form
(let-syntax ([identifier (let-syntax ([identifier expr]) body-expr)]) ...)
is always illegal, assuming that let-syntax
is bound in both the
normal and transformer environments to the let-syntax
of
. No syntax (not even function application) is bound in
mzscheme
expr
's environment. This restriction in the
language is of little consequence, however, since for-syntax exports
allow the definition of syntax applicable to the above
mzscheme
body-expr
.
In the same way that the normal and transformer environments are kept separate at the top level, a module's normal and transformer environments are also separated. Normal imports and definitions in a module -- both variable and syntax -- contribute to the module's normal environment, only.
For example, the module expression
(module mmzscheme
(define (id x) x) (define-syntax (macro stx) (id (syntax (printf
"hi~n")))))
is ill-formed because id
is not bound in the transformer
environment for the macro
implementation. To make
id
usable from the transformer, the body of the module m
would have to be executed -- which is impossible in general, because
a syntax definition such as macro
affects the expansion of the
rest of the module body.
Consequently, if a procedure such as id
is to be used in a
transformer, it must either remain local to the transformer
expression, or reside in a different module. For example, the above
module is trivially repaired as
(module mmzscheme
(define-syntax macro (let ([id (lambda (x) x)]) (lambda (stx) (id (syntax (printf
"hi~n")))))))
The define-syntaxes
form (see section 12.4) is useful
for defining multiple macros that share helper functions. See also
define-syntax-set
in Chapter 14
in PLT MzLib: Libraries Manual.
In the
language, the base environment for a
transformer expression includes all of MzScheme. The
mzscheme
language also provides a
mzscheme
require-for-syntax
form (in the normal environment) for
importing bindings from another module into the importing module's
transformer environment:
(require-for-syntax require-spec ...)
A for-syntax import causes the referenced module to be executed at expansion time, instead of (or possibly in addition to) run time for the module being expanded. The syntax and variable identifiers exported by the for-syntax module are visible within the module's transformer environment, but not its normal environment. Like a normal expression, a transformer expression in a module cannot contain free variables.
Transformer expressions and imports for a module M are executed once each time a module is expanded using M's syntax bindings or using M as a for-syntax import. After the module is expanded, its transformer environment is destroyed, including bindings from modules used at expansion time.
Example:
(module rtmzscheme
(printf
"RT here~n") (define mx (lambda () 7)) (provide mx)) (module etmzscheme
(printf
"ET here~n") (define mx (lambda () 700)) (provide mx)) (module mmzscheme
(require-for-syntaxmzscheme
) (require rt) ; rt provides run-time mx (require-for-syntax et) ; et provides exp-time mx ; The mx below is run-time: (printf
"~a~n" (mx)) ; prints7
when run ; The mx below is exp-time: (define-syntax onem (lambda (stx) (datum->syntax-object
(mx) stx stx))) (printf
"~a~n" (onem)) ; prints700
when run ; The mx below is run-time: (define-syntax twom (lambda (stx) (syntax (mx)))) (printf
"~a~n" (twom))) ; prints7
when run ;"ET here"
is printed during the expansion of m (require m) ; prints"RT here"
, then7
, then700
, then7
This expansion-time execution model explains the need to execute
declared modules only when they are invoked. If a declared module is
imported into other modules only for syntax, then the module is
needed only at expansion time and can be ignored at run time. The
separation of declaration and execution also allows a for-syntax
module to be executed once for each module that it expands through
require-for-syntax
.
The hierarchy of run times avoids confusion among expansion and executing layers that can prevent separate compilation. By ensuring that the layers are separate, a compiler or programming environment can expand, partially expand, or re-expand a module without affecting the module's run-time behavior, whether the module is currently executing or not.
Since transformer expressions may themselves use macros defined by modules with for-syntax imports (to implement the macros), expansion of a module creates a hierarchy of run times (or "tower of expanders"). The expansion time of each layer corresponds to the run time of the next deeper layer.
In the absence of let-syntax
and letrec-syntax
, the
hierarchy of run times would be limited to three levels, since the
transformer expressions for run-time imports would have been expanded
before the importing module must be expanded. The let-syntax
and
letrec-syntax
forms, however, allow syntax visible in a
for-syntax import's transformers to appear in the expansion of
transformer expressions in the module. Consequently, the hierarchy is
bounded in principle only by the number of declared modules. In
practice, the hierarchy will rarely exceed a few levels.
In addition to define-syntax
, let-syntax
, and
letrec-syntax
, MzScheme provides
define-syntaxes
, let-syntaxes
, and
letrec-syntaxes
. These forms are analogous to
define-values
, let-values
, and
letrec-values
, allowing multiple syntax bindings at once
(see section 2.8).
(define-syntaxes (variable ···) expr) (let-syntaxes (((variable ···) expr) ···) expr ···1) (letrec-syntaxes (((variable ···) expr) ···) expr ···1)
MzScheme also provides a letrec-syntaxes+values
form for
binding both values and syntax in a single, mutually recursive scope:
(letrec-syntaxes+values (((variable ···) expr) ···) (((variable ···) expr) ···) expr ···1)
The first set of bindings are syntax bindings (as in
letrec-syntaxes
), and the second set of bindings are normal
variable bindings (as in letrec-values
).
Examples:
;; Definesand
let/cc
let-current-continuation
as the same macro: (define-syntaxes (let/cc
let-current-continuation) (let ([macro (syntax-rules () [(_ id body1 body ...) (call/cc
(lambda (id) body1 body ...))])]) (values
macro macro))) (letrec-syntaxes+values ([(get-id) (syntax-rules () [(_) id])]) ([(id) (lambda (x) x)] [(x) (get-id)]) x) ; => theid
identify procedure
Finally, MzScheme provides fluid-let-syntax
, which is
roughly analogous to fluid-let
.
(fluid-let-syntax ((variable expr) ···) body-expr ···1)
Instead of introducing a new binding, fluid-let-syntax
alters
the mapping for each variable
while expanding the
body-expr
s. Each variable
need not have been mapped to
expansion-time values before, and the re-mapping is not restricted to
instances of variable
in the body-expr
s; it applies when
resolving any identifier that is
to
bound-identifier=?
variable
while the body-expr
s are expanded. However,
fluid-let-syntax
does not mutate any state that is visible to
other expansions (that are possibly running in other threads).
To enable the definition of syntax transformers for application forms
and other data (numbers, vectors, etc.), the syntax expander treats
#%app
, #%top
, and #%datum
as special identifiers.
Any expandable expression of the form
(datum . datum)
where the first datum
is not an identifier bound to an
expansion-time value, is treated as
(#%app datum . datum)
so that the syntax transformer bound to #%app
is applied.
In addition, ()
is treated as (#%app)
.
Similarly, an expression
identifier
where identifier
has no binding other than a top-level or local
module binding, is treated as
(#%top . identifier)
Finally, an expression
datum
where datum
is not an identifier or pair,
is treated as
(#%datum . datum)
The
module binds mzscheme
#%app
, #%top
, and
#%datum
as regular application, top-level variable
reference, and implicit quote, respectively. A module can export
different transformers with these names to support languages
different from conventional Scheme.
In addition, #%module-begin
is used as a transformer for a
module body. The
module binds mzscheme
#%module-begin
to a form that inserts a for-syntax import of
for
syntax definitions. It also exports mzscheme
#%plain-module-begin
,
which can be substituted for #%module-begin
to avoid the
for-syntax import of
. Any other transformer used for
mzscheme
#%module-begin
must expand to
's
mzscheme
#%module-begin
or #%plain-module-begin
.
When an expression is fully expanded, all applications, top-level
variable references, and literal datum expressions will appear as
explicit #%app
, #%top
, and #%datum
forms,
respectively. Those forms can also be used directly by source
code. The #%module-begin
form can never usefully appear in an
expression, and the body of a fully expanded module
declaration
is not wrapped with #%module-begin
.
The following example shows how the special syntax identifiers can be defined to create a non-Scheme module language:
(module lambda-calculusmzscheme
; Restrict lambda to one argument: (define-syntax lc-lambda (syntax-rules () [(_ (x) E) (lambda (x) E)])) ; Restrict application to two expressions: (define-syntax lc-app (syntax-rules () [(_ E1 E2) (E1 E2)])) ; Restrict a lambda calculus module to one body expression: (define-syntax lc-module-begin (syntax-rules () [(_ E) (#%module-begin E)])) ; Disallow numbers, vectors, etc. (define-syntax lc-datum (syntax-rules ())) ; Provide (with renaming): (provide #%top ; keep mzscheme's free-variable error (rename lc-lambda lambda) (rename lc-app #%app) (rename lc-module-begin #%module-begin) (rename lc-datum #%datum))) (module m lambda-calculus ; The only syntax defined bylambda-calculus
is ; unarylambda
, unary application, and variables. ; Also, the module must contain exactly one expression. ((lambda (y) (y y)) (lambda (y) (y y)))) (require m) ; executes m, loops forever
A define-syntax
, let-syntax
, or
letrec-syntax
form associates an identifier to an
expansion-time value. If the expansion-time value is a procedure of
one argument, then the procedure is applied by the syntax expander
when the identifier is used in the scope of the syntax binding.
The transformer for an identifier
is applied whenever the
identifier
appears in an expression position -- not just when
it appears after a parenthesis as (
.
When it does appear as identifier
...)(
, the entire
identifier
...)(
expression is provided as the
argument to the transformer. Otherwise only identifier
...)identifier
is
provided to the transformer.
A typical transformer is implemented as
(lambda (stx) (syntax-case stx () [(_ rest-of-pattern) expr]))
so that identifier
by itself does not match the pattern; thus,
the exn:syntax
exception is raised when identifier
does not appear as
(
.identifier
...)
(make-set!-transformer
proc
)
also
creates a transformer procedure. The proc
argument must be a
procedure of one argument; if the result of
(
is bound as syntax to
make-set!-transformer
proc)identifier
, then proc
is applied as a transformer when
identifier
is used in an expression position, or when it is
used as the target of a set!
assignment:
(set!
.identifier
expr
)
Example:
(let ([x 1] [y 2]) (let-syntax ([x (make-set!-transformer
(lambda (stx) (syntax-case stx (set!) ; Redirect mutation of x to y [(set! id v) (syntax (set! y v))])))] ; Normal use ofx
really getsx
[id (identifier?
(syntax id)) (syntax x)])))]) (begin (set! x 3) (list
x y)))) ; =>'(1 3)
(set!-transformer?
v
)
returns #t
if v
is a
value created by make-set!-transformer
, #f
otherwise.
If a transformer expression produces a non-procedure value, the value
is associated with the identifier as a generic expansion-time
value. Any use of the identifier in an expression position is
rejected as a syntax error, but syntax transformers can access the
value. For example, the define-signature
form (see
Chapter 35
in PLT MzLib: Libraries Manual) associates a component interface
description to the defined identifier.
When a syntax transformer is applied, it can query the bindings of
identifiers in the lexical environment of the expression being
transformed. For example, the unit/sig
form can access a
named interface description with
:
syntax-local-value
(syntax-local-value
id-stx
[failure-thunk
])
returns the
expansion-time value of id-stx
in the transformed expression's
context. If id-stx
is not bound to an expansion-time value (via
define-syntax
, let-syntax
, etc.) in the environment
of the expression being transformed, the result is obtained by
applying failure-thunk
. If failure-thunk
is not provided,
the exn:application:mismatch
exception is raised.
(syntax-local-name
)
returns an inferred name for the
expression position being transformed, or #f
; see also
section 6.2.4.
(syntax-local-context
)
returns either 'expression
,
'top-level
, 'internal-define
, or 'module
,
indicating whether the expression is being expanded for a
(non-definition) expression position, a top-level position, a
(potential) internal-definition position, or a module top-level
position, respectively.
A transformer can also expand or partially expand subexpressions from its input syntax object:
(local-expand
stx context-symbol stop-id-stx-list
)
expands
stx
in the lexical context of the expression currently being
expanded. The context-symbol
argument is used as the result of
syntax-local-context
for immediate expansions; it must be
one of the legal return values for
syntax-local-context
. When an identifier in
stop-id-stx-list
is encountered by the expander in a
subexpression, expansions stops for the subexpression.
If #%app
, #%top
, or #%datum
(see
section 12.5) appears in stop-id-stx-list
, then
application, top-level variable reference, and literal data
expressions without the respective explicit form are not wrapped with
the explicit form.
To track the introduction of identifiers by a macro (see
section 12.3), the syntax expander adds a special ``mark'' to a
syntax object that is provided to a transformer, and also marks the
result of the transformer. Double marks cancel, and each transformer
application has a distinct mark, so the only parts of the resulting
syntax object with marks are the parts that were introduced by the
transformer. A transformer can explicitly add a current mark to a
syntax object using syntax-local-introduce
:
(syntax-local-introduce
stx
)
produces a syntax object that
is like stx
, except that a mark for the current expansion is
added (possibly canceling an existing mark in parts of stx
).
Explicit marking is useful on syntax objects that flow into or out of
a transformer without being the transformer argument or result. For
example, DrScheme's Check Syntax tool recognizes a
'bound-in-source
property that specifies bound-binding
identifier pairs in the source program that do not appear as bound
and binding identifiers in the expansion. Example:
(define-syntax (match-list stx) (syntax-case stx () [(_ expr (id ...) result-id) (let ([ids (syntax->list
(syntax (id ...)))] [result-id (syntax result-id)]) ;; Make sure the expression is well formed: (for-each
(lambda (id) (unless (identifier?
id) (raise-syntax-error
#f "not an identifier" stx id))) (append
ids (list
result-id))) ;; Find the matching variable and produce aexpression: (let loop ([ids ids] [pos 0]) (cond [(
list-ref
null?
ids) (raise-syntax-error
#f "no pattern binding" stx result-id)] [(bound-identifier=?
(car
ids) result-id) ;; Found it; produce theexpression, and ;; tell the Check Syntax tool about the pattern-variable binding: (with-syntax ([pos pos]) (
list-ref
syntax-property
(syntax (list-ref
expr pos)) ; the expansion result 'bound-in-source (cons
(syntax-local-introduce
(car
ids)) (syntax-local-introduce
result-id))))] [else (loop (cdr
ids) (add1
pos))])))])) ;; Test it: (match-list '(1 2 3) (a b c) b) ; =>2
In this example, Check Syntax will draw a binding arrow from the first
b
to the second b
. Without the calls to
syntax-local-introduce
, the identifiers stored in the
property would appear to have originated from the transformer,
instead of from the transformer's argument; consequently, Check
Syntax would not draw the arrow, because it would not know that the
b
s exist in the source program.
(expand
stx-or-sexpr
)
expands all non-primitive syntax in
stx-or-sexpr
, and returns a syntax object for the expanded
expression. See below for the grammar of fully expanded
expressions. Use
to convert the
returned syntax object into an S-expression.syntax-object->datum
(expand-once
stx-or-sexpr
)
partially expands syntax in the
stx-or-sexpr
and returns a syntax object for the
partially-expanded expression. Due to limitations in the expansion
mechanism, some context information may be lost. In particular,
calling
on the result may produce a result that
is different from expansion via expand-once
.expand
(expand-to-top-form
stx-or-sexpr
)
partially expands syntax in
stx-or-sexpr
to reveal the outermost syntactic form. This
partial expansion is mainly useful for detecting top-level uses of
begin
. Unlike expanding the result of
,
expanding the result of expand-once
with
expand-to-top-form
produces the same result as using expand
on
the original syntax.expand
The possible shapes of a fully expanded expression are defined by
top-level-expr
:
top-level-expr is one of general-top-level-expr (module identifier name (#%plain-module-begin module-level-expr ···)) (begin top-level-expr ···) module-level-expr is one of general-top-level-expr (provide provide-spec ...) (begin module-level-expr ···) general-top-level-expr is one of expr (define-values (variable ···) expr) (define-syntaxes (variable ···) expr) (require require-spec ···) (require-for-syntax require-spec ···) expr is one of variable (lambda formals expr ···1) (case-lambda (formals expr ···1) ···) (if expr expr) (if expr expr expr) (begin expr ···1) (begin0 expr expr ···) (let-values (((variable ···) expr) ···) expr ···1) (letrec-values (((variable ···) expr) ···) expr ···1) (set! variable expr) (quote datum) (quote-syntax datum) (with-continuation-mark expr expr expr) (#%app expr ···1) (#%datum . datum) (#%top . variable)
where formals
is defined in section 2.9, and
require-spec
and provide-spec
are defined in
section 5.2.
When a variable
expression appears in a fully-expanded
expression, it either refers to a variable bound by lambda
,
case-lambda
, let-values
, or letrec-values
,
or it refers to an imported variable. (In other words, a
variable
not wrapped by #%top
never refers to a
top-level variable, and it never refers to a non-imported variable
that is defined at the top-level of a module.)
The keywords in the above grammar are placeholders for identifiers
that are
(or
module-identifier=?
for module-transformer-identifier=?
define-syntax
expressions) to the same-named exports of
. Due to
import renamings, the printed identifier names can be different in
the expanded expression.mzscheme
Every syntax object has an associated property list, which can be
queried or extended with syntax-property
:
(syntax-property
stx key-v v
)
extends stx
by
associating an arbitrary property value v
with the key
key-v
; the result is a new syntax object with the association
(while stx
itself is unchanged).
(syntax-property
stx key-v
)
returns an arbitrary property
value associated to stx
with the key key-v
, or
#f
if no value is associated to stx
for key-v
.
Both the syntax input to a transformer and the syntax result of a
transformer may have associated properties. The two sets of
properties are merged by the syntax expander: each property in the
original and not present in the result is copied to the result, and
the values of properties present in both are combined with
(result value first, original value second).cons-immutable
Before performing the merge, however, the syntax expander
automatically adjusts a property on the original syntax object using
the key 'origin
. If the source syntax has no
'origin
property, it is set to the empty list. Then, still
before the merge, the identifier that triggered the macro expansion
(as syntax) is
d onto the cons-immutable
'origin
property so far.
The 'origin
property thus records (in reverse order) the
sequence of macro expansions that produced an expanded
expression. Usually, the 'origin
value is an immutable list
of identifiers. However, a transformer might return syntax that has
already been expanded, in which case an 'origin
list can
contain other lists after a merge.
For example, the expression
(or x y)
expands to
(let ((or-part x)) (if or-part or-part (or y)))
which, in turn, expands to
(let-values ([(or-part) x]) (if or-part or-part y))
The syntax object for the final expression will have an
'origin
property whose value is (
.list-immutable
(quote-syntax let) (quote-syntax or))
When read-syntax
generates a syntax object, it attaches a
property to the object (using a private key) to mark the object as
originating from a read. The
predicate
looks for the property to recognize such syntax objects.syntax-original?
See section 12.6.4 for information about properties generated by the expansion of a module declaration. See section 3.10.1 and section 6.2.4 for information about properties recognized when compiling a procedure. See section 14.6 for information on properties and byte codes.
The define-struct
form (see section 4.1) binds
the name of a structure type to an expansion-time value that records
the identifiers bound to the structure type, the constructor
procedure, the predicate procedure, and the field accessor and
mutator procedures. This information can be used during the expansion
of other expressions.
For example, the define-struct
variant for subtypes (see
section 4.2) uses the base type name t
to find the
variable struct
containing the base type's
descriptor; it also folds the field accessor and mutator information
for the base type into the information for the subtype. The
:t
match
form (see Chapter 19
in PLT MzLib: Libraries Manual) uses a type name to
find the predicates and field accessors for the structure type.
Besides using the information, other syntactic forms can even generate
information with the same shape. For example, the struct
form in an imported signature for unit/sig
(see
Chapter 35
in PLT MzLib: Libraries Manual) causes the unit/sig
transformer
to generate information about imported structure types, so that
match
and subtyping define-struct
expressions work
within the unit.
The expansion-time information for a structure type is represented as an immutable list of five items:
an identifier that is bound to the structure type's descriptor,
or #f
it none is known;
an identifier that is bound to the structure type's constructor,
or #f
it none is known;
an identifier that is bound to the structure type's predicate,
or #f
it none is known;
an immutable list of identifiers bound to the field accessors
of the structure type, optionally with #f
as the list's last
element. A #f
as the last element indicates that the
structure type may have additional fields, otherwise the list is a
reliable indicator of the number of fields in the structure
type. Furthermore, the accessors are listed in reverse order for the
corresponding constructor arguments. (The reverse order enables
sharing in the lists for a subtype and its base type.)
an immutable list of identifiers bound to the field mutators of
the structure type, optionally with #f
as the list's last
element. The meaning of #f
and the order are the same as for
the accessor identifiers.
The implementor of a syntactic form can expect users of the form to
know what kind of information is available about a structure
type. For example, the match
implementation works with
structure information containing an incomplete set of accessor
bindings, because the user is assumed to know what information is
available in the context of the match
expression. In
particular, the match
expression can appear in a
unit/sig
form with an imported structure type, in which case
the user is expected to know the set of fields that are listed in the
signature for the structure type.
MzScheme provides an interface for obtaining information about a expanded or compiled module declaration's imports and exports. This information is intended for use by tools such as a compilation manager. The information usually identifies modules through a module path index, which is a semi-interned31 opaque value that encodes a relative module path (see section 5.4) and another index to which it is relative.
Where an index is expected, a symbol can usually take its place,
representing a literal module name. A symbol is used instead of
an index when a module is imported using its name directly with
require
instead of a module path.
An index that returns #f
for its path and base index
represents ``self'' -- i.e., the module declaration that was the
source of the index -- and such an index is always used as the root
for a chain of indices. For example, when extracting information
about an identifier's binding within a module, if the identifier is
bound by a definition within the same module, the identifier's source
module will be reported using the ``self'' index. If the identifier
is instead defined in a module that is imported via a module
path (as opposed to a literal module name), then the identifier's
source module will be reported using an index that contains the
require
d module path and the ``self'' index.
(module-path-index?
v
)
returns #t
if v
is a
module path index, #f
otherwise.
(module-path-index-split
module-path-index
)
returns two
values: a non-symbol S-expression representing a module path, and a
base index (to which the module path is relative), symbol, or
#f
. A #f
second result means ``relative to a
top-level environment''. A #f
for the first result implies a
#f
for the second result, and means that
module-path-index
represents ``self'' (see above).
(module-path-index-join
module-path module-path-index
)
combines module-path
and module-path-index
to create a
new module path index. The module-path
argument can be anything
except a symbol, and the module-path-index
argument can be
a index, symbol, or #f
.
Information for an expanded module declaration is stored in a set of properties attached to the syntax object:
'module-direct-requires
-- an immutable list of
module path indices (or symbols) representing the modules explicitly
imported into the module.
'module-direct-for-syntax-requires
-- an
immutable list of module path indices (or symbols) representing the
modules explicitly for-syntax imported into the module.
'module-variable-provides
-- an immutable list of
provided items, where each item is one of the following:
symbol
-- represents a locally defined variable that
is provided with its defined name.
(cons-immutable
-- represents a locally defined variable
that is provided with renaming; the first symbol is the exported
name, and the second symbol is the defined name.provided-symbol
defined-symbol
)
(list*-immutable
-- represents a
re-exported and possibly re-named variable from the specified
module; module-path-index
provided-symbol
defined-symbol
)module-path-index
is either an index or symbol,
indicating the source module for the binding. The
provided-symbol
is the external name for the re-export, and
defined-symbol
is the originally defined name in the module
specified by module-path-index
.
'module-syntax-provides
-- like
'module-variable-provides
, but for syntax exports instead of
variable exports.
'module-indirect-provides
-- an immutable list of
symbols for variables that are defined in the module but not
exported; they may be exported indirectly through macro expansions.
'module-kernel-reprovide-hint
-- either
#f
, #t
, or a symbol. If it is #t
, then the
module re-exports all of the functionality from MzScheme's internal
kernel module. If it is a symbol, then all kernel exports but the
indicated one is re-exported, and some other export is provided with
the indicated name. This ad hoc information is used in an
optimization by the mzc compiler.
'module-self-path-index
-- a module path index
whose parts are both #f
. This information is used by the
mzc compiler to manage syntax objects (which contain
module-relative information keyed on the module's own index).
(compiled-module-expression?
v
)
returns #t
if v
is
a compiled expression for a module
declaration, #f
otherwise. See also section 14.6.
(module-compiled-name
compiled-module-code
)
takes a module
declaration in compiled form (see section 14.6) and returns a
symbol for the module's declared name.
(module-compiled-imports
compiled-module-code
)
takes a module
declaration in compiled form (see section 14.6) and returns
two values: an immutable list of module path indices (and symbols)
for the module's explicit imports, and an immutable list of module
path indices (and symbols) for the module's explicit for-syntax
imports.
28 In general, modules and for-syntax imports create a hierarchy of run times and expansion times. See section 12.3.4 for more information.
29 In this
particular case, Shriram Krishnamurthi points out changing if-it
to use
(datum->syntax-object (
solves the problem in a sensible way.syntax
test
) 'it
)
30 In contrast, a namespace
created by (scheme-report-environment 5)
imports only
syntax-rules
into the transformer environment.
31 Multiple references to the same relative module tend to use the same index value, but not always.