aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-07-13 20:13:51 -0700
committerAndrew Kelley <andrew@ziglang.org>2020-07-13 20:13:51 -0700
commit14cef9dd3d8074b0dc2ee48a0905300ce6317aed (patch)
tree246220eb1846d00a982c33d8e0fae5b5f4ee150c /lib/std
parent204f61d7f5aa7b5e27bdaaab9237fca17096ad3e (diff)
downloadzig-14cef9dd3d8074b0dc2ee48a0905300ce6317aed.tar.gz
zig-14cef9dd3d8074b0dc2ee48a0905300ce6317aed.zip
stage2 parser: split out PrefixOp into separate AST Nodes
This is part of a larger effort to improve the memory layout of AST nodes of the self-hosted parser to reduce wasted memory. Reduction of wasted memory also translates to improved performance because of fewer memory allocations, and fewer cache misses. Compared to master, when running `zig fmt` on the std lib: * cache-misses: 801,829 => 768,624 * instructions: 3,234,877,167 => 3,232,075,022 * peak memory: 81480 KB => 75964 KB
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/zig/ast.zig240
-rw-r--r--lib/std/zig/parse.zig348
-rw-r--r--lib/std/zig/render.zig356
3 files changed, 623 insertions, 321 deletions
diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig
index 0502f5bb64..1de00dd0b0 100644
--- a/lib/std/zig/ast.zig
+++ b/lib/std/zig/ast.zig
@@ -410,7 +410,20 @@ pub const Node = struct {
// Operators
InfixOp,
- PrefixOp,
+ AddressOf,
+ Await,
+ BitNot,
+ BoolNot,
+ OptionalType,
+ Negation,
+ NegationWrap,
+ Resume,
+ Try,
+ ArrayType,
+ /// ArrayType but has a sentinel node.
+ ArrayTypeSentinel,
+ PtrType,
+ SliceType,
/// Not all suffix operations are under this tag. To save memory, some
/// suffix operations have dedicated Node tags.
SuffixOp,
@@ -1797,85 +1810,116 @@ pub const Node = struct {
}
};
- pub const PrefixOp = struct {
- base: Node = Node{ .id = .PrefixOp },
+ pub const AddressOf = SimplePrefixOp(.AddressOf);
+ pub const Await = SimplePrefixOp(.Await);
+ pub const BitNot = SimplePrefixOp(.BitNot);
+ pub const BoolNot = SimplePrefixOp(.BoolNot);
+ pub const OptionalType = SimplePrefixOp(.OptionalType);
+ pub const Negation = SimplePrefixOp(.Negation);
+ pub const NegationWrap = SimplePrefixOp(.NegationWrap);
+ pub const Resume = SimplePrefixOp(.Resume);
+ pub const Try = SimplePrefixOp(.Try);
+
+ pub fn SimplePrefixOp(comptime tag: Id) type {
+ return struct {
+ base: Node = Node{ .id = tag },
+ op_token: TokenIndex,
+ rhs: *Node,
+
+ const Self = @This();
+
+ pub fn iterate(self: *const Self, index: usize) ?*Node {
+ if (index == 0) return self.rhs;
+ return null;
+ }
+
+ pub fn firstToken(self: *const Self) TokenIndex {
+ return self.op_token;
+ }
+
+ pub fn lastToken(self: *const Self) TokenIndex {
+ return self.rhs.lastToken();
+ }
+ };
+ }
+
+ pub const ArrayType = struct {
+ base: Node = Node{ .id = .ArrayType },
op_token: TokenIndex,
- op: Op,
rhs: *Node,
+ len_expr: *Node,
- pub const Op = union(enum) {
- AddressOf,
- ArrayType: ArrayInfo,
- Await,
- BitNot,
- BoolNot,
- OptionalType,
- Negation,
- NegationWrap,
- Resume,
- PtrType: PtrInfo,
- SliceType: PtrInfo,
- Try,
- };
+ pub fn iterate(self: *const ArrayType, index: usize) ?*Node {
+ var i = index;
- pub const ArrayInfo = struct {
- len_expr: *Node,
- sentinel: ?*Node,
- };
+ if (i < 1) return self.len_expr;
+ i -= 1;
- pub const PtrInfo = struct {
- allowzero_token: ?TokenIndex = null,
- align_info: ?Align = null,
- const_token: ?TokenIndex = null,
- volatile_token: ?TokenIndex = null,
- sentinel: ?*Node = null,
-
- pub const Align = struct {
- node: *Node,
- bit_range: ?BitRange,
-
- pub const BitRange = struct {
- start: *Node,
- end: *Node,
- };
- };
- };
+ if (i < 1) return self.rhs;
+ i -= 1;
+
+ return null;
+ }
- pub fn iterate(self: *const PrefixOp, index: usize) ?*Node {
+ pub fn firstToken(self: *const ArrayType) TokenIndex {
+ return self.op_token;
+ }
+
+ pub fn lastToken(self: *const ArrayType) TokenIndex {
+ return self.rhs.lastToken();
+ }
+ };
+
+ pub const ArrayTypeSentinel = struct {
+ base: Node = Node{ .id = .ArrayTypeSentinel },
+ op_token: TokenIndex,
+ rhs: *Node,
+ len_expr: *Node,
+ sentinel: *Node,
+
+ pub fn iterate(self: *const ArrayTypeSentinel, index: usize) ?*Node {
var i = index;
- switch (self.op) {
- .PtrType, .SliceType => |addr_of_info| {
- if (addr_of_info.sentinel) |sentinel| {
- if (i < 1) return sentinel;
- i -= 1;
- }
+ if (i < 1) return self.len_expr;
+ i -= 1;
- if (addr_of_info.align_info) |align_info| {
- if (i < 1) return align_info.node;
- i -= 1;
- }
- },
+ if (i < 1) return self.sentinel;
+ i -= 1;
- .ArrayType => |array_info| {
- if (i < 1) return array_info.len_expr;
- i -= 1;
- if (array_info.sentinel) |sentinel| {
- if (i < 1) return sentinel;
- i -= 1;
- }
- },
+ if (i < 1) return self.rhs;
+ i -= 1;
- .AddressOf,
- .Await,
- .BitNot,
- .BoolNot,
- .OptionalType,
- .Negation,
- .NegationWrap,
- .Try,
- .Resume,
- => {},
+ return null;
+ }
+
+ pub fn firstToken(self: *const ArrayTypeSentinel) TokenIndex {
+ return self.op_token;
+ }
+
+ pub fn lastToken(self: *const ArrayTypeSentinel) TokenIndex {
+ return self.rhs.lastToken();
+ }
+ };
+
+ pub const PtrType = struct {
+ base: Node = Node{ .id = .PtrType },
+ op_token: TokenIndex,
+ rhs: *Node,
+ /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
+ /// one of these possibly-null things. Then we have them directly follow the PtrType in memory.
+ ptr_info: PtrInfo = .{},
+
+ pub fn iterate(self: *const PtrType, index: usize) ?*Node {
+ var i = index;
+
+ if (self.ptr_info.sentinel) |sentinel| {
+ if (i < 1) return sentinel;
+ i -= 1;
+ }
+
+ if (self.ptr_info.align_info) |align_info| {
+ if (i < 1) return align_info.node;
+ i -= 1;
}
if (i < 1) return self.rhs;
@@ -1884,11 +1928,47 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: *const PrefixOp) TokenIndex {
+ pub fn firstToken(self: *const PtrType) TokenIndex {
return self.op_token;
}
- pub fn lastToken(self: *const PrefixOp) TokenIndex {
+ pub fn lastToken(self: *const PtrType) TokenIndex {
+ return self.rhs.lastToken();
+ }
+ };
+
+ pub const SliceType = struct {
+ base: Node = Node{ .id = .SliceType },
+ op_token: TokenIndex,
+ rhs: *Node,
+ /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
+ /// one of these possibly-null things. Then we have them directly follow the SliceType in memory.
+ ptr_info: PtrInfo = .{},
+
+ pub fn iterate(self: *const SliceType, index: usize) ?*Node {
+ var i = index;
+
+ if (self.ptr_info.sentinel) |sentinel| {
+ if (i < 1) return sentinel;
+ i -= 1;
+ }
+
+ if (self.ptr_info.align_info) |align_info| {
+ if (i < 1) return align_info.node;
+ i -= 1;
+ }
+
+ if (i < 1) return self.rhs;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: *const SliceType) TokenIndex {
+ return self.op_token;
+ }
+
+ pub fn lastToken(self: *const SliceType) TokenIndex {
return self.rhs.lastToken();
}
};
@@ -2797,6 +2877,24 @@ pub const Node = struct {
};
};
+pub const PtrInfo = struct {
+ allowzero_token: ?TokenIndex = null,
+ align_info: ?Align = null,
+ const_token: ?TokenIndex = null,
+ volatile_token: ?TokenIndex = null,
+ sentinel: ?*Node = null,
+
+ pub const Align = struct {
+ node: *Node,
+ bit_range: ?BitRange = null,
+
+ pub const BitRange = struct {
+ start: *Node,
+ end: *Node,
+ };
+ };
+};
+
test "iterate" {
var root = Node.Root{
.base = Node{ .id = Node.Id.Root },
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
index f5f0c10826..d46833c23f 100644
--- a/lib/std/zig/parse.zig
+++ b/lib/std/zig/parse.zig
@@ -1120,10 +1120,9 @@ const Parser = struct {
const expr_node = try p.expectNode(parseExpr, .{
.ExpectedExpr = .{ .token = p.tok_i },
});
- const node = try p.arena.allocator.create(Node.PrefixOp);
+ const node = try p.arena.allocator.create(Node.Resume);
node.* = .{
.op_token = token,
- .op = .Resume,
.rhs = expr_node,
};
return &node.base;
@@ -2413,24 +2412,25 @@ const Parser = struct {
/// / KEYWORD_await
fn parsePrefixOp(p: *Parser) !?*Node {
const token = p.nextToken();
- const op: Node.PrefixOp.Op = switch (p.token_ids[token]) {
- .Bang => .BoolNot,
- .Minus => .Negation,
- .Tilde => .BitNot,
- .MinusPercent => .NegationWrap,
- .Ampersand => .AddressOf,
- .Keyword_try => .Try,
- .Keyword_await => .Await,
+ switch (p.token_ids[token]) {
+ .Bang => return p.allocSimplePrefixOp(.BoolNot, token),
+ .Minus => return p.allocSimplePrefixOp(.Negation, token),
+ .Tilde => return p.allocSimplePrefixOp(.BitNot, token),
+ .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token),
+ .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token),
+ .Keyword_try => return p.allocSimplePrefixOp(.Try, token),
+ .Keyword_await => return p.allocSimplePrefixOp(.Await, token),
else => {
p.putBackToken(token);
return null;
},
- };
+ }
+ }
- const node = try p.arena.allocator.create(Node.PrefixOp);
+ fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Id, token: TokenIndex) !?*Node {
+ const node = try p.arena.allocator.create(Node.SimplePrefixOp(tag));
node.* = .{
.op_token = token,
- .op = op,
.rhs = undefined, // set by caller
};
return &node.base;
@@ -2450,19 +2450,14 @@ const Parser = struct {
/// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
fn parsePrefixTypeOp(p: *Parser) !?*Node {
if (p.eatToken(.QuestionMark)) |token| {
- const node = try p.arena.allocator.create(Node.PrefixOp);
+ const node = try p.arena.allocator.create(Node.OptionalType);
node.* = .{
.op_token = token,
- .op = .OptionalType,
.rhs = undefined, // set by caller
};
return &node.base;
}
- // TODO: Returning a AnyFrameType instead of PrefixOp makes casting and setting .rhs or
- // .return_type more difficult for the caller (see parsePrefixOpExpr helper).
- // Consider making the AnyFrameType a member of PrefixOp and add a
- // PrefixOp.AnyFrameType variant?
if (p.eatToken(.Keyword_anyframe)) |token| {
const arrow = p.eatToken(.Arrow) orelse {
p.putBackToken(token);
@@ -2482,11 +2477,15 @@ const Parser = struct {
if (try p.parsePtrTypeStart()) |node| {
// If the token encountered was **, there will be two nodes instead of one.
// The attributes should be applied to the rightmost operator.
- const prefix_op = node.cast(Node.PrefixOp).?;
- var ptr_info = if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk)
- &prefix_op.rhs.cast(Node.PrefixOp).?.op.PtrType
+ var ptr_info = if (node.cast(Node.PtrType)) |ptr_type|
+ if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk)
+ &ptr_type.rhs.cast(Node.PtrType).?.ptr_info
+ else
+ &ptr_type.ptr_info
+ else if (node.cast(Node.SliceType)) |slice_type|
+ &slice_type.ptr_info
else
- &prefix_op.op.PtrType;
+ unreachable;
while (true) {
if (p.eatToken(.Keyword_align)) |align_token| {
@@ -2505,7 +2504,7 @@ const Parser = struct {
.ExpectedIntegerLiteral = .{ .token = p.tok_i },
});
- break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{
+ break :bit_range_value ast.PtrInfo.Align.BitRange{
.start = range_start,
.end = range_end,
};
@@ -2519,7 +2518,7 @@ const Parser = struct {
continue;
}
- ptr_info.align_info = Node.PrefixOp.PtrInfo.Align{
+ ptr_info.align_info = ast.PtrInfo.Align{
.node = expr_node,
.bit_range = bit_range,
};
@@ -2563,58 +2562,54 @@ const Parser = struct {
}
if (try p.parseArrayTypeStart()) |node| {
- switch (node.cast(Node.PrefixOp).?.op) {
- .ArrayType => {},
- .SliceType => |*slice_type| {
- // Collect pointer qualifiers in any order, but disallow duplicates
- while (true) {
- if (try p.parseByteAlign()) |align_expr| {
- if (slice_type.align_info != null) {
- try p.errors.append(p.gpa, .{
- .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
- }
- slice_type.align_info = Node.PrefixOp.PtrInfo.Align{
- .node = align_expr,
- .bit_range = null,
- };
+ if (node.cast(Node.SliceType)) |slice_type| {
+ // Collect pointer qualifiers in any order, but disallow duplicates
+ while (true) {
+ if (try p.parseByteAlign()) |align_expr| {
+ if (slice_type.ptr_info.align_info != null) {
+ try p.errors.append(p.gpa, .{
+ .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
+ });
continue;
}
- if (p.eatToken(.Keyword_const)) |const_token| {
- if (slice_type.const_token != null) {
- try p.errors.append(p.gpa, .{
- .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
- }
- slice_type.const_token = const_token;
+ slice_type.ptr_info.align_info = ast.PtrInfo.Align{
+ .node = align_expr,
+ .bit_range = null,
+ };
+ continue;
+ }
+ if (p.eatToken(.Keyword_const)) |const_token| {
+ if (slice_type.ptr_info.const_token != null) {
+ try p.errors.append(p.gpa, .{
+ .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
+ });
continue;
}
- if (p.eatToken(.Keyword_volatile)) |volatile_token| {
- if (slice_type.volatile_token != null) {
- try p.errors.append(p.gpa, .{
- .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
- }
- slice_type.volatile_token = volatile_token;
+ slice_type.ptr_info.const_token = const_token;
+ continue;
+ }
+ if (p.eatToken(.Keyword_volatile)) |volatile_token| {
+ if (slice_type.ptr_info.volatile_token != null) {
+ try p.errors.append(p.gpa, .{
+ .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
+ });
continue;
}
- if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
- if (slice_type.allowzero_token != null) {
- try p.errors.append(p.gpa, .{
- .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
- }
- slice_type.allowzero_token = allowzero_token;
+ slice_type.ptr_info.volatile_token = volatile_token;
+ continue;
+ }
+ if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
+ if (slice_type.ptr_info.allowzero_token != null) {
+ try p.errors.append(p.gpa, .{
+ .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
+ });
continue;
}
- break;
+ slice_type.ptr_info.allowzero_token = allowzero_token;
+ continue;
}
- },
- else => unreachable,
+ break;
+ }
}
return node;
}
@@ -2728,29 +2723,32 @@ const Parser = struct {
null;
const rbracket = try p.expectToken(.RBracket);
- const op: Node.PrefixOp.Op = if (expr) |len_expr|
- .{
- .ArrayType = .{
+ if (expr) |len_expr| {
+ if (sentinel) |s| {
+ const node = try p.arena.allocator.create(Node.ArrayTypeSentinel);
+ node.* = .{
+ .op_token = lbracket,
+ .rhs = undefined, // set by caller
.len_expr = len_expr,
- .sentinel = sentinel,
- },
+ .sentinel = s,
+ };
+ return &node.base;
+ } else {
+ const node = try p.arena.allocator.create(Node.ArrayType);
+ node.* = .{
+ .op_token = lbracket,
+ .rhs = undefined, // set by caller
+ .len_expr = len_expr,
+ };
+ return &node.base;
}
- else
- .{
- .SliceType = Node.PrefixOp.PtrInfo{
- .allowzero_token = null,
- .align_info = null,
- .const_token = null,
- .volatile_token = null,
- .sentinel = sentinel,
- },
- };
+ }
- const node = try p.arena.allocator.create(Node.PrefixOp);
+ const node = try p.arena.allocator.create(Node.SliceType);
node.* = .{
.op_token = lbracket,
- .op = op,
.rhs = undefined, // set by caller
+ .ptr_info = .{ .sentinel = sentinel },
};
return &node.base;
}
@@ -2768,28 +2766,26 @@ const Parser = struct {
})
else
null;
- const node = try p.arena.allocator.create(Node.PrefixOp);
+ const node = try p.arena.allocator.create(Node.PtrType);
node.* = .{
.op_token = asterisk,
- .op = .{ .PtrType = .{ .sentinel = sentinel } },
.rhs = undefined, // set by caller
+ .ptr_info = .{ .sentinel = sentinel },
};
return &node.base;
}
if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| {
- const node = try p.arena.allocator.create(Node.PrefixOp);
+ const node = try p.arena.allocator.create(Node.PtrType);
node.* = .{
.op_token = double_asterisk,
- .op = .{ .PtrType = .{} },
.rhs = undefined, // set by caller
};
// Special case for **, which is its own token
- const child = try p.arena.allocator.create(Node.PrefixOp);
+ const child = try p.arena.allocator.create(Node.PtrType);
child.* = .{
.op_token = double_asterisk,
- .op = .{ .PtrType = .{} },
.rhs = undefined, // set by caller
};
node.rhs = &child.base;
@@ -2808,10 +2804,9 @@ const Parser = struct {
p.putBackToken(ident);
} else {
_ = try p.expectToken(.RBracket);
- const node = try p.arena.allocator.create(Node.PrefixOp);
+ const node = try p.arena.allocator.create(Node.PtrType);
node.* = .{
.op_token = lbracket,
- .op = .{ .PtrType = .{} },
.rhs = undefined, // set by caller
};
return &node.base;
@@ -2824,11 +2819,11 @@ const Parser = struct {
else
null;
_ = try p.expectToken(.RBracket);
- const node = try p.arena.allocator.create(Node.PrefixOp);
+ const node = try p.arena.allocator.create(Node.PtrType);
node.* = .{
.op_token = lbracket,
- .op = .{ .PtrType = .{ .sentinel = sentinel } },
.rhs = undefined, // set by caller
+ .ptr_info = .{ .sentinel = sentinel },
};
return &node.base;
}
@@ -3146,10 +3141,9 @@ const Parser = struct {
fn parseTry(p: *Parser) !?*Node {
const token = p.eatToken(.Keyword_try) orelse return null;
- const node = try p.arena.allocator.create(Node.PrefixOp);
+ const node = try p.arena.allocator.create(Node.Try);
node.* = .{
.op_token = token,
- .op = .Try,
.rhs = undefined, // set by caller
};
return &node.base;
@@ -3228,15 +3222,87 @@ const Parser = struct {
var rightmost_op = first_op;
while (true) {
switch (rightmost_op.id) {
- .PrefixOp => {
- var prefix_op = rightmost_op.cast(Node.PrefixOp).?;
+ .AddressOf => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.AddressOf).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .Await => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.Await).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .BitNot => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.BitNot).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .BoolNot => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.BoolNot).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .OptionalType => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.OptionalType).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .Negation => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.Negation).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .NegationWrap => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.NegationWrap).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .Resume => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.Resume).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .Try => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.Try).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .ArrayType => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.ArrayType).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .ArrayTypeSentinel => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.ArrayTypeSentinel).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .SliceType => {
+ if (try opParseFn(p)) |rhs| {
+ rightmost_op.cast(Node.SliceType).?.rhs = rhs;
+ rightmost_op = rhs;
+ } else break;
+ },
+ .PtrType => {
+ var ptr_type = rightmost_op.cast(Node.PtrType).?;
// If the token encountered was **, there will be two nodes
- if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk) {
- rightmost_op = prefix_op.rhs;
- prefix_op = rightmost_op.cast(Node.PrefixOp).?;
+ if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) {
+ rightmost_op = ptr_type.rhs;
+ ptr_type = rightmost_op.cast(Node.PtrType).?;
}
if (try opParseFn(p)) |rhs| {
- prefix_op.rhs = rhs;
+ ptr_type.rhs = rhs;
rightmost_op = rhs;
} else break;
},
@@ -3253,8 +3319,80 @@ const Parser = struct {
// If any prefix op existed, a child node on the RHS is required
switch (rightmost_op.id) {
- .PrefixOp => {
- const prefix_op = rightmost_op.cast(Node.PrefixOp).?;
+ .AddressOf => {
+ const prefix_op = rightmost_op.cast(Node.AddressOf).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .Await => {
+ const prefix_op = rightmost_op.cast(Node.Await).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .BitNot => {
+ const prefix_op = rightmost_op.cast(Node.BitNot).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .BoolNot => {
+ const prefix_op = rightmost_op.cast(Node.BoolNot).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .OptionalType => {
+ const prefix_op = rightmost_op.cast(Node.OptionalType).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .Negation => {
+ const prefix_op = rightmost_op.cast(Node.Negation).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .NegationWrap => {
+ const prefix_op = rightmost_op.cast(Node.NegationWrap).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .Resume => {
+ const prefix_op = rightmost_op.cast(Node.Resume).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .Try => {
+ const prefix_op = rightmost_op.cast(Node.Try).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .ArrayType => {
+ const prefix_op = rightmost_op.cast(Node.ArrayType).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .ArrayTypeSentinel => {
+ const prefix_op = rightmost_op.cast(Node.ArrayTypeSentinel).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .PtrType => {
+ const prefix_op = rightmost_op.cast(Node.PtrType).?;
+ prefix_op.rhs = try p.expectNode(childParseFn, .{
+ .InvalidToken = .{ .token = p.tok_i },
+ });
+ },
+ .SliceType => {
+ const prefix_op = rightmost_op.cast(Node.SliceType).?;
prefix_op.rhs = try p.expectNode(childParseFn, .{
.InvalidToken = .{ .token = p.tok_i },
});
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
index f24886c0ab..b75e6f6ca3 100644
--- a/lib/std/zig/render.zig
+++ b/lib/std/zig/render.zig
@@ -468,166 +468,192 @@ fn renderExpression(
return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
},
- .PrefixOp => {
- const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
-
- switch (prefix_op_node.op) {
- .PtrType => |ptr_info| {
- const op_tok_id = tree.token_ids[prefix_op_node.op_token];
- switch (op_tok_id) {
- .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
- .LBracket => if (tree.token_ids[prefix_op_node.op_token + 2] == .Identifier)
- try stream.writeAll("[*c")
- else
- try stream.writeAll("[*"),
- else => unreachable,
- }
- if (ptr_info.sentinel) |sentinel| {
- const colon_token = tree.prevToken(sentinel.firstToken());
- try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
- const sentinel_space = switch (op_tok_id) {
- .LBracket => Space.None,
- else => Space.Space,
- };
- try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);
- }
- switch (op_tok_id) {
- .Asterisk, .AsteriskAsterisk => {},
- .LBracket => try stream.writeByte(']'),
- else => unreachable,
- }
- if (ptr_info.allowzero_token) |allowzero_token| {
- try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
- }
- if (ptr_info.align_info) |align_info| {
- const lparen_token = tree.prevToken(align_info.node.firstToken());
- const align_token = tree.prevToken(lparen_token);
-
- try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
- try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
-
- try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
-
- if (align_info.bit_range) |bit_range| {
- const colon1 = tree.prevToken(bit_range.start.firstToken());
- const colon2 = tree.prevToken(bit_range.end.firstToken());
+ .BitNot => {
+ const bit_not = @fieldParentPtr(ast.Node.BitNot, "base", base);
+ try renderToken(tree, stream, bit_not.op_token, indent, start_col, Space.None);
+ return renderExpression(allocator, stream, tree, indent, start_col, bit_not.rhs, space);
+ },
+ .BoolNot => {
+ const bool_not = @fieldParentPtr(ast.Node.BoolNot, "base", base);
+ try renderToken(tree, stream, bool_not.op_token, indent, start_col, Space.None);
+ return renderExpression(allocator, stream, tree, indent, start_col, bool_not.rhs, space);
+ },
+ .Negation => {
+ const negation = @fieldParentPtr(ast.Node.Negation, "base", base);
+ try renderToken(tree, stream, negation.op_token, indent, start_col, Space.None);
+ return renderExpression(allocator, stream, tree, indent, start_col, negation.rhs, space);
+ },
+ .NegationWrap => {
+ const negation_wrap = @fieldParentPtr(ast.Node.NegationWrap, "base", base);
+ try renderToken(tree, stream, negation_wrap.op_token, indent, start_col, Space.None);
+ return renderExpression(allocator, stream, tree, indent, start_col, negation_wrap.rhs, space);
+ },
+ .OptionalType => {
+ const opt_type = @fieldParentPtr(ast.Node.OptionalType, "base", base);
+ try renderToken(tree, stream, opt_type.op_token, indent, start_col, Space.None);
+ return renderExpression(allocator, stream, tree, indent, start_col, opt_type.rhs, space);
+ },
+ .AddressOf => {
+ const addr_of = @fieldParentPtr(ast.Node.AddressOf, "base", base);
+ try renderToken(tree, stream, addr_of.op_token, indent, start_col, Space.None);
+ return renderExpression(allocator, stream, tree, indent, start_col, addr_of.rhs, space);
+ },
+ .Try => {
+ const try_node = @fieldParentPtr(ast.Node.Try, "base", base);
+ try renderToken(tree, stream, try_node.op_token, indent, start_col, Space.Space);
+ return renderExpression(allocator, stream, tree, indent, start_col, try_node.rhs, space);
+ },
+ .Resume => {
+ const resume_node = @fieldParentPtr(ast.Node.Resume, "base", base);
+ try renderToken(tree, stream, resume_node.op_token, indent, start_col, Space.Space);
+ return renderExpression(allocator, stream, tree, indent, start_col, resume_node.rhs, space);
+ },
+ .Await => {
+ const await_node = @fieldParentPtr(ast.Node.Await, "base", base);
+ try renderToken(tree, stream, await_node.op_token, indent, start_col, Space.Space);
+ return renderExpression(allocator, stream, tree, indent, start_col, await_node.rhs, space);
+ },
- try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
- try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
- try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
- try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
+ .ArrayType => {
+ const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);
+ return renderArrayType(
+ allocator,
+ stream,
+ tree,
+ indent,
+ start_col,
+ array_type.op_token,
+ array_type.rhs,
+ array_type.len_expr,
+ null,
+ space,
+ );
+ },
+ .ArrayTypeSentinel => {
+ const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base);
+ return renderArrayType(
+ allocator,
+ stream,
+ tree,
+ indent,
+ start_col,
+ array_type.op_token,
+ array_type.rhs,
+ array_type.len_expr,
+ array_type.sentinel,
+ space,
+ );
+ },
- const rparen_token = tree.nextToken(bit_range.end.lastToken());
- try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
- } else {
- const rparen_token = tree.nextToken(align_info.node.lastToken());
- try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
- }
- }
- if (ptr_info.const_token) |const_token| {
- try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const
- }
- if (ptr_info.volatile_token) |volatile_token| {
- try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile
- }
- },
+ .PtrType => {
+ const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);
+ const op_tok_id = tree.token_ids[ptr_type.op_token];
+ switch (op_tok_id) {
+ .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
+ .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)
+ try stream.writeAll("[*c")
+ else
+ try stream.writeAll("[*"),
+ else => unreachable,
+ }
+ if (ptr_type.ptr_info.sentinel) |sentinel| {
+ const colon_token = tree.prevToken(sentinel.firstToken());
+ try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
+ const sentinel_space = switch (op_tok_id) {
+ .LBracket => Space.None,
+ else => Space.Space,
+ };
+ try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);
+ }
+ switch (op_tok_id) {
+ .Asterisk, .AsteriskAsterisk => {},
+ .LBracket => try stream.writeByte(']'),
+ else => unreachable,
+ }
+ if (ptr_type.ptr_info.allowzero_token) |allowzero_token| {
+ try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
+ }
+ if (ptr_type.ptr_info.align_info) |align_info| {
+ const lparen_token = tree.prevToken(align_info.node.firstToken());
+ const align_token = tree.prevToken(lparen_token);
- .SliceType => |ptr_info| {
- try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [
- if (ptr_info.sentinel) |sentinel| {
- const colon_token = tree.prevToken(sentinel.firstToken());
- try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
- try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
- try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]
- } else {
- try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ]
- }
+ try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
+ try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
- if (ptr_info.allowzero_token) |allowzero_token| {
- try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
- }
- if (ptr_info.align_info) |align_info| {
- const lparen_token = tree.prevToken(align_info.node.firstToken());
- const align_token = tree.prevToken(lparen_token);
+ try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
- try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
- try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
+ if (align_info.bit_range) |bit_range| {
+ const colon1 = tree.prevToken(bit_range.start.firstToken());
+ const colon2 = tree.prevToken(bit_range.end.firstToken());
- try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
+ try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
+ try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
+ try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
+ try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
- if (align_info.bit_range) |bit_range| {
- const colon1 = tree.prevToken(bit_range.start.firstToken());
- const colon2 = tree.prevToken(bit_range.end.firstToken());
+ const rparen_token = tree.nextToken(bit_range.end.lastToken());
+ try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
+ } else {
+ const rparen_token = tree.nextToken(align_info.node.lastToken());
+ try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
+ }
+ }
+ if (ptr_type.ptr_info.const_token) |const_token| {
+ try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const
+ }
+ if (ptr_type.ptr_info.volatile_token) |volatile_token| {
+ try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile
+ }
+ return renderExpression(allocator, stream, tree, indent, start_col, ptr_type.rhs, space);
+ },
- try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
- try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
- try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
- try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
+ .SliceType => {
+ const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base);
+ try renderToken(tree, stream, slice_type.op_token, indent, start_col, Space.None); // [
+ if (slice_type.ptr_info.sentinel) |sentinel| {
+ const colon_token = tree.prevToken(sentinel.firstToken());
+ try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
+ try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
+ try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]
+ } else {
+ try renderToken(tree, stream, tree.nextToken(slice_type.op_token), indent, start_col, Space.None); // ]
+ }
- const rparen_token = tree.nextToken(bit_range.end.lastToken());
- try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
- } else {
- const rparen_token = tree.nextToken(align_info.node.lastToken());
- try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
- }
- }
- if (ptr_info.const_token) |const_token| {
- try renderToken(tree, stream, const_token, indent, start_col, Space.Space);
- }
- if (ptr_info.volatile_token) |volatile_token| {
- try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space);
- }
- },
+ if (slice_type.ptr_info.allowzero_token) |allowzero_token| {
+ try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
+ }
+ if (slice_type.ptr_info.align_info) |align_info| {
+ const lparen_token = tree.prevToken(align_info.node.firstToken());
+ const align_token = tree.prevToken(lparen_token);
- .ArrayType => |array_info| {
- const lbracket = prefix_op_node.op_token;
- const rbracket = tree.nextToken(if (array_info.sentinel) |sentinel|
- sentinel.lastToken()
- else
- array_info.len_expr.lastToken());
+ try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
+ try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
- try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
+ try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
- const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
- const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
- const new_indent = if (ends_with_comment) indent + indent_delta else indent;
- const new_space = if (ends_with_comment) Space.Newline else Space.None;
- try renderExpression(allocator, stream, tree, new_indent, start_col, array_info.len_expr, new_space);
- if (starts_with_comment) {
- try stream.writeByte('\n');
- }
- if (ends_with_comment or starts_with_comment) {
- try stream.writeByteNTimes(' ', indent);
- }
- if (array_info.sentinel) |sentinel| {
- const colon_token = tree.prevToken(sentinel.firstToken());
- try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
- try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
- }
- try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ]
- },
- .BitNot,
- .BoolNot,
- .Negation,
- .NegationWrap,
- .OptionalType,
- .AddressOf,
- => {
- try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None);
- },
+ if (align_info.bit_range) |bit_range| {
+ const colon1 = tree.prevToken(bit_range.start.firstToken());
+ const colon2 = tree.prevToken(bit_range.end.firstToken());
- .Try,
- .Resume,
- => {
- try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);
- },
+ try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
+ try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
+ try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
+ try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
- .Await => |await_info| {
- try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);
- },
+ const rparen_token = tree.nextToken(bit_range.end.lastToken());
+ try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
+ } else {
+ const rparen_token = tree.nextToken(align_info.node.lastToken());
+ try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
+ }
}
-
- return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space);
+ if (slice_type.ptr_info.const_token) |const_token| {
+ try renderToken(tree, stream, const_token, indent, start_col, Space.Space);
+ }
+ if (slice_type.ptr_info.volatile_token) |volatile_token| {
+ try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space);
+ }
+ return renderExpression(allocator, stream, tree, indent, start_col, slice_type.rhs, space);
},
.ArrayInitializer, .ArrayInitializerDot => {
@@ -2057,6 +2083,46 @@ fn renderExpression(
}
}
+fn renderArrayType(
+ allocator: *mem.Allocator,
+ stream: anytype,
+ tree: *ast.Tree,
+ indent: usize,
+ start_col: *usize,
+ lbracket: ast.TokenIndex,
+ rhs: *ast.Node,
+ len_expr: *ast.Node,
+ opt_sentinel: ?*ast.Node,
+ space: Space,
+) (@TypeOf(stream).Error || Error)!void {
+ const rbracket = tree.nextToken(if (opt_sentinel) |sentinel|
+ sentinel.lastToken()
+ else
+ len_expr.lastToken());
+
+ try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
+
+ const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
+ const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
+ const new_indent = if (ends_with_comment) indent + indent_delta else indent;
+ const new_space = if (ends_with_comment) Space.Newline else Space.None;
+ try renderExpression(allocator, stream, tree, new_indent, start_col, len_expr, new_space);
+ if (starts_with_comment) {
+ try stream.writeByte('\n');
+ }
+ if (ends_with_comment or starts_with_comment) {
+ try stream.writeByteNTimes(' ', indent);
+ }
+ if (opt_sentinel) |sentinel| {
+ const colon_token = tree.prevToken(sentinel.firstToken());
+ try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
+ try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
+ }
+ try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ]
+
+ return renderExpression(allocator, stream, tree, indent, start_col, rhs, space);
+}
+
fn renderAsmOutput(
allocator: *mem.Allocator,
stream: anytype,