racket/pkgs/racket-doc/scribblings/reference/syntax-model.scrbl
Jason Hemann e13f68f332 'body' v. 'body definition' of a begin-for-syntax
As written the prose describes a 'body definition'; I can't find that phrase used elsewhere. I suspect it may be an artifact of an earlier rewrite. If correct as written, that term wants a definition or a pointer.
2021-05-30 11:05:47 -04:00

1278 lines
58 KiB
Racket
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#lang scribble/doc
@(require scribble/struct "mz.rkt" (for-syntax mzscheme))
@(define racket-eval (make-base-eval))
@examples[#:hidden #:eval racket-eval (require (for-syntax racket/base))]
@;------------------------------------------------------------------------
@title[#:tag "syntax-model"]{Syntax Model}
The syntax of a Racket program is defined by
@itemize[
@item{a @deftech{read} pass that processes a character stream into a
@tech{syntax object}; and}
@item{an @deftech{expand} pass that processes a syntax object to
produce one that is fully parsed.}
]
For details on the @tech{read} pass, see @secref["reader"]. Source
code is normally read in @racket[read-syntax] mode, which produces a
@tech{syntax object}.
The @tech{expand} pass recursively processes a @tech{syntax object}
to produce a complete @tech{parse} of the program. @tech{Binding}
information in a @tech{syntax object} drives the @tech{expansion}
process, and when the @tech{expansion} process encounters a
@tech{binding} form, it extends syntax objects for sub-expressions with
new binding information.
@;------------------------------------------------------------------------
@section[#:tag "id-model"]{Identifiers, Binding, and Scopes}
@guideintro["binding"]{binding}
An @deftech{identifier} is a source-program entity. Parsing (i.e.,
expanding) a Racket program reveals that some @tech{identifiers}
correspond to @tech{variables}, some refer to @tech{syntactic forms}
(such as @racket[lambda], which is the @tech{syntactic form} for
functions), some refer to @tech{transformers} for macro expansion, and
some are quoted to produce @tech{symbols} or @tech{syntax objects}. An
identifier @deftech{binds} another (i.e., it is a @deftech{binding})
when the former is parsed as a @tech{variable} or syntactic form and
the latter is parsed as a @deftech{reference} to the former; the
latter is @deftech{bound}.
For example, as a fragment of source, the text
@racketblock[(let ([x 5]) x)]
includes two @tech{identifiers}: @racket[let] and @racket[x] (which
appears twice). When this source is parsed in a context where
@racket[let] has its usual meaning, the first @racket[x] @tech{binds}
the second @racket[x].
Bindings and references are determined through @tech{scope sets}. A
@deftech{scope} corresponds to a region of the program that is either
in part of the source or synthesized through elaboration of the
source. Nested binding contexts (such as nested functions) create
nested @tech{scopes}, while macro expansion creates scopes that
overlap in more complex ways. Conceptually, each @tech{scope} is
represented by a unique token, but the token is not directly
accessible. Instead, each @tech{scope} is represented by a value that
is internal to the representation of a program.
A @deftech{form} is a fragment of a program, such as an identifier or
a function call. A @tech{form} is represented as a @tech{syntax
object}, and each syntax object has an associated set of @tech{scopes}
(i.e., a @deftech{scope set}). In the above example,
the representations of the @racket[x]s include the @tech{scope} that
corresponds to the @racket[let] form.
When a @tech{form} parses as the binding of a particular identifier,
parsing updates a global table that maps a combination of an
identifier's @tech{symbol} and @tech{scope set} to its meaning: a
@tech{variable}, a @tech{syntactic form}, or a @tech{transformer}. An
identifier refers to a particular binding when the reference's symbol
and the identifier's symbol are the same, and when the reference's
@tech{scope set} is a superset of the binding's
@tech{scope set}. For a given identifier, multiple bindings may have
@tech{scope sets} that are subsets of the identifier's; in that case,
the identifier refers to the binding whose set is a superset of all
others; if no such binding exists, the reference is ambiguous (and triggers a syntax
error if it is parsed as an expression). A binding @deftech{shadows}
any @tech{binding} (i.e., it is @deftech{shadowing} any @tech{binding})
with the same symbol but a subset of scopes.
For example, in
@racketblock[(let ([x 5]) x)]
in a context where @racket[let] corresponds to the usual
@tech{syntactic form}, the parsing of @racket[let] introduces a new
scope for the binding of @racket[x]. Since the second @racket[x]
receives that scope as part of the @racket[let] body, the first
@racket[x] @tech{binds} the second @racket[x]. In the more complex
case
@racketblock[(let ([x 5])
(let ([x 6])
x))]
the inner @racket[let] creates a second scope for the second
@racket[x], so its @tech{scope set} is a superset of the first
@racket[x]'s @tech{scope set}---which means that the binding for the
second @racket[x] @tech{shadows} the one for the first @racket[x], and
the third @racket[x] refers to the binding created by the second one.
A @deftech{top-level binding} is a @tech{binding} from a definition at
the top-level; a @deftech{module binding} is a binding from a
definition in a module; all other bindings are @deftech{local
bindings}. Within a module, references to @tech{top-level bindings}
are disallowed. An identifier without a binding is @deftech{unbound}.
Throughout the documentation, @tech{identifiers} are typeset to
suggest the way that they are parsed. A hyperlinked identifier
like @racket[lambda] indicates a reference to a syntactic form or
variable. A plain identifier like @racketidfont{x} is a
@tech{variable} or a reference to an unspecified @tech{top-level
variable}.
Every binding has a @deftech{phase level} in which it can be
referenced, where a @tech{phase level} normally corresponds to an
integer (but the special @tech{label phase level} does not
correspond to an integer). @tech{Phase level} 0 corresponds to the
run time of the enclosing module (or the run time of top-level
expressions). Bindings in @tech{phase level} 0 constitute the
@deftech{base environment}. @tech{Phase level} 1 corresponds to the
time during which the enclosing module (or top-level expression) is
expanded; bindings in @tech{phase level} 1 constitute the
@deftech{transformer environment}. Phase level -1 corresponds to the
run time of a different module for which the enclosing module is
imported for use at @tech{phase level} 1 (relative to the importing
module); bindings in @tech{phase level} -1 constitute the
@deftech{template environment}. The @deftech{label phase level} does not
correspond to any execution time; it is used to track bindings (e.g.,
to identifiers within documentation) without implying an execution
dependency.
An identifier can have different bindings in different @tech{phase
levels}. More precisely, the @tech{scope set} associated with a
@tech{form} can be different at different phase levels; a top-level or
module context implies a distinct scope at every phase level, while
scopes from macro expansion or other syntactic forms are added to a
form's @tech{scope sets} at all phases. The context of each binding
and reference determines the @tech{phase level} whose @tech{scope set} is
relevant.
@history[#:changed "6.3" @elem{Changed local bindings to have a
specific phase level, like top-level
and module bindings.}]
@;------------------------------------------------------------------------
@section[#:tag "stxobj-model"]{Syntax Objects}
A @deftech{syntax object} combines a simpler Racket value, such as a symbol or pair, with
@tech{lexical information}, @tech{source-location} information, @tech{syntax properties}, and @tech{tamper
status}. The @deftech{lexical information} of a @tech{syntax object} comprises a set of @tech{scope
sets}, one for each @tech{phase level}. In particular, an @tech{identifier} is represented as a syntax
object containing a @tech{symbol}, and its @tech{lexical information} can be combined with the global
table of bindings to determine its @tech{binding} (if any) at each @tech{phase level}.
For example, a @racketidfont{car} @tech{identifier} might have
@tech{lexical information} that designates it as the @racket[car] from
the @racketmodname[racket/base] language (i.e., the built-in
@racket[car]). Similarly, a @racketidfont{lambda} identifier's
@tech{lexical information} may indicate that it represents a procedure
form. Some other @tech{identifier}'s @tech{lexical information} may
indicate that it references a @tech{top-level variable}.
When a @tech{syntax object} represents a more complex expression than
an @tech{identifier} or simple constant, its internal components can
be extracted. Even for extracted identifiers, detailed information
about binding is available mostly indirectly; two identifiers can be
compared to determine whether they refer to the same binding (i.e.,
@racket[free-identifier=?]), or whether the identifiers have the same
@tech{scope set} so that each identifier would bind the
other if one were in a binding position and the other in an expression
position (i.e., @racket[bound-identifier=?]).
For example, when the program written as
@racketblock[(let ([x 5]) (+ x 6))]
is represented as a @tech{syntax object}, then two @tech{syntax
objects} can be extracted for the two @racket[x]s. Both the
@racket[free-identifier=?] and @racket[bound-identifier=?] predicates
will indicate that the @racket[x]s are the same. In contrast, the
@racket[let] @tech{identifier} is not @racket[free-identifier=?] or
@racket[bound-identifier=?] to either @racket[x].
The @tech{lexical information} in a @tech{syntax object} is
independent of the rest of the @tech{syntax object}, and it can be copied to a new syntax
object in combination with an arbitrary other Racket value. Thus,
identifier-@tech{binding} information in a @tech{syntax object} is
predicated on the symbolic name of the @tech{identifier} as well as
the identifier's @tech{lexical information}; the same question with
the same @tech{lexical information} but different base value can
produce a different answer.
For example, combining the lexical information from @racket[let] in
the program above to @racket['x] would not produce an identifier that
is @racket[free-identifier=?] to either @racket[x], since it does not
appear in the scope of the @racket[x] binding. Combining the lexical
context of the @racket[6] with @racket['x], in contrast, would produce
an identifier that is @racket[bound-identifier=?] to both @racket[x]s.
The @racket[quote-syntax] form bridges the evaluation of a program and
the representation of a program. Specifically, @racket[(quote-syntax
_datum #:local)] produces a syntax object that preserves all of the
lexical information that @racket[_datum] had when it was parsed as
part of the @racket[quote-syntax] form. Note that the
@racket[(quote-syntax _datum)] form is similar, but it removes certain
@tech{scopes} from the @racket[_datum]'s @tech{scope sets};
see @racket[quote-syntax] for more information.
@;------------------------------------------------------------------------
@section[#:tag "expansion"]{Expansion@aux-elem{ (Parsing)}}
@deftech{Expansion} recursively processes a @tech{syntax object} in a
particular phase level, starting with @tech{phase level} 0. @tech{Bindings}
from the @tech{syntax object}'s @tech{lexical information} drive the
expansion process, and cause new bindings to be introduced for the
lexical information of sub-expressions. In some cases, a
sub-expression is expanded in a phase deeper (having a
bigger phase level number) than the enclosing expression.
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@subsection[#:tag "fully-expanded"]{Fully Expanded Programs}
A complete expansion produces a @tech{syntax object} matching the
following grammar:
@margin-note{Beware that the symbolic names of identifiers in a fully
expanded program may not match the symbolic names in the grammar. Only
the binding (according to @racket[free-identifier=?]) matters.}
@racketgrammar*[
#:literals (#%expression module module* #%plain-module-begin begin #%provide
define-values define-syntaxes begin-for-syntax
#%require #%declare
#%plain-lambda case-lambda if begin begin0 let-values letrec-values
set! quote-syntax quote with-continuation-mark
#%plain-app #%top #%variable-reference)
[top-level-form general-top-level-form
(#%expression expr)
(module id module-path
(#%plain-module-begin
module-level-form ...))
(begin top-level-form ...)
(begin-for-syntax top-level-form ...)]
[module-level-form general-top-level-form
(#%provide raw-provide-spec ...)
(begin-for-syntax module-level-form ...)
submodule-form
(#%declare declaration-keyword ...)]
[submodule-form (module id module-path
(#%plain-module-begin
module-level-form ...))
(module* id module-path
(#%plain-module-begin
module-level-form ...))
(module* id #f
(#%plain-module-begin
module-level-form ...))]
[general-top-level-form expr
(define-values (id ...) expr)
(define-syntaxes (id ...) expr)
(#%require raw-require-spec ...)]
[expr id
(#%plain-lambda formals expr ...+)
(case-lambda (formals expr ...+) ...)
(if expr expr expr)
(begin expr ...+)
(begin0 expr expr ...)
(let-values ([(id ...) expr] ...)
expr ...+)
(letrec-values ([(id ...) expr] ...)
expr ...+)
(set! id expr)
(@#,racket[quote] datum)
(quote-syntax datum)
(quote-syntax datum #:local)
(with-continuation-mark expr expr expr)
(#%plain-app expr ...+)
(#%top . id)
(#%variable-reference id)
(#%variable-reference (#%top . id))
(#%variable-reference)]
[formals (id ...)
(id ...+ . id)
id]]
A @deftech{fully-expanded} @tech{syntax object} corresponds to a @deftech{parse}
of a program (i.e., a @deftech{parsed} program), and @tech{lexical
information} on its @tech{identifiers} indicates the
@tech{parse}.
More specifically, the typesetting of identifiers in the above grammar
is significant. For example, the second case for @racket[_expr] is a
@tech{syntax-object} list whose first element is an @tech{identifier},
where the @tech{identifier}'s @tech{lexical information} specifies a
binding to the @racket[#%plain-lambda] of the
@racketmodname[racket/base] language (i.e., the @tech{identifier} is
@racket[free-identifier=?] to one whose binding is
@racket[#%plain-lambda]). In all cases, identifiers above typeset as
syntactic-form names refer to the bindings defined in
@secref["syntax"].
In a fully expanded program for a namespace whose @tech{base phase} is
0, the relevant @tech{phase level} for a binding in the program is
@math{N} if the binding has @math{N} surrounding
@racket[begin-for-syntax] and/or @racket[define-syntaxes] forms---not
counting any @racket[begin-for-syntax] forms that wrap a
@racket[module] or @racket[module*] form for the body of the @racket[module]
or @racket[module*], unless a @racket[module*] form has @racket[#f] in place
of a @racket[_module-path] after the @racket[_id]. The
@racket[_datum] in a @racket[quote-syntax] form
preserves its information for all @tech{phase level}s.
A reference to a @tech{local binding} in a fully expanded program has
a @tech{scope set} that matches its binding identifier exactly.
Additional @tech{scopes}, if any, are removed. As a result,
@racket[bound-identifier=?] can be used to correlate local binding
identifiers with reference identifiers, while
@racket[free-identifier=?] must be used to relate references to
@tech{module bindings} or @tech{top-level bindings}.
In addition to the grammar above, @racket[#%expression] can appear in
a fully local-expanded expression position. For example,
@racket[#%expression] can appear in the result from
@racket[local-expand] when the stop list is empty.
Reference-identifier @tech{scope sets} are reduced in local-expanded
expressions only when the @racket[local-expand] stop list is empty.
@history[#:changed "6.3" @elem{Added the @racket[#:local] variant of
@racket[quote-syntax]; removed
@racket[letrec-syntaxes+values] from
possibly appearing in a fully
local-expanded form.}]
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@subsection[#:tag "expand-steps"]{Expansion Steps}
In a recursive expansion, each single step in expanding a @tech{syntax
object} at a particular @tech{phase level} depends on the immediate shape of
the @tech{syntax object} being expanded:
@itemize[
@item{If it is an @tech{identifier} (i.e., a syntax-object symbol),
then a @tech{binding} is determined by the @tech{identifier}'s
@tech{lexical information}. If the @tech{identifier} has a
@tech{binding}, that @tech{binding} is used to continue. If the @tech{identifier}
is @tech{unbound}, a new @tech{syntax-object} symbol
@racket['#%top] is created using the @tech{lexical information}
of the @tech{identifier} with @tech{implicit-made-explicit properties};
if this @racketidfont{#%top}
@tech{identifier} has no @tech{binding}, then parsing fails with an
@racket[exn:fail:syntax] exception. Otherwise, the new
@tech{identifier} is combined with the original
@tech{identifier} in a new @tech{syntax-object} pair (also
using the same @tech{lexical information} as the original
@tech{identifier}), and the @racketidfont{#%top} @tech{binding}
is used to continue.
@history[#:changed "6.3" @elem{Changed the introduction of
@racket[#%top] in a top-level context
to @tech{unbound} identifiers only.}]}
@item{If it is a @tech{syntax-object} pair whose first element is an
@tech{identifier}, and if the @tech{identifier} has a
@tech{binding} other than as a @tech{top-level variable}, then
the @tech{identifier}'s @tech{binding} is used to continue.}
@item{If it is a @tech{syntax-object} pair of any other form, then a
new @tech{syntax-object} symbol @racket['#%app] is created
using the @tech{lexical information} of the pair with
@tech{implicit-made-explicit properties}. If the
resulting @racketidfont{#%app} @tech{identifier} has no
binding, parsing fails with an @racket[exn:fail:syntax]
exception. Otherwise, the new @tech{identifier} is combined
with the original pair to form a new @tech{syntax-object} pair
(also using the same @tech{lexical information} as the original
pair), and the @racketidfont{#%app} @tech{binding} is used to
continue.}
@item{If it is any other syntax object, then a new
@tech{syntax-object} symbol @racket['#%datum] is created using
the @tech{lexical information} of the original @tech{syntax
object} with @tech{implicit-made-explicit properties}. If the resulting @racketidfont{#%datum}
@tech{identifier} has no @tech{binding}, parsing fails with an
@racket[exn:fail:syntax] exception. Otherwise, the new
@tech{identifier} is combined with the original @tech{syntax
object} in a new @tech{syntax-object} pair (using the same
@tech{lexical information} as the original pair), and the
@racketidfont{#%datum} @tech{binding} is used to continue.}
]
Thus, the possibilities that do not fail lead to an @tech{identifier}
with a particular @tech{binding}. This binding refers to one of three
things:
@itemize[
@item{A @deftech{transformer}, such as introduced by
@racket[define-syntax] or @racket[let-syntax]. If the
associated value is a procedure of one argument, the procedure
is called as a @tech{syntax transformer} (described below), and
parsing starts again with the @tech{syntax-object} result. If
the @tech{transformer} binding is to any other kind of value,
parsing fails with an @racket[exn:fail:syntax] exception. The
call to the @tech{syntax transformer} is @racket[parameterize]d
to set @racket[current-namespace] to a @tech{namespace} that
shares @tech{bindings} and @tech{variables} with the namespace
being used to expand, except that its @tech{base phase} is one
greater.}
@item{A @tech{variable} @tech{binding}, such as introduced by a
module-level @racket[define] or by @racket[let]. In this case,
if the form being parsed is just an @tech{identifier}, then it
is parsed as a reference to the corresponding
@tech{variable}. If the form being parsed is a
@tech{syntax-object} pair, then an @racket[#%app] is added to
the front of the @tech{syntax-object} pair in the same way as
when the first item in the @tech{syntax-object} pair is not an
identifier (third case in the previous enumeration), and
parsing continues.}
@item{A core @deftech{syntactic form}, which is parsed as described
for each form in @secref["syntax"]. Parsing a core syntactic
form typically involves recursive parsing of sub-forms, and may
introduce @tech{bindings} that determine the parsing of
sub-forms.}
]
When a @racketidfont{#%top}, @racketidfont{#%app}, or
@racketidfont{#%datum} identifier is added by the expander, it is
given @deftech{implicit-made-explicit properties}: an
@racket['implicit-made-explicit] @tech{syntax property} whose value is
@racket[#t], and a hidden property to indicate that the implicit
identifier is original in the sense of @racket[syntax-original?] if
the syntax object that gives the identifier its @tech{lexical information}
has that property.
@history[#:changed "7.9.0.13" @elem{Added @tech{implicit-made-explicit
properties}.}]
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@subsection[#:tag "expand-context-model"]{Expansion Context}
Each expansion step occurs in a particular @deftech{context}, and
transformers and core syntactic forms may expand differently for
different @tech{contexts}. For example, a @racket[module] form is
allowed only in a @tech{top-level context}, and it fails in other
contexts. The possible @tech{contexts} are as follows:
@itemize[
@item{@deftech{top-level context} : outside of any module, definition, or
expression, except that sub-expressions of a top-level
@racket[begin] form are also expanded as top-level forms.}
@item{@deftech{module-begin context} : inside the body of a module, as the
only form within the module.}
@item{@deftech{module context} : in the body of a module (inside the
module-begin layer).}
@item{@deftech{internal-definition context} : in a nested context that allows
both definitions and expressions.}
@item{@deftech{expression context} : in a context where only
expressions are allowed.}
]
Different core @tech{syntactic forms} parse sub-forms using different
@tech{contexts}. For example, a @racket[let] form always parses the
right-hand expressions of a binding in an @tech{expression context},
but it starts parsing the body in an @tech{internal-definition
context}.
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@subsection[#:tag "intro-binding"]{Introducing Bindings}
@tech{Bindings} are introduced during @tech{expansion} when certain
core syntactic forms are encountered:
@itemize[
@item{When a @racket[require] form is encountered at the top level or
module level, each symbol specified by the form is paired with the
@tech{scope set} of the specification to introduce new bindings.
If not otherwise indicated in the
@racket[require] form, bindings are introduced at the
@tech{phase level}s specified by the exporting modules:
@tech{phase level} 0 for each normal @racket[provide],
@tech{phase level} 1 for each @racket[for-syntax]
@racket[provide], and so on. The @racket[for-meta]
@racket[provide] form allows exports at an arbitrary
@tech{phase level} (as long as a binding exists within the
module at the @tech{phase level}).
A @racket[for-syntax] sub-form within @racket[require] imports
similarly, but the resulting bindings have a @tech{phase level}
that is one more than the exported @tech{phase levels}, when
exports for the @tech{label phase level} are still imported at
the @tech{label phase level}. More generally, a
@racket[for-meta] sub-form within @racket[require] imports with
the specified @tech{phase level} shift; if the specified shift
is @racket[#f], or if @racket[for-label] is used to import,
then all bindings are imported into the @tech{label phase
level}.}
@item{When a @racket[define], @racket[define-values],
@racket[define-syntax], or @racket[define-syntaxes] form is
encountered at the top level or module level, a binding is
added to @tech{phase level} 0 (i.e., the @tech{base environment}
is extended) for each defined identifier.}
@item{When a @racket[begin-for-syntax] form is encountered at the top
level or module level, bindings are introduced as for
@racket[define-values] and @racket[define-syntaxes], but at
@tech{phase level} 1 (i.e., the @tech{transformer environment}
is extended). More generally, @racket[begin-for-syntax] forms
can be nested, and each @racket[begin-for-syntax] shifts its
body by one @tech{phase level}.}
@item{When a @racket[let-values] form is encountered, the body of the
@racket[let-values] form is extended (by creating new
@tech{syntax objects}) with a fresh @tech{scope}. The @tech{scope} is added to the identifiers
themselves, so that the identifiers in binding position are
@racket[bound-identifier=?] to uses in the fully expanded form,
and so they are not @racket[bound-identifier=?] to other
identifiers. The new bindings are at the
@tech{phase level} at which the @racket[let-values] form is
expanded.}
@item{When a @racket[letrec-values] or
@racket[letrec-syntaxes+values] form is encountered, bindings
are added as for @racket[let-values], except that the
right-hand-side expressions are also extended with the
new @tech{scope}.}
@item{Definitions in @tech{internal-definition contexts} introduce
new scopes and bindings as described in @secref["intdef-body"].}
]
For example, in
@racketblock[
(let-values ([(x) 10]) (+ x y))
]
the binding introduced for @racket[x] applies to the @racket[x] in the
body, because a fresh @tech{scope} is created and added to both the binding
@racket[x] and reference @racket[x]. The same scope is added to the
@racket[y], but since it has a different symbol than the binding
@racket[x], it does not refer to the new binding. Any @racket[x]
outside of this @racket[let-values] form does not receive the fresh
@tech{scope} and therefore does not refer to the new binding.
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@subsection[#:tag "transformer-model"]{Transformer Bindings}
In a @tech{top-level context} or @tech{module context}, when the
expander encounters a @racket[define-syntaxes] form, the binding that
it introduces for the defined identifiers is a @tech{transformer}
binding. The @tech{value} of the @tech{binding} exists at expansion
time, rather than run time (though the two times can overlap), though
the binding itself is introduced with @tech{phase level} 0 (i.e., in
the @tech{base environment}).
The @tech{value} for the binding is obtained by evaluating the
expression in the @racket[define-syntaxes] form. This expression must
be @tech{expand}ed (i.e., parsed) before it can be evaluated, and it is
expanded at @tech{phase level} 1 (i.e., in the @tech{transformer
environment}) instead of @tech{phase level} 0.
If the resulting @tech{value} is a procedure of one argument or
the result of @racket[make-set!-transformer] on a procedure, then it
is used as a @deftech{syntax transformer} (a.k.a. @deftech{macro}).
The procedure is expected to accept a syntax object and return a
syntax object. A use of the binding (at @tech{phase level} 0) triggers
a call of the @tech{syntax transformer} by the expander; see
@secref["expand-steps"].
Before the expander passes a @tech{syntax object} to a transformer,
the @tech{syntax object} is extended with a fresh @deftech{macro-introduction scope}
(that applies to all sub-@tech{syntax objects}) to distinguish @tech{syntax objects}
at the macro's use site from @tech{syntax objects} that are introduced by the macro;
in the result of the transformer the presence of the @tech{scope} is
flipped, so that introduced @tech{syntax objects} retain the @tech{scope},
and use-site @tech{syntax objects} do not have it. In addition, if
the use of a transformer is in the same definition context as its binding,
the use-site @tech{syntax object} is extended with an additional fresh
@deftech{use-site scope} that is not flipped in the transformer's result,
so that only use-site @tech{syntax objects} have the @tech{use-site scope}.
The @tech{scope}-introduction process for macro expansion helps keep
binding in an expanded program consistent with the lexical structure
of the source program. For example, the expanded form of the program
@racketblock[
(define x 12)
(define-syntax m
(syntax-rules ()
[(_ id) (let ([x 10]) id)]))
(m x)
]
is
@racketblock[
(define x 12)
(define-syntax m ....)
(let ([x 10]) x)
]
However, the result of the last expression is @racket[12], not
@racket[10]. The reason is that the transformer bound to @racket[m]
introduces the binding @racket[x], but the referencing @racket[x] is
present in the argument to the transformer. The introduced @racket[x]
is left with one fresh @tech{scope}, while the reference @racket[x] has a different fresh @tech{scope},
so the binding @racket[x] is not @racket[bound-identifier=?] to the
body @racket[x].
A @tech{use-site scope} on a binding identifier is ignored when the
definition is in the same context where the @tech{use-site scope} was
introduced. This special treatment of @tech{use-site scopes} allows a
macro to expand to a visible definition. For example, the expanded
form of the program
@racketblock[
(define-syntax m
(syntax-rules ()
[(_ id) (define id 5)]))
(m x)
x
]
is
@racketblock[
(define-syntax m ....)
(define x 5)
x
]
where the @racket[x] in the @racket[define] form has a @tech{use-site
scope} that is not present on the final @racket[x]. The final
@racket[x] nevertheless refers to the definition, because the
@tech{use-site scope} is effectively removed before installing the
definition's binding. In contrast, the expansion of
@racketblock[
(define-syntax m
(syntax-rules ()
[(_ id) (let ([x 4])
(let ([id 5])
x))]))
(m x)
]
is
@racketblock[
(define-syntax m ....)
(let ([x 4])
(let ([x 5])
x))
]
where the second @racket[x] has a @tech{use-site scope} that prevents
it from binding the final @racket[x]. The @tech{use-site scope} is not
ignored in this case, because the binding is not part of the definition
context where @racket[(m x)] was expanded.
The @racket[set!] form works with the @racket[make-set!-transformer]
and @racket[prop:set!-transformer] property to support
@deftech{assignment transformers} that transform @racket[set!]
expressions. An @tech{assignment transformer} contains a procedure
that is applied by @racket[set!] in the same way as a normal
transformer by the expander.
The @racket[make-rename-transformer] procedure or
@racket[prop:rename-transformer] property creates a value that is also
handled specially by the expander and by @racket[set!] as a
transformer binding's value. When @racket[_id] is bound to a
@deftech{rename transformer} produced by
@racket[make-rename-transformer], it is replaced with the target
identifier passed to @racket[make-rename-transformer]. In addition, as
long as the target identifier does not have a true value for the
@racket['not-free-identifier=?] @tech{syntax property}, the
binding table is extended to indicate that @racket[_id] is an alias
for the identifier in the @tech{rename transformer}. The
@racket[free-identifier=?] function follows aliasing chains to determine
equality of bindings, the @racket[identifier-binding] function
similarly follows aliasing chains, and the @racket[provide] form
exports @racket[_id] as the target identifier. Finally, the
@racket[syntax-local-value] function follows @tech{rename transformer}
chains even when binding aliases are not installed.
In addition to using scopes to track introduced identifiers, the
expander tracks the expansion history of a form through @tech{syntax
properties} such as @racket['origin]. See @secref["stxprops"] for
more information.
Finally, the expander uses a @tech{tamper status} to control the way
that unexported and protected @tech{module bindings} are used. See
@secref["stxcerts"] for more information on a @tech{tamper status}.
The expander's handling of @racket[letrec-syntaxes+values] is similar
to its handling of @racket[define-syntaxes]. A
@racket[letrec-syntaxes+values] can be expanded in an arbitrary phase
level @math{n} (not just 0), in which case the expression for the
@tech{transformer} binding is expanded at @tech{phase level} @math{n+1}.
The expressions in a @racket[begin-for-syntax] form are expanded and
evaluated in the same way as for @racket[define-syntaxes]. However,
any introduced bindings from definition within
@racket[begin-for-syntax] are at @tech{phase level} 1 (not a
@tech{transformer} binding at @tech{phase level} 0).
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@subsection[#:tag "local-binding-context"]{Local Binding Context}
Although the @tech{binding} of an @tech{identifier} can be uniquely determined from the combination of
its @tech{lexical information} and the global binding table, the expander also maintains a
@deftech{local binding context} that records additional information about @tech{local bindings} to
ensure they are not used outside of the lexical region in which they are bound.
Due to the way local binding forms like @racket[let] add a fresh @tech{scope} to both bound
@tech{identifiers} and body forms, it isnt ordinarily possible for an @tech{identifier} to reference
a @tech{local binding} without appearing in the body of the @racket[let]. However, if macros use
compile-time state to stash bound @tech{identifiers}, or use @racket[local-expand] to extract
@tech{identifiers} from an expanded binding form, they can violate this constraint. For example, the
following @racket[stash-id] and @racket[unstash-id] macros cooperate to move a reference to a
locally-bound @racket[x] @tech{identifier} outside of the lexical region in which it is bound:
@(examples
#:label #f
#:eval racket-eval
(begin-for-syntax
(define stashed-id #f))
(define-syntax (stash-id stx)
(syntax-case stx ()
[(_ id)
(begin
(set! stashed-id #'id)
#'(void))]))
(define-syntax (unstash-id stx)
stashed-id)
(let ([x 42])
(stash-id x)
(unstash-id))
(eval:error (unstash-id)))
In general, an @tech{identifier}s @tech{lexical information} is not sufficient to know whether or not
its @tech{binding} is available in the enclosing context, since the @tech{scope set} for the
@tech{identifier} stored in @racket[stashed-id] unambiguously refers to a binding in the global
binding table. This can be observed by the fact that @racket[identifier-binding] produces
@racket['lexical], not @racket[#f]:
@(examples
#:label #f
#:eval racket-eval
#:escape UNSYNTAX
(define-syntax (stashed-id-binding stx)
#`(quote #,(identifier-binding stashed-id)))
(eval:check (stashed-id-binding) 'lexical))
However, the reference produced by @racket[(unstash-id)] in the above program is still illegal, even
if it isnt technically unbound. To record the fact that @racket[x]s @tech{binding} is in scope only
within the body of its corresponding @racket[let] form, the expander adds @racket[x]s @tech{binding}
to the @tech{local binding context} while expanding the @racket[let] body. More generally, the
expander adds all @tech{local variable} @tech{bindings} to the @tech{local binding context} while
expanding expressions in which a reference to the @tech{variable} would be legal. When the expander
encounters an @tech{identifier} bound to a @tech{local variable}, and the associated @tech{binding} is
not in the current @tech{local binding context}, it raises a syntax error.
The @tech{local binding context} also tracks local @tech{transformer} @tech{bindings} (i.e. bindings
bound by forms like @racket[let-syntax]) in a similar way, except that the context also stores the
compile-time value associated with the @tech{transformer}. When an @tech{identifier} that is locally
bound as a @tech{transformer} is used in application position as a @tech{syntax transformer}, or its
compile-time value is looked up using @racket[syntax-local-value], the @tech{local binding context} is
consulted to retrieve the value. If the @tech{binding} is in scope, its associated compile-time value
is used; otherwise, the expander raises a syntax error.
@(examples
#:eval racket-eval
#:escape UNSYNTAX
(define-syntax (stashed-id-local-value stx)
#`(quote #,(syntax-local-value stashed-id)))
(let-syntax ([y 42])
(stash-id y)
(stashed-id-local-value))
(eval:error (stashed-id-local-value)))
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@subsection[#:tag "partial-expansion"]{Partial Expansion}
In certain contexts, such as an @tech{internal-definition context} or
@tech{module context}, @deftech{partial expansion} is used to determine
whether forms represent definitions, expressions, or other declaration
forms. Partial expansion works by cutting off the normal recursive
expansion when the relevant binding is for a primitive syntactic form.
As a special case, when expansion would otherwise add an
@racketidfont{#%app}, @racketidfont{#%datum}, or @racketidfont{#%top}
identifier to an expression, and when the binding turns out to be the
primitive @racket[#%app], @racket[#%datum], or @racket[#%top] form,
then expansion stops without adding the identifier.
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@subsection[#:tag "intdef-body"]{Internal Definitions}
An @tech{internal-definition context} supports local definitions mixed
with expressions. Forms that allow internal definitions document such
positions using the @racket[_body] meta-variable. Definitions in an
internal-definition context are equivalent to local binding via
@racket[letrec-syntaxes+values]; macro expansion converts internal
definitions to a @racket[letrec-syntaxes+values] form.
Expansion relies on @tech{partial expansion} of each @racket[_body] in
an internal-definition sequence. Partial expansion of each
@racket[_body] produces a form matching one of the following cases:
@itemize[
@item{A @racket[define-values] form: The binding table is immediately enriched
with bindings for the @racket[define-values] form. Further
expansion of the definition is deferred, and partial expansion
continues with the rest of the body.}
@item{A @racket[define-syntaxes] form: The right-hand side is
expanded and evaluated (as for a
@racket[letrec-syntaxes+values] form), and a transformer
binding is installed for the body sequence before partial
expansion continues with the rest of the body.}
@item{A primitive expression form other than @racket[begin]: Further
expansion of the expression is deferred, and partial expansion
continues with the rest of the body.}
@item{A @racket[begin] form: The sub-forms of the @racket[begin] are
spliced into the internal-definition sequence, and partial
expansion continues with the first of the newly-spliced forms
(or the next form, if the @racket[begin] had no sub-forms).}
]
After all body forms are partially expanded, if no definitions were
encountered, then the expressions are collected into a @racket[begin]
form as the internal-definition context's expansion. Otherwise, at
least one expression must appear after the last definition, and any
@racket[_expr] that appears between definitions is converted to
@racket[(define-values () (begin _expr (values)))]; the definitions
are then converted to bindings in a @racket[letrec-syntaxes+values]
form, and all expressions after the last definition become the body of
the @racket[letrec-syntaxes+values] form.
Before partial expansion begins, expansion of an internal-definition
context begins with the introduction of a fresh @deftech{outside-edge
scope} on the content of the internal-definition context. This
outside-edge scope effectively identifies syntax objects that are
present in the original form. An @deftech{inside-edge scope} is also
created and added to the original content; furthermore, the
inside-edge scope is added to the result of any partial expansion.
This inside-edge scope ensures that all bindings introduced by the
internal-definition context have a particular scope in common.
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@subsection[#:tag "mod-parse"]{Module Expansion, Phases, and Visits}
Expansion of a @racket[module] form proceeds in a similar way to
@seclink["intdef-body"]{expansion of an internal-definition context}:
an @tech{outside-edge scope} is created for the original module
content, and an @tech{inside-edge scope} is added to both the original
module and any form that appears during a partial expansion of the
module's top-level forms to uncover definitions and imports.
A @racket[require] form not only introduces @tech{bindings} at
expansion time, but also @deftech{visits} the referenced module when
it is encountered by the expander. That is, the expander instantiates
any variables defined in the module within @racket[begin-for-syntax],
and it also evaluates all expressions for @racket[define-syntaxes]
@tech{transformer} bindings.
Module @tech{visits} propagate through @racket[require]s in the same
way as module @tech{instantiation}. Moreover, when a module is
@tech{visit}ed at @tech{phase} 0, any module that it @racket[require]s
@racket[for-syntax] is @tech{instantiate}d at @tech{phase} 1, while
further @racket[require]s @racket[for-template] leading back
to @tech{phase} 0 causes the required module to be visited at
@tech{phase} 0 (i.e., not @tech{instantiate}d).
During compilation, the top-level of module context is itself
implicitly @tech{visit}ed. Thus, when the expander encounters
@racket[(require (for-syntax ....))], it immediately
@tech{instantiate}s the required module at @tech{phase} 1, in addition
to adding bindings at @tech{phase level} 1 (i.e., the
@tech{transformer environment}). Similarly, the expander immediately
evaluates any form that it encounters within
@racket[begin-for-syntax].
@tech{Phases} beyond 0 are @tech{visit}ed on demand. For example,
when the right-hand side of a @tech{phase}-0 @racket[let-syntax] is to
be expanded, then modules that are @tech{available} at @tech{phase} 1
are visited. More generally, initiating expansion at @tech{phase}
@math{n} @tech{visit}s modules at @tech{phase} @math{n}, which in turn
@tech{instantiates} modules at @tech{phase} @math{n+1}. These
@tech{visits} and @tech{instantiations} apply to @tech{available}
modules in the enclosing @tech{namespace}'s @tech{module registry};
a per-registry lock prevents multiple threads from concurrently
instantiating and visiting available modules. On-demand instantiation
of available modules uses the same reentrant lock as
@racket[namespace-call-with-registry-lock].
When the expander encounters @racket[require] and @racket[(require
(for-syntax ....))] within a @tech{module context}, the resulting
@tech{visits} and @tech{instantiations} are specific to the expansion
of the enclosing module, and are kept separate from @tech{visits} and
@tech{instantiations} triggered from a @tech{top-level context} or
from the expansion of a different module. Along the same lines, when a
module is attached to a namespace through
@racket[namespace-attach-module], modules that it @racket[require]s
are transitively attached, but instances are attached only at
phases at or below the namespace's @tech{base phase}.
@;------------------------------------------------------------------------
@subsection[#:tag "macro-introduced-bindings"]{Macro-Introduced Bindings}
When a top-level definition binds an identifier that originates from a
macro expansion, the definition captures only uses of the identifier
that are generated by the same expansion due to the fresh @tech{scope}
that is generated for the expansion.
@examples[
(define-syntax def-and-use-of-x
(syntax-rules ()
[(def-and-use-of-x val)
(code:comment @#,t{@racket[x] below originates from this macro:})
(begin (define x val) x)]))
(define x 1)
x
(def-and-use-of-x 2)
x
(define-syntax def-and-use
(syntax-rules ()
[(def-and-use x val)
(code:comment @#,t{@racket{x} below was provided by the macro use:})
(begin (define x val) x)]))
(def-and-use x 3)
x
]
For a top-level definition (outside of a module), the order of
evaluation affects the binding of a generated definition for a
generated identifier use. If the use precedes the definition, then
the use is resolved with the bindings that are in place that at
point, which will not be a macro-generated binding.
(No such dependency on order occurs
within a module, since a module binding covers the entire module
body.) To support the declaration of an identifier before its use,
the @racket[define-syntaxes] form avoids binding an identifier if the
body of the @racket[define-syntaxes] declaration produces zero
results.
@examples[
#:eval racket-eval
(define bucket-1 0)
(define bucket-2 0)
(define-syntax def-and-set!-use-of-x
(syntax-rules ()
[(def-and-set!-use-of-x val)
(begin (set! bucket-1 x) (define x val) (set! bucket-2 x))]))
(define x 1)
(def-and-set!-use-of-x 2)
x
bucket-1
bucket-2
(define-syntax defs-and-uses/fail
(syntax-rules ()
[(def-and-use)
(begin
(code:comment @#,t{Initial reference to @racket[even] precedes definition:})
(define (odd x) (if (zero? x) #f (even (sub1 x))))
(define (even x) (if (zero? x) #t (odd (sub1 x))))
(odd 17))]))
(eval:error (defs-and-uses/fail))
(define-syntax defs-and-uses
(syntax-rules ()
[(def-and-use)
(begin
(code:comment @#,t{Declare before definition via no-values @racket[define-syntaxes]:})
(define-syntaxes (odd even) (values))
(define (odd x) (if (zero? x) #f (even (sub1 x))))
(define (even x) (if (zero? x) #t (odd (sub1 x))))
(odd 17))]))
(defs-and-uses)
]
Macro-generated @racket[require] and @racket[provide]
clauses also introduce and reference generation-specific bindings
(due to the added @tech{scope}) with the same ordering effects as
for definitions. The bindings depend on the @tech{scope set} attached
to specific parts of the form:
@itemize[
@item{In @racket[require], for a @racket[_require-spec] of the form
@racket[(rename-in [_orig-id _bind-id])] or @racket[(only-in
.... [_orig-id _bind-id])], the @racket[_bind-id] supplies the
@tech{scope set} for the binding. In @racket[require] for other
@racket[_require-spec]s, the generator of the @racket[_require-spec]
determines the @tech{scope set}.}
@item{In @racket[provide], for a @racket[_provide-spec] of the form
@racket[_id], the exported identifier is the one that binds
@racket[_id], but the
external name is the plain, symbolic part of @racket[_id]. The exceptions for
@racket[all-except-out] are similarly determined, as is the @racket[_orig-id] binding of a
@racket[rename-out] form, and plain symbols are used for the
external names. For @racket[all-defined-out], only identifiers with
definitions having only the scopes of
@racket[(all-defined-out)] form are exported; the external name is
the plain symbol from the definition.}
]
@;------------------------------------------------------------------------
@section[#:tag "compilation-model"]{Compilation}
Before expanded code is evaluated, it is first @deftech{compiled}. A
compiled form has essentially the same information as the
corresponding expanded form, though the internal representation
naturally dispenses with identifiers for syntactic forms and local
bindings. One significant difference is that a compiled form is almost
entirely opaque, so the information that it contains cannot be
accessed directly (which is why some identifiers can be dropped). At
the same time, a compiled form can be marshaled to and from a byte
string, so it is suitable for saving and re-loading code.
Although individual read, expand, compile, and evaluate operations are
available, the operations are often combined automatically. For
example, the @racket[eval] procedure takes a syntax object and expands
it, compiles it, and evaluates it.
@;------------------------------------------------------------------------
@section[#:tag "namespace-model"]{Namespaces}
@margin-note/ref{See @secref["Namespaces"] for functions that
manipulate namespaces.}
A @deftech{namespace} is both a starting point for parsing and a
starting point for running @tech{compiled} code. A @tech{namespace}
also has a @deftech{module registry} that maps module names to module
declarations (see @secref["module-eval-model"]). This registry is
shared by all @tech{phase level}s, and it applies both to parsing and
to running @tech{compiled} code.
As a starting point for parsing, a @tech{namespace} provides scopes
(one per @tech{phase level}, plus one that spans all @tech{phase
levels}). Operations such as @racket[namespace-require] create initial
@tech{bindings} using the namespace's @tech{scopes}, and the further
expansion and evaluation in the namespace can create additional
@tech{bindings}. Evaluation of a form with a namespace always adds the
namespace's phase-specific @tech{scopes} to the form and to any result
of expanding the top-level form; as a result, every binding identifier
has at least one @tech{scope}. The namespace's additional scope, which
is added at all @tech{phase levels}, is added only on request (e.g.,
by using @racket[eval] as opposed to @racket[eval-syntax]). Except for
namespaces generated by a module (see @racket[module->namespace]),
every namespace uses the same @tech{scope} as the one added to all
@tech{phase levels}, while the @tech{scopes} specific to a @tech{phase
level} are always distinct.
As a starting point evaluating @tech{compiled} code, each namespace
encapsulates a distinct set of top-level variables at various
@tech{phases}, as well as a potentially distinct set of module
instances in each @tech{phase}. That is, even though module
declarations are shared for all @tech{phase levels}, module instances
are distinct for each @tech{phase}. Each namespace has a @deftech{base
phase}, which corresponds to the phase used by reflective operations
such as @racket[eval] and @racket[dynamic-require]. In particular,
using @racket[eval] on a @racket[require] form @tech{instantiates} a
module in the namespace's @tech{base phase}.
After a namespace is created, module instances from existing
namespaces can be attached to the new namespace. In terms of the
evaluation model, top-level variables from different namespaces
essentially correspond to definitions with different prefixes, but
attaching a module uses the same prefix for the module's definitions
in namespaces where it is attached. The first step in evaluating any
compiled expression is to link its top-level variable and module-level
variable references to specific variables in the namespace.
At all times during evaluation, some namespace is designated as the
@deftech{current namespace}. The current namespace has no particular
relationship, however, with the namespace that was used to expand the
code that is executing, or with the namespace that was used to link
the compiled form of the currently evaluating code. In particular,
changing the current namespace during evaluation does not change the
variables to which executing expressions refer. The current namespace
only determines the behavior of reflective operations to expand code
and to start evaluating expanded/compiled code.
@examples[
(code:line
(define x 'orig) (code:comment @#,t{define in the original namespace}))
(code:comment @#,t{The following @racket[let] expression is compiled in the original})
(code:comment @#,t{namespace, so direct references to @racket[x] see @racket['orig].})
(code:line
(let ([n (make-base-namespace)]) (code:comment @#,t{make new namespace})
(parameterize ([current-namespace n])
(eval '(define x 'new)) (code:comment @#,t{evals in the new namespace})
(display x) (code:comment @#,t{displays @racket['orig]})
(display (eval 'x)))) (code:comment @#,t{displays @racket['new]}))
]
If an @tech{identifier} is bound to syntax or to an import, then
defining the @tech{identifier} as a @tech{variable} shadows the syntax
or import in future uses of the environment. Similarly, if an
@tech{identifier} is bound to a @tech{top-level variable}, then
binding the identifier to syntax or an import shadows the variable;
the variable's value remains unchanged, however, and may be accessible
through previously evaluated expressions.
@examples[
(define x 5)
(define (f) x)
x
(f)
(define-syntax x (syntax-id-rules () [_ 10]))
x
(f)
(define x 7)
x
(f)
(module m racket (define x 8) (provide x))
(require 'm)
(eval:alts x (eval 'x))
(f)
]
Like a top-level @tech{namespace}, each @racket[module] form has an
associated @tech{scope} to span all @tech{phase levels} of the
module's content, plus a @tech{scope} at each @tech{phase level}. The
latter is added to every form, original or appearing through partial
macro expansion, within the module's immediate body. Those same scopes
are propagated to a namespace created by @racket[module->namespace]
for the module. Meanwhile, parsing of a @racket[module] form begins by
removing the all scopes that correspond to the enclosing top-level or
(in the case of @tech{submodules}) @racket[module] and
@racket[module*] forms.
@;------------------------------------------------------------------------
@section[#:tag "infernames"]{Inferred Value Names}
To improve error reporting, names are inferred at compile-time for
certain kinds of values, such as procedures. For example, evaluating
the following expression:
@racketblock[
(let ([f (lambda () 0)]) (f 1 2 3))
]
produces an error message because too many arguments are provided to
the procedure. The error message is able to report @racketidfont{f} as
the name of the procedure. In this case, Racket decides, at
compile-time, to name as @racket['f] all procedures created by the
@racket[let]-bound @racket[lambda].
@margin-note/ref{See @racket[procedure-rename] to override a procedure's
inferred name at runtime.}
Names are inferred whenever possible for procedures. Names closer to
an expression take precedence. For example, in
@racketblock[
(define my-f
(let ([f (lambda () 0)]) f))
]
the procedure bound to @racket[my-f] will have the inferred name
@racket['f].
When an @indexed-racket['inferred-name] property is attached to a
syntax object for an expression (see @secref["stxprops"]), the
property value is used for naming the expression, and it overrides any
name that was inferred from the expression's context. Normally, the
property value should be a symbol. A @racket['inferred-name]
property value of @|void-const| hides a name that would otherwise be
inferred from context (perhaps because a binding identifier's was
automatically generated and should not be exposed).
To support the propagation and merging of consistent properties during
expansions, the value of the @racket['inferred-name] property can be a
tree formed with @racket[cons] where all of the leaves are the same.
For example, @racket[(cons 'name 'name)] is equivalent to
@racket['name], and @racket[(cons (void) (void))] is equivalent to
@|void-const|.
When an inferred name is not available, but a source location is
available, a name is constructed using the source location
information. Inferred and property-assigned names are also available
to syntax transformers, via @racket[syntax-local-name].
@;----------------------------------------
@section[#:tag "cross-phase persistent-grammar"]{Cross-Phase Persistent Module Declarations}
A module is @tech{cross-phase persistent} only if it fits the following grammar,
which uses non-terminals from @secref["fully-expanded"], only if
it includes @racket[(#%declare #:cross-phase-persistent)], only
it includes no uses of @racket[quote-syntax] or @racket[#%variable-reference],
and only if no module-level binding is @racket[set!]ed.
@racketgrammar*[
#:literals (module module* #%plain-module-begin begin #%provide
define-values #%require #%declare
#%plain-lambda case-lambda begin
set! quote-syntax quote with-continuation-mark
#%plain-app
cons list make-struct-type make-struct-type-property
gensym string->uninterned-symbol #%variable-reference
variable-reference-from-unsafe?)
[cross-module (module id module-path
(#%plain-module-begin
cross-form ...))]
[cross-form (#%declare #:cross-phase-persistent)
(begin cross-form ...)
(#%provide raw-provide-spec ...)
submodule-form
(define-values (id ...) cross-expr)
(#%require raw-require-spec ...)]
[cross-expr id
(@#,racket[quote] cross-datum)
(#%plain-lambda formals expr ...+)
(case-lambda (formals expr ...+) ...)
(#%plain-app cons cross-expr ...+)
(#%plain-app list cross-expr ...+)
(#%plain-app make-struct-type cross-expr ...+)
(#%plain-app make-struct-type-property
cross-expr ...+)
(#%plain-app gensym)
(#%plain-app gensym string)
(#%plain-app string->uninterned-symbol string)
(#%plain-app variable-reference-from-unsafe?
(#%variable-reference))]
[cross-datum number
boolean
identifier
string
bytes
()]
]
This grammar applies after @tech{expansion}, but because a @tech{cross-phase persistent}
module imports only from other cross-phase persistent modules, the only relevant
expansion steps are the implicit introduction of
@racket[#%plain-module-begin], implicit introduction of @racket[#%plain-app],
and implicit introduction and/or expansion of @racket[#%datum].
@history[#:changed "7.5.0.12" @elem{Allow @racket[(#%plain-app variable-reference-from-unsafe? (#%variable-reference))].}]
@;----------------------------------------
@close-eval[racket-eval]