diff --git a/src/parser.cpp b/src/parser.cpp index 529d5e73a7..312e5c3418 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -302,13 +302,13 @@ static void ast_parse_param_decl_list(ParseContext *pc, size_t *token_index, ast_eat_token(pc, token_index, TokenIdLParen); - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRParen) { - *token_index += 1; - return; - } - for (;;) { + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdRParen) { + *token_index += 1; + return; + } + AstNode *param_decl_node = ast_parse_param_decl(pc, token_index); bool expect_end = false; assert(param_decl_node); @@ -316,31 +316,33 @@ static void ast_parse_param_decl_list(ParseContext *pc, size_t *token_index, expect_end = param_decl_node->data.param_decl.is_var_args; *is_var_args = expect_end; - Token *token = &pc->tokens->at(*token_index); + token = &pc->tokens->at(*token_index); *token_index += 1; if (token->id == TokenIdRParen) { return; - } else if (expect_end) { - ast_invalid_token_error(pc, token); } else { ast_expect_token(pc, token, TokenIdComma); + if (expect_end) { + ast_eat_token(pc, token_index, TokenIdRParen); + return; + } } } zig_unreachable(); } static void ast_parse_fn_call_param_list(ParseContext *pc, size_t *token_index, ZigList *params) { - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRParen) { - *token_index += 1; - return; - } - for (;;) { + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdRParen) { + *token_index += 1; + return; + } + AstNode *expr = ast_parse_expression(pc, token_index, true); params->append(expr); - Token *token = &pc->tokens->at(*token_index); + token = &pc->tokens->at(*token_index); *token_index += 1; if (token->id == TokenIdRParen) { return; @@ -482,7 +484,13 @@ static void ast_parse_asm_clobbers(ParseContext *pc, size_t *token_index, AstNod if (comma->id == TokenIdComma) { *token_index += 1; - continue; + + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdRParen) { + break; + } else { + continue; + } } else { break; } @@ -513,7 +521,13 @@ static void ast_parse_asm_input(ParseContext *pc, size_t *token_index, AstNode * if (comma->id == TokenIdComma) { *token_index += 1; - continue; + + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdColon || token->id == TokenIdRParen) { + break; + } else { + continue; + } } else { break; } @@ -546,7 +560,13 @@ static void ast_parse_asm_output(ParseContext *pc, size_t *token_index, AstNode if (comma->id == TokenIdComma) { *token_index += 1; - continue; + + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdColon || token->id == TokenIdRParen) { + break; + } else { + continue; + } } else { break; } @@ -1783,7 +1803,13 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo Token *comma_tok = &pc->tokens->at(*token_index); if (comma_tok->id == TokenIdComma) { *token_index += 1; - continue; + + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdFatArrow) { + break; + } else { + continue; + } } break; } @@ -1812,7 +1838,15 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo } prong_node->data.switch_prong.expr = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdComma); + + Token *trailing_token = &pc->tokens->at(*token_index); + if (trailing_token->id == TokenIdRBrace) { + *token_index += 1; + + return node; + } else { + ast_eat_token(pc, token_index, TokenIdComma); + } } } @@ -2364,17 +2398,28 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, field_node->data.struct_field.visib_mod = visib_mod; field_node->data.struct_field.name = token_buf(token); - Token *expr_or_comma = &pc->tokens->at(*token_index); - if (expr_or_comma->id == TokenIdComma) { - field_node->data.struct_field.type = ast_create_void_type_node(pc, expr_or_comma); + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdComma || token->id == TokenIdRBrace) { + field_node->data.struct_field.type = ast_create_void_type_node(pc, token); *token_index += 1; + node->data.container_decl.fields.append(field_node); + + if (token->id == TokenIdRBrace) { + break; + } } else { ast_eat_token(pc, token_index, TokenIdColon); field_node->data.struct_field.type = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdComma); - } + node->data.container_decl.fields.append(field_node); - node->data.container_decl.fields.append(field_node); + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdRBrace) { + *token_index += 1; + break; + } else { + ast_eat_token(pc, token_index, TokenIdComma); + } + } } else { ast_invalid_token_error(pc, token); } diff --git a/test/behavior.zig b/test/behavior.zig index 09236b8096..e3d48e4833 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -34,6 +34,7 @@ comptime { _ = @import("cases/switch.zig"); _ = @import("cases/switch_prong_err_enum.zig"); _ = @import("cases/switch_prong_implicit_cast.zig"); + _ = @import("cases/syntax.zig"); _ = @import("cases/this.zig"); _ = @import("cases/try.zig"); _ = @import("cases/undefined.zig"); diff --git a/test/cases/syntax.zig b/test/cases/syntax.zig new file mode 100644 index 0000000000..3ebca96ef9 --- /dev/null +++ b/test/cases/syntax.zig @@ -0,0 +1,60 @@ +// Test trailing comma syntax + +const struct_trailing_comma = struct { x: i32, y: i32, }; +const struct_no_comma = struct { x: i32, y: i32 }; +const struct_no_comma_void_type = struct { x: i32, y }; +const struct_fn_no_comma = struct { fn m() {} y: i32 }; + +const enum_no_comma = enum { A, B }; +const enum_no_comma_type = enum { A, B: i32 }; + +fn container_init() { + const S = struct { x: i32, y: i32 }; + _ = S { .x = 1, .y = 2 }; + _ = S { .x = 1, .y = 2, }; +} + +fn switch_cases(x: i32) { + switch (x) { + 1,2,3 => {}, + 4,5, => {}, + 6...8, => {}, + else => {}, + } +} + +fn switch_prongs(x: i32) { + switch (x) { + 0 => {}, + else => {}, + } + switch (x) { + 0 => {}, + else => {} + } +} + +const fn_no_comma = fn(i32, i32); +const fn_trailing_comma = fn(i32, i32,); +const fn_vararg_trailing_comma = fn(i32, i32, ...,); + +fn fn_calls() { + fn add(x: i32, y: i32,) -> i32 { x + y }; + _ = add(1, 2); + _ = add(1, 2,); + + fn swallow(x: ...,) {}; + _ = swallow(1,2,3,); + _ = swallow(); +} + +fn asm_lists() { + if (false) { // Build AST but don't analyze + asm ("not real assembly" + :[a] "x" (x),); + asm ("not real assembly" + :[a] "x" (->i32),:[a] "x" (1),); + asm ("still not real assembly" + :::"a","b",); + } +}