authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-21 22:28:30-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-21 22:28:30-04:00
log9377af934f5be92821f36b5d2255f086e5a84cda
treee58814eecf85d3e59da6b7bc04c6a432fd1689cd
parentd37b81d43bc52daa94dd1ad1631018ea0cd11f77

stage2 parser: SwitchCase uses intrusive array instead of linkedlist

no perf impact, but the API is better

3 files changed, 49 insertions(+), 29 deletions(-)

lib/std/zig/ast.zig+30-9
...@@ -1552,28 +1552,35 @@ pub const Node = struct {...@@ -1552,28 +1552,35 @@ pub const Node = struct {
1552 }1552 }
1553 };1553 };
15541554
1555 /// Items sub-nodes appear in memory directly following SwitchCase.
1555 pub const SwitchCase = struct {1556 pub const SwitchCase = struct {
1556 base: Node = Node{ .id = .SwitchCase },1557 base: Node = Node{ .id = .SwitchCase },
1557 items: ItemList,
1558 arrow_token: TokenIndex,1558 arrow_token: TokenIndex,
1559 payload: ?*Node,1559 payload: ?*Node,
1560 expr: *Node,1560 expr: *Node,
1561 items_len: NodeIndex,
15611562
1562 pub const ItemList = LinkedList(*Node);1563 /// After this the caller must initialize the fields_and_decls list.
1564 pub fn alloc(allocator: *mem.Allocator, items_len: NodeIndex) !*SwitchCase {
1565 const bytes = try allocator.alignedAlloc(u8, @alignOf(SwitchCase), sizeInBytes(items_len));
1566 return @ptrCast(*SwitchCase, bytes.ptr);
1567 }
1568
1569 pub fn free(self: *SwitchCase, allocator: *mem.Allocator) void {
1570 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.items_len)];
1571 allocator.free(bytes);
1572 }
15631573
1564 pub fn iterate(self: *const SwitchCase) Node.Iterator {1574 pub fn iterate(self: *const SwitchCase) Node.Iterator {
1565 return .{ .parent_node = &self.base, .index = 0, .node = self.items.first };1575 return .{ .parent_node = &self.base, .index = 0, .node = null };
1566 }1576 }
15671577
1568 pub fn iterateNext(self: *const SwitchCase, it: *Node.Iterator) ?*Node {1578 pub fn iterateNext(self: *const SwitchCase, it: *Node.Iterator) ?*Node {
1569 var i = it.index;1579 var i = it.index;
1570 it.index += 1;1580 it.index += 1;
15711581
1572 if (it.node) |child| {1582 if (i < self.items_len) return self.itemsConst()[i];
1573 it.index -= 1;1583 i -= self.items_len;
1574 it.node = child.next;
1575 return child.data;
1576 }
15771584
1578 if (self.payload) |payload| {1585 if (self.payload) |payload| {
1579 if (i < 1) return payload;1586 if (i < 1) return payload;
...@@ -1587,12 +1594,26 @@ pub const Node = struct {...@@ -1587,12 +1594,26 @@ pub const Node = struct {
1587 }1594 }
15881595
1589 pub fn firstToken(self: *const SwitchCase) TokenIndex {1596 pub fn firstToken(self: *const SwitchCase) TokenIndex {
1590 return self.items.first.?.data.firstToken();1597 return self.itemsConst()[0].firstToken();
1591 }1598 }
15921599
1593 pub fn lastToken(self: *const SwitchCase) TokenIndex {1600 pub fn lastToken(self: *const SwitchCase) TokenIndex {
1594 return self.expr.lastToken();1601 return self.expr.lastToken();
1595 }1602 }
1603
1604 pub fn items(self: *SwitchCase) []*Node {
1605 const decls_start = @ptrCast([*]u8, self) + @sizeOf(SwitchCase);
1606 return @ptrCast([*]*Node, decls_start)[0..self.items_len];
1607 }
1608
1609 pub fn itemsConst(self: *const SwitchCase) []const *Node {
1610 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(SwitchCase);
1611 return @ptrCast([*]const *Node, decls_start)[0..self.items_len];
1612 }
1613
1614 fn sizeInBytes(items_len: NodeIndex) usize {
1615 return @sizeOf(SwitchCase) + @sizeOf(*Node) * @as(usize, items_len);
1616 }
1596 };1617 };
15971618
1598 pub const SwitchElse = struct {1619 pub const SwitchElse = struct {
lib/std/zig/parse.zig+8-7
...@@ -2205,30 +2205,31 @@ const Parser = struct {...@@ -2205,30 +2205,31 @@ const Parser = struct {
2205 /// <- SwitchItem (COMMA SwitchItem)* COMMA?2205 /// <- SwitchItem (COMMA SwitchItem)* COMMA?
2206 /// / KEYWORD_else2206 /// / KEYWORD_else
2207 fn parseSwitchCase(p: *Parser) !?*Node {2207 fn parseSwitchCase(p: *Parser) !?*Node {
2208 var list = Node.SwitchCase.ItemList{};2208 var list = std.ArrayList(*Node).init(p.gpa);
2209 var list_it = &list.first;2209 defer list.deinit();
22102210
2211 if (try p.parseSwitchItem()) |first_item| {2211 if (try p.parseSwitchItem()) |first_item| {
2212 list_it = try p.llpush(*Node, list_it, first_item);2212 try list.append(first_item);
2213 while (p.eatToken(.Comma) != null) {2213 while (p.eatToken(.Comma) != null) {
2214 const next_item = (try p.parseSwitchItem()) orelse break;2214 const next_item = (try p.parseSwitchItem()) orelse break;
2215 list_it = try p.llpush(*Node, list_it, next_item);2215 try list.append(next_item);
2216 }2216 }
2217 } else if (p.eatToken(.Keyword_else)) |else_token| {2217 } else if (p.eatToken(.Keyword_else)) |else_token| {
2218 const else_node = try p.arena.allocator.create(Node.SwitchElse);2218 const else_node = try p.arena.allocator.create(Node.SwitchElse);
2219 else_node.* = .{2219 else_node.* = .{
2220 .token = else_token,2220 .token = else_token,
2221 };2221 };
2222 list_it = try p.llpush(*Node, list_it, &else_node.base);2222 try list.append(&else_node.base);
2223 } else return null;2223 } else return null;
22242224
2225 const node = try p.arena.allocator.create(Node.SwitchCase);2225 const node = try Node.SwitchCase.alloc(&p.arena.allocator, list.items.len);
2226 node.* = .{2226 node.* = .{
2227 .items = list,2227 .items_len = list.items.len,
2228 .arrow_token = undefined, // set by caller2228 .arrow_token = undefined, // set by caller
2229 .payload = null,2229 .payload = null,
2230 .expr = undefined, // set by caller2230 .expr = undefined, // set by caller
2231 };2231 };
2232 std.mem.copy(*Node, node.items(), list.items);
2232 return &node.base;2233 return &node.base;
2233 }2234 }
22342235
lib/std/zig/render.zig+11-13
...@@ -1613,37 +1613,35 @@ fn renderExpression(...@@ -1613,37 +1613,35 @@ fn renderExpression(
1613 .SwitchCase => {1613 .SwitchCase => {
1614 const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);1614 const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
16151615
1616 assert(switch_case.items.first != null);1616 assert(switch_case.items_len != 0);
1617 const src_has_trailing_comma = blk: {1617 const src_has_trailing_comma = blk: {
1618 const last_node = switch_case.items.first.?.findLast().data;1618 const last_node = switch_case.items()[switch_case.items_len - 1];
1619 const maybe_comma = tree.nextToken(last_node.lastToken());1619 const maybe_comma = tree.nextToken(last_node.lastToken());
1620 break :blk tree.tokens[maybe_comma].id == .Comma;1620 break :blk tree.tokens[maybe_comma].id == .Comma;
1621 };1621 };
16221622
1623 if (switch_case.items.first.?.next == null or !src_has_trailing_comma) {1623 if (switch_case.items_len == 1 or !src_has_trailing_comma) {
1624 var it = switch_case.items.first;1624 const items = switch_case.items();
1625 while (it) |node_node| : (it = node_node.next) {1625 for (items) |node, i| {
1626 const node = node_node.data;1626 if (i + 1 < items.len) {
1627 if (node_node.next) |next_node| {
1628 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None);1627 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None);
16291628
1630 const comma_token = tree.nextToken(node.lastToken());1629 const comma_token = tree.nextToken(node.lastToken());
1631 try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // ,1630 try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // ,
1632 try renderExtraNewline(tree, stream, start_col, next_node.data);1631 try renderExtraNewline(tree, stream, start_col, items[i + 1]);
1633 } else {1632 } else {
1634 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.Space);1633 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.Space);
1635 }1634 }
1636 }1635 }
1637 } else {1636 } else {
1638 var it = switch_case.items.first;1637 const items = switch_case.items();
1639 while (it) |node_node| : (it = node_node.next) {1638 for (items) |node, i| {
1640 const node = node_node.data;1639 if (i + 1 < items.len) {
1641 if (node_node.next) |next_node| {
1642 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None);1640 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None);
16431641
1644 const comma_token = tree.nextToken(node.lastToken());1642 const comma_token = tree.nextToken(node.lastToken());
1645 try renderToken(tree, stream, comma_token, indent, start_col, Space.Newline); // ,1643 try renderToken(tree, stream, comma_token, indent, start_col, Space.Newline); // ,
1646 try renderExtraNewline(tree, stream, start_col, next_node.data);1644 try renderExtraNewline(tree, stream, start_col, items[i + 1]);
1647 try stream.writeByteNTimes(' ', indent);1645 try stream.writeByteNTimes(' ', indent);
1648 } else {1646 } else {
1649 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.Comma);1647 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.Comma);