parser: implement while loops, port while test

Implement in parser.c:
- parseWhileExpr: while (cond) body, with optional payload,
  continue expression, and else clause
- while_simple, while_cont, while AST nodes

Port test "while else err prong with no block".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-02-10 18:32:24 +00:00
parent 26c73c4f87
commit a8bca43940
2 changed files with 73 additions and 6 deletions

View File

@@ -33,6 +33,10 @@ static AstNodeIndex parseFnProto(Parser*);
static Members parseContainerMembers(Parser*);
static AstNodeIndex parseInitList(Parser*, AstNodeIndex, AstTokenIndex);
static AstNodeIndex expectBlockExprStatement(Parser*);
static AstNodeIndex parseWhileExpr(Parser*);
static AstNodeIndex parseAssignExpr(Parser*);
static void parsePtrPayload(Parser*);
static void parsePayload(Parser*);
typedef struct {
enum { FIELD_STATE_NONE, FIELD_STATE_SEEN, FIELD_STATE_END } tag;
@@ -1277,14 +1281,64 @@ static AstNodeIndex parseForStatement(Parser* p) {
return 0; // tcc
}
static AstNodeIndex parseWhileStatement(Parser* p) {
const AstNodeIndex while_token = eatToken(p, TOKEN_KEYWORD_WHILE);
static AstNodeIndex parseWhileExpr(Parser* p) {
const AstTokenIndex while_token = eatToken(p, TOKEN_KEYWORD_WHILE);
if (while_token == null_token)
return null_node;
(void)while_token;
fprintf(stderr, "parseWhileStatement cannot parse while statements\n");
return 0; // tcc
expectToken(p, TOKEN_L_PAREN);
const AstNodeIndex condition = expectExpr(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 body = expectExpr(p);
if (eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) {
if (cont_expr != 0) {
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_WHILE_CONT,
.main_token = while_token,
.data = {
.lhs = condition,
.rhs = addExtra(p,
(AstNodeIndex[]) { cont_expr, body }, 2),
},
});
}
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_WHILE_SIMPLE,
.main_token = while_token,
.data = { .lhs = condition, .rhs = body },
});
}
parsePayload(p);
const AstNodeIndex else_expr = expectExpr(p);
return addNode(&p->nodes,
(AstNodeItem) {
.tag = AST_NODE_WHILE,
.main_token = while_token,
.data = {
.lhs = condition,
.rhs = addExtra(p,
(AstNodeIndex[]) { OPT(cont_expr), body, else_expr },
3),
},
});
}
static AstNodeIndex parseWhileStatement(Parser* p) {
return parseWhileExpr(p);
}
static AstNodeIndex parseLoopStatement(Parser* p) {
@@ -1851,9 +1905,10 @@ static AstNodeIndex parsePrimaryExpr(Parser* p) {
} else {
return parseCurlySuffixExpr(p);
}
case TOKEN_KEYWORD_WHILE:
return parseWhileExpr(p);
case TOKEN_KEYWORD_INLINE:
case TOKEN_KEYWORD_FOR:
case TOKEN_KEYWORD_WHILE:
fprintf(stderr, "parsePrimaryExpr does not implement %s\n", tok);
exit(1);
return 0; // tcc

View File

@@ -1517,6 +1517,18 @@ test "zig fmt: whitespace fixes" {
);
}
test "zig fmt: while else err prong with no block" {
try testCanonical(
\\test "" {
\\ const result = while (returnError()) |value| {
\\ break value;
\\ } else |err| @as(i32, 2);
\\ try expect(result == 2);
\\}
\\
);
}
test "zig fmt: tagged union with enum values" {
try testCanonical(
\\const MultipleChoice2 = union(enum(u32)) {