diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 11:50:52 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | 7c329baddb0d45ce7937cf29941dde861f9ea27f (patch) | |
| tree | 76919df40dbaccf6cd148dcbf2f46efc508df76c /src/regexp.zig | |
| parent | fc8e1abf361ab2151109e68e5c88a0ed45650b38 (diff) | |
| download | pardes-7c329baddb0d45ce7937cf29941dde861f9ea27f.tar.gz pardes-7c329baddb0d45ce7937cf29941dde861f9ea27f.zip | |
A pattern anchoring some alternatives with ^ and not others is refused with why, not a bare bad regular expression
`^def|x` and `foo$|^zèbre` were refused with a bare "bad regular expression",
the same words as a syntax error, so a script could not tell what to change.
They now say "an alternation anchors every branch with ^ or none (^a|^b, not
^a|b)", through addr and Edit alike. fs.md states the rule and that a `$`
does not count toward it (`foo$|bar` is fine).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/regexp.zig')
| -rw-r--r-- | src/regexp.zig | 10 |
1 files changed, 6 insertions, 4 deletions
diff --git a/src/regexp.zig b/src/regexp.zig index ddb5eb28..575e6861 100644 --- a/src/regexp.zig +++ b/src/regexp.zig @@ -56,6 +56,7 @@ pub const Regex = struct { pub const e_anchor = "bad regular expression: in a pattern with \\n, ^ can only come first and $ only just before a \\n"; pub const e_wide = std.fmt.comptimePrint("bad regular expression: a range of runes wider than {d} in [...] is not supported", .{max_range}); pub const e_negated = "bad regular expression: a [^...] with non-ASCII runes is not supported (mvzr's classes hold bytes)"; + pub const e_mixed = "bad regular expression: an alternation anchors every branch with ^ or none (^a|^b, not ^a|b)"; /// The most runes a non-ASCII range in a class is spelled out as. pub const max_range = 256; @@ -79,7 +80,7 @@ pub const Regex = struct { return @intCast(n); } - pub const Error = error{ Bad, Anchor, TooLong, WideRange, NegatedRunes }; + pub const Error = error{ Bad, Anchor, TooLong, WideRange, NegatedRunes, Mixed }; pub fn compile(pat: []const u8) Error!Regex { if (pat.len == 0) return error.Bad; @@ -273,7 +274,7 @@ pub const Regex = struct { /// `^a|^b` as `^(a|b)` in `out`, when the pattern is an alternation at /// its top level and every branch starts with `^`; null when it is not /// one, or no branch does. - fn anchoredAlternation(pat: []const u8, out: *[5 * max_ops + 2]u8) error{Bad}!?[]const u8 { + fn anchoredAlternation(pat: []const u8, out: *[5 * max_ops + 2]u8) error{ Bad, Mixed }!?[]const u8 { var bars: [16]usize = undefined; var n: usize = 0; var depth: usize = 0; @@ -306,7 +307,8 @@ pub const Regex = struct { from = to + 1; } if (anchored == 0) return null; - if (anchored != n + 1) return error.Bad; + // Only `^` counts: `$` is mvzr's own at a branch's end (`a$|b`). + if (anchored != n + 1) return error.Mixed; var w = std.Io.Writer.fixed(out); w.writeAll("^(") catch return error.Bad; from = 0; @@ -481,7 +483,7 @@ test "a ^ after | anchors that branch: ^def|^ finds a line that starts either w var both = try Regex.compile("^def|^ "); try std.testing.expectEqual(@as(usize, 6), (try both.find(text, 0, text.len, text.len)).?.start); try std.testing.expectEqual(@as(usize, 9), (try both.find(text, 7, text.len, text.len)).?.start); - try std.testing.expectError(error.Bad, Regex.compile("^def|x")); + try std.testing.expectError(error.Mixed, Regex.compile("^def|x")); var plain = try Regex.compile("a|b"); try std.testing.expectEqual(@as(usize, 7), (try plain.find(text, 0, text.len, text.len)).?.start); } |
