parser: align function names with upstream Parse.zig

Rename assignOpTag to assignOpNode to match upstream. Extract inlined
code into separate functions to match upstream's structure:
expectTestDecl, expectIfStatement, expectParamDecl, parseSwitchProngList.
Add parseSingleAssignExpr for upstream API surface alignment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 13:48:45 +00:00
parent 237a05a2fc
commit 57e033e4b3

234
parser.c
View File

@@ -50,7 +50,7 @@ typedef struct {
static AstNodeIndex addExtra(Parser*, const AstNodeIndex*, uint32_t); static AstNodeIndex addExtra(Parser*, const AstNodeIndex*, uint32_t);
static AstNodeIndex addNode(AstNodeList*, AstNodeItem); static AstNodeIndex addNode(AstNodeList*, AstNodeItem);
static AstNodeTag assignOpTag(TokenizerTag); static AstNodeTag assignOpNode(TokenizerTag);
static AstTokenIndex assertToken(Parser*, TokenizerTag); static AstTokenIndex assertToken(Parser*, TokenizerTag);
static void astNodeListEnsureCapacity(AstNodeList*, uint32_t); static void astNodeListEnsureCapacity(AstNodeList*, uint32_t);
static void cleanupScratch(CleanupScratch*); static void cleanupScratch(CleanupScratch*);
@@ -59,8 +59,11 @@ static AstTokenIndex eatToken(Parser*, TokenizerTag);
static AstNodeIndex expectBlockExprStatement(Parser*); static AstNodeIndex expectBlockExprStatement(Parser*);
static AstNodeIndex expectContainerField(Parser*); static AstNodeIndex expectContainerField(Parser*);
static AstNodeIndex expectExpr(Parser*); static AstNodeIndex expectExpr(Parser*);
static AstNodeIndex expectIfStatement(Parser*);
static AstNodeIndex expectParamDecl(Parser*);
static AstNodeIndex expectSemicolon(Parser*); static AstNodeIndex expectSemicolon(Parser*);
static AstNodeIndex expectStatement(Parser*, bool); static AstNodeIndex expectStatement(Parser*, bool);
static AstNodeIndex expectTestDecl(Parser*);
static AstTokenIndex expectToken(Parser*, TokenizerTag); static AstTokenIndex expectToken(Parser*, TokenizerTag);
static AstNodeIndex expectTopLevelDecl(Parser*); static AstNodeIndex expectTopLevelDecl(Parser*);
static AstNodeIndex expectVarDeclExprStatement(Parser*, AstTokenIndex); static AstNodeIndex expectVarDeclExprStatement(Parser*, AstTokenIndex);
@@ -107,11 +110,13 @@ static AstNodeIndex parsePrimaryExpr(Parser*);
static AstNodeIndex parsePrimaryTypeExpr(Parser*); static AstNodeIndex parsePrimaryTypeExpr(Parser*);
static PtrModifiers parsePtrModifiers(Parser*); static PtrModifiers parsePtrModifiers(Parser*);
static void parsePtrPayload(Parser*); static void parsePtrPayload(Parser*);
static AstNodeIndex parseSingleAssignExpr(Parser*);
static AstNodeIndex parseSuffixExpr(Parser*); static AstNodeIndex parseSuffixExpr(Parser*);
static AstNodeIndex parseSuffixOp(Parser*, AstNodeIndex); static AstNodeIndex parseSuffixOp(Parser*, AstNodeIndex);
static AstNodeIndex parseSwitchExpr(Parser*); static AstNodeIndex parseSwitchExpr(Parser*);
static AstNodeIndex parseSwitchItem(Parser*); static AstNodeIndex parseSwitchItem(Parser*);
static AstNodeIndex parseSwitchProng(Parser*); static AstNodeIndex parseSwitchProng(Parser*);
static AstSubRange parseSwitchProngList(Parser*);
static AstNodeIndex parseTypeExpr(Parser*); static AstNodeIndex parseTypeExpr(Parser*);
static AstNodeIndex parseVarDeclProto(Parser*); static AstNodeIndex parseVarDeclProto(Parser*);
static AstNodeIndex parseWhileContinueExpr(Parser*); static AstNodeIndex parseWhileContinueExpr(Parser*);
@@ -1220,6 +1225,18 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
return 0; // tcc return 0; // tcc
} }
static AstNodeIndex expectParamDecl(Parser* p) {
eatDocComments(p);
eatToken(p, TOKEN_KEYWORD_COMPTIME);
eatToken(p, TOKEN_KEYWORD_NOALIAS);
if (p->token_tags[p->tok_i] == TOKEN_IDENTIFIER
&& p->token_tags[p->tok_i + 1] == TOKEN_COLON)
p->tok_i += 2;
if (eatToken(p, TOKEN_KEYWORD_ANYTYPE) != null_token)
return 0;
return parseTypeExpr(p);
}
static SmallSpan parseParamDeclList(Parser* p) { static SmallSpan parseParamDeclList(Parser* p) {
expectToken(p, TOKEN_L_PAREN); expectToken(p, TOKEN_L_PAREN);
@@ -1235,18 +1252,7 @@ static SmallSpan parseParamDeclList(Parser* p) {
if (varargs == 1) if (varargs == 1)
varargs = 2; varargs = 2;
eatDocComments(p); if (p->token_tags[p->tok_i] == TOKEN_ELLIPSIS3) {
// Check for comptime or noalias
eatToken(p, TOKEN_KEYWORD_COMPTIME);
eatToken(p, TOKEN_KEYWORD_NOALIAS);
// Check for name: type or just type
if (p->token_tags[p->tok_i] == TOKEN_IDENTIFIER
&& p->token_tags[p->tok_i + 1] == TOKEN_COLON) {
p->tok_i += 2; // consume name and colon
} else if (p->token_tags[p->tok_i] == TOKEN_ELLIPSIS3) {
// varargs (...)
p->tok_i++; p->tok_i++;
if (varargs == 0) if (varargs == 0)
varargs = 1; varargs = 1;
@@ -1256,17 +1262,7 @@ static SmallSpan parseParamDeclList(Parser* p) {
continue; continue;
} }
// anytype params are omitted from the AST const AstNodeIndex type_expr = expectParamDecl(p);
if (eatToken(p, TOKEN_KEYWORD_ANYTYPE) != null_token) {
if (p->token_tags[p->tok_i] == TOKEN_COMMA) {
p->tok_i++;
continue;
}
expectToken(p, TOKEN_R_PAREN);
break;
}
const AstNodeIndex type_expr = parseTypeExpr(p);
if (type_expr != 0) if (type_expr != 0)
SLICE_APPEND(AstNodeIndex, &p->scratch, type_expr); SLICE_APPEND(AstNodeIndex, &p->scratch, type_expr);
@@ -2442,19 +2438,9 @@ static AstNodeIndex parseSwitchProng(Parser* p) {
return case_node; return case_node;
} }
static AstNodeIndex parseSwitchExpr(Parser* p) { static AstSubRange parseSwitchProngList(Parser* p) {
const AstTokenIndex switch_token = eatToken(p, TOKEN_KEYWORD_SWITCH);
if (switch_token == null_token)
return null_node;
expectToken(p, TOKEN_L_PAREN);
const AstNodeIndex operand = expectExpr(p);
expectToken(p, TOKEN_R_PAREN);
expectToken(p, TOKEN_L_BRACE);
CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch))) CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch)))
= initCleanupScratch(p); = initCleanupScratch(p);
while (true) { while (true) {
if (eatToken(p, TOKEN_R_BRACE) != null_token) if (eatToken(p, TOKEN_R_BRACE) != null_token)
break; break;
@@ -2466,11 +2452,22 @@ static AstNodeIndex parseSwitchExpr(Parser* p) {
if (p->token_tags[p->tok_i] == TOKEN_COMMA) if (p->token_tags[p->tok_i] == TOKEN_COMMA)
p->tok_i++; p->tok_i++;
} }
const bool comma = p->token_tags[p->tok_i - 2] == TOKEN_COMMA;
const uint32_t cases_len = p->scratch.len - scratch_top.old_len; const uint32_t cases_len = p->scratch.len - scratch_top.old_len;
const AstSubRange span return listToSpan(p, &p->scratch.arr[scratch_top.old_len], cases_len);
= listToSpan(p, &p->scratch.arr[scratch_top.old_len], cases_len); }
static AstNodeIndex parseSwitchExpr(Parser* p) {
const AstTokenIndex switch_token = eatToken(p, TOKEN_KEYWORD_SWITCH);
if (switch_token == null_token)
return null_node;
expectToken(p, TOKEN_L_PAREN);
const AstNodeIndex operand = expectExpr(p);
expectToken(p, TOKEN_R_PAREN);
expectToken(p, TOKEN_L_BRACE);
const AstSubRange span = parseSwitchProngList(p);
const bool comma = p->token_tags[p->tok_i - 2] == TOKEN_COMMA;
return addNode(&p->nodes, return addNode(&p->nodes,
(AstNodeItem) { (AstNodeItem) {
.tag = comma ? AST_NODE_SWITCH_COMMA : AST_NODE_SWITCH, .tag = comma ? AST_NODE_SWITCH_COMMA : AST_NODE_SWITCH,
@@ -2682,7 +2679,7 @@ static AstNodeIndex parsePrefixExpr(Parser* p) {
}); });
} }
static AstNodeTag assignOpTag(TokenizerTag tok) { static AstNodeTag assignOpNode(TokenizerTag tok) {
switch (tok) { switch (tok) {
case TOKEN_EQUAL: case TOKEN_EQUAL:
return AST_NODE_ASSIGN; return AST_NODE_ASSIGN;
@@ -2726,7 +2723,7 @@ static AstNodeTag assignOpTag(TokenizerTag tok) {
} }
static AstNodeIndex finishAssignExpr(Parser* p, AstNodeIndex lhs) { static AstNodeIndex finishAssignExpr(Parser* p, AstNodeIndex lhs) {
const AstNodeTag assign_tag = assignOpTag(p->token_tags[p->tok_i]); const AstNodeTag assign_tag = assignOpNode(p->token_tags[p->tok_i]);
if (assign_tag == AST_NODE_ROOT) if (assign_tag == AST_NODE_ROOT)
return lhs; return lhs;
@@ -2747,6 +2744,23 @@ static AstNodeIndex parseAssignExpr(Parser* p) {
return finishAssignExpr(p, expr); return finishAssignExpr(p, expr);
} }
static AstNodeIndex parseSingleAssignExpr(Parser* p) {
const AstNodeIndex expr = parseExpr(p);
if (expr == 0)
return null_node;
const AstNodeTag tag = assignOpNode(p->token_tags[p->tok_i]);
if (tag == AST_NODE_ROOT)
return expr;
const AstTokenIndex op_token = nextToken(p);
const AstNodeIndex rhs = expectExpr(p);
return addNode(&p->nodes,
(AstNodeItem) {
.tag = tag,
.main_token = op_token,
.data = { .lhs = expr, .rhs = rhs },
});
}
static AstNodeIndex parseBlockExpr(Parser* p) { static AstNodeIndex parseBlockExpr(Parser* p) {
if (p->token_tags[p->tok_i] == TOKEN_L_BRACE) if (p->token_tags[p->tok_i] == TOKEN_L_BRACE)
return parseBlock(p); return parseBlock(p);
@@ -2869,6 +2883,54 @@ static AstNodeIndex expectVarDeclExprStatement(
}); });
} }
static AstNodeIndex expectIfStatement(Parser* p) {
const AstTokenIndex if_token = assertToken(p, TOKEN_KEYWORD_IF);
expectToken(p, TOKEN_L_PAREN);
const AstNodeIndex condition = expectExpr(p);
expectToken(p, TOKEN_R_PAREN);
parsePtrPayload(p);
bool else_required = false;
AstNodeIndex then_body;
const AstNodeIndex block2 = parseBlockExpr(p);
if (block2 != 0) {
then_body = block2;
} else {
then_body = parseAssignExpr(p);
if (then_body == 0)
fail(p, "expected block or assignment");
if (eatToken(p, TOKEN_SEMICOLON) != null_token)
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_IF_SIMPLE,
.main_token = if_token,
.data = { .lhs = condition, .rhs = then_body },
});
else_required = true;
}
if (eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) {
if (else_required)
fail(p, "expected_semi_or_else");
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_IF_SIMPLE,
.main_token = if_token,
.data = { .lhs = condition, .rhs = then_body },
});
}
parsePayload(p);
const AstNodeIndex else_body = expectStatement(p, false);
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_IF,
.main_token = if_token,
.data = {
.lhs = condition,
.rhs = addExtra(p,
(AstNodeIndex[]) { then_body, else_body }, 2),
},
});
}
static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) {
const AstTokenIndex comptime_token = eatToken(p, TOKEN_KEYWORD_COMPTIME); const AstTokenIndex comptime_token = eatToken(p, TOKEN_KEYWORD_COMPTIME);
if (comptime_token != null_token) { if (comptime_token != null_token) {
@@ -2950,55 +3012,8 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) {
.rhs = 0, .rhs = 0,
}, },
}); });
case TOKEN_KEYWORD_IF: { case TOKEN_KEYWORD_IF:
const AstTokenIndex if_token = nextToken(p); return expectIfStatement(p);
expectToken(p, TOKEN_L_PAREN);
const AstNodeIndex condition = expectExpr(p);
expectToken(p, TOKEN_R_PAREN);
parsePtrPayload(p);
bool else_required = false;
AstNodeIndex then_body;
const AstNodeIndex block2 = parseBlockExpr(p);
if (block2 != 0) {
then_body = block2;
} else {
then_body = parseAssignExpr(p);
if (then_body == 0) {
fail(p, "expected block or assignment");
}
if (eatToken(p, TOKEN_SEMICOLON) != null_token)
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_IF_SIMPLE,
.main_token = if_token,
.data = { .lhs = condition, .rhs = then_body },
});
else_required = true;
}
if (eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) {
if (else_required) {
fail(p, "expected_semi_or_else");
}
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_IF_SIMPLE,
.main_token = if_token,
.data = { .lhs = condition, .rhs = then_body },
});
}
parsePayload(p);
const AstNodeIndex else_body = expectStatement(p, false);
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_IF,
.main_token = if_token,
.data = {
.lhs = condition,
.rhs = addExtra(p,
(AstNodeIndex[]) { then_body, else_body }, 2),
},
});
}
case TOKEN_KEYWORD_ENUM: case TOKEN_KEYWORD_ENUM:
case TOKEN_KEYWORD_STRUCT: case TOKEN_KEYWORD_STRUCT:
case TOKEN_KEYWORD_UNION:; case TOKEN_KEYWORD_UNION:;
@@ -3235,6 +3250,24 @@ static void findNextContainerMember(Parser* p) {
} }
} }
static AstNodeIndex expectTestDecl(Parser* p) {
const AstTokenIndex test_token = assertToken(p, TOKEN_KEYWORD_TEST);
const AstTokenIndex test_name
= (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL
|| p->token_tags[p->tok_i] == TOKEN_IDENTIFIER)
? nextToken(p)
: null_token;
const AstNodeIndex body = parseBlock(p);
if (body == 0)
fail(p, "expected block after test");
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_TEST_DECL,
.main_token = test_token,
.data = { .lhs = test_name, .rhs = body },
});
}
static Members parseContainerMembers(Parser* p) { static Members parseContainerMembers(Parser* p) {
CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch))) CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch)))
= initCleanupScratch(p); = initCleanupScratch(p);
@@ -3248,26 +3281,9 @@ static Members parseContainerMembers(Parser* p) {
const AstTokenIndex doc_comment = eatDocComments(p); const AstTokenIndex doc_comment = eatDocComments(p);
switch (p->token_tags[p->tok_i]) { switch (p->token_tags[p->tok_i]) {
case TOKEN_KEYWORD_TEST: { case TOKEN_KEYWORD_TEST: {
if (doc_comment != null_token) { if (doc_comment != null_token)
fail(p, "test_doc_comment"); fail(p, "test_doc_comment");
} const AstNodeIndex test_decl = expectTestDecl(p);
const AstTokenIndex test_token = nextToken(p);
// test name can be a string literal or identifier, or omitted
const AstTokenIndex test_name
= (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL
|| p->token_tags[p->tok_i] == TOKEN_IDENTIFIER)
? nextToken(p)
: null_token;
const AstNodeIndex body = parseBlock(p);
if (body == 0) {
fail(p, "expected block after test");
}
const AstNodeIndex test_decl = addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_TEST_DECL,
.main_token = test_token,
.data = { .lhs = test_name, .rhs = body },
});
SLICE_APPEND(AstNodeIndex, &p->scratch, test_decl); SLICE_APPEND(AstNodeIndex, &p->scratch, test_decl);
trailing = p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE; trailing = p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE;
break; break;