zig fmt: for loops

This commit is contained in:
Andrew Kelley 2021-02-09 20:08:40 -07:00
parent 1c79eea125
commit 39acc4c020
4 changed files with 200 additions and 212 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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" {

View File

@ -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,
};