parser: add fn params, return, call args, enum literal tests
Port tests from upstream parser_test.zig: - "trailing comma in fn parameter list" (all combinations) - "enum literal inside array literal" - "builtin call with trailing comma" Implement in parser.c: - parseParamDeclList: full parameter parsing with names, comptime, noalias, doc comments, varargs - parseFnProto: fn_proto_multi, fn_proto_one, fn_proto with align/addrspace/section/callconv - parseSuffixExpr: function call with arguments - parsePrimaryExpr: return, comptime, nosuspend, resume expressions - parseAddrSpace, parseLinkSection, parseCallconv: full parsing - Use OPT() macro for OptionalIndex encoding in extra data Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
180
parser.c
180
parser.c
@@ -153,25 +153,28 @@ static AstNodeIndex parseByteAlign(Parser* p) {
|
||||
static AstNodeIndex parseAddrSpace(Parser* p) {
|
||||
if (eatToken(p, TOKEN_KEYWORD_ADDRSPACE) == null_token)
|
||||
return null_node;
|
||||
fprintf(stderr, "parseAddrSpace cannot parse addrspace\n");
|
||||
exit(1);
|
||||
return 0; // tcc
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
const AstNodeIndex expr = expectExpr(p);
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
return expr;
|
||||
}
|
||||
|
||||
static AstNodeIndex parseLinkSection(Parser* p) {
|
||||
if (eatToken(p, TOKEN_KEYWORD_LINKSECTION) == null_token)
|
||||
return null_node;
|
||||
fprintf(stderr, "parseLinkSection cannot parse linksection\n");
|
||||
exit(1);
|
||||
return 0; // tcc
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
const AstNodeIndex expr = expectExpr(p);
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
return expr;
|
||||
}
|
||||
|
||||
static AstNodeIndex parseCallconv(Parser* p) {
|
||||
if (eatToken(p, TOKEN_KEYWORD_CALLCONV) == null_token)
|
||||
return null_node;
|
||||
fprintf(stderr, "parseCallconv cannot parse callconv\n");
|
||||
exit(1);
|
||||
return 0; // tcc
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
const AstNodeIndex expr = expectExpr(p);
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
return expr;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -697,8 +700,14 @@ static AstNodeIndex parseSuffixExpr(Parser* p) {
|
||||
while (true) {
|
||||
if (eatToken(p, TOKEN_R_PAREN) != null_token)
|
||||
break;
|
||||
fprintf(stderr, "parseSuffixExpr can only parse ()\n");
|
||||
exit(1);
|
||||
const AstNodeIndex arg = expectExpr(p);
|
||||
SLICE_APPEND(AstNodeIndex, &p->scratch, arg);
|
||||
if (p->token_tags[p->tok_i] == TOKEN_COMMA) {
|
||||
p->tok_i++;
|
||||
continue;
|
||||
}
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
break;
|
||||
}
|
||||
|
||||
const bool comma = p->token_tags[p->tok_i - 2] == TOKEN_COMMA;
|
||||
@@ -901,13 +910,66 @@ static AstNodeIndex parseTypeExpr(Parser* p) {
|
||||
}
|
||||
|
||||
static SmallSpan parseParamDeclList(Parser* p) {
|
||||
// can only parse functions with no declarations
|
||||
expectToken(p, TOKEN_L_PAREN);
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
|
||||
return (SmallSpan) {
|
||||
.tag = SMALL_SPAN_ZERO_OR_ONE,
|
||||
};
|
||||
CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch)))
|
||||
= initCleanupScratch(p);
|
||||
|
||||
while (true) {
|
||||
if (eatToken(p, TOKEN_R_PAREN) != null_token)
|
||||
break;
|
||||
|
||||
eatDocComments(p);
|
||||
|
||||
// Check for comptime or noalias
|
||||
eatToken(p, TOKEN_KEYWORD_COMPTIME);
|
||||
eatToken(p, TOKEN_KEYWORD_NOALIAS);
|
||||
|
||||
// Check for name: type or just type
|
||||
if (p->token_tags[p->tok_i] == TOKEN_IDENTIFIER
|
||||
&& p->token_tags[p->tok_i + 1] == TOKEN_COLON) {
|
||||
p->tok_i += 2; // consume name and colon
|
||||
} else if (p->token_tags[p->tok_i] == TOKEN_ELLIPSIS3) {
|
||||
// anytype (...) varargs
|
||||
p->tok_i++;
|
||||
if (eatToken(p, TOKEN_R_PAREN) != null_token)
|
||||
break;
|
||||
expectToken(p, TOKEN_COMMA);
|
||||
continue;
|
||||
}
|
||||
|
||||
const AstNodeIndex type_expr = parseTypeExpr(p);
|
||||
if (type_expr != 0)
|
||||
SLICE_APPEND(AstNodeIndex, &p->scratch, type_expr);
|
||||
|
||||
if (p->token_tags[p->tok_i] == TOKEN_COMMA) {
|
||||
p->tok_i++;
|
||||
continue;
|
||||
}
|
||||
expectToken(p, TOKEN_R_PAREN);
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t params_len = p->scratch.len - scratch_top.old_len;
|
||||
switch (params_len) {
|
||||
case 0:
|
||||
return (SmallSpan) {
|
||||
.tag = SMALL_SPAN_ZERO_OR_ONE,
|
||||
.payload = { .zero_or_one = 0 },
|
||||
};
|
||||
case 1:
|
||||
return (SmallSpan) {
|
||||
.tag = SMALL_SPAN_ZERO_OR_ONE,
|
||||
.payload = { .zero_or_one = p->scratch.arr[scratch_top.old_len] },
|
||||
};
|
||||
default:;
|
||||
const AstSubRange span
|
||||
= listToSpan(p, &p->scratch.arr[scratch_top.old_len], params_len);
|
||||
return (SmallSpan) {
|
||||
.tag = SMALL_SPAN_MULTI,
|
||||
.payload = { .multi = span },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t reserveNode(Parser* p, AstNodeTag tag) {
|
||||
@@ -939,9 +1001,7 @@ static AstNodeIndex parseFnProto(Parser* p) {
|
||||
&& addrspace_expr == 0) {
|
||||
switch (params.tag) {
|
||||
case SMALL_SPAN_ZERO_OR_ONE:
|
||||
return setNode(
|
||||
p,
|
||||
fn_proto_index,
|
||||
return setNode(p, fn_proto_index,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_FN_PROTO_SIMPLE,
|
||||
.main_token = fn_token,
|
||||
@@ -950,15 +1010,60 @@ static AstNodeIndex parseFnProto(Parser* p) {
|
||||
.rhs = return_type_expr,
|
||||
},
|
||||
});
|
||||
break;
|
||||
case SMALL_SPAN_MULTI:
|
||||
fprintf(stderr, "parseFnProto does not support multi params\n");
|
||||
exit(1);
|
||||
return setNode(p, fn_proto_index,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_FN_PROTO_MULTI,
|
||||
.main_token = fn_token,
|
||||
.data = {
|
||||
.lhs = addExtra(p,
|
||||
(AstNodeIndex[]) {
|
||||
params.payload.multi.start,
|
||||
params.payload.multi.end },
|
||||
2),
|
||||
.rhs = return_type_expr,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "parseFnProto does not support complex function decls\n");
|
||||
exit(1);
|
||||
// Complex fn proto with align/section/callconv/addrspace
|
||||
// Extra data fields are OptionalIndex: 0 → ~0 (none)
|
||||
#define OPT(x) ((x) == 0 ? ~(AstNodeIndex)0 : (x))
|
||||
switch (params.tag) {
|
||||
case SMALL_SPAN_ZERO_OR_ONE:
|
||||
return setNode(p, fn_proto_index,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_FN_PROTO_ONE,
|
||||
.main_token = fn_token,
|
||||
.data = {
|
||||
.lhs = addExtra(p,
|
||||
(AstNodeIndex[]) {
|
||||
OPT(params.payload.zero_or_one),
|
||||
OPT(align_expr), OPT(addrspace_expr),
|
||||
OPT(section_expr), OPT(callconv_expr) },
|
||||
5),
|
||||
.rhs = return_type_expr,
|
||||
},
|
||||
});
|
||||
case SMALL_SPAN_MULTI:
|
||||
return setNode(p, fn_proto_index,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_FN_PROTO,
|
||||
.main_token = fn_token,
|
||||
.data = {
|
||||
.lhs = addExtra(p,
|
||||
(AstNodeIndex[]) {
|
||||
params.payload.multi.start,
|
||||
params.payload.multi.end,
|
||||
OPT(align_expr), OPT(addrspace_expr),
|
||||
OPT(section_expr), OPT(callconv_expr) },
|
||||
6),
|
||||
.rhs = return_type_expr,
|
||||
},
|
||||
});
|
||||
}
|
||||
#undef OPT
|
||||
return 0; // tcc
|
||||
}
|
||||
|
||||
@@ -1447,12 +1552,33 @@ static AstNodeIndex parsePrimaryExpr(Parser* p) {
|
||||
},
|
||||
});
|
||||
case TOKEN_KEYWORD_COMPTIME:
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_COMPTIME,
|
||||
.main_token = nextToken(p),
|
||||
.data = { .lhs = expectExpr(p), .rhs = 0 },
|
||||
});
|
||||
case TOKEN_KEYWORD_NOSUSPEND:
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_NOSUSPEND,
|
||||
.main_token = nextToken(p),
|
||||
.data = { .lhs = expectExpr(p), .rhs = 0 },
|
||||
});
|
||||
case TOKEN_KEYWORD_RESUME:
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_RESUME,
|
||||
.main_token = nextToken(p),
|
||||
.data = { .lhs = expectExpr(p), .rhs = 0 },
|
||||
});
|
||||
case TOKEN_KEYWORD_RETURN:
|
||||
fprintf(stderr, "parsePrimaryExpr does not implement %s\n", tok);
|
||||
exit(1);
|
||||
return 0; // tcc
|
||||
return addNode(&p->nodes,
|
||||
(AstNodeItem) {
|
||||
.tag = AST_NODE_RETURN,
|
||||
.main_token = nextToken(p),
|
||||
.data = { .lhs = parseExpr(p), .rhs = 0 },
|
||||
});
|
||||
case TOKEN_IDENTIFIER:
|
||||
if (p->token_tags[p->tok_i + 1] == TOKEN_COLON) {
|
||||
switch (p->token_tags[p->tok_i + 2]) {
|
||||
|
||||
@@ -1360,3 +1360,75 @@ test "zig fmt: comment to disable/enable zig fmt first" {
|
||||
\\const struct_trailing_comma = struct { x: i32, y: i32, };
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: trailing comma in fn parameter list" {
|
||||
try testCanonical(
|
||||
\\pub fn f(
|
||||
\\ a: i32,
|
||||
\\ b: i32,
|
||||
\\) i32 {}
|
||||
\\pub fn f(
|
||||
\\ a: i32,
|
||||
\\ b: i32,
|
||||
\\) align(8) i32 {}
|
||||
\\pub fn f(
|
||||
\\ a: i32,
|
||||
\\ b: i32,
|
||||
\\) addrspace(.generic) i32 {}
|
||||
\\pub fn f(
|
||||
\\ a: i32,
|
||||
\\ b: i32,
|
||||
\\) linksection(".text") i32 {}
|
||||
\\pub fn f(
|
||||
\\ a: i32,
|
||||
\\ b: i32,
|
||||
\\) callconv(.c) i32 {}
|
||||
\\pub fn f(
|
||||
\\ a: i32,
|
||||
\\ b: i32,
|
||||
\\) align(8) linksection(".text") i32 {}
|
||||
\\pub fn f(
|
||||
\\ a: i32,
|
||||
\\ b: i32,
|
||||
\\) align(8) callconv(.c) i32 {}
|
||||
\\pub fn f(
|
||||
\\ a: i32,
|
||||
\\ b: i32,
|
||||
\\) align(8) linksection(".text") callconv(.c) i32 {}
|
||||
\\pub fn f(
|
||||
\\ a: i32,
|
||||
\\ b: i32,
|
||||
\\) linksection(".text") callconv(.c) i32 {}
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: enum literal inside array literal" {
|
||||
try testCanonical(
|
||||
\\test "enums in arrays" {
|
||||
\\ var colors = []Color{.Green};
|
||||
\\ colors = []Colors{ .Green, .Cyan };
|
||||
\\ colors = []Colors{
|
||||
\\ .Grey,
|
||||
\\ .Green,
|
||||
\\ .Cyan,
|
||||
\\ };
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: builtin call with trailing comma" {
|
||||
try testCanonical(
|
||||
\\pub fn main() void {
|
||||
\\ @breakpoint();
|
||||
\\ _ = @intFromBool(a);
|
||||
\\ _ = @call(
|
||||
\\ a,
|
||||
\\ b,
|
||||
\\ c,
|
||||
\\ );
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user