diff --git a/doc/langref/grammar.peg b/doc/langref/grammar.peg index 5053da2c0bf135addfd995d1f56d8b2dae8bacbc..bc9ebb40cfbac6134664ed1b5080f9cd4d005311 100644 --- a/doc/langref/grammar.peg +++ b/doc/langref/grammar.peg @@ -457,7 +457,11 @@ line_comment <- '//' ![!/] non_control_utf8* newline / '////' non_control_utf8* newline line_string <- '\\\\' non_control_utf8* newline -newline <- "\n" / "\r\n" / eof + +# This uses a positive lookahead rather than consuming input to make e.g. +# the newline terminating a multiline string literal or doc comment visible +# to the pre_op_white non-terminal. +newline <- &("\n" / "\r\n" / eof) skip <- ([ \n\t\r] / line_comment)* skip_require_newline <- [ \t\r]* ([\n] / line_comment) skip pre_op_white <- ([ \n\t\r] / line_comment)+ diff --git a/lib/std/zig/parser_fuzz.zig b/lib/std/zig/parser_fuzz.zig index cd6e7ea7dad1c3978fc3c022d19bef0b085c2c27..dcf20ccb69d3337a8a39b6dc946872d784850c9c 100644 --- a/lib/std/zig/parser_fuzz.zig +++ b/lib/std/zig/parser_fuzz.zig @@ -24,6 +24,10 @@ test "operator whitespace" { \\ _!= 0; \\} ); + try checkAgainstOracle( + \\test{{\\ + \\*0;}} + ); } // Found using AFL++ diff --git a/lib/std/zig/parser_generated_oracle.zig b/lib/std/zig/parser_generated_oracle.zig index 4f775052cd2ccda652c4ed14d9dc50afb40fb5e9..cb46b2ef6e8bc053c12a9bff5fffd871ac7a64e8 100644 --- a/lib/std/zig/parser_generated_oracle.zig +++ b/lib/std/zig/parser_generated_oracle.zig @@ -2502,23 +2502,33 @@ const Parser = struct { return blk_0: { const pos_0 = p.i; if (blk_1: { - if (std.mem.startsWith(u8, p.source[p.i..], "\n")) { - p.i += 1; - break :blk_1 true; - } - break :blk_1 false; + const pos_1 = p.i; + const match_1 = blk_3: { + const pos_3 = p.i; + if (blk_4: { + if (std.mem.startsWith(u8, p.source[p.i..], "\n")) { + p.i += 1; + break :blk_4 true; + } + break :blk_4 false; + }) break :blk_3 true; + p.i = pos_3; + if (blk_4: { + if (std.mem.startsWith(u8, p.source[p.i..], "\r\n")) { + p.i += 2; + break :blk_4 true; + } + break :blk_4 false; + }) break :blk_3 true; + p.i = pos_3; + if (p.parseeof()) break :blk_3 true; + p.i = pos_3; + break :blk_3 false; + }; + p.i = pos_1; + break :blk_1 match_1; }) break :blk_0 true; p.i = pos_0; - if (blk_1: { - if (std.mem.startsWith(u8, p.source[p.i..], "\r\n")) { - p.i += 2; - break :blk_1 true; - } - break :blk_1 false; - }) break :blk_0 true; - p.i = pos_0; - if (p.parseeof()) break :blk_0 true; - p.i = pos_0; break :blk_0 false; }; } diff --git a/tools/gen_parser_oracle.zig b/tools/gen_parser_oracle.zig index 958b5502ff69e14ea1a6985c8e8e7cbaa0a3df5a..ae536622c55a042a8ad581190bbfa9e4d34ecade 100644 --- a/tools/gen_parser_oracle.zig +++ b/tools/gen_parser_oracle.zig @@ -141,7 +141,21 @@ const Generator = struct { switch (node.get(g.p)) { .id => |id| try g.w.print("p.parse{s}()", .{id}), .expr => try g.genExpr(node), - .@"&" => @panic("'&' not supported, unused in Zig's grammar.peg"), + .@"&" => |child| { + // XXX forbid unbounded lookahead + try g.w.print( + \\blk_{d}: {{ + \\const pos_{d} = p.i; + \\const match_{d} = + , .{ suffix, suffix, suffix }); + try g.genNode(child); + try g.w.print( + \\; + \\p.i = pos_{d}; + \\ break :blk_{d} match_{d}; + \\}} + , .{ suffix, suffix, suffix }); + }, .@"!" => |child| { // XXX forbid unbounded lookahead try g.w.print(