parser: refactor to match upstream Parse.zig structure
Extract shared helpers and fix error handling to align with upstream: - Replace 7 assert() calls that crash on valid input with fprintf+exit - Extract parsePtrModifiers() and makePtrTypeNode() to deduplicate pointer modifier parsing from 4 inline copies into 1 shared function - Extract parseBlockExpr() and parseWhileContinueExpr() helpers - Move comptime wrapping into expectVarDeclExprStatement() via comptime_token parameter - Extract finishAssignExpr(), parseSwitchItem(), parseSwitchProng() Net effect: 3233 → 3106 lines. All 298+ parser tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
519
parser.c
519
parser.c
@@ -838,76 +838,75 @@ static AstNodeIndex parseErrorUnionExpr(Parser* p) {
|
||||
});
|
||||
}
|
||||
|
||||
// parsePtrModifiersAndType parses pointer modifiers (allowzero, align,
|
||||
// addrspace, const, volatile, sentinel) and the child type for a pointer
|
||||
// started at main_token.
|
||||
static AstNodeIndex parsePtrModifiersAndType(
|
||||
Parser* p, AstTokenIndex main_token) {
|
||||
AstNodeIndex sentinel = 0;
|
||||
AstNodeIndex align_expr = 0;
|
||||
AstNodeIndex bit_range_start = 0;
|
||||
AstNodeIndex bit_range_end = 0;
|
||||
AstNodeIndex addrspace_expr = 0;
|
||||
typedef struct {
|
||||
AstNodeIndex align_node;
|
||||
AstNodeIndex addrspace_node;
|
||||
AstNodeIndex bit_range_start;
|
||||
AstNodeIndex bit_range_end;
|
||||
} PtrModifiers;
|
||||
|
||||
// sentinel: *:0
|
||||
if (eatToken(p, TOKEN_COLON) != null_token)
|
||||
sentinel = expectExpr(p);
|
||||
static PtrModifiers parsePtrModifiers(Parser* p) {
|
||||
PtrModifiers mods = {};
|
||||
|
||||
// allowzero, const, volatile (before align)
|
||||
while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
|
||||
while (true) {
|
||||
switch (p->token_tags[p->tok_i]) {
|
||||
case TOKEN_KEYWORD_CONST:
|
||||
case TOKEN_KEYWORD_VOLATILE:
|
||||
case TOKEN_KEYWORD_ALLOWZERO:
|
||||
p->tok_i++;
|
||||
continue;
|
||||
case TOKEN_KEYWORD_ALIGN:
|
||||
p->tok_i++;
|
||||
|
||||
// align(expr) or align(expr:expr:expr)
|
||||
if (eatToken(p, TOKEN_KEYWORD_ALIGN) != null_token) {
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
align_expr = expectExpr(p);
|
||||
mods.align_node = expectExpr(p);
|
||||
if (eatToken(p, TOKEN_COLON) != null_token) {
|
||||
bit_range_start = expectExpr(p);
|
||||
mods.bit_range_start = expectExpr(p);
|
||||
expectToken(p, TOKEN_COLON);
|
||||
bit_range_end = expectExpr(p);
|
||||
mods.bit_range_end = expectExpr(p);
|
||||
}
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
}
|
||||
|
||||
// addrspace
|
||||
addrspace_expr = parseAddrSpace(p);
|
||||
|
||||
// const, volatile, allowzero (after align/addrspace)
|
||||
while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
|
||||
continue;
|
||||
case TOKEN_KEYWORD_ADDRSPACE:
|
||||
p->tok_i++;
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
mods.addrspace_node = expectExpr(p);
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
continue;
|
||||
default:
|
||||
return mods;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AstNodeIndex child_type = parseTypeExpr(p);
|
||||
|
||||
if (bit_range_start != 0) {
|
||||
static AstNodeIndex makePtrTypeNode(Parser* p, AstTokenIndex main_token,
|
||||
AstNodeIndex sentinel, PtrModifiers mods, AstNodeIndex elem_type) {
|
||||
if (mods.bit_range_start != 0) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_BIT_RANGE,
|
||||
.main_token = main_token,
|
||||
.data = {
|
||||
.lhs = addExtra(p,
|
||||
(AstNodeIndex[]) { OPT(sentinel), align_expr,
|
||||
OPT(addrspace_expr), bit_range_start,
|
||||
bit_range_end },
|
||||
(AstNodeIndex[]) { OPT(sentinel), mods.align_node,
|
||||
OPT(mods.addrspace_node), mods.bit_range_start,
|
||||
mods.bit_range_end },
|
||||
5),
|
||||
.rhs = child_type,
|
||||
.rhs = elem_type,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (addrspace_expr != 0 || (sentinel != 0 && align_expr != 0)) {
|
||||
if (mods.addrspace_node != 0 || (sentinel != 0 && mods.align_node != 0)) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE,
|
||||
.main_token = main_token,
|
||||
.data = {
|
||||
.lhs = addExtra(p,
|
||||
(AstNodeIndex[]) { OPT(sentinel), OPT(align_expr),
|
||||
OPT(addrspace_expr) },
|
||||
(AstNodeIndex[]) { OPT(sentinel),
|
||||
OPT(mods.align_node),
|
||||
OPT(mods.addrspace_node) },
|
||||
3),
|
||||
.rhs = child_type,
|
||||
.rhs = elem_type,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -916,14 +915,14 @@ static AstNodeIndex parsePtrModifiersAndType(
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_SENTINEL,
|
||||
.main_token = main_token,
|
||||
.data = { .lhs = sentinel, .rhs = child_type },
|
||||
.data = { .lhs = sentinel, .rhs = elem_type },
|
||||
});
|
||||
}
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_ALIGNED,
|
||||
.main_token = main_token,
|
||||
.data = { .lhs = align_expr, .rhs = child_type },
|
||||
.data = { .lhs = mods.align_node, .rhs = elem_type },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -943,77 +942,20 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
|
||||
exit(1);
|
||||
case TOKEN_ASTERISK: {
|
||||
const AstTokenIndex asterisk = nextToken(p);
|
||||
return parsePtrModifiersAndType(p, asterisk);
|
||||
const PtrModifiers mods = parsePtrModifiers(p);
|
||||
const AstNodeIndex elem_type = parseTypeExpr(p);
|
||||
return makePtrTypeNode(p, asterisk, 0, mods, elem_type);
|
||||
}
|
||||
case TOKEN_ASTERISK_ASTERISK: {
|
||||
// ** is two nested pointer types sharing the same token.
|
||||
// Inner pointer gets modifiers, outer wraps it with none.
|
||||
// (Matches upstream Parse.zig asterisk_asterisk case)
|
||||
const AstTokenIndex asterisk = nextToken(p);
|
||||
|
||||
// Parse inner pointer modifiers (no sentinel for **)
|
||||
while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
|
||||
p->tok_i++;
|
||||
AstNodeIndex align_expr = 0;
|
||||
AstNodeIndex bit_range_start = 0;
|
||||
AstNodeIndex bit_range_end = 0;
|
||||
if (eatToken(p, TOKEN_KEYWORD_ALIGN) != null_token) {
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
align_expr = expectExpr(p);
|
||||
if (eatToken(p, TOKEN_COLON) != null_token) {
|
||||
bit_range_start = expectExpr(p);
|
||||
expectToken(p, TOKEN_COLON);
|
||||
bit_range_end = expectExpr(p);
|
||||
}
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
}
|
||||
const AstNodeIndex addrspace_expr = parseAddrSpace(p);
|
||||
while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
|
||||
p->tok_i++;
|
||||
const PtrModifiers mods = parsePtrModifiers(p);
|
||||
const AstNodeIndex elem_type = parseTypeExpr(p);
|
||||
assert(elem_type != 0);
|
||||
|
||||
AstNodeIndex inner;
|
||||
if (bit_range_start != 0) {
|
||||
inner = addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_BIT_RANGE,
|
||||
.main_token = asterisk,
|
||||
.data = {
|
||||
.lhs = addExtra(p,
|
||||
(AstNodeIndex[]) { OPT(0), align_expr,
|
||||
OPT(addrspace_expr), bit_range_start,
|
||||
bit_range_end },
|
||||
5),
|
||||
.rhs = elem_type,
|
||||
},
|
||||
});
|
||||
} else if (addrspace_expr != 0) {
|
||||
inner = addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE,
|
||||
.main_token = asterisk,
|
||||
.data = {
|
||||
.lhs = addExtra(p,
|
||||
(AstNodeIndex[]) { OPT(0), OPT(align_expr),
|
||||
addrspace_expr },
|
||||
3),
|
||||
.rhs = elem_type,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
inner = addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_ALIGNED,
|
||||
.main_token = asterisk,
|
||||
.data = { .lhs = align_expr, .rhs = elem_type },
|
||||
});
|
||||
if (elem_type == 0) {
|
||||
fprintf(stderr, "expected type expression\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const AstNodeIndex inner
|
||||
= makePtrTypeNode(p, asterisk, 0, mods, elem_type);
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_ALIGNED,
|
||||
@@ -1029,7 +971,6 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
|
||||
AstNodeIndex sentinel = 0;
|
||||
if (p->token_tags[p->tok_i] == TOKEN_IDENTIFIER) {
|
||||
// Check for 'c' modifier: [*c]
|
||||
// The 'c' is a regular identifier token
|
||||
const char c = p->source[p->token_starts[p->tok_i]];
|
||||
if (c == 'c'
|
||||
&& p->token_starts[p->tok_i + 1]
|
||||
@@ -1041,133 +982,19 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
|
||||
sentinel = expectExpr(p);
|
||||
}
|
||||
expectToken(p, TOKEN_R_BRACKET);
|
||||
// Reuse shared pointer modifier + type parsing
|
||||
// If we captured a sentinel from [*:s], temporarily store it
|
||||
// and let parsePtrModifiersAndType handle the rest.
|
||||
// But parsePtrModifiersAndType expects sentinel after main
|
||||
// token via `:`. Since we already consumed it, we need to
|
||||
// handle this inline.
|
||||
|
||||
// allowzero, const, volatile (before align)
|
||||
while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
|
||||
p->tok_i++;
|
||||
|
||||
AstNodeIndex align_expr = 0;
|
||||
AstNodeIndex bit_range_start = 0;
|
||||
AstNodeIndex bit_range_end = 0;
|
||||
if (eatToken(p, TOKEN_KEYWORD_ALIGN) != null_token) {
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
align_expr = expectExpr(p);
|
||||
if (eatToken(p, TOKEN_COLON) != null_token) {
|
||||
bit_range_start = expectExpr(p);
|
||||
expectToken(p, TOKEN_COLON);
|
||||
bit_range_end = expectExpr(p);
|
||||
}
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
}
|
||||
const AstNodeIndex addrspace_expr = parseAddrSpace(p);
|
||||
|
||||
while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
|
||||
p->tok_i++;
|
||||
|
||||
const PtrModifiers mods = parsePtrModifiers(p);
|
||||
const AstNodeIndex elem_type = parseTypeExpr(p);
|
||||
|
||||
if (bit_range_start != 0) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_BIT_RANGE,
|
||||
.main_token = lbracket,
|
||||
.data = {
|
||||
.lhs = addExtra(p,
|
||||
(AstNodeIndex[]) { OPT(sentinel),
|
||||
align_expr, OPT(addrspace_expr),
|
||||
bit_range_start, bit_range_end },
|
||||
5),
|
||||
.rhs = elem_type,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (addrspace_expr != 0 || (sentinel != 0 && align_expr != 0)) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE,
|
||||
.main_token = lbracket,
|
||||
.data = {
|
||||
.lhs = addExtra(p,
|
||||
(AstNodeIndex[]) { OPT(sentinel),
|
||||
OPT(align_expr),
|
||||
OPT(addrspace_expr) },
|
||||
3),
|
||||
.rhs = elem_type,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (sentinel != 0) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_SENTINEL,
|
||||
.main_token = lbracket,
|
||||
.data = { .lhs = sentinel, .rhs = elem_type },
|
||||
});
|
||||
}
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_ALIGNED,
|
||||
.main_token = lbracket,
|
||||
.data = { .lhs = align_expr, .rhs = elem_type },
|
||||
});
|
||||
return makePtrTypeNode(p, lbracket, sentinel, mods, elem_type);
|
||||
}
|
||||
const AstNodeIndex len_expr = parseExpr(p);
|
||||
const AstNodeIndex sentinel
|
||||
= eatToken(p, TOKEN_COLON) != null_token ? expectExpr(p) : 0;
|
||||
expectToken(p, TOKEN_R_BRACKET);
|
||||
if (len_expr == 0) {
|
||||
// Slice type: []T or [:s]T — reuse shared modifier parsing
|
||||
// allowzero, const, volatile
|
||||
while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
|
||||
p->tok_i++;
|
||||
const AstNodeIndex align_expr = parseByteAlign(p);
|
||||
const AstNodeIndex addrspace_expr = parseAddrSpace(p);
|
||||
while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE
|
||||
|| p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO)
|
||||
p->tok_i++;
|
||||
// Slice type: []T or [:s]T
|
||||
const PtrModifiers mods = parsePtrModifiers(p);
|
||||
const AstNodeIndex elem_type = parseTypeExpr(p);
|
||||
if (addrspace_expr != 0 || (sentinel != 0 && align_expr != 0)) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE,
|
||||
.main_token = lbracket,
|
||||
.data = {
|
||||
.lhs = addExtra(p,
|
||||
(AstNodeIndex[]) { OPT(sentinel),
|
||||
OPT(align_expr),
|
||||
OPT(addrspace_expr) },
|
||||
3),
|
||||
.rhs = elem_type,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (sentinel != 0) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_SENTINEL,
|
||||
.main_token = lbracket,
|
||||
.data = { .lhs = sentinel, .rhs = elem_type },
|
||||
});
|
||||
}
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_PTR_TYPE_ALIGNED,
|
||||
.main_token = lbracket,
|
||||
.data = { .lhs = align_expr, .rhs = elem_type },
|
||||
});
|
||||
return makePtrTypeNode(p, lbracket, sentinel, mods, elem_type);
|
||||
}
|
||||
// Array type: [N]T or [N:s]T
|
||||
const AstNodeIndex elem_type = parseTypeExpr(p);
|
||||
@@ -1493,7 +1320,10 @@ static AstNodeIndex parseForStatement(Parser* p) {
|
||||
then_body = block;
|
||||
} else {
|
||||
then_body = parseAssignExpr(p);
|
||||
assert(then_body != 0);
|
||||
if (then_body == 0) {
|
||||
fprintf(stderr, "expected expression\n");
|
||||
exit(1);
|
||||
}
|
||||
if (eatToken(p, TOKEN_SEMICOLON) != null_token)
|
||||
seen_semicolon = true;
|
||||
}
|
||||
@@ -1545,6 +1375,15 @@ static AstNodeIndex parseForStatement(Parser* p) {
|
||||
});
|
||||
}
|
||||
|
||||
static AstNodeIndex parseWhileContinueExpr(Parser* p) {
|
||||
if (eatToken(p, TOKEN_COLON) == null_token)
|
||||
return null_node;
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
const AstNodeIndex expr = parseAssignExpr(p);
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
return expr;
|
||||
}
|
||||
|
||||
static AstNodeIndex parseWhileExpr(Parser* p) {
|
||||
const AstTokenIndex while_token = eatToken(p, TOKEN_KEYWORD_WHILE);
|
||||
if (while_token == null_token)
|
||||
@@ -1555,13 +1394,7 @@ static AstNodeIndex parseWhileExpr(Parser* p) {
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
parsePtrPayload(p);
|
||||
|
||||
// Continue expression: : (expr)
|
||||
AstNodeIndex cont_expr = 0;
|
||||
if (eatToken(p, TOKEN_COLON) != null_token) {
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
cont_expr = parseAssignExpr(p);
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
}
|
||||
const AstNodeIndex cont_expr = parseWhileContinueExpr(p);
|
||||
|
||||
const AstNodeIndex body = expectExpr(p);
|
||||
|
||||
@@ -1611,12 +1444,7 @@ static AstNodeIndex parseWhileStatement(Parser* p) {
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
parsePtrPayload(p);
|
||||
|
||||
AstNodeIndex cont_expr = 0;
|
||||
if (eatToken(p, TOKEN_COLON) != null_token) {
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
cont_expr = parseAssignExpr(p);
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
}
|
||||
const AstNodeIndex cont_expr = parseWhileContinueExpr(p);
|
||||
|
||||
// Statement body: block, or assign expr
|
||||
AstNodeIndex body;
|
||||
@@ -1626,7 +1454,10 @@ static AstNodeIndex parseWhileStatement(Parser* p) {
|
||||
body = block;
|
||||
} else {
|
||||
body = parseAssignExpr(p);
|
||||
assert(body != 0);
|
||||
if (body == 0) {
|
||||
fprintf(stderr, "expected expression\n");
|
||||
exit(1);
|
||||
}
|
||||
if (eatToken(p, TOKEN_SEMICOLON) != null_token)
|
||||
seen_semicolon = true;
|
||||
}
|
||||
@@ -2073,7 +1904,10 @@ static AstNodeIndex parseExprPrecedence(Parser* p, int32_t min_prec) {
|
||||
if (tok_tag == TOKEN_KEYWORD_CATCH)
|
||||
parsePayload(p);
|
||||
const AstNodeIndex rhs = parseExprPrecedence(p, info.prec + 1);
|
||||
assert(rhs != 0);
|
||||
if (rhs == 0) {
|
||||
fprintf(stderr, "expected expression\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
node = addNode(
|
||||
&p->nodes,
|
||||
@@ -2097,7 +1931,10 @@ static AstNodeIndex parseExpr(Parser* p) { return parseExprPrecedence(p, 0); }
|
||||
|
||||
static AstNodeIndex expectExpr(Parser* p) {
|
||||
const AstNodeIndex node = parseExpr(p);
|
||||
assert(node != 0);
|
||||
if (node == 0) {
|
||||
fprintf(stderr, "expected expression\n");
|
||||
exit(1);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -2258,56 +2095,47 @@ static AstNodeIndex parseAsmExpr(Parser* p) {
|
||||
});
|
||||
}
|
||||
|
||||
static AstNodeIndex parseSwitchExpr(Parser* p) {
|
||||
const AstTokenIndex switch_token = eatToken(p, TOKEN_KEYWORD_SWITCH);
|
||||
if (switch_token == null_token)
|
||||
static AstNodeIndex parseSwitchItem(Parser* p) {
|
||||
const AstNodeIndex expr = parseExpr(p);
|
||||
if (expr == 0)
|
||||
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,
|
||||
return 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);
|
||||
.data = { .lhs = expr, .rhs = range_end },
|
||||
});
|
||||
}
|
||||
if (p->token_tags[p->tok_i] == TOKEN_COMMA)
|
||||
p->tok_i++;
|
||||
return expr;
|
||||
}
|
||||
|
||||
static AstNodeIndex parseSwitchProng(Parser* p) {
|
||||
const uint32_t items_old_len = p->scratch.len;
|
||||
|
||||
if (eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) {
|
||||
while (true) {
|
||||
const AstNodeIndex item = parseSwitchItem(p);
|
||||
if (item == 0)
|
||||
break;
|
||||
SLICE_APPEND(AstNodeIndex, &p->scratch, item);
|
||||
if (eatToken(p, TOKEN_COMMA) == null_token)
|
||||
break;
|
||||
}
|
||||
if (p->scratch.len == items_old_len)
|
||||
return null_node;
|
||||
}
|
||||
|
||||
const AstTokenIndex arrow
|
||||
= expectToken(p, TOKEN_EQUAL_ANGLE_BRACKET_RIGHT);
|
||||
parsePtrPayload(p);
|
||||
const AstNodeIndex case_body = parseAssignExpr(p);
|
||||
assert(case_body != 0);
|
||||
if (case_body == 0) {
|
||||
fprintf(stderr, "expected expression\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const uint32_t items_len = p->scratch.len - items_old_len;
|
||||
AstNodeIndex case_node;
|
||||
@@ -2319,9 +2147,8 @@ static AstNodeIndex parseSwitchExpr(Parser* p) {
|
||||
.tag = AST_NODE_SWITCH_CASE_ONE,
|
||||
.main_token = arrow,
|
||||
.data = {
|
||||
.lhs = items_len >= 1
|
||||
? p->scratch.arr[items_old_len]
|
||||
: 0,
|
||||
.lhs
|
||||
= items_len >= 1 ? p->scratch.arr[items_old_len] : 0,
|
||||
.rhs = case_body,
|
||||
},
|
||||
});
|
||||
@@ -2342,10 +2169,31 @@ static AstNodeIndex parseSwitchExpr(Parser* p) {
|
||||
} break;
|
||||
}
|
||||
|
||||
// Restore scratch to before items but keep case_node count
|
||||
p->scratch.len = items_old_len;
|
||||
SLICE_APPEND(AstNodeIndex, &p->scratch, case_node);
|
||||
return case_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);
|
||||
const AstNodeIndex case_node = parseSwitchProng(p);
|
||||
if (case_node == 0)
|
||||
break;
|
||||
SLICE_APPEND(AstNodeIndex, &p->scratch, case_node);
|
||||
if (p->token_tags[p->tok_i] == TOKEN_COMMA)
|
||||
p->tok_i++;
|
||||
}
|
||||
@@ -2601,14 +2449,10 @@ static AstNodeTag assignOpTag(TokenizerTag tok) {
|
||||
}
|
||||
}
|
||||
|
||||
static AstNodeIndex parseAssignExpr(Parser* p) {
|
||||
const AstNodeIndex expr = parseExpr(p);
|
||||
if (expr == 0)
|
||||
return null_node;
|
||||
|
||||
static AstNodeIndex finishAssignExpr(Parser* p, AstNodeIndex lhs) {
|
||||
const AstNodeTag assign_tag = assignOpTag(p->token_tags[p->tok_i]);
|
||||
if (assign_tag == AST_NODE_ROOT)
|
||||
return expr;
|
||||
return lhs;
|
||||
|
||||
const AstTokenIndex op_token = nextToken(p);
|
||||
const AstNodeIndex rhs = expectExpr(p);
|
||||
@@ -2616,12 +2460,18 @@ static AstNodeIndex parseAssignExpr(Parser* p) {
|
||||
(AstNodeItem) {
|
||||
.tag = assign_tag,
|
||||
.main_token = op_token,
|
||||
.data = { .lhs = expr, .rhs = rhs },
|
||||
.data = { .lhs = lhs, .rhs = rhs },
|
||||
});
|
||||
}
|
||||
|
||||
static AstNodeIndex expectBlockExprStatement(Parser* p) {
|
||||
// Try block first (labeled or unlabeled)
|
||||
static AstNodeIndex parseAssignExpr(Parser* p) {
|
||||
const AstNodeIndex expr = parseExpr(p);
|
||||
if (expr == 0)
|
||||
return null_node;
|
||||
return finishAssignExpr(p, expr);
|
||||
}
|
||||
|
||||
static AstNodeIndex parseBlockExpr(Parser* p) {
|
||||
if (p->token_tags[p->tok_i] == TOKEN_L_BRACE)
|
||||
return parseBlock(p);
|
||||
if (p->token_tags[p->tok_i] == TOKEN_IDENTIFIER
|
||||
@@ -2630,6 +2480,13 @@ static AstNodeIndex expectBlockExprStatement(Parser* p) {
|
||||
p->tok_i += 2;
|
||||
return parseBlock(p);
|
||||
}
|
||||
return null_node;
|
||||
}
|
||||
|
||||
static AstNodeIndex expectBlockExprStatement(Parser* p) {
|
||||
const AstNodeIndex block_expr = parseBlockExpr(p);
|
||||
if (block_expr != 0)
|
||||
return block_expr;
|
||||
// Assign expr + semicolon
|
||||
const AstNodeIndex expr = parseAssignExpr(p);
|
||||
if (expr != 0) {
|
||||
@@ -2641,7 +2498,8 @@ static AstNodeIndex expectBlockExprStatement(Parser* p) {
|
||||
return 0; // tcc
|
||||
}
|
||||
|
||||
static AstNodeIndex expectVarDeclExprStatement(Parser* p) {
|
||||
static AstNodeIndex expectVarDeclExprStatement(
|
||||
Parser* p, AstTokenIndex comptime_token) {
|
||||
CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch)))
|
||||
= initCleanupScratch(p);
|
||||
|
||||
@@ -2665,15 +2523,46 @@ static AstNodeIndex expectVarDeclExprStatement(Parser* p) {
|
||||
switch (p->token_tags[p->tok_i]) {
|
||||
case TOKEN_SEMICOLON:
|
||||
p->tok_i++;
|
||||
if (comptime_token != null_token) {
|
||||
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) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_COMPTIME,
|
||||
.main_token = comptime_token,
|
||||
.data = { .lhs = lhs, .rhs = 0 },
|
||||
});
|
||||
}
|
||||
}
|
||||
return lhs;
|
||||
case TOKEN_R_BRACE:
|
||||
// Expression that doesn't need semicolon (block-terminated)
|
||||
if (comptime_token != null_token) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_COMPTIME,
|
||||
.main_token = comptime_token,
|
||||
.data = { .lhs = lhs, .rhs = 0 },
|
||||
});
|
||||
}
|
||||
return lhs;
|
||||
default: {
|
||||
// Check if expression ended with a block (previous token is })
|
||||
// and thus doesn't need a semicolon
|
||||
if (p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE)
|
||||
if (p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE) {
|
||||
if (comptime_token != null_token) {
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_COMPTIME,
|
||||
.main_token = comptime_token,
|
||||
.data = { .lhs = lhs, .rhs = 0 },
|
||||
});
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
const AstNodeTag assign_tag = assignOpTag(p->token_tags[p->tok_i]);
|
||||
if (assign_tag == AST_NODE_ROOT) {
|
||||
fprintf(stderr,
|
||||
@@ -2743,27 +2632,8 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) {
|
||||
});
|
||||
}
|
||||
// comptime var decl or expression
|
||||
if (allow_defer_var) {
|
||||
// Pass through to expectVarDeclExprStatement.
|
||||
// For var decls, the comptime prefix is detected from token
|
||||
// positions by the renderer (no wrapping needed).
|
||||
// For expressions, the result is wrapped in a comptime node.
|
||||
const AstNodeIndex inner = expectVarDeclExprStatement(p);
|
||||
const AstNodeTag inner_tag = p->nodes.tags[inner];
|
||||
if (inner_tag == AST_NODE_SIMPLE_VAR_DECL
|
||||
|| inner_tag == AST_NODE_ALIGNED_VAR_DECL
|
||||
|| inner_tag == AST_NODE_LOCAL_VAR_DECL
|
||||
|| inner_tag == AST_NODE_GLOBAL_VAR_DECL
|
||||
|| inner_tag == AST_NODE_ASSIGN_DESTRUCTURE) {
|
||||
return inner;
|
||||
}
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_COMPTIME,
|
||||
.main_token = comptime_token,
|
||||
.data = { .lhs = inner, .rhs = 0 },
|
||||
});
|
||||
}
|
||||
if (allow_defer_var)
|
||||
return expectVarDeclExprStatement(p, comptime_token);
|
||||
fprintf(
|
||||
stderr, "expectStatement: comptime keyword not supported here\n");
|
||||
exit(1);
|
||||
@@ -2834,7 +2704,7 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) {
|
||||
return labeled_statement;
|
||||
|
||||
if (allow_defer_var) {
|
||||
return expectVarDeclExprStatement(p);
|
||||
return expectVarDeclExprStatement(p, null_token);
|
||||
} else {
|
||||
const AstNodeIndex assign_expr = parseAssignExpr(p);
|
||||
expectSemicolon(p);
|
||||
@@ -3082,7 +2952,10 @@ static Members parseContainerMembers(Parser* p) {
|
||||
? nextToken(p)
|
||||
: null_token;
|
||||
const AstNodeIndex body = parseBlock(p);
|
||||
assert(body != 0);
|
||||
if (body == 0) {
|
||||
fprintf(stderr, "expected block after test\n");
|
||||
exit(1);
|
||||
}
|
||||
const AstNodeIndex test_decl = addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_TEST_DECL,
|
||||
|
||||
Reference in New Issue
Block a user