diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 00:36:25 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | fad6bf0cc7cc2cb579d9ad03147a7014906f6b92 (patch) | |
| tree | 0c7dcd37de52bf992375ca290ff794af8be9798c /src/regexp.zig | |
| parent | 4881c5d13c6a705a8c8bdb0181c33f9a500b5a74 (diff) | |
| download | pardes-fad6bf0cc7cc2cb579d9ad03147a7014906f6b92.tar.gz pardes-fad6bf0cc7cc2cb579d9ad03147a7014906f6b92.zip | |
A repeat inside a regexp group gives back to what follows the group
On aab, /a+ab/ matched but /(a+)ab/ and /(a*)ab/ missed, and so did
(.+)_area and ([a-z_]+)_area, in addr, Edit and look alike. mvzr's hasAlt
counts a group's own ) as an alternative, so every group with pattern after
it takes matchGroup's alternatives branch, which never tries a repeat's
shorter matches. No newer mvzr fixes it (trunk is the pinned commit), so
the build patches the fetched source as it does the step budget,
anchor-checked: when the rest fails after a group, the group's shorter
matches are tried, longest first. docs/mvzr-group-backtrack.md is a report
for upstream.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/regexp.zig')
| -rw-r--r-- | src/regexp.zig | 23 |
1 files changed, 23 insertions, 0 deletions
diff --git a/src/regexp.zig b/src/regexp.zig index 249763c2..97e3dc91 100644 --- a/src/regexp.zig +++ b/src/regexp.zig @@ -235,3 +235,26 @@ test "a \\x without two hex digits is refused, not handed to mvzr to panic on" { var rx = try Regex.compile("\\x41"); try std.testing.expectEqual(@as(usize, 1), (try rx.find("xA", 0, 1, 2)).?.start); } + +test "a repeat inside a group gives back to what follows the group" { + const Case = struct { pat: []const u8, text: []const u8, start: usize, end: usize }; + for ([_]Case{ + .{ .pat = "a+ab", .text = "aab", .start = 0, .end = 3 }, + .{ .pat = "(a+)ab", .text = "aab", .start = 0, .end = 3 }, + .{ .pat = "(a*)ab", .text = "aab", .start = 0, .end = 3 }, + .{ .pat = "(.+)_area", .text = "the_total_area x", .start = 0, .end = 14 }, + .{ .pat = "([a-z_]+)_area", .text = "the_total_area x", .start = 0, .end = 14 }, + .{ .pat = "\\w+_area", .text = "the_total_area x", .start = 0, .end = 14 }, + .{ .pat = "(\\w)+_area", .text = "the_total_area x", .start = 0, .end = 14 }, + .{ .pat = "([a-z]+)_area", .text = "the_total_area x", .start = 4, .end = 14 }, + .{ .pat = "x(ab|a)bc", .text = "xabc", .start = 0, .end = 4 }, + }) |c| { + var rx = try Regex.compile(c.pat); + const m = (try rx.find(c.text, 0, c.text.len, c.text.len)) orelse { + std.debug.print("no match for {s}\n", .{c.pat}); + return error.NoMatch; + }; + try std.testing.expectEqual(c.start, m.start); + try std.testing.expectEqual(c.end, m.end); + } +} |
