authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-06-18 14:39:41+02:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-07-08 11:48:59+02:00
log78a196d04fb2c3a978fabec4c8c236f09add591e
tree9bb21608330e35faad22d176b46869b95fb97e8f
parentec04ae6e0003a7fd35782dbc26c89f9905119aee
signaturelock-open Commit is signed but in an unrecognized format.

grammar: fix escaped backslash tokenization


3 files changed, 29 insertions(+), 2 deletions(-)

doc/langref/grammar.peg+6-2
...@@ -483,19 +483,23 @@ non_control_utf8 <- [\040-\176\200-\377]...@@ -483,19 +483,23 @@ non_control_utf8 <- [\040-\176\200-\377]
483#483#
484# char_char484# char_char
485# <- multibyte_utf8485# <- multibyte_utf8
486# / "\\\\"
486# / "\\'"487# / "\\'"
487# / !['] non_control_ascii488# / !['] non_control_ascii
488# string_char489# string_char
489# <- multibyte_utf8490# <- multibyte_utf8
491# / '\\\\'
490# / '\\"'492# / '\\"'
491# / !["] non_control_ascii493# / !["] non_control_ascii
492494
493char_char495char_char
494 <- "\\'"496 <- "\\\\"
497 / "\\'"
495 / !['] non_control_utf8498 / !['] non_control_utf8
496499
497string_char500string_char
498 <- '\\"'501 <- '\\\\'
502 / '\\"'
499 / !["] non_control_utf8503 / !["] non_control_utf8
500504
501505
lib/std/zig/parser_fuzz.zig+7
...@@ -86,6 +86,13 @@ test "at newline string" {...@@ -86,6 +86,13 @@ test "at newline string" {
86 );86 );
87}87}
8888
89// Found using AFL++
90test "string lit" {
91 try checkAgainstOracle(
92 \\"\\"
93 );
94}
95
89fn checkAgainstOracle(source: [:0]const u8) !void {96fn checkAgainstOracle(source: [:0]const u8) !void {
90 var fba_buf: [1 << 18]u8 = undefined;97 var fba_buf: [1 << 18]u8 = undefined;
91 var fba: std.heap.FixedBufferAllocator = .init(&fba_buf);98 var fba: std.heap.FixedBufferAllocator = .init(&fba_buf);
lib/std/zig/parser_generated_oracle.zig+16
...@@ -2362,6 +2362,14 @@ const Parser = struct {...@@ -2362,6 +2362,14 @@ const Parser = struct {
2362 pub fn parsechar_char(p: *Parser) bool {2362 pub fn parsechar_char(p: *Parser) bool {
2363 return blk_0: {2363 return blk_0: {
2364 const pos_0 = p.i;2364 const pos_0 = p.i;
2365 if (blk_1: {
2366 if (std.mem.startsWith(u8, p.source[p.i..], "\\\\")) {
2367 p.i += 2;
2368 break :blk_1 true;
2369 }
2370 break :blk_1 false;
2371 }) break :blk_0 true;
2372 p.i = pos_0;
2365 if (blk_1: {2373 if (blk_1: {
2366 if (std.mem.startsWith(u8, p.source[p.i..], "\\'")) {2374 if (std.mem.startsWith(u8, p.source[p.i..], "\\'")) {
2367 p.i += 2;2375 p.i += 2;
...@@ -2390,6 +2398,14 @@ const Parser = struct {...@@ -2390,6 +2398,14 @@ const Parser = struct {
2390 pub fn parsestring_char(p: *Parser) bool {2398 pub fn parsestring_char(p: *Parser) bool {
2391 return blk_0: {2399 return blk_0: {
2392 const pos_0 = p.i;2400 const pos_0 = p.i;
2401 if (blk_1: {
2402 if (std.mem.startsWith(u8, p.source[p.i..], "\\\\")) {
2403 p.i += 2;
2404 break :blk_1 true;
2405 }
2406 break :blk_1 false;
2407 }) break :blk_0 true;
2408 p.i = pos_0;
2393 if (blk_1: {2409 if (blk_1: {
2394 if (std.mem.startsWith(u8, p.source[p.i..], "\\\"")) {2410 if (std.mem.startsWith(u8, p.source[p.i..], "\\\"")) {
2395 p.i += 2;2411 p.i += 2;