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

View File

@ -7,77 +7,85 @@
(provide (all-defined-out)) (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, If P = (make-pattern Attrs ...) and A is in Attrs,
the depth of A is with respect to P, the depth of A is with respect to P,
not with respect to the entire enclosing pattern. 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 A SinglePattern is one of
(make-pat:any SPBase) (make-pat:any Base)
(make-pat:var SPBase id id (listof stx) (listof IAttr)) (make-pat:var Base id id (listof stx) (listof IAttr))
(make-pat:datum SPBase datum) (make-pat:literal Base identifier)
(make-pat:literal SPBase identifier) (make-pat:datum Base datum)
(make-pat:head SPBase HeadPattern SinglePattern) (make-pat:ghost Base GhostPattern SinglePattern)
(make-pat:dots SPBase (listof EllipsisHeadPattern) SinglePattern) (make-pat:head Base HeadPattern SinglePattern)
(make-pat:and SPBase (listof SinglePattern)) (make-pat:dots Base (listof EllipsisHeadPattern) SinglePattern)
(make-pat:or SPBase (listof SinglePattern)) (make-pat:and Base (listof SinglePattern))
(make-pat:not SPBase SinglePattern) (make-pat:or Base (listof SinglePattern))
(make-pat:compound SPBase Kind (listof SinglePattern)) (make-pat:not Base SinglePattern)
(make-pat:cut SPBase SinglePattern) (make-pat:compound Base Kind (listof SinglePattern))
(make-pat:describe SPBase stx boolean SinglePattern) (make-pat:describe Base stx boolean SinglePattern)
(make-pat:fail SPBase stx stx)
(make-pat:bind SPBase (listof clause:attr))
A ListPattern is a subtype of SinglePattern; one of A ListPattern is a subtype of SinglePattern; one of
(make-pat:datum SPBase '()) (make-pat:datum Base '())
(make-pat:head SPBase HeadPattern ListPattern) (make-pat:ghost Base GhostPattern ListPattern)
(make-pat:compound SPBase '#:pair (list SinglePattern ListPattern)) (make-pat:head Base HeadPattern ListPattern)
(make-pat:dots SPBase EllipsisHeadPattern SinglePattern) (make-pat:compound Base '#:pair (list SinglePattern ListPattern))
(make-pat:cut SPBase ListPattern) (make-pat:dots Base EllipsisHeadPattern SinglePattern)
|# |#
(define-struct pat:any (attrs) #:prefab) (define-struct pat:any (attrs) #:prefab)
(define-struct pat:var (attrs name parser args nested-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: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:head (attrs head tail) #:prefab)
(define-struct pat:dots (attrs heads tail) #:prefab) (define-struct pat:dots (attrs heads tail) #:prefab)
(define-struct pat:and (attrs patterns) #:prefab) (define-struct pat:and (attrs patterns) #:prefab)
(define-struct pat:or (attrs patterns) #:prefab) (define-struct pat:or (attrs patterns) #:prefab)
(define-struct pat:not (attrs pattern) #:prefab) (define-struct pat:not (attrs pattern) #:prefab)
(define-struct pat:compound (attrs kind patterns) #: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: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 A HeadPattern is one of
(make-hpat:var SPBase id id (listof stx) (listof IAttr)) (make-hpat:var Base id id (listof stx) (listof IAttr))
(make-hpat:seq HPBase ListPattern) (make-hpat:seq Base ListPattern)
(make-hpat:and HPBase HeadPattern SinglePattern) (make-hpat:ghost Base GhostPattern HeadPattern)
(make-hpat:or HPBase (listof HeadPattern)) (make-hpat:and Base HeadPattern SinglePattern)
(make-hpat:describe HPBase stx/#f boolean HeadPattern) (make-hpat:or Base (listof HeadPattern))
(make-hpat:optional HPBase HeadPattern (listof clause:attr)) (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:var (attrs name parser args nested-attrs) #:prefab)
(define-struct hpat:seq (attrs inner) #: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: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:optional (attrs inner defaults) #:prefab)
(define-struct hpat:describe (attrs description transparent? pattern) #:prefab)
#| #|
An EllipsisHeadPattern is An EllipsisHeadPattern is
(make-ehpat EHPBase HeadPattern RepConstraint) (make-ehpat Base HeadPattern RepConstraint)
A RepConstraint is one of A RepConstraint is one of
(make-rep:once stx stx stx) (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) (make-rep:bounds nat/#f nat/#f stx stx stx)
#f #f
|# |#
(define-struct ehpat (attrs head repc) #:prefab) (define-struct ehpat (attrs head repc) #:prefab)
(define-struct rep:once (name under-message over-message) #:prefab) (define-struct rep:once (name under-message over-message) #:prefab)
(define-struct rep:optional (name over-message defaults) #:prefab) (define-struct rep:optional (name over-message defaults) #:prefab)
@ -102,26 +111,31 @@ A Kind is one of
(define (pattern? x) (define (pattern? x)
(or (pat:any? x) (or (pat:any? x)
(pat:var? x) (pat:var? x)
(pat:datum? x)
(pat:literal? x) (pat:literal? x)
(pat:datum? x)
(pat:ghost? x)
(pat:head? x) (pat:head? x)
(pat:dots? x) (pat:dots? x)
(pat:and? x) (pat:and? x)
(pat:or? x) (pat:or? x)
(pat:not? x) (pat:not? x)
(pat:compound? x) (pat:compound? x)
(pat:cut? x) (pat:describe? x)))
(pat:describe? x)
(pat:bind? x) (define (ghost-pattern? x)
(pat:fail? x))) (or (ghost:cut? x)
(ghost:bind? x)
(ghost:fail? x)
(ghost:and? x)))
(define (head-pattern? x) (define (head-pattern? x)
(or (hpat:var? x) (or (hpat:var? x)
(hpat:seq? x) (hpat:seq? x)
(hpat:ghost? x)
(hpat:and? x) (hpat:and? x)
(hpat:or? x) (hpat:or? x)
(hpat:describe? x) (hpat:optional? x)
(hpat:optional? x))) (hpat:describe? x)))
(define (ellipsis-head-pattern? x) (define (ellipsis-head-pattern? x)
(ehpat? x)) (ehpat? x))
@ -147,10 +161,10 @@ A Kind is one of
#'(lambda (x) #'(lambda (x)
(cond [(pred x) (accessor x)] ... (cond [(pred x) (accessor x)] ...
[else (raise-type-error 'pattern-attrs "pattern" x)])))])) [else (raise-type-error 'pattern-attrs "pattern" x)])))]))
(mk-get-attrs pat:any pat:var pat:datum pat:literal pat:head pat:dots (mk-get-attrs pat:any pat:var pat:datum pat:literal pat:ghost pat:head
pat:and pat:or pat:not pat:compound pat:dots pat:and pat:or pat:not pat:compound pat:describe
pat:cut pat:describe pat:bind pat:fail ghost:cut ghost:bind ghost:fail ghost:and
hpat:var hpat:seq hpat:and hpat:or hpat:describe hpat:var hpat:seq hpat:ghost hpat:and hpat:or hpat:describe
hpat:optional hpat:optional
ehpat))) ehpat)))
@ -174,12 +188,21 @@ A Kind is one of
(define (create-pat:literal literal) (define (create-pat:literal literal)
(make pat:literal null 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) (define (create-pat:compound kind ps)
(make pat:compound (append-iattrs (map pattern-attrs ps)) 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) (define (create-pat:describe description transparent? p)
(make pat:describe (pattern-attrs p) 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)))]) (let ([attrs (append-iattrs (map pattern-attrs (cons tailp headps)))])
(make pat:dots attrs headps tailp))) (make pat:dots attrs headps tailp)))
(define (create-pat:fail condition message) ;; ----
(make pat:fail null condition message))
(define (create-pat:head headp tailp) (define (create-ghost:cut)
(let ([attrs (append-iattrs (map pattern-attrs (list headp tailp)))]) (make ghost:cut null))
(make pat:head attrs headp tailp)))
(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) (define (create-hpat:seq lp)
(make hpat:seq (pattern-attrs lp) 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) (define (create-hpat:describe description transparent? p)
(make hpat:describe (pattern-attrs p) 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) (define (ghost/head-pattern->list-pattern p)
;; simplification: just extract list pattern from hpat:seq (cond [(ghost-pattern? p)
(if (hpat:seq? hp) (create-pat:ghost p (create-pat:any))]
(hpat:seq-inner hp) [(hpat:seq? p)
(create-pat:head hp (create-pat:datum '())))) ;; 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 ;; parse-single-pattern : stx DeclEnv -> SinglePattern
(define (parse-single-pattern stx decls) (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 (syntax-case stx (~var ~literal ~and ~or ~not ~rest ~struct
~! ~describe ~bind ~fail) ~! ~describe ~bind ~fail ~seq ~optional)
[wildcard [wildcard
(wildcard? #'wildcard) (wildcard? #'wildcard)
(create-pat:any)] (create-pat:any)]
[~!
(check-ghost!
(create-ghost:cut))]
[reserved [reserved
(reserved? #'reserved) (reserved? #'reserved)
(wrong-syntax stx "not allowed here")] (wrong-syntax stx "pattern keyword not allowed here")]
[id [id
(identifier? #'id) (identifier? #'id)
(parse-pat:id stx decls #f)] (parse-pat:id stx decls allow-head?)]
[datum [datum
(atomic-datum? #'datum) (atomic-datum? #'datum)
(create-pat:datum (syntax->datum #'datum))] (create-pat:datum (syntax->datum #'datum))]
[(~var . rest) [(~var . rest)
(parse-pat:var stx decls #f)] (parse-pat:var stx decls allow-head?)]
[(~literal . rest) [(~literal . rest)
(parse-pat:literal stx decls)] (parse-pat:literal stx decls)]
[(~and . rest) [(~and . rest)
(parse-pat:and stx decls #f)] (parse-pat:and stx decls allow-head? allow-ghost?)]
[(~or . rest) [(~or . rest)
(parse-pat:or stx decls #f)] (parse-pat:or stx decls allow-head?)]
[(~not . rest) [(~not . rest)
(parse-pat:not stx decls)] (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) [(head dots . tail)
(dots? #'dots) (dots? #'dots)
(parse-pat:dots stx #'head #'tail decls)] (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) [(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 ...) [#(a ...)
(let ([lp (parse-single-pattern (syntax/loc stx (a ...)) decls)]) (let ([lp (parse-single-pattern (syntax/loc stx (a ...)) decls)])
(create-pat:compound '#:vector (list lp)))] (create-pat:compound '#:vector (list lp)))]
@ -318,27 +352,6 @@
(let ([lp (parse-single-pattern (datum->syntax #f contents #'s) decls)]) (let ([lp (parse-single-pattern (datum->syntax #f contents #'s) decls)])
(create-pat:compound `(#:pstruct ,key) (list lp))))])) (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 ;; parse-ellipsis-head-pattern : stx DeclEnv number -> EllipsisHeadPattern
(define (parse-ellipsis-head-pattern stx decls) (define (parse-ellipsis-head-pattern stx decls)
(syntax-case stx (~bounds ~optional ~once) (syntax-case stx (~bounds ~optional ~once)
@ -374,10 +387,9 @@
[(list 'splicing-parser parser description attrs) [(list 'splicing-parser parser description attrs)
(parse-pat:id/h id parser null attrs)] (parse-pat:id/h id parser null attrs)]
[#f [#f
#| (when #f ;; FIXME: enable?
(unless (safe-name? id) (unless (safe-name? id)
(wrong-syntax id "expected identifier not starting with ~ character")) (wrong-syntax id "expected identifier not starting with ~ character")))
|#
(let-values ([(name sc) (split-id/get-stxclass id decls)]) (let-values ([(name sc) (split-id/get-stxclass id decls)])
(if sc (if sc
(parse-pat:var* id allow-head? name sc null) (parse-pat:var* id allow-head? name sc null)
@ -486,11 +498,60 @@
(define transparent? (and (assq '#:transparent chunks) #t)) (define transparent? (and (assq '#:transparent chunks) #t))
(syntax-case rest () (syntax-case rest ()
[(description pattern) [(description pattern)
(let ([p (parse-some-pattern #'pattern decls allow-head?)]) (let ([p (parse-*-pattern #'pattern decls allow-head? #f)])
(if (head-pattern? p) (if (head-pattern? p)
(create-hpat:describe #'description transparent? p) (create-hpat:describe #'description transparent? p)
(create-pat: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 (parse-pat:or stx decls allow-head?)
(define patterns (parse-cdr-patterns stx decls allow-head? #f)) (define patterns (parse-cdr-patterns stx decls allow-head? #f))
(cond [(null? (cdr patterns)) (cond [(null? (cdr patterns))
@ -501,24 +562,6 @@
[else [else
(create-pat:or patterns)])])) (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) (define (parse-pat:not stx decls)
(syntax-case stx (~not) (syntax-case stx (~not)
[(~not pattern) [(~not pattern)
@ -532,31 +575,16 @@
(check-list-pattern pattern stx) (check-list-pattern pattern stx)
(create-hpat:seq pattern)) (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) (unless (stx-list? stx)
(wrong-syntax stx "expected sequence of patterns")) (wrong-syntax stx "expected sequence of patterns"))
(let ([result (let ([result
(for/list ([sub (cdr (stx->list stx))]) (for/list ([sub (cdr (stx->list stx))])
(if allow-cut? (parse-*-pattern sub decls allow-head? allow-ghost?))])
(or (parse-cut-in-and sub)
(parse-some-pattern sub decls allow-head?))
(parse-some-pattern sub decls allow-head?)))])
(when (null? result) (when (null? result)
(wrong-syntax stx "expected at least one pattern")) (wrong-syntax stx "expected at least one pattern"))
result)) 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 (parse-pat:dots stx head tail decls)
(define headps (define headps
(syntax-case head (~or) (syntax-case head (~or)
@ -577,7 +605,7 @@
(syntax-case stx () (syntax-case stx ()
[(_ clause ...) [(_ clause ...)
(let ([clauses (check-bind-clause-list #'(clause ...) stx)]) (let ([clauses (check-bind-clause-list #'(clause ...) stx)])
(make pat:bind (make ghost:bind
(append-iattrs (side-clauses-attrss clauses)) (append-iattrs (side-clauses-attrss clauses))
clauses))])) clauses))]))
@ -598,7 +626,7 @@
#`(not #,(caddr chunk)))))]) #`(not #,(caddr chunk)))))])
(syntax-case rest () (syntax-case rest ()
[(message) [(message)
(create-pat:fail condition #'message)] (create-ghost:fail condition #'message)]
[() [()
(wrong-syntax stx "missing message expression")] (wrong-syntax stx "missing message expression")]
[_ [_
@ -609,15 +637,6 @@
[(_ pattern) [(_ pattern)
(parse-single-pattern #'pattern decls)])) (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) (define (check-list-pattern pattern stx)
(match pattern (match pattern
[(struct pat:datum (_base '())) [(struct pat:datum (_base '()))

View File

@ -12,6 +12,18 @@
@(define ellipses @scheme[...]) @(define ellipses @scheme[...])
@(define-syntax-rule (defhere id) (defidentifier #'id #:form? #t)) @(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 @(begin
(define-syntax ref (define-syntax ref
(syntax-rules () (syntax-rules ()
@ -56,15 +68,20 @@
The grammar of @deftech{syntax patterns} used by The grammar of @deftech{syntax patterns} used by
@schememodname[syntax/parse] facilities is given in the following @schememodname[syntax/parse] facilities is given in the following
table. There are three main kinds of syntax pattern: @tech{S-patterns} table. There are four main kinds of syntax pattern:
(for ``single-term patterns''), @tech{H-patterns} (for ``head @itemize[
patterns''), and @tech{EH-patterns} (for ``ellipsis head @item{@tech{@Spatterns}, abbreviated @svar[S-pattern]}
patterns''). A fourth kind, @tech{L-patterns} (for ``list patterns''), @item{@tech{@Hpatterns}, abbreviated @svar[H-pattern]}
is a restricted subset of @tech{S-patterns}. @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] When a special form in this manual refers to @svar[syntax-pattern]
(eg, the description of the @scheme[syntax-parse] special form), it (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 @schemegrammar*[#:literals (_ ~var ~literal ~or ~and ~not ~seq
~rep ~once ~optional ~rep ~once ~optional
@ -78,81 +95,118 @@ means specifically @tech{S-pattern}.
(~literal literal-id) (~literal literal-id)
atomic-datum atomic-datum
(H-pattern . S-pattern) (H-pattern . S-pattern)
(A-pattern . S-pattern)
((@#,ref[~or eh] EH-pattern ...+) #,ellipses . S-pattern) ((@#,ref[~or eh] EH-pattern ...+) #,ellipses . S-pattern)
(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 ...+) (@#,ref[~or s] S-pattern ...+)
(~not S-pattern) (~not S-pattern)
#((unsyntax @svar[pattern-part]) ...) #((unsyntax @svar[pattern-part]) ...)
#s(prefab-struct-key (unsyntax @svar[pattern-part]) ...) #s(prefab-struct-key (unsyntax @svar[pattern-part]) ...)
(~rest S-pattern) (~rest S-pattern)
(@#,ref[~describe s] expr S-pattern) (@#,ref[~describe s] expr S-pattern)
(~! . S-pattern) A-pattern]
(~bind [attr-id expr] ...)
(~fail maybe-fail-condition message-expr)]
[L-pattern [L-pattern
() ()
(A-pattern . L-pattern)
(H-pattern . L-pattern) (H-pattern . L-pattern)
((@#,ref[~or eh] EH-pattern ...+) #,ellipses . L-pattern) ((@#,ref[~or eh] EH-pattern ...+) #,ellipses . L-pattern)
(EH-pattern #,ellipses . L-pattern) (EH-pattern #,ellipses . L-pattern)
(~rest L-pattern) (~rest L-pattern)]
(~! . L-pattern)]
[H-pattern [H-pattern
pvar-id:splicing-syntax-class-id pvar-id:splicing-syntax-class-id
(@#,ref[~var h] id splicing-syntax-class) (@#,ref[~var h] id splicing-syntax-class)
(~seq . L-pattern) (~seq . L-pattern)
(@#,ref[~and h] strict-H-pattern ...+) (@#,ref[~and h] proper-H/A-pattern ...+)
(@#,ref[~or h] H-pattern ...+) (@#,ref[~or h] H-pattern ...+)
(@#,ref[~optional h] H-pattern maybe-optional-option) (@#,ref[~optional h] H-pattern maybe-optional-option)
(@#,ref[~describe h] expr H-pattern) (@#,ref[~describe h] expr H-pattern)
S-pattern] proper-S-pattern]
[EH-pattern [EH-pattern
(~once H-pattern once-option ...) (~once H-pattern once-option ...)
(@#,ref[~optional eh] H-pattern optional-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 The following pattern keywords can be used in multiple pattern
variants: variants:
@defidform[~var]{ @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]{ @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]{ @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]{ @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]{ @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 A @deftech{@Spattern} (abbreviated @svar[S-pattern]) is a pattern that
describes a single term. The pattern may, of course, consist of other describes a single term. These are like the traditional patterns used
parts. For example, @scheme[(17 ...)] is an @tech{S-pattern} in @scheme[syntax-rules] and @scheme[syntax-case], but with additional
that matches any term that is a proper list of repeated variants that make them more expressive.
@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.
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]{ @specsubform[id]{
@ -285,32 +339,46 @@ literals.
@specsubform[(H-pattern . S-pattern)]{ @specsubform[(H-pattern . S-pattern)]{
Matches any term that can be decomposed into a list prefix matching 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 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 lists; if the head pattern can match a zero-length head, then the
whole pattern matches whatever the tail pattern accepts. 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 pattern matches any pair whose first element matches the first pattern
and whose rest matches the second. 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)]{ @specsubform[((@#,def[~or eh] EH-pattern ...+) #,ellipses . S-pattern)]{
Matches any term that can be decomposed into a list head matching some Matches any term that can be decomposed into a list head matching some
number of repetitions of the @tech{EH-pattern} alternatives (subject number of repetitions of @scheme[EH-pattern] alternatives (subject to
to its repetition constraints) followed by a list tail matching the its repetition constraints) followed by a list tail matching
S-pattern. @scheme[S-pattern].
In other words, the whole pattern matches either the second 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 (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 alternatives of the first pattern and whose tail recursively matches
the whole sequence pattern. the whole sequence pattern.
See @tech{EH-patterns} for more information. See @tech{@EHpatterns} for more information.
} }
@specsubform[(EH-pattern #,ellipses . S-pattern)]{ @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. 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. 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 Attributes bound in subpatterns are available to subsequent
subpatterns. The whole pattern binds all of the subpatterns' subpatterns. The whole pattern binds all of the subpatterns'
attributes. attributes.
@ -348,7 +419,6 @@ purpose, but @scheme[~and] can be lighter weight.
#f "bad import" #'import-clause bad)) #f "bad import" #'import-clause bad))
'ok)]) 'ok)])
] ]
} }
@specsubform[(@#,def[~or s] S-pattern ...)]{ @specsubform[(@#,def[~or s] S-pattern ...)]{
@ -372,8 +442,9 @@ to have a value if the whole pattern matches.
@specsubform[(@#,defhere[~not] S-pattern)]{ @specsubform[(@#,defhere[~not] S-pattern)]{
Matches any term that does not match the subpattern. The subpattern's Matches any term that does not match the subpattern. None of the
attributes are @emph{not} bound outside of the @scheme[~not]-pattern. subpattern's attributes are bound outside of the
@scheme[~not]-pattern.
@myexamples[ @myexamples[
(syntax-parse #'(x y z => u v) (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] ...)]{ @specsubform[#(#, @svar[pattern-part] ...)]{
Matches a term that is a vector whose elements, when considered as a 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 ...)]. @scheme[(pattern-part ...)].
@myexamples[ @myexamples[
@ -397,14 +468,13 @@ list, match the @tech{S-pattern} corresponding to
(syntax-parse #'#(1 2 3) (syntax-parse #'#(1 2 3)
[#(x ~rest y) (syntax->datum #'y)]) [#(x ~rest y) (syntax->datum #'y)])
] ]
} }
@specsubform[#s(prefab-struct-key #, @svar[pattern-part] ...)]{ @specsubform[#s(prefab-struct-key #, @svar[pattern-part] ...)]{
Matches a term that is a prefab struct whose key is exactly the given 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 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[ @myexamples[
(syntax-parse #'#s(point 1 2 3) (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)]{ @specsubform[(#, @defhere[~rest] S-pattern)]{
Matches just like the inner @scheme[S-pattern]. The @scheme[~rest] Matches just like @scheme[S-pattern]. The @scheme[~rest] pattern form
pattern form is useful in positions where improper lists (``dots'') is useful in positions where improper (``dotted'') lists are not
are not allowed by the reader, such as vector and structure patterns allowed by the reader, such as vector and structure patterns (see
(see above). above).
@myexamples[ @myexamples[
(syntax-parse #'(1 2 3) (syntax-parse #'(1 2 3)
@ -442,19 +512,234 @@ A describe-pattern also affects backtracking in two ways:
@itemize{ @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.} eliminates choice-points created within the describe-pattern.}
@item{If a describe-pattern succeeds, then all choice points created @item{If a describe-pattern succeeds, then all choice points
within the describe-pattern are discarded, and a failure @emph{after} created within the describe-pattern are discarded, and a failure
the describe-pattern backtracks to a choice point @emph{before} the @emph{after} the describe-pattern backtracks to a choice point
describe-pattern, never one @emph{within} it.}}} @emph{before} the describe-pattern, never one @emph{within} it.}
}
}
@specsubform[(@#,defhere[~!] . S-pattern)]{ @specsubform[A-pattern]{
The @scheme[~!] operator, pronounced ``cut'', eliminates backtracking An @tech{@Apattern} is considered a @Spattern when there is no
choice points and commits parsing to the current branch of the pattern ambiguity; it matches any term.
it is exploring. }
@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 Common opportunities for cut-patterns come from recognizing special
forms based on keywords. Consider the following expression: forms based on keywords. Consider the following expression:
@ -496,9 +781,8 @@ are discarded.
@specsubform[(@#,defhere[~bind] [attr-id expr] ...)]{ @specsubform[(@#,defhere[~bind] [attr-id expr] ...)]{
This pattern matches any term. Its effect is to evaluate the Evaluates the @scheme[expr]s and binds them to the given
@scheme[expr]s and bind them to the given @scheme[attr-id]s as @scheme[attr-id]s as attributes.
attributes.
} }
@specsubform/subs[(@#,defhere[~fail] maybe-fail-condition message-expr) @specsubform/subs[(@#,defhere[~fail] maybe-fail-condition message-expr)
@ -506,206 +790,17 @@ attributes.
(code:line #:when condition-expr) (code:line #:when condition-expr)
(code:line #:unless condition-expr)])]{ (code:line #:unless condition-expr)])]{
This pattern succeeds or fails independent of the term being matched If the condition is absent, or if the @scheme[#:when]
against. If the condition is absent, or if the @scheme[#:when]
condition evaluates to a true value, or if the @scheme[#:unless] condition evaluates to a true value, or if the @scheme[#:unless]
condition evaluates to @scheme[#f], then the pattern fails with the 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 Fail patterns can be used together with cut patterns to recognize
specific ill-formed terms and address them with specially-created specific ill-formed terms and address them with specially-created
failure messages. failure messages.
} }
@specsubform[(@#,def[~and a] A-pattern ...+)]{
@section{H-pattern variants} Performs the actions of each @scheme[A-pattern].
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.
} }

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 attribute is bound using a @scheme[#:with] clause, which matches the
pattern @scheme[e] against the syntax from evaluating @scheme[#'#f]. pattern @scheme[e] against the syntax from evaluating @scheme[#'#f].
Optional keyword arguments are supported via ``head patterns'' (called Optional keyword arguments are supported via @tech{head
@tech{H-patterns} in the reference documentation). Unlike normal patterns}. Unlike normal patterns, which match one term, head patterns
patterns, which match one term, head patterns can match a variable can match a variable number of subterms in a list.
number of subterms in a list.
Suppose @schemekeywordfont{mylet} accepted an optional Suppose @schemekeywordfont{mylet} accepted an optional
@scheme[#:check] keyword with one argument, a procedure that would be @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 ...)])]{ (pattern syntax-pattern pattern-directive ...)])]{
Defines @scheme[name-id] as a @deftech{syntax class}, which 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 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 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 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) @defform*[#:literals (pattern)
@ -459,13 +459,13 @@ Each variant of a syntax class is specified as a separate
stxclass-variant ...+)]]{ stxclass-variant ...+)]]{
Defines @scheme[name-id] as a @deftech{splicing syntax class}, Defines @scheme[name-id] as a @deftech{splicing syntax class},
analogous to a @tech{syntax class} but encapsulating @tech{H-patterns} analogous to a @tech{syntax class} but encapsulating @tech{head
rather than @tech{S-patterns}. patterns} rather than @tech{single-term patterns}.
The options are the same as for @scheme[define-syntax-class]. The options are the same as for @scheme[define-syntax-class].
Each variant of a splicing syntax class is specified as a separate 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) @defform[#:literals (pattern)
@ -476,9 +476,9 @@ class. The variant accepts syntax matching the given syntax pattern
with the accompanying @tech{pattern directives}. with the accompanying @tech{pattern directives}.
When used within @scheme[define-syntax-class], @scheme[syntax-pattern] When used within @scheme[define-syntax-class], @scheme[syntax-pattern]
should be an @tech{S-pattern}; within should be a @tech{single-term pattern}; within
@scheme[define-splicing-syntax-class], it should be an @scheme[define-splicing-syntax-class], it should be a @tech{head
@tech{H-pattern}. pattern}.
The attributes of the variant are the attributes of the pattern The attributes of the variant are the attributes of the pattern
together with all attributes bound by @scheme[#:with] clauses, together with all attributes bound by @scheme[#:with] clauses,