summaryrefslogtreecommitdiff
path: root/src/regexp.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 00:36:25 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commitfad6bf0cc7cc2cb579d9ad03147a7014906f6b92 (patch)
tree0c7dcd37de52bf992375ca290ff794af8be9798c /src/regexp.zig
parent4881c5d13c6a705a8c8bdb0181c33f9a500b5a74 (diff)
downloadpardes-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.zig23
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);
+ }
+}