89 lines
4.3 KiB
Racket
89 lines
4.3 KiB
Racket
#lang scribble/manual
|
|
@require[scribble/example
|
|
"utils.rkt"
|
|
@for-label[phc-toolkit/untyped
|
|
extensible-parser-specifications
|
|
generic-syntax-expanders
|
|
racket/base
|
|
syntax/parse
|
|
(only-in racket/base [... …])]]
|
|
|
|
@title{Parsing the tail of improper lists}
|
|
|
|
@defform[#:kind "eh-mixin expander"
|
|
{~lift-rest pat}]{
|
|
Lifts @racket[pat] out of the current mixin, so that it is used as a pattern to
|
|
match the tail of the improper list being matched. It is subject to the
|
|
following restrictions:
|
|
@itemlist[
|
|
@item{@racket[~lift-rest] is allowed only within @racket[~no-order], but not
|
|
within @racket[~seq-no-order]. @racket[~seq-no-order] always matches against
|
|
a proper sequence of elements, while @racket[~no-order] may match a proper or
|
|
improper list.}
|
|
@item{The tail of the improper list must not be a pair, otherwise the
|
|
@racket[car] would have been included in the main part of the list.}
|
|
@item{The @racket[pat] is used to match the tail only if its surrounding
|
|
pattern successfully matched some elements of the main section of the list.
|
|
|
|
If the @racket[{~lift-rest pat}] is the only pattern present within an
|
|
alternative, then it is always used.
|
|
|
|
@examples[#:eval (make-evaluator)
|
|
(syntax-parse #'(x y z . 1)
|
|
[(~no-order {~lift-rest r:nat} i:id)
|
|
(syntax->datum #'(r i ...))])]}
|
|
@item{
|
|
Among the lifted rest patterns which are considered (see the point
|
|
above), only one may successfully match. An error is raised if two or more
|
|
lifted rest patterns successfully match against the tail of the list.
|
|
|
|
@examples[#:eval (make-evaluator)
|
|
(eval:no-prompt
|
|
(define p
|
|
(syntax-parser
|
|
[(~no-order {~and {~literal x}
|
|
{~lift-rest rn:nat}
|
|
{~lift-rest ri:id}}
|
|
{~and {~literal y}
|
|
{~lift-rest rs:str}
|
|
{~lift-rest rj:id}})
|
|
'match]
|
|
#;[_
|
|
'fail])))
|
|
(code:line (p #'(x . 1)) (code:comment "rn and ri considered, rn matched"))
|
|
(code:line (p #'(x . z)) (code:comment "rn and ri considered, ri matched"))
|
|
(code:line (p #'(y . "a")) (code:comment "rs and rj considered, rs matched"))
|
|
(code:line (p #'(y . z)) (code:comment "rs and rj considered, rj matched"))
|
|
(code:line (p #'(x y . 1)) (code:comment "all four considered, rn matched"))
|
|
(eval:alts (code:line (p #'(x y . z)) (code:comment "all four considered, both ri and rj matched"))
|
|
(eval:error (p #'(x y . z))))]
|
|
|
|
The rationale is that selecting the first lifted rest pattern that matches
|
|
would result in unclear behaviour, as the order of the alternative clauses
|
|
should not be significant.}
|
|
@item{Post and global operations can be used within the @racket[pat]. This
|
|
combination of features is not thoroughly tested, however. Please report any
|
|
issues you run into.}]}
|
|
|
|
@defform[#:kind "eh-mixin expander"
|
|
{~as-rest pat ...}]{
|
|
|
|
Like @racket[~seq], but the @racket[pat]s are injected as part of the same
|
|
@racket[~or] as @racket[~lift-rest]. This means that syntax/parse will not
|
|
throw an error for the following code:
|
|
|
|
@examples[#:eval (make-evaluator)
|
|
(eval:no-prompt
|
|
(define p2
|
|
(syntax-parser
|
|
[(~no-order {~once name:id}
|
|
{~once message:str}
|
|
(~once (~or {~as-rest val:nat}
|
|
{~seq {~lift-rest val:nat}})))
|
|
(syntax->datum
|
|
#'(#:name name #:messsage message #:val val))])))
|
|
(code:line (p2 #'(x 123 "msg")) (code:comment "matched by ~as-rest"))
|
|
(code:line (p2 #'(x "msg" 123)) (code:comment "matched by ~as-rest"))
|
|
(code:line (p2 #'(x "msg" . 456)) (code:comment "matched by ~lift-rest"))
|
|
(eval:alts (code:line (p2 #'(x "msg" 123 . 456)) (code:comment "can't have both"))
|
|
(eval:error (p2 #'(x "msg" 123 . 456))))]} |