authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-20 19:18:14-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-20 19:18:14-04:00
log897f23f20ff30e379e8d320cfd0013cb48dcc5b2
treef4dacd6935dd023014c41f45a4632ed95262374a
parent7c2c0e36f8378d8efaf64d859bdffb91007db82a

stage2 parser: split off some SuffixOp AST nodes into separate tags

These SuffixOp nodes have their own ast.Node tags now: * ArrayInitializer * ArrayInitializerDot * StructInitializer * StructInitializerDot Their sub-expression lists are general-purpose-allocator allocated and then copied into the arena after completion of parsing. throughput: 72.9 MiB/s => 74.4 MiB/s maxrss: 68 KB => 72 KB The API is also nicer since the sub expression lists are now flat arrays instead of singly linked lists.

3 files changed, 523 insertions(+), 354 deletions(-)

lib/std/zig/ast.zig+134-37
...@@ -429,6 +429,14 @@ pub const Node = struct {...@@ -429,6 +429,14 @@ pub const Node = struct {
429 InfixOp,429 InfixOp,
430 PrefixOp,430 PrefixOp,
431 SuffixOp,431 SuffixOp,
432 /// This is a suffix operation but to save memory we have a dedicated Node id for it.
433 ArrayInitializer,
434 /// ArrayInitializer but with `.` instead of a left-hand-side operand.
435 ArrayInitializerDot,
436 /// This is a suffix operation but to save memory we have a dedicated Node id for it.
437 StructInitializer,
438 /// StructInitializer but with `.` instead of a left-hand-side operand.
439 StructInitializerDot,
432440
433 // Control flow441 // Control flow
434 Switch,442 Switch,
...@@ -1961,28 +1969,141 @@ pub const Node = struct {...@@ -1961,28 +1969,141 @@ pub const Node = struct {
1961 }1969 }
1962 };1970 };
19631971
1972 pub const ArrayInitializer = struct {
1973 base: Node = Node{ .id = .ArrayInitializer },
1974 rtoken: TokenIndex,
1975 lhs: *Node,
1976 list: []*Node,
1977
1978 pub fn iterate(self: *const ArrayInitializer) Node.Iterator {
1979 return .{ .parent_node = &self.base, .index = 0, .node = null };
1980 }
1981
1982 pub fn iterateNext(self: *const ArrayInitializer, it: *Node.Iterator) ?*Node {
1983 var i = it.index;
1984 it.index += 1;
1985
1986 if (i < 1) return self.lhs;
1987 i -= 1;
1988
1989 if (i < self.list.len) return self.list[i];
1990 i -= self.list.len;
1991
1992 return null;
1993 }
1994
1995 pub fn firstToken(self: *const ArrayInitializer) TokenIndex {
1996 return self.lhs.firstToken();
1997 }
1998
1999 pub fn lastToken(self: *const ArrayInitializer) TokenIndex {
2000 return self.rtoken;
2001 }
2002 };
2003
2004 pub const ArrayInitializerDot = struct {
2005 base: Node = Node{ .id = .ArrayInitializerDot },
2006 dot: TokenIndex,
2007 rtoken: TokenIndex,
2008 list: []*Node,
2009
2010 pub fn iterate(self: *const ArrayInitializerDot) Node.Iterator {
2011 return .{ .parent_node = &self.base, .index = 0, .node = null };
2012 }
2013
2014 pub fn iterateNext(self: *const ArrayInitializerDot, it: *Node.Iterator) ?*Node {
2015 var i = it.index;
2016 it.index += 1;
2017
2018 if (i < self.list.len) return self.list[i];
2019 i -= self.list.len;
2020
2021 return null;
2022 }
2023
2024 pub fn firstToken(self: *const ArrayInitializerDot) TokenIndex {
2025 return self.dot;
2026 }
2027
2028 pub fn lastToken(self: *const ArrayInitializerDot) TokenIndex {
2029 return self.rtoken;
2030 }
2031 };
2032
2033 pub const StructInitializer = struct {
2034 base: Node = Node{ .id = .StructInitializer },
2035 rtoken: TokenIndex,
2036 lhs: *Node,
2037 list: []*Node,
2038
2039 pub fn iterate(self: *const StructInitializer) Node.Iterator {
2040 return .{ .parent_node = &self.base, .index = 0, .node = null };
2041 }
2042
2043 pub fn iterateNext(self: *const StructInitializer, it: *Node.Iterator) ?*Node {
2044 var i = it.index;
2045 it.index += 1;
2046
2047 if (i < 1) return self.lhs;
2048 i -= 1;
2049
2050 if (i < self.list.len) return self.list[i];
2051 i -= self.list.len;
2052
2053 return null;
2054 }
2055
2056 pub fn firstToken(self: *const StructInitializer) TokenIndex {
2057 return self.lhs.firstToken();
2058 }
2059
2060 pub fn lastToken(self: *const StructInitializer) TokenIndex {
2061 return self.rtoken;
2062 }
2063 };
2064
2065 pub const StructInitializerDot = struct {
2066 base: Node = Node{ .id = .StructInitializerDot },
2067 dot: TokenIndex,
2068 rtoken: TokenIndex,
2069 list: []*Node,
2070
2071 pub fn iterate(self: *const StructInitializerDot) Node.Iterator {
2072 return .{ .parent_node = &self.base, .index = 0, .node = null };
2073 }
2074
2075 pub fn iterateNext(self: *const StructInitializerDot, it: *Node.Iterator) ?*Node {
2076 var i = it.index;
2077 it.index += 1;
2078
2079 if (i < self.list.len) return self.list[i];
2080 i -= self.list.len;
2081
2082 return null;
2083 }
2084
2085 pub fn firstToken(self: *const StructInitializerDot) TokenIndex {
2086 return self.dot;
2087 }
2088
2089 pub fn lastToken(self: *const StructInitializerDot) TokenIndex {
2090 return self.rtoken;
2091 }
2092 };
2093
1964 pub const SuffixOp = struct {2094 pub const SuffixOp = struct {
1965 base: Node = Node{ .id = .SuffixOp },2095 base: Node = Node{ .id = .SuffixOp },
1966 lhs: Lhs,
1967 op: Op,2096 op: Op,
2097 lhs: *Node,
1968 rtoken: TokenIndex,2098 rtoken: TokenIndex,
19692099
1970 pub const Lhs = union(enum) {
1971 node: *Node,
1972 dot: TokenIndex,
1973 };
1974
1975 pub const Op = union(enum) {2100 pub const Op = union(enum) {
1976 Call: Call,2101 Call: Call,
1977 ArrayAccess: *Node,2102 ArrayAccess: *Node,
1978 Slice: Slice,2103 Slice: Slice,
1979 ArrayInitializer: InitList,
1980 StructInitializer: InitList,
1981 Deref,2104 Deref,
1982 UnwrapOptional,2105 UnwrapOptional,
19832106
1984 pub const InitList = LinkedList(*Node);
1985
1986 pub const Call = struct {2107 pub const Call = struct {
1987 params: ParamList,2108 params: ParamList,
1988 async_token: ?TokenIndex,2109 async_token: ?TokenIndex,
...@@ -2001,8 +2122,6 @@ pub const Node = struct {...@@ -2001,8 +2122,6 @@ pub const Node = struct {
2001 return .{ .parent_node = &self.base, .index = 0,2122 return .{ .parent_node = &self.base, .index = 0,
2002 .node = switch(self.op) {2123 .node = switch(self.op) {
2003 .Call => |call| call.params.first,2124 .Call => |call| call.params.first,
2004 .ArrayInitializer => |ai| ai.first,
2005 .StructInitializer => |si| si.first,
2006 else => null,2125 else => null,
2007 },2126 },
2008 };2127 };
...@@ -2012,13 +2131,8 @@ pub const Node = struct {...@@ -2012,13 +2131,8 @@ pub const Node = struct {
2012 var i = it.index;2131 var i = it.index;
2013 it.index += 1;2132 it.index += 1;
20142133
2015 switch (self.lhs) {2134 if (i == 0) return self.lhs;
2016 .node => |node| {2135 i -= 1;
2017 if (i == 0) return node;
2018 i -= 1;
2019 },
2020 .dot => {},
2021 }
20222136
2023 switch (self.op) {2137 switch (self.op) {
2024 .Call => |call_info| {2138 .Call => |call_info| {
...@@ -2045,20 +2159,6 @@ pub const Node = struct {...@@ -2045,20 +2159,6 @@ pub const Node = struct {
2045 i -= 1;2159 i -= 1;
2046 }2160 }
2047 },2161 },
2048 .ArrayInitializer => |exprs| {
2049 if (it.node) |child| {
2050 it.index -= 1;
2051 it.node = child.next;
2052 return child.data;
2053 }
2054 },
2055 .StructInitializer => |fields| {
2056 if (it.node) |child| {
2057 it.index -= 1;
2058 it.node = child.next;
2059 return child.data;
2060 }
2061 },
2062 .UnwrapOptional,2162 .UnwrapOptional,
2063 .Deref,2163 .Deref,
2064 => {},2164 => {},
...@@ -2072,10 +2172,7 @@ pub const Node = struct {...@@ -2072,10 +2172,7 @@ pub const Node = struct {
2072 .Call => |*call_info| if (call_info.async_token) |async_token| return async_token,2172 .Call => |*call_info| if (call_info.async_token) |async_token| return async_token,
2073 else => {},2173 else => {},
2074 }2174 }
2075 switch (self.lhs) {2175 return self.lhs.firstToken();
2076 .node => |node| return node.firstToken(),
2077 .dot => |dot| return dot,
2078 }
2079 }2176 }
20802177
2081 pub fn lastToken(self: *const SuffixOp) TokenIndex {2178 pub fn lastToken(self: *const SuffixOp) TokenIndex {
lib/std/zig/parse.zig+90-38
...@@ -1287,50 +1287,105 @@ const Parser = struct {...@@ -1287,50 +1287,105 @@ const Parser = struct {
12871287
1288 /// CurlySuffixExpr <- TypeExpr InitList?1288 /// CurlySuffixExpr <- TypeExpr InitList?
1289 fn parseCurlySuffixExpr(p: *Parser) !?*Node {1289 fn parseCurlySuffixExpr(p: *Parser) !?*Node {
1290 const type_expr = (try p.parseTypeExpr()) orelse return null;1290 const lhs = (try p.parseTypeExpr()) orelse return null;
1291 const suffix_op = (try p.parseInitList()) orelse return type_expr;1291 const suffix_op = (try p.parseInitList(lhs)) orelse return lhs;
1292 suffix_op.lhs.node = type_expr;1292 return suffix_op;
1293 return &suffix_op.base;
1294 }1293 }
12951294
1296 /// InitList1295 /// InitList
1297 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE1296 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
1298 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE1297 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
1299 /// / LBRACE RBRACE1298 /// / LBRACE RBRACE
1300 fn parseInitList(p: *Parser) !?*Node.SuffixOp {1299 fn parseInitList(p: *Parser, lhs: *Node) !?*Node {
1301 const lbrace = p.eatToken(.LBrace) orelse return null;1300 const lbrace = p.eatToken(.LBrace) orelse return null;
1302 var init_list = Node.SuffixOp.Op.InitList{};1301 var init_list = std.ArrayList(*Node).init(p.gpa);
1303 var init_list_it = &init_list.first;1302 defer init_list.deinit();
13041303
1305 const op: Node.SuffixOp.Op = blk: {1304 if (try p.parseFieldInit()) |field_init| {
1306 if (try p.parseFieldInit()) |field_init| {1305 try init_list.append(field_init);
1307 init_list_it = try p.llpush(*Node, init_list_it, field_init);1306 while (p.eatToken(.Comma)) |_| {
1308 while (p.eatToken(.Comma)) |_| {1307 const next = (try p.parseFieldInit()) orelse break;
1309 const next = (try p.parseFieldInit()) orelse break;1308 try init_list.append(next);
1310 init_list_it = try p.llpush(*Node, init_list_it, next);
1311 }
1312 break :blk .{ .StructInitializer = init_list };
1313 }1309 }
1310 const node = try p.arena.allocator.create(Node.StructInitializer);
1311 node.* = .{
1312 .lhs = lhs,
1313 .rtoken = try p.expectToken(.RBrace),
1314 .list = try p.arena.allocator.dupe(*Node, init_list.items),
1315 };
1316 return &node.base;
1317 }
13141318
1315 if (try p.parseExpr()) |expr| {1319 if (try p.parseExpr()) |expr| {
1316 init_list_it = try p.llpush(*Node, init_list_it, expr);1320 try init_list.append(expr);
1317 while (p.eatToken(.Comma)) |_| {1321 while (p.eatToken(.Comma)) |_| {
1318 const next = (try p.parseExpr()) orelse break;1322 const next = (try p.parseExpr()) orelse break;
1319 init_list_it = try p.llpush(*Node, init_list_it, next);1323 try init_list.append(next);
1320 }
1321 break :blk .{ .ArrayInitializer = init_list };
1322 }1324 }
1325 const node = try p.arena.allocator.create(Node.ArrayInitializer);
1326 node.* = .{
1327 .lhs = lhs,
1328 .rtoken = try p.expectToken(.RBrace),
1329 .list = try p.arena.allocator.dupe(*Node, init_list.items),
1330 };
1331 return &node.base;
1332 }
13231333
1324 break :blk .{ .StructInitializer = init_list };1334 const node = try p.arena.allocator.create(Node.StructInitializer);
1335 node.* = .{
1336 .lhs = lhs,
1337 .rtoken = try p.expectToken(.RBrace),
1338 .list = &[0]*Node{},
1325 };1339 };
1340 return &node.base;
1341 }
13261342
1327 const node = try p.arena.allocator.create(Node.SuffixOp);1343 /// InitList
1344 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
1345 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
1346 /// / LBRACE RBRACE
1347 fn parseAnonInitList(p: *Parser, dot: TokenIndex) !?*Node {
1348 const lbrace = p.eatToken(.LBrace) orelse return null;
1349 var init_list = std.ArrayList(*Node).init(p.gpa);
1350 defer init_list.deinit();
1351
1352 if (try p.parseFieldInit()) |field_init| {
1353 try init_list.append(field_init);
1354 while (p.eatToken(.Comma)) |_| {
1355 const next = (try p.parseFieldInit()) orelse break;
1356 try init_list.append(next);
1357 }
1358 const node = try p.arena.allocator.create(Node.StructInitializerDot);
1359 node.* = .{
1360 .dot = dot,
1361 .rtoken = try p.expectToken(.RBrace),
1362 .list = try p.arena.allocator.dupe(*Node, init_list.items),
1363 };
1364 return &node.base;
1365 }
1366
1367 if (try p.parseExpr()) |expr| {
1368 try init_list.append(expr);
1369 while (p.eatToken(.Comma)) |_| {
1370 const next = (try p.parseExpr()) orelse break;
1371 try init_list.append(next);
1372 }
1373 const node = try p.arena.allocator.create(Node.ArrayInitializerDot);
1374 node.* = .{
1375 .dot = dot,
1376 .rtoken = try p.expectToken(.RBrace),
1377 .list = try p.arena.allocator.dupe(*Node, init_list.items),
1378 };
1379 return &node.base;
1380 }
1381
1382 const node = try p.arena.allocator.create(Node.StructInitializerDot);
1328 node.* = .{1383 node.* = .{
1329 .lhs = .{ .node = undefined }, // set by caller1384 .dot = dot,
1330 .op = op,
1331 .rtoken = try p.expectToken(.RBrace),1385 .rtoken = try p.expectToken(.RBrace),
1386 .list = &[0]*Node{},
1332 };1387 };
1333 return node;1388 return &node.base;
1334 }1389 }
13351390
1336 /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr1391 /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr
...@@ -1378,7 +1433,7 @@ const Parser = struct {...@@ -1378,7 +1433,7 @@ const Parser = struct {
13781433
1379 while (try p.parseSuffixOp()) |node| {1434 while (try p.parseSuffixOp()) |node| {
1380 switch (node.id) {1435 switch (node.id) {
1381 .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{ .node = res },1436 .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
1382 .InfixOp => node.cast(Node.InfixOp).?.lhs = res,1437 .InfixOp => node.cast(Node.InfixOp).?.lhs = res,
1383 else => unreachable,1438 else => unreachable,
1384 }1439 }
...@@ -1394,7 +1449,7 @@ const Parser = struct {...@@ -1394,7 +1449,7 @@ const Parser = struct {
1394 };1449 };
1395 const node = try p.arena.allocator.create(Node.SuffixOp);1450 const node = try p.arena.allocator.create(Node.SuffixOp);
1396 node.* = .{1451 node.* = .{
1397 .lhs = .{ .node = res },1452 .lhs = res,
1398 .op = .{1453 .op = .{
1399 .Call = .{1454 .Call = .{
1400 .params = params.list,1455 .params = params.list,
...@@ -1411,7 +1466,7 @@ const Parser = struct {...@@ -1411,7 +1466,7 @@ const Parser = struct {
1411 while (true) {1466 while (true) {
1412 if (try p.parseSuffixOp()) |node| {1467 if (try p.parseSuffixOp()) |node| {
1413 switch (node.id) {1468 switch (node.id) {
1414 .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{ .node = res },1469 .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
1415 .InfixOp => node.cast(Node.InfixOp).?.lhs = res,1470 .InfixOp => node.cast(Node.InfixOp).?.lhs = res,
1416 else => unreachable,1471 else => unreachable,
1417 }1472 }
...@@ -1421,7 +1476,7 @@ const Parser = struct {...@@ -1421,7 +1476,7 @@ const Parser = struct {
1421 if (try p.parseFnCallArguments()) |params| {1476 if (try p.parseFnCallArguments()) |params| {
1422 const call = try p.arena.allocator.create(Node.SuffixOp);1477 const call = try p.arena.allocator.create(Node.SuffixOp);
1423 call.* = .{1478 call.* = .{
1424 .lhs = .{ .node = res },1479 .lhs = res,
1425 .op = .{1480 .op = .{
1426 .Call = .{1481 .Call = .{
1427 .params = params.list,1482 .params = params.list,
...@@ -1757,10 +1812,8 @@ const Parser = struct {...@@ -1757,10 +1812,8 @@ const Parser = struct {
1757 return &node.base;1812 return &node.base;
1758 }1813 }
17591814
1760 // anon container literal1815 if (try p.parseAnonInitList(dot)) |node| {
1761 if (try p.parseInitList()) |node| {1816 return node;
1762 node.lhs = .{ .dot = dot };
1763 return &node.base;
1764 }1817 }
17651818
1766 p.putBackToken(dot);1819 p.putBackToken(dot);
...@@ -3282,8 +3335,7 @@ const Parser = struct {...@@ -3282,8 +3335,7 @@ const Parser = struct {
3282 }3335 }
32833336
3284 fn expectToken(p: *Parser, id: Token.Id) Error!TokenIndex {3337 fn expectToken(p: *Parser, id: Token.Id) Error!TokenIndex {
3285 return (try p.expectTokenRecoverable(id)) orelse3338 return (try p.expectTokenRecoverable(id)) orelse error.ParseError;
3286 error.ParseError;
3287 }3339 }
32883340
3289 fn expectTokenRecoverable(p: *Parser, id: Token.Id) !?TokenIndex {3341 fn expectTokenRecoverable(p: *Parser, id: Token.Id) !?TokenIndex {
lib/std/zig/render.zig+299-279
...@@ -611,6 +611,298 @@ fn renderExpression(...@@ -611,6 +611,298 @@ fn renderExpression(
611 return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space);611 return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space);
612 },612 },
613613
614 .ArrayInitializer, .ArrayInitializerDot => {
615 var rtoken: ast.TokenIndex = undefined;
616 var exprs: []*ast.Node = undefined;
617 const lhs: union(enum) {dot: ast.TokenIndex, node: *ast.Node } = switch (base.id){
618 .ArrayInitializerDot => blk: {
619 const casted = @fieldParentPtr(ast.Node.ArrayInitializerDot, "base", base);
620 rtoken = casted.rtoken;
621 exprs = casted.list;
622 break :blk .{ .dot = casted.dot };
623 },
624 .ArrayInitializer => blk: {
625 const casted = @fieldParentPtr(ast.Node.ArrayInitializer, "base", base);
626 rtoken = casted.rtoken;
627 exprs = casted.list;
628 break :blk .{ .node = casted.lhs };
629 },
630 else => unreachable,
631 };
632
633 const lbrace = switch (lhs) {
634 .dot => |dot| tree.nextToken(dot),
635 .node => |node| tree.nextToken(node.lastToken()),
636 };
637
638 if (exprs.len == 0) {
639 switch (lhs) {
640 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
641 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
642 }
643 try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
644 return renderToken(tree, stream, rtoken, indent, start_col, space);
645 }
646
647 if (exprs.len == 1 and tree.tokens[exprs[0].lastToken() + 1].id == .RBrace) {
648 const expr = exprs[0];
649 switch (lhs) {
650 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
651 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
652 }
653 try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
654 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);
655 return renderToken(tree, stream, rtoken, indent, start_col, space);
656 }
657
658 switch (lhs) {
659 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
660 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
661 }
662
663 // scan to find row size
664 const maybe_row_size: ?usize = blk: {
665 var count: usize = 1;
666 for (exprs) |expr, i| {
667 if (i + 1 < exprs.len) {
668 const expr_last_token = expr.lastToken() + 1;
669 const loc = tree.tokenLocation(tree.tokens[expr_last_token].end, exprs[i+1].firstToken());
670 if (loc.line != 0) break :blk count;
671 count += 1;
672 } else {
673 const expr_last_token = expr.lastToken();
674 const loc = tree.tokenLocation(tree.tokens[expr_last_token].end, rtoken);
675 if (loc.line == 0) {
676 // all on one line
677 const src_has_trailing_comma = trailblk: {
678 const maybe_comma = tree.prevToken(rtoken);
679 break :trailblk tree.tokens[maybe_comma].id == .Comma;
680 };
681 if (src_has_trailing_comma) {
682 break :blk 1; // force row size 1
683 } else {
684 break :blk null; // no newlines
685 }
686 }
687 break :blk count;
688 }
689 }
690 unreachable;
691 };
692
693 if (maybe_row_size) |row_size| {
694 // A place to store the width of each expression and its column's maximum
695 var widths = try allocator.alloc(usize, exprs.len + row_size);
696 defer allocator.free(widths);
697 mem.set(usize, widths, 0);
698
699 var expr_widths = widths[0 .. widths.len - row_size];
700 var column_widths = widths[widths.len - row_size ..];
701
702 // Null stream for counting the printed length of each expression
703 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
704
705 for (exprs) |expr, i| {
706 counting_stream.bytes_written = 0;
707 var dummy_col: usize = 0;
708 try renderExpression(allocator, counting_stream.outStream(), tree, indent, &dummy_col, expr, Space.None);
709 const width = @intCast(usize, counting_stream.bytes_written);
710 const col = i % row_size;
711 column_widths[col] = std.math.max(column_widths[col], width);
712 expr_widths[i] = width;
713 }
714
715 var new_indent = indent + indent_delta;
716
717 if (tree.tokens[tree.nextToken(lbrace)].id != .MultilineStringLiteralLine) {
718 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline);
719 try stream.writeByteNTimes(' ', new_indent);
720 } else {
721 new_indent -= indent_delta;
722 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.None);
723 }
724
725 var col: usize = 1;
726 for (exprs) |expr, i| {
727 if (i + 1 < exprs.len) {
728 const next_expr = exprs[i + 1];
729 try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.None);
730
731 const comma = tree.nextToken(expr.lastToken());
732
733 if (col != row_size) {
734 try renderToken(tree, stream, comma, new_indent, start_col, Space.Space); // ,
735
736 const padding = column_widths[i % row_size] - expr_widths[i];
737 try stream.writeByteNTimes(' ', padding);
738
739 col += 1;
740 continue;
741 }
742 col = 1;
743
744 if (tree.tokens[tree.nextToken(comma)].id != .MultilineStringLiteralLine) {
745 try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); // ,
746 } else {
747 try renderToken(tree, stream, comma, new_indent, start_col, Space.None); // ,
748 }
749
750 try renderExtraNewline(tree, stream, start_col, next_expr);
751 if (next_expr.id != .MultilineStringLiteral) {
752 try stream.writeByteNTimes(' ', new_indent);
753 }
754 } else {
755 try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.Comma); // ,
756 }
757 }
758 if (exprs[exprs.len - 1].id != .MultilineStringLiteral) {
759 try stream.writeByteNTimes(' ', indent);
760 }
761 return renderToken(tree, stream, rtoken, indent, start_col, space);
762 } else {
763 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
764 for (exprs) |expr, i| {
765 if (i + 1 < exprs.len) {
766 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);
767 const comma = tree.nextToken(expr.lastToken());
768 try renderToken(tree, stream, comma, indent, start_col, Space.Space); // ,
769 } else {
770 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.Space);
771 }
772 }
773
774 return renderToken(tree, stream, rtoken, indent, start_col, space);
775 }
776 },
777
778 .StructInitializer, .StructInitializerDot => {
779 var rtoken: ast.TokenIndex = undefined;
780 var field_inits: []*ast.Node = undefined;
781 const lhs: union(enum) {dot: ast.TokenIndex, node: *ast.Node } = switch (base.id){
782 .StructInitializerDot => blk: {
783 const casted = @fieldParentPtr(ast.Node.StructInitializerDot, "base", base);
784 rtoken = casted.rtoken;
785 field_inits = casted.list;
786 break :blk .{ .dot = casted.dot };
787 },
788 .StructInitializer => blk: {
789 const casted = @fieldParentPtr(ast.Node.StructInitializer, "base", base);
790 rtoken = casted.rtoken;
791 field_inits = casted.list;
792 break :blk .{ .node = casted.lhs };
793 },
794 else => unreachable,
795 };
796
797 const lbrace = switch (lhs) {
798 .dot => |dot| tree.nextToken(dot),
799 .node => |node| tree.nextToken(node.lastToken()),
800 };
801
802 if (field_inits.len == 0) {
803 switch (lhs) {
804 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
805 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
806 }
807 try renderToken(tree, stream, lbrace, indent + indent_delta, start_col, Space.None);
808 return renderToken(tree, stream, rtoken, indent, start_col, space);
809 }
810
811 const src_has_trailing_comma = blk: {
812 const maybe_comma = tree.prevToken(rtoken);
813 break :blk tree.tokens[maybe_comma].id == .Comma;
814 };
815
816 const src_same_line = blk: {
817 const loc = tree.tokenLocation(tree.tokens[lbrace].end, rtoken);
818 break :blk loc.line == 0;
819 };
820
821 const expr_outputs_one_line = blk: {
822 // render field expressions until a LF is found
823 for (field_inits) |field_init| {
824 var find_stream = FindByteOutStream.init('\n');
825 var dummy_col: usize = 0;
826 try renderExpression(allocator, find_stream.outStream(), tree, 0, &dummy_col, field_init, Space.None);
827 if (find_stream.byte_found) break :blk false;
828 }
829 break :blk true;
830 };
831
832 if (field_inits.len == 1) blk: {
833 const field_init = field_inits[0].cast(ast.Node.FieldInitializer).?;
834
835 switch (field_init.expr.id) {
836 .StructInitializer,
837 .StructInitializerDot,
838 => break :blk,
839
840 else => {},
841 }
842
843 // if the expression outputs to multiline, make this struct multiline
844 if (!expr_outputs_one_line or src_has_trailing_comma) {
845 break :blk;
846 }
847
848 switch (lhs) {
849 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
850 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
851 }
852 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
853 try renderExpression(allocator, stream, tree, indent, start_col, &field_init.base, Space.Space);
854 return renderToken(tree, stream, rtoken, indent, start_col, space);
855 }
856
857 if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) {
858 // render all on one line, no trailing comma
859 switch (lhs) {
860 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
861 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
862 }
863 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
864
865 for (field_inits) |field_init, i| {
866 if (i + 1 < field_inits.len) {
867 try renderExpression(allocator, stream, tree, indent, start_col, field_init, Space.None);
868
869 const comma = tree.nextToken(field_init.lastToken());
870 try renderToken(tree, stream, comma, indent, start_col, Space.Space);
871 } else {
872 try renderExpression(allocator, stream, tree, indent, start_col, field_init, Space.Space);
873 }
874 }
875
876 return renderToken(tree, stream, rtoken, indent, start_col, space);
877 }
878
879 const new_indent = indent + indent_delta;
880
881 switch (lhs) {
882 .dot => |dot| try renderToken(tree, stream, dot, new_indent, start_col, Space.None),
883 .node => |node| try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.None),
884 }
885 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline);
886
887 for (field_inits) |field_init, i| {
888 try stream.writeByteNTimes(' ', new_indent);
889
890 if (i + 1 < field_inits.len) {
891 try renderExpression(allocator, stream, tree, new_indent, start_col, field_init, Space.None);
892
893 const comma = tree.nextToken(field_init.lastToken());
894 try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline);
895
896 try renderExtraNewline(tree, stream, start_col, field_inits[i + 1]);
897 } else {
898 try renderExpression(allocator, stream, tree, new_indent, start_col, field_init, Space.Comma);
899 }
900 }
901
902 try stream.writeByteNTimes(' ', indent);
903 return renderToken(tree, stream, rtoken, indent, start_col, space);
904 },
905
614 .SuffixOp => {906 .SuffixOp => {
615 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base);907 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base);
616908
...@@ -620,9 +912,9 @@ fn renderExpression(...@@ -620,9 +912,9 @@ fn renderExpression(
620 try renderToken(tree, stream, async_token, indent, start_col, Space.Space);912 try renderToken(tree, stream, async_token, indent, start_col, Space.Space);
621 }913 }
622914
623 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None);915 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
624916
625 const lparen = tree.nextToken(suffix_op.lhs.node.lastToken());917 const lparen = tree.nextToken(suffix_op.lhs.lastToken());
626918
627 if (call_info.params.first == null) {919 if (call_info.params.first == null) {
628 try renderToken(tree, stream, lparen, indent, start_col, Space.None);920 try renderToken(tree, stream, lparen, indent, start_col, Space.None);
...@@ -680,10 +972,10 @@ fn renderExpression(...@@ -680,10 +972,10 @@ fn renderExpression(
680 },972 },
681973
682 .ArrayAccess => |index_expr| {974 .ArrayAccess => |index_expr| {
683 const lbracket = tree.nextToken(suffix_op.lhs.node.lastToken());975 const lbracket = tree.nextToken(suffix_op.lhs.lastToken());
684 const rbracket = tree.nextToken(index_expr.lastToken());976 const rbracket = tree.nextToken(index_expr.lastToken());
685977
686 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None);978 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
687 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [979 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
688980
689 const starts_with_comment = tree.tokens[lbracket + 1].id == .LineComment;981 const starts_with_comment = tree.tokens[lbracket + 1].id == .LineComment;
...@@ -701,18 +993,18 @@ fn renderExpression(...@@ -701,18 +993,18 @@ fn renderExpression(
701 },993 },
702994
703 .Deref => {995 .Deref => {
704 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None);996 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
705 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // .*997 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // .*
706 },998 },
707999
708 .UnwrapOptional => {1000 .UnwrapOptional => {
709 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None);1001 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
710 try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // .1002 try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // .
711 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ?1003 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ?
712 },1004 },
7131005
714 .Slice => |range| {1006 .Slice => |range| {
715 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None);1007 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
7161008
717 const lbracket = tree.prevToken(range.start.firstToken());1009 const lbracket = tree.prevToken(range.start.firstToken());
718 const dotdot = tree.nextToken(range.start.lastToken());1010 const dotdot = tree.nextToken(range.start.lastToken());
...@@ -737,278 +1029,6 @@ fn renderExpression(...@@ -737,278 +1029,6 @@ fn renderExpression(
737 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ]1029 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ]
738 },1030 },
7391031
740 .StructInitializer => |*field_inits| {
741 const lbrace = switch (suffix_op.lhs) {
742 .dot => |dot| tree.nextToken(dot),
743 .node => |node| tree.nextToken(node.lastToken()),
744 };
745
746 if (field_inits.first == null) {
747 switch (suffix_op.lhs) {
748 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
749 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
750 }
751 try renderToken(tree, stream, lbrace, indent + indent_delta, start_col, Space.None);
752 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
753 }
754
755 const src_has_trailing_comma = blk: {
756 const maybe_comma = tree.prevToken(suffix_op.rtoken);
757 break :blk tree.tokens[maybe_comma].id == .Comma;
758 };
759
760 const src_same_line = blk: {
761 const loc = tree.tokenLocation(tree.tokens[lbrace].end, suffix_op.rtoken);
762 break :blk loc.line == 0;
763 };
764
765 const expr_outputs_one_line = blk: {
766 // render field expressions until a LF is found
767 var it = field_inits.first;
768 while (it) |field_init_node| : (it = field_init_node.next) {
769 const field_init = field_init_node.data;
770 var find_stream = FindByteOutStream.init('\n');
771 var dummy_col: usize = 0;
772 try renderExpression(allocator, find_stream.outStream(), tree, 0, &dummy_col, field_init, Space.None);
773 if (find_stream.byte_found) break :blk false;
774 }
775 break :blk true;
776 };
777
778 if (field_inits.first != null and field_inits.first.?.next == null) blk: {
779 const field_init = field_inits.first.?.data.cast(ast.Node.FieldInitializer).?;
780
781 if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| {
782 if (nested_suffix_op.op == .StructInitializer) {
783 break :blk;
784 }
785 }
786
787 // if the expression outputs to multiline, make this struct multiline
788 if (!expr_outputs_one_line or src_has_trailing_comma) {
789 break :blk;
790 }
791
792 switch (suffix_op.lhs) {
793 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
794 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
795 }
796 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
797 try renderExpression(allocator, stream, tree, indent, start_col, &field_init.base, Space.Space);
798 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
799 }
800
801 if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) {
802 // render all on one line, no trailing comma
803 switch (suffix_op.lhs) {
804 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
805 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
806 }
807 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
808
809 var it = field_inits.first;
810 while (it) |field_init_node| : (it = field_init_node.next) {
811 const field_init = field_init_node.data;
812 if (field_init_node.next != null) {
813 try renderExpression(allocator, stream, tree, indent, start_col, field_init, Space.None);
814
815 const comma = tree.nextToken(field_init.lastToken());
816 try renderToken(tree, stream, comma, indent, start_col, Space.Space);
817 } else {
818 try renderExpression(allocator, stream, tree, indent, start_col, field_init, Space.Space);
819 }
820 }
821
822 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
823 }
824
825 const new_indent = indent + indent_delta;
826
827 switch (suffix_op.lhs) {
828 .dot => |dot| try renderToken(tree, stream, dot, new_indent, start_col, Space.None),
829 .node => |node| try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.None),
830 }
831 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline);
832
833 var it = field_inits.first;
834 while (it) |field_init_node| : (it = field_init_node.next) {
835 const field_init = field_init_node.data;
836 try stream.writeByteNTimes(' ', new_indent);
837
838 if (field_init_node.next) |next_field_init| {
839 try renderExpression(allocator, stream, tree, new_indent, start_col, field_init, Space.None);
840
841 const comma = tree.nextToken(field_init.lastToken());
842 try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline);
843
844 try renderExtraNewline(tree, stream, start_col, next_field_init.data);
845 } else {
846 try renderExpression(allocator, stream, tree, new_indent, start_col, field_init, Space.Comma);
847 }
848 }
849
850 try stream.writeByteNTimes(' ', indent);
851 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
852 },
853
854 .ArrayInitializer => |*exprs| {
855 const lbrace = switch (suffix_op.lhs) {
856 .dot => |dot| tree.nextToken(dot),
857 .node => |node| tree.nextToken(node.lastToken()),
858 };
859
860 if (exprs.first == null) {
861 switch (suffix_op.lhs) {
862 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
863 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
864 }
865 try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
866 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
867 }
868 if (exprs.first) |first_expr_node| {
869 const expr = first_expr_node.data;
870 if (first_expr_node.next == null and tree.tokens[expr.lastToken() + 1].id == .RBrace) {
871 switch (suffix_op.lhs) {
872 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
873 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
874 }
875 try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
876 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);
877 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
878 }
879 }
880
881 switch (suffix_op.lhs) {
882 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
883 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
884 }
885
886 // scan to find row size
887 const maybe_row_size: ?usize = blk: {
888 var count: usize = 1;
889 var it = exprs.first;
890 while (true) {
891 const expr_node = it.?;
892 it = expr_node.next;
893 const expr = expr_node.data;
894 if (expr_node.next) |next_expr| {
895 const expr_last_token = expr.lastToken() + 1;
896 const loc = tree.tokenLocation(tree.tokens[expr_last_token].end, next_expr.data.firstToken());
897 if (loc.line != 0) break :blk count;
898 count += 1;
899 } else {
900 const expr_last_token = expr.lastToken();
901 const loc = tree.tokenLocation(tree.tokens[expr_last_token].end, suffix_op.rtoken);
902 if (loc.line == 0) {
903 // all on one line
904 const src_has_trailing_comma = trailblk: {
905 const maybe_comma = tree.prevToken(suffix_op.rtoken);
906 break :trailblk tree.tokens[maybe_comma].id == .Comma;
907 };
908 if (src_has_trailing_comma) {
909 break :blk 1; // force row size 1
910 } else {
911 break :blk null; // no newlines
912 }
913 }
914 break :blk count;
915 }
916 }
917 };
918
919 if (maybe_row_size) |row_size| {
920 // A place to store the width of each expression and its column's maximum
921 var widths = try allocator.alloc(usize, exprs.len() + row_size);
922 defer allocator.free(widths);
923 mem.set(usize, widths, 0);
924
925 var expr_widths = widths[0 .. widths.len - row_size];
926 var column_widths = widths[widths.len - row_size ..];
927
928 // Null stream for counting the printed length of each expression
929 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
930
931 var it = exprs.first;
932 var i: usize = 0;
933
934 while (it) |expr_node| : ({i += 1; it = expr_node.next;}) {
935 const expr = expr_node.data;
936 counting_stream.bytes_written = 0;
937 var dummy_col: usize = 0;
938 try renderExpression(allocator, counting_stream.outStream(), tree, indent, &dummy_col, expr, Space.None);
939 const width = @intCast(usize, counting_stream.bytes_written);
940 const col = i % row_size;
941 column_widths[col] = std.math.max(column_widths[col], width);
942 expr_widths[i] = width;
943 }
944
945 var new_indent = indent + indent_delta;
946
947 if (tree.tokens[tree.nextToken(lbrace)].id != .MultilineStringLiteralLine) {
948 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline);
949 try stream.writeByteNTimes(' ', new_indent);
950 } else {
951 new_indent -= indent_delta;
952 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.None);
953 }
954
955 it = exprs.first;
956 i = 0;
957 var col: usize = 1;
958 var last_node = it.?;
959 while (it) |expr_node| : ({i += 1; it = expr_node.next; last_node = expr_node;}) {
960 const expr = expr_node.data;
961 if (expr_node.next) |next_expr| {
962 try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.None);
963
964 const comma = tree.nextToken(expr.lastToken());
965
966 if (col != row_size) {
967 try renderToken(tree, stream, comma, new_indent, start_col, Space.Space); // ,
968
969 const padding = column_widths[i % row_size] - expr_widths[i];
970 try stream.writeByteNTimes(' ', padding);
971
972 col += 1;
973 continue;
974 }
975 col = 1;
976
977 if (tree.tokens[tree.nextToken(comma)].id != .MultilineStringLiteralLine) {
978 try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); // ,
979 } else {
980 try renderToken(tree, stream, comma, new_indent, start_col, Space.None); // ,
981 }
982
983 try renderExtraNewline(tree, stream, start_col, next_expr.data);
984 if (next_expr.data.id != .MultilineStringLiteral) {
985 try stream.writeByteNTimes(' ', new_indent);
986 }
987 } else {
988 try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.Comma); // ,
989 }
990 }
991 if (last_node.data.id != .MultilineStringLiteral) {
992 try stream.writeByteNTimes(' ', indent);
993 }
994 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
995 } else {
996 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
997 var it = exprs.first;
998 while (it) |expr_node| : (it = expr_node.next) {
999 const expr = expr_node.data;
1000 if (expr_node.next) |next_expr| {
1001 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);
1002 const comma = tree.nextToken(expr.lastToken());
1003 try renderToken(tree, stream, comma, indent, start_col, Space.Space); // ,
1004 } else {
1005 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.Space);
1006 }
1007 }
1008
1009 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
1010 }
1011 },
1012 }1032 }
1013 },1033 },
10141034