syntax/parse:

separated ~!, ~bind, ~fail as "action patterns", documented
  docs use eg "single-term pattern" instead of "S-pattern" in text

svn: r16089
This commit is contained in:
Ryan Culpepper 2009-09-20 20:47:54 +00:00
parent 6387797aa4
commit 491808e717
5 changed files with 598 additions and 435 deletions

View File

@ -227,6 +227,8 @@
(fail x
#:expect (expectation pattern0)
#:fce fc))]
[#s(pat:ghost attrs ghost subpattern)
#'(parse:G x fc ghost (parse:S x fc subpattern k))]
[#s(pat:head attrs head tail)
#`(parse:H x fc head rest index
(parse:S rest #,(frontier:add-index (wash #'fc) #'index) tail k))]
@ -269,9 +271,6 @@
(fail x
#:expect (expectation pattern0)
#:fce fc))))))]
[#s(pat:cut attrs pattern)
#`(with-enclosing-fail enclosing-cut-fail
(parse:S x fc pattern k))]
[#s(pat:describe attrs description transparent? pattern)
#`(let ([previous-fail enclosing-fail]
[previous-cut-fail enclosing-cut-fail])
@ -283,15 +282,7 @@
(parse:S x #,(empty-frontier #'x) pattern
(with-enclosing-cut-fail previous-cut-fail
(with-enclosing-fail previous-fail
k)))))]
[#s(pat:bind _ clauses)
#'(convert-sides x clauses (clause-success () k))]
[#s(pat:fail _ condition message)
#`(if condition
(fail x
#:expect (expectation pattern0)
#:fce fc)
k)])]))
k)))))])]))
;; (parse:S* (id ...) (FCE ...) (SinglePattern ...) expr) : expr
(define-syntax parse:S*
@ -323,6 +314,23 @@
(let ([sub-id alt-sub-id] ...)
(success pre ... id ...))))))]))
;; (parse:G id FCE SinglePattern expr) : expr
(define-syntax (parse:G stx)
(syntax-case stx ()
[(parse:G x fc pattern0 k)
(syntax-case #'pattern0 ()
[#s(ghost:cut _)
#`(with-enclosing-fail enclosing-cut-fail k)]
[#s(ghost:bind _ clauses)
#`(convert-sides x clauses (clause-success () k))]
[#s(ghost:fail _ condition message)
#`(if condition
(fail x
#:expect (expectation pattern0)
#:fce fc)
k)])]))
(begin-for-syntax
;; convert-list-pattern : ListPattern id -> SinglePattern
;; Converts '() datum pattern at end of list to bind (cons stx index)
@ -560,11 +568,10 @@
;; #'(make-expect:pair)]
[(_ #s(pat:compound attrs kind0 (part-pattern ...)))
#''ineffable]
[(_ #s(pat:fail _ condition message))
#'(expectation-of-message message)]
[(_ #s(pat:not _ pattern))
#''ineffable]
))
[(_ #s(ghost:fail _ condition message))
#'(expectation-of-message message)]))
;; ----

View File

@ -7,77 +7,85 @@
(provide (all-defined-out))
#|
A PBase/HPBase/EHPBase is (listof IAttr)
A Base is (listof IAttr)
If P = (make-pattern Attrs ...) and A is in Attrs,
the depth of A is with respect to P,
not with respect to the entire enclosing pattern.
An IdPrefix is an identifier/#f
If #f, it means bind no attributes
If identifier, it already includes the colon part, unless epsilon
|#
#|
A SinglePattern is one of
(make-pat:any SPBase)
(make-pat:var SPBase id id (listof stx) (listof IAttr))
(make-pat:datum SPBase datum)
(make-pat:literal SPBase identifier)
(make-pat:head SPBase HeadPattern SinglePattern)
(make-pat:dots SPBase (listof EllipsisHeadPattern) SinglePattern)
(make-pat:and SPBase (listof SinglePattern))
(make-pat:or SPBase (listof SinglePattern))
(make-pat:not SPBase SinglePattern)
(make-pat:compound SPBase Kind (listof SinglePattern))
(make-pat:cut SPBase SinglePattern)
(make-pat:describe SPBase stx boolean SinglePattern)
(make-pat:fail SPBase stx stx)
(make-pat:bind SPBase (listof clause:attr))
(make-pat:any Base)
(make-pat:var Base id id (listof stx) (listof IAttr))
(make-pat:literal Base identifier)
(make-pat:datum Base datum)
(make-pat:ghost Base GhostPattern SinglePattern)
(make-pat:head Base HeadPattern SinglePattern)
(make-pat:dots Base (listof EllipsisHeadPattern) SinglePattern)
(make-pat:and Base (listof SinglePattern))
(make-pat:or Base (listof SinglePattern))
(make-pat:not Base SinglePattern)
(make-pat:compound Base Kind (listof SinglePattern))
(make-pat:describe Base stx boolean SinglePattern)
A ListPattern is a subtype of SinglePattern; one of
(make-pat:datum SPBase '())
(make-pat:head SPBase HeadPattern ListPattern)
(make-pat:compound SPBase '#:pair (list SinglePattern ListPattern))
(make-pat:dots SPBase EllipsisHeadPattern SinglePattern)
(make-pat:cut SPBase ListPattern)
(make-pat:datum Base '())
(make-pat:ghost Base GhostPattern ListPattern)
(make-pat:head Base HeadPattern ListPattern)
(make-pat:compound Base '#:pair (list SinglePattern ListPattern))
(make-pat:dots Base EllipsisHeadPattern SinglePattern)
|#
(define-struct pat:any (attrs) #:prefab)
(define-struct pat:var (attrs name parser args nested-attrs) #:prefab)
(define-struct pat:datum (attrs datum) #:prefab)
(define-struct pat:literal (attrs id) #:prefab)
(define-struct pat:datum (attrs datum) #:prefab)
(define-struct pat:ghost (attrs ghost inner) #:prefab)
(define-struct pat:head (attrs head tail) #:prefab)
(define-struct pat:dots (attrs heads tail) #:prefab)
(define-struct pat:and (attrs patterns) #:prefab)
(define-struct pat:or (attrs patterns) #:prefab)
(define-struct pat:not (attrs pattern) #:prefab)
(define-struct pat:compound (attrs kind patterns) #:prefab)
(define-struct pat:cut (attrs pattern) #:prefab)
(define-struct pat:describe (attrs description transparent? pattern) #:prefab)
(define-struct pat:fail (attrs when message) #:prefab)
(define-struct pat:bind (attrs clauses) #:prefab)
#|
A GhostPattern is one of
(make-ghost:cut Base)
(make-ghost:fail Base stx stx)
(make-ghost:bind Base (listof clause:attr))
* (make-ghost:and Base (listof GhostPattern))
ghost:and is desugared below in create-* procedures
|#
(define-struct ghost:cut (attrs) #:prefab)
(define-struct ghost:fail (attrs when message) #:prefab)
(define-struct ghost:bind (attrs clauses) #:prefab)
(define-struct ghost:and (attrs patterns) #:prefab)
#|
A HeadPattern is one of
(make-hpat:var SPBase id id (listof stx) (listof IAttr))
(make-hpat:seq HPBase ListPattern)
(make-hpat:and HPBase HeadPattern SinglePattern)
(make-hpat:or HPBase (listof HeadPattern))
(make-hpat:describe HPBase stx/#f boolean HeadPattern)
(make-hpat:optional HPBase HeadPattern (listof clause:attr))
(make-hpat:var Base id id (listof stx) (listof IAttr))
(make-hpat:seq Base ListPattern)
(make-hpat:ghost Base GhostPattern HeadPattern)
(make-hpat:and Base HeadPattern SinglePattern)
(make-hpat:or Base (listof HeadPattern))
(make-hpat:optional Base HeadPattern (listof clause:attr))
(make-hpat:describe Base stx/#f boolean HeadPattern)
|#
(define-struct hpat:var (attrs name parser args nested-attrs) #:prefab)
(define-struct hpat:seq (attrs inner) #:prefab)
(define-struct hpat:or (attrs patterns) #:prefab)
(define-struct hpat:ghost (attrs ghost inner) #:prefab)
(define-struct hpat:and (attrs head single) #:prefab)
(define-struct hpat:describe (attrs description transparent? pattern) #:prefab)
(define-struct hpat:or (attrs patterns) #:prefab)
(define-struct hpat:optional (attrs inner defaults) #:prefab)
(define-struct hpat:describe (attrs description transparent? pattern) #:prefab)
#|
An EllipsisHeadPattern is
(make-ehpat EHPBase HeadPattern RepConstraint)
(make-ehpat Base HeadPattern RepConstraint)
A RepConstraint is one of
(make-rep:once stx stx stx)
@ -85,6 +93,7 @@ A RepConstraint is one of
(make-rep:bounds nat/#f nat/#f stx stx stx)
#f
|#
(define-struct ehpat (attrs head repc) #:prefab)
(define-struct rep:once (name under-message over-message) #:prefab)
(define-struct rep:optional (name over-message defaults) #:prefab)
@ -102,26 +111,31 @@ A Kind is one of
(define (pattern? x)
(or (pat:any? x)
(pat:var? x)
(pat:datum? x)
(pat:literal? x)
(pat:datum? x)
(pat:ghost? x)
(pat:head? x)
(pat:dots? x)
(pat:and? x)
(pat:or? x)
(pat:not? x)
(pat:compound? x)
(pat:cut? x)
(pat:describe? x)
(pat:bind? x)
(pat:fail? x)))
(pat:describe? x)))
(define (ghost-pattern? x)
(or (ghost:cut? x)
(ghost:bind? x)
(ghost:fail? x)
(ghost:and? x)))
(define (head-pattern? x)
(or (hpat:var? x)
(hpat:seq? x)
(hpat:ghost? x)
(hpat:and? x)
(hpat:or? x)
(hpat:describe? x)
(hpat:optional? x)))
(hpat:optional? x)
(hpat:describe? x)))
(define (ellipsis-head-pattern? x)
(ehpat? x))
@ -147,10 +161,10 @@ A Kind is one of
#'(lambda (x)
(cond [(pred x) (accessor x)] ...
[else (raise-type-error 'pattern-attrs "pattern" x)])))]))
(mk-get-attrs pat:any pat:var pat:datum pat:literal pat:head pat:dots
pat:and pat:or pat:not pat:compound
pat:cut pat:describe pat:bind pat:fail
hpat:var hpat:seq hpat:and hpat:or hpat:describe
(mk-get-attrs pat:any pat:var pat:datum pat:literal pat:ghost pat:head
pat:dots pat:and pat:or pat:not pat:compound pat:describe
ghost:cut ghost:bind ghost:fail ghost:and
hpat:var hpat:seq hpat:ghost hpat:and hpat:or hpat:describe
hpat:optional
ehpat)))
@ -174,12 +188,21 @@ A Kind is one of
(define (create-pat:literal literal)
(make pat:literal null literal))
(define (create-pat:ghost g sp)
(cond [(ghost:and? g)
(for/fold ([sp sp]) ([g (reverse (ghost:and-patterns g))])
(create-pat:ghost g sp))]
[else
(let ([attrs (append-iattrs (map pattern-attrs (list g sp)))])
(make pat:ghost attrs g sp))]))
(define (create-pat:head headp tailp)
(let ([attrs (append-iattrs (map pattern-attrs (list headp tailp)))])
(make pat:head attrs headp tailp)))
(define (create-pat:compound kind ps)
(make pat:compound (append-iattrs (map pattern-attrs ps)) kind ps))
(define (create-pat:cut inner)
(make pat:cut (pattern-attrs inner) inner))
(define (create-pat:describe description transparent? p)
(make pat:describe (pattern-attrs p) description transparent? p))
@ -198,12 +221,17 @@ A Kind is one of
(let ([attrs (append-iattrs (map pattern-attrs (cons tailp headps)))])
(make pat:dots attrs headps tailp)))
(define (create-pat:fail condition message)
(make pat:fail null condition message))
;; ----
(define (create-pat:head headp tailp)
(let ([attrs (append-iattrs (map pattern-attrs (list headp tailp)))])
(make pat:head attrs headp tailp)))
(define (create-ghost:cut)
(make ghost:cut null))
(define (create-ghost:fail condition message)
(make ghost:fail null condition message))
(define (create-ghost:and patterns)
(let ([attrs (append-iattrs (map pattern-attrs patterns))])
(make ghost:and attrs patterns)))
;; ----
@ -215,6 +243,14 @@ A Kind is one of
(define (create-hpat:seq lp)
(make hpat:seq (pattern-attrs lp) lp))
(define (create-hpat:ghost g hp)
(cond [(ghost:and? g)
(for/fold ([hp hp]) ([g (reverse (ghost:and-patterns g))])
(create-hpat:ghost g hp))]
[else
(let ([attrs (append-iattrs (map pattern-attrs (list g hp)))])
(make hpat:ghost attrs g hp))]))
(define (create-hpat:describe description transparent? p)
(make hpat:describe (pattern-attrs p) description transparent? p))
@ -227,8 +263,14 @@ A Kind is one of
;; ----
(define (head-pattern->list-pattern hp)
;; simplification: just extract list pattern from hpat:seq
(if (hpat:seq? hp)
(hpat:seq-inner hp)
(create-pat:head hp (create-pat:datum '()))))
(define (ghost/head-pattern->list-pattern p)
(cond [(ghost-pattern? p)
(create-pat:ghost p (create-pat:any))]
[(hpat:seq? p)
;; simplification: just extract list pattern from hpat:seq
(hpat:seq-inner p)]
[else
(create-pat:head p (create-pat:datum '()))]))
(define (ghost-pattern->single-pattern gp)
(create-pat:ghost gp (create-pat:any)))

View File

@ -259,50 +259,84 @@
;; parse-single-pattern : stx DeclEnv -> SinglePattern
(define (parse-single-pattern stx decls)
;; FIXME: allow ghosts, convert to single-term pattern???
(let ([p (parse-*-pattern stx decls #f #f)])
p))
;; parse-head-pattern : stx DeclEnv -> HeadPattern
(define (parse-head-pattern stx decls)
(parse-*-pattern stx decls #t #f))
;; parse-*-pattern : stx DeclEnv boolean boolean -> Pattern
(define (parse-*-pattern stx decls allow-head? allow-ghost?)
(define (check-head! x)
(unless allow-head?
(wrong-syntax stx "head pattern not allowed here"))
x)
(define (check-ghost! x)
;; Coerce to S-pattern IF only S-patterns allowed
(cond [allow-ghost? x]
[(not allow-head?) (ghost-pattern->single-pattern x)]
[else
(wrong-syntax stx "action pattern not allowed here")]))
(syntax-case stx (~var ~literal ~and ~or ~not ~rest ~struct
~! ~describe ~bind ~fail)
~! ~describe ~bind ~fail ~seq ~optional)
[wildcard
(wildcard? #'wildcard)
(create-pat:any)]
[~!
(check-ghost!
(create-ghost:cut))]
[reserved
(reserved? #'reserved)
(wrong-syntax stx "not allowed here")]
(wrong-syntax stx "pattern keyword not allowed here")]
[id
(identifier? #'id)
(parse-pat:id stx decls #f)]
(parse-pat:id stx decls allow-head?)]
[datum
(atomic-datum? #'datum)
(create-pat:datum (syntax->datum #'datum))]
[(~var . rest)
(parse-pat:var stx decls #f)]
(parse-pat:var stx decls allow-head?)]
[(~literal . rest)
(parse-pat:literal stx decls)]
[(~and . rest)
(parse-pat:and stx decls #f)]
(parse-pat:and stx decls allow-head? allow-ghost?)]
[(~or . rest)
(parse-pat:or stx decls #f)]
(parse-pat:or stx decls allow-head?)]
[(~not . rest)
(parse-pat:not stx decls)]
[(~rest . rest)
(parse-pat:rest stx decls)]
[(~describe . rest)
(parse-pat:describe stx decls allow-head?)]
[(~seq . rest)
(check-head!
(parse-hpat:seq stx #'rest decls))]
[(~optional . rest)
(check-head!
(parse-hpat:optional stx decls))]
[(~bind . rest)
(check-ghost!
(parse-pat:bind stx decls))]
[(~fail . rest)
(check-ghost!
(parse-pat:fail stx decls))]
[(head dots . tail)
(dots? #'dots)
(parse-pat:dots stx #'head #'tail decls)]
[(~struct key . contents)
(let ([lp (parse-single-pattern (syntax/loc stx contents) decls)]
[key (syntax->datum #'key)])
(create-pat:compound `(#:pstruct ,key) (list lp)))]
[(~! . rest)
(let ([inner (parse-single-pattern (syntax/loc stx rest) decls)])
(create-pat:cut inner))]
[(~describe . rest)
(parse-pat:describe stx decls #f)]
[(~bind . rest)
(parse-pat:bind stx decls)]
[(~fail . rest)
(parse-pat:fail stx decls)]
[(~rest . rest)
(parse-pat:rest stx decls)]
[(head . tail)
(parse-pat:pair stx #'head #'tail decls)]
(let ([headp (parse-*-pattern #'head decls #t #t)]
[tailp (parse-single-pattern #'tail decls)])
;; Only make pat:head if head is complicated;
;; otherwise simple compound/pair
;; FIXME: Could also inline ~seq patterns from head...?
(cond [(ghost-pattern? headp)
(create-pat:ghost headp tailp)]
[(head-pattern? headp)
(create-pat:head headp tailp)]
[else
(create-pat:compound '#:pair (list headp tailp))]))]
[#(a ...)
(let ([lp (parse-single-pattern (syntax/loc stx (a ...)) decls)])
(create-pat:compound '#:vector (list lp)))]
@ -318,27 +352,6 @@
(let ([lp (parse-single-pattern (datum->syntax #f contents #'s) decls)])
(create-pat:compound `(#:pstruct ,key) (list lp))))]))
;; parse-head-pattern : stx DeclEnv -> HeadPattern
(define (parse-head-pattern stx decls)
(syntax-case stx (~var ~and ~or ~seq ~describe ~optional)
[id
(and (identifier? #'id) (not (reserved? #'id)))
(parse-pat:id stx decls #t)]
[(~var . rest)
(parse-pat:var stx decls #t)]
[(~and . rest)
(parse-pat:and stx decls #t)]
[(~or . rest)
(parse-pat:or stx decls #t)]
[(~seq . rest)
(parse-hpat:seq stx #'rest decls)]
[(~describe . rest)
(parse-pat:describe stx decls #t)]
[(~optional . rest)
(parse-hpat:optional stx decls)]
[_
(parse-single-pattern stx decls)]))
;; parse-ellipsis-head-pattern : stx DeclEnv number -> EllipsisHeadPattern
(define (parse-ellipsis-head-pattern stx decls)
(syntax-case stx (~bounds ~optional ~once)
@ -374,10 +387,9 @@
[(list 'splicing-parser parser description attrs)
(parse-pat:id/h id parser null attrs)]
[#f
#|
(unless (safe-name? id)
(wrong-syntax id "expected identifier not starting with ~ character"))
|#
(when #f ;; FIXME: enable?
(unless (safe-name? id)
(wrong-syntax id "expected identifier not starting with ~ character")))
(let-values ([(name sc) (split-id/get-stxclass id decls)])
(if sc
(parse-pat:var* id allow-head? name sc null)
@ -486,11 +498,60 @@
(define transparent? (and (assq '#:transparent chunks) #t))
(syntax-case rest ()
[(description pattern)
(let ([p (parse-some-pattern #'pattern decls allow-head?)])
(let ([p (parse-*-pattern #'pattern decls allow-head? #f)])
(if (head-pattern? p)
(create-hpat:describe #'description transparent? p)
(create-pat:describe #'description transparent? p)))]))]))
(define (split-prefix xs pred)
(let loop ([xs xs] [rprefix null])
(cond [(and (pair? xs) (pred (car xs)))
(loop (cdr xs) (cons (car xs) rprefix))]
[else
(values (reverse rprefix) xs)])))
(define (parse-pat:and stx decls allow-head? allow-ghost?)
;; allow-ghost? = allowed to *return* pure ghost pattern;
;; all ~and patterns are allowed to *contain* ghost patterns
(define patterns0 (parse-cdr-patterns stx decls allow-head? #t))
(define-values (ghosts patterns) (split-prefix patterns0 ghost-pattern?))
(cond [(null? patterns)
(cond [allow-ghost?
(create-ghost:and ghosts)]
[allow-head?
(wrong-syntax stx "expected at least one head pattern")]
[else
(wrong-syntax stx "expected at least one single-term pattern")])]
[else
(let ([p (parse-pat:and* stx patterns)])
(if (head-pattern? p)
(for/fold ([p p]) ([ghost (reverse ghosts)])
(create-hpat:ghost ghost p))
(for/fold ([p p]) ([ghost (reverse ghosts)])
(create-pat:ghost ghost p))))]))
(define (parse-pat:and* stx patterns)
;; patterns is non-empty (empty case handled above)
(cond [(null? (cdr patterns))
(car patterns)]
[(ormap head-pattern? patterns)
;; Check to make sure *all* are head patterns
(for ([pattern patterns]
[pattern-stx (stx->list (stx-cdr stx))])
(unless (or (ghost-pattern? pattern) (head-pattern? pattern))
(wrong-syntax
pattern-stx
"single-term pattern not allowed after head pattern")))
(let ([p0 (car patterns)]
[lps (map ghost/head-pattern->list-pattern (cdr patterns))])
(create-hpat:and p0 (create-pat:and lps)))]
[else
(create-pat:and
(for/list ([p patterns])
(if (ghost-pattern? p)
(ghost-pattern->single-pattern p)
p)))]))
(define (parse-pat:or stx decls allow-head?)
(define patterns (parse-cdr-patterns stx decls allow-head? #f))
(cond [(null? (cdr patterns))
@ -501,24 +562,6 @@
[else
(create-pat:or patterns)])]))
(define (parse-pat:and stx decls allow-head?)
(define patterns (parse-cdr-patterns stx decls allow-head? #t))
(cond [(null? (cdr patterns))
(car patterns)]
[(ormap head-pattern? patterns)
;; Check to make sure *all* are head patterns
(for ([pattern patterns]
[pattern-stx (stx->list (stx-cdr stx))])
(unless (head-pattern? pattern)
(wrong-syntax
pattern-stx
"single-term pattern not allowed after head pattern")))
(let ([p0 (car patterns)]
[lps (map head-pattern->list-pattern (cdr patterns))])
(create-hpat:and p0 (create-pat:and lps)))]
[else
(create-pat:and patterns)]))
(define (parse-pat:not stx decls)
(syntax-case stx (~not)
[(~not pattern)
@ -532,31 +575,16 @@
(check-list-pattern pattern stx)
(create-hpat:seq pattern))
(define (parse-cdr-patterns stx decls allow-head? allow-cut?)
(define (parse-cdr-patterns stx decls allow-head? allow-ghost?)
(unless (stx-list? stx)
(wrong-syntax stx "expected sequence of patterns"))
(let ([result
(for/list ([sub (cdr (stx->list stx))])
(if allow-cut?
(or (parse-cut-in-and sub)
(parse-some-pattern sub decls allow-head?))
(parse-some-pattern sub decls allow-head?)))])
(parse-*-pattern sub decls allow-head? allow-ghost?))])
(when (null? result)
(wrong-syntax stx "expected at least one pattern"))
result))
(define (parse-cut-in-and stx)
(syntax-case stx (~!)
[~! (create-pat:cut (create-pat:any))]
[_ #f]))
(define (parse-some-pattern stx decl allow-head?)
(define p (parse-head-pattern stx decl))
(when (head-pattern? p)
(unless allow-head?
(wrong-syntax stx "head pattern not allowed here")))
p)
(define (parse-pat:dots stx head tail decls)
(define headps
(syntax-case head (~or)
@ -577,7 +605,7 @@
(syntax-case stx ()
[(_ clause ...)
(let ([clauses (check-bind-clause-list #'(clause ...) stx)])
(make pat:bind
(make ghost:bind
(append-iattrs (side-clauses-attrss clauses))
clauses))]))
@ -598,7 +626,7 @@
#`(not #,(caddr chunk)))))])
(syntax-case rest ()
[(message)
(create-pat:fail condition #'message)]
(create-ghost:fail condition #'message)]
[()
(wrong-syntax stx "missing message expression")]
[_
@ -609,15 +637,6 @@
[(_ pattern)
(parse-single-pattern #'pattern decls)]))
(define (parse-pat:pair stx head tail decls)
(define headp (parse-head-pattern head decls))
(define tailp (parse-single-pattern tail decls))
;; Only make pat:head if head is complicated; otherwise simple compound/pair
;; FIXME: Could also inline ~seq patterns from head...?
(if (head-pattern? headp)
(create-pat:head headp tailp)
(create-pat:compound '#:pair (list headp tailp))))
(define (check-list-pattern pattern stx)
(match pattern
[(struct pat:datum (_base '()))

View File

@ -12,6 +12,18 @@
@(define ellipses @scheme[...])
@(define-syntax-rule (defhere id) (defidentifier #'id #:form? #t))
@(define Spattern "single-term pattern")
@(define Lpattern "list pattern")
@(define Hpattern "head pattern")
@(define EHpattern "ellipsis-head pattern")
@(define Apattern "action pattern")
@(define Spatterns "single-term patterns")
@(define Lpatterns "list patterns")
@(define Hpatterns "head patterns")
@(define EHpatterns "ellipsis-head patterns")
@(define Apatterns "action patterns")
@(begin
(define-syntax ref
(syntax-rules ()
@ -56,15 +68,20 @@
The grammar of @deftech{syntax patterns} used by
@schememodname[syntax/parse] facilities is given in the following
table. There are three main kinds of syntax pattern: @tech{S-patterns}
(for ``single-term patterns''), @tech{H-patterns} (for ``head
patterns''), and @tech{EH-patterns} (for ``ellipsis head
patterns''). A fourth kind, @tech{L-patterns} (for ``list patterns''),
is a restricted subset of @tech{S-patterns}.
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 @scheme[syntax-parse] special form), it
means specifically @tech{S-pattern}.
means specifically @tech{@Spattern}.
@schemegrammar*[#:literals (_ ~var ~literal ~or ~and ~not ~seq
~rep ~once ~optional
@ -78,81 +95,118 @@ means specifically @tech{S-pattern}.
(~literal literal-id)
atomic-datum
(H-pattern . S-pattern)
(A-pattern . S-pattern)
((@#,ref[~or eh] EH-pattern ...+) #,ellipses . S-pattern)
(EH-pattern #,ellipses . S-pattern)
(@#,ref[~and s] 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]) ...)
(~rest S-pattern)
(@#,ref[~describe s] expr S-pattern)
(~! . S-pattern)
(~bind [attr-id expr] ...)
(~fail maybe-fail-condition message-expr)]
A-pattern]
[L-pattern
()
(A-pattern . L-pattern)
(H-pattern . L-pattern)
((@#,ref[~or eh] EH-pattern ...+) #,ellipses . L-pattern)
(EH-pattern #,ellipses . L-pattern)
(~rest L-pattern)
(~! . L-pattern)]
(~rest L-pattern)]
[H-pattern
pvar-id:splicing-syntax-class-id
(@#,ref[~var h] id splicing-syntax-class)
(~seq . L-pattern)
(@#,ref[~and h] strict-H-pattern ...+)
(@#,ref[~and h] proper-H/A-pattern ...+)
(@#,ref[~or h] H-pattern ...+)
(@#,ref[~optional h] H-pattern maybe-optional-option)
(@#,ref[~describe h] expr H-pattern)
S-pattern]
proper-S-pattern]
[EH-pattern
(~once H-pattern once-option ...)
(@#,ref[~optional eh] H-pattern optional-option ...)
H-pattern]]
H-pattern]
[A-pattern
~!
(~bind [attr-id expr] ...)
(~fail maybe-fail-condition message-expr)
(@#,ref[~and a] A-pattern ...+)]
[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]{
Pattern keyword; see @ref[~var s-], @ref[~var s+], or @ref[~var h].
One of @ref[~var s-], @ref[~var s+], or @ref[~var h].
}
@defidform[~and]{
Pattern keyword; see @ref[~and s] or @ref[~and h].
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]{
Pattern keyword; see @ref[~or s], @ref[~or h]), or @ref[~or eh].
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)}
@item{@ref[~or h] if any of the disjuncts is a @tech{proper @Hpattern}}
@item{@ref[~or s] otherwise}
]
}
@defidform[~describe]{
Pattern keyword; see @ref[~describe s] or @ref[~describe h].
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[~optional]{
Pattern keyword; see @ref[~optional h] or @ref[~optional eh].
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{S-pattern variants}
@section{Single-term patterns}
An @deftech{S-pattern} (for ``single-term pattern'') is a pattern that
describes a single term. The pattern may, of course, consist of other
parts. For example, @scheme[(17 ...)] is an @tech{S-pattern}
that matches any term that is a proper list of repeated
@schemeresult[17] numerals. The @deftech{L-pattern}s (for ``list
pattern'') are @tech{S-pattern} having a restricted structure that
constrains it to match only terms that are proper lists.
A @deftech{@Spattern} (abbreviated @svar[S-pattern]) is a pattern that
describes a single term. These are like the traditional patterns used
in @scheme[syntax-rules] and @scheme[syntax-case], but with additional
variants that make them more expressive.
Here are the variants of @tech{S-pattern}:
``Single-term'' does not mean ``atomic''; a @Spattern can have
complex structure, and it can match terms that have many parts. For
example, @scheme[(17 ...)] is a @Spattern that matches any
term that is a proper list of repeated @schemeresult[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]{
@ -285,32 +339,46 @@ literals.
@specsubform[(H-pattern . S-pattern)]{
Matches any term that can be decomposed into a list prefix matching
the @tech{H-pattern} and a suffix matching the S-pattern.
@scheme[H-pattern] and a suffix matching @scheme[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 an @tech{S-pattern}, in which case the whole
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{H-patterns} for more information.
See @tech{@Hpatterns} for more information.
}
@specsubform[(A-pattern . S-pattern)]{
Performs the actions specified by @scheme[A-pattern], then matches
any term that matches @scheme[S-pattern].
Pragmatically, one can throw an @tech{@Apattern} into any list
pattern. Thus, @scheme[(x y z)] is a pattern matching a list of three
terms, and @scheme[(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[((@#,def[~or eh] EH-pattern ...+) #,ellipses . S-pattern)]{
Matches any term that can be decomposed into a list head matching some
number of repetitions of the @tech{EH-pattern} alternatives (subject
to its repetition constraints) followed by a list tail matching the
S-pattern.
number of repetitions of @scheme[EH-pattern] alternatives (subject to
its repetition constraints) followed by a list tail matching
@scheme[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{EH-patterns} for more information.
See @tech{@EHpatterns} for more information.
}
@specsubform[(EH-pattern #,ellipses . S-pattern)]{
@ -319,10 +387,13 @@ The @scheme[~or]-free variant of ellipses (@ellipses) pattern is
equivalent to the @scheme[~or] variant with just one alternative.
}
@specsubform[(@#,def[~and s] S-pattern ...)]{
@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.
@ -348,7 +419,6 @@ purpose, but @scheme[~and] can be lighter weight.
#f "bad import" #'import-clause bad))
'ok)])
]
}
@specsubform[(@#,def[~or s] S-pattern ...)]{
@ -372,8 +442,9 @@ to have a value if the whole pattern matches.
@specsubform[(@#,defhere[~not] S-pattern)]{
Matches any term that does not match the subpattern. The subpattern's
attributes are @emph{not} bound outside of the @scheme[~not]-pattern.
Matches any term that does not match the subpattern. None of the
subpattern's attributes are bound outside of the
@scheme[~not]-pattern.
@myexamples[
(syntax-parse #'(x y z => u v)
@ -386,7 +457,7 @@ attributes are @emph{not} bound outside of the @scheme[~not]-pattern.
@specsubform[#(#, @svar[pattern-part] ...)]{
Matches a term that is a vector whose elements, when considered as a
list, match the @tech{S-pattern} corresponding to
list, match the @tech{@Spattern} corresponding to
@scheme[(pattern-part ...)].
@myexamples[
@ -397,14 +468,13 @@ list, match the @tech{S-pattern} corresponding to
(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{S-pattern} corresponding to @scheme[(pattern-part ...)].
@tech{@Spattern} corresponding to @scheme[(pattern-part ...)].
@myexamples[
(syntax-parse #'#s(point 1 2 3)
@ -418,10 +488,10 @@ key and whose sequence of fields, when considered as a list, match the
@specsubform[(#, @defhere[~rest] S-pattern)]{
Matches just like the inner @scheme[S-pattern]. The @scheme[~rest]
pattern form is useful in positions where improper lists (``dots'')
are not allowed by the reader, such as vector and structure patterns
(see above).
Matches just like @scheme[S-pattern]. The @scheme[~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)
@ -442,19 +512,234 @@ A describe-pattern also affects backtracking in two ways:
@itemize{
@item{A cut-pattern (@scheme[~!]) within a describe-pattern only
@item{A cut (@scheme[~!]) within a describe-pattern only
eliminates choice-points created within the describe-pattern.}
@item{If a describe-pattern succeeds, then all choice points created
within the describe-pattern are discarded, and a failure @emph{after}
the describe-pattern backtracks to a choice point @emph{before} the
describe-pattern, never one @emph{within} it.}}}
@item{If a describe-pattern succeeds, then all choice points
created within the describe-pattern are discarded, and a failure
@emph{after} the describe-pattern backtracks to a choice point
@emph{before} the describe-pattern, never one @emph{within} it.}
}
}
@specsubform[(@#,defhere[~!] . S-pattern)]{
@specsubform[A-pattern]{
The @scheme[~!] operator, pronounced ``cut'', eliminates backtracking
choice points and commits parsing to the current branch of the pattern
it is exploring.
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 @scheme[(~var pvar-id splicing-syntax-class-id)].
}
@specsubform/subs[(@#,def[~var h] pvar-id splicing-syntax-class)
([splicing-syntax-class splicing-syntax-class-id
(splicing-syntax-class-id arg-expr ...)])]{
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 head whose elements, if put in a list, would match
@scheme[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 @scheme[~seq].
}
@specsubform[(@#,def[~and h] H-pattern ...)]{
Like the @Spattern version of @scheme[~and], but matches a term head
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 @scheme[~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 @scheme[~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 of @scheme[~or], but matches a term head
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-id expr] ...))])]{
Matches either the given head subpattern or an empty head. If the
@scheme[#: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)])
]
}
@specsubform[(@#,def[~describe h] expr H-pattern)]{
Like the @Spattern version of @scheme[~describe], but matches a head
pattern instead.
}
@specsubform[S-pattern]{
Matches a head of one element, which must be a term matching
@scheme[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 may
also place contraints on the number of times it occurs in a
repetition. They are useful for matching keyword arguments where the
keywords may come in any order.
@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 @scheme[#:a]
keyword and argument, at most one occurrence of the @scheme[#:b]
keyword and argument, and any number of @scheme[#:c] keywords and
arguments. The ``pieces'' can occur in any order.
Here are the variants of @elem{@EHpattern}:
@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)])]{
Matches if the inner @scheme[H-pattern] matches. This pattern must be
selected exactly once in the match of the entire repetition sequence.
If the pattern is not chosen in the repetition sequence, then an error
is raised with a message, either @scheme[too-few-message-expr] or
@schemevalfont{"missing required occurrence of @scheme[name-expr]"}.
If the pattern is chosen more than once in the repetition sequence,
then an error is raised with a message, either
@scheme[too-many-message-expr] or @schemevalfont{"too many occurrences
of @scheme[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] ...))])]{
Matches if the inner @scheme[H-pattern] matches. This pattern may be used at
most once in the match of the entire repetition.
If the pattern is chosen more than once in the repetition sequence,
then an error is raised with a message, either
@scheme[too-many-message-expr] or @schemevalfont{"too many occurrences
of @scheme[name-expr]"}.
If the @scheme[#: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.
}
@;{--------}
@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.
The grammar describing where an @Apattern may occur may look
complicated, but the essence is this: ``@Apatterns don't take up
space.'' They can be freely added to a list pattern or inserted into
an @scheme[~and] pattern.
@specsubform[@#,defhere[~!]]{
The @deftech{cut} operator, written @scheme[~!], 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:
@ -496,9 +781,8 @@ are discarded.
@specsubform[(@#,defhere[~bind] [attr-id expr] ...)]{
This pattern matches any term. Its effect is to evaluate the
@scheme[expr]s and bind them to the given @scheme[attr-id]s as
attributes.
Evaluates the @scheme[expr]s and binds them to the given
@scheme[attr-id]s as attributes.
}
@specsubform/subs[(@#,defhere[~fail] maybe-fail-condition message-expr)
@ -506,206 +790,17 @@ attributes.
(code:line #:when condition-expr)
(code:line #:unless condition-expr)])]{
This pattern succeeds or fails independent of the term being matched
against. If the condition is absent, or if the @scheme[#:when]
If the condition is absent, or if the @scheme[#:when]
condition evaluates to a true value, or if the @scheme[#:unless]
condition evaluates to @scheme[#f], then the pattern fails with the
given message. Otherwise the pattern succeeds.
given message.
Fail patterns can be used together with cut patterns to recognize
specific ill-formed terms and address them with specially-created
failure messages.
}
@specsubform[(@#,def[~and a] A-pattern ...+)]{
@section{H-pattern variants}
An @deftech{H-pattern} (for ``head pattern'') is a pattern that
describes some number of terms that occur at the head of some list
(possibly an improper list). An H-pattern's usefulness comes from
being able to match heads of different lengths. H-patterns are useful
for specifying optional forms such as keyword arguments.
Here are the variants of @tech{H-pattern}:
@specsubform[pvar-id:splicing-syntax-class-id]{
Equivalent to @scheme[(~var pvar-id
splicing-syntax-class-id)].
}
@specsubform/subs[(@#,def[~var h] pvar-id splicing-syntax-class)
([splicing-syntax-class splicing-syntax-class-id
(splicing-syntax-class-id arg-expr ...)])]{
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 head whose elements, if put in a list, would match the given
@tech{L-pattern}.
@myexamples[
(syntax-parse #'(1 2 3 4)
[((~seq 1 2 3) 4) 'ok])
]
See also the section on @tech{EH-patterns} for more interesting
examples of @scheme[~seq].
}
@specsubform[(@#,def[~and h] H-pattern ...)]{
Like the S-pattern version of @scheme[~and], but matches a term head
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 H-pattern variant of @scheme[~and] requires that all of the
subpatterns be strictly @tech{H-patterns} and not
@tech{S-patterns}. This is to prevent typos like the following, a
variant of the previous example with the second @scheme[~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 S-pattern version of @scheme[~or], but matches a term head
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-id expr] ...))])]{
Matches either the given head subpattern or an empty head. If the
@scheme[#: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)])
]
}
@specsubform[(@#,def[~describe h] expr H-pattern)]{
Like the S-pattern version of @scheme[~describe], but matches a head
pattern instead.
}
@specsubform[S-pattern]{
Matches a head of one element, which must be a term matching the given
@tech{S-pattern}.
}
@;{--------}
@section{EH-pattern forms}
An @deftech{EH-pattern} (for ``ellipsis-head pattern'') is pattern
that describes some number of terms, like an @tech{H-pattern}, but may
also place contraints on the number of times it occurs in a
repetition. EH-patterns (and ellipses) are useful for matching keyword
arguments where the keywords may come in any order.
@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 @scheme[#:a]
keyword and argument, at most one occurrence of the @scheme[#:b]
keyword and argument, and any number of @scheme[#:c] keywords and
arguments. The ``pieces'' can occur in any order.
Here are the variants of @tech{EH-pattern}:
@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)])]{
Matches if the inner H-pattern matches. This pattern must be selected
exactly once in the match of the entire repetition sequence.
If the pattern is not chosen in the repetition sequence, then an error
is raised with a message, either @scheme[too-few-message-expr] or
@schemevalfont{"missing required occurrence of @scheme[name-expr]"}.
If the pattern is chosen more than once in the repetition sequence,
then an error is raised with a message, either
@scheme[too-many-message-expr] or @schemevalfont{"too many occurrences
of @scheme[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] ...))])]{
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 chosen more than once in the repetition sequence,
then an error is raised with a message, either
@scheme[too-many-message-expr] or @schemevalfont{"too many occurrences
of @scheme[name-expr]"}.
If the @scheme[#: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.
Performs the actions of each @scheme[A-pattern].
}

View File

@ -191,10 +191,9 @@ The second pattern matches unparenthesized identifiers. The @scheme[e]
attribute is bound using a @scheme[#:with] clause, which matches the
pattern @scheme[e] against the syntax from evaluating @scheme[#'#f].
Optional keyword arguments are supported via ``head patterns'' (called
@tech{H-patterns} in the reference documentation). Unlike normal
patterns, which match one term, head patterns can match a variable
number of subterms in a list.
Optional keyword arguments are supported via @tech{head
patterns}. Unlike normal patterns, which match one term, head patterns
can match a variable number of subterms in a list.
Suppose @schemekeywordfont{mylet} accepted an optional
@scheme[#:check] keyword with one argument, a procedure that would be
@ -394,7 +393,7 @@ structures can share syntax class definitions.
(pattern syntax-pattern pattern-directive ...)])]{
Defines @scheme[name-id] as a @deftech{syntax class}, which
encapsulates one or more @tech{S-patterns}.
encapsulates one or more @tech{single-term patterns}.
When the @scheme[arg-id]s are present, they are bound as variables in
the body. The body of the syntax-class definition contains a non-empty
@ -449,7 +448,8 @@ These options have the same meaning as in @scheme[syntax-parse].
}
Each variant of a syntax class is specified as a separate
@scheme[pattern]-form whose syntax pattern is an @tech{S-pattern}.
@scheme[pattern]-form whose syntax pattern is a @tech{single-term
pattern}.
}
@defform*[#:literals (pattern)
@ -459,13 +459,13 @@ Each variant of a syntax class is specified as a separate
stxclass-variant ...+)]]{
Defines @scheme[name-id] as a @deftech{splicing syntax class},
analogous to a @tech{syntax class} but encapsulating @tech{H-patterns}
rather than @tech{S-patterns}.
analogous to a @tech{syntax class} but encapsulating @tech{head
patterns} rather than @tech{single-term patterns}.
The options are the same as for @scheme[define-syntax-class].
Each variant of a splicing syntax class is specified as a separate
@scheme[pattern]-form whose syntax pattern is an @tech{H-pattern}.
@scheme[pattern]-form whose syntax pattern is a @tech{head pattern}.
}
@defform[#:literals (pattern)
@ -476,9 +476,9 @@ class. The variant accepts syntax matching the given syntax pattern
with the accompanying @tech{pattern directives}.
When used within @scheme[define-syntax-class], @scheme[syntax-pattern]
should be an @tech{S-pattern}; within
@scheme[define-splicing-syntax-class], it should be an
@tech{H-pattern}.
should be a @tech{single-term pattern}; within
@scheme[define-splicing-syntax-class], it should be a @tech{head
pattern}.
The attributes of the variant are the attributes of the pattern
together with all attributes bound by @scheme[#:with] clauses,