Files
zig/lib/std/zig/ast.zig
2021-02-03 17:02:12 -07:00

1399 lines
52 KiB
Zig

// SPDX-License-Identifier: MIT
// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
const std = @import("../std.zig");
const assert = std.debug.assert;
const testing = std.testing;
const mem = std.mem;
const Token = std.zig.Token;
pub const TokenIndex = u32;
pub const ByteOffset = u32;
pub const TokenList = std.MultiArrayList(struct {
tag: Token.Tag,
start: ByteOffset,
});
pub const NodeList = std.MultiArrayList(Node);
pub const Tree = struct {
/// Reference to externally-owned data.
source: []const u8,
tokens: TokenList.Slice,
/// The root AST node is assumed to be index 0. Since there can be no
/// references to the root node, this means 0 is available to indicate null.
nodes: NodeList.Slice,
extra_data: []Node.Index,
errors: []const Error,
pub const Location = struct {
line: usize,
column: usize,
line_start: usize,
line_end: usize,
};
pub fn deinit(tree: *Tree, gpa: *mem.Allocator) void {
tree.tokens.deinit(gpa);
tree.nodes.deinit(gpa);
gpa.free(tree.extra_data);
gpa.free(tree.errors);
tree.* = undefined;
}
pub fn tokenLocation(self: Tree, start_offset: ByteOffset, token_index: TokenIndex) Location {
var loc = Location{
.line = 0,
.column = 0,
.line_start = start_offset,
.line_end = self.source.len,
};
const token_start = self.tokens.items(.start)[token_index];
for (self.source[start_offset..]) |c, i| {
if (i + start_offset == token_start) {
loc.line_end = i + start_offset;
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 extraData(tree: Tree, index: usize, comptime T: type) T {
const fields = std.meta.fields(T);
var result: T = undefined;
inline for (fields) |field, i| {
comptime assert(field.field_type == Node.Index);
@field(result, field.name) = tree.extra_data[index + i];
}
return result;
}
pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {
const tokens = tree.tokens.items(.tag);
switch (parse_error) {
.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),
.ExpectedFn => |*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(tree, stream),
.ExpectedCallOrFnProto => |x| return x.render(tree, 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),
.ExpectedBlockOrField => |*x| return x.render(tokens, stream),
.DeclBetweenFields => |*x| return x.render(tokens, stream),
.InvalidAnd => |*x| return x.render(tokens, stream),
.AsteriskAfterPointerDereference => |*x| return x.render(tokens, stream),
}
}
pub fn errorToken(tree: Tree, parse_error: Error) TokenIndex {
switch (parse_error) {
.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,
.ExpectedFn => |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 tree.nodes.items(.main_token)[x.node],
.ExpectedCallOrFnProto => |x| return tree.nodes.items(.main_token)[x.node],
.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,
.ExpectedBlockOrField => |x| return x.token,
.DeclBetweenFields => |x| return x.token,
.InvalidAnd => |x| return x.token,
.AsteriskAfterPointerDereference => |x| return x.token,
}
}
pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
const tags = tree.nodes.items(.tag);
const datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
const token_tags = tree.tokens.items(.tag);
var n = node;
while (true) switch (tags[n]) {
.Root => return 0,
.UsingNamespace,
.TestDecl,
.ErrDefer,
.Defer,
.BoolNot,
.Negation,
.BitNot,
.NegationWrap,
.AddressOf,
.Try,
.Await,
.OptionalType,
.ArrayInitDotTwo,
.ArrayInitDot,
.StructInitDotTwo,
.StructInitDot,
.Switch,
.IfSimple,
.If,
.Suspend,
.Resume,
.Continue,
.Break,
.Return,
.AnyFrameType,
.Identifier,
.AnyFrameLiteral,
.CharLiteral,
.IntegerLiteral,
.FloatLiteral,
.FalseLiteral,
.TrueLiteral,
.NullLiteral,
.UndefinedLiteral,
.UnreachableLiteral,
.EnumLiteral,
.StringLiteral,
.GroupedExpression,
.BuiltinCallTwo,
.BuiltinCall,
.ErrorSetDecl,
.AnyType,
.Comptime,
.Nosuspend,
.Block,
.AsmSimple,
.Asm,
=> return main_tokens[n],
.Catch,
.FieldAccess,
.UnwrapOptional,
.EqualEqual,
.BangEqual,
.LessThan,
.GreaterThan,
.LessOrEqual,
.GreaterOrEqual,
.AssignMul,
.AssignDiv,
.AssignMod,
.AssignAdd,
.AssignSub,
.AssignBitShiftLeft,
.AssignBitShiftRight,
.AssignBitAnd,
.AssignBitXor,
.AssignBitOr,
.AssignMulWrap,
.AssignAddWrap,
.AssignSubWrap,
.Assign,
.MergeErrorSets,
.Mul,
.Div,
.Mod,
.ArrayMult,
.MulWrap,
.Add,
.Sub,
.ArrayCat,
.AddWrap,
.SubWrap,
.BitShiftLeft,
.BitShiftRight,
.BitAnd,
.BitXor,
.BitOr,
.OrElse,
.BoolAnd,
.BoolOr,
.SliceOpen,
.Slice,
.Deref,
.ArrayAccess,
.ArrayInitOne,
.ArrayInit,
.StructInitOne,
.CallOne,
.Call,
.SwitchCaseOne,
.SwitchRange,
.FnDecl,
=> n = datas[n].lhs,
.ContainerFieldInit,
.ContainerFieldAlign,
.ContainerField,
=> {
const name_token = main_tokens[n];
if (name_token > 0 and
token_tags[name_token - 1] == .Keyword_comptime)
{
return name_token - 1;
} else {
return name_token;
}
},
.GlobalVarDecl => unreachable,
.LocalVarDecl => unreachable,
.SimpleVarDecl => unreachable,
.AlignedVarDecl => unreachable,
.ArrayType => unreachable,
.ArrayTypeSentinel => unreachable,
.PtrTypeAligned => unreachable,
.PtrTypeSentinel => unreachable,
.PtrType => unreachable,
.SliceType => unreachable,
.StructInit => unreachable,
.SwitchCaseMulti => unreachable,
.WhileSimple => unreachable,
.WhileCont => unreachable,
.While => unreachable,
.ForSimple => unreachable,
.For => unreachable,
.FnProtoSimple => unreachable,
.FnProtoSimpleMulti => unreachable,
.FnProtoOne => unreachable,
.FnProto => unreachable,
.ContainerDecl => unreachable,
.ContainerDeclArg => unreachable,
.TaggedUnion => unreachable,
.TaggedUnionEnumTag => unreachable,
.AsmOutput => unreachable,
.AsmInput => unreachable,
.ErrorValue => unreachable,
.ErrorUnion => unreachable,
};
}
pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
const tags = tree.nodes.items(.tag);
const datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
var n = node;
var end_offset: TokenIndex = 0;
while (true) switch (tags[n]) {
.Root => return @intCast(TokenIndex, tree.tokens.len - 1),
.UsingNamespace,
.BoolNot,
.Negation,
.BitNot,
.NegationWrap,
.AddressOf,
.Try,
.Await,
.OptionalType,
.Suspend,
.Resume,
.Break,
.Return,
=> n = datas[n].lhs,
.TestDecl,
.ErrDefer,
.Defer,
.Catch,
.EqualEqual,
.BangEqual,
.LessThan,
.GreaterThan,
.LessOrEqual,
.GreaterOrEqual,
.AssignMul,
.AssignDiv,
.AssignMod,
.AssignAdd,
.AssignSub,
.AssignBitShiftLeft,
.AssignBitShiftRight,
.AssignBitAnd,
.AssignBitXor,
.AssignBitOr,
.AssignMulWrap,
.AssignAddWrap,
.AssignSubWrap,
.Assign,
.MergeErrorSets,
.Mul,
.Div,
.Mod,
.ArrayMult,
.MulWrap,
.Add,
.Sub,
.ArrayCat,
.AddWrap,
.SubWrap,
.BitShiftLeft,
.BitShiftRight,
.BitAnd,
.BitXor,
.BitOr,
.OrElse,
.BoolAnd,
.BoolOr,
.AnyFrameType,
.ErrorUnion,
.Comptime,
.Nosuspend,
.IfSimple,
.WhileSimple,
=> n = datas[n].rhs,
.FieldAccess,
.UnwrapOptional,
.GroupedExpression,
.StringLiteral,
=> return datas[n].rhs + end_offset,
.AnyType,
.AnyFrameLiteral,
.CharLiteral,
.IntegerLiteral,
.FloatLiteral,
.FalseLiteral,
.TrueLiteral,
.NullLiteral,
.UndefinedLiteral,
.UnreachableLiteral,
.Identifier,
=> return main_tokens[n] + end_offset,
.Call => {
end_offset += 1; // for the `)`
const params = tree.extraData(datas[n].rhs, Node.SubRange);
if (params.end - params.start == 0) {
return main_tokens[n] + end_offset;
}
n = tree.extra_data[params.end - 1]; // last parameter
},
.CallOne => {
end_offset += 1; // for the `)`
if (datas[n].rhs == 0) {
return main_tokens[n] + end_offset;
}
n = datas[n].rhs;
},
.ContainerFieldInit => unreachable,
.ContainerFieldAlign => unreachable,
.ContainerField => unreachable,
.ArrayInitDotTwo => unreachable,
.ArrayInitDot => unreachable,
.StructInitDotTwo => unreachable,
.StructInitDot => unreachable,
.Switch => unreachable,
.If => unreachable,
.Continue => unreachable,
.EnumLiteral => unreachable,
.BuiltinCallTwo => unreachable,
.BuiltinCall => unreachable,
.ErrorSetDecl => unreachable,
.Block => unreachable,
.AsmSimple => unreachable,
.Asm => unreachable,
.SliceOpen => unreachable,
.Slice => unreachable,
.Deref => unreachable,
.ArrayAccess => unreachable,
.ArrayInitOne => unreachable,
.ArrayInit => unreachable,
.StructInitOne => unreachable,
.SwitchCaseOne => unreachable,
.SwitchRange => unreachable,
.FnDecl => unreachable,
.GlobalVarDecl => unreachable,
.LocalVarDecl => unreachable,
.SimpleVarDecl => unreachable,
.AlignedVarDecl => unreachable,
.ArrayType => unreachable,
.ArrayTypeSentinel => unreachable,
.PtrTypeAligned => unreachable,
.PtrTypeSentinel => unreachable,
.PtrType => unreachable,
.SliceType => unreachable,
.StructInit => unreachable,
.SwitchCaseMulti => unreachable,
.WhileCont => unreachable,
.While => unreachable,
.ForSimple => unreachable,
.For => unreachable,
.FnProtoSimple => unreachable,
.FnProtoSimpleMulti => unreachable,
.FnProtoOne => unreachable,
.FnProto => unreachable,
.ContainerDecl => unreachable,
.ContainerDeclArg => unreachable,
.TaggedUnion => unreachable,
.TaggedUnionEnumTag => unreachable,
.AsmOutput => unreachable,
.AsmInput => unreachable,
.ErrorValue => unreachable,
};
}
pub fn tokensOnSameLine(tree: Tree, token1: TokenIndex, token2: TokenIndex) bool {
const token_starts = tree.tokens.items(.start);
const source = tree.source[token_starts[token1]..token_starts[token2]];
return mem.indexOfScalar(u8, source, '\n') == null;
}
pub fn globalVarDecl(tree: Tree, node: Node.Index) Full.VarDecl {
assert(tree.nodes.items(.tag)[node] == .GlobalVarDecl);
const data = tree.nodes.items(.data)[node];
const extra = tree.extraData(data.lhs, Node.GlobalVarDecl);
return tree.fullVarDecl(.{
.type_node = extra.type_node,
.align_node = extra.align_node,
.section_node = extra.section_node,
.init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node],
});
}
pub fn localVarDecl(tree: Tree, node: Node.Index) Full.VarDecl {
assert(tree.nodes.items(.tag)[node] == .LocalVarDecl);
const data = tree.nodes.items(.data)[node];
const extra = tree.extraData(data.lhs, Node.LocalVarDecl);
return tree.fullVarDecl(.{
.type_node = extra.type_node,
.align_node = extra.align_node,
.section_node = 0,
.init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node],
});
}
pub fn simpleVarDecl(tree: Tree, node: Node.Index) Full.VarDecl {
assert(tree.nodes.items(.tag)[node] == .SimpleVarDecl);
const data = tree.nodes.items(.data)[node];
return tree.fullVarDecl(.{
.type_node = data.lhs,
.align_node = 0,
.section_node = 0,
.init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node],
});
}
pub fn alignedVarDecl(tree: Tree, node: Node.Index) Full.VarDecl {
assert(tree.nodes.items(.tag)[node] == .AlignedVarDecl);
const data = tree.nodes.items(.data)[node];
return tree.fullVarDecl(.{
.type_node = 0,
.align_node = data.lhs,
.section_node = 0,
.init_node = data.rhs,
.mut_token = tree.nodes.items(.main_token)[node],
});
}
pub fn ifSimple(tree: Tree, node: Node.Index) Full.If {
assert(tree.nodes.items(.tag)[node] == .IfSimple);
const data = tree.nodes.items(.data)[node];
return tree.fullIf(.{
.cond_expr = data.lhs,
.then_expr = data.rhs,
.else_expr = 0,
.if_token = tree.nodes.items(.main_token)[node],
});
}
pub fn ifFull(tree: Tree, node: Node.Index) Full.If {
assert(tree.nodes.items(.tag)[node] == .If);
const data = tree.nodes.items(.data)[node];
const extra = tree.extraData(data.rhs, Node.If);
return tree.fullIf(.{
.cond_expr = data.lhs,
.then_expr = extra.then_expr,
.else_expr = extra.else_expr,
.if_token = tree.nodes.items(.main_token)[node],
});
}
pub fn containerField(tree: Tree, node: Node.Index) Full.ContainerField {
assert(tree.nodes.items(.tag)[node] == .ContainerField);
const data = tree.nodes.items(.data)[node];
const extra = tree.extraData(data.rhs, Node.ContainerField);
return tree.fullContainerField(.{
.name_token = tree.nodes.items(.main_token)[node],
.type_expr = data.lhs,
.value_expr = extra.value_expr,
.align_expr = extra.align_expr,
});
}
pub fn containerFieldInit(tree: Tree, node: Node.Index) Full.ContainerField {
assert(tree.nodes.items(.tag)[node] == .ContainerFieldInit);
const data = tree.nodes.items(.data)[node];
return tree.fullContainerField(.{
.name_token = tree.nodes.items(.main_token)[node],
.type_expr = data.lhs,
.value_expr = data.rhs,
.align_expr = 0,
});
}
pub fn containerFieldAlign(tree: Tree, node: Node.Index) Full.ContainerField {
assert(tree.nodes.items(.tag)[node] == .ContainerFieldAlign);
const data = tree.nodes.items(.data)[node];
return tree.fullContainerField(.{
.name_token = tree.nodes.items(.main_token)[node],
.type_expr = data.lhs,
.value_expr = 0,
.align_expr = data.rhs,
});
}
fn fullVarDecl(tree: Tree, info: Full.VarDecl.Ast) Full.VarDecl {
const token_tags = tree.tokens.items(.tag);
var result: Full.VarDecl = .{
.ast = info,
.visib_token = null,
.extern_export_token = null,
.lib_name = null,
.threadlocal_token = null,
.comptime_token = null,
};
var i = info.mut_token;
while (i > 0) {
i -= 1;
switch (token_tags[i]) {
.Keyword_extern, .Keyword_export => result.extern_export_token = i,
.Keyword_comptime => result.comptime_token = i,
.Keyword_pub => result.visib_token = i,
.Keyword_threadlocal => result.threadlocal_token = i,
.StringLiteral => result.lib_name = i,
else => break,
}
}
return result;
}
fn fullIf(tree: Tree, info: Full.If.Ast) Full.If {
const token_tags = tree.tokens.items(.tag);
var result: Full.If = .{
.ast = info,
.payload_token = null,
.error_token = null,
.else_token = undefined,
};
// if (cond_expr) |x|
// ^ ^
const payload_pipe = tree.lastToken(info.cond_expr) + 2;
if (token_tags[payload_pipe] == .Pipe) {
result.payload_token = payload_pipe + 1;
}
if (info.else_expr != 0) {
// then_expr else |x|
// ^ ^
result.else_token = tree.lastToken(info.then_expr) + 1;
if (token_tags[result.else_token + 1] == .Pipe) {
result.error_token = result.else_token + 2;
}
}
return result;
}
fn fullContainerField(tree: Tree, info: Full.ContainerField.Ast) Full.ContainerField {
const token_tags = tree.tokens.items(.tag);
var result: Full.ContainerField = .{
.ast = info,
.comptime_token = null,
};
// comptime name: type = init,
// ^
if (info.name_token > 0 and token_tags[info.name_token - 1] == .Keyword_comptime) {
result.comptime_token = info.name_token - 1;
}
return result;
}
};
/// Fully assembled AST node information.
pub const Full = struct {
pub const VarDecl = struct {
visib_token: ?TokenIndex,
extern_export_token: ?TokenIndex,
lib_name: ?TokenIndex,
threadlocal_token: ?TokenIndex,
comptime_token: ?TokenIndex,
ast: Ast,
pub const Ast = struct {
mut_token: TokenIndex,
type_node: Node.Index,
align_node: Node.Index,
section_node: Node.Index,
init_node: Node.Index,
};
};
pub const If = struct {
// Points to the first token after the `|`. Will either be an identifier or
// a `*` (with an identifier immediately after it).
payload_token: ?TokenIndex,
// Points to the identifier after the `|`.
error_token: ?TokenIndex,
// Populated only if else_expr != 0.
else_token: TokenIndex,
ast: Ast,
pub const Ast = struct {
if_token: TokenIndex,
cond_expr: Node.Index,
then_expr: Node.Index,
else_expr: Node.Index,
};
};
pub const ContainerField = struct {
comptime_token: ?TokenIndex,
ast: Ast,
pub const Ast = struct {
name_token: TokenIndex,
type_expr: Node.Index,
value_expr: Node.Index,
align_expr: Node.Index,
};
};
};
pub const Error = union(enum) {
InvalidToken: InvalidToken,
ExpectedContainerMembers: ExpectedContainerMembers,
ExpectedStringLiteral: ExpectedStringLiteral,
ExpectedIntegerLiteral: ExpectedIntegerLiteral,
ExpectedPubItem: ExpectedPubItem,
ExpectedIdentifier: ExpectedIdentifier,
ExpectedStatement: ExpectedStatement,
ExpectedVarDeclOrFn: ExpectedVarDeclOrFn,
ExpectedVarDecl: ExpectedVarDecl,
ExpectedFn: ExpectedFn,
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,
ExpectedBlockOrField: ExpectedBlockOrField,
DeclBetweenFields: DeclBetweenFields,
InvalidAnd: InvalidAnd,
AsteriskAfterPointerDereference: AsteriskAfterPointerDereference,
pub const InvalidToken = SingleTokenError("Invalid token '{s}'");
pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{s}'");
pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{s}'");
pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{s}'");
pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{s}'");
pub const ExpectedStatement = SingleTokenError("Expected statement, found '{s}'");
pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{s}'");
pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{s}'");
pub const ExpectedFn = SingleTokenError("Expected function, found '{s}'");
pub const ExpectedReturnType = SingleTokenError("Expected 'var' or return type expression, found '{s}'");
pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Tag.Keyword_struct.symbol() ++ "', '" ++ Token.Tag.Keyword_union.symbol() ++ "', '" ++ Token.Tag.Keyword_enum.symbol() ++ "', or '" ++ Token.Tag.Keyword_opaque.symbol() ++ "', found '{s}'");
pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{s}'");
pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{s}'");
pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{s}'");
pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{s}'");
pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{s}'");
pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{s}'");
pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'");
pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{s}'");
pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Tag.Identifier.symbol() ++ "', found '{s}'");
pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{s}'");
pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{s}'");
pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{s}'");
pub const ExpectedExpr = SingleTokenError("Expected expression, found '{s}'");
pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{s}'");
pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{s}'");
pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{s}'");
pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{s}'");
pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{s}'");
pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{s}'");
pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{s}'");
pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{s}'");
pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{s}'");
pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{s}'");
pub const ExpectedBlockOrField = SingleTokenError("Expected block or field, found '{s}'");
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 DeclBetweenFields = SimpleError("Declarations are not allowed between container fields");
pub const InvalidAnd = SimpleError("`&&` is invalid. Note that `and` is boolean AND.");
pub const AsteriskAfterPointerDereference = SimpleError("`.*` can't be followed by `*`. Are you missing a space?");
pub const ExpectedCall = struct {
node: Node.Index,
pub fn render(self: ExpectedCall, tree: Tree, stream: anytype) !void {
const node_tag = tree.nodes.items(.tag)[self.node];
return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {s}", .{
@tagName(node_tag),
});
}
};
pub const ExpectedCallOrFnProto = struct {
node: Node.Index,
pub fn render(self: ExpectedCallOrFnProto, tree: Tree, stream: anytype) !void {
const node_tag = tree.nodes.items(.tag)[self.node];
return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ " or " ++
@tagName(Node.Tag.FnProto) ++ ", found {s}", .{@tagName(node_tag)});
}
};
pub const ExpectedToken = struct {
token: TokenIndex,
expected_id: Token.Tag,
pub fn render(self: *const ExpectedToken, tokens: []const Token.Tag, stream: anytype) !void {
const found_token = tokens[self.token];
switch (found_token) {
.Invalid => {
return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()});
},
else => {
const token_name = found_token.symbol();
return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name });
},
}
}
};
pub const ExpectedCommaOrEnd = struct {
token: TokenIndex,
end_id: Token.Tag,
pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Tag, stream: anytype) !void {
const actual_token = tokens[self.token];
return stream.print("expected ',' or '{s}', found '{s}'", .{
self.end_id.symbol(),
actual_token.symbol(),
});
}
};
fn SingleTokenError(comptime msg: []const u8) type {
return struct {
const ThisError = @This();
token: TokenIndex,
pub fn render(self: *const ThisError, tokens: []const Token.Tag, stream: anytype) !void {
const actual_token = tokens[self.token];
return stream.print(msg, .{actual_token.symbol()});
}
};
}
fn SimpleError(comptime msg: []const u8) type {
return struct {
const ThisError = @This();
token: TokenIndex,
pub fn render(self: *const ThisError, tokens: []const Token.Tag, stream: anytype) !void {
return stream.writeAll(msg);
}
};
}
pub fn loc(self: 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,
.ExpectedFn => |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| @panic("TODO redo ast errors"),
.ExpectedCallOrFnProto => |x| @panic("TODO redo ast errors"),
.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,
.ExpectedBlockOrField => |x| return x.token,
.DeclBetweenFields => |x| return x.token,
.InvalidAnd => |x| return x.token,
.AsteriskAfterPointerDereference => |x| return x.token,
}
}
};
pub const Node = struct {
tag: Tag,
main_token: TokenIndex,
data: Data,
pub const Index = u32;
comptime {
// Goal is to keep this under one byte for efficiency.
assert(@sizeOf(Tag) == 1);
}
pub const Tag = enum {
/// sub_list[lhs...rhs]
Root,
/// `usingnamespace lhs;`. rhs unused. main_token is `usingnamespace`.
UsingNamespace,
/// lhs is test name token (must be string literal), if any.
/// rhs is the body node.
TestDecl,
/// lhs is the index into extra_data.
/// rhs is the initialization expression, if any.
/// main_token is `var` or `const`.
GlobalVarDecl,
/// `var a: x align(y) = rhs`
/// lhs is the index into extra_data.
/// main_token is `var` or `const`.
LocalVarDecl,
/// `var a: lhs = rhs`. lhs and rhs may be unused.
/// Can be local or global.
/// main_token is `var` or `const`.
SimpleVarDecl,
/// `var a align(lhs) = rhs`. lhs and rhs may be unused.
/// Can be local or global.
/// main_token is `var` or `const`.
AlignedVarDecl,
/// lhs is the identifier token payload if any,
/// rhs is the deferred expression.
ErrDefer,
/// lhs is unused.
/// rhs is the deferred expression.
Defer,
/// lhs is target expr; rhs is fallback expr.
/// payload is determined by looking at the prev tokens before rhs.
Catch,
/// `lhs.a`. main_token is the dot. rhs is the identifier token index.
FieldAccess,
/// `lhs.?`. main_token is the dot. rhs is the `?` token index.
UnwrapOptional,
/// `lhs == rhs`. main_token is op.
EqualEqual,
/// `lhs != rhs`. main_token is op.
BangEqual,
/// `lhs < rhs`. main_token is op.
LessThan,
/// `lhs > rhs`. main_token is op.
GreaterThan,
/// `lhs <= rhs`. main_token is op.
LessOrEqual,
/// `lhs >= rhs`. main_token is op.
GreaterOrEqual,
/// `lhs *= rhs`. main_token is op.
AssignMul,
/// `lhs /= rhs`. main_token is op.
AssignDiv,
/// `lhs *= rhs`. main_token is op.
AssignMod,
/// `lhs += rhs`. main_token is op.
AssignAdd,
/// `lhs -= rhs`. main_token is op.
AssignSub,
/// `lhs <<= rhs`. main_token is op.
AssignBitShiftLeft,
/// `lhs >>= rhs`. main_token is op.
AssignBitShiftRight,
/// `lhs &= rhs`. main_token is op.
AssignBitAnd,
/// `lhs ^= rhs`. main_token is op.
AssignBitXor,
/// `lhs |= rhs`. main_token is op.
AssignBitOr,
/// `lhs *%= rhs`. main_token is op.
AssignMulWrap,
/// `lhs +%= rhs`. main_token is op.
AssignAddWrap,
/// `lhs -%= rhs`. main_token is op.
AssignSubWrap,
/// `lhs = rhs`. main_token is op.
Assign,
/// `lhs || rhs`. main_token is the `||`.
MergeErrorSets,
/// `lhs * rhs`. main_token is the `*`.
Mul,
/// `lhs / rhs`. main_token is the `/`.
Div,
/// `lhs % rhs`. main_token is the `%`.
Mod,
/// `lhs ** rhs`. main_token is the `**`.
ArrayMult,
/// `lhs *% rhs`. main_token is the `*%`.
MulWrap,
/// `lhs + rhs`. main_token is the `+`.
Add,
/// `lhs - rhs`. main_token is the `-`.
Sub,
/// `lhs ++ rhs`. main_token is the `++`.
ArrayCat,
/// `lhs +% rhs`. main_token is the `+%`.
AddWrap,
/// `lhs -% rhs`. main_token is the `-%`.
SubWrap,
/// `lhs << rhs`. main_token is the `<<`.
BitShiftLeft,
/// `lhs >> rhs`. main_token is the `>>`.
BitShiftRight,
/// `lhs & rhs`. main_token is the `&`.
BitAnd,
/// `lhs ^ rhs`. main_token is the `^`.
BitXor,
/// `lhs | rhs`. main_token is the `|`.
BitOr,
/// `lhs orelse rhs`. main_token is the `orelse`.
OrElse,
/// `lhs and rhs`. main_token is the `and`.
BoolAnd,
/// `lhs or rhs`. main_token is the `or`.
BoolOr,
/// `op lhs`. rhs unused. main_token is op.
BoolNot,
/// `op lhs`. rhs unused. main_token is op.
Negation,
/// `op lhs`. rhs unused. main_token is op.
BitNot,
/// `op lhs`. rhs unused. main_token is op.
NegationWrap,
/// `op lhs`. rhs unused. main_token is op.
AddressOf,
/// `op lhs`. rhs unused. main_token is op.
Try,
/// `op lhs`. rhs unused. main_token is op.
Await,
/// `?lhs`. rhs unused. main_token is the `?`.
OptionalType,
/// `[lhs]rhs`. lhs can be omitted to make it a slice.
ArrayType,
/// `[lhs:a]b`. `ArrayTypeSentinel[rhs]`.
ArrayTypeSentinel,
/// `[*]align(lhs) rhs`. lhs can be omitted.
/// `*align(lhs) rhs`. lhs can be omitted.
/// `[]rhs`.
PtrTypeAligned,
/// `[*:lhs]rhs`. lhs can be omitted.
/// `*rhs`.
/// `[:lhs]rhs`.
PtrTypeSentinel,
/// lhs is index into PtrType. rhs is the element type expression.
PtrType,
/// lhs is index into SliceType. rhs is the element type expression.
/// Can be pointer or slice, depending on main_token.
SliceType,
/// `lhs[rhs..]`
/// main_token is the `[`.
SliceOpen,
/// `lhs[b..c :d]`. `slice_list[rhs]`.
/// main_token is the `[`.
Slice,
/// `lhs.*`. rhs is unused.
Deref,
/// `lhs[rhs]`.
ArrayAccess,
/// `lhs{rhs}`. rhs can be omitted.
ArrayInitOne,
/// `.{lhs, rhs}`. lhs and rhs can be omitted.
ArrayInitDotTwo,
/// `.{a, b}`. `sub_list[lhs..rhs]`.
ArrayInitDot,
/// `lhs{a, b}`. `sub_range_list[rhs]`. lhs can be omitted which means `.{a, b}`.
ArrayInit,
/// `lhs{.a = rhs}`. rhs can be omitted making it empty.
StructInitOne,
/// `.{.a = lhs, .b = rhs}`. lhs and rhs can be omitted.
StructInitDotTwo,
/// `.{.a = b, .c = d}`. `sub_list[lhs..rhs]`.
StructInitDot,
/// `lhs{.a = b, .c = d}`. `sub_range_list[rhs]`.
/// lhs can be omitted which means `.{.a = b, .c = d}`.
StructInit,
/// `lhs(rhs)`. rhs can be omitted.
CallOne,
/// `lhs(a, b, c)`. `sub_range_list[rhs]`.
/// main_token is the `(`.
Call,
/// `switch(lhs) {}`. `sub_range_list[rhs]`.
Switch,
/// `lhs => rhs`. If lhs is omitted it means `else`.
/// main_token is the `=>`
SwitchCaseOne,
/// `a, b, c => rhs`. `sub_range_list[lhs]`.
SwitchCaseMulti,
/// `lhs...rhs`.
SwitchRange,
/// `while (lhs) rhs`.
/// `while (lhs) |x| rhs`.
WhileSimple,
/// `while (lhs) : (a) b`. `WhileCont[rhs]`.
/// `while (lhs) : (a) b`. `WhileCont[rhs]`.
WhileCont,
/// `while (lhs) : (a) b else c`. `While[rhs]`.
/// `while (lhs) |x| : (a) b else c`. `While[rhs]`.
/// `while (lhs) |x| : (a) b else |y| c`. `While[rhs]`.
While,
/// `for (lhs) rhs`.
ForSimple,
/// `for (lhs) a else b`. `if_list[rhs]`.
For,
/// `if (lhs) rhs`.
/// `if (lhs) |a| rhs`.
IfSimple,
/// `if (lhs) a else b`. `if_list[rhs]`.
/// `if (lhs) |x| a else b`. `if_list[rhs]`.
/// `if (lhs) |x| a else |y| b`. `if_list[rhs]`.
If,
/// `suspend lhs`. lhs can be omitted. rhs is unused.
Suspend,
/// `resume lhs`. rhs is unused.
Resume,
/// `continue`. lhs is token index of label if any. rhs is unused.
Continue,
/// `break rhs`. rhs can be omitted. lhs is label token index, if any.
Break,
/// `return lhs`. lhs can be omitted. rhs is unused.
Return,
/// `fn(a: lhs) rhs`. lhs can be omitted.
/// anytype and ... parameters are omitted from the AST tree.
FnProtoSimple,
/// `fn(a: b, c: d) rhs`. `sub_range_list[lhs]`.
/// anytype and ... parameters are omitted from the AST tree.
FnProtoSimpleMulti,
/// `fn(a: b) rhs linksection(e) callconv(f)`. lhs is index into extra_data.
/// zero or one parameters.
/// anytype and ... parameters are omitted from the AST tree.
FnProtoOne,
/// `fn(a: b, c: d) rhs linksection(e) callconv(f)`. `fn_proto_list[lhs]`.
/// anytype and ... parameters are omitted from the AST tree.
FnProto,
/// lhs is the FnProto, rhs is the function body block.
FnDecl,
/// `anyframe->rhs`. main_token is `anyframe`. `lhs` is arrow token index.
AnyFrameType,
/// Both lhs and rhs unused.
AnyFrameLiteral,
/// Both lhs and rhs unused.
CharLiteral,
/// Both lhs and rhs unused.
IntegerLiteral,
/// Both lhs and rhs unused.
FloatLiteral,
/// Both lhs and rhs unused.
FalseLiteral,
/// Both lhs and rhs unused.
TrueLiteral,
/// Both lhs and rhs unused.
NullLiteral,
/// Both lhs and rhs unused.
UndefinedLiteral,
/// Both lhs and rhs unused.
UnreachableLiteral,
/// Both lhs and rhs unused.
/// Most identifiers will not have explicit AST nodes, however for expressions
/// which could be one of many different kinds of AST nodes, there will be an
/// Identifier AST node for it.
Identifier,
/// lhs is the dot token index, rhs unused, main_token is the identifier.
EnumLiteral,
/// main_token is the first token index (redundant with lhs)
/// lhs is the first token index; rhs is the last token index.
/// Could be a series of MultilineStringLiteralLine tokens, or a single
/// StringLiteral token.
StringLiteral,
/// `(lhs)`. main_token is the `(`; rhs is the token index of the `)`.
GroupedExpression,
/// `@a(lhs, rhs)`. lhs and rhs may be omitted.
BuiltinCallTwo,
/// `@a(b, c)`. `sub_list[lhs..rhs]`.
BuiltinCall,
/// `error{a, b}`.
/// lhs and rhs both unused.
ErrorSetDecl,
/// `struct {}`, `union {}`, etc. `sub_list[lhs..rhs]`.
ContainerDecl,
/// `union(lhs)` / `enum(lhs)`. `sub_range_list[rhs]`.
ContainerDeclArg,
/// `union(enum) {}`. `sub_list[lhs..rhs]`.
/// Note that tagged unions with explicitly provided enums are represented
/// by `ContainerDeclArg`.
TaggedUnion,
/// `union(enum(lhs)) {}`. `sub_list_range[rhs]`.
TaggedUnionEnumTag,
/// `a: lhs = rhs,`. lhs and rhs can be omitted.
ContainerFieldInit,
/// `a: lhs align(rhs),`. rhs can be omitted.
ContainerFieldAlign,
/// `a: lhs align(c) = d,`. `container_field_list[rhs]`.
ContainerField,
/// `anytype`. both lhs and rhs unused.
/// Used by `ContainerField`.
AnyType,
/// `comptime lhs`. rhs unused.
Comptime,
/// `nosuspend lhs`. rhs unused.
Nosuspend,
/// `{}`. `sub_list[lhs..rhs]`.
/// main_token points at the `{`.
Block,
/// `asm(lhs)`. rhs unused.
AsmSimple,
/// `asm(lhs, a)`. `sub_range_list[rhs]`.
Asm,
/// `[a] "b" (c)`. lhs is string literal token index, rhs is 0.
/// `[a] "b" (-> rhs)`. lhs is the string literal token index, rhs is type expr.
/// main_token is `a`.
AsmOutput,
/// `[a] "b" (rhs)`. lhs is string literal token index.
/// main_token is `a`.
AsmInput,
/// `error.a`. lhs is token index of `.`. rhs is token index of `a`.
ErrorValue,
/// `lhs!rhs`. main_token is the `!`.
ErrorUnion,
};
pub const Data = struct {
lhs: Index,
rhs: Index,
};
pub const LocalVarDecl = struct {
type_node: Index,
align_node: Index,
};
pub const ArrayTypeSentinel = struct {
elem_type: Index,
sentinel: Index,
};
pub const PtrType = struct {
sentinel: Index,
align_node: Index,
bit_range_start: Index,
bit_range_end: Index,
};
pub const SliceType = struct {
sentinel: Index,
align_node: Index,
};
pub const SubRange = struct {
/// Index into sub_list.
start: Index,
/// Index into sub_list.
end: Index,
};
pub const If = struct {
then_expr: Index,
else_expr: Index,
};
pub const ContainerField = struct {
value_expr: Index,
align_expr: Index,
};
pub const GlobalVarDecl = struct {
type_node: Index,
align_node: Index,
section_node: Index,
};
pub const Slice = struct {
start: Index,
end: Index,
sentinel: Index,
};
pub const While = struct {
continue_expr: Index,
then_expr: Index,
else_expr: Index,
};
pub const WhileCont = struct {
continue_expr: Index,
then_expr: Index,
};
pub const FnProtoOne = struct {
/// Populated if there is exactly 1 parameter. Otherwise there are 0 parameters.
param: Index,
/// Populated if align(A) is present.
align_expr: Index,
/// Populated if linksection(A) is present.
section_expr: Index,
/// Populated if callconv(A) is present.
callconv_expr: Index,
};
pub const FnProto = struct {
params_start: Index,
params_end: Index,
/// Populated if align(A) is present.
align_expr: Index,
/// Populated if linksection(A) is present.
section_expr: Index,
/// Populated if callconv(A) is present.
callconv_expr: Index,
};
};