racket/pkgs/racket-doc/scribblings/reference/syntax-util.scrbl
2020-03-17 12:12:25 -04:00

277 lines
9.3 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 "mz.rkt" (for-label racket/syntax))
@title[#:tag "syntax-util"]{Syntax Utilities}
@(define the-eval (make-base-eval))
@(the-eval '(require racket/syntax))
@(the-eval '(require (for-syntax racket/base racket/syntax)))
@note-lib-only[racket/syntax]
@;{----}
@section{Creating formatted identifiers}
@defproc[(format-id [lctx (or/c syntax? #f)]
[fmt string?]
[v (or/c string? symbol? identifier? keyword? char? number?)] ...
[#:source src (or/c syntax? #f) #f]
[#:props props (or/c syntax? #f) #f]
[#:cert ignored (or/c syntax? #f) #f]
[#:subs? subs? boolean? #f]
[#:subs-intro subs-introducer
(-> syntax? syntax?)
(if (syntax-transforming?) syntax-local-introduce values)])
identifier?]{
Like @racket[format], but produces an identifier using @racket[lctx]
for the lexical context, @racket[src] for the source location, and
@racket[props] for the properties. An argument supplied with
@racket[#:cert] is ignored. (See @racket[datum->syntax].)
The format string must use only @litchar{~a} placeholders. Identifiers
in the argument list are automatically converted to symbols.
@examples[#:eval the-eval
(define-syntax (make-pred stx)
(syntax-case stx ()
[(make-pred name)
(format-id #'name "~a?" (syntax-e #'name))]))
(make-pred pair)
(eval:error (make-pred none-such))
(define-syntax (better-make-pred stx)
(syntax-case stx ()
[(better-make-pred name)
(format-id #'name #:source #'name
"~a?" (syntax-e #'name))]))
(eval:error (better-make-pred none-such))
]
(Scribble doesn't show it, but the DrRacket pinpoints the location of
the second error but not of the first.)
If @racket[subs?] is @racket[#t], then a @racket['sub-range-binders]
syntax property is added to the result that records the position of
each identifier in the @racket[v]s. The @racket[subs-intro] procedure
is applied to each identifier, and its result is included in the
sub-range binder record. This property value overrides a
@racket['sub-range-binders] property copied from @racket[props].
@examples[#:eval the-eval
(syntax-property (format-id #'here "~a/~a-~a" #'point 2 #'y #:subs? #t)
'sub-range-binders)
]
@history[#:changed "7.4.0.5" @elem{Added the @racket[#:subs?] and
@racket[#:subs-intro] arguments.}]
}
@defproc[(format-symbol [fmt string?]
[v (or/c string? symbol? identifier? keyword? char? number?)] ...)
symbol?]{
Like @racket[format], but produces a symbol. The format string must
use only @litchar{~a} placeholders. Identifiers in the argument list
are automatically converted to symbols.
@examples[#:eval the-eval
(format-symbol "make-~a" 'triple)
]
}
@;{----}
@section{Pattern variables}
@defform[(define/with-syntax pattern stx-expr)
#:contracts ([stx-expr syntax?])]{
Definition form of @racket[with-syntax]. That is, it matches the
syntax object result of @racket[stx-expr] against @racket[pattern] and
creates pattern variable definitions for the pattern variables of
@racket[pattern].
@examples[#:eval the-eval
(define/with-syntax (px ...) #'(a b c))
(define/with-syntax (tmp ...) (generate-temporaries #'(px ...)))
#'([tmp px] ...)
(define/with-syntax name #'Alice)
#'(hello name)
]
}
@;{----}
@section{Error reporting}
@defparam[current-syntax-context stx (or/c syntax? false/c)]{
The current contextual syntax object, defaulting to @racket[#f]. It
determines the special form name that prefixes syntax errors created
by @racket[wrong-syntax].
}
@defproc[(wrong-syntax [stx syntax?] [format-string string?] [v any/c] ...)
any]{
Raises a syntax error using the result of
@racket[(current-syntax-context)] as the ``major'' syntax object and
the provided @racket[stx] as the specific syntax object. (The latter,
@racket[stx], is usually the one highlighted by DrRacket.) The error
message is constructed using the format string and arguments, and it
is prefixed with the special form name as described under
@racket[current-syntax-context].
@examples[#:eval the-eval
(eval:error (wrong-syntax #'here "expected ~s" 'there))
(eval:error
(parameterize ([current-syntax-context #'(look over here)])
(wrong-syntax #'here "expected ~s" 'there)))
]
A macro using @racket[wrong-syntax] might set the syntax context at the very
beginning of its transformation as follows:
@RACKETBLOCK[
(define-syntax (my-macro stx)
(parameterize ([current-syntax-context stx])
(syntax-case stx ()
___)))
]
Then any calls to @racket[wrong-syntax] during the macro's
transformation will refer to @racket[my-macro] (more precisely, the name that
referred to @racket[my-macro] where the macro was used, which may be
different due to renaming, prefixing, etc).
}
@;{----}
@section{Recording disappeared uses}
@defparam[current-recorded-disappeared-uses ids
(or/c (listof identifier?) false/c)]{
Parameter for tracking disappeared uses. Tracking is ``enabled'' when
the parameter has a non-false value. This is done automatically by
forms like @racket[with-disappeared-uses].
}
@defform[(with-disappeared-uses body-expr ... stx-expr)
#:contracts ([stx-expr syntax?])]{
Evaluates the @racket[body-expr]s and @racket[stx-expr], catching identifiers
looked up using @racket[syntax-local-value/record]. Adds the caught identifiers
to the @racket['disappeared-uses] syntax property of the syntax object produced
by @racket[stx-expr].
@history[#:changed "6.5.0.7" @elem{Added the option to include @racket[body-expr]s.}]
}
@defproc[(syntax-local-value/record [id identifier?] [predicate (-> any/c boolean?)])
any/c]{
Looks up @racket[id] in the syntactic environment (as
@racket[syntax-local-value]). If the lookup succeeds and returns a
value satisfying the predicate, the value is returned and @racket[id]
is recorded as a disappeared use by calling @racket[record-disappeared-uses].
If the lookup fails or if the value
does not satisfy the predicate, @racket[#f] is returned and the
identifier is not recorded as a disappeared use.
}
@defproc[(record-disappeared-uses [id (or/c identifier? (listof identifier?))]
[intro? boolean? (syntax-transforming?)])
void?]{
Add @racket[id] to @racket[(current-recorded-disappeared-uses)]. If
@racket[id] is a list, perform the same operation on all the
identifiers. If @racket[intro?] is true, then
@racket[syntax-local-introduce] is first called on the identifiers.
If not used within the extent of a @racket[with-disappeared-uses]
form or similar, has no effect.
@history[#:changed "6.5.0.7"
@elem{Added the option to pass a single identifier instead of
requiring a list.}
#:changed "7.2.0.11"
@elem{Added the @racket[intro?] argument.}]
}
@;{----}
@section{Miscellaneous utilities}
@defproc[(generate-temporary [name-base any/c 'g]) identifier?]{
Generates one fresh identifier. Singular form of
@racket[generate-temporaries]. If @racket[name-base] is supplied, it
is used as the basis for the identifier's name.
}
@defproc[(internal-definition-context-apply [intdef-ctx internal-definition-context?]
[stx syntax?])
syntax?]{
Equivalent to @racket[(internal-definition-context-introduce intdef-ctx stx 'add)]. The
@racket[internal-definition-context-apply] function is provided for backwards compatibility; the more
general @racket[internal-definition-context-introduce] function is preferred.
}
@defproc[(syntax-local-eval [stx syntax?]
[intdef-ctx (or/c internal-definition-context?
(listof internal-definition-context?)
#f)
'()])
any]{
Evaluates @racket[stx] as an expression in the current @tech{transformer environment} (that is, at
@tech{phase level} 1). If @racket[intdef-ctx] is not @racket[#f], the value provided for
@racket[intdef-ctx] is used to enrich @racket[stx]s @tech{lexical information} and extend the
@tech{local binding context} in the same way as the fourth argument to @racket[local-expand].
@examples[#:eval the-eval
(define-syntax (show-me stx)
(syntax-case stx ()
[(show-me expr)
(begin
(printf "at compile time produces ~s\n"
(syntax-local-eval #'expr))
#'(printf "at run time produces ~s\n"
expr))]))
(show-me (+ 2 5))
(define-for-syntax fruit 'apple)
(define fruit 'pear)
(show-me fruit)
]
@history[
#:changed "6.90.0.27" @elem{Changed @racket[intdef-ctx] to accept a list of internal-definition
contexts in addition to a single internal-definition context or
@racket[#f].}]
}
@defform[(with-syntax* ([pattern stx-expr] ...)
body ...+)
#:contracts ([stx-expr syntax?])]{
Similar to @racket[with-syntax], but the pattern variables of each
@racket[pattern] are bound in the @racket[stx-expr]s of subsequent
clauses as well as the @racket[body]s, and the @racket[pattern]s need
not bind distinct pattern variables; later bindings shadow earlier
bindings.
@examples[#:eval the-eval
(with-syntax* ([(x y) (list #'val1 #'val2)]
[nest #'((x) (y))])
#'nest)
]
}
@close-eval[the-eval]