parser: port test "errdefer with payload"
Implement in parser.c: - defer and errdefer statements with expectBlockExprStatement - parseAssignExpr for assignment expressions (expr op= expr) - expectBlockExprStatement: block or assign expr + semicolon - assignOpTag: map all assignment operator tokens to AST tags Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
165
parser.c
165
parser.c
@@ -29,6 +29,7 @@ static AstTokenIndex expectToken(Parser*, TokenizerTag);
|
||||
static AstNodeIndex parseFnProto(Parser*);
|
||||
static Members parseContainerMembers(Parser*);
|
||||
static AstNodeIndex parseInitList(Parser*, AstNodeIndex, AstTokenIndex);
|
||||
static AstNodeIndex expectBlockExprStatement(Parser*);
|
||||
|
||||
typedef struct {
|
||||
enum { FIELD_STATE_NONE, FIELD_STATE_SEEN, FIELD_STATE_END } tag;
|
||||
@@ -1129,13 +1130,6 @@ static AstNodeIndex parseLoopStatement(Parser* p) {
|
||||
return 0; // tcc
|
||||
}
|
||||
|
||||
static AstNodeIndex parseAssignExpr(Parser* p) {
|
||||
(void)p;
|
||||
fprintf(stderr, "parseAssignExpr not implemented\n");
|
||||
exit(1);
|
||||
return 0; // tcc
|
||||
}
|
||||
|
||||
static AstNodeIndex parseVarDeclProto(Parser* p) {
|
||||
AstTokenIndex mut_token;
|
||||
if ((mut_token = eatToken(p, TOKEN_KEYWORD_CONST)) == null_token)
|
||||
@@ -1714,6 +1708,89 @@ static AstNodeIndex parsePrefixExpr(Parser* p) {
|
||||
});
|
||||
}
|
||||
|
||||
static AstNodeTag assignOpTag(TokenizerTag tok) {
|
||||
switch (tok) {
|
||||
case TOKEN_EQUAL:
|
||||
return AST_NODE_ASSIGN;
|
||||
case TOKEN_PLUS_EQUAL:
|
||||
return AST_NODE_ASSIGN_ADD;
|
||||
case TOKEN_MINUS_EQUAL:
|
||||
return AST_NODE_ASSIGN_SUB;
|
||||
case TOKEN_ASTERISK_EQUAL:
|
||||
return AST_NODE_ASSIGN_MUL;
|
||||
case TOKEN_SLASH_EQUAL:
|
||||
return AST_NODE_ASSIGN_DIV;
|
||||
case TOKEN_PERCENT_EQUAL:
|
||||
return AST_NODE_ASSIGN_MOD;
|
||||
case TOKEN_AMPERSAND_EQUAL:
|
||||
return AST_NODE_ASSIGN_BIT_AND;
|
||||
case TOKEN_PIPE_EQUAL:
|
||||
return AST_NODE_ASSIGN_BIT_OR;
|
||||
case TOKEN_CARET_EQUAL:
|
||||
return AST_NODE_ASSIGN_BIT_XOR;
|
||||
case TOKEN_ANGLE_BRACKET_ANGLE_BRACKET_LEFT_EQUAL:
|
||||
return AST_NODE_ASSIGN_SHL;
|
||||
case TOKEN_ANGLE_BRACKET_ANGLE_BRACKET_RIGHT_EQUAL:
|
||||
return AST_NODE_ASSIGN_SHR;
|
||||
case TOKEN_PLUS_PERCENT_EQUAL:
|
||||
return AST_NODE_ASSIGN_ADD_WRAP;
|
||||
case TOKEN_MINUS_PERCENT_EQUAL:
|
||||
return AST_NODE_ASSIGN_SUB_WRAP;
|
||||
case TOKEN_ASTERISK_PERCENT_EQUAL:
|
||||
return AST_NODE_ASSIGN_MUL_WRAP;
|
||||
case TOKEN_PLUS_PIPE_EQUAL:
|
||||
return AST_NODE_ASSIGN_ADD_SAT;
|
||||
case TOKEN_MINUS_PIPE_EQUAL:
|
||||
return AST_NODE_ASSIGN_SUB_SAT;
|
||||
case TOKEN_ASTERISK_PIPE_EQUAL:
|
||||
return AST_NODE_ASSIGN_MUL_SAT;
|
||||
case TOKEN_ANGLE_BRACKET_ANGLE_BRACKET_LEFT_PIPE_EQUAL:
|
||||
return AST_NODE_ASSIGN_SHL_SAT;
|
||||
default:
|
||||
return AST_NODE_ROOT; // not an assignment op
|
||||
}
|
||||
}
|
||||
|
||||
static AstNodeIndex parseAssignExpr(Parser* p) {
|
||||
const AstNodeIndex expr = parseExpr(p);
|
||||
if (expr == 0)
|
||||
return null_node;
|
||||
|
||||
const AstNodeTag assign_tag = assignOpTag(p->token_tags[p->tok_i]);
|
||||
if (assign_tag == AST_NODE_ROOT)
|
||||
return expr;
|
||||
|
||||
const AstTokenIndex op_token = nextToken(p);
|
||||
const AstNodeIndex rhs = expectExpr(p);
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = assign_tag,
|
||||
.main_token = op_token,
|
||||
.data = { .lhs = expr, .rhs = rhs },
|
||||
});
|
||||
}
|
||||
|
||||
static AstNodeIndex expectBlockExprStatement(Parser* p) {
|
||||
// Try block first (labeled or unlabeled)
|
||||
if (p->token_tags[p->tok_i] == TOKEN_L_BRACE)
|
||||
return parseBlock(p);
|
||||
if (p->token_tags[p->tok_i] == TOKEN_IDENTIFIER
|
||||
&& p->token_tags[p->tok_i + 1] == TOKEN_COLON
|
||||
&& p->token_tags[p->tok_i + 2] == TOKEN_L_BRACE) {
|
||||
p->tok_i += 2;
|
||||
return parseBlock(p);
|
||||
}
|
||||
// Assign expr + semicolon
|
||||
const AstNodeIndex expr = parseAssignExpr(p);
|
||||
if (expr != 0) {
|
||||
expectSemicolon(p);
|
||||
return expr;
|
||||
}
|
||||
fprintf(stderr, "expectBlockExprStatement: expected block or expr\n");
|
||||
exit(1);
|
||||
return 0; // tcc
|
||||
}
|
||||
|
||||
static AstNodeIndex expectVarDeclExprStatement(Parser* p) {
|
||||
CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch)))
|
||||
= initCleanupScratch(p);
|
||||
@@ -1742,35 +1819,37 @@ static AstNodeIndex expectVarDeclExprStatement(Parser* p) {
|
||||
case TOKEN_R_BRACE:
|
||||
// Expression that doesn't need semicolon (block-terminated)
|
||||
return lhs;
|
||||
case TOKEN_EQUAL: {
|
||||
// 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;
|
||||
default: {
|
||||
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]));
|
||||
exit(1);
|
||||
}
|
||||
// Regular assignment expression
|
||||
const AstTokenIndex eq_token = nextToken(p);
|
||||
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);
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_ASSIGN,
|
||||
.main_token = eq_token,
|
||||
.tag = assign_tag,
|
||||
.main_token = op_token,
|
||||
.data = { .lhs = lhs, .rhs = rhs },
|
||||
});
|
||||
}
|
||||
default:
|
||||
fprintf(stderr,
|
||||
"expectVarDeclExprStatement: assignment not implemented "
|
||||
"for token %s\n",
|
||||
tokenizerGetTagString(p->token_tags[p->tok_i]));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1804,10 +1883,36 @@ 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 = 0,
|
||||
.rhs = expectBlockExprStatement(p),
|
||||
},
|
||||
});
|
||||
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);
|
||||
}
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_ERRDEFER,
|
||||
.main_token = errdefer_token,
|
||||
.data = {
|
||||
.lhs = payload,
|
||||
.rhs = expectBlockExprStatement(p),
|
||||
},
|
||||
});
|
||||
}
|
||||
case TOKEN_KEYWORD_NOSUSPEND:
|
||||
case TOKEN_KEYWORD_SUSPEND:
|
||||
case TOKEN_KEYWORD_DEFER:
|
||||
case TOKEN_KEYWORD_ERRDEFER:
|
||||
case TOKEN_KEYWORD_ENUM:
|
||||
case TOKEN_KEYWORD_STRUCT:
|
||||
case TOKEN_KEYWORD_UNION:;
|
||||
|
||||
@@ -1819,3 +1819,16 @@ test "zig fmt: top-level bare asterisk+asterisk+identifier" {
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: errdefer with payload" {
|
||||
try testCanonical(
|
||||
\\pub fn main() anyerror!void {
|
||||
\\ errdefer |a| x += 1;
|
||||
\\ errdefer |a| {}
|
||||
\\ errdefer |a| {
|
||||
\\ x += 1;
|
||||
\\ }
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user