1.5 Syntax Patterns
single-term patterns, abbreviated S-pattern
head patterns, abbreviated H-pattern
ellipsis-head patterns, abbreviated EH-pattern
action patterns, abbreviated A-pattern
When a special form in this manual refers to syntax-pattern (eg, the description of the syntax-parse special form), it means specifically single-term pattern.
S-pattern | = | pvar-id | ||||
| | pvar-id:syntax-class-id | |||||
| | pvar-id:literal-id | |||||
| | literal-id | |||||
| | (~vars- id) | |||||
| | (~vars+ id syntax-class-id maybe-role) | |||||
| | (~vars+ id (syntax-class-id arg ...) maybe-role) | |||||
| | (~literal literal-id maybe-phase) | |||||
| | atomic-datum | |||||
| | (~datum datum) | |||||
| | (H-pattern . S-pattern) | |||||
| | (A-pattern . S-pattern) | |||||
| | (EH-pattern ... . S-pattern) | |||||
| | (H-pattern ...+ . S-pattern) | |||||
| | (~ands proper-S/A-pattern ...+) | |||||
| | (~or*s S-pattern ...+) | |||||
| | (~not S-pattern) | |||||
| | #(pattern-part ...) | |||||
| | #s(prefab-struct-key pattern-part ...) | |||||
| | #&S-pattern | |||||
| | (~rest S-pattern) | |||||
| | (~describes maybe-opaque maybe-role expr S-pattern) | |||||
| | (~commits S-pattern) | |||||
| | (~delimit-cuts S-pattern) | |||||
| | (~posts S-pattern) | |||||
| | A-pattern | |||||
L-pattern | = | () | ||||
| | (A-pattern . L-pattern) | |||||
| | (H-pattern . L-pattern) | |||||
| | (EH-pattern ... . L-pattern) | |||||
| | (H-pattern ...+ . L-pattern) | |||||
| | (~rest L-pattern) | |||||
H-pattern | = | pvar-id:splicing-syntax-class-id | ||||
| | (~varh id splicing-syntax-class-id maybe-role) | |||||
| |
| |||||
| | (~seq . L-pattern) | |||||
| | (~andh proper-H/A-pattern ...+) | |||||
| | (~or*h H-pattern ...+) | |||||
| | (~optionalh H-pattern maybe-optional-option) | |||||
| | (~describeh maybe-opaque maybe-role expr H-pattern) | |||||
| | (~commith H-pattern) | |||||
| | (~delimit-cuth H-pattern) | |||||
| | (~posth H-patter) | |||||
| | (~peek H-pattern) | |||||
| | (~peek-not H-pattern) | |||||
| | proper-S-pattern | |||||
EH-pattern | = | (~alt EH-pattern ...) | ||||
| | (~once H-pattern once-option ...) | |||||
| | (~optionaleh 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) | |||||
| | (~anda A-pattern ...+) | |||||
| | (~posta A-pattern) | |||||
| | (~do defn-or-expr ...) | |||||
proper-S-pattern | = | a S-pattern that is not a A-pattern | ||||
proper-H-pattern | = | a H-pattern that is not a S-pattern |
The following pattern keywords can be used in multiple pattern variants:
syntax
syntax
~anda if all of the conjuncts are action patterns
~andh if any of the conjuncts is a proper head pattern
~ands otherwise
syntax
~or*h if any of the disjuncts is a proper head pattern
~or*s otherwise
syntax
The context-sensitive interpretation of ~or is a design mistake and a common source of confusion. Use ~alt and ~or* instead.
syntax
~describeh if the subpattern is a proper head pattern
~describes otherwise
syntax
~commith if the subpattern is a proper head pattern
~commits otherwise
syntax
~delimit-cuth if the subpattern is a proper head pattern
~delimit-cuts otherwise
syntax
~posta if the subpattern is an action pattern
~posth if the subpattern is a proper head pattern
~posts otherwise
syntax
~optionaleh if it is an immediate disjunct of an ~alt pattern
~optionalh otherwise
1.5.1 Single-term Patterns
A single-term pattern (abbreviated S-pattern) is a pattern that describes a single term. These are like the traditional patterns used in syntax-rules and syntax-case, but with additional variants that make them more expressive.
“Single-term” does not mean “atomic”; a single-term pattern can have complex structure, and it can match terms that have many parts. For example, (17 ...) is a single-term pattern that matches any term that is a proper list of repeated 17 numerals.
A proper single-term pattern is one that is not an action pattern.
The list patterns (for “list pattern”) are single-term patterns having a restricted structure that guarantees that they match only terms that are proper lists.
Here are the variants of single-term pattern:
id An identifier can be either a pattern variable, an annotated pattern variable, or a literal:
If id is the “pattern” name of an entry in the literals list, it is a literal pattern that behaves like (~literal id).
Examples:
> (syntax-parse #'(define x 12) #:literals (define) [(define var:id body:expr) 'ok]) 'ok
> (syntax-parse #'(lambda x 12) #:literals (define) [(define var:id body:expr) 'ok]) lambda: expected the identifier `define'
at: lambda
in: (lambda x 12)
> (syntax-parse #'(define x 12) #:literals ([def define]) [(def var:id body:expr) 'ok]) 'ok
> (syntax-parse #'(lambda x 12) #:literals ([def define]) [(def var:id body:expr) 'ok]) lambda: expected the identifier `define'
at: lambda
in: (lambda x 12)
If id is of the form pvar-id:syntax-class-id (that is, two names joined by a colon character), it is an annotated pattern variable, and the pattern is equivalent to (~var pvar-id syntax-class-id).
Examples:
> (syntax-parse #'a [var:id (syntax-e #'var)]) 'a
> (syntax-parse #'12 [var:id (syntax-e #'var)]) ?: expected identifier
at: 12
in: 12
> (define-syntax-class two #:attributes (x y) (pattern (x y)))
> (syntax-parse #'(a b) [t:two (syntax->datum #'(t t.x t.y))]) '((a b) a b)
> (syntax-parse #'(a b) [t #:declare t two (syntax->datum #'(t t.x t.y))]) '((a b) a b)
Note that an id of the form :syntax-class-id is legal; see the discussion of a ~vars+ form with a zero-length pvar-id.
If id is of the form pvar-id:literal-id, where literal-id is in the literals list, then it is equivalent to (~and (~var pvar-id) literal-id).
Examples:
> (require (only-in racket/base [define def]))
> (syntax-parse #'(def x 7) #:literals (define) [(d:define var:id body:expr) #'d]) #<syntax:11:0 def>
Otherwise, id is a pattern variable, and the pattern is equivalent to (~var id).
(~var pvar-id) A pattern variable. If pvar-id has no syntax class (by #:convention), the pattern variable matches anything. The pattern variable is bound to the matched subterm, unless the pattern variable is the wildcard (_), in which case no binding occurs.
If pvar-id does have an associated syntax class, it behaves like an annotated pattern variable with the implicit syntax class inserted.
(~var pvar-id syntax-class-use maybe-role)
syntax-class-use = syntax-class-id | (syntax-class-id arg ...) maybe-role =
| #:role role-expr
role-expr : (or/c string? #f) An annotated pattern variable. The pattern matches only terms accepted by syntax-class-id (parameterized by the args, if present).
In addition to binding pvar-id, an annotated pattern variable also binds nested attributes from the syntax class. The names of the nested attributes are formed by prefixing pvar-id. (that is, pvar-id followed by a “dot” character) to the name of the syntax class’s attribute.
If pvar-id is _, no attributes are bound. If pvar-id is the zero-length identifier (||), then pvar-id is not bound, but the nested attributes of syntax-class-use are bound without prefixes.
If role-expr is given and evaluates to a string, it is combined with the syntax class’s description in error messages.
Examples:
> (syntax-parse #'a [(~var var id) (syntax-e #'var)]) 'a
> (syntax-parse #'12 [(~var var id) (syntax-e #'var)]) ?: expected identifier
at: 12
in: 12
> (define-syntax-class two #:attributes (x y) (pattern (x y)))
> (syntax-parse #'(a b) [(~var t two) (syntax->datum #'(t t.x t.y))]) '((a b) a b)
> (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:17:0 (1 2 3)> #<syntax:17:0 (4 5)>)
> (syntax-parse #'(m a b 3) [(_ (~var x id #:role "variable") ...) 'ok]) m: expected identifier for variable
at: 3
in: (m a b 3)
(~literal literal-id maybe-phase)
maybe-phase =
| #:phase phase-expr A literal identifier pattern. Matches any identifier free-identifier=? to literal-id.
Examples:
> (syntax-parse #'(define x 12) [((~literal define) var:id body:expr) 'ok]) 'ok
> (syntax-parse #'(lambda x 12) [((~literal define) var:id body:expr) 'ok]) lambda: expected the identifier `define'
at: lambda
in: (lambda x 12)
The identifiers are compared at the phase given by phase-expr, if it is given, or (syntax-local-phase-level) otherwise.
atomic-datum Numbers, strings, booleans, keywords, and the empty list match as literals.
Examples:
> (syntax-parse #'(a #:foo bar) [(x #:foo y) (syntax->datum #'y)]) 'bar
> (syntax-parse #'(a foo bar) [(x #:foo y) (syntax->datum #'y)]) a: expected the literal #:foo
at: foo
in: (a foo bar)
(~datum datum) Matches syntax whose S-expression contents (obtained by syntax->datum) is equal? to the given datum.
Examples:
> (syntax-parse #'(a #:foo bar) [(x (~datum #:foo) y) (syntax->datum #'y)]) 'bar
> (syntax-parse #'(a foo bar) [(x (~datum #:foo) y) (syntax->datum #'y)]) a: expected the literal #:foo
at: foo
in: (a foo bar)
The ~datum form is useful for recognizing identifiers symbolically, in contrast to the ~literal form, which recognizes them by binding.
Examples:
> (define-syntax (is-define? stx) (syntax-parse stx [(is-define? id) (syntax-parse #'id [(~literal define) #''yes] [(~datum define) #''not-really] [_ #''not-even-close])])) > (is-define? define) 'yes
> (let ([define 42]) (is-define? define)) 'not-really
> (is-define? something-else) 'not-even-close
(H-pattern . S-pattern) Matches any term that can be decomposed into a list prefix matching H-pattern and a suffix matching 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 single-term pattern, in which case the whole pattern matches any pair whose first element matches the first pattern and whose rest matches the second.
See head patterns for more information.
(A-pattern . S-pattern) Performs the actions specified by A-pattern, then matches any term that matches S-pattern.
Pragmatically, one can throw an action pattern into any list pattern. Thus, (x y z) is a pattern matching a list of three terms, and (x y ~! z) is a pattern matching a list of three terms, with a cut performed after the second one. In other words, action patterns “don’t take up space.”
See action patterns for more information.
(EH-pattern ... . S-pattern) Matches any term that can be decomposed into a list head matching some number of repetitions of the EH-pattern alternatives (subject to its repetition constraints) followed by a list tail matching 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 ellipsis-head patterns for more information.
(H-pattern ...+ . S-pattern) Like an ellipses (...) pattern, but requires at least one occurrence of the head pattern to be present.
That is, the following patterns are equivalent:Examples:
> (syntax-parse #'(1 2 3) [(n:nat ...+) 'ok]) 'ok
> (syntax-parse #'() [(n:nat ...+) 'ok] [_ 'none]) 'none
(~and S/A-pattern ...) Matches any term that matches all of the subpatterns.
The subpatterns can contain a mixture of single-term patterns and action patterns, but must contain at least one single-term pattern.
Attributes bound in subpatterns are available to subsequent subpatterns. The whole pattern binds all of the subpatterns’ attributes.
One use for ~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 ~and can be lighter weight.
Examples:
> (define-syntax (import stx) (raise-syntax-error #f "illegal use of import" stx)) > (define (check-imports stx) ....)
> (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)]) 'ok
(~or* S-pattern ...) Matches any term that matches one of the included patterns. The alternatives are tried in order.
The whole pattern binds all of the subpatterns’ attributes. An attribute that is not bound by the “chosen” subpattern has a value of #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.
Examples:
> (syntax-parse #'a [(~or* x:id y:nat) (values (attribute x) (attribute y))]) #<syntax:34:0 a>
#f
> (syntax-parse #'(a 1) [(~or* (x:id y:nat) (x:id)) (values #'x (attribute y))]) #<syntax:35:0 a>
#<syntax:35:0 1>
> (syntax-parse #'(b) [(~or* (x:id y:nat) (x:id)) (values #'x (attribute y))]) #<syntax:36:0 b>
#f
(~not S-pattern) Matches any term that does not match the subpattern. None of the subpattern’s attributes are bound outside of the ~not-pattern.
Example:
#(pattern-part ...) Matches a term that is a vector whose elements, when considered as a list, match the single-term pattern corresponding to (pattern-part ...).
Examples:
> (syntax-parse #'#(1 2 3) [#(x y z) (syntax->datum #'z)]) 3
> (syntax-parse #'#(1 2 3) [#(x y ...) (syntax->datum #'(y ...))]) '(2 3)
> (syntax-parse #'#(1 2 3) [#(x ~rest y) (syntax->datum #'y)]) '(2 3)
#s(prefab-struct-key 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 single-term pattern corresponding to (pattern-part ...).
Examples:
> (syntax-parse #'#s(point 1 2 3) [#s(point x y z) 'ok]) 'ok
> (syntax-parse #'#s(point 1 2 3) [#s(point x y ...) (syntax->datum #'(y ...))]) '(2 3)
> (syntax-parse #'#s(point 1 2 3) [#s(point x ~rest y) (syntax->datum #'y)]) '(2 3)
#&S-pattern Matches a term that is a box whose contents matches the inner single-term pattern.
Example:
> (syntax-parse #'#&5 [#&n:nat 'ok]) 'ok
(~rest S-pattern) Matches just like S-pattern. The ~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).
Examples:
> (syntax-parse #'(1 2 3) [(x ~rest y) (syntax->datum #'y)]) '(2 3)
> (syntax-parse #'#(1 2 3) [#(x ~rest y) (syntax->datum #'y)]) '(2 3)
(~describe maybe-role maybe-opaque expr S-pattern)
maybe-opaque =
| #:opaque maybe-role =
| #:role role-expr
expr : (or/c string? #f)
role-expr : (or/c string? #f) The ~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 ~describe pattern does not influence backtracking.
If #:opaque is given, failure information from within S-pattern is discarded and the error is reported solely in terms of the description given.
If role-expr is given and produces a string, its value is combined with the description in error messages.
Examples:
> (syntax-parse #'(m 1) [(_ (~describe "id pair" (x:id y:id))) 'ok]) m: expected id pair
at: 1
in: (m 1)
> (syntax-parse #'(m (a 2)) [(_ (~describe "id pair" (x:id y:id))) 'ok]) m: expected identifier
at: 2
in: (m (a 2))
parsing context:
while parsing id pair
term: (a 2)
location: eval:48.0
> (syntax-parse #'(m (a 2)) [(_ (~describe #:opaque "id pair" (x:id y:id))) 'ok]) m: expected id pair
at: (a 2)
in: (m (a 2))
> (syntax-parse #'(m 1) [(_ (~describe #:role "formals" "id pair" (x y))) 'ok]) m: expected id pair for formals
at: 1
in: (m 1)
(~commit S-pattern) The ~commit pattern form affects backtracking in two ways:
If the pattern succeeds, then all choice points created within the subpattern are discarded, and a failure after the ~commit pattern backtracks only to choice points before the ~commit pattern, never one within it.
A cut (~!) within a ~commit pattern only eliminates choice-points created within the ~commit pattern. In this sense, it acts just like ~delimit-cut.
(~delimit-cut S-pattern) The ~delimit-cut pattern form affects backtracking in the following way:
A cut (~!) within a ~delimit-cut pattern only eliminates choice-points created within the ~delimit-cut pattern.
(~post S-pattern) Marks failures within the subpattern as occurring in a “post-order check”; that is, they are considered to have made greater progress than a normal failure.
A-pattern An action pattern is considered a single-term pattern when there is no ambiguity; it matches any term.
1.5.2 Head Patterns
A head pattern (abbreviated H-pattern) is a pattern that describes some number of terms that occur at the head of some list (possibly an improper list). A head pattern’s usefulness comes from being able to match heads of different lengths, such as optional forms like keyword arguments.
A proper head pattern is a head pattern that is not a single-term pattern.
Here are the variants of head pattern:
pvar-id:splicing-syntax-class-id Equivalent to (~var pvar-id splicing-syntax-class-id).
(~var pvar-id splicing-syntax-class-use maybe-role)
splicing-syntax-class-use = splicing-syntax-class-id | (splicing-syntax-class-id arg ...) maybe-role =
| #:role role-expr
role-expr : (or/c string? #f) Pattern variable annotated with a splicing syntax class. Similar to a normal annotated pattern variable, except matches a head pattern.
(~seq . L-pattern) Matches a sequence of terms whose elements, if put in a list, would match L-pattern.
Example:
> (syntax-parse #'(1 2 3 4) [((~seq 1 2 3) 4) 'ok]) 'ok
See also the section on ellipsis-head patterns for more interesting examples of ~seq.
(~and H-pattern ...) Like the single-term pattern version, ~ands, but matches a sequence of terms instead.
Example:
> (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 ...)))]) '((#:a #:b) (1 2) (#:a 1 #:b 2))
The head pattern variant of ~and requires that all of the subpatterns be proper head patterns (not single-term patterns). This is to prevent typos like the following, a variant of the previous example with the second ~seq omitted:
Examples:
> (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 ...)))]) syntax-parse: single-term pattern not allowed after head
pattern
at: (keyword-stuff ...)
in: (syntax-parse (syntax (#:a 1 #:b 2 3 4 5)) (((~and
(~seq (~seq k:keyword e:expr) ...) (keyword-stuff ...))
positional-stuff ...) (syntax->datum (syntax ((k ...) (e
...) (keyword-stuff ...))))))
; 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 ...)))]) ?: bad syntax
in: (#:a 1 #:b 2 3 4 5)
(~or* H-pattern ...) Like the single-term pattern version, ~or*s, but matches a sequence of terms instead.
Examples:
(~optional H-pattern maybe-optional-option)
maybe-optional-option =
| #: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 #: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.
Examples:
> (syntax-parse #'(m #:foo 2 a b c) [(_ (~optional (~seq #:foo x) #:defaults ([x #'#f])) y:id ...) (attribute x)]) #<syntax:57:0 2>
> (syntax-parse #'(m a b c) [(_ (~optional (~seq #:foo x) #:defaults ([x #'#f])) y:id ...) (attribute x)]) #<syntax:58:0 #f>
> (syntax-parse #'(m a b c) [(_ (~optional (~seq #:foo x)) y:id ...) (attribute x)]) #f
> (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 ...))]) #<syntax:60:0 (() (a b c))>
(~describe expr H-pattern) Like the single-term pattern version, ~describes, but matches a head pattern instead.
(~commit H-pattern) Like the single-term pattern version, ~commits, but matches a head pattern instead.
(~delimit-cut H-pattern) Like the single-term pattern version, ~delimit-cuts, but matches a head pattern instead.
(~post H-pattern) Like the single-term pattern version, ~posts, but matches a head pattern instead.
(~peek H-pattern) Matches the H-pattern but then resets the matching position, so the ~peek pattern consumes no input. Used to look ahead in a sequence.
Examples:
> (define-splicing-syntax-class nf-id ; 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 ...)))])
nf-ids are (a b)
rest is (c 1 2 3)
(~peek-not H-pattern) Like ~peek, but succeeds if the subpattern fails and fails if the subpattern succeeds. On success, the ~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 ~peek-not-pattern.
Examples:
> (define-splicing-syntax-class final ; final term (pattern (~seq x (~peek-not _))))
> (syntax-parse #'(a b c) [((~alt f:final other) ...) (printf "finals are ~s\n" (syntax->datum #'(f.x ...))) (printf "others are ~s\n" (syntax->datum #'(other ...)))])
finals are (c)
others are (a b)
S-pattern Matches a sequence of one element, which must be a term matching S-pattern.
1.5.3 Ellipsis-head Patterns
An ellipsis-head pattern (abbreviated EH-pattern) is pattern that describes some number of terms, like a head pattern, 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 ~alt.
> (define parser1 (syntax-parser [((~alt (~once (~seq #:a x) #:name "#:a keyword") (~optional (~seq #:b y) #:name "#:b keyword") (~seq #:c z)) ...) 'ok])) > (parser1 #'(#:a 1)) 'ok
> (parser1 #'(#:b 2 #:c 3 #:c 25 #:a 'hi)) 'ok
> (parser1 #'(#:a 1 #:a 2)) ?: too many occurrences of #:a keyword
at: ()
within: (#:a 1 #:a 2)
in: (#:a 1 #:a 2)
The pattern requires exactly one occurrence of the #:a keyword and argument, at most one occurrence of the #:b keyword and argument, and any number of #:c keywords and arguments. The “pieces” can occur in any order.
Here are the variants of ellipsis-head pattern:
(~alt EH-pattern ...) Matches if any of the inner EH-pattern alternatives match.
(~once H-pattern once-option ...)
once-option = #:name name-expr | #:too-few too-few-message-expr | #:too-many too-many-message-expr
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 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 too-few-message-expr or "missing required occurrence of name-expr".
If the pattern is chosen more than once in the repetition sequence, then the ellipsis pattern fails with the message either too-many-message-expr or "too many occurrences of name-expr".
(~optional H-pattern optional-option ...)
optional-option = #:name name-expr | #:too-many too-many-message-expr | #:defaults ([attr-id expr] ...)
name-expr : (or/c string? #f)
too-many-message-expr : (or/c string? #f) Matches if the inner 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 too-many-message-expr or "too many occurrences of name-expr".
If the #: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.
(~between H-pattern min-number max-number between-option ...)
reps-option = #:name name-expr | #:too-few too-few-message-expr | #:too-many too-many-message-expr
name-expr : (or/c syntax? #f)
too-few-message-expr : (or/c syntax? #f) Matches if the inner H-pattern matches. This pattern must be matched at least min-number and at most max-number times in the entire repetition.
If the pattern is matched too few times, then the ellipsis pattern fails with the message either too-few-message-expr or "too few occurrences of name-expr", when name-expr is provided.
If the pattern is chosen too many times, then the ellipsis pattern fails with the message either too-many-message-expr or "too many occurrences of name-expr", when name-expr is provided.
1.5.4 Action Patterns
An action pattern (abbreviated 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.
~! The cut operator, written ~!, 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:
> (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]) 'expression
Given the ill-formed term (define-values a 123), syntax-parse tries the first clause, fails to match a against the pattern (x:id ...), and then backtracks to the second clause and ultimately the third clause, producing the value 'expression. But the term is not an expression; it is an ill-formed use of define-values. The proper way to write the syntax-parse expression follows:
> (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]) define-values: bad syntax
in: (define-values a 123)
Now, given the same term, syntax-parse tries the first clause, and since the keyword 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 syntax-parse expression raises an error.
The effect of a ~! pattern is delimited by the nearest enclosing ~delimit-cut or ~commit pattern. If there is no enclosing ~describe pattern but the cut occurs within a syntax class definition, then only choice points within the syntax class definition are discarded. A ~! pattern is not allowed within a ~not pattern unless there is an intervening ~delimit-cut or ~commit pattern.
(~bind [attr-arity-decl expr] ...)
attr-arity-decl = attr-name-id | (attr-name-id depth) Evaluates the exprs and binds them to the given attr-ids as attributes.
(~fail maybe-fail-condition maybe-message-expr)
maybe-fail-condition =
| #:when condition-expr | #:unless condition-expr maybe-message-expr =
| message-expr
message-expr : (or/c string? #f) If the condition is absent, or if the #:when condition evaluates to a true value, or if the #:unless condition evaluates to #f, then the pattern fails with the given message. If the message is omitted, the default value #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.
(~parse S-pattern stx-expr) Evaluates stx-expr and matches it against S-pattern. If stx-expr does not produce a syntax object, the value is implicitly converted to a syntax object, unless the conversion would produce 3D syntax, in which case an exception is raised instead.
(~and A-pattern ...+) Performs the actions of each A-pattern.
(~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 ~do pattern. It is an error to shadow an attribute binding with a definition in a ~do block.
Example:
> (syntax-parse #'(1 2 3) [(a b (~do (printf "a was ~s\n" #'a)) c:id) 'ok]) a was #<syntax:71:0 1>
?: expected identifier
at: 3
in: (1 2 3)
(~post A-pattern) Like the single-term pattern version, ~posts, but contains only action patterns.
1.5.5 Pattern Expanders
The grammar of syntax patterns is extensible through the use of pattern expanders, which allow the definition of new pattern forms by rewriting them into existing pattern forms.
As a convention to avoid ambiguity, pattern expander names normally begin with a ~ character.
procedure
(pattern-expander proc) → pattern-expander?
proc : (-> syntax? syntax?)
> (define-syntax ~maybe (pattern-expander (syntax-rules () [(~maybe pat ...) (~optional (~seq pat ...))])))
(begin-for-syntax (struct thing (proc pattern-expander) #:property prop:procedure (struct-field-index proc) #:property prop:pattern-expander (λ (this) (thing-pattern-expander this)))) (define-syntax ~maybe (thing (lambda (stx) .... macro behavior ....) (lambda (stx) .... pattern-expander behavior ....)))
procedure
(pattern-expander? v) → boolean?
v : any/c
procedure
stx : syntax?