| author | |
| committer | |
| log | ac6930544f812582df10181210e80675ce6a42ee |
| tree | 5e5121343fa7587a921ef4060f4e7e03f09ec303 |
| parent | 27765bb87f1591aac40c80dcd9759fc15d49eebf |
| signature |
Currently the grammar fails to enforce matching whitespace or lack of
whitespace around operators that are immediately preceded by a multiline
string literal and terminating newline.4 files changed, 49 insertions(+), 17 deletions(-)
doc/langref/grammar.peg+5-1| ... | @@ -457,7 +457,11 @@ line_comment | ... | @@ -457,7 +457,11 @@ line_comment |
| 457 | <- '//' ![!/] non_control_utf8* newline | 457 | <- '//' ![!/] non_control_utf8* newline |
| 458 | / '////' non_control_utf8* newline | 458 | / '////' non_control_utf8* newline |
| 459 | line_string <- '\\\\' non_control_utf8* newline | 459 | line_string <- '\\\\' non_control_utf8* newline |
| 460 | newline <- "\n" / "\r\n" / eof | 460 | |
| 461 | # This uses a positive lookahead rather than consuming input to make e.g. | ||
| 462 | # the newline terminating a multiline string literal or doc comment visible | ||
| 463 | # to the pre_op_white non-terminal. | ||
| 464 | newline <- &("\n" / "\r\n" / eof) | ||
| 461 | skip <- ([ \n\t\r] / line_comment)* | 465 | skip <- ([ \n\t\r] / line_comment)* |
| 462 | skip_require_newline <- [ \t\r]* ([\n] / line_comment) skip | 466 | skip_require_newline <- [ \t\r]* ([\n] / line_comment) skip |
| 463 | pre_op_white <- ([ \n\t\r] / line_comment)+ | 467 | pre_op_white <- ([ \n\t\r] / line_comment)+ |
lib/std/zig/parser_fuzz.zig+4| ... | @@ -24,6 +24,10 @@ test "operator whitespace" { | ... | @@ -24,6 +24,10 @@ test "operator whitespace" { |
| 24 | \\ _!= 0; | 24 | \\ _!= 0; |
| 25 | \\} | 25 | \\} |
| 26 | ); | 26 | ); |
| 27 | try checkAgainstOracle( | ||
| 28 | \\test{{\\ | ||
| 29 | \\*0;}} | ||
| 30 | ); | ||
| 27 | } | 31 | } |
| 28 | 32 | ||
| 29 | // Found using AFL++ | 33 | // Found using AFL++ |
lib/std/zig/parser_generated_oracle.zig+25-15| ... | @@ -2502,23 +2502,33 @@ const Parser = struct { | ... | @@ -2502,23 +2502,33 @@ const Parser = struct { |
| 2502 | return blk_0: { | 2502 | return blk_0: { |
| 2503 | const pos_0 = p.i; | 2503 | const pos_0 = p.i; |
| 2504 | if (blk_1: { | 2504 | if (blk_1: { |
| 2505 | if (std.mem.startsWith(u8, p.source[p.i..], "\n")) { | 2505 | const pos_1 = p.i; |
| 2506 | p.i += 1; | 2506 | const match_1 = blk_3: { |
| 2507 | break :blk_1 true; | 2507 | const pos_3 = p.i; |
| 2508 | } | 2508 | if (blk_4: { |
| 2509 | break :blk_1 false; | 2509 | if (std.mem.startsWith(u8, p.source[p.i..], "\n")) { |
| 2510 | }) break :blk_0 true; | 2510 | p.i += 1; |
| 2511 | p.i = pos_0; | 2511 | break :blk_4 true; |
| 2512 | if (blk_1: { | 2512 | } |
| 2513 | if (std.mem.startsWith(u8, p.source[p.i..], "\r\n")) { | 2513 | break :blk_4 false; |
| 2514 | p.i += 2; | 2514 | }) break :blk_3 true; |
| 2515 | break :blk_1 true; | 2515 | p.i = pos_3; |
| 2516 | } | 2516 | if (blk_4: { |
| 2517 | break :blk_1 false; | 2517 | if (std.mem.startsWith(u8, p.source[p.i..], "\r\n")) { |
| 2518 | p.i += 2; | ||
| 2519 | break :blk_4 true; | ||
| 2520 | } | ||
| 2521 | break :blk_4 false; | ||
| 2522 | }) break :blk_3 true; | ||
| 2523 | p.i = pos_3; | ||
| 2524 | if (p.parseeof()) break :blk_3 true; | ||
| 2525 | p.i = pos_3; | ||
| 2526 | break :blk_3 false; | ||
| 2527 | }; | ||
| 2528 | p.i = pos_1; | ||
| 2529 | break :blk_1 match_1; | ||
| 2518 | }) break :blk_0 true; | 2530 | }) break :blk_0 true; |
| 2519 | p.i = pos_0; | 2531 | p.i = pos_0; |
| 2520 | if (p.parseeof()) break :blk_0 true; | ||
| 2521 | p.i = pos_0; | ||
| 2522 | break :blk_0 false; | 2532 | break :blk_0 false; |
| 2523 | }; | 2533 | }; |
| 2524 | } | 2534 | } |
tools/gen_parser_oracle.zig+15-1| ... | @@ -141,7 +141,21 @@ const Generator = struct { | ... | @@ -141,7 +141,21 @@ const Generator = struct { |
| 141 | switch (node.get(g.p)) { | 141 | switch (node.get(g.p)) { |
| 142 | .id => |id| try g.w.print("p.parse{s}()", .{id}), | 142 | .id => |id| try g.w.print("p.parse{s}()", .{id}), |
| 143 | .expr => try g.genExpr(node), | 143 | .expr => try g.genExpr(node), |
| 144 | .@"&" => @panic("'&' not supported, unused in Zig's grammar.peg"), | 144 | .@"&" => |child| { |
| 145 | // XXX forbid unbounded lookahead | ||
| 146 | try g.w.print( | ||
| 147 | \\blk_{d}: {{ | ||
| 148 | \\const pos_{d} = p.i; | ||
| 149 | \\const match_{d} = | ||
| 150 | , .{ suffix, suffix, suffix }); | ||
| 151 | try g.genNode(child); | ||
| 152 | try g.w.print( | ||
| 153 | \\; | ||
| 154 | \\p.i = pos_{d}; | ||
| 155 | \\ break :blk_{d} match_{d}; | ||
| 156 | \\}} | ||
| 157 | , .{ suffix, suffix, suffix }); | ||
| 158 | }, | ||
| 145 | .@"!" => |child| { | 159 | .@"!" => |child| { |
| 146 | // XXX forbid unbounded lookahead | 160 | // XXX forbid unbounded lookahead |
| 147 | try g.w.print( | 161 | try g.w.print( |