parser: add function, comptime, var declaration tests
Port tests from upstream parser_test.zig: - "respect line breaks before functions" - "simple top level comptime block" - "two spaced line comments before decl" - "respect line breaks after var declarations" Implement in parser.c: - parseSuffixOp: array access (a[i]), field access (a.b), deref (a.*), unwrap optional (a.?) - Multiline string literal parsing - Slice types ([]T, [:s]T) and array types ([N]T, [N:s]T) - Fix comptime block main_token in parseContainerMembers Fix zigData mapping in parser_test.zig: - field_access, unwrap_optional use node_and_token (not node_and_node) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
61
parser.c
61
parser.c
@@ -533,16 +533,67 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static AstNodeIndex parseSuffixOp(Parser* p, AstNodeIndex lhs) {
|
static AstNodeIndex parseSuffixOp(Parser* p, AstNodeIndex lhs) {
|
||||||
(void)lhs;
|
|
||||||
const TokenizerTag tok = p->token_tags[p->tok_i];
|
const TokenizerTag tok = p->token_tags[p->tok_i];
|
||||||
switch (tok) {
|
switch (tok) {
|
||||||
case TOKEN_L_BRACKET:
|
case TOKEN_L_BRACKET: {
|
||||||
|
const AstTokenIndex lbracket = nextToken(p);
|
||||||
|
const AstNodeIndex index_expr = expectExpr(p);
|
||||||
|
switch (p->token_tags[p->tok_i]) {
|
||||||
|
case TOKEN_R_BRACKET:
|
||||||
|
p->tok_i++;
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = AST_NODE_ARRAY_ACCESS,
|
||||||
|
.main_token = lbracket,
|
||||||
|
.data = { .lhs = lhs, .rhs = index_expr },
|
||||||
|
});
|
||||||
|
case TOKEN_ELLIPSIS2:
|
||||||
|
fprintf(stderr, "parseSuffixOp: slicing not implemented\n");
|
||||||
|
exit(1);
|
||||||
|
default:
|
||||||
|
fprintf(
|
||||||
|
stderr, "parseSuffixOp: expected ] or .. after index expr\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return 0; // tcc
|
||||||
|
}
|
||||||
case TOKEN_PERIOD_ASTERISK:
|
case TOKEN_PERIOD_ASTERISK:
|
||||||
case TOKEN_INVALID_PERIODASTERISKS:
|
case TOKEN_INVALID_PERIODASTERISKS:
|
||||||
case TOKEN_PERIOD:
|
|
||||||
fprintf(stderr, "parseSuffixOp does not support %s\n",
|
fprintf(stderr, "parseSuffixOp does not support %s\n",
|
||||||
tokenizerGetTagString(tok));
|
tokenizerGetTagString(tok));
|
||||||
exit(1);
|
exit(1);
|
||||||
|
case TOKEN_PERIOD:
|
||||||
|
if (p->token_tags[p->tok_i + 1] == TOKEN_IDENTIFIER) {
|
||||||
|
const AstTokenIndex dot = nextToken(p);
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = AST_NODE_FIELD_ACCESS,
|
||||||
|
.main_token = dot,
|
||||||
|
.data = { .lhs = lhs, .rhs = nextToken(p) },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (p->token_tags[p->tok_i + 1] == TOKEN_ASTERISK) {
|
||||||
|
const AstTokenIndex dot = nextToken(p);
|
||||||
|
nextToken(p); // consume the *
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = AST_NODE_DEREF,
|
||||||
|
.main_token = dot,
|
||||||
|
.data = { .lhs = lhs, .rhs = 0 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (p->token_tags[p->tok_i + 1] == TOKEN_QUESTION_MARK) {
|
||||||
|
const AstTokenIndex dot = nextToken(p);
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = AST_NODE_UNWRAP_OPTIONAL,
|
||||||
|
.main_token = dot,
|
||||||
|
.data = { .lhs = lhs, .rhs = nextToken(p) },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fprintf(stderr, "parseSuffixOp: unsupported period suffix\n");
|
||||||
|
exit(1);
|
||||||
|
return 0; // tcc
|
||||||
default:
|
default:
|
||||||
return null_node;
|
return null_node;
|
||||||
}
|
}
|
||||||
@@ -1521,13 +1572,13 @@ static Members parseContainerMembers(Parser* p) {
|
|||||||
// block/decl. Check if it's followed by a block (comptime { ...
|
// block/decl. Check if it's followed by a block (comptime { ...
|
||||||
// }).
|
// }).
|
||||||
if (p->token_tags[p->tok_i + 1] == TOKEN_L_BRACE) {
|
if (p->token_tags[p->tok_i + 1] == TOKEN_L_BRACE) {
|
||||||
p->tok_i++;
|
const AstTokenIndex comptime_token = nextToken(p);
|
||||||
const AstNodeIndex block_node = parseBlock(p);
|
const AstNodeIndex block_node = parseBlock(p);
|
||||||
SLICE_APPEND(AstNodeIndex, &p->scratch,
|
SLICE_APPEND(AstNodeIndex, &p->scratch,
|
||||||
addNode(&p->nodes,
|
addNode(&p->nodes,
|
||||||
(AstNodeItem) {
|
(AstNodeItem) {
|
||||||
.tag = AST_NODE_COMPTIME,
|
.tag = AST_NODE_COMPTIME,
|
||||||
.main_token = p->tok_i - 1,
|
.main_token = comptime_token,
|
||||||
.data = { .lhs = block_node, .rhs = 0 },
|
.data = { .lhs = block_node, .rhs = 0 },
|
||||||
}));
|
}));
|
||||||
trailing = p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE;
|
trailing = p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE;
|
||||||
|
|||||||
@@ -234,8 +234,6 @@ fn zigData(tag: Ast.Node.Tag, lhs: u32, rhs: u32) Ast.Node.Data {
|
|||||||
.container_field_align,
|
.container_field_align,
|
||||||
.error_union,
|
.error_union,
|
||||||
.@"catch",
|
.@"catch",
|
||||||
.field_access,
|
|
||||||
.unwrap_optional,
|
|
||||||
.equal_equal,
|
.equal_equal,
|
||||||
.bang_equal,
|
.bang_equal,
|
||||||
.less_than,
|
.less_than,
|
||||||
@@ -378,6 +376,8 @@ fn zigData(tag: Ast.Node.Tag, lhs: u32, rhs: u32) Ast.Node.Data {
|
|||||||
// .node_and_token
|
// .node_and_token
|
||||||
.grouped_expression,
|
.grouped_expression,
|
||||||
.asm_input,
|
.asm_input,
|
||||||
|
.field_access,
|
||||||
|
.unwrap_optional,
|
||||||
=> .{ .node_and_token = .{ toIndex(lhs), rhs } },
|
=> .{ .node_and_token = .{ toIndex(lhs), rhs } },
|
||||||
|
|
||||||
// .opt_node_and_token
|
// .opt_node_and_token
|
||||||
@@ -599,3 +599,53 @@ test "zig fmt: respect line breaks in struct field value declaration" {
|
|||||||
\\
|
\\
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "zig fmt: respect line breaks before functions" {
|
||||||
|
try testCanonical(
|
||||||
|
\\const std = @import("std");
|
||||||
|
\\
|
||||||
|
\\inline fn foo() void {}
|
||||||
|
\\
|
||||||
|
\\noinline fn foo() void {}
|
||||||
|
\\
|
||||||
|
\\export fn foo() void {}
|
||||||
|
\\
|
||||||
|
\\extern fn foo() void;
|
||||||
|
\\
|
||||||
|
\\extern "foo" fn foo() void;
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zig fmt: simple top level comptime block" {
|
||||||
|
try testCanonical(
|
||||||
|
\\// line comment
|
||||||
|
\\comptime {}
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zig fmt: two spaced line comments before decl" {
|
||||||
|
try testCanonical(
|
||||||
|
\\// line comment
|
||||||
|
\\
|
||||||
|
\\// another
|
||||||
|
\\comptime {}
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zig fmt: respect line breaks after var declarations" {
|
||||||
|
try testCanonical(
|
||||||
|
\\const crc =
|
||||||
|
\\ lookup_tables[0][p[7]] ^
|
||||||
|
\\ lookup_tables[1][p[6]] ^
|
||||||
|
\\ lookup_tables[2][p[5]] ^
|
||||||
|
\\ lookup_tables[3][p[4]] ^
|
||||||
|
\\ lookup_tables[4][@as(u8, self.crc >> 24)] ^
|
||||||
|
\\ lookup_tables[5][@as(u8, self.crc >> 16)] ^
|
||||||
|
\\ lookup_tables[6][@as(u8, self.crc >> 8)] ^
|
||||||
|
\\ lookup_tables[7][@as(u8, self.crc >> 0)];
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user