108 lines
4.2 KiB
Racket
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.
|