authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-06-17 15:18:24+02:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-07-08 11:48:59+02:00
logb4185600466bd14e39e68cd9f07017a8dc1fcf8e
tree211f0cc7fe177f6591bf4c9ad52a5dfb6138c642
parente488959c1f0504dc3b2aae19d79211e725916c76
signaturelock-open Commit is signed but in an unrecognized format.

grammar: statement-level consistency with Parse.zig

This is a first pass at consistency with Parse.zig on the statement level, adding constant lookaheads to the grammar in several places to match the behavior of Parse.zig.

3 files changed, 100 insertions(+), 86 deletions(-)

doc/langref/grammar.peg+11-13
......@@ -27,39 +27,37 @@ BlockStatement
2727 <- Statement
2828 / KEYWORD_defer BlockExprStatement
2929 / KEYWORD_errdefer BlockExprStatement
30 / (KEYWORD_comptime)? VarAssignStatement
30 / (KEYWORD_comptime !BlockExprPrefix)? VarAssignStatement
3131
3232Statement
33 <- ExprStatement
34 / KEYWORD_suspend BlockExprStatement
35 / (KEYWORD_comptime)? AssignExpr SEMICOLON
36
37ExprStatement
3833 <- IfStatement
3934 / LabeledStatement
4035 / KEYWORD_nosuspend BlockExprStatement
4136 / KEYWORD_comptime BlockExpr
37 / KEYWORD_suspend BlockExprStatement
38 / (KEYWORD_comptime !BlockExprPrefix)? AssignExpr SEMICOLON
4239
4340IfStatement
44 <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
45 / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
41 <- IfPrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else)
42 / IfPrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Payload? Statement)
4643
4744LabeledStatement <- BlockLabel? (Block / LoopStatement / SwitchExpr)
4845
4946LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
5047
5148ForStatement
52 <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else )
53 / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
49 <- ForPrefix BlockExpr (KEYWORD_else Statement / !KEYWORD_else)
50 / ForPrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Statement)
5451
5552WhileStatement
56 <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
57 / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
53 <- WhilePrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else)
54 / WhilePrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Payload? Statement)
5855
5956BlockExprStatement
6057 <- BlockExpr
61 / !BlockExpr AssignExpr SEMICOLON
58 / !BlockExprPrefix AssignExpr SEMICOLON
6259
60BlockExprPrefix <- BlockLabel? LBRACE
6361BlockExpr <- BlockLabel? Block
6462
6563# An assignment or a destructure whose LHS are all lvalue expressions or variable declarations.
lib/std/zig/Parse.zig+17-40
......@@ -864,7 +864,7 @@ fn parseGlobalVarDecl(p: *Parse) !?Node.Index {
864864 return var_decl;
865865}
866866
867/// ContainerField <- doc_comment? (KEYWORD_comptime / !KEYWORD_comptime) !KEYWORD_fn (IDENTIFIER COLON / !(IDENTIFIER COLON))? TypeExpr ByteAlign? (EQUAL Expr)?
867// ContainerField <- doc_comment? KEYWORD_comptime? (IDENTIFIER COLON)? TypeExpr ByteAlign? (EQUAL Expr)?
868868fn expectContainerField(p: *Parse) !Node.Index {
869869 _ = p.eatToken(.keyword_comptime);
870870 const main_token = p.tok_i;
......@@ -910,18 +910,15 @@ fn expectContainerField(p: *Parse) !Node.Index {
910910/// <- Statement
911911/// / KEYWORD_defer BlockExprStatement
912912/// / KEYWORD_errdefer BlockExprStatement
913/// / !ExprStatement (KEYWORD_comptime !BlockExpr)? VarAssignStatement
913/// / (KEYWORD_comptime !BlockExprPrefix)? VarAssignStatement
914914///
915915/// Statement
916/// <- ExprStatement
917/// / KEYWORD_suspend BlockExprStatement
918/// / !ExprStatement (KEYWORD_comptime !BlockExpr)? AssignExpr SEMICOLON
919///
920/// ExprStatement
921916/// <- IfStatement
922917/// / LabeledStatement
923918/// / KEYWORD_nosuspend BlockExprStatement
924919/// / KEYWORD_comptime BlockExpr
920/// / KEYWORD_suspend BlockExprStatement
921/// / (KEYWORD_comptime !BlockExprPrefix)? AssignExpr SEMICOLON
925922fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index {
926923 if (p.eatToken(.keyword_comptime)) |comptime_token| {
927924 const opt_block_expr = try p.parseBlockExpr();
......@@ -934,7 +931,7 @@ fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index {
934931 }
935932
936933 if (is_block_level) {
937 return p.expectVarDeclExprStatement(comptime_token);
934 return p.expectVarAssignStatement(comptime_token);
938935 } else {
939936 const assign = try p.expectAssignExpr();
940937 try p.expectSemicolon(.expected_semi_after_stmt, true);
......@@ -995,7 +992,7 @@ fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index {
995992 if (try p.parseLabeledStatement()) |labeled_statement| return labeled_statement;
996993
997994 if (is_block_level) {
998 return p.expectVarDeclExprStatement(null);
995 return p.expectVarAssignStatement(null);
999996 } else {
1000997 const assign = try p.expectAssignExpr();
1001998 try p.expectSemicolon(.expected_semi_after_stmt, true);
......@@ -1003,30 +1000,8 @@ fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index {
10031000 }
10041001}
10051002
1006/// ComptimeStatement
1007/// <- BlockExpr
1008/// / VarDeclExprStatement
1009fn expectComptimeStatement(p: *Parse, comptime_token: TokenIndex) !Node.Index {
1010 const maybe_block_expr = try p.parseBlockExpr();
1011 if (maybe_block_expr) |block_expr| {
1012 return p.addNode(.{
1013 .tag = .@"comptime",
1014 .main_token = comptime_token,
1015 .data = .{
1016 .lhs = .{ .node = block_expr },
1017 .rhs = undefined,
1018 },
1019 });
1020 }
1021 return p.expectVarDeclExprStatement(comptime_token);
1022}
1023
1024/// VarDeclExprStatement
1025/// <- Expr
1026/// / VarAssignStatement
1027///
10281003/// VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON
1029fn expectVarDeclExprStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Index {
1004fn expectVarAssignStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Index {
10301005 const scratch_top = p.scratch.items.len;
10311006 defer p.scratch.shrinkRetainingCapacity(scratch_top);
10321007
......@@ -1138,7 +1113,7 @@ fn expectVarDeclExprStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Inde
11381113
11391114/// If a parse error occurs, reports an error, but then finds the next statement
11401115/// and returns that one instead. If a parse error occurs but there is no following
1141/// statement, returns 0.
1116/// statement, returns null.
11421117fn expectStatementRecoverable(p: *Parse) Error!?Node.Index {
11431118 while (true) {
11441119 return p.expectStatement(true) catch |err| switch (err) {
......@@ -1156,8 +1131,10 @@ fn expectStatementRecoverable(p: *Parse) Error!?Node.Index {
11561131}
11571132
11581133/// IfStatement
1159/// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
1160/// / IfPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
1134/// <- IfPrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else)
1135/// / IfPrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Payload? Statement)
1136///
1137/// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
11611138fn expectIfStatement(p: *Parse) !Node.Index {
11621139 const if_token = p.assertToken(.keyword_if);
11631140 _ = try p.expectToken(.l_paren);
......@@ -1252,8 +1229,8 @@ fn parseLoopStatement(p: *Parse) !?Node.Index {
12521229}
12531230
12541231/// ForStatement
1255/// <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else )
1256/// / ForPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Statement )
1232/// <- ForPrefix BlockExpr (KEYWORD_else Statement / !KEYWORD_else)
1233/// / ForPrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Statement)
12571234fn parseForStatement(p: *Parse) !?Node.Index {
12581235 const for_token = p.eatToken(.keyword_for) orelse return null;
12591236
......@@ -1309,8 +1286,8 @@ fn parseForStatement(p: *Parse) !?Node.Index {
13091286/// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
13101287///
13111288/// WhileStatement
1312/// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
1313/// / WhilePrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
1289/// <- WhilePrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else)
1290/// / WhilePrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Payload? Statement)
13141291fn parseWhileStatement(p: *Parse) !?Node.Index {
13151292 const while_token = p.eatToken(.keyword_while) orelse return null;
13161293 _ = try p.expectToken(.l_paren);
......@@ -1400,7 +1377,7 @@ fn parseWhileStatement(p: *Parse) !?Node.Index {
14001377
14011378/// BlockExprStatement
14021379/// <- BlockExpr
1403/// / !BlockExpr AssignExpr SEMICOLON
1380/// / !BlockExprPrefix AssignExpr SEMICOLON
14041381fn parseBlockExprStatement(p: *Parse) !?Node.Index {
14051382 const block_expr = try p.parseBlockExpr();
14061383 if (block_expr) |expr| return expr;
lib/std/zig/parser_generated_oracle.zig+72-33
......@@ -190,7 +190,12 @@ const Parser = struct {
190190 p.i = pos_0;
191191 if ((blk_3: {
192192 const pos_3 = p.i;
193 if (p.parseKEYWORD_comptime()) break :blk_3 true;
193 if (p.parseKEYWORD_comptime() and blk_4: {
194 const pos_4 = p.i;
195 const match_4 = p.parseBlockExprPrefix();
196 p.i = pos_4;
197 break :blk_4 !match_4;
198 }) break :blk_3 true;
194199 p.i = pos_3;
195200 break :blk_3 false;
196201 } or true) and p.parseVarAssignStatement()) break :blk_0 true;
......@@ -201,13 +206,24 @@ const Parser = struct {
201206 pub fn parseStatement(p: *Parser) bool {
202207 return blk_0: {
203208 const pos_0 = p.i;
204 if (p.parseExprStatement()) break :blk_0 true;
209 if (p.parseIfStatement()) break :blk_0 true;
210 p.i = pos_0;
211 if (p.parseLabeledStatement()) break :blk_0 true;
212 p.i = pos_0;
213 if (p.parseKEYWORD_nosuspend() and p.parseBlockExprStatement()) break :blk_0 true;
214 p.i = pos_0;
215 if (p.parseKEYWORD_comptime() and p.parseBlockExpr()) break :blk_0 true;
205216 p.i = pos_0;
206217 if (p.parseKEYWORD_suspend() and p.parseBlockExprStatement()) break :blk_0 true;
207218 p.i = pos_0;
208219 if ((blk_3: {
209220 const pos_3 = p.i;
210 if (p.parseKEYWORD_comptime()) break :blk_3 true;
221 if (p.parseKEYWORD_comptime() and blk_4: {
222 const pos_4 = p.i;
223 const match_4 = p.parseBlockExprPrefix();
224 p.i = pos_4;
225 break :blk_4 !match_4;
226 }) break :blk_3 true;
211227 p.i = pos_3;
212228 break :blk_3 false;
213229 } or true) and p.parseAssignExpr() and p.parseSEMICOLON()) break :blk_0 true;
......@@ -215,31 +231,29 @@ const Parser = struct {
215231 break :blk_0 false;
216232 };
217233 }
218 pub fn parseExprStatement(p: *Parser) bool {
219 return blk_0: {
220 const pos_0 = p.i;
221 if (p.parseIfStatement()) break :blk_0 true;
222 p.i = pos_0;
223 if (p.parseLabeledStatement()) break :blk_0 true;
224 p.i = pos_0;
225 if (p.parseKEYWORD_nosuspend() and p.parseBlockExprStatement()) break :blk_0 true;
226 p.i = pos_0;
227 if (p.parseKEYWORD_comptime() and p.parseBlockExpr()) break :blk_0 true;
228 p.i = pos_0;
229 break :blk_0 false;
230 };
231 }
232234 pub fn parseIfStatement(p: *Parser) bool {
233235 return blk_0: {
234236 const pos_0 = p.i;
235 if (p.parseIfPrefix() and p.parseBlockExpr() and (blk_3: {
236 const pos_3 = p.i;
237 if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseStatement()) break :blk_3 true;
238 p.i = pos_3;
239 break :blk_3 false;
240 } or true)) break :blk_0 true;
237 if (p.parseIfPrefix() and p.parseBlockExpr() and blk_2: {
238 const pos_2 = p.i;
239 if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseStatement()) break :blk_2 true;
240 p.i = pos_2;
241 if (blk_3: {
242 const pos_3 = p.i;
243 const match_3 = p.parseKEYWORD_else();
244 p.i = pos_3;
245 break :blk_3 !match_3;
246 }) break :blk_2 true;
247 p.i = pos_2;
248 break :blk_2 false;
249 }) break :blk_0 true;
241250 p.i = pos_0;
242 if (p.parseIfPrefix() and p.parseAssignExpr() and blk_2: {
251 if (p.parseIfPrefix() and blk_1: {
252 const pos_1 = p.i;
253 const match_1 = p.parseBlockExprPrefix();
254 p.i = pos_1;
255 break :blk_1 !match_1;
256 } and p.parseAssignExpr() and blk_2: {
243257 const pos_2 = p.i;
244258 if (p.parseSEMICOLON()) break :blk_2 true;
245259 p.i = pos_2;
......@@ -300,7 +314,12 @@ const Parser = struct {
300314 break :blk_2 false;
301315 }) break :blk_0 true;
302316 p.i = pos_0;
303 if (p.parseForPrefix() and p.parseAssignExpr() and blk_2: {
317 if (p.parseForPrefix() and blk_1: {
318 const pos_1 = p.i;
319 const match_1 = p.parseBlockExprPrefix();
320 p.i = pos_1;
321 break :blk_1 !match_1;
322 } and p.parseAssignExpr() and blk_2: {
304323 const pos_2 = p.i;
305324 if (p.parseSEMICOLON()) break :blk_2 true;
306325 p.i = pos_2;
......@@ -315,14 +334,26 @@ const Parser = struct {
315334 pub fn parseWhileStatement(p: *Parser) bool {
316335 return blk_0: {
317336 const pos_0 = p.i;
318 if (p.parseWhilePrefix() and p.parseBlockExpr() and (blk_3: {
319 const pos_3 = p.i;
320 if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseStatement()) break :blk_3 true;
321 p.i = pos_3;
322 break :blk_3 false;
323 } or true)) break :blk_0 true;
337 if (p.parseWhilePrefix() and p.parseBlockExpr() and blk_2: {
338 const pos_2 = p.i;
339 if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseStatement()) break :blk_2 true;
340 p.i = pos_2;
341 if (blk_3: {
342 const pos_3 = p.i;
343 const match_3 = p.parseKEYWORD_else();
344 p.i = pos_3;
345 break :blk_3 !match_3;
346 }) break :blk_2 true;
347 p.i = pos_2;
348 break :blk_2 false;
349 }) break :blk_0 true;
324350 p.i = pos_0;
325 if (p.parseWhilePrefix() and p.parseAssignExpr() and blk_2: {
351 if (p.parseWhilePrefix() and blk_1: {
352 const pos_1 = p.i;
353 const match_1 = p.parseBlockExprPrefix();
354 p.i = pos_1;
355 break :blk_1 !match_1;
356 } and p.parseAssignExpr() and blk_2: {
326357 const pos_2 = p.i;
327358 if (p.parseSEMICOLON()) break :blk_2 true;
328359 p.i = pos_2;
......@@ -341,7 +372,7 @@ const Parser = struct {
341372 p.i = pos_0;
342373 if (blk_1: {
343374 const pos_1 = p.i;
344 const match_1 = p.parseBlockExpr();
375 const match_1 = p.parseBlockExprPrefix();
345376 p.i = pos_1;
346377 break :blk_1 !match_1;
347378 } and p.parseAssignExpr() and p.parseSEMICOLON()) break :blk_0 true;
......@@ -349,6 +380,14 @@ const Parser = struct {
349380 break :blk_0 false;
350381 };
351382 }
383 pub fn parseBlockExprPrefix(p: *Parser) bool {
384 return blk_0: {
385 const pos_0 = p.i;
386 if ((p.parseBlockLabel() or true) and p.parseLBRACE()) break :blk_0 true;
387 p.i = pos_0;
388 break :blk_0 false;
389 };
390 }
352391 pub fn parseBlockExpr(p: *Parser) bool {
353392 return blk_0: {
354393 const pos_0 = p.i;