| author | |
| committer | |
| log | 1b7055b514955f1787937b2ef6097d2e0663da74 |
| tree | f4a49c1740e6c9bbae31250d61b70a4d5e06173f |
| parent | 5e7b09ce9fbc95ec9fb9e277d262b9b5a5aa1917 |
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 | }, | ||
| 423 | 435 | ||
| 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, |
| 573 | 586 | ||
| ... | @@ -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, |
| 847 | 860 | ||
| 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 | } |
| 1918 | 1941 | ||
| 1919 | pub fn forSimple(tree: Ast, node: Node.Index) full.While { | 1942 | pub 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 | } |
| 1929 | 1952 | ||
| 1930 | pub fn forFull(tree: Ast, node: Node.Index) full.While { | 1953 | pub 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 | } |
| 1941 | 1966 | ||
| ... | @@ -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 | } |
| 2245 | 2270 | ||
| 2271 | fn 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 | |||
| 2246 | fn fullCallComponents(tree: Ast, info: full.Call.Components) full.Call { | 2298 | fn 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 | |||
| 2338 | pub 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 | }; |
| 2455 | 2513 | ||
| 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, | ||
| 2805 | 2883 | ||
| 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 | }; |
| 3371 | 3451 | ||
| 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 | } |
| 1151 | 1153 | ||
| 1152 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | ||
| 1153 | /// | ||
| 1154 | /// ForStatement | 1154 | /// 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 ) |
| 1157 | fn parseForStatement(p: *Parse) !Node.Index { | 1157 | fn 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); | ||
| 1164 | 1159 | ||
| 1165 | // TODO propose to change the syntax so that semicolons are always required | 1160 | 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 | } |
| 2058 | 2057 | ||
| 2059 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | ||
| 2060 | /// | ||
| 2061 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? | 2058 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? |
| 2062 | fn parseForExpr(p: *Parse) !Node.Index { | 2059 | fn 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); | ||
| 2069 | 2065 | ||
| 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 | } |
| 2094 | 2097 | ||
| 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 | ||
| 2103 | fn 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 | } |
| 2754 | 2829 | ||
| 2755 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | ||
| 2756 | /// | ||
| 2757 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? | 2830 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? |
| 2758 | fn parseForTypeExpr(p: *Parse) !Node.Index { | 2831 | fn 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); | ||
| 2765 | 2837 | ||
| 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 | } |
| 3503 | 3530 | ||
| 3504 | test "zig fmt: for if" { | 3531 | test "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" { |
| 4880 | test "zig fmt: for loop with ptr payload and index" { | 4907 | test "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 | }, | ||
| 356 | 366 | ||
| 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), |
| 700 | 712 | ||
| 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 | } |
| 1056 | 1068 | ||
| 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 |
| 1058 | /// respective values set to null. | 1070 | /// respective values set to null. |
| 1059 | fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, space: Space) Error!void { | 1071 | fn 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); |
| 1062 | 1073 | ||
| 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; // rparen | 1119 | last_prefix_token = tree.lastToken(while_node.ast.cont_expr) + 1; // rparen |
| 1109 | } | 1120 | } |
| 1110 | 1121 | ||
| 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 | |||
| 1135 | fn 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 and | 1148 | 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 | } |
| 1121 | 1157 | ||
| 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 | } |
| 1130 | 1166 | ||
| 1131 | var last_else_token = while_node.else_token; | 1167 | var last_else_token = else_token; |
| 1132 | 1168 | ||
| 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); // else | 1170 | 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); // identifier | 1172 | 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 | } |
| 1139 | 1175 | ||
| 1140 | const indent_else_expr = indent_then_expr and | 1176 | const indent_else_expr = indent_then_expr and |
| 1141 | !nodeIsBlock(node_tags[while_node.ast.else_expr]) and | 1177 | !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 | |||
| 1197 | fn 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 | } |
| 1160 | 1266 | ||
| 1161 | fn renderContainerField( | 1267 | fn 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 | |||
| 2322 | fn 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); | ||
| 2215 | 2331 | ||
| 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); |