diff --git a/parser.c b/parser.c index f256934677..3feb7aba35 100644 --- a/parser.c +++ b/parser.c @@ -37,6 +37,7 @@ static AstNodeIndex parseWhileExpr(Parser*); static AstNodeIndex parseAssignExpr(Parser*); static void parsePtrPayload(Parser*); static void parsePayload(Parser*); +static AstNodeIndex parseSwitchExpr(Parser*); typedef struct { enum { FIELD_STATE_NONE, FIELD_STATE_SEEN, FIELD_STATE_END } tag; @@ -474,10 +475,11 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) { case TOKEN_KEYWORD_FN: return parseFnProto(p); case TOKEN_KEYWORD_IF: - case TOKEN_KEYWORD_SWITCH: fprintf(stderr, "parsePrimaryTypeExpr does not support %s\n", tokenizerGetTagString(tok)); exit(1); + case TOKEN_KEYWORD_SWITCH: + return parseSwitchExpr(p); case TOKEN_KEYWORD_EXTERN: case TOKEN_KEYWORD_PACKED: // extern/packed can precede struct/union/enum @@ -1775,6 +1777,109 @@ static AstNodeIndex expectExpr(Parser* p) { return node; } +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); + + CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch))) + = initCleanupScratch(p); + + while (true) { + if (eatToken(p, TOKEN_R_BRACE) != null_token) + break; + eatDocComments(p); + // Parse switch case items + const uint32_t items_old_len = p->scratch.len; + + while (true) { + if (p->token_tags[p->tok_i] == TOKEN_KEYWORD_ELSE) { + p->tok_i++; + break; + } + if (p->token_tags[p->tok_i] == TOKEN_EQUAL_ANGLE_BRACKET_RIGHT) + break; + const AstNodeIndex item = expectExpr(p); + if (p->token_tags[p->tok_i] == TOKEN_ELLIPSIS3) { + const AstTokenIndex range_tok = nextToken(p); + const AstNodeIndex range_end = expectExpr(p); + SLICE_APPEND(AstNodeIndex, &p->scratch, + addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_SWITCH_RANGE, + .main_token = range_tok, + .data = { .lhs = item, .rhs = range_end }, + })); + } else { + SLICE_APPEND(AstNodeIndex, &p->scratch, item); + } + if (p->token_tags[p->tok_i] == TOKEN_COMMA) + p->tok_i++; + } + + const AstTokenIndex arrow + = expectToken(p, TOKEN_EQUAL_ANGLE_BRACKET_RIGHT); + parsePtrPayload(p); + const AstNodeIndex case_body = expectExpr(p); + + const uint32_t items_len = p->scratch.len - items_old_len; + AstNodeIndex case_node; + switch (items_len) { + case 0: + case 1: + case_node = addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_SWITCH_CASE_ONE, + .main_token = arrow, + .data = { + .lhs = items_len >= 1 + ? p->scratch.arr[items_old_len] + : 0, + .rhs = case_body, + }, + }); + break; + default: { + const AstSubRange span + = listToSpan(p, &p->scratch.arr[items_old_len], items_len); + case_node = addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_SWITCH_CASE, + .main_token = arrow, + .data = { .lhs = span.start, .rhs = case_body }, + }); + } break; + } + + // Restore scratch to before items but keep case_node count + p->scratch.len = items_old_len; + SLICE_APPEND(AstNodeIndex, &p->scratch, case_node); + + if (p->token_tags[p->tok_i] == TOKEN_COMMA) + 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 AstSubRange span + = listToSpan(p, &p->scratch.arr[scratch_top.old_len], cases_len); + return addNode(&p->nodes, + (AstNodeItem) { + .tag = comma ? AST_NODE_SWITCH_COMMA : AST_NODE_SWITCH, + .main_token = switch_token, + .data = { + .lhs = operand, + .rhs = addExtra(p, + (AstNodeIndex[]) { span.start, span.end }, 2), + }, + }); +} + static void parsePtrPayload(Parser* p) { if (eatToken(p, TOKEN_PIPE) == null_token) return; diff --git a/parser_test.zig b/parser_test.zig index 09ec6fbd77..38f57e82fc 100644 --- a/parser_test.zig +++ b/parser_test.zig @@ -2137,6 +2137,31 @@ test "zig fmt: function call with multiline argument" { ); } +test "zig fmt: switch comment before prong" { + try testCanonical( + \\comptime { + \\ switch (a) { + \\ // hi + \\ 0 => {}, + \\ } + \\} + \\ + ); +} + +test "zig fmt: switch comment after prong" { + try testCanonical( + \\comptime { + \\ switch (a) { + \\ 0, + \\ // hi + \\ => {}, + \\ } + \\} + \\ + ); +} + test "zig fmt: if-else with comment before else" { try testCanonical( \\comptime {