From a8bca439406a3c1d18b6b7d464f69ae02825d939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Tue, 10 Feb 2026 18:32:24 +0000 Subject: [PATCH] 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) --- parser.c | 67 ++++++++++++++++++++++++++++++++++++++++++++----- parser_test.zig | 12 +++++++++ 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/parser.c b/parser.c index 41374af164..fed9091395 100644 --- 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 index 7b34d415fb..c0c247b90f 100644 --- 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)) {