racket/collects/scribblings/reference/stx-trans.scrbl

727 lines
28 KiB
Racket

#lang scribble/doc
@(require (except-in "mz.ss" import export)
(for-syntax scheme/base)
(for-label scheme/require-transform
scheme/require-syntax
scheme/provide-transform
scheme/provide-syntax))
@(define stx-eval (make-base-eval))
@(interaction-eval #:eval stx-eval (require (for-syntax scheme/base)))
@(define (transform-time) @t{This procedure must be called during the
dynamic extent of a @tech{syntax transformer} application by the
expander, otherwise the @exnraise[exn:fail:contract].})
@title[#:tag "stxtrans"]{Syntax Transformers}
@defproc[(make-set!-transformer [proc (syntax? . -> . syntax?)])
set!-transformer?]{
Creates a @tech{syntax transformer} that cooperates with
@scheme[set!]. If the result of @scheme[make-set!-transformer] is
bound to @scheme[identifier] as a @tech{transformer binding}, then
@scheme[proc] is applied as a transformer when @scheme[identifier] is
used in an expression position, or when it is used as the target of a
@scheme[set!] assignment as @scheme[(set! identifier _expr)]. When the
identifier appears as a @scheme[set!] target, the entire @scheme[set!]
expression is provided to the transformer.
@examples[
#:eval stx-eval
(let ([x 1]
[y 2])
(let-syntax ([x (make-set!-transformer
(lambda (stx)
(syntax-case stx (set!)
(code:comment #, @t{Redirect mutation of x to y})
[(set! id v) (syntax (set! y v))]
(code:comment #, @t{Normal use of @scheme[x] really gets @scheme[x]})
[id (identifier? (syntax id)) (syntax x)])))])
(begin
(set! x 3)
(list x y))))
]}
@defproc[(set!-transformer? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a value created by
@scheme[make-set!-transformer], @scheme[#f] otherwise.}
@defproc[(set!-transformer-procedure [transformer set!-transformer?])
(syntax? . -> . syntax?)]{
Returns the procedure that was passed to
@scheme[make-set!-transformer] to create @scheme[transformer].}
@defproc[(make-rename-transformer [id-stx syntax?])
rename-transformer?]{
Creates a value that, when used as a @tech{transformer binding},
inserts the identifier @scheme[id-stx] in place of whatever identifier
binds the transformer, including in non-application positions, and in
@scheme[set!] expressions. Such a transformer could be written
manually, but the one created by @scheme[make-rename-transformer]
cooperates specially with @scheme[syntax-local-value] (see below).}
@defproc[(rename-transformer? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a value created by
@scheme[make-rename-transformer], @scheme[#f] otherwise.}
@defproc[(rename-transformer-target [transformer rename-transformer?])
syntax?]{
Returns the identifier passed to @scheme[make-rename-transformer] to
create @scheme[transformer].}
@defproc[(local-expand [stx syntax?]
[context-v (or/c (one-of 'expression 'top-level 'module
'module-begin)
list?)]
[stop-ids (or/c (listof identifier?) false/c)]
[intdef-ctx (or/c internal-definition-context?
false/c)
#f])
syntax?]{
Expands @scheme[stx] in the lexical context of the expression
currently being expanded. The @scheme[context-v] argument is used as
the result of @scheme[syntax-local-context] for immediate expansions;
for a particular @tech{internal-definition context}, generate a unique
value and @scheme[cons] it onto the current result of
@scheme[syntax-local-context] if it is a list.
When an identifier in @scheme[stop-ids] is encountered by the expander
in a subexpression, expansions stops for the subexpression. If
@scheme[#%app], @scheme[#%top], or @scheme[#%datum] appears in
@scheme[stop-ids], then application, top-level variable reference, and
literal data expressions without the respective explicit form are not
wrapped with the explicit form. If @scheme[stop-ids] is @scheme[#f]
instead of a list, then @scheme[stx] is expanded only as long as the
outermost form of @scheme[stx] is a macro (i.e., expansion does not
proceed to sub-expressions).
The optional @scheme[intdef-ctx] argument must be either @scheme[#f]
or the result of @scheme[syntax-local-make-definition-context]. In the
latter case, lexical information for internal definitions is added to
@scheme[stx] before it is expanded. The lexical information is also
added to the expansion result (because the expansion might introduce
bindings or references to internal-definition bindings).
Expansion of @scheme[stx] can use certificates for the expression
already being expanded (see @secref["stxcerts"]) , and @tech{inactive
certificates} associated with @scheme[stx] are activated for
@scheme[stx] (see @secref["stxcerts"]). Furthermore, if the
transformer is defined within a module (i.e., the current expansion
was triggered by a use of a module-defined identifier with a
@tech{transformer binding}) or if the current expression is being
expanded for the body of a module, then the expansion of @scheme[stx]
can use any identifier defined by the module.
@transform-time[]}
@defproc[(syntax-local-expand-expression [stx syntax?])
(values syntax? syntax?)]{
Like @scheme[local-expand] given @scheme['expression] and an empty
stop list, but with two results: a syntax object for the fully
expanded expression, and a syntax object whose content is opaque. The
latter can be used in place of the former (perhaps in a larger
expression produced by a macro transformer), and when the macro
expander encounters the opaque object, it substitutes the fully
expanded expression without re-expanding it; the
@exnraise[exn:fail:syntax] if the expansion context includes bindings
or marks that were not present for the original expansion, in which
case re-expansion might produce different results. Consistent use of
@scheme[syntax-local-expand-expression] and the opaque object thus
avoids quadratic expansion times when local expansions are nested.
@transform-time[]}
@defproc[(local-transformer-expand [stx syntax?]
[context-v (or/c (one-of 'expression 'top-level 'module
'module-begin)
list?)]
[stop-ids (or/c (listof identifier?) false/c)]
[intdef-ctx (or/c internal-definition-context?
false/c)
#f])
syntax?]{
Like @scheme[local-expand], but @scheme[stx] is expanded as a
transformer expression instead of a run-time expression.}
@defproc[(local-expand/capture-lifts [stx syntax?]
[context-v (or/c (one-of 'expression 'top-level 'module
'module-begin)
list?)]
[stop-ids (or/c (listof identifier?) false/c)]
[intdef-ctx (or/c internal-definition-context?
false/c)
#f]
[lift-ctx any/c (gensym 'lifts)])
syntax?]{
Like @scheme[local-expand], the result is a syntax object that
represents a @scheme[begin] expression. Lifted expressions---from
calls to @scheme[syntax-local-lift-expression] during the expansion of
@scheme[stx]---appear with their identifiers in @scheme[define-values]
forms, and the expansion of @scheme[stx] is the last expression in the
@scheme[begin]. The @scheme[lift-ctx] value is reported by
@scheme[syntax-local-lift-context] during local expansion. The lifted
expressions are not expanded, but instead left as provided in the
@scheme[begin] form.}
@defproc[(local-transformer-expand/capture-lifts [stx syntax?]
[context-v (or/c (one-of 'expression 'top-level 'module
'module-begin)
list?)]
[stop-ids (or/c (listof identifier?) false/c)]
[intdef-ctx (or/c internal-definition-context?
false/c)
#f])
syntax?]{
Like @scheme[local-expand/capture-lifts], but @scheme[stx] is expanded
as a transformer expression instead of a run-time expression. Lifted
expressions are reported as @scheme[define-values] forms (in the
transformer environment).}
@defproc[(syntax-local-make-definition-context) internal-definition-context?]{
Creates an opaque internal-definition context value to be used with
@scheme[local-expand] and other functions. A transformer should create
one context for each set of internal definitions to be expanded, and
use it when expanding any form whose lexical context should include
the definitions. After discovering an internal @scheme[define-values]
or @scheme[define-syntaxes] form, use
@scheme[syntax-local-bind-syntaxes] to add bindings to the context.
@transform-time[]}
@defproc[(syntax-local-bind-syntaxes [id-list (listof identifier?)]
[expr (or/c syntax? false/c)]
[intdef-ctx internal-definition-context?])
void?]{
Binds each identifier in @scheme[id-list] within the
internal-definition context represented by @scheme[intdef-ctx], where
@scheme[intdef-ctx] is the result of
@scheme[syntax-local-make-definition-context]. Supply @scheme[#f] for
@scheme[expr] when the identifiers correspond to
@scheme[define-values] bindings, and supply a compile-time expression
when the identifiers correspond to @scheme[define-syntaxes] bindings;
the later case, the number of values produces by the expression should
match the number of identifiers, otherwise the
@exnraise[exn:fail:contract:arity].
@transform-time[]}
@defproc[(syntax-local-value [id-stx syntax?]
[failure-thunk (or/c (-> any) false/c)
#f]
[intdef-ctx (or/c internal-definition-context?
false/c)
#f])
any]{
Returns the @tech{transformer binding} value of @scheme[id-stx] in
either the context asscoiated with @scheme[intdef-ctx] (if not
@scheme[#f]) or the context of the expression being expanded (if
@scheme[indef-ctx] is @scheme[#f]). If @scheme[intdef-ctx] is
provided, it must be an extension of the context of the expression
being expanded.
If @scheme[id-stx] is bound to a rename transformer created with
@scheme[make-rename-transformer], @scheme[syntax-local-value]
effectively calls itself with the target of the rename and returns
that result, instead of the rename transformer.
If @scheme[id-stx] has no @tech{transformer binding} (via
@scheme[define-syntax], @scheme[let-syntax], etc.) in that
environment, the result is obtained by applying @scheme[failure-thunk]
if not @scheme[#f]. If @scheme[failure-thunk] is @scheme[false], the
@exnraise[exn:fail:contract].
Resolving @scheme[id-stx] can use certificates for the expression
being transformed (see @secref["stxcerts"]) as well as @tech{inactive
certificates} associated with @scheme[id-stx] (see
@secref["stxcerts"]). Furthermore, if the transformer is defined
within a module (i.e., the current transformation was triggered by a
use of a module-defined identifier) or if the current expression is
being expanded for the body of a module, then resolving
@scheme[id-stx] can access any identifier defined by the module.
@transform-time[]}
@defproc[(syntax-local-lift-expression [stx syntax?])
identifier?]{
Returns a fresh identifier, and cooperates with the @scheme[module],
@scheme[letrec-syntaxes+values], @scheme[define-syntaxes],
@scheme[begin-for-syntax], and top-level expanders to bind the
generated identifier to the expression @scheme[stx].
A run-time expression within a module is lifted to the module's top
level, just before the expression whose expansion requests the
lift. Similarly, a run-time expression outside of a module is lifted
to a top-level definition. A compile-time expression in a
@scheme[letrec-syntaxes+values] or @scheme[define-syntaxes] binding is
lifted to a @scheme[let] wrapper around the corresponding right-hand
side of the binding. A compile-time expression within
@scheme[begin-for-syntax] is lifted to a @scheme[define-for-syntax]
declaration just before the requesting expression.
Other syntactic forms can capture lifts by using
@scheme[local-expand/capture-lifts] or
@scheme[local-transformer-expand/capture-lifts].
@transform-time[]}
@defproc[(syntax-local-lift-context)
any/c]{
Returns a value that represents the target for expressions lifted via
@scheme[syntax-local-lift-expression]. That is, for different
transformer calls for which this procedure returns the same value (as
determined by @scheme[eq?]), lifted expressions for the two
transformer are moved to the same place. Thus, the result is useful
for caching lift information to avoid redundant lifts.
@transform-time[]}
@defproc[(syntax-local-lift-module-end-declaration [stx syntax?])
void?]{
Cooperates with the @scheme[module] form to insert @scheme[stx] as
a top-level declaration at the end of the module currently being
expanded. If the current expression being transformed is not within a
@scheme[module] form, or if it is not a run-time expression, then the
@exnraise[exn:fail:contract]. If the current expression being
transformed is not in the module top-level, then @scheme[stx] is
eventually expanded in an expression context.
@transform-time[]}
@defproc[(syntax-local-name) (or/c symbol? false/c)]{
Returns an inferred name for the expression position being
transformed, or @scheme[#f] if no such name is available. See also
@secref["infernames"].
@transform-time[]}
@defproc[(syntax-local-context)
(or/c (one-of 'expression 'top-level 'module 'module-begin)
list?)]{
Returns an indication of the context for expansion that triggered a
@tech{syntax transformer} call. See @secref["expand-context-model"]
for more information on contexts.
The symbol results indicate that the expression is being expanded for
an @tech{expression context}, a @tech{top-level context}, a
@tech{module context}, or a @tech{module-begin context}.
A list result indicates expansion in an @tech{internal-definition
context}. The identity of the lists's first element (i.e., its
@scheme[eq?]ness) reflects the identity of the internal-definition
context; in particular two transformer expansions receive the same
first value if and only if they are invoked for the same
internal-definition context. Later values in the list similarly
identify internal-definition contexts that are still being expanded,
and that required the expansion of nested internal-definition
contexts.
@transform-time[]}
@defproc[(syntax-local-phase-level) (or/c exact-integer? false/c)]{
During the dynamic extent of a @tech{syntax transformer} application
by the expander, the result is the @tech{phase level} of the form
being expanded. Otherwise, the result is @scheme[0].}
@defproc[(syntax-local-module-exports [mod-path module-path?])
(values (listof symbol?) (listof symbol?) (listof symbol?))]{
Returns three lists of symbols that represent the @scheme[provide]d
bindings of the module named by @scheme[mod-path]. The first list
corresponds to the @tech{phase level} 0 exports of the module, the
second list corresponds to the @tech{phase level} -1 exports of the
module, and the last list corresponds to the @tech{label phase level}
exports of the module.
@transform-time[]}
@defproc[(syntax-local-get-shadower [id-stx identifier?]) identifier?]{
Returns @scheme[id-stx] if no binding in the current expansion context
shadows @scheme[id-stx], if @scheme[id-stx] has no module bindings in
its lexical information, and if the current expansion context is not a
@tech{module context}.
If a binding of @scheme[inner-identifier] shadows @scheme[id-stx], the
result is the same as @scheme[(syntax-local-get-shadower
@scheme[inner-identifier])], except that it has the location and
properties of @scheme[id-stx].
Otherwise, the result is the same as @scheme[id-stx] with its module
bindings (if any) removed from its lexical information, and the
lexical information of the current @tech{module context} (if any)
added.
Thus, the result is an identifier corresponding to the innermost
shadowing of @scheme[id-stx] in the current context if its shadowed,
and a module-contextless version of @scheme[id-stx] otherwise.
@transform-time[]}
@defproc[(syntax-local-certifier [active? boolean? #f])
((syntax?) (any/c (or/c procedure? false/c))
. ->* . syntax?)]{
Returns a procedure that captures any certificates currently available
for @scheme[syntax-local-value] or @scheme[local-expand]. The
procedure accepts one to three arguments: @scheme[_stx] (required),
@scheme[_key] (optional), and @scheme[_intro] (optional). The
procedure's result is a syntax object like @scheme[stx], except that
it includes the captured certificates as inactive (see
@secref["stxcerts"]) if @scheme[active?] is @scheme[#f] (the default)
or active otherwise. If @scheme[key] is supplied and not @scheme[#f],
it is associated with each captured certificate for later use through
@scheme[syntax-recertify]. If @scheme[_intro] is supplied, and if it
is not @scheme[#f] (the default), then it must be a procedure created
by @scheme[make-syntax-introducer], in which case the certificate
applies only to parts of @scheme[stx] that are marked as introduced by
@scheme[_intro].
Supply @scheme[#t] for @scheme[active?] when the syntax to be
certified can be safely used in any context by any party, and where
access to the syntax object should not confer any additional
access. Supply @scheme[#f] for @scheme[active?] when the syntax to be
certified is not accessible to parties that might abuse the access
that the certificate provides, and when the certified syntax
eventually appears (via macro expansion) within a larger expression
from which it cannot be safely extracted by other parties.
@transform-time[]}
@defproc[(syntax-transforming?) boolean?]{
Returns @scheme[#t] during the dynamic extent of a @tech{syntax
transformer} application by the expander, @scheme[#f] otherwise.}
@defproc[(syntax-local-introduce [stx syntax?]) syntax?]{
Produces a syntax object that is like @scheme[stx], except that a
@tech{syntax mark} for the current expansion is added (possibly
canceling an existing mark in parts of @scheme[stx]). See
@secref["transformer-model"] for information on @tech{syntax
marks}.
@transform-time[]}
@defproc[(make-syntax-introducer) (syntax? . -> . syntax?)]{
Produces a procedure that behaves like
@scheme[syntax-local-introduce], but using a fresh @tech{syntax
mark}. Multiple applications of the same
@scheme[make-syntax-introducer] result procedure use the same mark,
and different result procedures use distinct marks.}
@defproc[(syntax-local-transforming-module-provides?) boolean?]{
Returns @scheme[#t] while a @tech{provide transformer} is running (see
@scheme[make-provide-transformer]) or while a @schemeidfont{expand} sub-form of
@scheme[#%provide] is expanded, @scheme[#f] otherwise.}
@defproc[(syntax-local-module-defined-identifiers)
(values (listof identifier?) (listof identifier?))]{
Can be called only while
@scheme[syntax-local-transforming-module-provides?] returns
@scheme[#t].
It returns two lists of identifiers corresponding to all definitions
within the module being expanded. This information is used for
implementing @scheme[provide] sub-forms like @scheme[all-defined-out].
The first result list corresponds to @tech{phase} 0 (i.e., normal)
definitions, and the second corresponds to @tech{phase} -1 (i.e.,
for-syntax) definitions.}
@defproc[(syntax-local-module-required-identifiers
[mod-path (or/c module-path? false/c)]
[phase-level (or/c exact-integer? false/c (one-of/c #t))])
(listof (cons/c (or/c exact-integer? false/c)
(listof identifier?)))]{
Can be called only while
@scheme[syntax-local-transforming-module-provides?] returns
@scheme[#t].
It returns an association list mapping phase levels to lists of
identifiers. Each list of identifiers includes all bindings imported
(into the module being expanded) using the module path
@scheme[mod-path], or all modules if @scheme[mod-path] is
@scheme[#f]. The association list includes all identifiers imported
with a @scheme[phase-level] shift, of all shifts if
@scheme[phase-level] is @scheme[#t].
When an identifier is renamed on import, the result association list
includes the identifier by its internal name. Use
@scheme[identifier-binding] to obtain more information about the
identifier.}
@; ----------------------------------------------------------------------
@section[#:tag "require-trans"]{@scheme[require] Transformers}
@note-lib-only[scheme/require-transform]
A @tech{transformer binding} whose value is a structure with the
@scheme[prop:require-transformer] property implements a derived
@scheme[_require-spec] for @scheme[require].
The transformer is called with the syntax object representing its use
as a @scheme[_require-spec] within a @scheme[require] form, and the
result must be two lists: a list of @scheme[import]s and a list of
@scheme[import-source]s.
If the derived form contains a sub-form that is a
@scheme[_require-spec], then it can call @scheme[expand-import] to
transform the sub-@scheme[_require-spec] to lists of imports and
import sources.
See also @scheme[define-require-syntax], which supports macro-style
@scheme[require] transformers.
@defproc[(expand-import [stx syntax?])
(values (listof import?)
(listof import-source?))]{
Expands the given @scheme[_require-spec] to lists of imports and
import sources. The latter specifies modules to be
@tech{instantiate}d or @tech{visit}ed, so the modules that it
represents should be a superset of the modules represented in the
former list (so that a module will be @tech{instantiate}d or
@tech{visit}ed even if all of imports are eventually filtered from the
former list).}
@defproc[(make-require-transformer [proc (syntax? . -> . (values
(listof import?)
(listof import-source?)))])
require-transformer?]{
Creates a @deftech{require transformer} (i.e., a structure with the
@scheme[prop:require-transformer] property) using the given procedure
as the transformer.}
@defthing[prop:require-transformer struct-type-property?]{
A property to identify @scheme[require] transformers. The property
value must be a procedure that takes a syntax object and returns
import and import-source lists.}
@defproc[(require-transformer? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] has the
@scheme[prop:require-transformer] property, @scheme[#f] otherwise.}
@defstruct[import ([local-id identifier?]
[src-sym symbol?]
[src-mod-path module-path?]
[mode (or/c exact-integer? false/c)]
[req-mode (or/c exact-integer? false/c)]
[orig-mode (or/c exact-integer? false/c)]
[orig-stx syntax?])]{
A structure representing a single imported identifier:
@itemize{
@item{@scheme[local-id] --- the identifier to be bound within the
importing module.}
@item{@scheme[src-sym] --- the external name of the binding as
exported from its source module.}
@item{@scheme[src-mod-path] --- a @tech{module path} (relative to the
importing module) for the source of the imported binding.}
@item{@scheme[orig-stx] --- a @tech{syntax object} for the source of
the import, used for error reporting.}
@item{@scheme[mode] --- the @tech{phase level} of the binding in the
importing module.}
@item{@scheme[req-mode] --- the @tech{phase level} shift of the
import relative to the exporting module.}
@item{@scheme[orig-mode] --- the @tech{phase level} of the
binding as exported by the exporting module.}
}}
@defstruct[import-source ([mod-path-stx (and/c syntax?
(lambda (x)
(module-path? (syntax->datum x))))]
[mode (or/c exact-integer? false/c)])]{
A structure representing an imported module, which must be
@tech{instantiate}d or @tech{visit}ed even if no binding is imported
into a module.
@itemize{
@item{@scheme[mod-path-stx] --- a @tech{module path} (relative
to the importing module) for the source of the imported binding.}
@item{@scheme[mode] --- the @tech{phase level} shift the import.}
}}
@defproc[(syntax-local-require-certifier)
((syntax?) (or/c false/c (syntax? . -> . syntax?))
. ->* . syntax?)]{
Like @scheme[syntax-local-certifier], but to certify @tech{syntax
objects} that correspond to @scheme[require] sub-forms, so that
@scheme[expand-import] can deconstruct the @tech{syntax object} as
necessary to expand it.}
@; ----------------------------------------------------------------------
@section[#:tag "provide-trans"]{@scheme[provide] Transformers}
@note-lib-only[scheme/provide-transform]
A @tech{transformer binding} whose value is a structure with the
@scheme[prop:provide-transformer] property implements a derived
@scheme[_provide-spec] for @scheme[provide].
The transformer is called with the syntax object representing its use
as a @scheme[_provide-spec] within a @scheme[provide] form and a list
of symbols representing the export modes specified by enclosing
@scheme[_provide-spec]s. The result must be a list of
@scheme[export]s.
If the derived form contains a sub-form that is a
@scheme[_provide-spec], then it can call @scheme[expand-export] to
transform the sub-@scheme[_provide-spec] to a lists of exports.
See also @scheme[define-provide-syntax], which supports macro-style
@scheme[provide] transformers.
@defproc[(expand-export [stx syntax?] [modes (listof (or/c exact-integer? false/c))])
(listof export?)]{
Expands the given @scheme[_provide-spec] to a list of exports. The
@scheme[modes] list controls the expansion of
sub-@scheme[_provide-specs]; for example, an identifier refers to a
@tech{phase level} 0 binding unless the @scheme[modes] list specifies
otherwise. Normally, @scheme[modes] is either empty or contains a
single element.}
@defproc[(make-provide-transformer [proc (syntax? (listof (or/c exact-integer? false/c))
. -> . (listof export?))])
provide-transformer?]{
Creates a @deftech{provide transformer} (i.e., a structure with the
@scheme[prop:provide-transformer] property) using the given procedure
as the transformer.}
@defthing[prop:provide-transformer struct-type-property?]{
A property to identify @scheme[provide] transformers. The property
value must be a procedure that takes a syntax object and mode list and
returns an export list.}
@defproc[(provide-transformer? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] has the
@scheme[prop:provide-transformer] property, @scheme[#f] otherwise.}
@defstruct[export ([local-id identifier?]
[out-sym symbol?]
[mode (or/c exact-integer? false/c)]
[protect? any/c]
[orig-stx syntax?])]{
A structure representing a single imported identifier:
@itemize{
@item{@scheme[local-id] --- the identifier that is bound within the
exporting module.}
@item{@scheme[out-sym] --- the external name of the binding.}
@item{@scheme[orig-stx] --- a @tech{syntax object} for the source of
the export, used for error reporting.}
@item{@scheme[protect?] --- indicates whether the identifier should
be protected (see @secref["modprotect"]).}
@item{@scheme[mode] --- the @tech{phase level} of the binding in the
exporting module.}
}}
@defproc[(syntax-local-provide-certifier)
((syntax?) (or/c false/c (syntax? . -> . syntax?))
. ->* . syntax?)]{
Like @scheme[syntax-local-certifier], but to certify @tech{syntax
objects} that correspond to @scheme[provide] sub-forms, so that
@scheme[expand-export] can deconstruct the @tech{syntax object} as
necessary to expand it.}
@; ----------------------------------------------------------------------
@close-eval[stx-eval]