racket/collects/syntax/scribblings/parse/patterns.scrbl
2012-06-11 13:07:53 -06:00

1012 lines
33 KiB
Racket

#lang scribble/doc
@(require scribble/manual
scribble/struct
scribble/decode
scribble/eval
"parse-common.rkt")
@(define-syntax-rule (define-dotsplus-names dotsplus def-dotsplus)
(begin (require (for-label (only-in syntax/parse ...+)))
(define dotsplus (racket ...+))
(define def-dotsplus (defhere ...+))))
@(define-dotsplus-names dotsplus def-dotsplus)
@title[#:tag "stxparse-patterns"]{Syntax Patterns}
The grammar of @deftech{syntax patterns} used by
@racketmodname[syntax/parse] facilities is given in the following
table. There are four main kinds of syntax pattern:
@itemize[
@item{@tech{@Spatterns}, abbreviated @svar[S-pattern]}
@item{@tech{@Hpatterns}, abbreviated @svar[H-pattern]}
@item{@tech{@EHpatterns}, abbreviated @svar[EH-pattern]}
@item{@tech{@Apatterns}, abbreviated @svar[A-pattern]}
]
A fifth kind, @tech{@Lpatterns} (abbreviated
@svar[L-pattern]), is a just a syntactically restricted subset of
@tech{@Spatterns}.
When a special form in this manual refers to @svar[syntax-pattern]
(eg, the description of the @racket[syntax-parse] special form), it
means specifically @tech{@Spattern}.
@racketgrammar*[#:literals (_ ~var ~literal ~or ~and ~not ~rest ~datum
~describe ~seq ~optional ~rep ~once ~between
~! ~bind ~fail ~parse ~peek ~peek-not ~do)
[S-pattern
pvar-id
pvar-id:syntax-class-id
literal-id
(@#,ref[~var s-] id)
(@#,ref[~var s+] id syntax-class-id maybe-role)
(@#,ref[~var s+] id (syntax-class-id arg ...) maybe-role)
(~literal literal-id)
atomic-datum
(~datum datum)
(H-pattern . S-pattern)
(A-pattern . S-pattern)
(EH-pattern #,ellipses . S-pattern)
(H-pattern @#,dotsplus . S-pattern)
(@#,ref[~and s] proper-S/A-pattern ...+)
(@#,ref[~or s] S-pattern ...+)
(~not S-pattern)
#((unsyntax @svar[pattern-part]) ...)
#s(prefab-struct-key (unsyntax @svar[pattern-part]) ...)
#&@#,svar[S-pattern]
(~rest S-pattern)
(@#,ref[~describe s] maybe-opaque maybe-role expr S-pattern)
(@#,ref[~commit s] S-pattern)
(@#,ref[~delimit-cut s] S-pattern)
A-pattern]
[L-pattern
()
(A-pattern . L-pattern)
(H-pattern . L-pattern)
(EH-pattern #,ellipses . L-pattern)
(H-pattern @#,dotsplus . L-pattern)
(~rest L-pattern)]
[H-pattern
pvar-id:splicing-syntax-class-id
(@#,ref[~var h] id splicing-syntax-class-id maybe-role)
(@#,ref[~var h] id (splicing-syntax-class-id arg ...)
maybe-role)
(~seq . L-pattern)
(@#,ref[~and h] proper-H/A-pattern ...+)
(@#,ref[~or h] H-pattern ...+)
(@#,ref[~optional h] H-pattern maybe-optional-option)
(@#,ref[~describe h] maybe-opaque maybe-role expr H-pattern)
(@#,ref[~commit h] H-pattern)
(@#,ref[~delimit-cut h] H-pattern)
(~peek H-pattern)
(~peek-not H-pattern)
proper-S-pattern]
[EH-pattern
(@#,ref[~or eh] EH-pattern ...)
(~once H-pattern once-option ...)
(@#,ref[~optional eh] H-pattern optional-option ...)
(~between H min-number max-number between-option)
H-pattern]
[A-pattern
~!
(~bind [attr-arity-decl expr] ...)
(~fail maybe-fail-condition maybe-message-expr)
(~parse S-pattern stx-expr)
(@#,ref[~and a] A-pattern ...+)
(~do defn-or-expr ...)]
[proper-S-pattern
#, @elem{a @svar{S-pattern} that is not a @svar{A-pattern}}]
[proper-H-pattern
#, @elem{a @svar{H-pattern} that is not a @svar{S-pattern}}]]
The following pattern keywords can be used in multiple pattern
variants:
@defidform[~var]{
One of @ref[~var s-], @ref[~var s+], or @ref[~var h].
}
@defidform[~and]{
One of @ref[~and s], @ref[~and h], or @ref[~and a]:
@itemize[
@item{@ref[~and a] if all of the conjuncts are @tech{@Apatterns}}
@item{@ref[~and h] if any of the conjuncts is a @tech{proper
@Hpattern}}
@item{@ref[~and s] otherwise}
]
}
@defidform[~or]{
One of @ref[~or s], @ref[~or h], or @ref[~or eh]:
@itemize[
@item{@ref[~or eh] if the pattern occurs directly before ellipses
(@ellipses) or immediately within another @ref[~or eh] pattern}
@item{@ref[~or h] if any of the disjuncts is a @tech{proper @Hpattern}}
@item{@ref[~or s] otherwise}
]
}
@defidform[~describe]{
One of @ref[~describe s] or @ref[~describe h]:
@itemize[
@item{@ref[~describe h] if the subpattern is a @tech{proper @Hpattern}}
@item{@ref[~describe s] otherwise}
]
}
@defidform[~commit]{
One of @ref[~commit s] or @ref[~commit h]:
@itemize[
@item{@ref[~commit h] if the subpattern is a @tech{proper @Hpattern}}
@item{@ref[~commit s] otherwise}
]
}
@defidform[~delimit-cut]{
One of @ref[~delimit-cut s] or @ref[~delimit-cut h]:
@itemize[
@item{@ref[~delimit-cut h] if the subpattern is a @tech{proper @Hpattern}}
@item{@ref[~delimit-cut s] otherwise}
]
}
@defidform[~optional]{
One of @ref[~optional h] or @ref[~optional eh]:
@itemize[
@item{@ref[~optional eh] if it is an immediate disjunct of a @ref[~or
eh] pattern}
@item{@ref[~optional h] otherwise}
]
}
@;{--------}
@section{Single-term Patterns}
A @deftech{@Spattern} (abbreviated @svar[S-pattern]) is a pattern that
describes a single term. These are like the traditional patterns used
in @racket[syntax-rules] and @racket[syntax-case], but with additional
variants that make them more expressive.
``Single-term'' does not mean ``atomic''; a @Spattern can have
complex structure, and it can match terms that have many parts. For
example, @racket[(17 ...)] is a @Spattern that matches any
term that is a proper list of repeated @racketresult[17] numerals.
A @deftech{proper @Spattern} is one that is not an @tech{@Apattern}.
The @deftech{@Lpatterns} (for ``list pattern'') are @Spatterns
having a restricted structure that guarantees that they match only
terms that are proper lists.
Here are the variants of @elem{@Spattern}:
@specsubform[id]{
An identifier can be either a @tech{pattern variable}, an
@tech{annotated pattern variable}, or a @tech{literal}:
@itemize[
@item{If @racket[id] is the ``pattern'' name of an entry in the
literals list, it is a @tech{literal} pattern that behaves
like @racket[(~literal id)].
@myexamples[
(syntax-parse #'(define x 12)
#:literals (define)
[(define var:id body:expr) 'ok])
(syntax-parse #'(lambda x 12)
#:literals (define)
[(define var:id body:expr) 'ok])
(syntax-parse #'(define x 12)
#:literals ([def define])
[(def var:id body:expr) 'ok])
(syntax-parse #'(lambda x 12)
#:literals ([def define])
[(def var:id body:expr) 'ok])
]
}
@item{If @racket[id] is of the form @racket[_pvar-id:syntax-class-id]
(that is, two names joined by a colon character), it is an
@tech{annotated pattern variable}, and the pattern is equivalent to
@racket[(~var pvar-id syntax-class-id)].
@myexamples[
(syntax-parse #'a
[var:id (syntax-e #'var)])
(syntax-parse #'12
[var:id (syntax-e #'var)])
(define-syntax-class two
#:attributes (x y)
(pattern (x y)))
(syntax-parse #'(a b)
[t:two (syntax->datum #'(t t.x t.y))])
(syntax-parse #'(a b)
[t
#:declare t two
(syntax->datum #'(t t.x t.y))])
]
}
@item{Otherwise, @racket[id] is a @tech{pattern variable}, and the
pattern is equivalent to @racket[(~var id)].
}
]
}
@specsubform[(@#,def[~var s-] pvar-id)]{
A @deftech{pattern variable}. If @racket[pvar-id] has no syntax class
(by @racket[#:convention]), the pattern variable matches anything. The
pattern variable is bound to the matched subterm, unless the pattern
variable is the wildcard (@racket[_]), in which case no binding
occurs.
If @racket[pvar-id] does have an associated syntax class, it behaves
like an @tech{annotated pattern variable} with the implicit syntax
class inserted.
}
@specsubform/subs[(@#,def[~var s+] pvar-id syntax-class-use maybe-role)
([syntax-class-use syntax-class-id
(syntax-class-id arg ...)]
[maybe-role (code:line)
(code:line #:role role-expr)])
#:contracts ([role-expr (or/c string? #f)])]{
An @deftech{annotated pattern variable}. The pattern matches only
terms accepted by @svar[syntax-class-id] (parameterized by the
@racket[arg-expr]s, if present).
In addition to binding @svar[pvar-id], an annotated pattern
variable also binds @deftech{nested attributes} from the syntax
class. The names of the nested attributes are formed by prefixing
@svar[pvar-id.] (that is, @svar[pvar-id] followed by a ``dot''
character) to the name of the syntax class's attribute.
If @svar[pvar-id] is @racket[_], no attributes are bound.
If @racket[role-expr] is given and evaluates to a string, it is
combined with the syntax class's description in error messages.
@myexamples[
(syntax-parse #'a
[(~var var id) (syntax-e #'var)])
(syntax-parse #'12
[(~var var id) (syntax-e #'var)])
(define-syntax-class two
#:attributes (x y)
(pattern (x y)))
(syntax-parse #'(a b)
[(~var t two) (syntax->datum #'(t t.x t.y))])
(define-syntax-class (nat-less-than n)
(pattern x:nat #:when (< (syntax-e #'x) n)))
(syntax-parse #'(1 2 3 4 5)
[((~var small (nat-less-than 4)) ... large:nat ...)
(list #'(small ...) #'(large ...))])
(syntax-parse #'(m a b 3)
[(_ (~var x id #:role "variable") ...) 'ok])
]
}
@specsubform[(@#,defhere[~literal] literal-id)]{
A @deftech{literal} identifier pattern. Matches any identifier
@racket[free-identifier=?] to @racket[literal-id].
@myexamples[
(syntax-parse #'(define x 12)
[((~literal define) var:id body:expr) 'ok])
(syntax-parse #'(lambda x 12)
[((~literal define) var:id body:expr) 'ok])
]
}
@specsubform[atomic-datum]{
Numbers, strings, booleans, keywords, and the empty list match as
literals.
@myexamples[
(syntax-parse #'(a #:foo bar)
[(x #:foo y) (syntax->datum #'y)])
(syntax-parse #'(a foo bar)
[(x #:foo y) (syntax->datum #'y)])
]
}
@specsubform[(@#,defhere[~datum] datum)]{
Matches syntax whose S-expression contents (obtained by
@racket[syntax->datum]) is @racket[equal?] to the given
@racket[datum].
@myexamples[
(syntax-parse #'(a #:foo bar)
[(x (~datum #:foo) y) (syntax->datum #'y)])
(syntax-parse #'(a foo bar)
[(x (~datum #:foo) y) (syntax->datum #'y)])
]
The @racket[~datum] form is useful for recognizing identifiers
symbolically, in contrast to the @racket[~literal] form, which
recognizes them by binding.
@myexamples[
(syntax-parse (let ([define 'something-else]) #'(define x y))
[((~datum define) var:id e:expr) 'yes]
[_ 'no])
(syntax-parse (let ([define 'something-else]) #'(define x y))
[((~literal define) var:id e:expr) 'yes]
[_ 'no])
]
}
@specsubform[(H-pattern . S-pattern)]{
Matches any term that can be decomposed into a list prefix matching
@racket[H-pattern] and a suffix matching @racket[S-pattern].
Note that the pattern may match terms that are not even improper
lists; if the head pattern can match a zero-length head, then the
whole pattern matches whatever the tail pattern accepts.
The first pattern can be a @tech{@Spattern}, in which case the whole
pattern matches any pair whose first element matches the first pattern
and whose rest matches the second.
See @tech{@Hpatterns} for more information.
}
@specsubform[(A-pattern . S-pattern)]{
Performs the actions specified by @racket[A-pattern], then matches
any term that matches @racket[S-pattern].
Pragmatically, one can throw an @tech{@Apattern} into any list
pattern. Thus, @racket[(x y z)] is a pattern matching a list of three
terms, and @racket[(x y ~! z)] is a pattern matching a list of three
terms, with a @tech{cut} performed after the second one. In other
words, @Apatterns ``don't take up space.''
See @tech{@Apatterns} for more information.
}
@specsubform[(EH-pattern #,ellipses . S-pattern)]{
Matches any term that can be decomposed into a list head matching some
number of repetitions of the @racket[EH-pattern] alternatives (subject
to its repetition constraints) followed by a list tail matching
@racket[S-pattern].
In other words, the whole pattern matches either the second pattern
(which need not be a list) or a term whose head matches one of the
alternatives of the first pattern and whose tail recursively matches
the whole sequence pattern.
See @tech{@EHpatterns} for more information.
}
@specsubform[(H-pattern @#,def-dotsplus . S-pattern)]{
Like an ellipses (@ellipses) pattern, but requires at least one occurrence
of the head pattern to be present.
That is, the following patterns are equivalent:
@itemize[
@item[@racket[(H @#,dotsplus . S)]]
@item[@racket[((~between H 1 +inf.0) ... . S)]]
]
@myexamples[
(syntax-parse #'(1 2 3)
[(n:nat ...+) 'ok])
(syntax-parse #'()
[(n:nat ...+) 'ok]
[_ 'none])
]
}
@specsubform[(@#,def[~and s] S/A-pattern ...)]{
Matches any term that matches all of the subpatterns.
The subpatterns can contain a mixture of @tech{@Spatterns} and
@tech{@Apatterns}, but must contain at least one @tech{@Spattern}.
Attributes bound in subpatterns are available to subsequent
subpatterns. The whole pattern binds all of the subpatterns'
attributes.
One use for @racket[~and]-patterns is preserving a whole
term (including its lexical context, source location, etc) while also
examining its structure. Syntax classes are useful for the same
purpose, but @racket[~and] can be lighter weight.
@myexamples[
(define-syntax (import stx)
(raise-syntax-error #f "illegal use of import" stx))
(eval:alts
(define (check-imports stx) ....)
(define (check-imports . _) #f))
(syntax-parse #'(m (import one two))
#:literals (import)
[(_ (~and import-clause (import i ...)))
(let ([bad (check-imports
(syntax->list #'(i ...)))])
(when bad
(raise-syntax-error
#f "bad import" #'import-clause bad))
'ok)])
]
}
@specsubform[(@#,def[~or s] S-pattern ...)]{
Matches any term that matches one of the included patterns. The
alternatives are tried in order.
The whole pattern binds @emph{all} of the subpatterns' attributes. An
attribute that is not bound by the ``chosen'' subpattern has a value
of @racket[#f]. The same attribute may be bound by multiple
subpatterns, and if it is bound by all of the subpatterns, it is sure
to have a value if the whole pattern matches.
@myexamples[
(syntax-parse #'a
[(~or x:id y:nat) (values (attribute x) (attribute y))])
(syntax-parse #'(a 1)
[(~or (x:id y:nat) (x:id)) (values #'x (attribute y))])
(syntax-parse #'(b)
[(~or (x:id y:nat) (x:id)) (values #'x (attribute y))])
]
}
@specsubform[(@#,defhere[~not] S-pattern)]{
Matches any term that does not match the subpattern. None of the
subpattern's attributes are bound outside of the
@racket[~not]-pattern.
@myexamples[
(syntax-parse #'(x y z => u v)
#:literals (=>)
[((~and before (~not =>)) ... => after ...)
(list #'(before ...) #'(after ...))])
]
}
@specsubform[#(#, @svar[pattern-part] ...)]{
Matches a term that is a vector whose elements, when considered as a
list, match the @tech{@Spattern} corresponding to
@racket[(pattern-part ...)].
@myexamples[
(syntax-parse #'#(1 2 3)
[#(x y z) (syntax->datum #'z)])
(syntax-parse #'#(1 2 3)
[#(x y ...) (syntax->datum #'(y ...))])
(syntax-parse #'#(1 2 3)
[#(x ~rest y) (syntax->datum #'y)])
]
}
@specsubform[#s(prefab-struct-key #, @svar[pattern-part] ...)]{
Matches a term that is a prefab struct whose key is exactly the given
key and whose sequence of fields, when considered as a list, match the
@tech{@Spattern} corresponding to @racket[(pattern-part ...)].
@myexamples[
(syntax-parse #'#s(point 1 2 3)
[#s(point x y z) 'ok])
(syntax-parse #'#s(point 1 2 3)
[#s(point x y ...) (syntax->datum #'(y ...))])
(syntax-parse #'#s(point 1 2 3)
[#s(point x ~rest y) (syntax->datum #'y)])
]
}
@specsubform[#&@#,svar[S-pattern]]{
Matches a term that is a box whose contents matches the inner
@tech{@Spattern}.
@myexamples[
(syntax-parse #'#&5
[#&n:nat 'ok])
]
}
@specsubform[(#, @defhere[~rest] S-pattern)]{
Matches just like @racket[S-pattern]. The @racket[~rest] pattern form
is useful in positions where improper (``dotted'') lists are not
allowed by the reader, such as vector and structure patterns (see
above).
@myexamples[
(syntax-parse #'(1 2 3)
[(x ~rest y) (syntax->datum #'y)])
(syntax-parse #'#(1 2 3)
[#(x ~rest y) (syntax->datum #'y)])
]
}
@specsubform/subs[(@#,def[~describe s] maybe-opaque expr S-pattern)
([maybe-opaque (code:line)
(code:line #:opaque)]
[maybe-role (code:line)
(code:line #:role role-expr)])
#:contracts ([expr (or/c string? #f)]
[role-expr (or/c string? #f)])]{
The @racket[~describe] pattern form annotates a pattern with a
description, a string expression that is evaluated in the scope of all
prior attribute bindings. If parsing the inner pattern fails, then the
description is used to synthesize the error message. A
@racket[~describe] pattern does not influence backtracking.
If @racket[#:opaque] is given, failure information from within
@racket[S-pattern] is discarded and the error is reported solely in
terms of the description given.
If @racket[role-expr] is given and produces a string, its value is
combined with the description in error messages.
@myexamples[
(syntax-parse #'(m 1)
[(_ (~describe "id pair" (x:id y:id))) 'ok])
(syntax-parse #'(m (a 2))
[(_ (~describe "id pair" (x:id y:id))) 'ok])
(syntax-parse #'(m (a 2))
[(_ (~describe #:opaque "id pair" (x:id y:id))) 'ok])
(syntax-parse #'(m 1)
[(_ (~describe #:role "formals" "id pair" (x y))) 'ok])
]
}
@specsubform[(@#,def[~commit s] S-pattern)]{
The @racket[~commit] pattern form affects backtracking in two ways:
@itemize[
@item{If the pattern succeeds, then all choice points created within
the subpattern are discarded, and a failure @emph{after} the
@racket[~commit] pattern backtracks only to choice points
@emph{before} the @racket[~commit] pattern, never one @emph{within}
it.}
@item{A cut (@racket[~!]) within a @racket[~commit] pattern only
eliminates choice-points created within the @racket[~commit]
pattern. In this sense, it acts just like @racket[~delimit-cut].}
]
}
@specsubform[(@#,def[~delimit-cut s] S-pattern)]{
The @racket[~delimit-cut] pattern form affects backtracking in the
following way:
@itemize[
@item{A cut (@racket[~!]) within a @racket[~delimit-cut] pattern only
eliminates choice-points created within the @racket[~delimit-cut]
pattern.}
]
}
@specsubform[A-pattern]{
An @tech{@Apattern} is considered a @Spattern when there is no
ambiguity; it matches any term.
}
@section{Head Patterns}
A @deftech{@Hpattern} (abbreviated @svar[H-pattern]) is a pattern that
describes some number of terms that occur at the head of some list
(possibly an improper list). A @Hpattern's usefulness comes from being
able to match heads of different lengths, such as optional forms like
keyword arguments.
A @deftech{proper @Hpattern} is a @Hpattern that is not a @elem{@Spattern}.
Here are the variants of @elem{@Hpattern}:
@specsubform[pvar-id:splicing-syntax-class-id]{
Equivalent to @racket[(~var pvar-id splicing-syntax-class-id)].
}
@specsubform/subs[(@#,def[~var h] pvar-id splicing-syntax-class-use maybe-role)
([splicing-syntax-class-use splicing-syntax-class-id
(splicing-syntax-class-id arg ...)]
[maybe-role (code:line)
(code:line #:role role-expr)])
#:contracts ([role-expr (or/c string? #f)])]{
Pattern variable annotated with a @tech{splicing syntax
class}. Similar to a normal @tech{annotated pattern variable}, except
matches a head pattern.
}
@specsubform[(@#,defhere[~seq] . L-pattern)]{
Matches a sequence of terms whose elements, if put in a list, would
match @racket[L-pattern].
@myexamples[
(syntax-parse #'(1 2 3 4)
[((~seq 1 2 3) 4) 'ok])
]
See also the section on @tech{@EHpatterns} for more interesting
examples of @racket[~seq].
}
@specsubform[(@#,def[~and h] H-pattern ...)]{
Like the @Spattern version, @ref[~and s], but matches a sequence of
terms instead.
@myexamples[
(syntax-parse #'(#:a 1 #:b 2 3 4 5)
[((~and (~seq (~seq k:keyword e:expr) ...)
(~seq keyword-stuff ...))
positional-stuff ...)
(syntax->datum #'((k ...) (e ...) (keyword-stuff ...)))])
]
The @Hpattern variant of @racket[~and] requires that all of the
subpatterns be @tech{proper @Hpatterns} (not @tech{@Spatterns}). This
is to prevent typos like the following, a variant of the previous
example with the second @racket[~seq] omitted:
@myexamples[
(syntax-parse #'(#:a 1 #:b 2 3 4 5)
[((~and (~seq (~seq k:keyword e:expr) ...)
(keyword-stuff ...))
positional-stuff ...)
(syntax->datum #'((k ...) (e ...) (keyword-stuff ...)))])
(code:comment "If the example above were allowed, it would be equivalent to this:")
(syntax-parse #'(#:a 1 #:b 2 3 4 5)
[((~and (~seq (~seq k:keyword e:expr) ...)
(~seq (keyword-stuff ...)))
positional-stuff ...)
(syntax->datum #'((k ...) (e ...) (keyword-stuff ...)))])
]
}
@specsubform[(@#,def[~or h] H-pattern ...)]{
Like the @Spattern version, @ref[~or s], but matches a sequence of
terms instead.
@myexamples[
(syntax-parse #'(m #:foo 2 a b c)
[(_ (~or (~seq #:foo x) (~seq)) y:id ...)
(attribute x)])
(syntax-parse #'(m a b c)
[(_ (~or (~seq #:foo x) (~seq)) y:id ...)
(attribute x)])
]
}
@specsubform/subs[(@#,def[~optional h] H-pattern maybe-optional-option)
([maybe-optional-option
(code:line)
(code:line #:defaults ([attr-arity-decl expr] ...))]
[attr-arity-decl
attr-id
(attr-id depth)])]{
Matches either the given head subpattern or an empty sequence of
terms. If the @racket[#:defaults] option is given, the subsequent
attribute bindings are used if the subpattern does not match. The
default attributes must be a subset of the subpattern's attributes.
@myexamples[
(syntax-parse #'(m #:foo 2 a b c)
[(_ (~optional (~seq #:foo x) #:defaults ([x #'#f])) y:id ...)
(attribute x)])
(syntax-parse #'(m a b c)
[(_ (~optional (~seq #:foo x) #:defaults ([x #'#f])) y:id ...)
(attribute x)])
(syntax-parse #'(m a b c)
[(_ (~optional (~seq #:foo x)) y:id ...)
(attribute x)])
(syntax-parse #'(m #:syms a b c)
[(_ (~optional (~seq #:nums n:nat ...) #:defaults ([(n 1) null]))
(~optional (~seq #:syms s:id ...) #:defaults ([(s 1) null])))
#'((n ...) (s ...))])
]
}
@specsubform[(@#,def[~describe h] expr H-pattern)]{
Like the @Spattern version, @ref[~describe s], but matches a head
pattern instead.
}
@specsubform[(@#,def[~commit h] H-pattern)]{
Like the @Spattern version, @ref[~commit s], but matches a head
pattern instead.
}
@specsubform[(@#,def[~delimit-cut h] H-pattern)]{
Like the @Spattern version, @ref[~delimit-cut s], but matches a head
pattern instead.
}
@specsubform[(@#,defhere[~peek] H-pattern)]{
Matches the @racket[H-pattern] but then resets the matching position,
so the @racket[~peek] pattern consumes no input. Used to look ahead in
a sequence.
@examples[#:eval the-eval
(define-splicing-syntax-class nf-id (code:comment "non-final id")
(pattern (~seq x:id (~peek another:id))))
(syntax-parse #'(a b c 1 2 3)
[(n:nf-id ... rest ...)
(printf "nf-ids are ~s\n" (syntax->datum #'(n.x ...)))
(printf "rest is ~s\n" (syntax->datum #'(rest ...)))])
]
}
@specsubform[(@#,defhere[~peek-not] H-pattern)]{
Like @racket[~peek], but succeeds if the subpattern fails and fails if
the subpattern succeeds. On success, the @racket[~peek-not] resets the
matching position, so the pattern consumes no input. Used to look
ahead in a sequence. None of the subpattern's attributes are bound
outside of the @racket[~peek-not]-pattern.
@myexamples[
(define-splicing-syntax-class final (code:comment "final term")
(pattern (~seq x (~peek-not _))))
(syntax-parse #'(a b c)
[((~or f:final other) ...)
(printf "finals are ~s\n" (syntax->datum #'(f.x ...)))
(printf "others are ~s\n" (syntax->datum #'(other ...)))])
]
}
@specsubform[S-pattern]{
Matches a sequence of one element, which must be a term matching
@racket[S-pattern].
}
@;{--------}
@section{Ellipsis-head Patterns}
An @deftech{@EHpattern} (abbreviated @svar[EH-pattern]) is pattern
that describes some number of terms, like a @tech{@Hpattern}, but also
places constraints on the number of times it occurs in a
repetition. They are useful for matching, for example, keyword
arguments where the keywords may come in any order. Multiple
alternatives are grouped together via @ref[~or eh].
@myexamples[
(define parser1
(syntax-parser
[((~or (~once (~seq #:a x) #:name "#:a keyword")
(~optional (~seq #:b y) #:name "#:b keyword")
(~seq #:c z)) ...)
'ok]))
(parser1 #'(#:a 1))
(parser1 #'(#:b 2 #:c 3 #:c 25 #:a 'hi))
(parser1 #'(#:a 1 #:a 2))
]
The pattern requires exactly one occurrence of the @racket[#:a]
keyword and argument, at most one occurrence of the @racket[#:b]
keyword and argument, and any number of @racket[#:c] keywords and
arguments. The ``pieces'' can occur in any order.
Here are the variants of @elem{@EHpattern}:
@specsubform[(@#,def[~or eh] EH-pattern ...)]{
Matches if any of the inner @racket[EH-pattern] alternatives match.
}
@specsubform/subs[(@#,defhere[~once] H-pattern once-option ...)
([once-option (code:line #:name name-expr)
(code:line #:too-few too-few-message-expr)
(code:line #:too-many too-many-message-expr)])
#:contracts ([name-expr (or/c string? #f)]
[too-few-message-expr (or/c string? #f)]
[too-many-message-expr (or/c string? #f)])]{
Matches if the inner @racket[H-pattern] matches. This pattern must be
matched exactly once in the match of the entire repetition sequence.
If the pattern is not matched in the repetition sequence, then the
ellipsis pattern fails with the message either
@racket[too-few-message-expr] or @racketvalfont{"missing required
occurrence of @racket[name-expr]"}.
If the pattern is chosen more than once in the repetition sequence,
then the ellipsis pattern fails with the message either
@racket[too-many-message-expr] or @racketvalfont{"too many occurrences
of @racket[name-expr]"}.
}
@specsubform/subs[(@#,def[~optional eh] H-pattern optional-option ...)
([optional-option (code:line #:name name-expr)
(code:line #:too-many too-many-message-expr)
(code:line #:defaults ([attr-id expr] ...))])
#:contracts ([name-expr (or/c string? #f)]
[too-many-message-expr (or/c string? #f)])]{
Matches if the inner @racket[H-pattern] matches. This pattern may be used at
most once in the match of the entire repetition.
If the pattern is matched more than once in the repetition sequence,
then the ellipsis pattern fails with the message either
@racket[too-many-message-expr] or @racketvalfont{"too many occurrences
of @racket[name-expr]"}.
If the @racket[#:defaults] option is given, the following attribute
bindings are used if the subpattern does not match at all in the
sequence. The default attributes must be a subset of the subpattern's
attributes.
}
@specsubform/subs[(@#,defhere[~between] H-pattern min-number max-number between-option ...)
([reps-option (code:line #:name name-expr)
(code:line #:too-few too-few-message-expr)
(code:line #:too-many too-many-message-expr)])
#:contracts ([name-expr (or/c syntax? #f)]
[too-few-message-expr (or/c syntax? #f)])]{
Matches if the inner @racket[H-pattern] matches. This pattern must be
matched at least @racket[min-number] and at most @racket[max-number]
times in the entire repetition.
If the pattern is matched too few times, then the ellipsis pattern
fails with the message either @racket[too-few-message-expr] or
@racketvalfont{"too few occurrences of @racket[name-expr]"}.
If the pattern is chosen too many times, then the ellipsis pattern
fails with the message either @racket[too-many-message-expr] or
@racketvalfont{"too few occurrences of @racket[name-expr]"}.
}
@;{--------}
@section{Action Patterns}
An @deftech{@Apattern} (abbreviated @svar[A-pattern]) does not
describe any syntax; rather, it has an effect such as the binding of
attributes or the modification of the matching process.
@specsubform[@#,defhere[~!]]{
The @deftech{cut} operator, written @racket[~!], eliminates
backtracking choice points and commits parsing to the current branch
of the pattern it is exploring.
Common opportunities for cut-patterns come from recognizing special
forms based on keywords. Consider the following expression:
@interaction[#:eval the-eval
(syntax-parse #'(define-values a 123)
#:literals (define-values define-syntaxes)
[(define-values (x:id ...) e) 'define-values]
[(define-syntaxes (x:id ...) e) 'define-syntaxes]
[e 'expression])]
Given the ill-formed term @racket[(define-values a 123)],
@racket[syntax-parse] tries the first clause, fails to match
@racket[a] against the pattern @racket[(x:id ...)], and then
backtracks to the second clause and ultimately the third clause,
producing the value @racket['expression]. But the term is not an
expression; it is an ill-formed use of @racket[define-values]. The
proper way to write the @racket[syntax-parse] expression follows:
@interaction[#:eval the-eval
(syntax-parse #'(define-values a 123)
#:literals (define-values define-syntaxes)
[(define-values ~! (x:id ...) e) 'define-values]
[(define-syntaxes ~! (x:id ...) e) 'define-syntaxes]
[e 'expression])]
Now, given the same term, @racket[syntax-parse] tries the first
clause, and since the keyword @racket[define-values] matches, the
cut-pattern commits to the current pattern, eliminating the choice
points for the second and third clauses. So when the clause fails to
match, the @racket[syntax-parse] expression raises an error.
The effect of a @racket[~!] pattern is delimited by the nearest
enclosing @racket[~delimit-cut] or @racket[~commit] pattern. If there
is no enclosing @racket[~describe] pattern but the cut occurs within a
syntax class definition, then only choice points within the syntax
class definition are discarded. A @racket[~!] pattern is not allowed
within a @racket[~not] pattern unless there is an intervening
@racket[~delimit-cut] or @racket[~commit] pattern.
}
@specsubform/subs[(@#,defhere[~bind] [attr-arity-decl expr] ...)
([attr-arity-decl
attr-name-id
(attr-name-id depth)])]{
Evaluates the @racket[expr]s and binds them to the given
@racket[attr-id]s as attributes.
}
@specsubform/subs[(@#,defhere[~fail] maybe-fail-condition maybe-message-expr)
([maybe-fail-condition (code:line)
(code:line #:when condition-expr)
(code:line #:unless condition-expr)]
[maybe-message-expr (code:line)
(code:line message-expr)])
#:contracts ([message-expr (or/c string? #f)])]{
If the condition is absent, or if the @racket[#:when] condition
evaluates to a true value, or if the @racket[#:unless] condition
evaluates to @racket[#f], then the pattern fails with the given
message. If the message is omitted, the default value @racket[#f] is
used, representing ``no message.''
Fail patterns can be used together with cut patterns to recognize
specific ill-formed terms and address them with custom failure
messages.
}
@specsubform[(@#,defhere[~parse] S-pattern stx-expr)
#:contracts ([stx-expr syntax?])]{
Evaluates @racket[stx-expr] to a syntax object and matches it against
@racket[S-pattern].
}
@specsubform[(@#,def[~and a] A-pattern ...+)]{
Performs the actions of each @racket[A-pattern].
}
@specsubform[(@#,defhere[~do] defn-or-expr ...)]{
Takes a sequence of definitions and expressions, which may be
intermixed, and evaluates them in the scope of all previous attribute
bindings. The names bound by the definitions are in scope in the
expressions of subsequent patterns and clauses.
There is currently no way to bind attributes using a @racket[~do]
pattern. It is an error to shadow an attribute binding with a
definition in a @racket[~do] block.
@myexamples[
(syntax-parse #'(1 2 3)
[(a b (~do (printf "a was ~s\n" #'a)) c:id) 'ok])
]
}