`{n,m}` bounds repetition between a floor and a ceiling, and `{n,}` sets only a floor. Desugaring both into copies plus optionals or a star means no new matching code - and it closes the chapter.
Parse `x{n,m}` and `x{n,}` by expanding into required copies plus optional or starred ones.
Bounded repetition generalizes yesterday’s exact count. a{2,3} matches two or three
as; a{2,} matches two or more. Both desugar cleanly into quantifiers you already
built: the n required copies are plain repeats, the optional ones up to m are
Quest nodes, and an open-ended {n,} caps off with a Star. So a{2,3} becomes
a a a? and a{2,} becomes a a a*. Again the matcher gains nothing new - all the
work is in the parser’s suffix handler.
That completes the surface syntax of your engine. Take a moment with the last line of
the spec: ^(cat|dog)s?$ combines grouping, alternation, an optional, and anchors,
and it correctly accepts cat, cats, dog, dogs while rejecting fish. You’ve
built a genuinely capable matcher. But it is still a backtracker, and some
patterns can make it explore an exponential number of paths. The next chapter builds
a completely different matching engine that never does.
// Desugar using nodes you already have:// x{2,3} -> Concat[x, x, x?] (2 required, 1 optional)// x{2,} -> Concat[x, x, x*] (2 required, then any number)// n required copies, then (m-n) Quest copies, or a Star if open.