Files
zig/lib/std/zig/ast.zig
Andrew Kelley ba0e3be5cf (breaking) rework stream abstractions
The main goal here is to make the function pointers comptime, so that we
don't have to do the crazy stuff with async function frames.

Since InStream, OutStream, and SeekableStream are already generic
across error sets, it's not really worse to make them generic across the
vtable as well.

See #764 for the open issue acknowledging that using generics for these
abstractions is a design flaw.

See #130 for the efforts to make these abstractions non-generic.

This commit also changes the OutStream API so that `write` returns
number of bytes written, and `writeAll` is the one that loops until the
whole buffer is written.
2020-03-10 15:32:32 -04:00

2331 lines
70 KiB
Zig

const std = @import("../std.zig");
const assert = std.debug.assert;
const testing = std.testing;
const SegmentedList = std.SegmentedList;
const mem = std.mem;
const Token = std.zig.Token;
pub const TokenIndex = usize;
pub const Tree = struct {
source: []const u8,
tokens: TokenList,
/// undefined on parse error (errors not empty)
root_node: *Node.Root,
arena_allocator: std.heap.ArenaAllocator,
errors: ErrorList,
generated: bool = false,
pub const TokenList = SegmentedList(Token, 64);
pub const ErrorList = SegmentedList(Error, 0);
pub fn deinit(self: *Tree) void {
// Here we copy the arena allocator into stack memory, because
// otherwise it would destroy itself while it was still working.
var arena_allocator = self.arena_allocator;
arena_allocator.deinit();
// self is destroyed
}
pub fn renderError(self: *Tree, parse_error: *Error, stream: var) !void {
return parse_error.render(&self.tokens, stream);
}
pub fn tokenSlice(self: *Tree, token_index: TokenIndex) []const u8 {
return self.tokenSlicePtr(self.tokens.at(token_index));
}
pub fn tokenSlicePtr(self: *Tree, token: *const Token) []const u8 {
return self.source[token.start..token.end];
}
pub fn getNodeSource(self: *const Tree, node: *const Node) []const u8 {
const first_token = self.tokens.at(node.firstToken());
const last_token = self.tokens.at(node.lastToken());
return self.source[first_token.start..last_token.end];
}
pub const Location = struct {
line: usize,
column: usize,
line_start: usize,
line_end: usize,
};
/// Return the Location of the token relative to the offset specified by `start_index`.
pub fn tokenLocationPtr(self: *Tree, start_index: usize, token: *const Token) Location {
var loc = Location{
.line = 0,
.column = 0,
.line_start = start_index,
.line_end = self.source.len,
};
if (self.generated)
return loc;
const token_start = token.start;
for (self.source[start_index..]) |c, i| {
if (i + start_index == token_start) {
loc.line_end = i + start_index;
while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') : (loc.line_end += 1) {}
return loc;
}
if (c == '\n') {
loc.line += 1;
loc.column = 0;
loc.line_start = i + 1;
} else {
loc.column += 1;
}
}
return loc;
}
pub fn tokenLocation(self: *Tree, start_index: usize, token_index: TokenIndex) Location {
return self.tokenLocationPtr(start_index, self.tokens.at(token_index));
}
pub fn tokensOnSameLine(self: *Tree, token1_index: TokenIndex, token2_index: TokenIndex) bool {
return self.tokensOnSameLinePtr(self.tokens.at(token1_index), self.tokens.at(token2_index));
}
pub fn tokensOnSameLinePtr(self: *Tree, token1: *const Token, token2: *const Token) bool {
return mem.indexOfScalar(u8, self.source[token1.end..token2.start], '\n') == null;
}
pub fn dump(self: *Tree) void {
self.root_node.base.dump(0);
}
/// Skips over comments
pub fn prevToken(self: *Tree, token_index: TokenIndex) TokenIndex {
var index = token_index - 1;
while (self.tokens.at(index).id == Token.Id.LineComment) {
index -= 1;
}
return index;
}
/// Skips over comments
pub fn nextToken(self: *Tree, token_index: TokenIndex) TokenIndex {
var index = token_index + 1;
while (self.tokens.at(index).id == Token.Id.LineComment) {
index += 1;
}
return index;
}
};
pub const Error = union(enum) {
InvalidToken: InvalidToken,
ExpectedContainerMembers: ExpectedContainerMembers,
ExpectedStringLiteral: ExpectedStringLiteral,
ExpectedIntegerLiteral: ExpectedIntegerLiteral,
ExpectedPubItem: ExpectedPubItem,
ExpectedIdentifier: ExpectedIdentifier,
ExpectedStatement: ExpectedStatement,
ExpectedVarDeclOrFn: ExpectedVarDeclOrFn,
ExpectedVarDecl: ExpectedVarDecl,
ExpectedReturnType: ExpectedReturnType,
ExpectedAggregateKw: ExpectedAggregateKw,
UnattachedDocComment: UnattachedDocComment,
ExpectedEqOrSemi: ExpectedEqOrSemi,
ExpectedSemiOrLBrace: ExpectedSemiOrLBrace,
ExpectedSemiOrElse: ExpectedSemiOrElse,
ExpectedLabelOrLBrace: ExpectedLabelOrLBrace,
ExpectedLBrace: ExpectedLBrace,
ExpectedColonOrRParen: ExpectedColonOrRParen,
ExpectedLabelable: ExpectedLabelable,
ExpectedInlinable: ExpectedInlinable,
ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType,
ExpectedCall: ExpectedCall,
ExpectedCallOrFnProto: ExpectedCallOrFnProto,
ExpectedSliceOrRBracket: ExpectedSliceOrRBracket,
ExtraAlignQualifier: ExtraAlignQualifier,
ExtraConstQualifier: ExtraConstQualifier,
ExtraVolatileQualifier: ExtraVolatileQualifier,
ExtraAllowZeroQualifier: ExtraAllowZeroQualifier,
ExpectedTypeExpr: ExpectedTypeExpr,
ExpectedPrimaryTypeExpr: ExpectedPrimaryTypeExpr,
ExpectedParamType: ExpectedParamType,
ExpectedExpr: ExpectedExpr,
ExpectedPrimaryExpr: ExpectedPrimaryExpr,
ExpectedToken: ExpectedToken,
ExpectedCommaOrEnd: ExpectedCommaOrEnd,
ExpectedParamList: ExpectedParamList,
ExpectedPayload: ExpectedPayload,
ExpectedBlockOrAssignment: ExpectedBlockOrAssignment,
ExpectedBlockOrExpression: ExpectedBlockOrExpression,
ExpectedExprOrAssignment: ExpectedExprOrAssignment,
ExpectedPrefixExpr: ExpectedPrefixExpr,
ExpectedLoopExpr: ExpectedLoopExpr,
ExpectedDerefOrUnwrap: ExpectedDerefOrUnwrap,
ExpectedSuffixOp: ExpectedSuffixOp,
pub fn render(self: *const Error, tokens: *Tree.TokenList, stream: var) !void {
switch (self.*) {
.InvalidToken => |*x| return x.render(tokens, stream),
.ExpectedContainerMembers => |*x| return x.render(tokens, stream),
.ExpectedStringLiteral => |*x| return x.render(tokens, stream),
.ExpectedIntegerLiteral => |*x| return x.render(tokens, stream),
.ExpectedPubItem => |*x| return x.render(tokens, stream),
.ExpectedIdentifier => |*x| return x.render(tokens, stream),
.ExpectedStatement => |*x| return x.render(tokens, stream),
.ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream),
.ExpectedVarDecl => |*x| return x.render(tokens, stream),
.ExpectedReturnType => |*x| return x.render(tokens, stream),
.ExpectedAggregateKw => |*x| return x.render(tokens, stream),
.UnattachedDocComment => |*x| return x.render(tokens, stream),
.ExpectedEqOrSemi => |*x| return x.render(tokens, stream),
.ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream),
.ExpectedSemiOrElse => |*x| return x.render(tokens, stream),
.ExpectedLabelOrLBrace => |*x| return x.render(tokens, stream),
.ExpectedLBrace => |*x| return x.render(tokens, stream),
.ExpectedColonOrRParen => |*x| return x.render(tokens, stream),
.ExpectedLabelable => |*x| return x.render(tokens, stream),
.ExpectedInlinable => |*x| return x.render(tokens, stream),
.ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream),
.ExpectedCall => |*x| return x.render(tokens, stream),
.ExpectedCallOrFnProto => |*x| return x.render(tokens, stream),
.ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream),
.ExtraAlignQualifier => |*x| return x.render(tokens, stream),
.ExtraConstQualifier => |*x| return x.render(tokens, stream),
.ExtraVolatileQualifier => |*x| return x.render(tokens, stream),
.ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream),
.ExpectedTypeExpr => |*x| return x.render(tokens, stream),
.ExpectedPrimaryTypeExpr => |*x| return x.render(tokens, stream),
.ExpectedParamType => |*x| return x.render(tokens, stream),
.ExpectedExpr => |*x| return x.render(tokens, stream),
.ExpectedPrimaryExpr => |*x| return x.render(tokens, stream),
.ExpectedToken => |*x| return x.render(tokens, stream),
.ExpectedCommaOrEnd => |*x| return x.render(tokens, stream),
.ExpectedParamList => |*x| return x.render(tokens, stream),
.ExpectedPayload => |*x| return x.render(tokens, stream),
.ExpectedBlockOrAssignment => |*x| return x.render(tokens, stream),
.ExpectedBlockOrExpression => |*x| return x.render(tokens, stream),
.ExpectedExprOrAssignment => |*x| return x.render(tokens, stream),
.ExpectedPrefixExpr => |*x| return x.render(tokens, stream),
.ExpectedLoopExpr => |*x| return x.render(tokens, stream),
.ExpectedDerefOrUnwrap => |*x| return x.render(tokens, stream),
.ExpectedSuffixOp => |*x| return x.render(tokens, stream),
}
}
pub fn loc(self: *const Error) TokenIndex {
switch (self.*) {
.InvalidToken => |x| return x.token,
.ExpectedContainerMembers => |x| return x.token,
.ExpectedStringLiteral => |x| return x.token,
.ExpectedIntegerLiteral => |x| return x.token,
.ExpectedPubItem => |x| return x.token,
.ExpectedIdentifier => |x| return x.token,
.ExpectedStatement => |x| return x.token,
.ExpectedVarDeclOrFn => |x| return x.token,
.ExpectedVarDecl => |x| return x.token,
.ExpectedReturnType => |x| return x.token,
.ExpectedAggregateKw => |x| return x.token,
.UnattachedDocComment => |x| return x.token,
.ExpectedEqOrSemi => |x| return x.token,
.ExpectedSemiOrLBrace => |x| return x.token,
.ExpectedSemiOrElse => |x| return x.token,
.ExpectedLabelOrLBrace => |x| return x.token,
.ExpectedLBrace => |x| return x.token,
.ExpectedColonOrRParen => |x| return x.token,
.ExpectedLabelable => |x| return x.token,
.ExpectedInlinable => |x| return x.token,
.ExpectedAsmOutputReturnOrType => |x| return x.token,
.ExpectedCall => |x| return x.node.firstToken(),
.ExpectedCallOrFnProto => |x| return x.node.firstToken(),
.ExpectedSliceOrRBracket => |x| return x.token,
.ExtraAlignQualifier => |x| return x.token,
.ExtraConstQualifier => |x| return x.token,
.ExtraVolatileQualifier => |x| return x.token,
.ExtraAllowZeroQualifier => |x| return x.token,
.ExpectedTypeExpr => |x| return x.token,
.ExpectedPrimaryTypeExpr => |x| return x.token,
.ExpectedParamType => |x| return x.token,
.ExpectedExpr => |x| return x.token,
.ExpectedPrimaryExpr => |x| return x.token,
.ExpectedToken => |x| return x.token,
.ExpectedCommaOrEnd => |x| return x.token,
.ExpectedParamList => |x| return x.token,
.ExpectedPayload => |x| return x.token,
.ExpectedBlockOrAssignment => |x| return x.token,
.ExpectedBlockOrExpression => |x| return x.token,
.ExpectedExprOrAssignment => |x| return x.token,
.ExpectedPrefixExpr => |x| return x.token,
.ExpectedLoopExpr => |x| return x.token,
.ExpectedDerefOrUnwrap => |x| return x.token,
.ExpectedSuffixOp => |x| return x.token,
}
}
pub const InvalidToken = SingleTokenError("Invalid token '{}'");
pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{}'");
pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{}'");
pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{}'");
pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{}'");
pub const ExpectedStatement = SingleTokenError("Expected statement, found '{}'");
pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{}'");
pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{}'");
pub const ExpectedReturnType = SingleTokenError("Expected 'var' or return type expression, found '{}'");
pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Id.Keyword_struct.symbol() ++ "', '" ++ Token.Id.Keyword_union.symbol() ++ "', or '" ++ Token.Id.Keyword_enum.symbol() ++ "', found '{}'");
pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{}'");
pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{}'");
pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{}'");
pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{}'");
pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{}'");
pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{}'");
pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{}'");
pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{}'");
pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Id.Identifier.symbol() ++ "', found '{}'");
pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{}'");
pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{}'");
pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{}'");
pub const ExpectedExpr = SingleTokenError("Expected expression, found '{}'");
pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{}'");
pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{}'");
pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{}'");
pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{}'");
pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{}'");
pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{}'");
pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{}'");
pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{}'");
pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{}'");
pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{}'");
pub const ExpectedParamType = SimpleError("Expected parameter type");
pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub");
pub const UnattachedDocComment = SimpleError("Unattached documentation comment");
pub const ExtraAlignQualifier = SimpleError("Extra align qualifier");
pub const ExtraConstQualifier = SimpleError("Extra const qualifier");
pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier");
pub const ExtraAllowZeroQualifier = SimpleError("Extra allowzero qualifier");
pub const ExpectedCall = struct {
node: *Node,
pub fn render(self: *const ExpectedCall, tokens: *Tree.TokenList, stream: var) !void {
return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", .{
@tagName(self.node.id),
});
}
};
pub const ExpectedCallOrFnProto = struct {
node: *Node,
pub fn render(self: *const ExpectedCallOrFnProto, tokens: *Tree.TokenList, stream: var) !void {
return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++
@tagName(Node.Id.FnProto) ++ ", found {}", .{@tagName(self.node.id)});
}
};
pub const ExpectedToken = struct {
token: TokenIndex,
expected_id: Token.Id,
pub fn render(self: *const ExpectedToken, tokens: *Tree.TokenList, stream: var) !void {
const found_token = tokens.at(self.token);
switch (found_token.id) {
.Invalid_ampersands => {
return stream.print("`&&` is invalid. Note that `and` is boolean AND.", .{});
},
.Invalid => {
return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()});
},
else => {
const token_name = found_token.id.symbol();
return stream.print("expected '{}', found '{}'", .{ self.expected_id.symbol(), token_name });
},
}
}
};
pub const ExpectedCommaOrEnd = struct {
token: TokenIndex,
end_id: Token.Id,
pub fn render(self: *const ExpectedCommaOrEnd, tokens: *Tree.TokenList, stream: var) !void {
const actual_token = tokens.at(self.token);
return stream.print("expected ',' or '{}', found '{}'", .{
self.end_id.symbol(),
actual_token.id.symbol(),
});
}
};
fn SingleTokenError(comptime msg: []const u8) type {
return struct {
const ThisError = @This();
token: TokenIndex,
pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void {
const actual_token = tokens.at(self.token);
return stream.print(msg, .{actual_token.id.symbol()});
}
};
}
fn SimpleError(comptime msg: []const u8) type {
return struct {
const ThisError = @This();
token: TokenIndex,
pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void {
return stream.writeAll(msg);
}
};
}
};
pub const Node = struct {
id: Id,
pub const Id = enum {
// Top level
Root,
Use,
TestDecl,
// Statements
VarDecl,
Defer,
// Operators
InfixOp,
PrefixOp,
SuffixOp,
// Control flow
Switch,
While,
For,
If,
ControlFlowExpression,
Suspend,
// Type expressions
VarType,
ErrorType,
FnProto,
AnyFrameType,
// Primary expressions
IntegerLiteral,
FloatLiteral,
EnumLiteral,
StringLiteral,
MultilineStringLiteral,
CharLiteral,
BoolLiteral,
NullLiteral,
UndefinedLiteral,
Unreachable,
Identifier,
GroupedExpression,
BuiltinCall,
ErrorSetDecl,
ContainerDecl,
Asm,
Comptime,
Noasync,
Block,
// Misc
DocComment,
SwitchCase,
SwitchElse,
Else,
Payload,
PointerPayload,
PointerIndexPayload,
ContainerField,
ErrorTag,
AsmInput,
AsmOutput,
ParamDecl,
FieldInitializer,
};
pub fn cast(base: *Node, comptime T: type) ?*T {
if (base.id == comptime typeToId(T)) {
return @fieldParentPtr(T, "base", base);
}
return null;
}
pub fn iterate(base: *Node, index: usize) ?*Node {
inline for (@typeInfo(Id).Enum.fields) |f| {
if (base.id == @field(Id, f.name)) {
const T = @field(Node, f.name);
return @fieldParentPtr(T, "base", base).iterate(index);
}
}
unreachable;
}
pub fn firstToken(base: *const Node) TokenIndex {
inline for (@typeInfo(Id).Enum.fields) |f| {
if (base.id == @field(Id, f.name)) {
const T = @field(Node, f.name);
return @fieldParentPtr(T, "base", base).firstToken();
}
}
unreachable;
}
pub fn lastToken(base: *const Node) TokenIndex {
inline for (@typeInfo(Id).Enum.fields) |f| {
if (base.id == @field(Id, f.name)) {
const T = @field(Node, f.name);
return @fieldParentPtr(T, "base", base).lastToken();
}
}
unreachable;
}
pub fn typeToId(comptime T: type) Id {
inline for (@typeInfo(Id).Enum.fields) |f| {
if (T == @field(Node, f.name)) {
return @field(Id, f.name);
}
}
unreachable;
}
pub fn requireSemiColon(base: *const Node) bool {
var n = base;
while (true) {
switch (n.id) {
.Root,
.ContainerField,
.ParamDecl,
.Block,
.Payload,
.PointerPayload,
.PointerIndexPayload,
.Switch,
.SwitchCase,
.SwitchElse,
.FieldInitializer,
.DocComment,
.TestDecl,
=> return false,
.While => {
const while_node = @fieldParentPtr(While, "base", n);
if (while_node.@"else") |@"else"| {
n = &@"else".base;
continue;
}
return while_node.body.id != .Block;
},
.For => {
const for_node = @fieldParentPtr(For, "base", n);
if (for_node.@"else") |@"else"| {
n = &@"else".base;
continue;
}
return for_node.body.id != .Block;
},
.If => {
const if_node = @fieldParentPtr(If, "base", n);
if (if_node.@"else") |@"else"| {
n = &@"else".base;
continue;
}
return if_node.body.id != .Block;
},
.Else => {
const else_node = @fieldParentPtr(Else, "base", n);
n = else_node.body;
continue;
},
.Defer => {
const defer_node = @fieldParentPtr(Defer, "base", n);
return defer_node.expr.id != .Block;
},
.Comptime => {
const comptime_node = @fieldParentPtr(Comptime, "base", n);
return comptime_node.expr.id != .Block;
},
.Suspend => {
const suspend_node = @fieldParentPtr(Suspend, "base", n);
if (suspend_node.body) |body| {
return body.id != .Block;
}
return true;
},
.Noasync => {
const noasync_node = @fieldParentPtr(Noasync, "base", n);
return noasync_node.expr.id != .Block;
},
else => return true,
}
}
}
pub fn dump(self: *Node, indent: usize) void {
{
var i: usize = 0;
while (i < indent) : (i += 1) {
std.debug.warn(" ", .{});
}
}
std.debug.warn("{}\n", .{@tagName(self.id)});
var child_i: usize = 0;
while (self.iterate(child_i)) |child| : (child_i += 1) {
child.dump(indent + 2);
}
}
pub const Root = struct {
base: Node = Node{ .id = .Root },
decls: DeclList,
eof_token: TokenIndex,
pub const DeclList = SegmentedList(*Node, 4);
pub fn iterate(self: *Root, index: usize) ?*Node {
if (index < self.decls.len) {
return self.decls.at(index).*;
}
return null;
}
pub fn firstToken(self: *const Root) TokenIndex {
return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken();
}
pub fn lastToken(self: *const Root) TokenIndex {
return if (self.decls.len == 0) self.eof_token else (self.decls.at(self.decls.len - 1).*).lastToken();
}
};
pub const VarDecl = struct {
base: Node = Node{ .id = .VarDecl },
doc_comments: ?*DocComment,
visib_token: ?TokenIndex,
thread_local_token: ?TokenIndex,
name_token: TokenIndex,
eq_token: ?TokenIndex,
mut_token: TokenIndex,
comptime_token: ?TokenIndex,
extern_export_token: ?TokenIndex,
lib_name: ?*Node,
type_node: ?*Node,
align_node: ?*Node,
section_node: ?*Node,
init_node: ?*Node,
semicolon_token: TokenIndex,
pub fn iterate(self: *VarDecl, index: usize) ?*Node {
var i = index;
if (self.type_node) |type_node| {
if (i < 1) return type_node;
i -= 1;
}
if (self.align_node) |align_node| {
if (i < 1) return align_node;
i -= 1;
}
if (self.section_node) |section_node| {
if (i < 1) return section_node;
i -= 1;
}
if (self.init_node) |init_node| {
if (i < 1) return init_node;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const VarDecl) TokenIndex {
if (self.visib_token) |visib_token| return visib_token;
if (self.thread_local_token) |thread_local_token| return thread_local_token;
if (self.comptime_token) |comptime_token| return comptime_token;
if (self.extern_export_token) |extern_export_token| return extern_export_token;
assert(self.lib_name == null);
return self.mut_token;
}
pub fn lastToken(self: *const VarDecl) TokenIndex {
return self.semicolon_token;
}
};
pub const Use = struct {
base: Node = Node{ .id = .Use },
doc_comments: ?*DocComment,
visib_token: ?TokenIndex,
use_token: TokenIndex,
expr: *Node,
semicolon_token: TokenIndex,
pub fn iterate(self: *Use, index: usize) ?*Node {
var i = index;
if (i < 1) return self.expr;
i -= 1;
return null;
}
pub fn firstToken(self: *const Use) TokenIndex {
if (self.visib_token) |visib_token| return visib_token;
return self.use_token;
}
pub fn lastToken(self: *const Use) TokenIndex {
return self.semicolon_token;
}
};
pub const ErrorSetDecl = struct {
base: Node = Node{ .id = .ErrorSetDecl },
error_token: TokenIndex,
decls: DeclList,
rbrace_token: TokenIndex,
pub const DeclList = SegmentedList(*Node, 2);
pub fn iterate(self: *ErrorSetDecl, index: usize) ?*Node {
var i = index;
if (i < self.decls.len) return self.decls.at(i).*;
i -= self.decls.len;
return null;
}
pub fn firstToken(self: *const ErrorSetDecl) TokenIndex {
return self.error_token;
}
pub fn lastToken(self: *const ErrorSetDecl) TokenIndex {
return self.rbrace_token;
}
};
pub const ContainerDecl = struct {
base: Node = Node{ .id = .ContainerDecl },
layout_token: ?TokenIndex,
kind_token: TokenIndex,
init_arg_expr: InitArg,
fields_and_decls: DeclList,
lbrace_token: TokenIndex,
rbrace_token: TokenIndex,
pub const DeclList = Root.DeclList;
pub const InitArg = union(enum) {
None,
Enum: ?*Node,
Type: *Node,
};
pub fn iterate(self: *ContainerDecl, index: usize) ?*Node {
var i = index;
switch (self.init_arg_expr) {
InitArg.Type => |t| {
if (i < 1) return t;
i -= 1;
},
InitArg.None, InitArg.Enum => {},
}
if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i).*;
i -= self.fields_and_decls.len;
return null;
}
pub fn firstToken(self: *const ContainerDecl) TokenIndex {
if (self.layout_token) |layout_token| {
return layout_token;
}
return self.kind_token;
}
pub fn lastToken(self: *const ContainerDecl) TokenIndex {
return self.rbrace_token;
}
};
pub const ContainerField = struct {
base: Node = Node{ .id = .ContainerField },
doc_comments: ?*DocComment,
comptime_token: ?TokenIndex,
name_token: TokenIndex,
type_expr: ?*Node,
value_expr: ?*Node,
align_expr: ?*Node,
pub fn iterate(self: *ContainerField, index: usize) ?*Node {
var i = index;
if (self.type_expr) |type_expr| {
if (i < 1) return type_expr;
i -= 1;
}
if (self.align_expr) |align_expr| {
if (i < 1) return align_expr;
i -= 1;
}
if (self.value_expr) |value_expr| {
if (i < 1) return value_expr;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const ContainerField) TokenIndex {
return self.comptime_token orelse self.name_token;
}
pub fn lastToken(self: *const ContainerField) TokenIndex {
if (self.value_expr) |value_expr| {
return value_expr.lastToken();
}
if (self.align_expr) |align_expr| {
// The expression refers to what's inside the parenthesis, the
// last token is the closing one
return align_expr.lastToken() + 1;
}
if (self.type_expr) |type_expr| {
return type_expr.lastToken();
}
return self.name_token;
}
};
pub const ErrorTag = struct {
base: Node = Node{ .id = .ErrorTag },
doc_comments: ?*DocComment,
name_token: TokenIndex,
pub fn iterate(self: *ErrorTag, index: usize) ?*Node {
var i = index;
if (self.doc_comments) |comments| {
if (i < 1) return &comments.base;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const ErrorTag) TokenIndex {
return self.name_token;
}
pub fn lastToken(self: *const ErrorTag) TokenIndex {
return self.name_token;
}
};
pub const Identifier = struct {
base: Node = Node{ .id = .Identifier },
token: TokenIndex,
pub fn iterate(self: *Identifier, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const Identifier) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const Identifier) TokenIndex {
return self.token;
}
};
pub const FnProto = struct {
base: Node = Node{ .id = .FnProto },
doc_comments: ?*DocComment,
visib_token: ?TokenIndex,
fn_token: TokenIndex,
name_token: ?TokenIndex,
params: ParamList,
return_type: ReturnType,
var_args_token: ?TokenIndex,
extern_export_inline_token: ?TokenIndex,
cc_token: ?TokenIndex,
body_node: ?*Node,
lib_name: ?*Node, // populated if this is an extern declaration
align_expr: ?*Node, // populated if align(A) is present
section_expr: ?*Node, // populated if linksection(A) is present
callconv_expr: ?*Node, // populated if callconv(A) is present
pub const ParamList = SegmentedList(*Node, 2);
pub const ReturnType = union(enum) {
Explicit: *Node,
InferErrorSet: *Node,
};
pub fn iterate(self: *FnProto, index: usize) ?*Node {
var i = index;
if (self.lib_name) |lib_name| {
if (i < 1) return lib_name;
i -= 1;
}
if (i < self.params.len) return self.params.at(self.params.len - i - 1).*;
i -= self.params.len;
if (self.align_expr) |align_expr| {
if (i < 1) return align_expr;
i -= 1;
}
if (self.section_expr) |section_expr| {
if (i < 1) return section_expr;
i -= 1;
}
switch (self.return_type) {
// TODO allow this and next prong to share bodies since the types are the same
ReturnType.Explicit => |node| {
if (i < 1) return node;
i -= 1;
},
ReturnType.InferErrorSet => |node| {
if (i < 1) return node;
i -= 1;
},
}
if (self.body_node) |body_node| {
if (i < 1) return body_node;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const FnProto) TokenIndex {
if (self.visib_token) |visib_token| return visib_token;
if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
assert(self.lib_name == null);
if (self.cc_token) |cc_token| return cc_token;
return self.fn_token;
}
pub fn lastToken(self: *const FnProto) TokenIndex {
if (self.body_node) |body_node| return body_node.lastToken();
switch (self.return_type) {
// TODO allow this and next prong to share bodies since the types are the same
ReturnType.Explicit => |node| return node.lastToken(),
ReturnType.InferErrorSet => |node| return node.lastToken(),
}
}
};
pub const AnyFrameType = struct {
base: Node = Node{ .id = .AnyFrameType },
anyframe_token: TokenIndex,
result: ?Result,
pub const Result = struct {
arrow_token: TokenIndex,
return_type: *Node,
};
pub fn iterate(self: *AnyFrameType, index: usize) ?*Node {
var i = index;
if (self.result) |result| {
if (i < 1) return result.return_type;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const AnyFrameType) TokenIndex {
return self.anyframe_token;
}
pub fn lastToken(self: *const AnyFrameType) TokenIndex {
if (self.result) |result| return result.return_type.lastToken();
return self.anyframe_token;
}
};
pub const ParamDecl = struct {
base: Node = Node{ .id = .ParamDecl },
doc_comments: ?*DocComment,
comptime_token: ?TokenIndex,
noalias_token: ?TokenIndex,
name_token: ?TokenIndex,
type_node: *Node,
var_args_token: ?TokenIndex,
pub fn iterate(self: *ParamDecl, index: usize) ?*Node {
var i = index;
if (i < 1) {
return if (self.var_args_token == null) self.type_node else null;
}
i -= 1;
return null;
}
pub fn firstToken(self: *const ParamDecl) TokenIndex {
if (self.comptime_token) |comptime_token| return comptime_token;
if (self.noalias_token) |noalias_token| return noalias_token;
if (self.name_token) |name_token| return name_token;
return self.type_node.firstToken();
}
pub fn lastToken(self: *const ParamDecl) TokenIndex {
if (self.var_args_token) |var_args_token| return var_args_token;
return self.type_node.lastToken();
}
};
pub const Block = struct {
base: Node = Node{ .id = .Block },
label: ?TokenIndex,
lbrace: TokenIndex,
statements: StatementList,
rbrace: TokenIndex,
pub const StatementList = Root.DeclList;
pub fn iterate(self: *Block, index: usize) ?*Node {
var i = index;
if (i < self.statements.len) return self.statements.at(i).*;
i -= self.statements.len;
return null;
}
pub fn firstToken(self: *const Block) TokenIndex {
if (self.label) |label| {
return label;
}
return self.lbrace;
}
pub fn lastToken(self: *const Block) TokenIndex {
return self.rbrace;
}
};
pub const Defer = struct {
base: Node = Node{ .id = .Defer },
defer_token: TokenIndex,
expr: *Node,
pub fn iterate(self: *Defer, index: usize) ?*Node {
var i = index;
if (i < 1) return self.expr;
i -= 1;
return null;
}
pub fn firstToken(self: *const Defer) TokenIndex {
return self.defer_token;
}
pub fn lastToken(self: *const Defer) TokenIndex {
return self.expr.lastToken();
}
};
pub const Comptime = struct {
base: Node = Node{ .id = .Comptime },
doc_comments: ?*DocComment,
comptime_token: TokenIndex,
expr: *Node,
pub fn iterate(self: *Comptime, index: usize) ?*Node {
var i = index;
if (i < 1) return self.expr;
i -= 1;
return null;
}
pub fn firstToken(self: *const Comptime) TokenIndex {
return self.comptime_token;
}
pub fn lastToken(self: *const Comptime) TokenIndex {
return self.expr.lastToken();
}
};
pub const Noasync = struct {
base: Node = Node{ .id = .Noasync },
noasync_token: TokenIndex,
expr: *Node,
pub fn iterate(self: *Noasync, index: usize) ?*Node {
var i = index;
if (i < 1) return self.expr;
i -= 1;
return null;
}
pub fn firstToken(self: *const Noasync) TokenIndex {
return self.noasync_token;
}
pub fn lastToken(self: *const Noasync) TokenIndex {
return self.expr.lastToken();
}
};
pub const Payload = struct {
base: Node = Node{ .id = .Payload },
lpipe: TokenIndex,
error_symbol: *Node,
rpipe: TokenIndex,
pub fn iterate(self: *Payload, index: usize) ?*Node {
var i = index;
if (i < 1) return self.error_symbol;
i -= 1;
return null;
}
pub fn firstToken(self: *const Payload) TokenIndex {
return self.lpipe;
}
pub fn lastToken(self: *const Payload) TokenIndex {
return self.rpipe;
}
};
pub const PointerPayload = struct {
base: Node = Node{ .id = .PointerPayload },
lpipe: TokenIndex,
ptr_token: ?TokenIndex,
value_symbol: *Node,
rpipe: TokenIndex,
pub fn iterate(self: *PointerPayload, index: usize) ?*Node {
var i = index;
if (i < 1) return self.value_symbol;
i -= 1;
return null;
}
pub fn firstToken(self: *const PointerPayload) TokenIndex {
return self.lpipe;
}
pub fn lastToken(self: *const PointerPayload) TokenIndex {
return self.rpipe;
}
};
pub const PointerIndexPayload = struct {
base: Node = Node{ .id = .PointerIndexPayload },
lpipe: TokenIndex,
ptr_token: ?TokenIndex,
value_symbol: *Node,
index_symbol: ?*Node,
rpipe: TokenIndex,
pub fn iterate(self: *PointerIndexPayload, index: usize) ?*Node {
var i = index;
if (i < 1) return self.value_symbol;
i -= 1;
if (self.index_symbol) |index_symbol| {
if (i < 1) return index_symbol;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const PointerIndexPayload) TokenIndex {
return self.lpipe;
}
pub fn lastToken(self: *const PointerIndexPayload) TokenIndex {
return self.rpipe;
}
};
pub const Else = struct {
base: Node = Node{ .id = .Else },
else_token: TokenIndex,
payload: ?*Node,
body: *Node,
pub fn iterate(self: *Else, index: usize) ?*Node {
var i = index;
if (self.payload) |payload| {
if (i < 1) return payload;
i -= 1;
}
if (i < 1) return self.body;
i -= 1;
return null;
}
pub fn firstToken(self: *const Else) TokenIndex {
return self.else_token;
}
pub fn lastToken(self: *const Else) TokenIndex {
return self.body.lastToken();
}
};
pub const Switch = struct {
base: Node = Node{ .id = .Switch },
switch_token: TokenIndex,
expr: *Node,
/// these must be SwitchCase nodes
cases: CaseList,
rbrace: TokenIndex,
pub const CaseList = SegmentedList(*Node, 2);
pub fn iterate(self: *Switch, index: usize) ?*Node {
var i = index;
if (i < 1) return self.expr;
i -= 1;
if (i < self.cases.len) return self.cases.at(i).*;
i -= self.cases.len;
return null;
}
pub fn firstToken(self: *const Switch) TokenIndex {
return self.switch_token;
}
pub fn lastToken(self: *const Switch) TokenIndex {
return self.rbrace;
}
};
pub const SwitchCase = struct {
base: Node = Node{ .id = .SwitchCase },
items: ItemList,
arrow_token: TokenIndex,
payload: ?*Node,
expr: *Node,
pub const ItemList = SegmentedList(*Node, 1);
pub fn iterate(self: *SwitchCase, index: usize) ?*Node {
var i = index;
if (i < self.items.len) return self.items.at(i).*;
i -= self.items.len;
if (self.payload) |payload| {
if (i < 1) return payload;
i -= 1;
}
if (i < 1) return self.expr;
i -= 1;
return null;
}
pub fn firstToken(self: *const SwitchCase) TokenIndex {
return (self.items.at(0).*).firstToken();
}
pub fn lastToken(self: *const SwitchCase) TokenIndex {
return self.expr.lastToken();
}
};
pub const SwitchElse = struct {
base: Node = Node{ .id = .SwitchElse },
token: TokenIndex,
pub fn iterate(self: *SwitchElse, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const SwitchElse) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const SwitchElse) TokenIndex {
return self.token;
}
};
pub const While = struct {
base: Node = Node{ .id = .While },
label: ?TokenIndex,
inline_token: ?TokenIndex,
while_token: TokenIndex,
condition: *Node,
payload: ?*Node,
continue_expr: ?*Node,
body: *Node,
@"else": ?*Else,
pub fn iterate(self: *While, index: usize) ?*Node {
var i = index;
if (i < 1) return self.condition;
i -= 1;
if (self.payload) |payload| {
if (i < 1) return payload;
i -= 1;
}
if (self.continue_expr) |continue_expr| {
if (i < 1) return continue_expr;
i -= 1;
}
if (i < 1) return self.body;
i -= 1;
if (self.@"else") |@"else"| {
if (i < 1) return &@"else".base;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const While) TokenIndex {
if (self.label) |label| {
return label;
}
if (self.inline_token) |inline_token| {
return inline_token;
}
return self.while_token;
}
pub fn lastToken(self: *const While) TokenIndex {
if (self.@"else") |@"else"| {
return @"else".body.lastToken();
}
return self.body.lastToken();
}
};
pub const For = struct {
base: Node = Node{ .id = .For },
label: ?TokenIndex,
inline_token: ?TokenIndex,
for_token: TokenIndex,
array_expr: *Node,
payload: *Node,
body: *Node,
@"else": ?*Else,
pub fn iterate(self: *For, index: usize) ?*Node {
var i = index;
if (i < 1) return self.array_expr;
i -= 1;
if (i < 1) return self.payload;
i -= 1;
if (i < 1) return self.body;
i -= 1;
if (self.@"else") |@"else"| {
if (i < 1) return &@"else".base;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const For) TokenIndex {
if (self.label) |label| {
return label;
}
if (self.inline_token) |inline_token| {
return inline_token;
}
return self.for_token;
}
pub fn lastToken(self: *const For) TokenIndex {
if (self.@"else") |@"else"| {
return @"else".body.lastToken();
}
return self.body.lastToken();
}
};
pub const If = struct {
base: Node = Node{ .id = .If },
if_token: TokenIndex,
condition: *Node,
payload: ?*Node,
body: *Node,
@"else": ?*Else,
pub fn iterate(self: *If, index: usize) ?*Node {
var i = index;
if (i < 1) return self.condition;
i -= 1;
if (self.payload) |payload| {
if (i < 1) return payload;
i -= 1;
}
if (i < 1) return self.body;
i -= 1;
if (self.@"else") |@"else"| {
if (i < 1) return &@"else".base;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const If) TokenIndex {
return self.if_token;
}
pub fn lastToken(self: *const If) TokenIndex {
if (self.@"else") |@"else"| {
return @"else".body.lastToken();
}
return self.body.lastToken();
}
};
pub const InfixOp = struct {
base: Node = Node{ .id = .InfixOp },
op_token: TokenIndex,
lhs: *Node,
op: Op,
rhs: *Node,
pub const Op = union(enum) {
Add,
AddWrap,
ArrayCat,
ArrayMult,
Assign,
AssignBitAnd,
AssignBitOr,
AssignBitShiftLeft,
AssignBitShiftRight,
AssignBitXor,
AssignDiv,
AssignSub,
AssignSubWrap,
AssignMod,
AssignAdd,
AssignAddWrap,
AssignMul,
AssignMulWrap,
BangEqual,
BitAnd,
BitOr,
BitShiftLeft,
BitShiftRight,
BitXor,
BoolAnd,
BoolOr,
Catch: ?*Node,
Div,
EqualEqual,
ErrorUnion,
GreaterOrEqual,
GreaterThan,
LessOrEqual,
LessThan,
MergeErrorSets,
Mod,
Mul,
MulWrap,
Period,
Range,
Sub,
SubWrap,
UnwrapOptional,
};
pub fn iterate(self: *InfixOp, index: usize) ?*Node {
var i = index;
if (i < 1) return self.lhs;
i -= 1;
switch (self.op) {
Op.Catch => |maybe_payload| {
if (maybe_payload) |payload| {
if (i < 1) return payload;
i -= 1;
}
},
Op.Add,
Op.AddWrap,
Op.ArrayCat,
Op.ArrayMult,
Op.Assign,
Op.AssignBitAnd,
Op.AssignBitOr,
Op.AssignBitShiftLeft,
Op.AssignBitShiftRight,
Op.AssignBitXor,
Op.AssignDiv,
Op.AssignSub,
Op.AssignSubWrap,
Op.AssignMod,
Op.AssignAdd,
Op.AssignAddWrap,
Op.AssignMul,
Op.AssignMulWrap,
Op.BangEqual,
Op.BitAnd,
Op.BitOr,
Op.BitShiftLeft,
Op.BitShiftRight,
Op.BitXor,
Op.BoolAnd,
Op.BoolOr,
Op.Div,
Op.EqualEqual,
Op.ErrorUnion,
Op.GreaterOrEqual,
Op.GreaterThan,
Op.LessOrEqual,
Op.LessThan,
Op.MergeErrorSets,
Op.Mod,
Op.Mul,
Op.MulWrap,
Op.Period,
Op.Range,
Op.Sub,
Op.SubWrap,
Op.UnwrapOptional,
=> {},
}
if (i < 1) return self.rhs;
i -= 1;
return null;
}
pub fn firstToken(self: *const InfixOp) TokenIndex {
return self.lhs.firstToken();
}
pub fn lastToken(self: *const InfixOp) TokenIndex {
return self.rhs.lastToken();
}
};
pub const PrefixOp = struct {
base: Node = Node{ .id = .PrefixOp },
op_token: TokenIndex,
op: Op,
rhs: *Node,
pub const Op = union(enum) {
AddressOf,
ArrayType: ArrayInfo,
Await,
BitNot,
BoolNot,
Cancel,
OptionalType,
Negation,
NegationWrap,
Resume,
PtrType: PtrInfo,
SliceType: PtrInfo,
Try,
};
pub const ArrayInfo = struct {
len_expr: *Node,
sentinel: ?*Node,
};
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,
};
};
};
pub fn iterate(self: *PrefixOp, index: usize) ?*Node {
var i = index;
switch (self.op) {
// TODO https://github.com/ziglang/zig/issues/1107
Op.SliceType => |addr_of_info| {
if (addr_of_info.sentinel) |sentinel| {
if (i < 1) return sentinel;
i -= 1;
}
if (addr_of_info.align_info) |align_info| {
if (i < 1) return align_info.node;
i -= 1;
}
},
Op.PtrType => |addr_of_info| {
if (addr_of_info.align_info) |align_info| {
if (i < 1) return align_info.node;
i -= 1;
}
},
Op.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;
}
},
Op.AddressOf,
Op.Await,
Op.BitNot,
Op.BoolNot,
Op.Cancel,
Op.OptionalType,
Op.Negation,
Op.NegationWrap,
Op.Try,
Op.Resume,
=> {},
}
if (i < 1) return self.rhs;
i -= 1;
return null;
}
pub fn firstToken(self: *const PrefixOp) TokenIndex {
return self.op_token;
}
pub fn lastToken(self: *const PrefixOp) TokenIndex {
return self.rhs.lastToken();
}
};
pub const FieldInitializer = struct {
base: Node = Node{ .id = .FieldInitializer },
period_token: TokenIndex,
name_token: TokenIndex,
expr: *Node,
pub fn iterate(self: *FieldInitializer, index: usize) ?*Node {
var i = index;
if (i < 1) return self.expr;
i -= 1;
return null;
}
pub fn firstToken(self: *const FieldInitializer) TokenIndex {
return self.period_token;
}
pub fn lastToken(self: *const FieldInitializer) TokenIndex {
return self.expr.lastToken();
}
};
pub const SuffixOp = struct {
base: Node = Node{ .id = .SuffixOp },
lhs: Lhs,
op: Op,
rtoken: TokenIndex,
pub const Lhs = union(enum) {
node: *Node,
dot: TokenIndex,
};
pub const Op = union(enum) {
Call: Call,
ArrayAccess: *Node,
Slice: Slice,
ArrayInitializer: InitList,
StructInitializer: InitList,
Deref,
UnwrapOptional,
pub const InitList = SegmentedList(*Node, 2);
pub const Call = struct {
params: ParamList,
async_token: ?TokenIndex,
pub const ParamList = SegmentedList(*Node, 2);
};
pub const Slice = struct {
start: *Node,
end: ?*Node,
sentinel: ?*Node,
};
};
pub fn iterate(self: *SuffixOp, index: usize) ?*Node {
var i = index;
switch (self.lhs) {
.node => |node| {
if (i == 0) return node;
i -= 1;
},
.dot => {},
}
switch (self.op) {
.Call => |*call_info| {
if (i < call_info.params.len) return call_info.params.at(i).*;
i -= call_info.params.len;
},
.ArrayAccess => |index_expr| {
if (i < 1) return index_expr;
i -= 1;
},
.Slice => |range| {
if (i < 1) return range.start;
i -= 1;
if (range.end) |end| {
if (i < 1) return end;
i -= 1;
}
if (range.sentinel) |sentinel| {
if (i < 1) return sentinel;
i -= 1;
}
},
.ArrayInitializer => |*exprs| {
if (i < exprs.len) return exprs.at(i).*;
i -= exprs.len;
},
.StructInitializer => |*fields| {
if (i < fields.len) return fields.at(i).*;
i -= fields.len;
},
.UnwrapOptional,
.Deref,
=> {},
}
return null;
}
pub fn firstToken(self: *const SuffixOp) TokenIndex {
switch (self.op) {
.Call => |*call_info| if (call_info.async_token) |async_token| return async_token,
else => {},
}
switch (self.lhs) {
.node => |node| return node.firstToken(),
.dot => |dot| return dot,
}
}
pub fn lastToken(self: *const SuffixOp) TokenIndex {
return self.rtoken;
}
};
pub const GroupedExpression = struct {
base: Node = Node{ .id = .GroupedExpression },
lparen: TokenIndex,
expr: *Node,
rparen: TokenIndex,
pub fn iterate(self: *GroupedExpression, index: usize) ?*Node {
var i = index;
if (i < 1) return self.expr;
i -= 1;
return null;
}
pub fn firstToken(self: *const GroupedExpression) TokenIndex {
return self.lparen;
}
pub fn lastToken(self: *const GroupedExpression) TokenIndex {
return self.rparen;
}
};
pub const ControlFlowExpression = struct {
base: Node = Node{ .id = .ControlFlowExpression },
ltoken: TokenIndex,
kind: Kind,
rhs: ?*Node,
pub const Kind = union(enum) {
Break: ?*Node,
Continue: ?*Node,
Return,
};
pub fn iterate(self: *ControlFlowExpression, index: usize) ?*Node {
var i = index;
switch (self.kind) {
Kind.Break => |maybe_label| {
if (maybe_label) |label| {
if (i < 1) return label;
i -= 1;
}
},
Kind.Continue => |maybe_label| {
if (maybe_label) |label| {
if (i < 1) return label;
i -= 1;
}
},
Kind.Return => {},
}
if (self.rhs) |rhs| {
if (i < 1) return rhs;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const ControlFlowExpression) TokenIndex {
return self.ltoken;
}
pub fn lastToken(self: *const ControlFlowExpression) TokenIndex {
if (self.rhs) |rhs| {
return rhs.lastToken();
}
switch (self.kind) {
Kind.Break => |maybe_label| {
if (maybe_label) |label| {
return label.lastToken();
}
},
Kind.Continue => |maybe_label| {
if (maybe_label) |label| {
return label.lastToken();
}
},
Kind.Return => return self.ltoken,
}
return self.ltoken;
}
};
pub const Suspend = struct {
base: Node = Node{ .id = .Suspend },
suspend_token: TokenIndex,
body: ?*Node,
pub fn iterate(self: *Suspend, index: usize) ?*Node {
var i = index;
if (self.body) |body| {
if (i < 1) return body;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const Suspend) TokenIndex {
return self.suspend_token;
}
pub fn lastToken(self: *const Suspend) TokenIndex {
if (self.body) |body| {
return body.lastToken();
}
return self.suspend_token;
}
};
pub const IntegerLiteral = struct {
base: Node = Node{ .id = .IntegerLiteral },
token: TokenIndex,
pub fn iterate(self: *IntegerLiteral, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const IntegerLiteral) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const IntegerLiteral) TokenIndex {
return self.token;
}
};
pub const EnumLiteral = struct {
base: Node = Node{ .id = .EnumLiteral },
dot: TokenIndex,
name: TokenIndex,
pub fn iterate(self: *EnumLiteral, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const EnumLiteral) TokenIndex {
return self.dot;
}
pub fn lastToken(self: *const EnumLiteral) TokenIndex {
return self.name;
}
};
pub const FloatLiteral = struct {
base: Node = Node{ .id = .FloatLiteral },
token: TokenIndex,
pub fn iterate(self: *FloatLiteral, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const FloatLiteral) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const FloatLiteral) TokenIndex {
return self.token;
}
};
pub const BuiltinCall = struct {
base: Node = Node{ .id = .BuiltinCall },
builtin_token: TokenIndex,
params: ParamList,
rparen_token: TokenIndex,
pub const ParamList = SegmentedList(*Node, 2);
pub fn iterate(self: *BuiltinCall, index: usize) ?*Node {
var i = index;
if (i < self.params.len) return self.params.at(i).*;
i -= self.params.len;
return null;
}
pub fn firstToken(self: *const BuiltinCall) TokenIndex {
return self.builtin_token;
}
pub fn lastToken(self: *const BuiltinCall) TokenIndex {
return self.rparen_token;
}
};
pub const StringLiteral = struct {
base: Node = Node{ .id = .StringLiteral },
token: TokenIndex,
pub fn iterate(self: *StringLiteral, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const StringLiteral) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const StringLiteral) TokenIndex {
return self.token;
}
};
pub const MultilineStringLiteral = struct {
base: Node = Node{ .id = .MultilineStringLiteral },
lines: LineList,
pub const LineList = SegmentedList(TokenIndex, 4);
pub fn iterate(self: *MultilineStringLiteral, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const MultilineStringLiteral) TokenIndex {
return self.lines.at(0).*;
}
pub fn lastToken(self: *const MultilineStringLiteral) TokenIndex {
return self.lines.at(self.lines.len - 1).*;
}
};
pub const CharLiteral = struct {
base: Node = Node{ .id = .CharLiteral },
token: TokenIndex,
pub fn iterate(self: *CharLiteral, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const CharLiteral) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const CharLiteral) TokenIndex {
return self.token;
}
};
pub const BoolLiteral = struct {
base: Node = Node{ .id = .BoolLiteral },
token: TokenIndex,
pub fn iterate(self: *BoolLiteral, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const BoolLiteral) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const BoolLiteral) TokenIndex {
return self.token;
}
};
pub const NullLiteral = struct {
base: Node = Node{ .id = .NullLiteral },
token: TokenIndex,
pub fn iterate(self: *NullLiteral, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const NullLiteral) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const NullLiteral) TokenIndex {
return self.token;
}
};
pub const UndefinedLiteral = struct {
base: Node = Node{ .id = .UndefinedLiteral },
token: TokenIndex,
pub fn iterate(self: *UndefinedLiteral, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const UndefinedLiteral) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const UndefinedLiteral) TokenIndex {
return self.token;
}
};
pub const AsmOutput = struct {
base: Node = Node{ .id = .AsmOutput },
lbracket: TokenIndex,
symbolic_name: *Node,
constraint: *Node,
kind: Kind,
rparen: TokenIndex,
pub const Kind = union(enum) {
Variable: *Identifier,
Return: *Node,
};
pub fn iterate(self: *AsmOutput, index: usize) ?*Node {
var i = index;
if (i < 1) return self.symbolic_name;
i -= 1;
if (i < 1) return self.constraint;
i -= 1;
switch (self.kind) {
Kind.Variable => |variable_name| {
if (i < 1) return &variable_name.base;
i -= 1;
},
Kind.Return => |return_type| {
if (i < 1) return return_type;
i -= 1;
},
}
return null;
}
pub fn firstToken(self: *const AsmOutput) TokenIndex {
return self.lbracket;
}
pub fn lastToken(self: *const AsmOutput) TokenIndex {
return self.rparen;
}
};
pub const AsmInput = struct {
base: Node = Node{ .id = .AsmInput },
lbracket: TokenIndex,
symbolic_name: *Node,
constraint: *Node,
expr: *Node,
rparen: TokenIndex,
pub fn iterate(self: *AsmInput, index: usize) ?*Node {
var i = index;
if (i < 1) return self.symbolic_name;
i -= 1;
if (i < 1) return self.constraint;
i -= 1;
if (i < 1) return self.expr;
i -= 1;
return null;
}
pub fn firstToken(self: *const AsmInput) TokenIndex {
return self.lbracket;
}
pub fn lastToken(self: *const AsmInput) TokenIndex {
return self.rparen;
}
};
pub const Asm = struct {
base: Node = Node{ .id = .Asm },
asm_token: TokenIndex,
volatile_token: ?TokenIndex,
template: *Node,
outputs: OutputList,
inputs: InputList,
clobbers: ClobberList,
rparen: TokenIndex,
pub const OutputList = SegmentedList(*AsmOutput, 2);
pub const InputList = SegmentedList(*AsmInput, 2);
pub const ClobberList = SegmentedList(*Node, 2);
pub fn iterate(self: *Asm, index: usize) ?*Node {
var i = index;
if (i < self.outputs.len) return &self.outputs.at(i).*.base;
i -= self.outputs.len;
if (i < self.inputs.len) return &self.inputs.at(i).*.base;
i -= self.inputs.len;
return null;
}
pub fn firstToken(self: *const Asm) TokenIndex {
return self.asm_token;
}
pub fn lastToken(self: *const Asm) TokenIndex {
return self.rparen;
}
};
pub const Unreachable = struct {
base: Node = Node{ .id = .Unreachable },
token: TokenIndex,
pub fn iterate(self: *Unreachable, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const Unreachable) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const Unreachable) TokenIndex {
return self.token;
}
};
pub const ErrorType = struct {
base: Node = Node{ .id = .ErrorType },
token: TokenIndex,
pub fn iterate(self: *ErrorType, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const ErrorType) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const ErrorType) TokenIndex {
return self.token;
}
};
pub const VarType = struct {
base: Node = Node{ .id = .VarType },
token: TokenIndex,
pub fn iterate(self: *VarType, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const VarType) TokenIndex {
return self.token;
}
pub fn lastToken(self: *const VarType) TokenIndex {
return self.token;
}
};
pub const DocComment = struct {
base: Node = Node{ .id = .DocComment },
lines: LineList,
pub const LineList = SegmentedList(TokenIndex, 4);
pub fn iterate(self: *DocComment, index: usize) ?*Node {
return null;
}
pub fn firstToken(self: *const DocComment) TokenIndex {
return self.lines.at(0).*;
}
pub fn lastToken(self: *const DocComment) TokenIndex {
return self.lines.at(self.lines.len - 1).*;
}
};
pub const TestDecl = struct {
base: Node = Node{ .id = .TestDecl },
doc_comments: ?*DocComment,
test_token: TokenIndex,
name: *Node,
body_node: *Node,
pub fn iterate(self: *TestDecl, index: usize) ?*Node {
var i = index;
if (i < 1) return self.body_node;
i -= 1;
return null;
}
pub fn firstToken(self: *const TestDecl) TokenIndex {
return self.test_token;
}
pub fn lastToken(self: *const TestDecl) TokenIndex {
return self.body_node.lastToken();
}
};
};
test "iterate" {
var root = Node.Root{
.base = Node{ .id = Node.Id.Root },
.decls = Node.Root.DeclList.init(std.testing.allocator),
.eof_token = 0,
};
var base = &root.base;
testing.expect(base.iterate(0) == null);
}