authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-07-04 13:41:19+02:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-07-08 11:49:01+02:00
loge054442e2371b0176c14ff740d78607d078ca9fb
tree5469c4564c8d4b0a020b3cc444affb64fdd66022
parent203e8279539220db84823bb79671c5c6c7d1065f
signaturelock-open Commit is signed but in an unrecognized format.

grammar: actually forbid chained compare ops

Currently the grammar doesn't forbid chained compare ops, see the new test case which now passes.

3 files changed, 16 insertions(+), 13 deletions(-)

doc/langref/grammar.peg+1-1
......@@ -126,7 +126,7 @@ BoolOrExpr <- BoolAndExpr (OrOp BoolAndExpr)* !OrOp
126126
127127BoolAndExpr <- CompareExpr (AndOp CompareExpr)* !AndOp
128128
129CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr / !CompareOp)
129CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? !CompareOp
130130
131131BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* !BitwiseOp
132132
lib/std/zig/parser_fuzz.zig+5
......@@ -140,6 +140,11 @@ test "bin op at end of file" {
140140 );
141141}
142142
143// Found using AFL++
144test "resume block chained compare ops" {
145 try checkAgainstOracle("test{resume{0 > 0;} > 0 > 0;}");
146}
147
143148fn checkAgainstOracle(source: [:0]const u8) !void {
144149 var fba_buf: [1 << 18]u8 = undefined;
145150 var fba: std.heap.FixedBufferAllocator = .init(&fba_buf);
lib/std/zig/parser_generated_oracle.zig+10-12
......@@ -639,18 +639,16 @@ const Parser = struct {
639639 pub fn parseCompareExpr(p: *Parser) Error!bool {
640640 return blk_0: {
641641 const pos_0 = p.i;
642 if (try p.parseBitwiseExpr() and blk_2: {
643 const pos_2 = p.i;
644 if (try p.parseCompareOp() and try p.parseBitwiseExpr()) break :blk_2 true;
645 p.i = pos_2;
646 if (blk_3: {
647 const pos_3 = p.i;
648 const match_3 = try p.parseCompareOp();
649 p.i = pos_3;
650 break :blk_3 !match_3;
651 }) break :blk_2 true;
652 p.i = pos_2;
653 break :blk_2 false;
642 if (try p.parseBitwiseExpr() and (blk_3: {
643 const pos_3 = p.i;
644 if (try p.parseCompareOp() and try p.parseBitwiseExpr()) break :blk_3 true;
645 p.i = pos_3;
646 break :blk_3 false;
647 } or true) and blk_1: {
648 const pos_1 = p.i;
649 const match_1 = try p.parseCompareOp();
650 p.i = pos_1;
651 break :blk_1 !match_1;
654652 }) break :blk_0 true;
655653 p.i = pos_0;
656654 break :blk_0 false;