racket/collects/syntax/scribblings/parse/ex-varied.scrbl
2011-08-09 12:29:28 -06:00

108 lines
4.2 KiB
Racket

#lang scribble/doc
@(require scribble/manual
scribble/struct
scribble/decode
scribble/eval
"parse-common.rkt"
(for-label racket/class))
@title[#:tag "varied-meanings"]{Variants with Varied Meanings}
As explained in the @seclink["uniform-meanings"]{previous section},
the meaning of a syntax class can be uniform, or it can be varied;
that is, different instances of the syntax class can carry different
kinds of information. This section discusses the latter kind of syntax
class.
A good example of a syntax class with varied meanings is the
@racket[for-clause] of the @racket[for] family of special forms.
@racketgrammar[for-clause
[id seq-expr]
[(id ...) seq-expr]
(code:line #:when guard-expr)]
The first two variants carry the same kind of information; both
consist of identifiers to bind and a sequence expression. The third
variant, however, means something totally different: a condition that
determines whether to continue the current iteration of the loop, plus
a change in scoping for subsequent @racket[seq-expr]s. The information
of a @racket[for-clause] must be represented in a way that a client
macro can do further case analysis to distinguish the ``bind variables
from a sequence'' case from the ``skip or continue this iteration and
enter a new scope'' case.
This section discusses two ways of representing varied kinds of
information.
@section{Syntactic Normalization}
One approach is based on the observation that the syntactic variants
already constitute a representation of the information they carry. So
why not adapt that representation, removing redundancies and
eliminating simplifying the syntax to make subsequent re-parsing
trivial.
@racketblock[
(define-splicing-syntax-class for-clause
#:attribute (norm)
(pattern [var:id seq:expr]
#:with norm #'[(var) seq])
(pattern [(var:id ...) seq:expr]
#:with norm #'[(var ...) seq])
(pattern (~seq #:when guard:expr)
#:with norm #'[#:when guard]))
]
First, note that since the @racket[#:when] variant consists of two
separate terms, we define @racket[for-clause] as a splicing syntax
class. Second, that kind of irregularity is just the sort of thing
we'd like to remove so we don't have to deal with it again later. Thus
we represent the normalized syntax as a single term beginning with
either a sequence of identifiers (the first two cases) or the keyword
@racket[#:when] (the third case). The two normalized cases are easy to
process and easy to tell apart. We have also taken the opportunity to
desugar the first case into the second.
A normalized syntactic representation is most useful when the
subsequent case analysis is performed by @racket[syntax-parse] or a
similar form.
@section{Non-syntax-valued Attributes}
When the information carried by the syntax is destined for complicated
processing by Racket code, it is often better to parse it into an
intermediate representation using idiomatic Racket data structures,
such as lists, hashes, structs, and even objects.
Thus far we have only used syntax pattern variables and the
@racket[#:with] keyword to bind attribues, and the values of the
attributes have always been syntax. To bind attributes to values other
than syntax, use the @racket[#:attr] keyword.
@racketblock[
(code:comment "A ForClause is either")
(code:comment " - (bind-clause (listof identifier) syntax)")
(code:comment " - (when-clause syntax)")
(struct bind-clause (vars seq-expr))
(struct when-clause (guard))
(define-splicing-syntax-class for-clause
#:attributes (ast)
(pattern [var:id seq:expr]
#:attr ast (bind-clause (list #'var) #'seq))
(pattern [(var:id ...) seq:expr]
#:attr ast (bind-clause (syntax->list #'(var ...))
#'seq))
(pattern (~seq #:when guard:expr)
#:attr ast (when-clause #'guard)))
]
Be careful! If we had used @racket[#:with] instead of @racket[#:attr],
the @racket[#f] would have been coerced to a syntax object before
being matched against the pattern @racket[default].
Attributes with non-syntax values cannot be used in syntax
templates. Use the @racket[attribute] form to get the value of an
attribute.