diff --git a/parser.c b/parser.c index c93e55cb77..c29c79f1db 100644 --- a/parser.c +++ b/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]) { diff --git a/parser_test.zig b/parser_test.zig index e69896e5db..87c3b5ac20 100644 --- a/parser_test.zig +++ b/parser_test.zig @@ -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, + \\ ); + \\} + \\ + ); +}