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].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/871629c7-e004-421d-bb04-4496480390d1%40googlegroups.com.

Reply via email to