parser: refactor expectVarDeclExprStatement to match upstream
Restructure expectVarDeclExprStatement to match upstream Parse.zig's
approach: check for '=' first, then handle var decl init vs expression
statement separately. This fixes parsing of var decls with container
types (e.g., `const x: struct {} = val`), where the '}' of the type
was incorrectly treated as a block-terminated expression.
Also make container member parsing strict (longjmp on unexpected tokens
instead of recovery), and add for/while/labeled-block handling in
parseTypeExpr for function return types.
376/381 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
131
parser.c
131
parser.c
@@ -2650,88 +2650,66 @@ static AstNodeIndex expectVarDeclExprStatement(
|
||||
const uint32_t lhs_count = p->scratch.len - scratch_top.old_len;
|
||||
assert(lhs_count > 0);
|
||||
|
||||
if (lhs_count == 1) {
|
||||
// Try to eat '=' for assignment/initialization
|
||||
// (matches upstream: `const equal_token = p.eatToken(.equal) orelse eql:`)
|
||||
AstTokenIndex equal_token = eatToken(p, TOKEN_EQUAL);
|
||||
if (equal_token == null_token) {
|
||||
if (lhs_count > 1) {
|
||||
// Destructure requires '='
|
||||
fprintf(stderr, "expected '='\n");
|
||||
longjmp(p->error_jmp, 1);
|
||||
}
|
||||
const AstNodeIndex lhs = p->scratch.arr[scratch_top.old_len];
|
||||
switch (p->token_tags[p->tok_i]) {
|
||||
case TOKEN_SEMICOLON:
|
||||
p->tok_i++;
|
||||
if (comptime_token != null_token) {
|
||||
const AstNodeTag lhs_tag = p->nodes.tags[lhs];
|
||||
if (lhs_tag != AST_NODE_SIMPLE_VAR_DECL
|
||||
&& lhs_tag != AST_NODE_ALIGNED_VAR_DECL
|
||||
&& lhs_tag != AST_NODE_LOCAL_VAR_DECL
|
||||
&& lhs_tag != AST_NODE_GLOBAL_VAR_DECL) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_COMPTIME,
|
||||
.main_token = comptime_token,
|
||||
.data = { .lhs = lhs, .rhs = 0 },
|
||||
});
|
||||
}
|
||||
}
|
||||
return lhs;
|
||||
case TOKEN_R_BRACE:
|
||||
// Expression that doesn't need semicolon (block-terminated)
|
||||
if (comptime_token != null_token) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_COMPTIME,
|
||||
.main_token = comptime_token,
|
||||
.data = { .lhs = lhs, .rhs = 0 },
|
||||
});
|
||||
}
|
||||
return lhs;
|
||||
default: {
|
||||
// Check if expression ended with a block (previous token is })
|
||||
// and thus doesn't need a semicolon
|
||||
if (p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE) {
|
||||
if (comptime_token != null_token) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_COMPTIME,
|
||||
.main_token = comptime_token,
|
||||
.data = { .lhs = lhs, .rhs = 0 },
|
||||
});
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
const AstNodeTag assign_tag = assignOpTag(p->token_tags[p->tok_i]);
|
||||
if (assign_tag == AST_NODE_ROOT) {
|
||||
fprintf(stderr,
|
||||
"expectVarDeclExprStatement: unexpected token %s\n",
|
||||
tokenizerGetTagString(p->token_tags[p->tok_i]));
|
||||
longjmp(p->error_jmp, 1);
|
||||
}
|
||||
if (assign_tag == AST_NODE_ASSIGN) {
|
||||
// Check if lhs is a var decl that needs initialization
|
||||
const AstNodeTag lhs_tag = p->nodes.tags[lhs];
|
||||
if (lhs_tag == AST_NODE_SIMPLE_VAR_DECL
|
||||
|| lhs_tag == AST_NODE_ALIGNED_VAR_DECL
|
||||
|| lhs_tag == AST_NODE_LOCAL_VAR_DECL
|
||||
|| lhs_tag == AST_NODE_GLOBAL_VAR_DECL) {
|
||||
p->tok_i++;
|
||||
p->nodes.datas[lhs].rhs = expectExpr(p);
|
||||
expectSemicolon(p);
|
||||
return lhs;
|
||||
}
|
||||
}
|
||||
const AstTokenIndex op_token = nextToken(p);
|
||||
const AstNodeIndex rhs = expectExpr(p);
|
||||
expectSemicolon(p);
|
||||
const AstNodeTag lhs_tag = p->nodes.tags[lhs];
|
||||
if (lhs_tag == AST_NODE_SIMPLE_VAR_DECL
|
||||
|| lhs_tag == AST_NODE_ALIGNED_VAR_DECL
|
||||
|| lhs_tag == AST_NODE_LOCAL_VAR_DECL
|
||||
|| lhs_tag == AST_NODE_GLOBAL_VAR_DECL) {
|
||||
// var decl without init requires '='
|
||||
fprintf(stderr, "expected '='\n");
|
||||
longjmp(p->error_jmp, 1);
|
||||
}
|
||||
// Expression statement: finish with assignment operators or semicolon
|
||||
const AstNodeIndex expr = finishAssignExpr(p, lhs);
|
||||
// Semicolon is optional for block-terminated expressions
|
||||
eatToken(p, TOKEN_SEMICOLON);
|
||||
if (comptime_token != null_token) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = assign_tag,
|
||||
.main_token = op_token,
|
||||
.data = { .lhs = lhs, .rhs = rhs },
|
||||
.tag = AST_NODE_COMPTIME,
|
||||
.main_token = comptime_token,
|
||||
.data = { .lhs = expr, .rhs = 0 },
|
||||
});
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
// Have '=', parse RHS and semicolon
|
||||
const AstNodeIndex rhs = expectExpr(p);
|
||||
expectSemicolon(p);
|
||||
|
||||
if (lhs_count == 1) {
|
||||
const AstNodeIndex lhs = p->scratch.arr[scratch_top.old_len];
|
||||
const AstNodeTag lhs_tag = p->nodes.tags[lhs];
|
||||
if (lhs_tag == AST_NODE_SIMPLE_VAR_DECL
|
||||
|| lhs_tag == AST_NODE_ALIGNED_VAR_DECL
|
||||
|| lhs_tag == AST_NODE_LOCAL_VAR_DECL
|
||||
|| lhs_tag == AST_NODE_GLOBAL_VAR_DECL) {
|
||||
// var decl initialization: const x = val;
|
||||
p->nodes.datas[lhs].rhs = rhs;
|
||||
return lhs;
|
||||
}
|
||||
// Simple assignment: x = val;
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_ASSIGN,
|
||||
.main_token = equal_token,
|
||||
.data = { .lhs = lhs, .rhs = rhs },
|
||||
});
|
||||
}
|
||||
|
||||
// Destructure: a, b, c = rhs
|
||||
const AstTokenIndex equal_token = expectToken(p, TOKEN_EQUAL);
|
||||
const AstNodeIndex rhs = expectExpr(p);
|
||||
expectSemicolon(p);
|
||||
// rhs and semicolon already parsed above
|
||||
|
||||
// Store count + lhs nodes in extra_data
|
||||
const AstNodeIndex extra_start = p->extra_data.len;
|
||||
@@ -3181,11 +3159,10 @@ static Members parseContainerMembers(Parser* p) {
|
||||
case TOKEN_EOF:
|
||||
trailing = false;
|
||||
goto break_loop;
|
||||
default:;
|
||||
default:
|
||||
fprintf(stderr, "expected comma after field\n");
|
||||
longjmp(p->error_jmp, 1);
|
||||
}
|
||||
|
||||
findNextContainerMember(p);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user