252 lines
9.7 KiB
Racket
252 lines
9.7 KiB
Racket
#lang scribble/doc
|
|
@(require "mz.rkt")
|
|
|
|
@(define stx-eval (make-base-eval))
|
|
@examples[#:hidden #:eval stx-eval (require (for-syntax racket/base))]
|
|
|
|
@title[#:tag "stxcmp"]{Syntax Object Bindings}
|
|
|
|
@defproc[(bound-identifier=? [a-id syntax?] [b-id syntax?]
|
|
[phase-level (or/c exact-integer? #f)
|
|
(syntax-local-phase-level)])
|
|
boolean?]{
|
|
|
|
Returns @racket[#t] if the identifier @racket[a-id] would bind
|
|
@racket[b-id] (or vice versa) if the identifiers were substituted in a
|
|
suitable expression context at the @tech{phase level} indicated by
|
|
@racket[phase-level], @racket[#f] otherwise. A @racket[#f] value for
|
|
@racket[phase-level] corresponds to the @tech{label phase level}.
|
|
|
|
@examples[
|
|
#:eval stx-eval
|
|
(define-syntax (check stx)
|
|
(syntax-case stx ()
|
|
[(_ x y)
|
|
(if (bound-identifier=? #'x #'y)
|
|
#'(let ([y 'wrong]) (let ([x 'binds]) y))
|
|
#'(let ([y 'no-binds]) (let ([x 'wrong]) y)))]))
|
|
(check a a)
|
|
(check a b)
|
|
(define-syntax-rule (check-a x) (check a x))
|
|
(check-a a)
|
|
]}
|
|
|
|
|
|
@defproc[(free-identifier=? [a-id identifier?] [b-id identifier?]
|
|
[a-phase-level (or/c exact-integer? #f)
|
|
(syntax-local-phase-level)]
|
|
[b-phase-level (or/c exact-integer? #f)
|
|
a-phase-level])
|
|
boolean?]{
|
|
|
|
Returns @racket[#t] if @racket[a-id] and @racket[b-id] access the same
|
|
@tech{local binding}, @tech{module binding}, or @tech{top-level
|
|
binding}---perhaps via @tech{rename transformers}---at the @tech{phase
|
|
levels} indicated by @racket[a-phase-level] and
|
|
@racket[b-phase-level], respectively. A @racket[#f] value for
|
|
@racket[a-phase-level] or @racket[b-phase-level] corresponds to the
|
|
@tech{label phase level}.
|
|
|
|
``Same module binding'' means that the identifiers refer to the same
|
|
original definition site, and not necessarily to the same
|
|
@racket[require] or @racket[provide] site. Due to renaming in
|
|
@racket[require] and @racket[provide], or due to a transformer binding
|
|
to a @tech{rename transformer}, the identifiers may return distinct
|
|
results with @racket[syntax-e].
|
|
|
|
@examples[
|
|
#:eval stx-eval
|
|
(define-syntax (check stx)
|
|
(syntax-case stx ()
|
|
[(_ x)
|
|
(if (free-identifier=? #'car #'x)
|
|
#'(list 'same: x)
|
|
#'(list 'different: x))]))
|
|
(check car)
|
|
(check mcar)
|
|
(let ([car list])
|
|
(check car))
|
|
(require (rename-in racket/base [car kar]))
|
|
(check kar)
|
|
]}
|
|
|
|
@defproc[(free-transformer-identifier=? [a-id identifier?] [b-id identifier?]) boolean?]{
|
|
|
|
Same as @racket[(free-identifier=? a-id b-id (add1 (syntax-local-phase-level)))].}
|
|
|
|
@defproc[(free-template-identifier=? [a-id identifier?] [b-id identifier?]) boolean?]{
|
|
|
|
Same as @racket[(free-identifier=? a-id b-id (sub1 (syntax-local-phase-level)))].}
|
|
|
|
@defproc[(free-label-identifier=? [a-id identifier?] [b-id identifier?]) boolean?]{
|
|
|
|
Same as @racket[(free-identifier=? a-id b-id #f)].}
|
|
|
|
|
|
@defproc[(check-duplicate-identifier [ids (listof identifier?)])
|
|
(or/c identifier? #f)]{
|
|
|
|
Compares each identifier in @racket[ids] with every other identifier
|
|
in the list with @racket[bound-identifier=?]. If any comparison
|
|
returns @racket[#t], one of the duplicate identifiers is returned (the
|
|
first one in @racket[ids] that is a duplicate), otherwise the result
|
|
is @racket[#f].}
|
|
|
|
|
|
@defproc[(identifier-binding [id-stx identifier?]
|
|
[phase-level (or/c exact-integer? #f)
|
|
(syntax-local-phase-level)]
|
|
[top-level-symbol? any/c #f])
|
|
(or/c 'lexical
|
|
#f
|
|
(list/c module-path-index?
|
|
symbol?
|
|
module-path-index?
|
|
symbol?
|
|
exact-nonnegative-integer?
|
|
(or/c exact-integer? #f)
|
|
(or/c exact-integer? #f))
|
|
(list/c symbol?))]{
|
|
|
|
Returns one of three (if @racket[top-level-symbol?] is @racket[#f])
|
|
or four (if @racket[top-level-symbol?] is true) kinds of values, depending on the binding of
|
|
@racket[id-stx] at the @tech{phase level} indicated by
|
|
@racket[phase-level] (where a @racket[#f] value for
|
|
@racket[phase-level] corresponds to the @tech{label phase level}):
|
|
|
|
@itemize[
|
|
|
|
@item{The result is @indexed-racket['lexical] if @racket[id-stx]
|
|
has a @tech{local binding}.}
|
|
|
|
@item{The result is a list of seven items when @racket[id-stx]
|
|
has a @tech{module binding}: @racket[(list _source-mod _source-id
|
|
_nominal-source-mod _nominal-source-id _source-phase _import-phase
|
|
_nominal-export-phase)].
|
|
|
|
@itemize[
|
|
|
|
@item{@racket[_source-mod] is a module path index (see
|
|
@secref["modpathidx"]) that indicates the defining module.}
|
|
|
|
@item{@racket[_source-id] is a symbol for the identifier's name
|
|
at its definition site in the source module. This can be
|
|
different from the local name returned by
|
|
@racket[syntax->datum] for several reasons: the identifier is
|
|
renamed on import, it is renamed on export, or it is
|
|
implicitly renamed because the identifier (or its import) was
|
|
generated by a macro invocation.}
|
|
|
|
@item{@racket[_nominal-source-mod] is a module path index (see
|
|
@secref["modpathidx"]) that indicates the module
|
|
@racket[require]d into the context of @racket[id-stx] to
|
|
provide its binding. It can be different from
|
|
@racket[_source-mod] due to a re-export in
|
|
@racket[_nominal-source-mod] of some imported identifier. If
|
|
the same binding is imported in multiple ways, an arbitrary
|
|
representative is chosen.}
|
|
|
|
@item{@racket[_nominal-source-id] is a symbol for the
|
|
identifier's name as exported by
|
|
@racket[_nominal-source-mod]. It can be different from
|
|
@racket[_source-id] due to a renaming @racket[provide], even if
|
|
@racket[_source-mod] and @racket[_nominal-source-mod] are the
|
|
same.}
|
|
|
|
@item{@racket[_source-phase] is an exact non-negative integer
|
|
representing the source phase. For example, it is @racket[1] if the source
|
|
definition is for-syntax.}
|
|
|
|
@item{@racket[_import-phase] is @racket[0] if the binding
|
|
import of @racket[_nominal-source-mode] is a plain
|
|
@racket[require], @racket[1] if it is from a
|
|
@racket[for-syntax] import, etc.}
|
|
|
|
@item{@racket[_nominal-export-phase] is the @tech{phase level}
|
|
of the export from @racket[_nominal-source-mod].}
|
|
|
|
]}
|
|
|
|
@item{The result is @racket[(list _source-id)] if @racket[id-stx] has a
|
|
@tech{top-level binding} and @racket[top-level-symbol?] is true.}
|
|
|
|
@item{The result is @racket[#f] if @racket[id-stx] has a
|
|
@tech{top-level binding} and @racket[top-level-symbol?] is
|
|
@racket[#f] or if @racket[id-stx] is @tech{unbound}. An
|
|
unbound identifier is typically treated the same as an
|
|
identifier whose top-level binding is a variable.}
|
|
|
|
]
|
|
|
|
If @racket[id-stx] is bound to a @tech{rename-transformer}, the result
|
|
from @racket[identifier-binding] is for the identifier in the
|
|
transformer, so that @racket[identifier-binding] is consistent with
|
|
@racket[free-identifier=?].
|
|
|
|
@history[#:changed "6.6.0.4" @elem{Added the @racket[top-level-symbol?] argument to report
|
|
information on top-level bindings.}]}
|
|
|
|
|
|
@defproc[(identifier-transformer-binding [id-stx identifier?]
|
|
[rt-phase-level (or/c exact-integer? #f)
|
|
(syntax-local-phase-level)])
|
|
(or/c 'lexical
|
|
#f
|
|
(listof module-path-index?
|
|
symbol?
|
|
module-path-index?
|
|
symbol?
|
|
exact-nonnegative-integer?
|
|
(or/c exact-integer? #f)
|
|
(or/c exact-integer? #f)))]{
|
|
|
|
Same as @racket[(identifier-binding id-stx (and rt-phase-level (add1 rt-phase-level)))].}
|
|
|
|
|
|
@defproc[(identifier-template-binding [id-stx identifier?])
|
|
(or/c 'lexical
|
|
#f
|
|
(listof module-path-index?
|
|
symbol?
|
|
module-path-index?
|
|
symbol?
|
|
exact-nonnegative-integer?
|
|
(or/c exact-integer? #f)
|
|
(or/c exact-integer? #f)))]{
|
|
|
|
Same as @racket[(identifier-binding id-stx (sub1 (syntax-local-phase-level)))].}
|
|
|
|
|
|
@defproc[(identifier-label-binding [id-stx identifier?])
|
|
(or/c 'lexical
|
|
#f
|
|
(listof module-path-index?
|
|
symbol?
|
|
module-path-index?
|
|
symbol?
|
|
exact-nonnegative-integer?
|
|
(or/c exact-integer? #f)
|
|
(or/c exact-integer? #f)))]{
|
|
|
|
Same as @racket[(identifier-binding id-stx #f)].}
|
|
|
|
|
|
@defproc[(identifier-binding-symbol [id-stx identifier?]
|
|
[phase-level (or/c exact-integer? #f)
|
|
(syntax-local-phase-level)])
|
|
symbol?]{
|
|
|
|
Like @racket[identifier-binding], but produces a symbol that
|
|
corresponds to the binding. The symbol result is the same for any
|
|
identifiers that are @racket[free-identifier=?], but the result may
|
|
also be the same for identifiers that are not
|
|
@racket[free-identifier=?] (i.e., different symbols imply different
|
|
bindings, but the same symbol does not imply the same binding).
|
|
|
|
When @racket[identifier-binding] would produce a list, then the second
|
|
element of that list is the result that
|
|
@racket[identifier-binding-symbol] produces.}
|
|
|
|
|
|
@close-eval[stx-eval]
|