> (prefix 6 5 4) raises the same error as before, and the definition of 
> prefix/parse complains "syntax-parse: duplicate attribute in: tail".

This is because ~or behaves differently under ellipses, and nested ~or are just 
inlined. Your code for prefix/parse is therefore equivalent to (~or alt1 (~seq 
#:tail) (~and (~seq) (~parse tail #'null))) ...

To make a ~or pattern behave as a normal ~or, and not as a list of 
possibly-repeated elements, you can wrap it with a dummy ~and, i.e. writing 
(~or alt1 alt2 (~and (~or foo bar)) alt4) ... instead of (~or alt1 alt2 (~or 
foo bar) alt4) ...

I like to define a (~either . pats) pattern expander which aliases to (~and 
(~or . pats)), for better readability [1] .

In your case, since you want to limit it to a single occurrence anyway, you can 
simply wrap the inner ~or with ~once, as follows:

#lang racket
(require (for-syntax syntax/parse))
(define-syntax (prefix/parse stx)
  (syntax-parse stx
    [(_ (~or (~seq nat:exact-nonnegative-integer)
             (~once (~or (~seq #:tail tail)
                         (~and (~seq) (~parse tail #'null)))))
        ...)
     #:declare tail (expr/c #'(listof natural-number/c))
     #'(list* nat ... tail.c)]))


The issue with this solution, however, is that it allows the empty sequence 
(~seq) to be matched as part of a ellipsis-head pattern. This means that the 
(~seq) could match anywhere, and, save for the ~once, could match an infinite 
amount of times without consuming any element. To prevent such issues, 
syntax/parse guards against this, and you will get the following error:

syntax-parse: an ellipsis-head pattern matched an empty sequence

The simplest solution I can see is to re-bind the tail attribute to a real 
pattern variable, safe in the knowledge that the tail attribute is never #f, 
thanks to the #:defaults (note that if you nest uses of ~optional, e.g. 
(~optional (~seq (~seq a (~optional b #:defaults ([b c]))) ...)), then I think 
this is not true anymore, you'd have to provide #:defaults for the outer 
~optional too.

I'll use #:with here, but you could equally use a {~parse pvar-tail #'tail) in 
the pattern, somewhere after the (~or . pats) ...:

#lang racket
(require (for-syntax syntax/parse))

(define-syntax (prefix/parse stx)
  (syntax-parse stx
    [(_ (~or (~seq nat:exact-nonnegative-integer)
             (~optional (~seq #:tail tail)
                        #:defaults ([tail #'null])))
        ...)
     #:with pvar-tail #'tail
     #:declare pvar-tail (expr/c #'(listof natural-number/c))
     #'(list* nat ... pvar-tail.c)]))

(prefix/parse 1 2 3)
(prefix/parse 1 2 3 #:tail 42) ;; contract violation, as expected.


It would probably be possible to abstract this using my 
extensible-parser-specifications library [2] which defines a ~seq-no-order 
similar to the one by Alexander Knauth, but allows some "post" operations which 
are executed after matching the whole sequence. I would recommend against it, 
however, as my library has severe limitations, and could really use a redesign 
(which might need some changes in syntax/parse itself, as achieving these 
"post" operations required a few hacks).

Alternatively, it would make sense to fix #:declare so that it handles 
gracefully #f values. Could you file an issue for this so that it is not 
forgotten?

[1]: 
http://docs.racket-lang.org/phc-toolkit/syntax-parse_helpers.html#%28form._%28%28submod._%28lib._phc-toolkit%2Fsyntax-parse..rkt%29._typed%29._~7eeither%29%29
[2]: http://docs.racket-lang.org/extensible-parser-specifications/

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to