diff --git a/parser.c b/parser.c index 1f96dee260..484220c2e4 100644 --- a/parser.c +++ b/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:; diff --git a/parser_test.zig b/parser_test.zig index 3f1e2fefd8..e12627adf8 100644 --- a/parser_test.zig +++ b/parser_test.zig @@ -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; + \\ } + \\} + \\ + ); +}