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 {...@@ -386,6 +386,12 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
386 .expected_comma_after_switch_prong => {386 .expected_comma_after_switch_prong => {
387 return stream.writeAll("expected ',' after switch prong");387 return stream.writeAll("expected ',' after switch prong");
388 },388 },
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 },
389 .expected_initializer => {395 .expected_initializer => {
390 return stream.writeAll("expected field initializer");396 return stream.writeAll("expected field initializer");
391 },397 },
...@@ -420,6 +426,12 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {...@@ -420,6 +426,12 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
420 .var_const_decl => {426 .var_const_decl => {
421 return stream.writeAll("use 'var' or 'const' to declare variable");427 return stream.writeAll("use 'var' or 'const' to declare variable");
422 },428 },
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
424 .expected_token => {436 .expected_token => {
425 const found_tag = token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)];437 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 {...@@ -568,6 +580,7 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
568 .call,580 .call,
569 .call_comma,581 .call_comma,
570 .switch_range,582 .switch_range,
583 .for_range,
571 .error_union,584 .error_union,
572 => n = datas[n].lhs,585 => n = datas[n].lhs,
573586
...@@ -845,6 +858,12 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -845,6 +858,12 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
845 .switch_range,858 .switch_range,
846 => n = datas[n].rhs,859 => 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
848 .field_access,867 .field_access,
849 .unwrap_optional,868 .unwrap_optional,
850 .grouped_expression,869 .grouped_expression,
...@@ -1263,11 +1282,15 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -1263,11 +1282,15 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
1263 assert(extra.else_expr != 0);1282 assert(extra.else_expr != 0);
1264 n = extra.else_expr;1283 n = extra.else_expr;
1265 },1284 },
1266 .@"if", .@"for" => {1285 .@"if" => {
1267 const extra = tree.extraData(datas[n].rhs, Node.If);1286 const extra = tree.extraData(datas[n].rhs, Node.If);
1268 assert(extra.else_expr != 0);1287 assert(extra.else_expr != 0);
1269 n = extra.else_expr;1288 n = extra.else_expr;
1270 },1289 },
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 },
1271 .@"suspend" => {1294 .@"suspend" => {
1272 if (datas[n].lhs != 0) {1295 if (datas[n].lhs != 0) {
1273 n = datas[n].lhs;1296 n = datas[n].lhs;
...@@ -1916,26 +1939,28 @@ pub fn whileFull(tree: Ast, node: Node.Index) full.While {...@@ -1916,26 +1939,28 @@ pub fn whileFull(tree: Ast, node: Node.Index) full.While {
1916 });1939 });
1917}1940}
19181941
1919pub fn forSimple(tree: Ast, node: Node.Index) full.While {1942pub fn forSimple(tree: Ast, node: Node.Index) full.For {
1920 const data = tree.nodes.items(.data)[node];1943 const data = &tree.nodes.items(.data)[node];
1921 return tree.fullWhileComponents(.{1944 const inputs: *[1]Node.Index = &data.lhs;
1922 .while_token = tree.nodes.items(.main_token)[node],1945 return tree.fullForComponents(.{
1923 .cond_expr = data.lhs,1946 .for_token = tree.nodes.items(.main_token)[node],
1924 .cont_expr = 0,1947 .inputs = inputs[0..1],
1925 .then_expr = data.rhs,1948 .then_expr = data.rhs,
1926 .else_expr = 0,1949 .else_expr = 0,
1927 });1950 });
1928}1951}
19291952
1930pub fn forFull(tree: Ast, node: Node.Index) full.While {1953pub fn forFull(tree: Ast, node: Node.Index) full.For {
1931 const data = tree.nodes.items(.data)[node];1954 const data = tree.nodes.items(.data)[node];
1932 const extra = tree.extraData(data.rhs, Node.If);1955 const extra = @bitCast(Node.For, data.rhs);
1933 return tree.fullWhileComponents(.{1956 const inputs = tree.extra_data[data.lhs..][0..extra.inputs];
1934 .while_token = tree.nodes.items(.main_token)[node],1957 const then_expr = tree.extra_data[data.lhs + extra.inputs];
1935 .cond_expr = data.lhs,1958 const else_expr = if (extra.has_else) tree.extra_data[data.lhs + extra.inputs + 1] else 0;
1936 .cont_expr = 0,1959 return tree.fullForComponents(.{
1937 .then_expr = extra.then_expr,1960 .for_token = tree.nodes.items(.main_token)[node],
1938 .else_expr = extra.else_expr,1961 .inputs = inputs,
1962 .then_expr = then_expr,
1963 .else_expr = else_expr,
1939 });1964 });
1940}1965}
19411966
...@@ -2243,6 +2268,33 @@ fn fullWhileComponents(tree: Ast, info: full.While.Components) full.While {...@@ -2243,6 +2268,33 @@ fn fullWhileComponents(tree: Ast, info: full.While.Components) full.While {
2243 return result;2268 return result;
2244}2269}
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
2246fn fullCallComponents(tree: Ast, info: full.Call.Components) full.Call {2298fn fullCallComponents(tree: Ast, info: full.Call.Components) full.Call {
2247 const token_tags = tree.tokens.items(.tag);2299 const token_tags = tree.tokens.items(.tag);
2248 var result: full.Call = .{2300 var result: full.Call = .{
...@@ -2279,6 +2331,12 @@ pub fn fullWhile(tree: Ast, node: Node.Index) ?full.While {...@@ -2279,6 +2331,12 @@ pub fn fullWhile(tree: Ast, node: Node.Index) ?full.While {
2279 .while_simple => tree.whileSimple(node),2331 .while_simple => tree.whileSimple(node),
2280 .while_cont => tree.whileCont(node),2332 .while_cont => tree.whileCont(node),
2281 .@"while" => tree.whileFull(node),2333 .@"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]) {
2282 .for_simple => tree.forSimple(node),2340 .for_simple => tree.forSimple(node),
2283 .@"for" => tree.forFull(node),2341 .@"for" => tree.forFull(node),
2284 else => null,2342 else => null,
...@@ -2453,6 +2511,22 @@ pub const full = struct {...@@ -2453,6 +2511,22 @@ pub const full = struct {
2453 };2511 };
2454 };2512 };
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
2456 pub const ContainerField = struct {2530 pub const ContainerField = struct {
2457 comptime_token: ?TokenIndex,2531 comptime_token: ?TokenIndex,
2458 ast: Components,2532 ast: Components,
...@@ -2795,6 +2869,8 @@ pub const Error = struct {...@@ -2795,6 +2869,8 @@ pub const Error = struct {
2795 expected_comma_after_param,2869 expected_comma_after_param,
2796 expected_comma_after_initializer,2870 expected_comma_after_initializer,
2797 expected_comma_after_switch_prong,2871 expected_comma_after_switch_prong,
2872 expected_comma_after_for_operand,
2873 expected_comma_after_capture,
2798 expected_initializer,2874 expected_initializer,
2799 mismatched_binary_op_whitespace,2875 mismatched_binary_op_whitespace,
2800 invalid_ampersand_ampersand,2876 invalid_ampersand_ampersand,
...@@ -2802,6 +2878,8 @@ pub const Error = struct {...@@ -2802,6 +2878,8 @@ pub const Error = struct {
2802 expected_var_const,2878 expected_var_const,
2803 wrong_equal_var_decl,2879 wrong_equal_var_decl,
2804 var_const_decl,2880 var_const_decl,
2881 extra_for_capture,
2882 for_input_not_captured,
28052883
2806 zig_style_container,2884 zig_style_container,
2807 previous_field,2885 previous_field,
...@@ -3112,8 +3190,10 @@ pub const Node = struct {...@@ -3112,8 +3190,10 @@ pub const Node = struct {
3112 @"while",3190 @"while",
3113 /// `for (lhs) rhs`.3191 /// `for (lhs) rhs`.
3114 for_simple,3192 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]`.
3116 @"for",3194 @"for",
3195 /// `lhs..rhs`.
3196 for_range,
3117 /// `if (lhs) rhs`.3197 /// `if (lhs) rhs`.
3118 /// `if (lhs) |a| rhs`.3198 /// `if (lhs) |a| rhs`.
3119 if_simple,3199 if_simple,
...@@ -3369,6 +3449,11 @@ pub const Node = struct {...@@ -3369,6 +3449,11 @@ pub const Node = struct {
3369 then_expr: Index,3449 then_expr: Index,
3370 };3450 };
33713451
3452 pub const For = packed struct(u32) {
3453 inputs: u31,
3454 has_else: bool,
3455 };
3456
3372 pub const FnProtoOne = struct {3457 pub const FnProtoOne = struct {
3373 /// Populated if there is exactly 1 parameter. Otherwise there are 0 parameters.3458 /// Populated if there is exactly 1 parameter. Otherwise there are 0 parameters.
3374 param: Index,3459 param: Index,
lib/std/zig/Parse.zig+136-57
...@@ -104,6 +104,8 @@ fn warnMsg(p: *Parse, msg: Ast.Error) error{OutOfMemory}!void {...@@ -104,6 +104,8 @@ fn warnMsg(p: *Parse, msg: Ast.Error) error{OutOfMemory}!void {
104 .expected_comma_after_param,104 .expected_comma_after_param,
105 .expected_comma_after_initializer,105 .expected_comma_after_initializer,
106 .expected_comma_after_switch_prong,106 .expected_comma_after_switch_prong,
107 .expected_comma_after_for_operand,
108 .expected_comma_after_capture,
107 .expected_semi_or_else,109 .expected_semi_or_else,
108 .expected_semi_or_lbrace,110 .expected_semi_or_lbrace,
109 .expected_token,111 .expected_token,
...@@ -1149,22 +1151,18 @@ fn parseLoopStatement(p: *Parse) !Node.Index {...@@ -1149,22 +1151,18 @@ fn parseLoopStatement(p: *Parse) !Node.Index {
1149 return p.fail(.expected_inlinable);1151 return p.fail(.expected_inlinable);
1150}1152}
11511153
1152/// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
1153///
1154/// ForStatement1154/// ForStatement
1155/// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?1155/// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
1156/// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )1156/// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
1157fn parseForStatement(p: *Parse) !Node.Index {1157fn parseForStatement(p: *Parse) !Node.Index {
1158 const for_token = p.eatToken(.keyword_for) orelse return null_node;1158 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 required1160 const scratch_top = p.scratch.items.len;
1166 // inside while statements, even if there is an `else`.1161 defer p.scratch.shrinkRetainingCapacity(scratch_top);
1162 const inputs = try p.forPrefix();
1163
1167 var else_required = false;1164 var else_required = false;
1165 var seen_semicolon = false;
1168 const then_expr = blk: {1166 const then_expr = blk: {
1169 const block_expr = try p.parseBlockExpr();1167 const block_expr = try p.parseBlockExpr();
1170 if (block_expr != 0) break :blk block_expr;1168 if (block_expr != 0) break :blk block_expr;
...@@ -1173,39 +1171,40 @@ fn parseForStatement(p: *Parse) !Node.Index {...@@ -1173,39 +1171,40 @@ fn parseForStatement(p: *Parse) !Node.Index {
1173 return p.fail(.expected_block_or_assignment);1171 return p.fail(.expected_block_or_assignment);
1174 }1172 }
1175 if (p.eatToken(.semicolon)) |_| {1173 if (p.eatToken(.semicolon)) |_| {
1176 return p.addNode(.{1174 seen_semicolon = true;
1177 .tag = .for_simple,1175 break :blk assign_expr;
1178 .main_token = for_token,
1179 .data = .{
1180 .lhs = array_expr,
1181 .rhs = assign_expr,
1182 },
1183 });
1184 }1176 }
1185 else_required = true;1177 else_required = true;
1186 break :blk assign_expr;1178 break :blk assign_expr;
1187 };1179 };
1188 _ = p.eatToken(.keyword_else) orelse {1180 var has_else = false;
1189 if (else_required) {1181 if (!seen_semicolon and p.eatToken(.keyword_else) != null) {
1190 try p.warn(.expected_semi_or_else);1182 try p.scratch.append(p.gpa, then_expr);
1191 }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);
1192 return p.addNode(.{1188 return p.addNode(.{
1193 .tag = .for_simple,1189 .tag = .for_simple,
1194 .main_token = for_token,1190 .main_token = for_token,
1195 .data = .{1191 .data = .{
1196 .lhs = array_expr,1192 .lhs = p.scratch.items[scratch_top],
1197 .rhs = then_expr,1193 .rhs = then_expr,
1198 },1194 },
1199 });1195 });
1200 };1196 } else {
1197 if (else_required) try p.warn(.expected_semi_or_else);
1198 try p.scratch.append(p.gpa, then_expr);
1199 }
1201 return p.addNode(.{1200 return p.addNode(.{
1202 .tag = .@"for",1201 .tag = .@"for",
1203 .main_token = for_token,1202 .main_token = for_token,
1204 .data = .{1203 .data = .{
1205 .lhs = array_expr,1204 .lhs = (try p.listToSpan(p.scratch.items[scratch_top..])).start,
1206 .rhs = try p.addExtra(Node.If{1205 .rhs = @bitCast(u32, Node.For{
1207 .then_expr = then_expr,1206 .inputs = @intCast(u31, inputs),
1208 .else_expr = try p.expectStatement(false),1207 .has_else = has_else,
1209 }),1208 }),
1210 },1209 },
1211 });1210 });
...@@ -2056,42 +2055,118 @@ fn parseBlock(p: *Parse) !Node.Index {...@@ -2056,42 +2055,118 @@ fn parseBlock(p: *Parse) !Node.Index {
2056 }2055 }
2057}2056}
20582057
2059/// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
2060///
2061/// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?2058/// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
2062fn parseForExpr(p: *Parse) !Node.Index {2059fn parseForExpr(p: *Parse) !Node.Index {
2063 const for_token = p.eatToken(.keyword_for) orelse return null_node;2060 const for_token = p.eatToken(.keyword_for) orelse return null_node;
2064 _ = try p.expectToken(.l_paren);2061
2065 const array_expr = try p.expectExpr();2062 const scratch_top = p.scratch.items.len;
2066 _ = try p.expectToken(.r_paren);2063 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2067 const found_payload = try p.parsePtrIndexPayload();2064 const inputs = try p.forPrefix();
2068 if (found_payload == 0) try p.warn(.expected_loop_payload);
20692065
2070 const then_expr = try p.expectExpr();2066 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) {
2072 return p.addNode(.{2074 return p.addNode(.{
2073 .tag = .for_simple,2075 .tag = .for_simple,
2074 .main_token = for_token,2076 .main_token = for_token,
2075 .data = .{2077 .data = .{
2076 .lhs = array_expr,2078 .lhs = p.scratch.items[scratch_top],
2077 .rhs = then_expr,2079 .rhs = then_expr,
2078 },2080 },
2079 });2081 });
2080 };2082 } else {
2081 const else_expr = try p.expectExpr();2083 try p.scratch.append(p.gpa, then_expr);
2084 }
2082 return p.addNode(.{2085 return p.addNode(.{
2083 .tag = .@"for",2086 .tag = .@"for",
2084 .main_token = for_token,2087 .main_token = for_token,
2085 .data = .{2088 .data = .{
2086 .lhs = array_expr,2089 .lhs = (try p.listToSpan(p.scratch.items[scratch_top..])).start,
2087 .rhs = try p.addExtra(Node.If{2090 .rhs = @bitCast(u32, Node.For{
2088 .then_expr = then_expr,2091 .inputs = @intCast(u31, inputs),
2089 .else_expr = else_expr,2092 .has_else = has_else,
2090 }),2093 }),
2091 },2094 },
2092 });2095 });
2093}2096}
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
2095/// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?2170/// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
2096///2171///
2097/// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?2172/// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
...@@ -2752,37 +2827,41 @@ fn expectPrimaryTypeExpr(p: *Parse) !Node.Index {...@@ -2752,37 +2827,41 @@ fn expectPrimaryTypeExpr(p: *Parse) !Node.Index {
2752 return node;2827 return node;
2753}2828}
27542829
2755/// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
2756///
2757/// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?2830/// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
2758fn parseForTypeExpr(p: *Parse) !Node.Index {2831fn parseForTypeExpr(p: *Parse) !Node.Index {
2759 const for_token = p.eatToken(.keyword_for) orelse return null_node;2832 const for_token = p.eatToken(.keyword_for) orelse return null_node;
2760 _ = try p.expectToken(.l_paren);2833
2761 const array_expr = try p.expectExpr();2834 const scratch_top = p.scratch.items.len;
2762 _ = try p.expectToken(.r_paren);2835 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2763 const found_payload = try p.parsePtrIndexPayload();2836 const inputs = try p.forPrefix();
2764 if (found_payload == 0) try p.warn(.expected_loop_payload);
27652837
2766 const then_expr = try p.expectTypeExpr();2838 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) {
2768 return p.addNode(.{2846 return p.addNode(.{
2769 .tag = .for_simple,2847 .tag = .for_simple,
2770 .main_token = for_token,2848 .main_token = for_token,
2771 .data = .{2849 .data = .{
2772 .lhs = array_expr,2850 .lhs = p.scratch.items[scratch_top],
2773 .rhs = then_expr,2851 .rhs = then_expr,
2774 },2852 },
2775 });2853 });
2776 };2854 } else {
2777 const else_expr = try p.expectTypeExpr();2855 try p.scratch.append(p.gpa, then_expr);
2856 }
2778 return p.addNode(.{2857 return p.addNode(.{
2779 .tag = .@"for",2858 .tag = .@"for",
2780 .main_token = for_token,2859 .main_token = for_token,
2781 .data = .{2860 .data = .{
2782 .lhs = array_expr,2861 .lhs = (try p.listToSpan(p.scratch.items[scratch_top..])).start,
2783 .rhs = try p.addExtra(Node.If{2862 .rhs = @bitCast(u32, Node.For{
2784 .then_expr = then_expr,2863 .inputs = @intCast(u31, inputs),
2785 .else_expr = else_expr,2864 .has_else = has_else,
2786 }),2865 }),
2787 },2866 },
2788 });2867 });
lib/std/zig/parser_test.zig+38-11
...@@ -3457,11 +3457,11 @@ test "zig fmt: for" {...@@ -3457,11 +3457,11 @@ test "zig fmt: for" {
3457 \\ for (a) |*v|3457 \\ for (a) |*v|
3458 \\ continue;3458 \\ continue;
3459 \\3459 \\
3460 \\ for (a) |v, i| {3460 \\ for (a, 0..) |v, i| {
3461 \\ continue;3461 \\ continue;
3462 \\ }3462 \\ }
3463 \\3463 \\
3464 \\ for (a) |v, i|3464 \\ for (a, 0..) |v, i|
3465 \\ continue;3465 \\ continue;
3466 \\3466 \\
3467 \\ for (a) |b| switch (b) {3467 \\ for (a) |b| switch (b) {
...@@ -3469,17 +3469,24 @@ test "zig fmt: for" {...@@ -3469,17 +3469,24 @@ test "zig fmt: for" {
3469 \\ d => {},3469 \\ d => {},
3470 \\ };3470 \\ };
3471 \\3471 \\
3472 \\ const res = for (a) |v, i| {3472 \\ const res = for (a, 0..) |v, i| {
3473 \\ break v;3473 \\ break v;
3474 \\ } else {3474 \\ } else {
3475 \\ unreachable;3475 \\ unreachable;
3476 \\ };3476 \\ };
3477 \\3477 \\
3478 \\ var num: usize = 0;3478 \\ var num: usize = 0;
3479 \\ inline for (a) |v, i| {3479 \\ inline for (a, 0..1) |v, i| {
3480 \\ num += v;3480 \\ num += v;
3481 \\ num += i;3481 \\ num += i;
3482 \\ }3482 \\ }
3483 \\
3484 \\ for (a, b) |
3485 \\ long_name,
3486 \\ another_long_name,
3487 \\ | {
3488 \\ continue;
3489 \\ }
3483 \\}3490 \\}
3484 \\3491 \\
3485 );3492 );
...@@ -3499,6 +3506,26 @@ test "zig fmt: for" {...@@ -3499,6 +3506,26 @@ test "zig fmt: for" {
3499 \\}3506 \\}
3500 \\3507 \\
3501 );3508 );
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 );
3502}3529}
35033530
3504test "zig fmt: for if" {3531test "zig fmt: for if" {
...@@ -4358,7 +4385,7 @@ test "zig fmt: hex literals with underscore separators" {...@@ -4358,7 +4385,7 @@ test "zig fmt: hex literals with underscore separators" {
4358 try testTransform(4385 try testTransform(
4359 \\pub fn orMask(a: [ 1_000 ]u64, b: [ 1_000] u64) [1_000]u64 {4386 \\pub fn orMask(a: [ 1_000 ]u64, b: [ 1_000] u64) [1_000]u64 {
4360 \\ var c: [1_000]u64 = [1]u64{ 0xFFFF_FFFF_FFFF_FFFF}**1_000;4387 \\ 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| {
4362 \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;4389 \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;
4363 \\ }4390 \\ }
4364 \\ return c;4391 \\ return c;
...@@ -4368,7 +4395,7 @@ test "zig fmt: hex literals with underscore separators" {...@@ -4368,7 +4395,7 @@ test "zig fmt: hex literals with underscore separators" {
4368 ,4395 ,
4369 \\pub fn orMask(a: [1_000]u64, b: [1_000]u64) [1_000]u64 {4396 \\pub fn orMask(a: [1_000]u64, b: [1_000]u64) [1_000]u64 {
4370 \\ var c: [1_000]u64 = [1]u64{0xFFFF_FFFF_FFFF_FFFF} ** 1_000;4397 \\ 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| {
4372 \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;4399 \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;
4373 \\ }4400 \\ }
4374 \\ return c;4401 \\ return c;
...@@ -4880,10 +4907,10 @@ test "zig fmt: remove trailing whitespace after doc comment" {...@@ -4880,10 +4907,10 @@ test "zig fmt: remove trailing whitespace after doc comment" {
4880test "zig fmt: for loop with ptr payload and index" {4907test "zig fmt: for loop with ptr payload and index" {
4881 try testCanonical(4908 try testCanonical(
4882 \\test {4909 \\test {
4883 \\ for (self.entries.items) |*item, i| {}4910 \\ for (self.entries.items, 0..) |*item, i| {}
4884 \\ for (self.entries.items) |*item, i|4911 \\ for (self.entries.items, 0..) |*item, i|
4885 \\ a = b;4912 \\ a = b;
4886 \\ for (self.entries.items) |*item, i| a = b;4913 \\ for (self.entries.items, 0..) |*item, i| a = b;
4887 \\}4914 \\}
4888 \\4915 \\
4889 );4916 );
...@@ -5471,7 +5498,7 @@ test "zig fmt: canonicalize symbols (primitive types)" {...@@ -5471,7 +5498,7 @@ test "zig fmt: canonicalize symbols (primitive types)" {
5471 \\ _ = @"void": {5498 \\ _ = @"void": {
5472 \\ break :@"void";5499 \\ break :@"void";
5473 \\ };5500 \\ };
5474 \\ for ("hi") |@"u3", @"i4"| {5501 \\ for ("hi", 0..) |@"u3", @"i4"| {
5475 \\ _ = @"u3";5502 \\ _ = @"u3";
5476 \\ _ = @"i4";5503 \\ _ = @"i4";
5477 \\ }5504 \\ }
...@@ -5523,7 +5550,7 @@ test "zig fmt: canonicalize symbols (primitive types)" {...@@ -5523,7 +5550,7 @@ test "zig fmt: canonicalize symbols (primitive types)" {
5523 \\ _ = void: {5550 \\ _ = void: {
5524 \\ break :void;5551 \\ break :void;
5525 \\ };5552 \\ };
5526 \\ for ("hi") |@"u3", @"i4"| {5553 \\ for ("hi", 0..) |@"u3", @"i4"| {
5527 \\ _ = @"u3";5554 \\ _ = @"u3";
5528 \\ _ = @"i4";5555 \\ _ = @"i4";
5529 \\ }5556 \\ }
lib/std/zig/render.zig+135-21
...@@ -353,6 +353,16 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -353,6 +353,16 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
353 try renderToken(ais, tree, main_tokens[node], .none);353 try renderToken(ais, tree, main_tokens[node], .none);
354 return renderExpression(gpa, ais, tree, infix.rhs, space);354 return renderExpression(gpa, ais, tree, infix.rhs, space);
355 },355 },
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
357 .add,367 .add,
358 .add_wrap,368 .add_wrap,
...@@ -694,9 +704,11 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -694,9 +704,11 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
694 .while_simple,704 .while_simple,
695 .while_cont,705 .while_cont,
696 .@"while",706 .@"while",
707 => return renderWhile(gpa, ais, tree, tree.fullWhile(node).?, space),
708
697 .for_simple,709 .for_simple,
698 .@"for",710 .@"for",
699 => return renderWhile(gpa, ais, tree, tree.fullWhile(node).?, space),711 => return renderFor(gpa, ais, tree, tree.fullFor(node).?, space),
700712
701 .if_simple,713 .if_simple,
702 .@"if",714 .@"if",
...@@ -1054,10 +1066,9 @@ fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: S...@@ -1054,10 +1066,9 @@ fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: S
1054 }, space);1066 }, space);
1055}1067}
10561068
1057/// Note that this function is additionally used to render if and for expressions, with1069/// Note that this function is additionally used to render if expressions, with
1058/// respective values set to null.1070/// respective values set to null.
1059fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, space: Space) Error!void {1071fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, space: Space) Error!void {
1060 const node_tags = tree.nodes.items(.tag);
1061 const token_tags = tree.tokens.items(.tag);1072 const token_tags = tree.tokens.items(.tag);
10621073
1063 if (while_node.label_token) |label| {1074 if (while_node.label_token) |label| {
...@@ -1108,9 +1119,34 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,...@@ -1108,9 +1119,34 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
1108 last_prefix_token = tree.lastToken(while_node.ast.cont_expr) + 1; // rparen1119 last_prefix_token = tree.lastToken(while_node.ast.cont_expr) + 1; // rparen
1109 }1120 }
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]);
1112 const indent_then_expr = !then_expr_is_block and1148 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));
1114 if (indent_then_expr or (then_expr_is_block and ais.isLineOverIndented())) {1150 if (indent_then_expr or (then_expr_is_block and ais.isLineOverIndented())) {
1115 ais.pushIndentNextLine();1151 ais.pushIndentNextLine();
1116 try renderToken(ais, tree, last_prefix_token, .newline);1152 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,...@@ -1119,43 +1155,113 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
1119 try renderToken(ais, tree, last_prefix_token, .space);1155 try renderToken(ais, tree, last_prefix_token, .space);
1120 }1156 }
11211157
1122 if (while_node.ast.else_expr != 0) {1158 if (else_expr != 0) {
1123 if (indent_then_expr) {1159 if (indent_then_expr) {
1124 ais.pushIndent();1160 ais.pushIndent();
1125 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .newline);1161 try renderExpression(gpa, ais, tree, then_expr, .newline);
1126 ais.popIndent();1162 ais.popIndent();
1127 } else {1163 } else {
1128 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space);1164 try renderExpression(gpa, ais, tree, then_expr, .space);
1129 }1165 }
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| {1169 if (maybe_error_token) |error_token| {
1134 try renderToken(ais, tree, while_node.else_token, .space); // else1170 try renderToken(ais, tree, else_token, .space); // else
1135 try renderToken(ais, tree, error_token - 1, .none); // |1171 try renderToken(ais, tree, error_token - 1, .none); // |
1136 try renderIdentifier(ais, tree, error_token, .none, .preserve_when_shadowing); // identifier1172 try renderIdentifier(ais, tree, error_token, .none, .preserve_when_shadowing); // identifier
1137 last_else_token = error_token + 1; // |1173 last_else_token = error_token + 1; // |
1138 }1174 }
11391175
1140 const indent_else_expr = indent_then_expr and1176 const indent_else_expr = indent_then_expr and
1141 !nodeIsBlock(node_tags[while_node.ast.else_expr]) and1177 !nodeIsBlock(node_tags[else_expr]) and
1142 !nodeIsIfForWhileSwitch(node_tags[while_node.ast.else_expr]);1178 !nodeIsIfForWhileSwitch(node_tags[else_expr]);
1143 if (indent_else_expr) {1179 if (indent_else_expr) {
1144 ais.pushIndentNextLine();1180 ais.pushIndentNextLine();
1145 try renderToken(ais, tree, last_else_token, .newline);1181 try renderToken(ais, tree, last_else_token, .newline);
1146 ais.popIndent();1182 ais.popIndent();
1147 try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space);1183 try renderExpressionIndented(gpa, ais, tree, else_expr, space);
1148 } else {1184 } else {
1149 try renderToken(ais, tree, last_else_token, .space);1185 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);
1151 }1187 }
1152 } else {1188 } else {
1153 if (indent_then_expr) {1189 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);
1155 } else {1191 } 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 }
1157 }1251 }
1158 }1252 }
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 );
1159}1265}
11601266
1161fn renderContainerField(1267fn renderContainerField(
...@@ -2206,15 +2312,23 @@ fn renderCall(...@@ -2206,15 +2312,23 @@ fn renderCall(
2206 call: Ast.full.Call,2312 call: Ast.full.Call,
2207 space: Space,2313 space: Space,
2208) Error!void {2314) Error!void {
2209 const token_tags = tree.tokens.items(.tag);
2210
2211 if (call.async_token) |async_token| {2315 if (call.async_token) |async_token| {
2212 try renderToken(ais, tree, async_token, .space);2316 try renderToken(ais, tree, async_token, .space);
2213 }2317 }
2214 try renderExpression(gpa, ais, tree, call.ast.fn_expr, .none);2318 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;
2218 if (params.len == 0) {2332 if (params.len == 0) {
2219 ais.pushIndentNextLine();2333 ais.pushIndentNextLine();
2220 try renderToken(ais, tree, lparen, .none);2334 try renderToken(ais, tree, lparen, .none);