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 <noreply@anthropic.com>
This commit is contained in:
179
parser.c
179
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(
|
||||
|
||||
Reference in New Issue
Block a user