diff --git a/parser.c b/parser.c index e4938c98ff..0879cead97 100644 --- a/parser.c +++ b/parser.c @@ -1515,14 +1515,65 @@ static AstNodeIndex expectExpr(Parser* p) { return node; } +static void parsePtrPayload(Parser* p) { + if (eatToken(p, TOKEN_PIPE) == null_token) + return; + eatToken(p, TOKEN_ASTERISK); + expectToken(p, TOKEN_IDENTIFIER); + expectToken(p, TOKEN_PIPE); +} + +static void parsePayload(Parser* p) { + if (eatToken(p, TOKEN_PIPE) == null_token) + return; + expectToken(p, TOKEN_IDENTIFIER); + expectToken(p, TOKEN_PIPE); +} + +static AstNodeIndex parseIfExpr(Parser* p) { + const AstTokenIndex if_token = eatToken(p, TOKEN_KEYWORD_IF); + if (if_token == null_token) + return null_node; + + expectToken(p, TOKEN_L_PAREN); + const AstNodeIndex condition = expectExpr(p); + expectToken(p, TOKEN_R_PAREN); + parsePtrPayload(p); + + const AstNodeIndex then_expr = expectExpr(p); + + if (eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) { + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_IF_SIMPLE, + .main_token = if_token, + .data = { .lhs = condition, .rhs = then_expr }, + }); + } + + parsePayload(p); + const AstNodeIndex else_expr = expectExpr(p); + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_IF, + .main_token = if_token, + .data = { + .lhs = condition, + .rhs = addExtra(p, + (AstNodeIndex[]) { then_expr, else_expr }, 2), + }, + }); +} + static AstNodeIndex parsePrimaryExpr(Parser* p) { const char* tok = tokenizerGetTagString(p->token_tags[p->tok_i]); switch (p->token_tags[p->tok_i]) { case TOKEN_KEYWORD_ASM: - case TOKEN_KEYWORD_IF: fprintf(stderr, "parsePrimaryExpr does not implement %s\n", tok); exit(1); break; + case TOKEN_KEYWORD_IF: + return parseIfExpr(p); case TOKEN_KEYWORD_BREAK: return addNode( &p->nodes, @@ -1670,6 +1721,9 @@ static AstNodeIndex expectVarDeclExprStatement(Parser* p) { case TOKEN_SEMICOLON: p->tok_i++; return lhs; + case TOKEN_R_BRACE: + // Expression that doesn't need semicolon (block-terminated) + return lhs; case TOKEN_EQUAL: { // Check if lhs is a var decl that needs initialization const AstNodeTag lhs_tag = p->nodes.tags[lhs]; @@ -1736,7 +1790,6 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { case TOKEN_KEYWORD_SUSPEND: case TOKEN_KEYWORD_DEFER: case TOKEN_KEYWORD_ERRDEFER: - case TOKEN_KEYWORD_IF: case TOKEN_KEYWORD_ENUM: case TOKEN_KEYWORD_STRUCT: case TOKEN_KEYWORD_UNION:; diff --git a/parser_test.zig b/parser_test.zig index 2d951e9b00..df9d00fff0 100644 --- a/parser_test.zig +++ b/parser_test.zig @@ -412,13 +412,15 @@ fn zigData(tag: Ast.Node.Tag, lhs: u32, rhs: u32) Ast.Node.Data { => .{ .node_and_node = .{ toIndex(lhs), toIndex(rhs) } }, .while_simple, - .while_cont, - .@"while", .for_simple, .if_simple, - .@"if", => .{ .node_and_node = .{ toIndex(lhs), toIndex(rhs) } }, + .while_cont, + .@"while", + .@"if", + => .{ .node_and_extra = .{ toIndex(lhs), toExtraIndex(rhs) } }, + .for_range, => .{ .node_and_opt_node = .{ toIndex(lhs), toOptIndex(rhs) } }, @@ -1594,3 +1596,75 @@ test "zig fmt: function call with multiline argument" { \\ ); } + +test "zig fmt: if statement" { + try testCanonical( + \\test "" { + \\ if (optional()) |some| + \\ bar = some.foo(); + \\} + \\ + ); +} + +test "zig fmt: respect line breaks in if-else" { + try testCanonical( + \\comptime { + \\ return if (cond) a else b; + \\ return if (cond) + \\ a + \\ else + \\ b; + \\ return if (cond) + \\ a + \\ else if (cond) + \\ b + \\ else + \\ c; + \\} + \\ + ); +} + +test "zig fmt: if nested" { + try testCanonical( + \\pub fn foo() void { + \\ return if ((aInt & bInt) >= 0) + \\ if (aInt < bInt) + \\ GE_LESS + \\ else if (aInt == bInt) + \\ GE_EQUAL + \\ else + \\ GE_GREATER + \\ // comment + \\ else if (aInt > bInt) + \\ GE_LESS + \\ else if (aInt == bInt) + \\ GE_EQUAL + \\ else + \\ GE_GREATER; + \\ // comment + \\} + \\ + ); +} + +test "zig fmt: remove empty lines at start/end of block" { + try testTransform( + \\test { + \\ + \\ if (foo) { + \\ foo(); + \\ } + \\ + \\} + \\ + , + \\test { + \\ if (foo) { + \\ foo(); + \\ } + \\} + \\ + ); +}