From 9d15552f1c61ceffefed881786a13a51b7b08210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Wed, 11 Feb 2026 18:10:26 +0000 Subject: [PATCH] parser: align structural logic with upstream Parse.zig Fix 11 divergences where parser.c differed from Parse.zig in logic or structure, not justified by C vs Zig language differences: - parseContainerMembers: set trailing=false after test decl, add field_state tracking (A1, A2) - expectStatement: guard defer/errdefer behind allow_defer_var (A3) - expectVarDeclExprStatement: wrap assignment in comptime node when comptime_token is set (A4) - parseBlock: guard semicolon check with statements_len != 0 (A5) - parseLabeledStatement: add parseSwitchExpr call (A6) - parseWhileStatement: restructure with else_required and early returns to match upstream control flow (A7) - parseForStatement: restructure with else_required/has_else and early returns to match upstream control flow (A8) - parseFnProto: fail when return_type_expr is missing (A9) - expectTopLevelDecl: track is_extern, reject extern fn body (A10) - parsePrefixExpr: remove TOKEN_KEYWORD_AWAIT case (A11) Co-Authored-By: Claude Opus 4.6 --- parser.c | 179 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 109 insertions(+), 70 deletions(-) diff --git a/parser.c b/parser.c index 7ade9c77e2..02080c503a 100644 --- a/parser.c +++ b/parser.c @@ -251,8 +251,12 @@ static Members parseContainerMembers(Parser* p) { if (doc_comment != null_token) fail(p, "test_doc_comment"); const AstNodeIndex test_decl = expectTestDecl(p); + if (field_state.tag == FIELD_STATE_SEEN) { + field_state.tag = FIELD_STATE_END; + field_state.payload.end = test_decl; + } SLICE_APPEND(AstNodeIndex, &p->scratch, test_decl); - trailing = p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE; + trailing = false; break; } case TOKEN_KEYWORD_USINGNAMESPACE:; @@ -464,10 +468,12 @@ static AstNodeIndex expectTestDecl(Parser* p) { static AstNodeIndex expectTopLevelDecl(Parser* p) { AstTokenIndex extern_export_inline_token = nextToken(p); + bool is_extern = false; switch (p->token_tags[extern_export_inline_token]) { case TOKEN_KEYWORD_EXTERN: eatToken(p, TOKEN_STRING_LITERAL); + is_extern = true; break; case TOKEN_KEYWORD_EXPORT: case TOKEN_KEYWORD_INLINE: @@ -484,6 +490,9 @@ static AstNodeIndex expectTopLevelDecl(Parser* p) { p->tok_i++; return fn_proto; case TOKEN_L_BRACE:; + if (is_extern) { + fail(p, "extern_fn_body"); + } AstNodeIndex fn_decl_index = reserveNode(p, AST_NODE_FN_DECL); AstNodeIndex body_block = parseBlock(p); return setNode(p, fn_decl_index, @@ -525,6 +534,9 @@ static AstNodeIndex parseFnProto(Parser* p) { eatToken(p, TOKEN_BANG); const AstNodeIndex return_type_expr = parseTypeExpr(p); + if (return_type_expr == 0) { + fail(p, "expected_return_type"); + } if (align_expr == 0 && section_expr == 0 && callconv_expr == 0 && addrspace_expr == 0) { @@ -747,33 +759,37 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { const AstNodeIndex tok = p->token_tags[p->tok_i]; switch (tok) { case TOKEN_KEYWORD_DEFER: - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_DEFER, - .main_token = nextToken(p), - .data = { - .lhs = expectBlockExprStatement(p), - .rhs = 0, - }, - }); - case TOKEN_KEYWORD_ERRDEFER: { - const AstTokenIndex errdefer_token = nextToken(p); - AstTokenIndex payload = null_token; - if (p->token_tags[p->tok_i] == TOKEN_PIPE) { - p->tok_i++; - payload = expectToken(p, TOKEN_IDENTIFIER); - expectToken(p, TOKEN_PIPE); + if (allow_defer_var) + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_DEFER, + .main_token = nextToken(p), + .data = { + .lhs = expectBlockExprStatement(p), + .rhs = 0, + }, + }); + break; + case TOKEN_KEYWORD_ERRDEFER: + if (allow_defer_var) { + const AstTokenIndex errdefer_token = nextToken(p); + AstTokenIndex payload = null_token; + if (p->token_tags[p->tok_i] == TOKEN_PIPE) { + p->tok_i++; + payload = expectToken(p, TOKEN_IDENTIFIER); + expectToken(p, TOKEN_PIPE); + } + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_ERRDEFER, + .main_token = errdefer_token, + .data = { + .lhs = payload, + .rhs = expectBlockExprStatement(p), + }, + }); } - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_ERRDEFER, - .main_token = errdefer_token, - .data = { - .lhs = payload, - .rhs = expectBlockExprStatement(p), - }, - }); - } + break; case TOKEN_KEYWORD_NOSUSPEND: return addNode(&p->nodes, (AstNodeItem) { @@ -884,12 +900,21 @@ static AstNodeIndex expectVarDeclExprStatement( return lhs; } // Simple assignment: x = val; - return addNode(&p->nodes, + const AstNodeIndex assign = addNode(&p->nodes, (AstNodeItem) { .tag = AST_NODE_ASSIGN, .main_token = equal_token, .data = { .lhs = lhs, .rhs = rhs }, }); + if (comptime_token != null_token) { + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_COMPTIME, + .main_token = comptime_token, + .data = { .lhs = assign, .rhs = 0 }, + }); + } + return assign; } // Destructure: a, b, c = rhs @@ -970,8 +995,12 @@ static AstNodeIndex parseLabeledStatement(Parser* p) { if (loop_stmt != 0) return loop_stmt; + const AstNodeIndex switch_expr = parseSwitchExpr(p); + if (switch_expr != 0) + return switch_expr; + if (label_token != 0) { - fail(p, "parseLabeledStatement does not support labels"); + fail(p, "expected_labelable"); } return null_node; @@ -1004,56 +1033,50 @@ static AstNodeIndex parseForStatement(Parser* p) { const uint32_t inputs = forPrefix(p); // Statement body: block or assign expr - AstNodeIndex then_body; + bool else_required = false; bool seen_semicolon = false; + AstNodeIndex then_body; const AstNodeIndex block = parseBlock(p); if (block != 0) { then_body = block; } else { then_body = parseAssignExpr(p); if (then_body == 0) { - fail(p, "expected expression"); + fail(p, "expected_block_or_assignment"); } - if (eatToken(p, TOKEN_SEMICOLON) != null_token) + if (eatToken(p, TOKEN_SEMICOLON) != null_token) { seen_semicolon = true; + } else { + else_required = true; + } } + bool has_else = false; if (!seen_semicolon && eatToken(p, TOKEN_KEYWORD_ELSE) != null_token) { parsePayload(p); SLICE_APPEND(AstNodeIndex, &p->scratch, then_body); - const AstNodeIndex else_body = expectBlockExprStatement(p); + const AstNodeIndex else_body = expectStatement(p, false); SLICE_APPEND(AstNodeIndex, &p->scratch, else_body); - const uint32_t total = p->scratch.len - scratch_top; - const AstSubRange span - = listToSpan(p, &p->scratch.arr[scratch_top], total); - p->scratch.len = scratch_top; - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_FOR, - .main_token = for_token, - .data = { - .lhs = span.start, - .rhs = ((uint32_t)inputs & 0x7FFFFFFF) | (1u << 31), - }, - }); - } - - if (!seen_semicolon && block == 0) { - fail(p, "expected_semi_or_else"); - } - - if (inputs == 1) { - const AstNodeIndex input = p->scratch.arr[scratch_top]; + has_else = true; + } else if (inputs == 1) { + if (else_required) + fail(p, "expected_semi_or_else"); p->scratch.len = scratch_top; return addNode(&p->nodes, (AstNodeItem) { .tag = AST_NODE_FOR_SIMPLE, .main_token = for_token, - .data = { .lhs = input, .rhs = then_body }, + .data = { + .lhs = p->scratch.arr[scratch_top], + .rhs = then_body, + }, }); + } else { + if (else_required) + fail(p, "expected_semi_or_else"); + SLICE_APPEND(AstNodeIndex, &p->scratch, then_body); } - SLICE_APPEND(AstNodeIndex, &p->scratch, then_body); const uint32_t total = p->scratch.len - scratch_top; const AstSubRange span = listToSpan(p, &p->scratch.arr[scratch_top], total); @@ -1064,7 +1087,8 @@ static AstNodeIndex parseForStatement(Parser* p) { .main_token = for_token, .data = { .lhs = span.start, - .rhs = (uint32_t)inputs & 0x7FFFFFFF, + .rhs = ((uint32_t)inputs & 0x7FFFFFFF) + | (has_else ? (1u << 31) : 0), }, }); } @@ -1139,24 +1163,42 @@ static AstNodeIndex parseWhileStatement(Parser* p) { const AstNodeIndex cont_expr = parseWhileContinueExpr(p); // Statement body: block, or assign expr + bool else_required = false; AstNodeIndex body; - bool seen_semicolon = false; const AstNodeIndex block = parseBlock(p); if (block != 0) { body = block; } else { body = parseAssignExpr(p); if (body == 0) { - fail(p, "expected expression"); + fail(p, "expected_block_or_assignment"); } - if (eatToken(p, TOKEN_SEMICOLON) != null_token) - seen_semicolon = true; + if (eatToken(p, TOKEN_SEMICOLON) != null_token) { + if (cont_expr != 0) { + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_WHILE_CONT, + .main_token = while_token, + .data = { + .lhs = condition, + .rhs = addExtra(p, + (AstNodeIndex[]) { cont_expr, body }, 2), + }, + }); + } + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_WHILE_SIMPLE, + .main_token = while_token, + .data = { .lhs = condition, .rhs = body }, + }); + } + else_required = true; } - if (seen_semicolon || eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) { - if (!seen_semicolon && block == 0) { + if (eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) { + if (else_required) fail(p, "expected_semi_or_else"); - } if (cont_expr != 0) { return addNode(&p->nodes, (AstNodeItem) { @@ -1178,7 +1220,7 @@ static AstNodeIndex parseWhileStatement(Parser* p) { } parsePayload(p); - const AstNodeIndex else_body = expectBlockExprStatement(p); + const AstNodeIndex else_body = expectStatement(p, false); return addNode(&p->nodes, (AstNodeItem) { .tag = AST_NODE_WHILE, @@ -1520,9 +1562,6 @@ static AstNodeIndex parsePrefixExpr(Parser* p) { case TOKEN_KEYWORD_TRY: tag = AST_NODE_TRY; break; - case TOKEN_KEYWORD_AWAIT: - tag = AST_NODE_AWAIT; - break; default: return parsePrimaryExpr(p); } @@ -1951,9 +1990,9 @@ static AstNodeIndex parseBlock(Parser* p) { SLICE_APPEND(AstNodeIndex, &p->scratch, statement); } expectToken(p, TOKEN_R_BRACE); - const bool semicolon = (p->token_tags[p->tok_i - 2] == TOKEN_SEMICOLON); - const uint32_t statements_len = p->scratch.len - scratch_top.old_len; + const bool semicolon = statements_len != 0 + && (p->token_tags[p->tok_i - 2] == TOKEN_SEMICOLON); switch (statements_len) { case 0: return addNode(