commit a8bca439406a3c1d18b6b7d464f69ae02825d939 (tree)
parent 26c73c4f879d1542f484dd2079e806b5b9015329
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Tue, 10 Feb 2026 18:32:24 +0000
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>
Diffstat:
| M | parser.c | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ |
| M | parser_test.zig | | | 12 | ++++++++++++ |
2 files changed, 73 insertions(+), 6 deletions(-)
diff --git a/parser.c b/parser.c
@@ -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
diff --git a/parser_test.zig b/parser_test.zig
@@ -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)) {