I'm not sure exactly why my syntax class wasn't working, but it is working 
now. I had an extra set of parentheses around the ~between pattern, so it 
may have been related to that. Whatever the case may be, the non-splicing 
syntax class is working now.

I am very close to getting everything working but I am still having trouble 
using ~between as part of a ~seq. Here's an example:

(syntax-parse #'(1 2 'bar 4 5 'bar 'foo) [((~seq (~between x:integer 2 2) 
... z) ...+ expr) #'foo])
; >: contract violation
;   expected: real?
;   given: #<syntax:stdin::15625 2>
;   argument position: 1st
;   other arguments...:
;    0

I want to match one or more sequences of two integers and an expression, 
finally ending in one final expression. This is a contrived example but 
demonstrates how I'll eventually need to use ~between. The syntax error I'm 
getting here isn't particularly enlightening.

Once again, I really appreciate any help here.

-- Jonathan

On Saturday, October 12, 2019 at 2:28:05 PM UTC-4, Alexis King wrote:
>
> I believe your two syntax classes are identical, except for the fact that 
> the splicing variant will not be allowed as a single term pattern. 
> Therefore, I don’t think there’s ever any reason to prefer the splicing 
> version.
>
> I tried an example using your mag-lvl syntax class with ~between, and it 
> worked fine. This program successfully prints a list of length 3:
>
> #lang racket
>
> (require syntax/parse)
>
> (define-syntax-class mag-lvl
>   (pattern ({~datum level})))
>
> (syntax-parse #'((level) (level) (level))
>   [((~between lvls:mag-lvl 3 3) ...)
>    (attribute lvls)])
>
> So I’m not sure what problem you’re bumping into, and it’s not something I 
> can guess without knowing more information.
>
> On Oct 12, 2019, at 11:13, Jonathan Simpson <[email protected] 
> <javascript:>> wrote:
>
> Regarding my custom syntax-class issue, I realize now that it is probably 
> because ~between only accepts splicing syntax classes. So, I created one 
> that matches my regular syntax class. I'm not 100 percent sure that these 
> are interchangeable in my use case though:
>
> (define-syntax-class mag-lvl
>     (pattern ({~datum level})))
>
> (define-splicing-syntax-class mag-slvl
>     (pattern ({~datum level})))
>
> Does anyone know if :mag-slvl is interchangeable with :mag-lvl in most 
> uses? Are there cases where :mag-slvl won't work the way I expect it to. 
> I'm not confident in my understanding of the differences between using head 
> patterns and single term patterns.
>
> -- Jonathan
>
> On Friday, October 11, 2019 at 10:55:19 PM UTC-4, Jonathan Simpson wrote:
>>
>> Thank you Alexis for the clear explanation. I now understand how to use 
>> ~between and it is working for me.
>>
>> One small hitch I encountered is a custom syntax class I defined doesn't 
>> work in the ~between statement but works elsewhere within the same syntax 
>> pattern. This isn't a huge issue for me as I just copied the pattern in 
>> place of the syntax class but I am curious why the :integer syntax class 
>> works and my custom one doesn't.
>>
>> Once again, thanks for taking the time to explain this!
>>
>> -- Jonathan
>>
>> On Thursday, October 10, 2019 at 11:17:53 PM UTC-4, Alexis King wrote:
>>>
>>> tl;dr: You need to use an ellipsis, so your pattern should be ((~between 
>>> x:integer 3 3) ...). A (much) more detailed explanation of why follows.
>>>
>>> ~between is an *ellipsis-head* pattern. The most common ellipsis-head 
>>> pattern, ~optional, also works as a plain head pattern, but ~between does 
>>> not. What’s the difference?
>>>
>>> Let’s start by answering what a head pattern is. The simplest kind of 
>>> syntax/parse pattern is a single-term pattern, which (as the name implies) 
>>> only matches a single syntax object at a time. Head patterns are special in 
>>> that they can match zero or more consecutive syntax objects in the head of 
>>> a list. What is the head of a list? Well, if you have a list like '(1 2 3 
>>> 4), its *head* is the sequence of elements “1 2 3 4” and its *tail* is 
>>> simply the empty list, '(). It’s possible to write the list '(1 2 3 4 . ()) 
>>> to make that more explicit.
>>>
>>> So when you have a head pattern like (~optional x:integer), it might 
>>> parse an integer, but it also might parse nothing. In the latter case, the 
>>> next head pattern in the sequence would get a chance to parse the same 
>>> element that (~optional x:integer) did. Head patterns are able to do this 
>>> because lists introduce a kind of linear sequencing (not just tree-like 
>>> nesting), so “skipping” an element is an operation that makes sense.
>>>
>>> But what about ellipsis-head patterns? These are patterns that don’t 
>>> just appear inside a list pattern, they appear inside a list pattern 
>>> *and* under an ellipsis. For example, in the pattern (x y ... z), x and 
>>> z are head patterns, but y is an ellipsis-head pattern. While head patterns 
>>> introduce the ability to consume one or more elements at a time, 
>>> ellipsis-head patterns extend that with the power to match elements in the 
>>> list *out of order*. This is most useful when parsing keyword options, 
>>> such as in the following pattern:
>>>
>>>     ((~alt (~once (~seq #:foo foo:integer)) (~once (~seq #:bar 
>>> bar:string))) ...)
>>>
>>> The above pattern will match (#:foo 1 #:bar "two") *or* (#:bar "two" 
>>> #:foo 1), but not (#:foo 1) or (#:foo 1 #:foo 2 #:bar "three"). This is 
>>> because ~alt introduces a set of alternatives that can be matched, but 
>>> unlike a simple ~or* pattern, it also keeps track of how many *times* each 
>>> case matched, and patterns like ~once, ~optional, and ~between introduce 
>>> constraints on the number of times a given case must match for the overall 
>>> parse to be successful.
>>>
>>> Interestingly, note that pattern variables bound under ~once and 
>>> ~optional don’t have an ellipsis depth of 1, they have an ellipsis depth of 
>>> 0. This is why, in the given example, you can refer to the foo and bar 
>>> pattern variables in a template without any ellipses. ~between, however, 
>>> still increments the ellipsis depth, since the pattern can actually match 
>>> multiple times.
>>>
>>> In the pattern I suggested at the beginning of this email, ((~between 
>>> x:integer 3 3) ...), you’re creating an ellipsis-head context with exactly 
>>> one alternative: (~between x:integer 3 3). That is exactly what you want, 
>>> so everything works out fine.
>>>
>>> The one remaining question, however, is why ~between is only allowed as 
>>> an ellipsis-head pattern, but ~optional is also allowed as a head pattern. 
>>> I can’t say for certain, since you can think of ((~optional x:integer)) as 
>>> being sort of implicitly expanded to ((~optional x:integer) ...), and the 
>>> same could be done for ~between. However, my guess is that it isn’t allowed 
>>> because ~between increments the ellipsis depth of its sub-pattern, and Ryan 
>>> thought it would be confusing for a pattern variable’s ellipsis depth to be 
>>> incremented despite there not actually being any ellipses in the pattern. 
>>> Therefore, when using ~between, you have to write the ellipsis explicitly.
>>>
>>> Alexis
>>>
>>> On Oct 10, 2019, at 20:37, Jonathan Simpson <[email protected]> wrote:
>>>
>>> This seems like it should be simple but I've never been able to figure 
>>> out how to do this. What I've been doing instead is this:
>>>
>>> (x:integer ...+) to match two or more integers.
>>>
>>> (x:integer y:integer ...+) to match three or more.
>>>
>>> And so on.
>>>
>>> I'm at a point now where I need to build patterns dynamically to match 
>>> an exact number of elements. I'd also like to avoid having to create unique 
>>> names for a bunch of pattern variables. ~between seems like what I want but 
>>> I haven't been able to get it to work. I've been using ~seq without issue 
>>> but that isn't exactly what I need.
>>>
>>> Example of an attempt to use ~between:
>>>
>>> (syntax-parse #'(1 1 1) [((~between x 3 3)) #'(x ...)])
>>> ; stdin::2631: syntax-parse: pattern keyword not allowed here
>>> ;   at: ~between
>>>
>>>
>>> Can anyone give me a quick example of how to do this, using ~between or 
>>> otherwise? I'm using syntax-parse, if that makes a difference.
>>>
>>> Thanks!
>>>
>>> -- Jonathan
>>>
>>>
>>>
> -- 
> 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 [email protected] <javascript:>.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/racket-users/871629c7-e004-421d-bb04-4496480390d1%40googlegroups.com
>  
> <https://groups.google.com/d/msgid/racket-users/871629c7-e004-421d-bb04-4496480390d1%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>
>
>

-- 
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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/f7c0b4ac-b11c-419a-b8e6-49176cd561fd%40googlegroups.com.

Reply via email to