Commit 39acc4c020

Andrew Kelley <andrew@ziglang.org>
2021-02-10 04:08:40
zig fmt: for loops
1 parent 1c79eea
Changed files (4)
lib/std/zig/ast.zig
@@ -512,6 +512,7 @@ pub const Tree = struct {
             .ErrorUnion,
             .IfSimple,
             .WhileSimple,
+            .ForSimple,
             .FnDecl,
             .PtrTypeAligned,
             .PtrTypeSentinel,
@@ -531,6 +532,7 @@ pub const Tree = struct {
             .AsmInput,
             .FnProtoSimple,
             .FnProtoMulti,
+            .ErrorValue,
             => return datas[n].rhs + end_offset,
 
             .AnyType,
@@ -854,7 +856,7 @@ pub const Tree = struct {
                 assert(extra.else_expr != 0);
                 n = extra.else_expr;
             },
-            .If => {
+            .If, .For => {
                 const extra = tree.extraData(datas[n].rhs, Node.If);
                 assert(extra.else_expr != 0);
                 n = extra.else_expr;
@@ -876,9 +878,6 @@ pub const Tree = struct {
             .SwitchRange => unreachable, // TODO
             .ArrayType => unreachable, // TODO
             .ArrayTypeSentinel => unreachable, // TODO
-            .ForSimple => unreachable, // TODO
-            .For => unreachable, // TODO
-            .ErrorValue => unreachable, // TODO
         };
     }
 
@@ -1457,6 +1456,29 @@ pub const Tree = struct {
         });
     }
 
+    pub fn forSimple(tree: Tree, node: Node.Index) full.While {
+        const data = tree.nodes.items(.data)[node];
+        return tree.fullWhile(.{
+            .while_token = tree.nodes.items(.main_token)[node],
+            .cond_expr = data.lhs,
+            .cont_expr = 0,
+            .then_expr = data.rhs,
+            .else_expr = 0,
+        });
+    }
+
+    pub fn forFull(tree: Tree, node: Node.Index) full.While {
+        const data = tree.nodes.items(.data)[node];
+        const extra = tree.extraData(data.rhs, Node.If);
+        return tree.fullWhile(.{
+            .while_token = tree.nodes.items(.main_token)[node],
+            .cond_expr = data.lhs,
+            .cont_expr = 0,
+            .then_expr = extra.then_expr,
+            .else_expr = extra.else_expr,
+        });
+    }
+
     fn fullVarDecl(tree: Tree, info: full.VarDecl.Ast) full.VarDecl {
         const token_tags = tree.tokens.items(.tag);
         var result: full.VarDecl = .{
@@ -2356,6 +2378,7 @@ pub const Node = struct {
         /// main_token is the `=>`
         SwitchCaseOne,
         /// `a, b, c => rhs`. `SubRange[lhs]`.
+        /// main_token is the `=>`
         SwitchCase,
         /// `lhs...rhs`.
         SwitchRange,
lib/std/zig/parse.zig
@@ -835,12 +835,11 @@ const Parser = struct {
             });
         }
 
-        const token = p.nextToken();
-        switch (p.token_tags[token]) {
+        switch (p.token_tags[p.tok_i]) {
             .Keyword_nosuspend => {
                 return p.addNode(.{
                     .tag = .Nosuspend,
-                    .main_token = token,
+                    .main_token = p.nextToken(),
                     .data = .{
                         .lhs = try p.expectBlockExprStatement(),
                         .rhs = undefined,
@@ -848,6 +847,7 @@ const Parser = struct {
                 });
             },
             .Keyword_suspend => {
+                const token = p.nextToken();
                 const block_expr: Node.Index = if (p.eatToken(.Semicolon) != null)
                     0
                 else
@@ -863,7 +863,7 @@ const Parser = struct {
             },
             .Keyword_defer => return p.addNode(.{
                 .tag = .Defer,
-                .main_token = token,
+                .main_token = p.nextToken(),
                 .data = .{
                     .lhs = undefined,
                     .rhs = try p.expectBlockExprStatement(),
@@ -871,24 +871,20 @@ const Parser = struct {
             }),
             .Keyword_errdefer => return p.addNode(.{
                 .tag = .ErrDefer,
-                .main_token = token,
+                .main_token = p.nextToken(),
                 .data = .{
                     .lhs = try p.parsePayload(),
                     .rhs = try p.expectBlockExprStatement(),
                 },
             }),
-            else => p.tok_i -= 1,
+            .Keyword_switch => return p.expectSwitchExpr(),
+            .Keyword_if => return p.expectIfStatement(),
+            else => {},
         }
 
-        const if_statement = try p.parseIfStatement();
-        if (if_statement != 0) return if_statement;
-
         const labeled_statement = try p.parseLabeledStatement();
         if (labeled_statement != 0) return labeled_statement;
 
-        const switch_expr = try p.parseSwitchExpr();
-        if (switch_expr != 0) return switch_expr;
-
         const assign_expr = try p.parseAssignExpr();
         if (assign_expr != 0) {
             _ = try p.expectTokenRecoverable(.Semicolon);
@@ -925,8 +921,8 @@ const Parser = struct {
     /// IfStatement
     ///     <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
     ///      / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
-    fn parseIfStatement(p: *Parser) !Node.Index {
-        const if_token = p.eatToken(.Keyword_if) orelse return null_node;
+    fn expectIfStatement(p: *Parser) !Node.Index {
+        const if_token = p.assertToken(.Keyword_if);
         _ = try p.expectToken(.LParen);
         const condition = try p.expectExpr();
         _ = try p.expectToken(.RParen);
@@ -2441,7 +2437,7 @@ const Parser = struct {
             .Builtin => return p.parseBuiltinCall(),
             .Keyword_fn => return p.parseFnProto(),
             .Keyword_if => return p.parseIf(parseTypeExpr),
-            .Keyword_switch => return p.parseSwitchExpr(),
+            .Keyword_switch => return p.expectSwitchExpr(),
 
             .Keyword_extern,
             .Keyword_packed,
@@ -2880,8 +2876,8 @@ const Parser = struct {
     }
 
     /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
-    fn parseSwitchExpr(p: *Parser) !Node.Index {
-        const switch_token = p.eatToken(.Keyword_switch) orelse return null_node;
+    fn expectSwitchExpr(p: *Parser) !Node.Index {
+        const switch_token = p.assertToken(.Keyword_switch);
         _ = try p.expectToken(.LParen);
         const expr_node = try p.expectExpr();
         _ = try p.expectToken(.RParen);
@@ -3191,8 +3187,7 @@ const Parser = struct {
         const first_item = try p.parseSwitchItem();
         if (first_item == 0) return null_node;
 
-        if (p.token_tags[p.tok_i] == .RBrace) {
-            const arrow_token = try p.expectToken(.EqualAngleBracketRight);
+        if (p.eatToken(.EqualAngleBracketRight)) |arrow_token| {
             _ = try p.parsePtrPayload();
             return p.addNode(.{
                 .tag = .SwitchCaseOne,
lib/std/zig/parser_test.zig
@@ -2830,86 +2830,81 @@ test "zig fmt: switch" {
 //        \\
 //    );
 //}
-//
-//test "zig fmt: for" {
-//    try testCanonical(
-//        \\test "for" {
-//        \\    for (a) |v| {
-//        \\        continue;
-//        \\    }
-//        \\
-//        \\    for (a) |v| continue;
-//        \\
-//        \\    for (a) |v| continue else return;
-//        \\
-//        \\    for (a) |v| {
-//        \\        continue;
-//        \\    } else return;
-//        \\
-//        \\    for (a) |v| continue else {
-//        \\        return;
-//        \\    }
-//        \\
-//        \\    for (a) |v|
-//        \\        continue
-//        \\    else
-//        \\        return;
-//        \\
-//        \\    for (a) |v|
-//        \\        continue;
-//        \\
-//        \\    for (a) |*v|
-//        \\        continue;
-//        \\
-//        \\    for (a) |v, i| {
-//        \\        continue;
-//        \\    }
-//        \\
-//        \\    for (a) |v, i|
-//        \\        continue;
-//        \\
-//        \\    for (a) |b| switch (b) {
-//        \\        c => {},
-//        \\        d => {},
-//        \\    };
-//        \\
-//        \\    for (a) |b|
-//        \\        switch (b) {
-//        \\            c => {},
-//        \\            d => {},
-//        \\        };
-//        \\
-//        \\    const res = for (a) |v, i| {
-//        \\        break v;
-//        \\    } else {
-//        \\        unreachable;
-//        \\    };
-//        \\
-//        \\    var num: usize = 0;
-//        \\    inline for (a) |v, i| {
-//        \\        num += v;
-//        \\        num += i;
-//        \\    }
-//        \\}
-//        \\
-//    );
-//
-//    try testTransform(
-//        \\test "fix for" {
-//        \\    for (a) |x|
-//        \\        f(x) else continue;
-//        \\}
-//        \\
-//    ,
-//        \\test "fix for" {
-//        \\    for (a) |x|
-//        \\        f(x)
-//        \\    else continue;
-//        \\}
-//        \\
-//    );
-//}
-//
+
+test "zig fmt: for" {
+    try testCanonical(
+        \\test "for" {
+        \\    for (a) |v| {
+        \\        continue;
+        \\    }
+        \\
+        \\    for (a) |v| continue;
+        \\
+        \\    for (a) |v| continue else return;
+        \\
+        \\    for (a) |v| {
+        \\        continue;
+        \\    } else return;
+        \\
+        \\    for (a) |v| continue else {
+        \\        return;
+        \\    }
+        \\
+        \\    for (a) |v|
+        \\        continue
+        \\    else
+        \\        return;
+        \\
+        \\    for (a) |v|
+        \\        continue;
+        \\
+        \\    for (a) |*v|
+        \\        continue;
+        \\
+        \\    for (a) |v, i| {
+        \\        continue;
+        \\    }
+        \\
+        \\    for (a) |v, i|
+        \\        continue;
+        \\
+        \\    for (a) |b| switch (b) {
+        \\        c => {},
+        \\        d => {},
+        \\    };
+        \\
+        \\    const res = for (a) |v, i| {
+        \\        break v;
+        \\    } else {
+        \\        unreachable;
+        \\    };
+        \\
+        \\    var num: usize = 0;
+        \\    inline for (a) |v, i| {
+        \\        num += v;
+        \\        num += i;
+        \\    }
+        \\}
+        \\
+    );
+
+    try testTransform(
+        \\test "fix for" {
+        \\    for (a) |x|
+        \\        f(x) else continue;
+        \\}
+        \\
+    ,
+        \\test "fix for" {
+        \\    for (a) |x|
+        \\        f(x)
+        \\    else
+        \\        continue;
+        \\}
+        \\
+    );
+}
+
 //test "zig fmt: if" {
 //    try testCanonical(
 //        \\test "if" {
lib/std/zig/render.zig
@@ -498,18 +498,17 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
             }
         },
 
-        .Continue => unreachable, // TODO
-        //.Continue => {
-        //    const flow_expr = base.castTag(.Continue).?;
-        //    if (flow_expr.getLabel()) |label| {
-        //        try renderToken(ais, tree, flow_expr.ltoken, Space.Space); // continue
-        //        const colon = tree.nextToken(flow_expr.ltoken);
-        //        try renderToken(ais, tree, colon, Space.None); // :
-        //        return renderToken(ais, tree, label, space); // label
-        //    } else {
-        //        return renderToken(ais, tree, flow_expr.ltoken, space); // continue
-        //    }
-        //},
+        .Continue => {
+            const main_token = main_tokens[node];
+            const label = datas[node].lhs;
+            if (label != 0) {
+                try renderToken(ais, tree, main_token, .Space); // continue
+                try renderToken(ais, tree, label - 1, .None); // :
+                return renderToken(ais, tree, label, space); // label
+            } else {
+                return renderToken(ais, tree, main_token, space); // continue
+            }
+        },
 
         .Return => {
             if (datas[node].lhs != 0) {
@@ -664,17 +663,16 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
             if (cases.len == 0) {
                 try renderToken(ais, tree, rparen + 1, .None); // lbrace
                 return renderToken(ais, tree, rparen + 2, space); // rbrace
-            } else {
-                try renderToken(ais, tree, rparen + 1, .Newline); // lbrace
-                ais.pushIndent();
-                try renderExpression(ais, tree, cases[0], .Comma);
-                for (cases[1..]) |case| {
-                    try renderExtraNewline(ais, tree, case);
-                    try renderExpression(ais, tree, case, .Comma);
-                }
-                ais.popIndent();
-                return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
             }
+            try renderToken(ais, tree, rparen + 1, .Newline); // lbrace
+            ais.pushIndent();
+            try renderExpression(ais, tree, cases[0], .Comma);
+            for (cases[1..]) |case| {
+                try renderExtraNewline(ais, tree, case);
+                try renderExpression(ais, tree, case, .Comma);
+            }
+            ais.popIndent();
+            return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
         },
 
         .SwitchCaseOne => return renderSwitchCase(ais, tree, tree.switchCaseOne(node), space),
@@ -683,59 +681,8 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
         .WhileSimple => return renderWhile(ais, tree, tree.whileSimple(node), space),
         .WhileCont => return renderWhile(ais, tree, tree.whileCont(node), space),
         .While => return renderWhile(ais, tree, tree.whileFull(node), space),
-
-        .ForSimple => unreachable, // TODO
-        .For => unreachable, // TODO
-        //.For => {
-        //    const for_node = @fieldParentPtr(ast.Node.For, "base", base);
-
-        //    if (for_node.label) |label| {
-        //        try renderToken(ais, tree, label, Space.None); // label
-        //        try renderToken(ais, tree, tree.nextToken(label), Space.Space); // :
-        //    }
-
-        //    if (for_node.inline_token) |inline_token| {
-        //        try renderToken(ais, tree, inline_token, Space.Space); // inline
-        //    }
-
-        //    try renderToken(ais, tree, for_node.for_token, Space.Space); // for
-        //    try renderToken(ais, tree, tree.nextToken(for_node.for_token), Space.None); // (
-        //    try renderExpression(ais, tree, for_node.array_expr, Space.None);
-
-        //    const rparen = tree.nextToken(for_node.array_expr.lastToken());
-
-        //    const body_is_block = for_node.body.tag.isBlock();
-        //    const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());
-        //    const body_on_same_line = body_is_block or src_one_line_to_body;
-
-        //    try renderToken(ais, tree, rparen, Space.Space); // )
-
-        //    const space_after_payload = if (body_on_same_line) Space.Space else Space.Newline;
-        //    try renderExpression(ais, tree, for_node.payload, space_after_payload); // |x|
-
-        //    const space_after_body = blk: {
-        //        if (for_node.@"else") |@"else"| {
-        //            const src_one_line_to_else = tree.tokensOnSameLine(rparen, @"else".firstToken());
-        //            if (body_is_block or src_one_line_to_else) {
-        //                break :blk Space.Space;
-        //            } else {
-        //                break :blk Space.Newline;
-        //            }
-        //        } else {
-        //            break :blk space;
-        //        }
-        //    };
-
-        //    {
-        //        if (!body_on_same_line) ais.pushIndent();
-        //        defer if (!body_on_same_line) ais.popIndent();
-        //        try renderExpression(ais, tree, for_node.body, space_after_body); // { body }
-        //    }
-
-        //    if (for_node.@"else") |@"else"| {
-        //        return renderExpression(ais, tree, &@"else".base, space); // else
-        //    }
-        //},
+        .ForSimple => return renderWhile(ais, tree, tree.forSimple(node), space),
+        .For => return renderWhile(ais, tree, tree.forFull(node), space),
 
         .IfSimple => return renderIf(ais, tree, tree.ifSimple(node), space),
         .If => return renderIf(ais, tree, tree.ifFull(node), space),
@@ -1041,7 +988,7 @@ fn renderIf(ais: *Ais, tree: ast.Tree, if_node: ast.full.If, space: Space) Error
     }, space);
 }
 
-/// Note that this function is additionally used to render if expressions, with
+/// Note that this function is additionally used to render if and for expressions, with
 /// respective values set to null.
 fn renderWhile(ais: *Ais, tree: ast.Tree, while_node: ast.full.While, space: Space) Error!void {
     const node_tags = tree.nodes.items(.tag);
@@ -1061,21 +1008,31 @@ fn renderWhile(ais: *Ais, tree: ast.Tree, while_node: ast.full.While, space: Spa
     try renderExpression(ais, tree, while_node.ast.cond_expr, .None); // condition
 
     if (nodeIsBlock(node_tags[while_node.ast.then_expr])) {
-        const payload_space: Space = if (while_node.ast.cont_expr != 0) .Space else .BlockStart;
         if (while_node.payload_token) |payload_token| {
             try renderToken(ais, tree, payload_token - 2, .Space); // )
             try renderToken(ais, tree, payload_token - 1, .None); // |
-            if (token_tags[payload_token] == .Asterisk) {
-                try renderToken(ais, tree, payload_token, .None); // *
-                try renderToken(ais, tree, payload_token + 1, .None); // identifier
-                try renderToken(ais, tree, payload_token + 2, payload_space); // |
-            } else {
-                try renderToken(ais, tree, payload_token, .None); // identifier
-                try renderToken(ais, tree, payload_token + 1, payload_space); // |
-            }
+            const ident = blk: {
+                if (token_tags[payload_token] == .Asterisk) {
+                    try renderToken(ais, tree, payload_token, .None); // *
+                    break :blk payload_token + 1;
+                } else {
+                    break :blk payload_token;
+                }
+            };
+            try renderToken(ais, tree, ident, .None); // identifier
+            const pipe = blk: {
+                if (token_tags[ident + 1] == .Comma) {
+                    try renderToken(ais, tree, ident + 1, .Space); // ,
+                    try renderToken(ais, tree, ident + 2, .None); // index
+                    break :blk payload_token + 3;
+                } else {
+                    break :blk ident + 1;
+                }
+            };
+            try renderToken(ais, tree, pipe, .Space); // |
         } else {
             const rparen = tree.lastToken(while_node.ast.cond_expr) + 1;
-            try renderToken(ais, tree, rparen, payload_space); // )
+            try renderToken(ais, tree, rparen, .Space); // )
         }
         if (while_node.ast.cont_expr != 0) {
             const rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
@@ -1083,7 +1040,7 @@ fn renderWhile(ais: *Ais, tree: ast.Tree, while_node: ast.full.While, space: Spa
             try renderToken(ais, tree, lparen - 1, .Space); // :
             try renderToken(ais, tree, lparen, .None); // lparen
             try renderExpression(ais, tree, while_node.ast.cont_expr, .None);
-            try renderToken(ais, tree, rparen, .BlockStart); // rparen
+            try renderToken(ais, tree, rparen, .Space); // rparen
         }
         if (while_node.ast.else_expr != 0) {
             try renderExpression(ais, tree, while_node.ast.then_expr, Space.Space);
@@ -1104,15 +1061,31 @@ fn renderWhile(ais: *Ais, tree: ast.Tree, while_node: ast.full.While, space: Spa
     const src_has_newline = !tree.tokensOnSameLine(rparen, last_then_token);
 
     if (src_has_newline) {
-        const payload_space: Space = if (while_node.ast.cont_expr != 0) .Space else .Newline;
         if (while_node.payload_token) |payload_token| {
             try renderToken(ais, tree, payload_token - 2, .Space); // )
             try renderToken(ais, tree, payload_token - 1, .None); // |
-            try renderToken(ais, tree, payload_token, .None); // identifier
-            try renderToken(ais, tree, payload_token + 1, payload_space); // |
+            const ident = blk: {
+                if (token_tags[payload_token] == .Asterisk) {
+                    try renderToken(ais, tree, payload_token, .None); // *
+                    break :blk payload_token + 1;
+                } else {
+                    break :blk payload_token;
+                }
+            };
+            try renderToken(ais, tree, ident, .None); // identifier
+            const pipe = blk: {
+                if (token_tags[ident + 1] == .Comma) {
+                    try renderToken(ais, tree, ident + 1, .Space); // ,
+                    try renderToken(ais, tree, ident + 2, .None); // index
+                    break :blk payload_token + 3;
+                } else {
+                    break :blk ident + 1;
+                }
+            };
+            try renderToken(ais, tree, pipe, .Newline); // |
         } else {
             ais.pushIndent();
-            try renderToken(ais, tree, rparen, payload_space); // )
+            try renderToken(ais, tree, rparen, .Newline); // )
             ais.popIndent();
         }
         if (while_node.ast.cont_expr != 0) {
@@ -1164,14 +1137,25 @@ fn renderWhile(ais: *Ais, tree: ast.Tree, while_node: ast.full.While, space: Spa
         assert(payload_token - 2 == rparen);
         try renderToken(ais, tree, payload_token - 2, .Space); // )
         try renderToken(ais, tree, payload_token - 1, .None); // |
-        if (token_tags[payload_token] == .Asterisk) {
-            try renderToken(ais, tree, payload_token, .None); // *
-            try renderToken(ais, tree, payload_token + 1, .None); // identifier
-            try renderToken(ais, tree, payload_token + 2, .Space); // |
-        } else {
-            try renderToken(ais, tree, payload_token, .None); // identifier
-            try renderToken(ais, tree, payload_token + 1, .Space); // |
-        }
+        const ident = blk: {
+            if (token_tags[payload_token] == .Asterisk) {
+                try renderToken(ais, tree, payload_token, .None); // *
+                break :blk payload_token + 1;
+            } else {
+                break :blk payload_token;
+            }
+        };
+        try renderToken(ais, tree, ident, .None); // identifier
+        const pipe = blk: {
+            if (token_tags[ident + 1] == .Comma) {
+                try renderToken(ais, tree, ident + 1, .Space); // ,
+                try renderToken(ais, tree, ident + 2, .None); // index
+                break :blk payload_token + 3;
+            } else {
+                break :blk ident + 1;
+            }
+        };
+        try renderToken(ais, tree, pipe, .Space); // |
     } else {
         try renderToken(ais, tree, rparen, .Space); // )
     }
@@ -1952,18 +1936,9 @@ const Space = enum {
     Semicolon,
     /// Skips writing the possible line comment after the token.
     NoComment,
-    /// Intended when rendering lbrace tokens. Depending on whether the line is
-    /// "over indented", will output a newline or a single space afterwards.
-    /// See `std.io.AutoIndentingStream` for the definition of "over indented".
-    BlockStart,
 };
 
 fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Space) Error!void {
-    if (space == Space.BlockStart) {
-        const new_space: Space = if (ais.isLineOverIndented()) .Newline else .Space;
-        return renderToken(ais, tree, token_index, new_space);
-    }
-
     const token_tags = tree.tokens.items(.tag);
     const token_starts = tree.tokens.items(.start);
 
@@ -2020,7 +1995,6 @@ fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Sp
                 try ais.insertNewline();
             }
         },
-        .BlockStart => unreachable,
     }
 }
 
@@ -2069,6 +2043,7 @@ fn nodeIsBlock(tag: ast.Node.Tag) bool {
         .WhileSimple,
         .WhileCont,
         .Switch,
+        .SwitchComma,
         => true,
         else => false,
     };