authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-01-30 19:10:03+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-18 19:17:20-07:00
log1b7055b514955f1787937b2ef6097d2e0663da74
treef4a49c1740e6c9bbae31250d61b70a4d5e06173f
parent5e7b09ce9fbc95ec9fb9e277d262b9b5a5aa1917

parse and render new for loop syntax


4 files changed, 410 insertions(+), 105 deletions(-)

lib/std/zig/Ast.zig+101-16
......@@ -386,6 +386,12 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
386386 .expected_comma_after_switch_prong => {
387387 return stream.writeAll("expected ',' after switch prong");
388388 },
389 .expected_comma_after_for_operand => {
390 return stream.writeAll("expected ',' after for operand");
391 },
392 .expected_comma_after_capture => {
393 return stream.writeAll("expected ',' after for capture");
394 },
389395 .expected_initializer => {
390396 return stream.writeAll("expected field initializer");
391397 },
......@@ -420,6 +426,12 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
420426 .var_const_decl => {
421427 return stream.writeAll("use 'var' or 'const' to declare variable");
422428 },
429 .extra_for_capture => {
430 return stream.writeAll("excess for captures");
431 },
432 .for_input_not_captured => {
433 return stream.writeAll("for input is not captured");
434 },
423435
424436 .expected_token => {
425437 const found_tag = token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)];
......@@ -568,6 +580,7 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
568580 .call,
569581 .call_comma,
570582 .switch_range,
583 .for_range,
571584 .error_union,
572585 => n = datas[n].lhs,
573586
......@@ -845,6 +858,12 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
845858 .switch_range,
846859 => n = datas[n].rhs,
847860
861 .for_range => if (datas[n].rhs != 0) {
862 n = datas[n].rhs;
863 } else {
864 return main_tokens[n] + end_offset;
865 },
866
848867 .field_access,
849868 .unwrap_optional,
850869 .grouped_expression,
......@@ -1263,11 +1282,15 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
12631282 assert(extra.else_expr != 0);
12641283 n = extra.else_expr;
12651284 },
1266 .@"if", .@"for" => {
1285 .@"if" => {
12671286 const extra = tree.extraData(datas[n].rhs, Node.If);
12681287 assert(extra.else_expr != 0);
12691288 n = extra.else_expr;
12701289 },
1290 .@"for" => {
1291 const extra = @bitCast(Node.For, datas[n].rhs);
1292 n = tree.extra_data[datas[n].lhs + extra.inputs + @boolToInt(extra.has_else)];
1293 },
12711294 .@"suspend" => {
12721295 if (datas[n].lhs != 0) {
12731296 n = datas[n].lhs;
......@@ -1916,26 +1939,28 @@ pub fn whileFull(tree: Ast, node: Node.Index) full.While {
19161939 });
19171940}
19181941
1919pub fn forSimple(tree: Ast, node: Node.Index) full.While {
1920 const data = tree.nodes.items(.data)[node];
1921 return tree.fullWhileComponents(.{
1922 .while_token = tree.nodes.items(.main_token)[node],
1923 .cond_expr = data.lhs,
1924 .cont_expr = 0,
1942pub fn forSimple(tree: Ast, node: Node.Index) full.For {
1943 const data = &tree.nodes.items(.data)[node];
1944 const inputs: *[1]Node.Index = &data.lhs;
1945 return tree.fullForComponents(.{
1946 .for_token = tree.nodes.items(.main_token)[node],
1947 .inputs = inputs[0..1],
19251948 .then_expr = data.rhs,
19261949 .else_expr = 0,
19271950 });
19281951}
19291952
1930pub fn forFull(tree: Ast, node: Node.Index) full.While {
1953pub fn forFull(tree: Ast, node: Node.Index) full.For {
19311954 const data = tree.nodes.items(.data)[node];
1932 const extra = tree.extraData(data.rhs, Node.If);
1933 return tree.fullWhileComponents(.{
1934 .while_token = tree.nodes.items(.main_token)[node],
1935 .cond_expr = data.lhs,
1936 .cont_expr = 0,
1937 .then_expr = extra.then_expr,
1938 .else_expr = extra.else_expr,
1955 const extra = @bitCast(Node.For, data.rhs);
1956 const inputs = tree.extra_data[data.lhs..][0..extra.inputs];
1957 const then_expr = tree.extra_data[data.lhs + extra.inputs];
1958 const else_expr = if (extra.has_else) tree.extra_data[data.lhs + extra.inputs + 1] else 0;
1959 return tree.fullForComponents(.{
1960 .for_token = tree.nodes.items(.main_token)[node],
1961 .inputs = inputs,
1962 .then_expr = then_expr,
1963 .else_expr = else_expr,
19391964 });
19401965}
19411966
......@@ -2243,6 +2268,33 @@ fn fullWhileComponents(tree: Ast, info: full.While.Components) full.While {
22432268 return result;
22442269}
22452270
2271fn fullForComponents(tree: Ast, info: full.For.Components) full.For {
2272 const token_tags = tree.tokens.items(.tag);
2273 var result: full.For = .{
2274 .ast = info,
2275 .inline_token = null,
2276 .label_token = null,
2277 .payload_token = undefined,
2278 .else_token = undefined,
2279 };
2280 var tok_i = info.for_token - 1;
2281 if (token_tags[tok_i] == .keyword_inline) {
2282 result.inline_token = tok_i;
2283 tok_i -= 1;
2284 }
2285 if (token_tags[tok_i] == .colon and
2286 token_tags[tok_i - 1] == .identifier)
2287 {
2288 result.label_token = tok_i - 1;
2289 }
2290 const last_cond_token = tree.lastToken(info.inputs[info.inputs.len - 1]);
2291 result.payload_token = last_cond_token + 3 + @boolToInt(token_tags[last_cond_token + 1] == .comma);
2292 if (info.else_expr != 0) {
2293 result.else_token = tree.lastToken(info.then_expr) + 1;
2294 }
2295 return result;
2296}
2297
22462298fn fullCallComponents(tree: Ast, info: full.Call.Components) full.Call {
22472299 const token_tags = tree.tokens.items(.tag);
22482300 var result: full.Call = .{
......@@ -2279,6 +2331,12 @@ pub fn fullWhile(tree: Ast, node: Node.Index) ?full.While {
22792331 .while_simple => tree.whileSimple(node),
22802332 .while_cont => tree.whileCont(node),
22812333 .@"while" => tree.whileFull(node),
2334 else => null,
2335 };
2336}
2337
2338pub fn fullFor(tree: Ast, node: Node.Index) ?full.For {
2339 return switch (tree.nodes.items(.tag)[node]) {
22822340 .for_simple => tree.forSimple(node),
22832341 .@"for" => tree.forFull(node),
22842342 else => null,
......@@ -2453,6 +2511,22 @@ pub const full = struct {
24532511 };
24542512 };
24552513
2514 pub const For = struct {
2515 ast: Components,
2516 inline_token: ?TokenIndex,
2517 label_token: ?TokenIndex,
2518 payload_token: TokenIndex,
2519 /// Populated only if else_expr != 0.
2520 else_token: TokenIndex,
2521
2522 pub const Components = struct {
2523 for_token: TokenIndex,
2524 inputs: []const Node.Index,
2525 then_expr: Node.Index,
2526 else_expr: Node.Index,
2527 };
2528 };
2529
24562530 pub const ContainerField = struct {
24572531 comptime_token: ?TokenIndex,
24582532 ast: Components,
......@@ -2795,6 +2869,8 @@ pub const Error = struct {
27952869 expected_comma_after_param,
27962870 expected_comma_after_initializer,
27972871 expected_comma_after_switch_prong,
2872 expected_comma_after_for_operand,
2873 expected_comma_after_capture,
27982874 expected_initializer,
27992875 mismatched_binary_op_whitespace,
28002876 invalid_ampersand_ampersand,
......@@ -2802,6 +2878,8 @@ pub const Error = struct {
28022878 expected_var_const,
28032879 wrong_equal_var_decl,
28042880 var_const_decl,
2881 extra_for_capture,
2882 for_input_not_captured,
28052883
28062884 zig_style_container,
28072885 previous_field,
......@@ -3112,8 +3190,10 @@ pub const Node = struct {
31123190 @"while",
31133191 /// `for (lhs) rhs`.
31143192 for_simple,
3115 /// `for (lhs) a else b`. `if_list[rhs]`.
3193 /// `for (lhs[0..inputs]) lhs[inputs + 1] else lhs[inputs + 2]`. `For[rhs]`.
31163194 @"for",
3195 /// `lhs..rhs`.
3196 for_range,
31173197 /// `if (lhs) rhs`.
31183198 /// `if (lhs) |a| rhs`.
31193199 if_simple,
......@@ -3369,6 +3449,11 @@ pub const Node = struct {
33693449 then_expr: Index,
33703450 };
33713451
3452 pub const For = packed struct(u32) {
3453 inputs: u31,
3454 has_else: bool,
3455 };
3456
33723457 pub const FnProtoOne = struct {
33733458 /// Populated if there is exactly 1 parameter. Otherwise there are 0 parameters.
33743459 param: Index,
lib/std/zig/Parse.zig+136-57
......@@ -104,6 +104,8 @@ fn warnMsg(p: *Parse, msg: Ast.Error) error{OutOfMemory}!void {
104104 .expected_comma_after_param,
105105 .expected_comma_after_initializer,
106106 .expected_comma_after_switch_prong,
107 .expected_comma_after_for_operand,
108 .expected_comma_after_capture,
107109 .expected_semi_or_else,
108110 .expected_semi_or_lbrace,
109111 .expected_token,
......@@ -1149,22 +1151,18 @@ fn parseLoopStatement(p: *Parse) !Node.Index {
11491151 return p.fail(.expected_inlinable);
11501152}
11511153
1152/// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
1153///
11541154/// ForStatement
11551155/// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
11561156/// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
11571157fn parseForStatement(p: *Parse) !Node.Index {
11581158 const for_token = p.eatToken(.keyword_for) orelse return null_node;
1159 _ = try p.expectToken(.l_paren);
1160 const array_expr = try p.expectExpr();
1161 _ = try p.expectToken(.r_paren);
1162 const found_payload = try p.parsePtrIndexPayload();
1163 if (found_payload == 0) try p.warn(.expected_loop_payload);
11641159
1165 // TODO propose to change the syntax so that semicolons are always required
1166 // inside while statements, even if there is an `else`.
1160 const scratch_top = p.scratch.items.len;
1161 defer p.scratch.shrinkRetainingCapacity(scratch_top);
1162 const inputs = try p.forPrefix();
1163
11671164 var else_required = false;
1165 var seen_semicolon = false;
11681166 const then_expr = blk: {
11691167 const block_expr = try p.parseBlockExpr();
11701168 if (block_expr != 0) break :blk block_expr;
......@@ -1173,39 +1171,40 @@ fn parseForStatement(p: *Parse) !Node.Index {
11731171 return p.fail(.expected_block_or_assignment);
11741172 }
11751173 if (p.eatToken(.semicolon)) |_| {
1176 return p.addNode(.{
1177 .tag = .for_simple,
1178 .main_token = for_token,
1179 .data = .{
1180 .lhs = array_expr,
1181 .rhs = assign_expr,
1182 },
1183 });
1174 seen_semicolon = true;
1175 break :blk assign_expr;
11841176 }
11851177 else_required = true;
11861178 break :blk assign_expr;
11871179 };
1188 _ = p.eatToken(.keyword_else) orelse {
1189 if (else_required) {
1190 try p.warn(.expected_semi_or_else);
1191 }
1180 var has_else = false;
1181 if (!seen_semicolon and p.eatToken(.keyword_else) != null) {
1182 try p.scratch.append(p.gpa, then_expr);
1183 const else_stmt = try p.expectStatement(false);
1184 try p.scratch.append(p.gpa, else_stmt);
1185 has_else = true;
1186 } else if (inputs == 1) {
1187 if (else_required) try p.warn(.expected_semi_or_else);
11921188 return p.addNode(.{
11931189 .tag = .for_simple,
11941190 .main_token = for_token,
11951191 .data = .{
1196 .lhs = array_expr,
1192 .lhs = p.scratch.items[scratch_top],
11971193 .rhs = then_expr,
11981194 },
11991195 });
1200 };
1196 } else {
1197 if (else_required) try p.warn(.expected_semi_or_else);
1198 try p.scratch.append(p.gpa, then_expr);
1199 }
12011200 return p.addNode(.{
12021201 .tag = .@"for",
12031202 .main_token = for_token,
12041203 .data = .{
1205 .lhs = array_expr,
1206 .rhs = try p.addExtra(Node.If{
1207 .then_expr = then_expr,
1208 .else_expr = try p.expectStatement(false),
1204 .lhs = (try p.listToSpan(p.scratch.items[scratch_top..])).start,
1205 .rhs = @bitCast(u32, Node.For{
1206 .inputs = @intCast(u31, inputs),
1207 .has_else = has_else,
12091208 }),
12101209 },
12111210 });
......@@ -2056,42 +2055,118 @@ fn parseBlock(p: *Parse) !Node.Index {
20562055 }
20572056}
20582057
2059/// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
2060///
20612058/// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
20622059fn parseForExpr(p: *Parse) !Node.Index {
20632060 const for_token = p.eatToken(.keyword_for) orelse return null_node;
2064 _ = try p.expectToken(.l_paren);
2065 const array_expr = try p.expectExpr();
2066 _ = try p.expectToken(.r_paren);
2067 const found_payload = try p.parsePtrIndexPayload();
2068 if (found_payload == 0) try p.warn(.expected_loop_payload);
2061
2062 const scratch_top = p.scratch.items.len;
2063 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2064 const inputs = try p.forPrefix();
20692065
20702066 const then_expr = try p.expectExpr();
2071 _ = p.eatToken(.keyword_else) orelse {
2067 var has_else = false;
2068 if (p.eatToken(.keyword_else)) |_| {
2069 try p.scratch.append(p.gpa, then_expr);
2070 const else_expr = try p.expectExpr();
2071 try p.scratch.append(p.gpa, else_expr);
2072 has_else = true;
2073 } else if (inputs == 1) {
20722074 return p.addNode(.{
20732075 .tag = .for_simple,
20742076 .main_token = for_token,
20752077 .data = .{
2076 .lhs = array_expr,
2078 .lhs = p.scratch.items[scratch_top],
20772079 .rhs = then_expr,
20782080 },
20792081 });
2080 };
2081 const else_expr = try p.expectExpr();
2082 } else {
2083 try p.scratch.append(p.gpa, then_expr);
2084 }
20822085 return p.addNode(.{
20832086 .tag = .@"for",
20842087 .main_token = for_token,
20852088 .data = .{
2086 .lhs = array_expr,
2087 .rhs = try p.addExtra(Node.If{
2088 .then_expr = then_expr,
2089 .else_expr = else_expr,
2089 .lhs = (try p.listToSpan(p.scratch.items[scratch_top..])).start,
2090 .rhs = @bitCast(u32, Node.For{
2091 .inputs = @intCast(u31, inputs),
2092 .has_else = has_else,
20902093 }),
20912094 },
20922095 });
20932096}
20942097
2098/// ForPrefix <- KEYWORD_for LPAREN ForInput (COMMA ForInput)* COMMA? RPAREN ForPayload
2099///
2100/// ForInput <- Expr (DOT2 Expr?)?
2101///
2102/// ForPayload <- PIPE ASTERISK? IDENTIFIER (COMMA ASTERISK? IDENTIFIER)* PIPE
2103fn forPrefix(p: *Parse) Error!usize {
2104 const start = p.scratch.items.len;
2105 _ = try p.expectToken(.l_paren);
2106
2107 while (true) {
2108 var input = try p.expectExpr();
2109 if (p.eatToken(.ellipsis2)) |ellipsis| {
2110 input = try p.addNode(.{
2111 .tag = .for_range,
2112 .main_token = ellipsis,
2113 .data = .{
2114 .lhs = input,
2115 .rhs = try p.parseExpr(),
2116 },
2117 });
2118 }
2119
2120 try p.scratch.append(p.gpa, input);
2121 switch (p.token_tags[p.tok_i]) {
2122 .comma => p.tok_i += 1,
2123 .r_paren => {
2124 p.tok_i += 1;
2125 break;
2126 },
2127 .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren),
2128 // Likely just a missing comma; give error but continue parsing.
2129 else => try p.warn(.expected_comma_after_for_operand),
2130 }
2131 if (p.eatToken(.r_paren)) |_| break;
2132 }
2133 const inputs = p.scratch.items.len - start;
2134
2135 _ = p.eatToken(.pipe) orelse {
2136 try p.warn(.expected_loop_payload);
2137 return inputs;
2138 };
2139
2140 var warned_excess = false;
2141 var captures: u32 = 0;
2142 while (true) {
2143 _ = p.eatToken(.asterisk);
2144 const identifier = try p.expectToken(.identifier);
2145 captures += 1;
2146 if (captures > inputs and !warned_excess) {
2147 try p.warnMsg(.{ .tag = .extra_for_capture, .token = identifier });
2148 warned_excess = true;
2149 }
2150 switch (p.token_tags[p.tok_i]) {
2151 .comma => p.tok_i += 1,
2152 .pipe => {
2153 p.tok_i += 1;
2154 break;
2155 },
2156 // Likely just a missing comma; give error but continue parsing.
2157 else => try p.warn(.expected_comma_after_capture),
2158 }
2159 if (p.eatToken(.pipe)) |_| break;
2160 }
2161
2162 if (captures < inputs) {
2163 const index = p.scratch.items.len - captures;
2164 const input = p.nodes.items(.main_token)[p.scratch.items[index]];
2165 try p.warnMsg(.{ .tag = .for_input_not_captured, .token = input });
2166 }
2167 return inputs;
2168}
2169
20952170/// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
20962171///
20972172/// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
......@@ -2752,37 +2827,41 @@ fn expectPrimaryTypeExpr(p: *Parse) !Node.Index {
27522827 return node;
27532828}
27542829
2755/// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
2756///
27572830/// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
27582831fn parseForTypeExpr(p: *Parse) !Node.Index {
27592832 const for_token = p.eatToken(.keyword_for) orelse return null_node;
2760 _ = try p.expectToken(.l_paren);
2761 const array_expr = try p.expectExpr();
2762 _ = try p.expectToken(.r_paren);
2763 const found_payload = try p.parsePtrIndexPayload();
2764 if (found_payload == 0) try p.warn(.expected_loop_payload);
2833
2834 const scratch_top = p.scratch.items.len;
2835 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2836 const inputs = try p.forPrefix();
27652837
27662838 const then_expr = try p.expectTypeExpr();
2767 _ = p.eatToken(.keyword_else) orelse {
2839 var has_else = false;
2840 if (p.eatToken(.keyword_else)) |_| {
2841 try p.scratch.append(p.gpa, then_expr);
2842 const else_expr = try p.expectTypeExpr();
2843 try p.scratch.append(p.gpa, else_expr);
2844 has_else = true;
2845 } else if (inputs == 1) {
27682846 return p.addNode(.{
27692847 .tag = .for_simple,
27702848 .main_token = for_token,
27712849 .data = .{
2772 .lhs = array_expr,
2850 .lhs = p.scratch.items[scratch_top],
27732851 .rhs = then_expr,
27742852 },
27752853 });
2776 };
2777 const else_expr = try p.expectTypeExpr();
2854 } else {
2855 try p.scratch.append(p.gpa, then_expr);
2856 }
27782857 return p.addNode(.{
27792858 .tag = .@"for",
27802859 .main_token = for_token,
27812860 .data = .{
2782 .lhs = array_expr,
2783 .rhs = try p.addExtra(Node.If{
2784 .then_expr = then_expr,
2785 .else_expr = else_expr,
2861 .lhs = (try p.listToSpan(p.scratch.items[scratch_top..])).start,
2862 .rhs = @bitCast(u32, Node.For{
2863 .inputs = @intCast(u31, inputs),
2864 .has_else = has_else,
27862865 }),
27872866 },
27882867 });
lib/std/zig/parser_test.zig+38-11
......@@ -3457,11 +3457,11 @@ test "zig fmt: for" {
34573457 \\ for (a) |*v|
34583458 \\ continue;
34593459 \\
3460 \\ for (a) |v, i| {
3460 \\ for (a, 0..) |v, i| {
34613461 \\ continue;
34623462 \\ }
34633463 \\
3464 \\ for (a) |v, i|
3464 \\ for (a, 0..) |v, i|
34653465 \\ continue;
34663466 \\
34673467 \\ for (a) |b| switch (b) {
......@@ -3469,17 +3469,24 @@ test "zig fmt: for" {
34693469 \\ d => {},
34703470 \\ };
34713471 \\
3472 \\ const res = for (a) |v, i| {
3472 \\ const res = for (a, 0..) |v, i| {
34733473 \\ break v;
34743474 \\ } else {
34753475 \\ unreachable;
34763476 \\ };
34773477 \\
34783478 \\ var num: usize = 0;
3479 \\ inline for (a) |v, i| {
3479 \\ inline for (a, 0..1) |v, i| {
34803480 \\ num += v;
34813481 \\ num += i;
34823482 \\ }
3483 \\
3484 \\ for (a, b) |
3485 \\ long_name,
3486 \\ another_long_name,
3487 \\ | {
3488 \\ continue;
3489 \\ }
34833490 \\}
34843491 \\
34853492 );
......@@ -3499,6 +3506,26 @@ test "zig fmt: for" {
34993506 \\}
35003507 \\
35013508 );
3509
3510 try testTransform(
3511 \\test "fix for" {
3512 \\ for (a, b, c,) |long, another, third,| {}
3513 \\}
3514 \\
3515 ,
3516 \\test "fix for" {
3517 \\ for (
3518 \\ a,
3519 \\ b,
3520 \\ c,
3521 \\ ) |
3522 \\ long,
3523 \\ another,
3524 \\ third,
3525 \\ | {}
3526 \\}
3527 \\
3528 );
35023529}
35033530
35043531test "zig fmt: for if" {
......@@ -4358,7 +4385,7 @@ test "zig fmt: hex literals with underscore separators" {
43584385 try testTransform(
43594386 \\pub fn orMask(a: [ 1_000 ]u64, b: [ 1_000] u64) [1_000]u64 {
43604387 \\ var c: [1_000]u64 = [1]u64{ 0xFFFF_FFFF_FFFF_FFFF}**1_000;
4361 \\ for (c [ 1_0 .. ]) |_, i| {
4388 \\ for (c [ 1_0 .. ], 0..) |_, i| {
43624389 \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;
43634390 \\ }
43644391 \\ return c;
......@@ -4368,7 +4395,7 @@ test "zig fmt: hex literals with underscore separators" {
43684395 ,
43694396 \\pub fn orMask(a: [1_000]u64, b: [1_000]u64) [1_000]u64 {
43704397 \\ var c: [1_000]u64 = [1]u64{0xFFFF_FFFF_FFFF_FFFF} ** 1_000;
4371 \\ for (c[1_0..]) |_, i| {
4398 \\ for (c[1_0..], 0..) |_, i| {
43724399 \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;
43734400 \\ }
43744401 \\ return c;
......@@ -4880,10 +4907,10 @@ test "zig fmt: remove trailing whitespace after doc comment" {
48804907test "zig fmt: for loop with ptr payload and index" {
48814908 try testCanonical(
48824909 \\test {
4883 \\ for (self.entries.items) |*item, i| {}
4884 \\ for (self.entries.items) |*item, i|
4910 \\ for (self.entries.items, 0..) |*item, i| {}
4911 \\ for (self.entries.items, 0..) |*item, i|
48854912 \\ a = b;
4886 \\ for (self.entries.items) |*item, i| a = b;
4913 \\ for (self.entries.items, 0..) |*item, i| a = b;
48874914 \\}
48884915 \\
48894916 );
......@@ -5471,7 +5498,7 @@ test "zig fmt: canonicalize symbols (primitive types)" {
54715498 \\ _ = @"void": {
54725499 \\ break :@"void";
54735500 \\ };
5474 \\ for ("hi") |@"u3", @"i4"| {
5501 \\ for ("hi", 0..) |@"u3", @"i4"| {
54755502 \\ _ = @"u3";
54765503 \\ _ = @"i4";
54775504 \\ }
......@@ -5523,7 +5550,7 @@ test "zig fmt: canonicalize symbols (primitive types)" {
55235550 \\ _ = void: {
55245551 \\ break :void;
55255552 \\ };
5526 \\ for ("hi") |@"u3", @"i4"| {
5553 \\ for ("hi", 0..) |@"u3", @"i4"| {
55275554 \\ _ = @"u3";
55285555 \\ _ = @"i4";
55295556 \\ }
lib/std/zig/render.zig+135-21
......@@ -353,6 +353,16 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
353353 try renderToken(ais, tree, main_tokens[node], .none);
354354 return renderExpression(gpa, ais, tree, infix.rhs, space);
355355 },
356 .for_range => {
357 const infix = datas[node];
358 try renderExpression(gpa, ais, tree, infix.lhs, .none);
359 if (infix.rhs != 0) {
360 try renderToken(ais, tree, main_tokens[node], .none);
361 return renderExpression(gpa, ais, tree, infix.rhs, space);
362 } else {
363 return renderToken(ais, tree, main_tokens[node], space);
364 }
365 },
356366
357367 .add,
358368 .add_wrap,
......@@ -694,9 +704,11 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
694704 .while_simple,
695705 .while_cont,
696706 .@"while",
707 => return renderWhile(gpa, ais, tree, tree.fullWhile(node).?, space),
708
697709 .for_simple,
698710 .@"for",
699 => return renderWhile(gpa, ais, tree, tree.fullWhile(node).?, space),
711 => return renderFor(gpa, ais, tree, tree.fullFor(node).?, space),
700712
701713 .if_simple,
702714 .@"if",
......@@ -1054,10 +1066,9 @@ fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: S
10541066 }, space);
10551067}
10561068
1057/// Note that this function is additionally used to render if and for expressions, with
1069/// Note that this function is additionally used to render if expressions, with
10581070/// respective values set to null.
10591071fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, space: Space) Error!void {
1060 const node_tags = tree.nodes.items(.tag);
10611072 const token_tags = tree.tokens.items(.tag);
10621073
10631074 if (while_node.label_token) |label| {
......@@ -1108,9 +1119,34 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
11081119 last_prefix_token = tree.lastToken(while_node.ast.cont_expr) + 1; // rparen
11091120 }
11101121
1111 const then_expr_is_block = nodeIsBlock(node_tags[while_node.ast.then_expr]);
1122 try renderThenElse(
1123 gpa,
1124 ais,
1125 tree,
1126 last_prefix_token,
1127 while_node.ast.then_expr,
1128 while_node.else_token,
1129 while_node.error_token,
1130 while_node.ast.else_expr,
1131 space,
1132 );
1133}
1134
1135fn renderThenElse(
1136 gpa: Allocator,
1137 ais: *Ais,
1138 tree: Ast,
1139 last_prefix_token: Ast.TokenIndex,
1140 then_expr: Ast.Node.Index,
1141 else_token: Ast.TokenIndex,
1142 maybe_error_token: ?Ast.TokenIndex,
1143 else_expr: Ast.Node.Index,
1144 space: Space,
1145) Error!void {
1146 const node_tags = tree.nodes.items(.tag);
1147 const then_expr_is_block = nodeIsBlock(node_tags[then_expr]);
11121148 const indent_then_expr = !then_expr_is_block and
1113 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(while_node.ast.then_expr));
1149 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(then_expr));
11141150 if (indent_then_expr or (then_expr_is_block and ais.isLineOverIndented())) {
11151151 ais.pushIndentNextLine();
11161152 try renderToken(ais, tree, last_prefix_token, .newline);
......@@ -1119,43 +1155,113 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
11191155 try renderToken(ais, tree, last_prefix_token, .space);
11201156 }
11211157
1122 if (while_node.ast.else_expr != 0) {
1158 if (else_expr != 0) {
11231159 if (indent_then_expr) {
11241160 ais.pushIndent();
1125 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .newline);
1161 try renderExpression(gpa, ais, tree, then_expr, .newline);
11261162 ais.popIndent();
11271163 } else {
1128 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space);
1164 try renderExpression(gpa, ais, tree, then_expr, .space);
11291165 }
11301166
1131 var last_else_token = while_node.else_token;
1167 var last_else_token = else_token;
11321168
1133 if (while_node.error_token) |error_token| {
1134 try renderToken(ais, tree, while_node.else_token, .space); // else
1169 if (maybe_error_token) |error_token| {
1170 try renderToken(ais, tree, else_token, .space); // else
11351171 try renderToken(ais, tree, error_token - 1, .none); // |
11361172 try renderIdentifier(ais, tree, error_token, .none, .preserve_when_shadowing); // identifier
11371173 last_else_token = error_token + 1; // |
11381174 }
11391175
11401176 const indent_else_expr = indent_then_expr and
1141 !nodeIsBlock(node_tags[while_node.ast.else_expr]) and
1142 !nodeIsIfForWhileSwitch(node_tags[while_node.ast.else_expr]);
1177 !nodeIsBlock(node_tags[else_expr]) and
1178 !nodeIsIfForWhileSwitch(node_tags[else_expr]);
11431179 if (indent_else_expr) {
11441180 ais.pushIndentNextLine();
11451181 try renderToken(ais, tree, last_else_token, .newline);
11461182 ais.popIndent();
1147 try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space);
1183 try renderExpressionIndented(gpa, ais, tree, else_expr, space);
11481184 } else {
11491185 try renderToken(ais, tree, last_else_token, .space);
1150 try renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
1186 try renderExpression(gpa, ais, tree, else_expr, space);
11511187 }
11521188 } else {
11531189 if (indent_then_expr) {
1154 try renderExpressionIndented(gpa, ais, tree, while_node.ast.then_expr, space);
1190 try renderExpressionIndented(gpa, ais, tree, then_expr, space);
11551191 } else {
1156 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, space);
1192 try renderExpression(gpa, ais, tree, then_expr, space);
1193 }
1194 }
1195}
1196
1197fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space: Space) Error!void {
1198 const token_tags = tree.tokens.items(.tag);
1199
1200 if (for_node.label_token) |label| {
1201 try renderIdentifier(ais, tree, label, .none, .eagerly_unquote); // label
1202 try renderToken(ais, tree, label + 1, .space); // :
1203 }
1204
1205 if (for_node.inline_token) |inline_token| {
1206 try renderToken(ais, tree, inline_token, .space); // inline
1207 }
1208
1209 try renderToken(ais, tree, for_node.ast.for_token, .space); // if/for/while
1210
1211 const lparen = for_node.ast.for_token + 1;
1212 try renderParamList(gpa, ais, tree, lparen, for_node.ast.inputs, .space);
1213
1214 var cur = for_node.payload_token;
1215 const pipe = std.mem.indexOfScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;
1216 if (token_tags[pipe - 1] == .comma) {
1217 ais.pushIndentNextLine();
1218 try renderToken(ais, tree, cur - 1, .newline); // |
1219 while (true) {
1220 if (token_tags[cur] == .asterisk) {
1221 try renderToken(ais, tree, cur, .none); // *
1222 cur += 1;
1223 }
1224 try renderIdentifier(ais, tree, cur, .none, .preserve_when_shadowing); // identifier
1225 cur += 1;
1226 if (token_tags[cur] == .comma) {
1227 try renderToken(ais, tree, cur, .newline); // ,
1228 cur += 1;
1229 }
1230 if (token_tags[cur] == .pipe) {
1231 break;
1232 }
1233 }
1234 ais.popIndent();
1235 } else {
1236 try renderToken(ais, tree, cur - 1, .none); // |
1237 while (true) {
1238 if (token_tags[cur] == .asterisk) {
1239 try renderToken(ais, tree, cur, .none); // *
1240 cur += 1;
1241 }
1242 try renderIdentifier(ais, tree, cur, .none, .preserve_when_shadowing); // identifier
1243 cur += 1;
1244 if (token_tags[cur] == .comma) {
1245 try renderToken(ais, tree, cur, .space); // ,
1246 cur += 1;
1247 }
1248 if (token_tags[cur] == .pipe) {
1249 break;
1250 }
11571251 }
11581252 }
1253
1254 try renderThenElse(
1255 gpa,
1256 ais,
1257 tree,
1258 cur,
1259 for_node.ast.then_expr,
1260 for_node.else_token,
1261 null,
1262 for_node.ast.else_expr,
1263 space,
1264 );
11591265}
11601266
11611267fn renderContainerField(
......@@ -2206,15 +2312,23 @@ fn renderCall(
22062312 call: Ast.full.Call,
22072313 space: Space,
22082314) Error!void {
2209 const token_tags = tree.tokens.items(.tag);
2210
22112315 if (call.async_token) |async_token| {
22122316 try renderToken(ais, tree, async_token, .space);
22132317 }
22142318 try renderExpression(gpa, ais, tree, call.ast.fn_expr, .none);
2319 try renderParamList(gpa, ais, tree, call.ast.lparen, call.ast.params, space);
2320}
2321
2322fn renderParamList(
2323 gpa: Allocator,
2324 ais: *Ais,
2325 tree: Ast,
2326 lparen: Ast.TokenIndex,
2327 params: []const Ast.Node.Index,
2328 space: Space,
2329) Error!void {
2330 const token_tags = tree.tokens.items(.tag);
22152331
2216 const lparen = call.ast.lparen;
2217 const params = call.ast.params;
22182332 if (params.len == 0) {
22192333 ais.pushIndentNextLine();
22202334 try renderToken(ais, tree, lparen, .none);