diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 9bad013dea..aa6962c247 100644 --- a/lib/std/zig/ast.zig +++ b/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, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 7e2da72d00..eb15a29650 100644 --- a/lib/std/zig/parse.zig +++ b/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, diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 0d5775fd61..31d0a821d8 100644 --- a/lib/std/zig/parser_test.zig +++ b/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" { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 8ce7d8bb7b..17e0b0b297 100644 --- a/lib/std/zig/render.zig +++ b/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, };