parser: implement for loops, port for/while loop test
Implement in parser.c: - forPrefix: parse for input expressions and capture variables - parseForExpr: for_simple and for AST nodes with optional else - Handle for and while in parsePrimaryTypeExpr for top-level usage Remove stale cppcheck knownConditionTrueFalse suppression. Port test "top-level for/while loop". Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -88,7 +88,6 @@ pub fn build(b: *std.Build) !void {
|
||||
"--suppress=checkersReport",
|
||||
"--suppress=unusedFunction", // TODO remove after plumbing is done
|
||||
"--suppress=unusedStructMember", // TODO remove after plumbing is done
|
||||
"--suppress=knownConditionTrueFalse", // TODO remove after plumbing is done
|
||||
});
|
||||
for (all_c_files) |cfile| cppcheck.addFileArg(b.path(cfile));
|
||||
cppcheck.expectExitCode(0);
|
||||
|
||||
112
parser.c
112
parser.c
@@ -38,6 +38,7 @@ static AstNodeIndex parseAssignExpr(Parser*);
|
||||
static void parsePtrPayload(Parser*);
|
||||
static void parsePayload(Parser*);
|
||||
static AstNodeIndex parseSwitchExpr(Parser*);
|
||||
static AstNodeIndex parseForExpr(Parser*);
|
||||
|
||||
typedef struct {
|
||||
enum { FIELD_STATE_NONE, FIELD_STATE_SEEN, FIELD_STATE_END } tag;
|
||||
@@ -525,9 +526,11 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) {
|
||||
.main_token = nextToken(p),
|
||||
.data = {},
|
||||
});
|
||||
case TOKEN_KEYWORD_INLINE:
|
||||
case TOKEN_KEYWORD_FOR:
|
||||
return parseForExpr(p);
|
||||
case TOKEN_KEYWORD_WHILE:
|
||||
return parseWhileExpr(p);
|
||||
case TOKEN_KEYWORD_INLINE:
|
||||
case TOKEN_PERIOD:
|
||||
switch (p->token_tags[p->tok_i + 1]) {
|
||||
case TOKEN_IDENTIFIER: {
|
||||
@@ -1309,16 +1312,110 @@ static AstTokenIndex parseBlockLabel(Parser* p) {
|
||||
return null_node;
|
||||
}
|
||||
|
||||
static AstNodeIndex parseForStatement(Parser* p) {
|
||||
const AstNodeIndex for_token = eatToken(p, TOKEN_KEYWORD_FOR);
|
||||
// forPrefix parses the for prefix: (expr, expr, ...) |captures|.
|
||||
// Returns the number of input expressions. The inputs are appended
|
||||
// to the scratch buffer.
|
||||
static uint32_t forPrefix(Parser* p) {
|
||||
const uint32_t start = p->scratch.len;
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
|
||||
while (true) {
|
||||
AstNodeIndex input = expectExpr(p);
|
||||
if (eatToken(p, TOKEN_ELLIPSIS2) != null_token) {
|
||||
const AstTokenIndex ellipsis = p->tok_i - 1;
|
||||
const AstNodeIndex end = parseExpr(p);
|
||||
input = addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_FOR_RANGE,
|
||||
.main_token = ellipsis,
|
||||
.data = { .lhs = input, .rhs = end },
|
||||
});
|
||||
}
|
||||
SLICE_APPEND(AstNodeIndex, &p->scratch, input);
|
||||
if (p->token_tags[p->tok_i] == TOKEN_COMMA) {
|
||||
p->tok_i++;
|
||||
continue;
|
||||
}
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
break;
|
||||
}
|
||||
const uint32_t inputs = p->scratch.len - start;
|
||||
|
||||
// Parse payload |a, *b, c|
|
||||
if (eatToken(p, TOKEN_PIPE) != null_token) {
|
||||
while (true) {
|
||||
eatToken(p, TOKEN_ASTERISK);
|
||||
expectToken(p, TOKEN_IDENTIFIER);
|
||||
if (p->token_tags[p->tok_i] == TOKEN_COMMA) {
|
||||
p->tok_i++;
|
||||
continue;
|
||||
}
|
||||
expectToken(p, TOKEN_PIPE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
|
||||
static AstNodeIndex parseForExpr(Parser* p) {
|
||||
const AstTokenIndex for_token = eatToken(p, TOKEN_KEYWORD_FOR);
|
||||
if (for_token == null_token)
|
||||
return null_node;
|
||||
|
||||
(void)for_token;
|
||||
fprintf(stderr, "parseForStatement cannot parse for statements\n");
|
||||
return 0; // tcc
|
||||
const uint32_t scratch_top = p->scratch.len;
|
||||
const uint32_t inputs = forPrefix(p);
|
||||
|
||||
const AstNodeIndex then_expr = expectExpr(p);
|
||||
|
||||
if (eatToken(p, TOKEN_KEYWORD_ELSE) != null_token) {
|
||||
parsePayload(p);
|
||||
SLICE_APPEND(AstNodeIndex, &p->scratch, then_expr);
|
||||
const AstNodeIndex else_expr = expectExpr(p);
|
||||
SLICE_APPEND(AstNodeIndex, &p->scratch, else_expr);
|
||||
const uint32_t total = p->scratch.len - scratch_top;
|
||||
const AstSubRange span
|
||||
= listToSpan(p, &p->scratch.arr[scratch_top], total);
|
||||
p->scratch.len = scratch_top;
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_FOR,
|
||||
.main_token = for_token,
|
||||
.data = {
|
||||
.lhs = span.start,
|
||||
.rhs = ((uint32_t)inputs & 0x7FFFFFFF) | (1u << 31),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (inputs == 1) {
|
||||
const AstNodeIndex input = p->scratch.arr[scratch_top];
|
||||
p->scratch.len = scratch_top;
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_FOR_SIMPLE,
|
||||
.main_token = for_token,
|
||||
.data = { .lhs = input, .rhs = then_expr },
|
||||
});
|
||||
}
|
||||
|
||||
SLICE_APPEND(AstNodeIndex, &p->scratch, then_expr);
|
||||
const uint32_t total = p->scratch.len - scratch_top;
|
||||
const AstSubRange span
|
||||
= listToSpan(p, &p->scratch.arr[scratch_top], total);
|
||||
p->scratch.len = scratch_top;
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_FOR,
|
||||
.main_token = for_token,
|
||||
.data = {
|
||||
.lhs = span.start,
|
||||
.rhs = (uint32_t)inputs & 0x7FFFFFFF,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
static AstNodeIndex parseForStatement(Parser* p) { return parseForExpr(p); }
|
||||
|
||||
static AstNodeIndex parseWhileExpr(Parser* p) {
|
||||
const AstTokenIndex while_token = eatToken(p, TOKEN_KEYWORD_WHILE);
|
||||
if (while_token == null_token)
|
||||
@@ -2050,8 +2147,9 @@ static AstNodeIndex parsePrimaryExpr(Parser* p) {
|
||||
}
|
||||
case TOKEN_KEYWORD_WHILE:
|
||||
return parseWhileExpr(p);
|
||||
case TOKEN_KEYWORD_INLINE:
|
||||
case TOKEN_KEYWORD_FOR:
|
||||
return parseForExpr(p);
|
||||
case TOKEN_KEYWORD_INLINE:
|
||||
fprintf(stderr, "parsePrimaryExpr does not implement %s\n", tok);
|
||||
exit(1);
|
||||
return 0; // tcc
|
||||
|
||||
@@ -624,6 +624,17 @@ test "zig fmt: respect line breaks before functions" {
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: top-level for/while loop" {
|
||||
try testCanonical(
|
||||
\\for (foo) |_| foo
|
||||
\\
|
||||
);
|
||||
try testCanonical(
|
||||
\\while (foo) |_| foo
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: simple top level comptime block" {
|
||||
try testCanonical(
|
||||
\\// line comment
|
||||
|
||||
Reference in New Issue
Block a user