From b4185600466bd14e39e68cd9f07017a8dc1fcf8e Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 17 Jun 2026 15:18:24 +0200 Subject: [PATCH] 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. --- doc/langref/grammar.peg | 24 +++--- lib/std/zig/Parse.zig | 57 ++++--------- lib/std/zig/parser_generated_oracle.zig | 107 ++++++++++++++++-------- 3 files changed, 101 insertions(+), 87 deletions(-) diff --git a/doc/langref/grammar.peg b/doc/langref/grammar.peg index 0f7b8d223c7c55995295ef4981c7770effe87b24..abb9a96e6b1efe8b7467f953f5849ebd58aa053a 100644 --- a/doc/langref/grammar.peg +++ b/doc/langref/grammar.peg @@ -27,39 +27,37 @@ BlockStatement <- Statement / KEYWORD_defer BlockExprStatement / KEYWORD_errdefer BlockExprStatement - / (KEYWORD_comptime)? VarAssignStatement + / (KEYWORD_comptime !BlockExprPrefix)? VarAssignStatement Statement - <- ExprStatement - / KEYWORD_suspend BlockExprStatement - / (KEYWORD_comptime)? AssignExpr SEMICOLON - -ExprStatement <- IfStatement / LabeledStatement / KEYWORD_nosuspend BlockExprStatement / KEYWORD_comptime BlockExpr + / KEYWORD_suspend BlockExprStatement + / (KEYWORD_comptime !BlockExprPrefix)? AssignExpr SEMICOLON IfStatement - <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? - / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) + <- IfPrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else) + / IfPrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Payload? Statement) LabeledStatement <- BlockLabel? (Block / LoopStatement / SwitchExpr) LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) ForStatement - <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else ) - / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) + <- ForPrefix BlockExpr (KEYWORD_else Statement / !KEYWORD_else) + / ForPrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Statement) WhileStatement - <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? - / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) + <- WhilePrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else) + / WhilePrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Payload? Statement) BlockExprStatement <- BlockExpr - / !BlockExpr AssignExpr SEMICOLON + / !BlockExprPrefix AssignExpr SEMICOLON +BlockExprPrefix <- BlockLabel? LBRACE BlockExpr <- BlockLabel? Block # An assignment or a destructure whose LHS are all lvalue expressions or variable declarations. diff --git a/lib/std/zig/Parse.zig b/lib/std/zig/Parse.zig index 0978bad73823deb2a0a47dc68a1064cd9923e352..b0918b237c6c4d8e827d0a2182a7378646077a5c 100644 --- a/lib/std/zig/Parse.zig +++ b/lib/std/zig/Parse.zig @@ -864,7 +864,7 @@ fn parseGlobalVarDecl(p: *Parse) !?Node.Index { return var_decl; } -/// ContainerField <- doc_comment? (KEYWORD_comptime / !KEYWORD_comptime) !KEYWORD_fn (IDENTIFIER COLON / !(IDENTIFIER COLON))? TypeExpr ByteAlign? (EQUAL Expr)? +// ContainerField <- doc_comment? KEYWORD_comptime? (IDENTIFIER COLON)? TypeExpr ByteAlign? (EQUAL Expr)? fn expectContainerField(p: *Parse) !Node.Index { _ = p.eatToken(.keyword_comptime); const main_token = p.tok_i; @@ -910,18 +910,15 @@ fn expectContainerField(p: *Parse) !Node.Index { /// <- Statement /// / KEYWORD_defer BlockExprStatement /// / KEYWORD_errdefer BlockExprStatement -/// / !ExprStatement (KEYWORD_comptime !BlockExpr)? VarAssignStatement +/// / (KEYWORD_comptime !BlockExprPrefix)? VarAssignStatement /// /// Statement -/// <- ExprStatement -/// / KEYWORD_suspend BlockExprStatement -/// / !ExprStatement (KEYWORD_comptime !BlockExpr)? AssignExpr SEMICOLON -/// -/// ExprStatement /// <- IfStatement /// / LabeledStatement /// / KEYWORD_nosuspend BlockExprStatement /// / KEYWORD_comptime BlockExpr +/// / KEYWORD_suspend BlockExprStatement +/// / (KEYWORD_comptime !BlockExprPrefix)? AssignExpr SEMICOLON fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index { if (p.eatToken(.keyword_comptime)) |comptime_token| { const opt_block_expr = try p.parseBlockExpr(); @@ -934,7 +931,7 @@ fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index { } if (is_block_level) { - return p.expectVarDeclExprStatement(comptime_token); + return p.expectVarAssignStatement(comptime_token); } else { const assign = try p.expectAssignExpr(); try p.expectSemicolon(.expected_semi_after_stmt, true); @@ -995,7 +992,7 @@ fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index { if (try p.parseLabeledStatement()) |labeled_statement| return labeled_statement; if (is_block_level) { - return p.expectVarDeclExprStatement(null); + return p.expectVarAssignStatement(null); } else { const assign = try p.expectAssignExpr(); try p.expectSemicolon(.expected_semi_after_stmt, true); @@ -1003,30 +1000,8 @@ fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index { } } -/// ComptimeStatement -/// <- BlockExpr -/// / VarDeclExprStatement -fn expectComptimeStatement(p: *Parse, comptime_token: TokenIndex) !Node.Index { - const maybe_block_expr = try p.parseBlockExpr(); - if (maybe_block_expr) |block_expr| { - return p.addNode(.{ - .tag = .@"comptime", - .main_token = comptime_token, - .data = .{ - .lhs = .{ .node = block_expr }, - .rhs = undefined, - }, - }); - } - return p.expectVarDeclExprStatement(comptime_token); -} - -/// VarDeclExprStatement -/// <- Expr -/// / VarAssignStatement -/// /// VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON -fn expectVarDeclExprStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Index { +fn expectVarAssignStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Index { const scratch_top = p.scratch.items.len; defer p.scratch.shrinkRetainingCapacity(scratch_top); @@ -1138,7 +1113,7 @@ fn expectVarDeclExprStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Inde /// If a parse error occurs, reports an error, but then finds the next statement /// and returns that one instead. If a parse error occurs but there is no following -/// statement, returns 0. +/// statement, returns null. fn expectStatementRecoverable(p: *Parse) Error!?Node.Index { while (true) { return p.expectStatement(true) catch |err| switch (err) { @@ -1156,8 +1131,10 @@ fn expectStatementRecoverable(p: *Parse) Error!?Node.Index { } /// IfStatement -/// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? -/// / IfPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) +/// <- IfPrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else) +/// / IfPrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Payload? Statement) +/// +/// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? fn expectIfStatement(p: *Parse) !Node.Index { const if_token = p.assertToken(.keyword_if); _ = try p.expectToken(.l_paren); @@ -1252,8 +1229,8 @@ fn parseLoopStatement(p: *Parse) !?Node.Index { } /// ForStatement -/// <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else ) -/// / ForPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Statement ) +/// <- ForPrefix BlockExpr (KEYWORD_else Statement / !KEYWORD_else) +/// / ForPrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Statement) fn parseForStatement(p: *Parse) !?Node.Index { const for_token = p.eatToken(.keyword_for) orelse return null; @@ -1309,8 +1286,8 @@ fn parseForStatement(p: *Parse) !?Node.Index { /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? /// /// WhileStatement -/// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? -/// / WhilePrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) +/// <- WhilePrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else) +/// / WhilePrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Payload? Statement) fn parseWhileStatement(p: *Parse) !?Node.Index { const while_token = p.eatToken(.keyword_while) orelse return null; _ = try p.expectToken(.l_paren); @@ -1400,7 +1377,7 @@ fn parseWhileStatement(p: *Parse) !?Node.Index { /// BlockExprStatement /// <- BlockExpr -/// / !BlockExpr AssignExpr SEMICOLON +/// / !BlockExprPrefix AssignExpr SEMICOLON fn parseBlockExprStatement(p: *Parse) !?Node.Index { const block_expr = try p.parseBlockExpr(); if (block_expr) |expr| return expr; diff --git a/lib/std/zig/parser_generated_oracle.zig b/lib/std/zig/parser_generated_oracle.zig index ae0a81de132c97859bfe9f6edf1867545b456b6b..80bfe9db6c01d79b2bb5eda7e006f2855515b0e4 100644 --- a/lib/std/zig/parser_generated_oracle.zig +++ b/lib/std/zig/parser_generated_oracle.zig @@ -190,7 +190,12 @@ const Parser = struct { p.i = pos_0; if ((blk_3: { const pos_3 = p.i; - if (p.parseKEYWORD_comptime()) break :blk_3 true; + if (p.parseKEYWORD_comptime() and blk_4: { + const pos_4 = p.i; + const match_4 = p.parseBlockExprPrefix(); + p.i = pos_4; + break :blk_4 !match_4; + }) break :blk_3 true; p.i = pos_3; break :blk_3 false; } or true) and p.parseVarAssignStatement()) break :blk_0 true; @@ -199,23 +204,6 @@ const Parser = struct { }; } pub fn parseStatement(p: *Parser) bool { - return blk_0: { - const pos_0 = p.i; - if (p.parseExprStatement()) break :blk_0 true; - p.i = pos_0; - if (p.parseKEYWORD_suspend() and p.parseBlockExprStatement()) break :blk_0 true; - p.i = pos_0; - if ((blk_3: { - const pos_3 = p.i; - if (p.parseKEYWORD_comptime()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - } or true) and p.parseAssignExpr() and p.parseSEMICOLON()) break :blk_0 true; - p.i = pos_0; - break :blk_0 false; - }; - } - pub fn parseExprStatement(p: *Parser) bool { return blk_0: { const pos_0 = p.i; if (p.parseIfStatement()) break :blk_0 true; @@ -226,20 +214,46 @@ const Parser = struct { p.i = pos_0; if (p.parseKEYWORD_comptime() and p.parseBlockExpr()) break :blk_0 true; p.i = pos_0; + if (p.parseKEYWORD_suspend() and p.parseBlockExprStatement()) break :blk_0 true; + p.i = pos_0; + if ((blk_3: { + const pos_3 = p.i; + if (p.parseKEYWORD_comptime() and blk_4: { + const pos_4 = p.i; + const match_4 = p.parseBlockExprPrefix(); + p.i = pos_4; + break :blk_4 !match_4; + }) break :blk_3 true; + p.i = pos_3; + break :blk_3 false; + } or true) and p.parseAssignExpr() and p.parseSEMICOLON()) break :blk_0 true; + p.i = pos_0; break :blk_0 false; }; } pub fn parseIfStatement(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseIfPrefix() and p.parseBlockExpr() and (blk_3: { - const pos_3 = p.i; - if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseStatement()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - } or true)) break :blk_0 true; + if (p.parseIfPrefix() and p.parseBlockExpr() and blk_2: { + const pos_2 = p.i; + if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseStatement()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + const pos_3 = p.i; + const match_3 = p.parseKEYWORD_else(); + p.i = pos_3; + break :blk_3 !match_3; + }) break :blk_2 true; + p.i = pos_2; + break :blk_2 false; + }) break :blk_0 true; p.i = pos_0; - if (p.parseIfPrefix() and p.parseAssignExpr() and blk_2: { + if (p.parseIfPrefix() and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseBlockExprPrefix(); + p.i = pos_1; + break :blk_1 !match_1; + } and p.parseAssignExpr() and blk_2: { const pos_2 = p.i; if (p.parseSEMICOLON()) break :blk_2 true; p.i = pos_2; @@ -300,7 +314,12 @@ const Parser = struct { break :blk_2 false; }) break :blk_0 true; p.i = pos_0; - if (p.parseForPrefix() and p.parseAssignExpr() and blk_2: { + if (p.parseForPrefix() and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseBlockExprPrefix(); + p.i = pos_1; + break :blk_1 !match_1; + } and p.parseAssignExpr() and blk_2: { const pos_2 = p.i; if (p.parseSEMICOLON()) break :blk_2 true; p.i = pos_2; @@ -315,14 +334,26 @@ const Parser = struct { pub fn parseWhileStatement(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseWhilePrefix() and p.parseBlockExpr() and (blk_3: { - const pos_3 = p.i; - if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseStatement()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - } or true)) break :blk_0 true; + if (p.parseWhilePrefix() and p.parseBlockExpr() and blk_2: { + const pos_2 = p.i; + if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseStatement()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + const pos_3 = p.i; + const match_3 = p.parseKEYWORD_else(); + p.i = pos_3; + break :blk_3 !match_3; + }) break :blk_2 true; + p.i = pos_2; + break :blk_2 false; + }) break :blk_0 true; p.i = pos_0; - if (p.parseWhilePrefix() and p.parseAssignExpr() and blk_2: { + if (p.parseWhilePrefix() and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseBlockExprPrefix(); + p.i = pos_1; + break :blk_1 !match_1; + } and p.parseAssignExpr() and blk_2: { const pos_2 = p.i; if (p.parseSEMICOLON()) break :blk_2 true; p.i = pos_2; @@ -341,7 +372,7 @@ const Parser = struct { p.i = pos_0; if (blk_1: { const pos_1 = p.i; - const match_1 = p.parseBlockExpr(); + const match_1 = p.parseBlockExprPrefix(); p.i = pos_1; break :blk_1 !match_1; } and p.parseAssignExpr() and p.parseSEMICOLON()) break :blk_0 true; @@ -349,6 +380,14 @@ const Parser = struct { break :blk_0 false; }; } + pub fn parseBlockExprPrefix(p: *Parser) bool { + return blk_0: { + const pos_0 = p.i; + if ((p.parseBlockLabel() or true) and p.parseLBRACE()) break :blk_0 true; + p.i = pos_0; + break :blk_0 false; + }; + } pub fn parseBlockExpr(p: *Parser) bool { return blk_0: { const pos_0 = p.i; -- 2.54.0