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.