parser: implement switch, port switch comment tests
Implement parseSwitchExpr in parser.c:
- switch(expr) { cases... } with case items, ranges, else
- switch_case_one and switch_case node types
- Proper scratch management for nested case items
Port tests:
- "switch comment before prong"
- "switch comment after prong"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
107
parser.c
107
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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user