racket/collects/syntax/scribblings/parse/ex-varied.scrbl
Ryan Culpepper d7a87c79e0 Merged changes to syntax/parse
Changed backtracking algorithm, runtime representations
    - syntax classes, ~describe no longer implicitly commit
    - ~describe no longer delimits effect of cut
  Added keyword & optional args for stxclasses
  Added ~do and #:do, ~post, ~commit and #:commit,
    ~delimit-cut and #:no-delimit-cut
  Added syntax/parse/debug, syntax/parse/experimental/*
    - expr/c for contracting macro sub-expressions
      moved from syntax/parse to syntax/parse/experimental/contract
    - syntax class reflection (~reflect, ~splicing-reflect)
    - eh-alternative-sets (~eh-var)
    - provide-syntax-class/contract
      (only for params, not attrs so far)
  Changed ~fail to not include POST progress (#:fail still does)
    old (~fail _) is now (~post (~fail _))
  Made msg argument of ~fail optional
  Removed generic "repetition constraint violated" msg
  Removed atom-in-list stxclass
  Removed unnecessary datum->syntax on cdr of pair pattern
    massive improvements to long-list microbenchmarks
  Optimization: integrable syntax classes (id, expr, keyword)
    need better measurements
  Optimization: ad hoc elimination of head/tail choice point
    for (EH ... . ()) patterns
  Added unstable/wrapc (proc version of expr/c)
2010-08-31 10:55:58 -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
@scheme[for-clause] of the @scheme[for] family of special forms.
@schemegrammar[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 @scheme[seq-expr]s. The information
of a @scheme[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.
@schemeblock[
(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 @scheme[#:when] variant consists of two
separate terms, we define @scheme[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
@scheme[#: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 @scheme[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
@scheme[#: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 @scheme[#:attr] keyword.
@schemeblock[
(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 @scheme[#:with] instead of @scheme[#:attr],
the @scheme[#f] would have been coerced to a syntax object before
being matched against the pattern @scheme[default].
Attributes with non-syntax values cannot be used in syntax
templates. Use the @scheme[attribute] form to get the value of an
attribute.