add documentation for datum and syntax variables, attributes

This commit is contained in:
Ryan Culpepper 2020-02-25 18:19:33 +01:00
parent 07f9b843a6
commit 6f11f1f527
2 changed files with 173 additions and 50 deletions

View File

@ -2,7 +2,8 @@
@(require "common.rkt"
scribble/eval
(for-label racket/base
syntax/datum))
syntax/datum
syntax/parse))
@(define datum-eval (make-base-eval))
@interaction-eval[#:eval datum-eval (require syntax/datum)]
@ -32,23 +33,65 @@ in @racket[datum-case] should produce a @tech[#:doc refman]{datum}
(i.e., plain S-expression) instead of a @tech[#:doc refman]{syntax
object} to be matched in @racket[clause]s, and @racket[datum]
similarly produces a datum. Pattern variables bound in each
@racket[clause] of @racket[datum-case] are accessible via
@racket[datum] instead of @racket[syntax]. When a @racket[literal-id]
appears in a @racket[clause]'s pattern, it matches the corresponding
symbol (using @racket[eq?]).
@racket[clause] of @racket[datum-case] (or @racket[syntax-case], see
below) are accessible via @racket[datum] instead of
@racket[syntax]. When a @racket[literal-id] appears in a
@racket[clause]'s pattern, it matches the corresponding symbol (using
@racket[eq?]).
Using @racket[datum-case] and @racket[datum] is essentially equivalent
Using @racket[datum-case] and @racket[datum] is similar
to converting the input to @racket[syntax-case] using
@racket[datum->syntax] and then wrapping each use of @racket[syntax]
with @racket[syntax->datum], but @racket[datum-case] and
@racket[datum] to not create intermediate syntax objects.
@racket[datum] do not create intermediate syntax objects, and they do
not destroy existing syntax objects within the S-expression structure
of @racket[datum-expr].
@examples[
#:eval datum-eval
(datum-case '(1 "x" -> y) (->)
[(a ... -> b) (datum (b (+ a) ...))])
]}
]
The @racket[datum] form also cooperates with @tech[#:key "pattern
variable" #:doc '(lib "scribblings/reference/reference.scrbl")]{syntax
pattern variables} such as those bound by @racket[syntax-case] and
@tech{attributes} bound by @racket[syntax-parse] (see
@secref["stxparse-attrs"] for more information about attributes). As
one consequence, @racket[datum] provides a convenient way of getting
the list of syntax objects bound to a syntax pattern variable of depth
1. For example, the following expressions are equivalent, except that
the @racket[datum] version avoids creating and eliminating a
superfluous syntax object wrapper:
@interaction[#:eval datum-eval
(with-syntax ([(x ...) #'(a b c)])
(syntax->list #'(x ...)))
(with-syntax ([(x ...) #'(a b c)])
(datum (x ...)))
]
A template can also use multiple syntax or datum pattern variables and
datum constants, and it can use the @racket[~@] and @racket[~?]
template forms:
@interaction[#:eval datum-eval
(with-syntax ([(x ...) #'(a b c)])
(with-datum ([(y ...) (list 1 2 3)])
(datum ([x -> y] ...))))
(with-syntax ([(x ...) #'(a b c)])
(with-datum ([(y ...) (list 1 2 3)])
(datum ((~@ x y) ...))))
]
See @secref["attributes-and-datum"] for examples of @racket[~?] with
@racket[datum].
If a datum variable is used in a syntax template, a compile-time error
is raised.
@history[#:changed "7.8.0.9" @elem{Changed @racket[datum] to
cooperate with @racket[syntax-case], @racket[syntax-parse], etc.}]}
@defform[(with-datum ([pattern datum-expr] ...)

View File

@ -4,9 +4,11 @@
scribble/decode
scribble/eval
"parse-common.rkt"
(for-label syntax/datum)
(for-syntax racket/base))
@(define the-eval (make-sp-eval))
@(the-eval '(require syntax/datum))
@(define-syntax sub-kw-form
(lambda (stx)
@ -192,6 +194,28 @@ including nested attributes produced by syntax classes associated with
the pattern variables.
}
@defidform[this-syntax]{
When used as an expression within a syntax-class definition or
@racket[syntax-parse] expression, evaluates to the syntax object or
@tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{syntax
pair} being matched.
@examples[#:eval the-eval
(define-syntax-class one (pattern _ #:attr s this-syntax))
(syntax-parse #'(1 2 3) [(1 o:one _) (attribute o.s)])
(syntax-parse #'(1 2 3) [(1 . o:one) (attribute o.s)])
(define-splicing-syntax-class two (pattern (~seq _ _) #:attr s this-syntax))
(syntax-parse #'(1 2 3) [(t:two 3) (attribute t.s)])
(syntax-parse #'(1 2 3) [(1 t:two) (attribute t.s)])
]
Raises an error when used as an expression outside of a syntax-class
definition or @racket[syntax-parse] expression.
}
@defthing[prop:syntax-class (struct-type-property/c (or/c identifier?
(-> any/c identifier?)))]{
@ -484,28 +508,34 @@ variable}. The name of a nested attribute is computed by concatenating
the pattern variable name with the syntax class's exported attribute's
name, separated by a dot (see the example below).
Attributes can be used in two ways: with the @racket[attribute] form,
and inside syntax templates via @racket[syntax], @racket[quasisyntax],
etc. Attribute names cannot be used directly as expressions; that is,
attributes are not variables.
Attributes can be used in three ways: with the @racket[attribute]
form; inside syntax templates via @racket[syntax],
@racket[quasisyntax], etc; and inside @racket[datum]
templates. Attribute names cannot be used directly as expressions;
that is, attributes are not variables.
A @deftech{syntax-valued attribute} is an attribute whose value is a
syntax object, a syntax list of the appropriate @tech{ellipsis depth},
or a tree containing @tech[#:doc '(lib
syntax object or list of the appropriate @tech{ellipsis depth}. That
is, an attribute with ellipsis depth 0 is syntax-valued if its value
is @racket[syntax?]; an attribute with ellipis depth 1 is
syntax-valued if its value is @racket[(listof syntax?)]; an attribute
with ellipsis depth 2 is syntax-valued if its value is @racket[(listof
(listof syntax?))]; and so on. The value is considered syntax-valued
if it contains @tech[#:doc '(lib
"scribblings/reference/reference.scrbl")]{promises} that when
completely forced produces a suitable syntax object or syntax
completely forced produces a suitable syntax object or
list. Syntax-valued attributes can be used within @racket[syntax],
@racket[quasisyntax], etc as part of a syntax template. If an
attribute is used inside a syntax template but it is not
syntax-valued, an error is signaled.
The value of an attribute is not required to be syntax.
Non-syntax-valued attributes can be used to return a parsed
representation of a subterm or the results of an analysis on the
subterm. A non-syntax-valued attribute should be bound using the
@racket[#:attr] directive or a @racket[~bind] pattern; @racket[#:with]
and @racket[~parse] will convert the right-hand side to a (possibly
3D) syntax object.
There are uses for non-syntax-valued attributes. A non-syntax-valued
attribute can be used to return a parsed representation of a subterm
or the results of an analysis on the subterm. A non-syntax-valued
attribute must be bound using the @racket[#:attr] directive or a
@racket[~bind] pattern; @racket[#:with] and @racket[~parse] will
convert the right-hand side to a (possibly @tech[#:key "3d
syntax"]{3D}) syntax object.
@examples[#:eval the-eval
(define-syntax-class table
@ -525,10 +555,10 @@ and @racket[~parse] will convert the right-hand side to a (possibly
The @racket[table] syntax class provides four attributes:
@racket[key], @racket[value], @racket[hashtable], and
@racket[sorted-kv]. The @racket[hashtable] attribute has
@tech{ellipsis depth} 0 and the rest have depth 1; all but
@racket[hashtable] are syntax-valued. The @racket[sorted-kv]
attribute's value is a promise; it will be automatically forced if
used in a syntax template.
@tech{ellipsis depth} 0 and the rest have depth 1; @racket[key],
@racket[value], and @racket[sorted-kv] are syntax-valued, but
@racket[hashtable] is not. The @racket[sorted-kv] attribute's value is
a promise; it will be automatically forced if used in a template.
Syntax-valued attributes can be used in syntax templates:
@ -548,8 +578,8 @@ But non-syntax-valued attributes cannot:
#'t.hashtable])
]
Use the @racket[attribute] form to get the value of an attribute
(syntax-valued or not).
The @racket[attribute] form gets the value of an attribute, whether it
is syntax-valued or not.
@interaction[#:eval the-eval
(syntax-parse #'((a 1) (b 2) (c 3))
@ -589,10 +619,10 @@ binds the following nested attributes: @racket[y.a] at depth 2,
depth 1.
An attribute's ellipsis nesting depth is @emph{not} a guarantee that
it is syntax-valued. In particular, @racket[~or*] and
@racket[~optional] patterns may result in attributes with fewer than
expected levels of list nesting, and @racket[#:attr] and
@racket[~bind] can be used to bind attributes to arbitrary values.
it is syntax-valued or has any list structure. In particular,
@racket[~or*] and @racket[~optional] patterns may result in attributes
with fewer than expected levels of list nesting, and @racket[#:attr]
and @racket[~bind] can be used to bind attributes to arbitrary values.
@examples[#:eval the-eval
(syntax-parse #'(a b 3)
@ -603,27 +633,77 @@ expected levels of list nesting, and @racket[#:attr] and
@defform[(attribute attr-id)]{
Returns the value associated with the @tech{attribute} named
@racket[attr-id]. If @racket[attr-id] is not bound as an attribute, an
error is raised.
@racket[attr-id]. If @racket[attr-id] is not bound as an attribute, a
syntax error is raised.
}
@defidform[this-syntax]{
When used as an expression within a syntax-class definition or
@racket[syntax-parse] expression, evaluates to the syntax object or
@tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{syntax
pair} being matched.
@subsection[#:tag "attributes-and-datum"]{Attributes and @racket[datum]}
@examples[#:eval the-eval
(define-syntax-class one (pattern _ #:attr s this-syntax))
(syntax-parse #'(1 2 3) [(1 o:one _) (attribute o.s)])
(syntax-parse #'(1 2 3) [(1 . o:one) (attribute o.s)])
(define-splicing-syntax-class two (pattern (~seq _ _) #:attr s this-syntax))
(syntax-parse #'(1 2 3) [(t:two 3) (attribute t.s)])
(syntax-parse #'(1 2 3) [(1 t:two) (attribute t.s)])
]}
The @racket[datum] form is another way, in addition to @racket[syntax]
and @racket[attribute], of using syntax pattern variables and
attributes. Unlike @racket[syntax], @racket[datum] does not require
attributes to be syntax-valued. Wherever the @racket[syntax] form
would create syntax objects based on its template (as opposed to
reusing syntax objects bound by pattern variables), the @racket[datum]
form creates plain S-expressions.
Raises an error when used as an expression outside of a syntax-class
definition or @racket[syntax-parse] expression.
Continuing the @racket[table] example from above, we can use
@racket[datum] with the @racket[key] attribute as follows:
@interaction[#:eval the-eval
(syntax-parse #'((a 1) (b 2) (c 3))
[t:table (datum (t.key ...))])
]
A @racket[datum] template may contain multiple pattern variables
combined within some S-expression structure:
@interaction[#:eval the-eval
(syntax-parse #'((a 1) (b 2) (c 3))
[t:table (datum ([t.key t.value] ...))])
]
A @racket[datum] template can use the @racket[~@] and @racket[~?]
template forms:
@interaction[#:eval the-eval
(syntax-parse #'((a 1) (b 2) (c 3))
[t:table (datum ((~@ t.key t.value) ...))])
(syntax-parse #'((a 56) (b 71) (c 13))
[t:table (datum ((~@ . t.sorted-kv) ...))])
(syntax-parse #'( ((a 1) (b 2) (c 3)) ((d 4) (e 5)) )
[(t1:table (~or* t2:table #:nothing))
(datum (t1.key ... (~? (~@ t2.key ...))))])
(syntax-parse #'( ((a 1) (b 2) (c 3)) #:nothing )
[(t1:table (~or* t2:table #:nothing))
(datum (t1.key ... (~? (~@ t2.key ...))))])
]
However, unlike for @racket[syntax], a value of @racket[#f] only
signals a template failure to @racket[~?] if a list is needed for
ellipsis iteration, as in the previous example; it does not cause a
failure when it occurs as a leaf. Contrast the following:
@interaction[#:eval the-eval
(syntax-parse #'( ((a 1) (b 2) (c 3)) #:nothing )
[(t1:table (~or* t2:table #:nothing))
#'(~? t2 skipped)])
(syntax-parse #'( ((a 1) (b 2) (c 3)) #:nothing )
[(t1:table (~or* t2:table #:nothing))
(datum (~? t2 skipped))])
]
The @racket[datum] form is also useful for accessing non-syntax-valued
attributes. Compared to @racket[attribute], @racket[datum] has the
following advantage: The use of ellipses in @racket[datum] templates
provides a visual reminder of the list structure of their results. For
example, if the pattern is @racket[(t:table ...)], then both
@racket[(attribute t.hashtable)] and @racket[(datum (t.hashtable
...))] produce a @racket[(listof hash?)], but the ellipses make it
more apparent.
@history[#:changed "7.8.0.9" @elem{Added support for syntax pattern
variables and attributes to @racket[datum].}]
@(close-eval the-eval)