|
//! Abstract Syntax Tree for Zig source code. //! For Zig syntax, the root node is at nodes[0] and contains the list of //! sub-nodes. //! For Zon syntax, the root node is at nodes[0] and contains lhs as the node //! index of the main expression. |
TokenIndexReference to externally-owned data. |
/// Reference to externally-owned data. |
parse()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. |
source: [:0]const u8, |
TokenListRan out of memory allocating call stack frames to complete rendering, or ran out of memory allocating space in the output buffer. |
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, |
NodeListResult should be freed with tree.deinit() when there are no more references to any of the tokens or nodes. |
errors: []const Error, |
Location
|
pub const TokenIndex = u32; pub const ByteOffset = u32; |
deinit()Returns an extra offset for column and byte offset of errors that should point after the token in the error message. |
pub const TokenList = std.MultiArrayList(struct { tag: Token.Tag, start: ByteOffset, }); pub const NodeList = std.MultiArrayList(Node); |
RenderErrorFully assembled AST node information. |
pub const Location = struct { line: usize, column: usize, line_start: usize, line_end: usize, |
ModePoints to the first token after the |
}; |
parse()Points to the identifier after the |
pub fn deinit(tree: *Ast, gpa: Allocator) void { tree.tokens.deinit(gpa); tree.nodes.deinit(gpa); gpa.free(tree.extra_data); gpa.free(tree.errors); tree.* = undefined; } |
render()Populated only if else_expr != 0. |
pub const RenderError = error{ /// Ran out of memory allocating call stack frames to complete rendering, or /// ran out of memory allocating space in the output buffer. OutOfMemory, }; |
renderToArrayList()Populated only if else_expr != 0. |
pub const Mode = enum { zig, zon }; |
errorOffset()Populated only if else_expr != 0. |
/// Result should be freed with tree.deinit() when there are /// no more references to any of the tokens or nodes. pub fn parse(gpa: Allocator, source: [:0]const u8, mode: Mode) Allocator.Error!Ast { var tokens = Ast.TokenList{}; defer tokens.deinit(gpa); |
tokenLocation()TODO: remove this after zig 0.11.0 is tagged. |
// Empirically, the zig std lib has an 8:1 ratio of source bytes to token count. const estimated_token_count = source.len / 8; try tokens.ensureTotalCapacity(gpa, estimated_token_count); |
tokenSlice()Abstracts over the fact that anytype and ... are not included in the params slice, since they are simple identifiers and not sub-expressions. |
var tokenizer = std.zig.Tokenizer.init(source); while (true) { const token = tokenizer.next(); try tokens.append(gpa, .{ .tag = token.tag, .start = @as(u32, @intCast(token.loc.start)), }); if (token.tag == .eof) break; } |
extraData()Populated when main_token is Keyword_union. |
var parser: Parse = .{ .source = source, .gpa = gpa, .token_tags = tokens.items(.tag), .token_starts = tokens.items(.start), .errors = .{}, .nodes = .{}, .extra_data = .{}, .scratch = .{}, .tok_i = 0, }; defer parser.errors.deinit(gpa); defer parser.nodes.deinit(gpa); defer parser.extra_data.deinit(gpa); defer parser.scratch.deinit(gpa); |
rootDecls()Points to the first token after the |
// Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes. // Make sure at least 1 so we can use appendAssumeCapacity on the root node below. const estimated_node_count = (tokens.len + 2) / 2; try parser.nodes.ensureTotalCapacity(gpa, estimated_node_count); |
renderError()If empty, this is an else case |
switch (mode) { .zig => try parser.parseRoot(), .zon => try parser.parseZon(), } |
firstToken()True if |
// TODO experiment with compacting the MultiArrayList slices here return Ast{ .source = source, .tokens = tokens.toOwnedSlice(), .nodes = parser.nodes.toOwnedSlice(), .extra_data = try parser.extra_data.toOwnedSlice(gpa), .errors = try parser.errors.toOwnedSlice(gpa), }; } |
lastToken()
|
/// `gpa` is used for allocating the resulting formatted source code, as well as /// for allocating extra stack memory if needed, because this function utilizes recursion. /// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006. /// Caller owns the returned slice of bytes, allocated with `gpa`. pub fn render(tree: Ast, gpa: Allocator) RenderError![]u8 { var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); |
tokensOnSameLine()Note: The FooComma/FooSemicolon variants exist to ease the implementation of Ast.lastToken() |
try tree.renderToArrayList(&buffer); return buffer.toOwnedSlice(); } |
getNodeSource()sub_list[lhs...rhs] |
pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8)) RenderError!void { return @import("./render.zig").renderTree(buffer, tree); } |
globalVarDecl()
|
/// Returns an extra offset for column and byte offset of errors that /// should point after the token in the error message. pub fn errorOffset(tree: Ast, parse_error: Error) u32 { return if (parse_error.token_is_prev) @as(u32, @intCast(tree.tokenSlice(parse_error.token).len)) else 0; } |
localVarDecl()lhs is test name token (must be string literal or identifier), if any. rhs is the body node. |
pub fn tokenLocation(self: Ast, 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..], 0..) |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; } |
simpleVarDecl()lhs is the index into extra_data.
rhs is the initialization expression, if any.
main_token is |
pub fn tokenSlice(tree: Ast, token_index: TokenIndex) []const u8 { const token_starts = tree.tokens.items(.start); const token_tags = tree.tokens.items(.tag); const token_tag = token_tags[token_index]; |
alignedVarDecl()
|
// Many tokens can be determined entirely by their tag. if (token_tag.lexeme()) |lexeme| { return lexeme; } |
ifSimple()
|
// For some tokens, re-tokenization is needed to find the end. var tokenizer: std.zig.Tokenizer = .{ .buffer = tree.source, .index = token_starts[token_index], .pending_invalid_token = null, }; const token = tokenizer.findTagAtCurrentIndex(token_tag); assert(token.tag == token_tag); return tree.source[token.loc.start..token.loc.end]; } |
ifFull()
|
pub fn extraData(tree: Ast, index: usize, comptime T: type) T { const fields = std.meta.fields(T); var result: T = undefined; inline for (fields, 0..) |field, i| { comptime assert(field.type == Node.Index); @field(result, field.name) = tree.extra_data[index + i]; } return result; } |
containerField()lhs is the identifier token payload if any, rhs is the deferred expression. |
pub fn rootDecls(tree: Ast) []const Node.Index { // Root is always index 0. const nodes_data = tree.nodes.items(.data); return tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs]; } |
containerFieldInit()lhs is unused. rhs is the deferred expression. |
pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { const token_tags = tree.tokens.items(.tag); switch (parse_error.tag) { .asterisk_after_ptr_deref => { // Note that the token will point at the `.*` but ideally the source // location would point to the `*` after the `.*`. return stream.writeAll("'.*' cannot be followed by '*'. Are you missing a space?"); }, .chained_comparison_operators => { return stream.writeAll("comparison operators cannot be chained"); }, .decl_between_fields => { return stream.writeAll("declarations are not allowed between container fields"); }, .expected_block => { return stream.print("expected block, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_block_or_assignment => { return stream.print("expected block or assignment, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_block_or_expr => { return stream.print("expected block or expression, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_block_or_field => { return stream.print("expected block or field, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_container_members => { return stream.print("expected test, comptime, var decl, or container field, found '{s}'", .{ token_tags[parse_error.token].symbol(), }); }, .expected_expr => { return stream.print("expected expression, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_expr_or_assignment => { return stream.print("expected expression or assignment, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_fn => { return stream.print("expected function, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_inlinable => { return stream.print("expected 'while' or 'for', found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_labelable => { return stream.print("expected 'while', 'for', 'inline', or '{{', found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_param_list => { return stream.print("expected parameter list, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_prefix_expr => { return stream.print("expected prefix expression, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_primary_type_expr => { return stream.print("expected primary type expression, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_pub_item => { return stream.writeAll("expected function or variable declaration after pub"); }, .expected_return_type => { return stream.print("expected return type expression, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_semi_or_else => { return stream.writeAll("expected ';' or 'else' after statement"); }, .expected_semi_or_lbrace => { return stream.writeAll("expected ';' or block after function prototype"); }, .expected_statement => { return stream.print("expected statement, found '{s}'", .{ token_tags[parse_error.token].symbol(), }); }, .expected_suffix_op => { return stream.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_type_expr => { return stream.print("expected type expression, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_var_decl => { return stream.print("expected variable declaration, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_var_decl_or_fn => { return stream.print("expected variable declaration or function, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_loop_payload => { return stream.print("expected loop payload, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .expected_container => { return stream.print("expected a struct, enum or union, found '{s}'", .{ token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), }); }, .extern_fn_body => { return stream.writeAll("extern functions have no body"); }, .extra_addrspace_qualifier => { return stream.writeAll("extra addrspace qualifier"); }, .extra_align_qualifier => { return stream.writeAll("extra align qualifier"); }, .extra_allowzero_qualifier => { return stream.writeAll("extra allowzero qualifier"); }, .extra_const_qualifier => { return stream.writeAll("extra const qualifier"); }, .extra_volatile_qualifier => { return stream.writeAll("extra volatile qualifier"); }, .ptr_mod_on_array_child_type => { return stream.print("pointer modifier '{s}' not allowed on array child type", .{ token_tags[parse_error.token].symbol(), }); }, .invalid_bit_range => { return stream.writeAll("bit range not allowed on slices and arrays"); }, .same_line_doc_comment => { return stream.writeAll("same line documentation comment"); }, .unattached_doc_comment => { return stream.writeAll("unattached documentation comment"); }, .test_doc_comment => { return stream.writeAll("documentation comments cannot be attached to tests"); }, .comptime_doc_comment => { return stream.writeAll("documentation comments cannot be attached to comptime blocks"); }, .varargs_nonfinal => { return stream.writeAll("function prototype has parameter after varargs"); }, .expected_continue_expr => { return stream.writeAll("expected ':' before while continue expression"); }, |
containerFieldAlign()lhs catch rhs
lhs catch |err| rhs
main_token is the |
.expected_semi_after_decl => { return stream.writeAll("expected ';' after declaration"); }, .expected_semi_after_stmt => { return stream.writeAll("expected ';' after statement"); }, .expected_comma_after_field => { return stream.writeAll("expected ',' after field"); }, .expected_comma_after_arg => { return stream.writeAll("expected ',' after argument"); }, .expected_comma_after_param => { return stream.writeAll("expected ',' after parameter"); }, .expected_comma_after_initializer => { return stream.writeAll("expected ',' after initializer"); }, .expected_comma_after_switch_prong => { return stream.writeAll("expected ',' after switch prong"); }, .expected_comma_after_for_operand => { return stream.writeAll("expected ',' after for operand"); }, .expected_comma_after_capture => { return stream.writeAll("expected ',' after for capture"); }, .expected_initializer => { return stream.writeAll("expected field initializer"); }, .mismatched_binary_op_whitespace => { return stream.print("binary operator `{s}` has whitespace on one side, but not the other.", .{token_tags[parse_error.token].lexeme().?}); }, .invalid_ampersand_ampersand => { return stream.writeAll("ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND"); }, .c_style_container => { return stream.print("'{s} {s}' is invalid", .{ parse_error.extra.expected_tag.symbol(), tree.tokenSlice(parse_error.token), }); }, .zig_style_container => { return stream.print("to declare a container do 'const {s} = {s}'", .{ tree.tokenSlice(parse_error.token), parse_error.extra.expected_tag.symbol(), }); }, .previous_field => { return stream.writeAll("field before declarations here"); }, .next_field => { return stream.writeAll("field after declarations here"); }, .expected_var_const => { return stream.writeAll("expected 'var' or 'const' before variable declaration"); }, .wrong_equal_var_decl => { return stream.writeAll("variable initialized with '==' instead of '='"); }, .var_const_decl => { return stream.writeAll("use 'var' or 'const' to declare variable"); }, .extra_for_capture => { return stream.writeAll("excess for captures"); }, .for_input_not_captured => { return stream.writeAll("for input is not captured"); }, |
fnProtoSimple()
|
.expected_token => { const found_tag = token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)]; const expected_symbol = parse_error.extra.expected_tag.symbol(); switch (found_tag) { .invalid => return stream.print("expected '{s}', found invalid bytes", .{ expected_symbol, }), else => return stream.print("expected '{s}', found '{s}'", .{ expected_symbol, found_tag.symbol(), }), } }, } } |
fnProtoMulti()
|
pub fn firstToken(tree: Ast, 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 end_offset: TokenIndex = 0; var n = node; while (true) switch (tags[n]) { .root => return 0, |
fnProtoOne()
|
.test_decl, .@"errdefer", .@"defer", .bool_not, .negation, .bit_not, .negation_wrap, .address_of, .@"try", .@"await", .optional_type, .@"switch", .switch_comma, .if_simple, .@"if", .@"suspend", .@"resume", .@"continue", .@"break", .@"return", .anyframe_type, .identifier, .anyframe_literal, .char_literal, .number_literal, .unreachable_literal, .string_literal, .multiline_string_literal, .grouped_expression, .builtin_call_two, .builtin_call_two_comma, .builtin_call, .builtin_call_comma, .error_set_decl, .@"comptime", .@"nosuspend", .asm_simple, .@"asm", .array_type, .array_type_sentinel, .error_value, => return main_tokens[n] - end_offset, |
fnProto()
|
.array_init_dot, .array_init_dot_comma, .array_init_dot_two, .array_init_dot_two_comma, .struct_init_dot, .struct_init_dot_comma, .struct_init_dot_two, .struct_init_dot_two_comma, .enum_literal, => return main_tokens[n] - 1 - end_offset, |
structInitOne()
|
.@"catch", .field_access, .unwrap_optional, .equal_equal, .bang_equal, .less_than, .greater_than, .less_or_equal, .greater_or_equal, .assign_mul, .assign_div, .assign_mod, .assign_add, .assign_sub, .assign_shl, .assign_shl_sat, .assign_shr, .assign_bit_and, .assign_bit_xor, .assign_bit_or, .assign_mul_wrap, .assign_add_wrap, .assign_sub_wrap, .assign_mul_sat, .assign_add_sat, .assign_sub_sat, .assign, .merge_error_sets, .mul, .div, .mod, .array_mult, .mul_wrap, .mul_sat, .add, .sub, .array_cat, .add_wrap, .sub_wrap, .add_sat, .sub_sat, .shl, .shl_sat, .shr, .bit_and, .bit_xor, .bit_or, .@"orelse", .bool_and, .bool_or, .slice_open, .slice, .slice_sentinel, .deref, .array_access, .array_init_one, .array_init_one_comma, .array_init, .array_init_comma, .struct_init_one, .struct_init_one_comma, .struct_init, .struct_init_comma, .call_one, .call_one_comma, .call, .call_comma, .switch_range, .for_range, .error_union, => n = datas[n].lhs, |
structInitDotTwo()
|
.fn_decl, .fn_proto_simple, .fn_proto_multi, .fn_proto_one, .fn_proto, => { var i = main_tokens[n]; // fn token while (i > 0) { i -= 1; switch (token_tags[i]) { .keyword_extern, .keyword_export, .keyword_pub, .keyword_inline, .keyword_noinline, .string_literal, => continue, |
structInitDot()
|
else => return i + 1 - end_offset, } } return i - end_offset; }, |
structInit()
|
.@"usingnamespace" => { const main_token = main_tokens[n]; if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) { end_offset += 1; } return main_token - end_offset; }, |
arrayInitOne()
|
.async_call_one, .async_call_one_comma, .async_call, .async_call_comma, => { end_offset += 1; // async token n = datas[n].lhs; }, |
arrayInitDotTwo()
|
.container_field_init, .container_field_align, .container_field, => { const name_token = main_tokens[n]; if (token_tags[name_token] != .keyword_comptime and name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) { end_offset += 1; } return name_token - end_offset; }, |
arrayInitDot()
|
.global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl, => { var i = main_tokens[n]; // mut token while (i > 0) { i -= 1; switch (token_tags[i]) { .keyword_extern, .keyword_export, .keyword_comptime, .keyword_pub, .keyword_threadlocal, .string_literal, => continue, |
arrayInit()
|
else => return i + 1 - end_offset, } } return i - end_offset; }, |
arrayType()
|
.block, .block_semicolon, .block_two, .block_two_semicolon, => { // Look for a label. const lbrace = main_tokens[n]; if (token_tags[lbrace - 1] == .colon and token_tags[lbrace - 2] == .identifier) { end_offset += 2; } return lbrace - end_offset; }, |
arrayTypeSentinel()
|
.container_decl, .container_decl_trailing, .container_decl_two, .container_decl_two_trailing, .container_decl_arg, .container_decl_arg_trailing, .tagged_union, .tagged_union_trailing, .tagged_union_two, .tagged_union_two_trailing, .tagged_union_enum_tag, .tagged_union_enum_tag_trailing, => { const main_token = main_tokens[n]; switch (token_tags[main_token -| 1]) { .keyword_packed, .keyword_extern => end_offset += 1, else => {}, } return main_token - end_offset; }, |
ptrTypeAligned()
|
.ptr_type_aligned, .ptr_type_sentinel, .ptr_type, .ptr_type_bit_range, => { const main_token = main_tokens[n]; return switch (token_tags[main_token]) { .asterisk, .asterisk_asterisk, => switch (token_tags[main_token -| 1]) { .l_bracket => main_token -| 1, else => main_token, }, .l_bracket => main_token, else => unreachable, } - end_offset; }, |
ptrTypeSentinel()
|
.switch_case_one => { if (datas[n].lhs == 0) { return main_tokens[n] - 1 - end_offset; // else token } else { n = datas[n].lhs; } }, .switch_case_inline_one => { if (datas[n].lhs == 0) { return main_tokens[n] - 2 - end_offset; // else token } else { return firstToken(tree, datas[n].lhs) - 1; } }, .switch_case => { const extra = tree.extraData(datas[n].lhs, Node.SubRange); assert(extra.end - extra.start > 0); n = tree.extra_data[extra.start]; }, .switch_case_inline => { const extra = tree.extraData(datas[n].lhs, Node.SubRange); assert(extra.end - extra.start > 0); return firstToken(tree, tree.extra_data[extra.start]) - 1; }, |
ptrType()
|
.asm_output, .asm_input => { assert(token_tags[main_tokens[n] - 1] == .l_bracket); return main_tokens[n] - 1 - end_offset; }, |
ptrTypeBitRange()
|
.while_simple, .while_cont, .@"while", .for_simple, .@"for", => { // Look for a label and inline. const main_token = main_tokens[n]; var result = main_token; if (token_tags[result -| 1] == .keyword_inline) { result -= 1; } if (token_tags[result -| 1] == .colon) { result -|= 2; } return result - end_offset; }, }; } |
sliceOpen()
|
pub fn lastToken(tree: Ast, 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_starts = tree.tokens.items(.start); const token_tags = tree.tokens.items(.tag); var n = node; var end_offset: TokenIndex = 0; while (true) switch (tags[n]) { .root => return @as(TokenIndex, @intCast(tree.tokens.len - 1)), |
slice()
|
.@"usingnamespace", .bool_not, .negation, .bit_not, .negation_wrap, .address_of, .@"try", .@"await", .optional_type, .@"resume", .@"nosuspend", .@"comptime", => n = datas[n].lhs, |
sliceSentinel()
|
.test_decl, .@"errdefer", .@"defer", .@"catch", .equal_equal, .bang_equal, .less_than, .greater_than, .less_or_equal, .greater_or_equal, .assign_mul, .assign_div, .assign_mod, .assign_add, .assign_sub, .assign_shl, .assign_shl_sat, .assign_shr, .assign_bit_and, .assign_bit_xor, .assign_bit_or, .assign_mul_wrap, .assign_add_wrap, .assign_sub_wrap, .assign_mul_sat, .assign_add_sat, .assign_sub_sat, .assign, .merge_error_sets, .mul, .div, .mod, .array_mult, .mul_wrap, .mul_sat, .add, .sub, .array_cat, .add_wrap, .sub_wrap, .add_sat, .sub_sat, .shl, .shl_sat, .shr, .bit_and, .bit_xor, .bit_or, .@"orelse", .bool_and, .bool_or, .anyframe_type, .error_union, .if_simple, .while_simple, .for_simple, .fn_proto_simple, .fn_proto_multi, .ptr_type_aligned, .ptr_type_sentinel, .ptr_type, .ptr_type_bit_range, .array_type, .switch_case_one, .switch_case_inline_one, .switch_case, .switch_case_inline, .switch_range, => n = datas[n].rhs, |
containerDeclTwo()
|
.for_range => if (datas[n].rhs != 0) { n = datas[n].rhs; } else { return main_tokens[n] + end_offset; }, |
containerDecl()
|
.field_access, .unwrap_optional, .grouped_expression, .multiline_string_literal, .error_set_decl, .asm_simple, .asm_output, .asm_input, .error_value, => return datas[n].rhs + end_offset, |
containerDeclArg()
|
.anyframe_literal, .char_literal, .number_literal, .unreachable_literal, .identifier, .deref, .enum_literal, .string_literal, => return main_tokens[n] + end_offset, |
containerDeclRoot()
|
.@"return" => if (datas[n].lhs != 0) { n = datas[n].lhs; } else { return main_tokens[n] + end_offset; }, |
taggedUnionTwo()
|
.call, .async_call => { end_offset += 1; // for the rparen 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 }, .tagged_union_enum_tag => { const members = tree.extraData(datas[n].rhs, Node.SubRange); if (members.end - members.start == 0) { end_offset += 4; // for the rparen + rparen + lbrace + rbrace n = datas[n].lhs; } else { end_offset += 1; // for the rbrace n = tree.extra_data[members.end - 1]; // last parameter } }, .call_comma, .async_call_comma, .tagged_union_enum_tag_trailing, => { end_offset += 2; // for the comma/semicolon + rparen/rbrace const params = tree.extraData(datas[n].rhs, Node.SubRange); assert(params.end > params.start); n = tree.extra_data[params.end - 1]; // last parameter }, .@"switch" => { const cases = tree.extraData(datas[n].rhs, Node.SubRange); if (cases.end - cases.start == 0) { end_offset += 3; // rparen, lbrace, rbrace n = datas[n].lhs; // condition expression } else { end_offset += 1; // for the rbrace n = tree.extra_data[cases.end - 1]; // last case } }, .container_decl_arg => { const members = tree.extraData(datas[n].rhs, Node.SubRange); if (members.end - members.start == 0) { end_offset += 3; // for the rparen + lbrace + rbrace n = datas[n].lhs; } else { end_offset += 1; // for the rbrace n = tree.extra_data[members.end - 1]; // last parameter } }, .@"asm" => { const extra = tree.extraData(datas[n].rhs, Node.Asm); return extra.rparen + end_offset; }, .array_init, .struct_init, => { const elements = tree.extraData(datas[n].rhs, Node.SubRange); assert(elements.end - elements.start > 0); end_offset += 1; // for the rbrace n = tree.extra_data[elements.end - 1]; // last element }, .array_init_comma, .struct_init_comma, .container_decl_arg_trailing, .switch_comma, => { const members = tree.extraData(datas[n].rhs, Node.SubRange); assert(members.end - members.start > 0); end_offset += 2; // for the comma + rbrace n = tree.extra_data[members.end - 1]; // last parameter }, .array_init_dot, .struct_init_dot, .block, .container_decl, .tagged_union, .builtin_call, => { assert(datas[n].rhs - datas[n].lhs > 0); end_offset += 1; // for the rbrace n = tree.extra_data[datas[n].rhs - 1]; // last statement }, .array_init_dot_comma, .struct_init_dot_comma, .block_semicolon, .container_decl_trailing, .tagged_union_trailing, .builtin_call_comma, => { assert(datas[n].rhs - datas[n].lhs > 0); end_offset += 2; // for the comma/semicolon + rbrace/rparen n = tree.extra_data[datas[n].rhs - 1]; // last member }, .call_one, .async_call_one, .array_access, => { end_offset += 1; // for the rparen/rbracket if (datas[n].rhs == 0) { return main_tokens[n] + end_offset; } n = datas[n].rhs; }, .array_init_dot_two, .block_two, .builtin_call_two, .struct_init_dot_two, .container_decl_two, .tagged_union_two, => { if (datas[n].rhs != 0) { end_offset += 1; // for the rparen/rbrace n = datas[n].rhs; } else if (datas[n].lhs != 0) { end_offset += 1; // for the rparen/rbrace n = datas[n].lhs; } else { switch (tags[n]) { .array_init_dot_two, .block_two, .struct_init_dot_two, => end_offset += 1, // rbrace .builtin_call_two => end_offset += 2, // lparen/lbrace + rparen/rbrace .container_decl_two => { var i: u32 = 2; // lbrace + rbrace while (token_tags[main_tokens[n] + i] == .container_doc_comment) i += 1; end_offset += i; }, .tagged_union_two => { var i: u32 = 5; // (enum) {} while (token_tags[main_tokens[n] + i] == .container_doc_comment) i += 1; end_offset += i; }, else => unreachable, } return main_tokens[n] + end_offset; } }, .array_init_dot_two_comma, .builtin_call_two_comma, .block_two_semicolon, .struct_init_dot_two_comma, .container_decl_two_trailing, .tagged_union_two_trailing, => { end_offset += 2; // for the comma/semicolon + rbrace/rparen if (datas[n].rhs != 0) { n = datas[n].rhs; } else if (datas[n].lhs != 0) { n = datas[n].lhs; } else { unreachable; } }, .simple_var_decl => { if (datas[n].rhs != 0) { n = datas[n].rhs; } else if (datas[n].lhs != 0) { n = datas[n].lhs; } else { end_offset += 1; // from mut token to name return main_tokens[n] + end_offset; } }, .aligned_var_decl => { if (datas[n].rhs != 0) { n = datas[n].rhs; } else if (datas[n].lhs != 0) { end_offset += 1; // for the rparen n = datas[n].lhs; } else { end_offset += 1; // from mut token to name return main_tokens[n] + end_offset; } }, .global_var_decl => { if (datas[n].rhs != 0) { n = datas[n].rhs; } else { const extra = tree.extraData(datas[n].lhs, Node.GlobalVarDecl); if (extra.section_node != 0) { end_offset += 1; // for the rparen n = extra.section_node; } else if (extra.align_node != 0) { end_offset += 1; // for the rparen n = extra.align_node; } else if (extra.type_node != 0) { n = extra.type_node; } else { end_offset += 1; // from mut token to name return main_tokens[n] + end_offset; } } }, .local_var_decl => { if (datas[n].rhs != 0) { n = datas[n].rhs; } else { const extra = tree.extraData(datas[n].lhs, Node.LocalVarDecl); if (extra.align_node != 0) { end_offset += 1; // for the rparen n = extra.align_node; } else if (extra.type_node != 0) { n = extra.type_node; } else { end_offset += 1; // from mut token to name return main_tokens[n] + end_offset; } } }, .container_field_init => { if (datas[n].rhs != 0) { n = datas[n].rhs; } else if (datas[n].lhs != 0) { n = datas[n].lhs; } else { return main_tokens[n] + end_offset; } }, .container_field_align => { if (datas[n].rhs != 0) { end_offset += 1; // for the rparen n = datas[n].rhs; } else if (datas[n].lhs != 0) { n = datas[n].lhs; } else { return main_tokens[n] + end_offset; } }, .container_field => { const extra = tree.extraData(datas[n].rhs, Node.ContainerField); if (extra.value_expr != 0) { n = extra.value_expr; } else if (extra.align_expr != 0) { end_offset += 1; // for the rparen n = extra.align_expr; } else if (datas[n].lhs != 0) { n = datas[n].lhs; } else { return main_tokens[n] + end_offset; } }, |
taggedUnion()
|
.array_init_one, .struct_init_one, => { end_offset += 1; // rbrace if (datas[n].rhs == 0) { return main_tokens[n] + end_offset; } else { n = datas[n].rhs; } }, .slice_open, .call_one_comma, .async_call_one_comma, .array_init_one_comma, .struct_init_one_comma, => { end_offset += 2; // ellipsis2 + rbracket, or comma + rparen n = datas[n].rhs; assert(n != 0); }, .slice => { const extra = tree.extraData(datas[n].rhs, Node.Slice); assert(extra.end != 0); // should have used slice_open end_offset += 1; // rbracket n = extra.end; }, .slice_sentinel => { const extra = tree.extraData(datas[n].rhs, Node.SliceSentinel); assert(extra.sentinel != 0); // should have used slice end_offset += 1; // rbracket n = extra.sentinel; }, |
taggedUnionEnumTag()
|
.@"continue" => { if (datas[n].lhs != 0) { return datas[n].lhs + end_offset; } else { return main_tokens[n] + end_offset; } }, .@"break" => { if (datas[n].rhs != 0) { n = datas[n].rhs; } else if (datas[n].lhs != 0) { return datas[n].lhs + end_offset; } else { return main_tokens[n] + end_offset; } }, .fn_decl => { if (datas[n].rhs != 0) { n = datas[n].rhs; } else { n = datas[n].lhs; } }, .fn_proto_one => { const extra = tree.extraData(datas[n].lhs, Node.FnProtoOne); // addrspace, linksection, callconv, align can appear in any order, so we // find the last one here. var max_node: Node.Index = datas[n].rhs; var max_start = token_starts[main_tokens[max_node]]; var max_offset: TokenIndex = 0; if (extra.align_expr != 0) { const start = token_starts[main_tokens[extra.align_expr]]; if (start > max_start) { max_node = extra.align_expr; max_start = start; max_offset = 1; // for the rparen } } if (extra.addrspace_expr != 0) { const start = token_starts[main_tokens[extra.addrspace_expr]]; if (start > max_start) { max_node = extra.addrspace_expr; max_start = start; max_offset = 1; // for the rparen } } if (extra.section_expr != 0) { const start = token_starts[main_tokens[extra.section_expr]]; if (start > max_start) { max_node = extra.section_expr; max_start = start; max_offset = 1; // for the rparen } } if (extra.callconv_expr != 0) { const start = token_starts[main_tokens[extra.callconv_expr]]; if (start > max_start) { max_node = extra.callconv_expr; max_start = start; max_offset = 1; // for the rparen } } n = max_node; end_offset += max_offset; }, .fn_proto => { const extra = tree.extraData(datas[n].lhs, Node.FnProto); // addrspace, linksection, callconv, align can appear in any order, so we // find the last one here. var max_node: Node.Index = datas[n].rhs; var max_start = token_starts[main_tokens[max_node]]; var max_offset: TokenIndex = 0; if (extra.align_expr != 0) { const start = token_starts[main_tokens[extra.align_expr]]; if (start > max_start) { max_node = extra.align_expr; max_start = start; max_offset = 1; // for the rparen } } if (extra.addrspace_expr != 0) { const start = token_starts[main_tokens[extra.addrspace_expr]]; if (start > max_start) { max_node = extra.addrspace_expr; max_start = start; max_offset = 1; // for the rparen } } if (extra.section_expr != 0) { const start = token_starts[main_tokens[extra.section_expr]]; if (start > max_start) { max_node = extra.section_expr; max_start = start; max_offset = 1; // for the rparen } } if (extra.callconv_expr != 0) { const start = token_starts[main_tokens[extra.callconv_expr]]; if (start > max_start) { max_node = extra.callconv_expr; max_start = start; max_offset = 1; // for the rparen } } n = max_node; end_offset += max_offset; }, .while_cont => { const extra = tree.extraData(datas[n].rhs, Node.WhileCont); assert(extra.then_expr != 0); n = extra.then_expr; }, .@"while" => { const extra = tree.extraData(datas[n].rhs, Node.While); assert(extra.else_expr != 0); n = extra.else_expr; }, .@"if" => { const extra = tree.extraData(datas[n].rhs, Node.If); assert(extra.else_expr != 0); n = extra.else_expr; }, .@"for" => { const extra = @as(Node.For, @bitCast(datas[n].rhs)); n = tree.extra_data[datas[n].lhs + extra.inputs + @intFromBool(extra.has_else)]; }, .@"suspend" => { if (datas[n].lhs != 0) { n = datas[n].lhs; } else { return main_tokens[n] + end_offset; } }, .array_type_sentinel => { const extra = tree.extraData(datas[n].rhs, Node.ArrayTypeSentinel); n = extra.elem_type; }, }; } |
switchCaseOne()
|
pub fn tokensOnSameLine(tree: Ast, 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; } |
switchCase()
|
pub fn getNodeSource(tree: Ast, node: Node.Index) []const u8 { const token_starts = tree.tokens.items(.start); const first_token = tree.firstToken(node); const last_token = tree.lastToken(node); const start = token_starts[first_token]; const end = token_starts[last_token] + tree.tokenSlice(last_token).len; return tree.source[start..end]; } |
asmSimple()
|
pub fn globalVarDecl(tree: Ast, node: Node.Index) full.VarDecl { assert(tree.nodes.items(.tag)[node] == .global_var_decl); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.GlobalVarDecl); return tree.fullVarDeclComponents(.{ .type_node = extra.type_node, .align_node = extra.align_node, .addrspace_node = extra.addrspace_node, .section_node = extra.section_node, .init_node = data.rhs, .mut_token = tree.nodes.items(.main_token)[node], }); } |
asmFull()
|
pub fn localVarDecl(tree: Ast, node: Node.Index) full.VarDecl { assert(tree.nodes.items(.tag)[node] == .local_var_decl); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.LocalVarDecl); return tree.fullVarDeclComponents(.{ .type_node = extra.type_node, .align_node = extra.align_node, .addrspace_node = 0, .section_node = 0, .init_node = data.rhs, .mut_token = tree.nodes.items(.main_token)[node], }); } |
whileSimple()
|
pub fn simpleVarDecl(tree: Ast, node: Node.Index) full.VarDecl { assert(tree.nodes.items(.tag)[node] == .simple_var_decl); const data = tree.nodes.items(.data)[node]; return tree.fullVarDeclComponents(.{ .type_node = data.lhs, .align_node = 0, .addrspace_node = 0, .section_node = 0, .init_node = data.rhs, .mut_token = tree.nodes.items(.main_token)[node], }); } |
whileCont()
|
pub fn alignedVarDecl(tree: Ast, node: Node.Index) full.VarDecl { assert(tree.nodes.items(.tag)[node] == .aligned_var_decl); const data = tree.nodes.items(.data)[node]; return tree.fullVarDeclComponents(.{ .type_node = 0, .align_node = data.lhs, .addrspace_node = 0, .section_node = 0, .init_node = data.rhs, .mut_token = tree.nodes.items(.main_token)[node], }); } |
whileFull()
|
pub fn ifSimple(tree: Ast, node: Node.Index) full.If { assert(tree.nodes.items(.tag)[node] == .if_simple); const data = tree.nodes.items(.data)[node]; return tree.fullIfComponents(.{ .cond_expr = data.lhs, .then_expr = data.rhs, .else_expr = 0, .if_token = tree.nodes.items(.main_token)[node], }); } |
forSimple()
|
pub fn ifFull(tree: Ast, 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.fullIfComponents(.{ .cond_expr = data.lhs, .then_expr = extra.then_expr, .else_expr = extra.else_expr, .if_token = tree.nodes.items(.main_token)[node], }); } |
forFull()
|
pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField { assert(tree.nodes.items(.tag)[node] == .container_field); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.ContainerField); const main_token = tree.nodes.items(.main_token)[node]; return tree.fullContainerFieldComponents(.{ .main_token = main_token, .type_expr = data.lhs, .value_expr = extra.value_expr, .align_expr = extra.align_expr, .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or tree.tokens.items(.tag)[main_token + 1] != .colon, }); } |
callOne()
|
pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField { assert(tree.nodes.items(.tag)[node] == .container_field_init); const data = tree.nodes.items(.data)[node]; const main_token = tree.nodes.items(.main_token)[node]; return tree.fullContainerFieldComponents(.{ .main_token = main_token, .type_expr = data.lhs, .value_expr = data.rhs, .align_expr = 0, .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or tree.tokens.items(.tag)[main_token + 1] != .colon, }); } |
callFull()
|
pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField { assert(tree.nodes.items(.tag)[node] == .container_field_align); const data = tree.nodes.items(.data)[node]; const main_token = tree.nodes.items(.main_token)[node]; return tree.fullContainerFieldComponents(.{ .main_token = main_token, .type_expr = data.lhs, .value_expr = 0, .align_expr = data.rhs, .tuple_like = tree.tokens.items(.tag)[main_token] != .identifier or tree.tokens.items(.tag)[main_token + 1] != .colon, }); } |
fullVarDecl()
|
pub fn fnProtoSimple(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.FnProto { assert(tree.nodes.items(.tag)[node] == .fn_proto_simple); const data = tree.nodes.items(.data)[node]; buffer[0] = data.lhs; const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1]; return tree.fullFnProtoComponents(.{ .proto_node = node, .fn_token = tree.nodes.items(.main_token)[node], .return_type = data.rhs, .params = params, .align_expr = 0, .addrspace_expr = 0, .section_expr = 0, .callconv_expr = 0, }); } |
fullIf()
|
pub fn fnProtoMulti(tree: Ast, node: Node.Index) full.FnProto { assert(tree.nodes.items(.tag)[node] == .fn_proto_multi); const data = tree.nodes.items(.data)[node]; const params_range = tree.extraData(data.lhs, Node.SubRange); const params = tree.extra_data[params_range.start..params_range.end]; return tree.fullFnProtoComponents(.{ .proto_node = node, .fn_token = tree.nodes.items(.main_token)[node], .return_type = data.rhs, .params = params, .align_expr = 0, .addrspace_expr = 0, .section_expr = 0, .callconv_expr = 0, }); } |
fullWhile()
|
pub fn fnProtoOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.FnProto { assert(tree.nodes.items(.tag)[node] == .fn_proto_one); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.FnProtoOne); buffer[0] = extra.param; const params = if (extra.param == 0) buffer[0..0] else buffer[0..1]; return tree.fullFnProtoComponents(.{ .proto_node = node, .fn_token = tree.nodes.items(.main_token)[node], .return_type = data.rhs, .params = params, .align_expr = extra.align_expr, .addrspace_expr = extra.addrspace_expr, .section_expr = extra.section_expr, .callconv_expr = extra.callconv_expr, }); } |
fullFor()
|
pub fn fnProto(tree: Ast, node: Node.Index) full.FnProto { assert(tree.nodes.items(.tag)[node] == .fn_proto); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.FnProto); const params = tree.extra_data[extra.params_start..extra.params_end]; return tree.fullFnProtoComponents(.{ .proto_node = node, .fn_token = tree.nodes.items(.main_token)[node], .return_type = data.rhs, .params = params, .align_expr = extra.align_expr, .addrspace_expr = extra.addrspace_expr, .section_expr = extra.section_expr, .callconv_expr = extra.callconv_expr, }); } |
fullContainerField()
|
pub fn structInitOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.StructInit { assert(tree.nodes.items(.tag)[node] == .struct_init_one or tree.nodes.items(.tag)[node] == .struct_init_one_comma); const data = tree.nodes.items(.data)[node]; buffer[0] = data.rhs; const fields = if (data.rhs == 0) buffer[0..0] else buffer[0..1]; return .{ .ast = .{ .lbrace = tree.nodes.items(.main_token)[node], .fields = fields, .type_expr = data.lhs, }, }; } |
fullFnProto()
|
pub fn structInitDotTwo(tree: Ast, buffer: *[2]Node.Index, node: Node.Index) full.StructInit { assert(tree.nodes.items(.tag)[node] == .struct_init_dot_two or tree.nodes.items(.tag)[node] == .struct_init_dot_two_comma); const data = tree.nodes.items(.data)[node]; buffer.* = .{ data.lhs, data.rhs }; const fields = if (data.rhs != 0) buffer[0..2] else if (data.lhs != 0) buffer[0..1] else buffer[0..0]; return .{ .ast = .{ .lbrace = tree.nodes.items(.main_token)[node], .fields = fields, .type_expr = 0, }, }; } |
fullStructInit()
|
pub fn structInitDot(tree: Ast, node: Node.Index) full.StructInit { assert(tree.nodes.items(.tag)[node] == .struct_init_dot or tree.nodes.items(.tag)[node] == .struct_init_dot_comma); const data = tree.nodes.items(.data)[node]; return .{ .ast = .{ .lbrace = tree.nodes.items(.main_token)[node], .fields = tree.extra_data[data.lhs..data.rhs], .type_expr = 0, }, }; } |
fullArrayInit()
|
pub fn structInit(tree: Ast, node: Node.Index) full.StructInit { assert(tree.nodes.items(.tag)[node] == .struct_init or tree.nodes.items(.tag)[node] == .struct_init_comma); const data = tree.nodes.items(.data)[node]; const fields_range = tree.extraData(data.rhs, Node.SubRange); return .{ .ast = .{ .lbrace = tree.nodes.items(.main_token)[node], .fields = tree.extra_data[fields_range.start..fields_range.end], .type_expr = data.lhs, }, }; } |
fullArrayType()
|
pub fn arrayInitOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.ArrayInit { assert(tree.nodes.items(.tag)[node] == .array_init_one or tree.nodes.items(.tag)[node] == .array_init_one_comma); const data = tree.nodes.items(.data)[node]; buffer[0] = data.rhs; const elements = if (data.rhs == 0) buffer[0..0] else buffer[0..1]; return .{ .ast = .{ .lbrace = tree.nodes.items(.main_token)[node], .elements = elements, .type_expr = data.lhs, }, }; } |
fullPtrType()
|
pub fn arrayInitDotTwo(tree: Ast, buffer: *[2]Node.Index, node: Node.Index) full.ArrayInit { assert(tree.nodes.items(.tag)[node] == .array_init_dot_two or tree.nodes.items(.tag)[node] == .array_init_dot_two_comma); const data = tree.nodes.items(.data)[node]; buffer.* = .{ data.lhs, data.rhs }; const elements = if (data.rhs != 0) buffer[0..2] else if (data.lhs != 0) buffer[0..1] else buffer[0..0]; return .{ .ast = .{ .lbrace = tree.nodes.items(.main_token)[node], .elements = elements, .type_expr = 0, }, }; } |
fullSlice()
|
pub fn arrayInitDot(tree: Ast, node: Node.Index) full.ArrayInit { assert(tree.nodes.items(.tag)[node] == .array_init_dot or tree.nodes.items(.tag)[node] == .array_init_dot_comma); const data = tree.nodes.items(.data)[node]; return .{ .ast = .{ .lbrace = tree.nodes.items(.main_token)[node], .elements = tree.extra_data[data.lhs..data.rhs], .type_expr = 0, }, }; } |
fullContainerDecl()
|
pub fn arrayInit(tree: Ast, node: Node.Index) full.ArrayInit { assert(tree.nodes.items(.tag)[node] == .array_init or tree.nodes.items(.tag)[node] == .array_init_comma); const data = tree.nodes.items(.data)[node]; const elem_range = tree.extraData(data.rhs, Node.SubRange); return .{ .ast = .{ .lbrace = tree.nodes.items(.main_token)[node], .elements = tree.extra_data[elem_range.start..elem_range.end], .type_expr = data.lhs, }, }; } |
fullSwitchCase()
|
pub fn arrayType(tree: Ast, node: Node.Index) full.ArrayType { assert(tree.nodes.items(.tag)[node] == .array_type); const data = tree.nodes.items(.data)[node]; return .{ .ast = .{ .lbracket = tree.nodes.items(.main_token)[node], .elem_count = data.lhs, .sentinel = 0, .elem_type = data.rhs, }, }; } |
fullAsm()
|
pub fn arrayTypeSentinel(tree: Ast, node: Node.Index) full.ArrayType { assert(tree.nodes.items(.tag)[node] == .array_type_sentinel); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel); assert(extra.sentinel != 0); return .{ .ast = .{ .lbracket = tree.nodes.items(.main_token)[node], .elem_count = data.lhs, .sentinel = extra.sentinel, .elem_type = extra.elem_type, }, }; } |
fullCall()
|
pub fn ptrTypeAligned(tree: Ast, node: Node.Index) full.PtrType { assert(tree.nodes.items(.tag)[node] == .ptr_type_aligned); const data = tree.nodes.items(.data)[node]; return tree.fullPtrTypeComponents(.{ .main_token = tree.nodes.items(.main_token)[node], .align_node = data.lhs, .addrspace_node = 0, .sentinel = 0, .bit_range_start = 0, .bit_range_end = 0, .child_type = data.rhs, }); } |
full
|
pub fn ptrTypeSentinel(tree: Ast, node: Node.Index) full.PtrType { assert(tree.nodes.items(.tag)[node] == .ptr_type_sentinel); const data = tree.nodes.items(.data)[node]; return tree.fullPtrTypeComponents(.{ .main_token = tree.nodes.items(.main_token)[node], .align_node = 0, .addrspace_node = 0, .sentinel = data.lhs, .bit_range_start = 0, .bit_range_end = 0, .child_type = data.rhs, }); } |
VarDecl
|
pub fn ptrType(tree: Ast, node: Node.Index) full.PtrType { assert(tree.nodes.items(.tag)[node] == .ptr_type); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.PtrType); return tree.fullPtrTypeComponents(.{ .main_token = tree.nodes.items(.main_token)[node], .align_node = extra.align_node, .addrspace_node = extra.addrspace_node, .sentinel = extra.sentinel, .bit_range_start = 0, .bit_range_end = 0, .child_type = data.rhs, }); } |
Components
|
pub fn ptrTypeBitRange(tree: Ast, node: Node.Index) full.PtrType { assert(tree.nodes.items(.tag)[node] == .ptr_type_bit_range); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.PtrTypeBitRange); return tree.fullPtrTypeComponents(.{ .main_token = tree.nodes.items(.main_token)[node], .align_node = extra.align_node, .addrspace_node = extra.addrspace_node, .sentinel = extra.sentinel, .bit_range_start = extra.bit_range_start, .bit_range_end = extra.bit_range_end, .child_type = data.rhs, }); } |
firstToken()
|
pub fn sliceOpen(tree: Ast, node: Node.Index) full.Slice { assert(tree.nodes.items(.tag)[node] == .slice_open); const data = tree.nodes.items(.data)[node]; return .{ .ast = .{ .sliced = data.lhs, .lbracket = tree.nodes.items(.main_token)[node], .start = data.rhs, .end = 0, .sentinel = 0, }, }; } |
If
|
pub fn slice(tree: Ast, node: Node.Index) full.Slice { assert(tree.nodes.items(.tag)[node] == .slice); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.Slice); return .{ .ast = .{ .sliced = data.lhs, .lbracket = tree.nodes.items(.main_token)[node], .start = extra.start, .end = extra.end, .sentinel = 0, }, }; } |
Components
|
pub fn sliceSentinel(tree: Ast, node: Node.Index) full.Slice { assert(tree.nodes.items(.tag)[node] == .slice_sentinel); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.SliceSentinel); return .{ .ast = .{ .sliced = data.lhs, .lbracket = tree.nodes.items(.main_token)[node], .start = extra.start, .end = extra.end, .sentinel = extra.sentinel, }, }; } |
While
|
pub fn containerDeclTwo(tree: Ast, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .container_decl_two or tree.nodes.items(.tag)[node] == .container_decl_two_trailing); const data = tree.nodes.items(.data)[node]; buffer.* = .{ data.lhs, data.rhs }; const members = if (data.rhs != 0) buffer[0..2] else if (data.lhs != 0) buffer[0..1] else buffer[0..0]; return tree.fullContainerDeclComponents(.{ .main_token = tree.nodes.items(.main_token)[node], .enum_token = null, .members = members, .arg = 0, }); } |
Componentslhs is index into ptr_type. rhs is the element type expression. main_token is the asterisk if a pointer or the lbracket if a slice main_token might be a ** token, which is shared with a parent/child pointer type and may require special handling. |
pub fn containerDecl(tree: Ast, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .container_decl or tree.nodes.items(.tag)[node] == .container_decl_trailing); const data = tree.nodes.items(.data)[node]; return tree.fullContainerDeclComponents(.{ .main_token = tree.nodes.items(.main_token)[node], .enum_token = null, .members = tree.extra_data[data.lhs..data.rhs], .arg = 0, }); } |
Forlhs is index into ptr_type_bit_range. rhs is the element type expression. main_token is the asterisk if a pointer or the lbracket if a slice main_token might be a ** token, which is shared with a parent/child pointer type and may require special handling. |
pub fn containerDeclArg(tree: Ast, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .container_decl_arg or tree.nodes.items(.tag)[node] == .container_decl_arg_trailing); const data = tree.nodes.items(.data)[node]; const members_range = tree.extraData(data.rhs, Node.SubRange); return tree.fullContainerDeclComponents(.{ .main_token = tree.nodes.items(.main_token)[node], .enum_token = null, .members = tree.extra_data[members_range.start..members_range.end], .arg = data.lhs, }); } |
Components
|
pub fn containerDeclRoot(tree: Ast) full.ContainerDecl { return .{ .layout_token = null, .ast = .{ .main_token = undefined, .enum_token = null, .members = tree.rootDecls(), .arg = 0, }, }; } |
isOldSyntax()
|
pub fn taggedUnionTwo(tree: Ast, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .tagged_union_two or tree.nodes.items(.tag)[node] == .tagged_union_two_trailing); const data = tree.nodes.items(.data)[node]; buffer.* = .{ data.lhs, data.rhs }; const members = if (data.rhs != 0) buffer[0..2] else if (data.lhs != 0) buffer[0..1] else buffer[0..0]; const main_token = tree.nodes.items(.main_token)[node]; return tree.fullContainerDeclComponents(.{ .main_token = main_token, .enum_token = main_token + 2, // union lparen enum .members = members, .arg = 0, }); } |
ContainerField
|
pub fn taggedUnion(tree: Ast, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .tagged_union or tree.nodes.items(.tag)[node] == .tagged_union_trailing); const data = tree.nodes.items(.data)[node]; const main_token = tree.nodes.items(.main_token)[node]; return tree.fullContainerDeclComponents(.{ .main_token = main_token, .enum_token = main_token + 2, // union lparen enum .members = tree.extra_data[data.lhs..data.rhs], .arg = 0, }); } |
Components
|
pub fn taggedUnionEnumTag(tree: Ast, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .tagged_union_enum_tag or tree.nodes.items(.tag)[node] == .tagged_union_enum_tag_trailing); const data = tree.nodes.items(.data)[node]; const members_range = tree.extraData(data.rhs, Node.SubRange); const main_token = tree.nodes.items(.main_token)[node]; return tree.fullContainerDeclComponents(.{ .main_token = main_token, .enum_token = main_token + 2, // union lparen enum .members = tree.extra_data[members_range.start..members_range.end], .arg = data.lhs, }); } |
firstToken()
|
pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase { const data = &tree.nodes.items(.data)[node]; const values: *[1]Node.Index = &data.lhs; return tree.fullSwitchCaseComponents(.{ .values = if (data.lhs == 0) values[0..0] else values[0..1], .arrow_token = tree.nodes.items(.main_token)[node], .target_expr = data.rhs, }, node); } |
convertToNonTupleLike()
|
pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase { const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.SubRange); return tree.fullSwitchCaseComponents(.{ .values = tree.extra_data[extra.start..extra.end], .arrow_token = tree.nodes.items(.main_token)[node], .target_expr = data.rhs, }, node); } |
FnProto
|
pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm { const data = tree.nodes.items(.data)[node]; return tree.fullAsmComponents(.{ .asm_token = tree.nodes.items(.main_token)[node], .template = data.lhs, .items = &.{}, .rparen = data.rhs, }); } |
Components
|
pub fn asmFull(tree: Ast, node: Node.Index) full.Asm { const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.Asm); return tree.fullAsmComponents(.{ .asm_token = tree.nodes.items(.main_token)[node], .template = data.lhs, .items = tree.extra_data[extra.items_start..extra.items_end], .rparen = extra.rparen, }); } |
ParamSame as |
pub fn whileSimple(tree: Ast, node: Node.Index) full.While { const data = tree.nodes.items(.data)[node]; return tree.fullWhileComponents(.{ .while_token = tree.nodes.items(.main_token)[node], .cond_expr = data.lhs, .cont_expr = 0, .then_expr = data.rhs, .else_expr = 0, }); } |
firstToken()
|
pub fn whileCont(tree: Ast, node: Node.Index) full.While { const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.WhileCont); return tree.fullWhileComponents(.{ .while_token = tree.nodes.items(.main_token)[node], .cond_expr = data.lhs, .cont_expr = extra.cont_expr, .then_expr = extra.then_expr, .else_expr = 0, }); } |
IteratorSame as |
pub fn whileFull(tree: Ast, node: Node.Index) full.While { const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.While); return tree.fullWhileComponents(.{ .while_token = tree.nodes.items(.main_token)[node], .cond_expr = data.lhs, .cont_expr = extra.cont_expr, .then_expr = extra.then_expr, .else_expr = extra.else_expr, }); } |
next()
|
pub fn forSimple(tree: Ast, node: Node.Index) full.For { const data = &tree.nodes.items(.data)[node]; const inputs: *[1]Node.Index = &data.lhs; return tree.fullForComponents(.{ .for_token = tree.nodes.items(.main_token)[node], .inputs = inputs[0..1], .then_expr = data.rhs, .else_expr = 0, }); } |
iterate()Same as |
pub fn forFull(tree: Ast, node: Node.Index) full.For { const data = tree.nodes.items(.data)[node]; const extra = @as(Node.For, @bitCast(data.rhs)); const inputs = tree.extra_data[data.lhs..][0..extra.inputs]; const then_expr = tree.extra_data[data.lhs + extra.inputs]; const else_expr = if (extra.has_else) tree.extra_data[data.lhs + extra.inputs + 1] else 0; return tree.fullForComponents(.{ .for_token = tree.nodes.items(.main_token)[node], .inputs = inputs, .then_expr = then_expr, .else_expr = else_expr, }); } |
StructInit
|
pub fn callOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.Call { const data = tree.nodes.items(.data)[node]; buffer.* = .{data.rhs}; const params = if (data.rhs != 0) buffer[0..1] else buffer[0..0]; return tree.fullCallComponents(.{ .lparen = tree.nodes.items(.main_token)[node], .fn_expr = data.lhs, .params = params, }); } |
Components
|
pub fn callFull(tree: Ast, node: Node.Index) full.Call { const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.SubRange); return tree.fullCallComponents(.{ .lparen = tree.nodes.items(.main_token)[node], .fn_expr = data.lhs, .params = tree.extra_data[extra.start..extra.end], }); } |
ArrayInit
|
fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) 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, .string_literal => result.lib_name = i, else => break, } } return result; } |
ComponentsSame as |
fn fullIfComponents(tree: Ast, info: full.If.Components) 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; } |
ArrayType
|
fn fullContainerFieldComponents(tree: Ast, info: full.ContainerField.Components) full.ContainerField { const token_tags = tree.tokens.items(.tag); var result: full.ContainerField = .{ .ast = info, .comptime_token = null, }; if (token_tags[info.main_token] == .keyword_comptime) { // comptime type = init, // ^ result.comptime_token = info.main_token; } else if (info.main_token > 0 and token_tags[info.main_token - 1] == .keyword_comptime) { // comptime name: type = init, // ^ result.comptime_token = info.main_token - 1; } return result; } |
ComponentsSame as |
fn fullFnProtoComponents(tree: Ast, info: full.FnProto.Components) full.FnProto { const token_tags = tree.tokens.items(.tag); var result: full.FnProto = .{ .ast = info, .visib_token = null, .extern_export_inline_token = null, .lib_name = null, .name_token = null, .lparen = undefined, }; var i = info.fn_token; while (i > 0) { i -= 1; switch (token_tags[i]) { .keyword_extern, .keyword_export, .keyword_inline, .keyword_noinline, => result.extern_export_inline_token = i, .keyword_pub => result.visib_token = i, .string_literal => result.lib_name = i, else => break, } } const after_fn_token = info.fn_token + 1; if (token_tags[after_fn_token] == .identifier) { result.name_token = after_fn_token; result.lparen = after_fn_token + 1; } else { result.lparen = after_fn_token; } assert(token_tags[result.lparen] == .l_paren); |
PtrType
|
return result; } |
ComponentsSame as |
fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType { const token_tags = tree.tokens.items(.tag); const Size = std.builtin.Type.Pointer.Size; const size: Size = switch (token_tags[info.main_token]) { .asterisk, .asterisk_asterisk, => switch (token_tags[info.main_token + 1]) { .r_bracket, .colon => .Many, .identifier => if (token_tags[info.main_token -| 1] == .l_bracket) Size.C else .One, else => .One, }, .l_bracket => Size.Slice, else => unreachable, }; var result: full.PtrType = .{ .size = size, .allowzero_token = null, .const_token = null, .volatile_token = null, .ast = info, }; // We need to be careful that we don't iterate over any sub-expressions // here while looking for modifiers as that could result in false // positives. Therefore, start after a sentinel if there is one and // skip over any align node and bit range nodes. var i = if (info.sentinel != 0) tree.lastToken(info.sentinel) + 1 else info.main_token; const end = tree.firstToken(info.child_type); while (i < end) : (i += 1) { switch (token_tags[i]) { .keyword_allowzero => result.allowzero_token = i, .keyword_const => result.const_token = i, .keyword_volatile => result.volatile_token = i, .keyword_align => { assert(info.align_node != 0); if (info.bit_range_end != 0) { assert(info.bit_range_start != 0); i = tree.lastToken(info.bit_range_end) + 1; } else { i = tree.lastToken(info.align_node) + 1; } }, else => {}, } } return result; } |
Slice
|
fn fullContainerDeclComponents(tree: Ast, info: full.ContainerDecl.Components) full.ContainerDecl { const token_tags = tree.tokens.items(.tag); var result: full.ContainerDecl = .{ .ast = info, .layout_token = null, }; |
Components
|
if (info.main_token == 0) return result; |
ContainerDecl
|
switch (token_tags[info.main_token - 1]) { .keyword_extern, .keyword_packed => result.layout_token = info.main_token - 1, else => {}, } return result; } |
Components
|
fn fullSwitchCaseComponents(tree: Ast, info: full.SwitchCase.Components, node: Node.Index) full.SwitchCase { const token_tags = tree.tokens.items(.tag); const node_tags = tree.nodes.items(.tag); var result: full.SwitchCase = .{ .ast = info, .payload_token = null, .inline_token = null, }; if (token_tags[info.arrow_token + 1] == .pipe) { result.payload_token = info.arrow_token + 2; } switch (node_tags[node]) { .switch_case_inline, .switch_case_inline_one => result.inline_token = firstToken(tree, node), else => {}, } return result; } |
SwitchCase
|
fn fullAsmComponents(tree: Ast, info: full.Asm.Components) full.Asm { const token_tags = tree.tokens.items(.tag); const node_tags = tree.nodes.items(.tag); var result: full.Asm = .{ .ast = info, .volatile_token = null, .inputs = &.{}, .outputs = &.{}, .first_clobber = null, }; if (token_tags[info.asm_token + 1] == .keyword_volatile) { result.volatile_token = info.asm_token + 1; } const outputs_end: usize = for (info.items, 0..) |item, i| { switch (node_tags[item]) { .asm_output => continue, else => break i, } } else info.items.len; |
Components
|
result.outputs = info.items[0..outputs_end]; result.inputs = info.items[outputs_end..]; |
Asm
|
if (info.items.len == 0) { // asm ("foo" ::: "a", "b"); const template_token = tree.lastToken(info.template); if (token_tags[template_token + 1] == .colon and token_tags[template_token + 2] == .colon and token_tags[template_token + 3] == .colon and token_tags[template_token + 4] == .string_literal) { result.first_clobber = template_token + 4; } } else if (result.inputs.len != 0) { // asm ("foo" :: [_] "" (y) : "a", "b"); const last_input = result.inputs[result.inputs.len - 1]; const rparen = tree.lastToken(last_input); var i = rparen + 1; // Allow a (useless) comma right after the closing parenthesis. if (token_tags[i] == .comma) i += 1; if (token_tags[i] == .colon and token_tags[i + 1] == .string_literal) { result.first_clobber = i + 1; } } else { // asm ("foo" : [_] "" (x) :: "a", "b"); const last_output = result.outputs[result.outputs.len - 1]; const rparen = tree.lastToken(last_output); var i = rparen + 1; // Allow a (useless) comma right after the closing parenthesis. if (token_tags[i] == .comma) i += 1; if (token_tags[i] == .colon and token_tags[i + 1] == .colon and token_tags[i + 2] == .string_literal) { result.first_clobber = i + 2; } } |
Components
|
return result; } |
Call
|
fn fullWhileComponents(tree: Ast, info: full.While.Components) full.While { const token_tags = tree.tokens.items(.tag); var result: full.While = .{ .ast = info, .inline_token = null, .label_token = null, .payload_token = null, .else_token = undefined, .error_token = null, }; var tok_i = info.while_token -| 1; if (token_tags[tok_i] == .keyword_inline) { result.inline_token = tok_i; tok_i -|= 1; } if (token_tags[tok_i] == .colon and token_tags[tok_i -| 1] == .identifier) { result.label_token = tok_i - 1; } const last_cond_token = tree.lastToken(info.cond_expr); if (token_tags[last_cond_token + 2] == .pipe) { result.payload_token = last_cond_token + 3; } 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; } |
ComponentsSame as switch except there is known to be a trailing comma before the final rbrace |
fn fullForComponents(tree: Ast, info: full.For.Components) full.For { const token_tags = tree.tokens.items(.tag); var result: full.For = .{ .ast = info, .inline_token = null, .label_token = null, .payload_token = undefined, .else_token = undefined, }; var tok_i = info.for_token -| 1; if (token_tags[tok_i] == .keyword_inline) { result.inline_token = tok_i; tok_i -|= 1; } if (token_tags[tok_i] == .colon and token_tags[tok_i -| 1] == .identifier) { result.label_token = tok_i - 1; } const last_cond_token = tree.lastToken(info.inputs[info.inputs.len - 1]); result.payload_token = last_cond_token + 3 + @intFromBool(token_tags[last_cond_token + 1] == .comma); if (info.else_expr != 0) { result.else_token = tree.lastToken(info.then_expr) + 1; } return result; } |
Error
|
fn fullCallComponents(tree: Ast, info: full.Call.Components) full.Call { const token_tags = tree.tokens.items(.tag); var result: full.Call = .{ .ast = info, .async_token = null, }; const first_token = tree.firstToken(info.fn_expr); if (first_token != 0 and token_tags[first_token - 1] == .keyword_async) { result.async_token = first_token - 1; } return result; } |
TagSame ast |
pub fn fullVarDecl(tree: Ast, node: Node.Index) ?full.VarDecl { return switch (tree.nodes.items(.tag)[node]) { .global_var_decl => tree.globalVarDecl(node), .local_var_decl => tree.localVarDecl(node), .aligned_var_decl => tree.alignedVarDecl(node), .simple_var_decl => tree.simpleVarDecl(node), else => null, }; } |
Node
|
pub fn fullIf(tree: Ast, node: Node.Index) ?full.If { return switch (tree.nodes.items(.tag)[node]) { .if_simple => tree.ifSimple(node), .@"if" => tree.ifFull(node), else => null, }; } |
IndexSame ast |
pub fn fullWhile(tree: Ast, node: Node.Index) ?full.While { return switch (tree.nodes.items(.tag)[node]) { .while_simple => tree.whileSimple(node), .while_cont => tree.whileCont(node), .@"while" => tree.whileFull(node), else => null, }; } |
Tag
|
pub fn fullFor(tree: Ast, node: Node.Index) ?full.For { return switch (tree.nodes.items(.tag)[node]) { .for_simple => tree.forSimple(node), .@"for" => tree.forFull(node), else => null, }; } |
isContainerField()
|
pub fn fullContainerField(tree: Ast, node: Node.Index) ?full.ContainerField { return switch (tree.nodes.items(.tag)[node]) { .container_field_init => tree.containerFieldInit(node), .container_field_align => tree.containerFieldAlign(node), .container_field => tree.containerField(node), else => null, }; } |
Data
|
pub fn fullFnProto(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.FnProto { return switch (tree.nodes.items(.tag)[node]) { .fn_proto => tree.fnProto(node), .fn_proto_multi => tree.fnProtoMulti(node), .fn_proto_one => tree.fnProtoOne(buffer, node), .fn_proto_simple => tree.fnProtoSimple(buffer, node), .fn_decl => tree.fullFnProto(buffer, tree.nodes.items(.data)[node].lhs), else => null, }; } |
LocalVarDecl
|
pub fn fullStructInit(tree: Ast, buffer: *[2]Ast.Node.Index, node: Node.Index) ?full.StructInit { return switch (tree.nodes.items(.tag)[node]) { .struct_init_one, .struct_init_one_comma => tree.structInitOne(buffer[0..1], node), .struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(buffer, node), .struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node), .struct_init, .struct_init_comma => tree.structInit(node), else => null, }; } |
ArrayTypeSentinel
|
pub fn fullArrayInit(tree: Ast, buffer: *[2]Node.Index, node: Node.Index) ?full.ArrayInit { return switch (tree.nodes.items(.tag)[node]) { .array_init_one, .array_init_one_comma => tree.arrayInitOne(buffer[0..1], node), .array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(buffer, node), .array_init_dot, .array_init_dot_comma => tree.arrayInitDot(node), .array_init, .array_init_comma => tree.arrayInit(node), else => null, }; } |
PtrType
|
pub fn fullArrayType(tree: Ast, node: Node.Index) ?full.ArrayType { return switch (tree.nodes.items(.tag)[node]) { .array_type => tree.arrayType(node), .array_type_sentinel => tree.arrayTypeSentinel(node), else => null, }; } |
PtrTypeBitRange
|
pub fn fullPtrType(tree: Ast, node: Node.Index) ?full.PtrType { return switch (tree.nodes.items(.tag)[node]) { .ptr_type_aligned => tree.ptrTypeAligned(node), .ptr_type_sentinel => tree.ptrTypeSentinel(node), .ptr_type => tree.ptrType(node), .ptr_type_bit_range => tree.ptrTypeBitRange(node), else => null, }; } |
SubRange
|
pub fn fullSlice(tree: Ast, node: Node.Index) ?full.Slice { return switch (tree.nodes.items(.tag)[node]) { .slice_open => tree.sliceOpen(node), .slice => tree.slice(node), .slice_sentinel => tree.sliceSentinel(node), else => null, }; } |
If
|
pub fn fullContainerDecl(tree: Ast, buffer: *[2]Ast.Node.Index, node: Node.Index) ?full.ContainerDecl { return switch (tree.nodes.items(.tag)[node]) { .root => tree.containerDeclRoot(), .container_decl, .container_decl_trailing => tree.containerDecl(node), .container_decl_arg, .container_decl_arg_trailing => tree.containerDeclArg(node), .container_decl_two, .container_decl_two_trailing => tree.containerDeclTwo(buffer, node), .tagged_union, .tagged_union_trailing => tree.taggedUnion(node), .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => tree.taggedUnionEnumTag(node), .tagged_union_two, .tagged_union_two_trailing => tree.taggedUnionTwo(buffer, node), else => null, }; } |
ContainerField
|
pub fn fullSwitchCase(tree: Ast, node: Node.Index) ?full.SwitchCase { return switch (tree.nodes.items(.tag)[node]) { .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(node), .switch_case, .switch_case_inline => tree.switchCase(node), else => null, }; } |
GlobalVarDecl
|
pub fn fullAsm(tree: Ast, node: Node.Index) ?full.Asm { return switch (tree.nodes.items(.tag)[node]) { .asm_simple => tree.asmSimple(node), .@"asm" => tree.asmFull(node), else => null, }; } |
Slice
|
pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.Call { return switch (tree.nodes.items(.tag)[node]) { .call, .call_comma, .async_call, .async_call_comma => tree.callFull(node), .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(buffer, node), else => null, }; } |
SliceSentinel
|
/// 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: Components, |
While
|
pub const Components = struct { mut_token: TokenIndex, type_node: Node.Index, align_node: Node.Index, addrspace_node: Node.Index, section_node: Node.Index, init_node: Node.Index, }; |
WhileCont
|
pub fn firstToken(var_decl: VarDecl) TokenIndex { return var_decl.visib_token orelse var_decl.extern_export_token orelse var_decl.threadlocal_token orelse var_decl.comptime_token orelse var_decl.ast.mut_token; } }; |
For
|
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: Components, |
FnProtoOne
|
pub const Components = struct { if_token: TokenIndex, cond_expr: Node.Index, then_expr: Node.Index, else_expr: Node.Index, }; }; |
FnProto
|
pub const While = struct { ast: Components, inline_token: ?TokenIndex, label_token: ?TokenIndex, payload_token: ?TokenIndex, error_token: ?TokenIndex, /// Populated only if else_expr != 0. else_token: TokenIndex, |
Asmlhs is the fn_proto. rhs is the function body block. Note that extern function declarations use the fn_proto tags rather than this one. |
pub const Components = struct { while_token: TokenIndex, cond_expr: Node.Index, cont_expr: Node.Index, then_expr: Node.Index, else_expr: Node.Index, }; }; pub const For = struct { ast: Components, inline_token: ?TokenIndex, label_token: ?TokenIndex, payload_token: TokenIndex, /// Populated only if else_expr != 0. else_token: TokenIndex, pub const Components = struct { for_token: TokenIndex, inputs: []const Node.Index, then_expr: Node.Index, else_expr: Node.Index, }; /// TODO: remove this after zig 0.11.0 is tagged. pub fn isOldSyntax(f: For, token_tags: []const Token.Tag) bool { if (f.ast.inputs.len != 1) return false; if (token_tags[f.payload_token + 1] == .comma) return true; if (token_tags[f.payload_token] == .asterisk and token_tags[f.payload_token + 2] == .comma) { return true; } return false; } }; pub const ContainerField = struct { comptime_token: ?TokenIndex, ast: Components, pub const Components = struct { main_token: TokenIndex, type_expr: Node.Index, value_expr: Node.Index, align_expr: Node.Index, tuple_like: bool, }; pub fn firstToken(cf: ContainerField) TokenIndex { return cf.comptime_token orelse cf.ast.main_token; } pub fn convertToNonTupleLike(cf: *ContainerField, nodes: NodeList.Slice) void { if (!cf.ast.tuple_like) return; if (cf.ast.type_expr == 0) return; if (nodes.items(.tag)[cf.ast.type_expr] != .identifier) return; const ident = nodes.items(.main_token)[cf.ast.type_expr]; cf.ast.tuple_like = false; cf.ast.main_token = ident; cf.ast.type_expr = 0; } }; pub const FnProto = struct { visib_token: ?TokenIndex, extern_export_inline_token: ?TokenIndex, lib_name: ?TokenIndex, name_token: ?TokenIndex, lparen: TokenIndex, ast: Components, pub const Components = struct { proto_node: Node.Index, fn_token: TokenIndex, return_type: Node.Index, params: []const Node.Index, align_expr: Node.Index, addrspace_expr: Node.Index, section_expr: Node.Index, callconv_expr: Node.Index, }; pub const Param = struct { first_doc_comment: ?TokenIndex, name_token: ?TokenIndex, comptime_noalias: ?TokenIndex, anytype_ellipsis3: ?TokenIndex, type_expr: Node.Index, }; pub fn firstToken(fn_proto: FnProto) TokenIndex { return fn_proto.visib_token orelse fn_proto.extern_export_inline_token orelse fn_proto.ast.fn_token; } /// Abstracts over the fact that anytype and ... are not included /// in the params slice, since they are simple identifiers and /// not sub-expressions. pub const Iterator = struct { tree: *const Ast, fn_proto: *const FnProto, param_i: usize, tok_i: TokenIndex, tok_flag: bool, pub fn next(it: *Iterator) ?Param { const token_tags = it.tree.tokens.items(.tag); while (true) { var first_doc_comment: ?TokenIndex = null; var comptime_noalias: ?TokenIndex = null; var name_token: ?TokenIndex = null; if (!it.tok_flag) { if (it.param_i >= it.fn_proto.ast.params.len) { return null; } const param_type = it.fn_proto.ast.params[it.param_i]; var tok_i = it.tree.firstToken(param_type) - 1; while (true) : (tok_i -= 1) switch (token_tags[tok_i]) { .colon => continue, .identifier => name_token = tok_i, .doc_comment => first_doc_comment = tok_i, .keyword_comptime, .keyword_noalias => comptime_noalias = tok_i, else => break, }; it.param_i += 1; it.tok_i = it.tree.lastToken(param_type) + 1; // Look for anytype and ... params afterwards. if (token_tags[it.tok_i] == .comma) { it.tok_i += 1; } it.tok_flag = true; return Param{ .first_doc_comment = first_doc_comment, .comptime_noalias = comptime_noalias, .name_token = name_token, .anytype_ellipsis3 = null, .type_expr = param_type, }; } if (token_tags[it.tok_i] == .comma) { it.tok_i += 1; } if (token_tags[it.tok_i] == .r_paren) { return null; } if (token_tags[it.tok_i] == .doc_comment) { first_doc_comment = it.tok_i; while (token_tags[it.tok_i] == .doc_comment) { it.tok_i += 1; } } switch (token_tags[it.tok_i]) { .ellipsis3 => { it.tok_flag = false; // Next iteration should return null. return Param{ .first_doc_comment = first_doc_comment, .comptime_noalias = null, .name_token = null, .anytype_ellipsis3 = it.tok_i, .type_expr = 0, }; }, .keyword_noalias, .keyword_comptime => { comptime_noalias = it.tok_i; it.tok_i += 1; }, else => {}, } if (token_tags[it.tok_i] == .identifier and token_tags[it.tok_i + 1] == .colon) { name_token = it.tok_i; it.tok_i += 2; } if (token_tags[it.tok_i] == .keyword_anytype) { it.tok_i += 1; return Param{ .first_doc_comment = first_doc_comment, .comptime_noalias = comptime_noalias, .name_token = name_token, .anytype_ellipsis3 = it.tok_i - 1, .type_expr = 0, }; } it.tok_flag = false; } } }; pub fn iterate(fn_proto: *const FnProto, tree: *const Ast) Iterator { return .{ .tree = tree, .fn_proto = fn_proto, .param_i = 0, .tok_i = fn_proto.lparen + 1, .tok_flag = true, }; } }; pub const StructInit = struct { ast: Components, pub const Components = struct { lbrace: TokenIndex, fields: []const Node.Index, type_expr: Node.Index, }; }; pub const ArrayInit = struct { ast: Components, pub const Components = struct { lbrace: TokenIndex, elements: []const Node.Index, type_expr: Node.Index, }; }; pub const ArrayType = struct { ast: Components, pub const Components = struct { lbracket: TokenIndex, elem_count: Node.Index, sentinel: Node.Index, elem_type: Node.Index, }; }; pub const PtrType = struct { size: std.builtin.Type.Pointer.Size, allowzero_token: ?TokenIndex, const_token: ?TokenIndex, volatile_token: ?TokenIndex, ast: Components, pub const Components = struct { main_token: TokenIndex, align_node: Node.Index, addrspace_node: Node.Index, sentinel: Node.Index, bit_range_start: Node.Index, bit_range_end: Node.Index, child_type: Node.Index, }; }; pub const Slice = struct { ast: Components, pub const Components = struct { sliced: Node.Index, lbracket: TokenIndex, start: Node.Index, end: Node.Index, sentinel: Node.Index, }; }; pub const ContainerDecl = struct { layout_token: ?TokenIndex, ast: Components, pub const Components = struct { main_token: TokenIndex, /// Populated when main_token is Keyword_union. enum_token: ?TokenIndex, members: []const Node.Index, arg: Node.Index, }; }; pub const SwitchCase = struct { inline_token: ?TokenIndex, /// Points to the first token after the `|`. Will either be an identifier or /// a `*` (with an identifier immediately after it). payload_token: ?TokenIndex, ast: Components, pub const Components = struct { /// If empty, this is an else case values: []const Node.Index, arrow_token: TokenIndex, target_expr: Node.Index, }; }; pub const Asm = struct { ast: Components, volatile_token: ?TokenIndex, first_clobber: ?TokenIndex, outputs: []const Node.Index, inputs: []const Node.Index, pub const Components = struct { asm_token: TokenIndex, template: Node.Index, items: []const Node.Index, rparen: TokenIndex, }; }; pub const Call = struct { ast: Components, async_token: ?TokenIndex, pub const Components = struct { lparen: TokenIndex, fn_expr: Node.Index, params: []const Node.Index, }; }; }; pub const Error = struct { tag: Tag, is_note: bool = false, /// True if `token` points to the token before the token causing an issue. token_is_prev: bool = false, token: TokenIndex, extra: union { none: void, expected_tag: Token.Tag, } = .{ .none = {} }, pub const Tag = enum { asterisk_after_ptr_deref, chained_comparison_operators, decl_between_fields, expected_block, expected_block_or_assignment, expected_block_or_expr, expected_block_or_field, expected_container_members, expected_expr, expected_expr_or_assignment, expected_fn, expected_inlinable, expected_labelable, expected_param_list, expected_prefix_expr, expected_primary_type_expr, expected_pub_item, expected_return_type, expected_semi_or_else, expected_semi_or_lbrace, expected_statement, expected_suffix_op, expected_type_expr, expected_var_decl, expected_var_decl_or_fn, expected_loop_payload, expected_container, extern_fn_body, extra_addrspace_qualifier, extra_align_qualifier, extra_allowzero_qualifier, extra_const_qualifier, extra_volatile_qualifier, ptr_mod_on_array_child_type, invalid_bit_range, same_line_doc_comment, unattached_doc_comment, test_doc_comment, comptime_doc_comment, varargs_nonfinal, expected_continue_expr, expected_semi_after_decl, expected_semi_after_stmt, expected_comma_after_field, expected_comma_after_arg, expected_comma_after_param, expected_comma_after_initializer, expected_comma_after_switch_prong, expected_comma_after_for_operand, expected_comma_after_capture, expected_initializer, mismatched_binary_op_whitespace, invalid_ampersand_ampersand, c_style_container, expected_var_const, wrong_equal_var_decl, var_const_decl, extra_for_capture, for_input_not_captured, zig_style_container, previous_field, next_field, /// `expected_tag` is populated. expected_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); } /// Note: The FooComma/FooSemicolon variants exist to ease the implementation of /// Ast.lastToken() 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 or identifier), if any. /// rhs is the body node. test_decl, /// lhs is the index into extra_data. /// rhs is the initialization expression, if any. /// main_token is `var` or `const`. global_var_decl, /// `var a: x align(y) = rhs` /// lhs is the index into extra_data. /// main_token is `var` or `const`. local_var_decl, /// `var a: lhs = rhs`. lhs and rhs may be unused. /// Can be local or global. /// main_token is `var` or `const`. simple_var_decl, /// `var a align(lhs) = rhs`. lhs and rhs may be unused. /// Can be local or global. /// main_token is `var` or `const`. aligned_var_decl, /// lhs is the identifier token payload if any, /// rhs is the deferred expression. @"errdefer", /// lhs is unused. /// rhs is the deferred expression. @"defer", /// lhs catch rhs /// lhs catch |err| rhs /// main_token is the `catch` keyword. /// payload is determined by looking at the next token after the `catch` keyword. @"catch", /// `lhs.a`. main_token is the dot. rhs is the identifier token index. field_access, /// `lhs.?`. main_token is the dot. rhs is the `?` token index. unwrap_optional, /// `lhs == rhs`. main_token is op. equal_equal, /// `lhs != rhs`. main_token is op. bang_equal, /// `lhs < rhs`. main_token is op. less_than, /// `lhs > rhs`. main_token is op. greater_than, /// `lhs <= rhs`. main_token is op. less_or_equal, /// `lhs >= rhs`. main_token is op. greater_or_equal, /// `lhs *= rhs`. main_token is op. assign_mul, /// `lhs /= rhs`. main_token is op. assign_div, /// `lhs *= rhs`. main_token is op. assign_mod, /// `lhs += rhs`. main_token is op. assign_add, /// `lhs -= rhs`. main_token is op. assign_sub, /// `lhs <<= rhs`. main_token is op. assign_shl, /// `lhs <<|= rhs`. main_token is op. assign_shl_sat, /// `lhs >>= rhs`. main_token is op. assign_shr, /// `lhs &= rhs`. main_token is op. assign_bit_and, /// `lhs ^= rhs`. main_token is op. assign_bit_xor, /// `lhs |= rhs`. main_token is op. assign_bit_or, /// `lhs *%= rhs`. main_token is op. assign_mul_wrap, /// `lhs +%= rhs`. main_token is op. assign_add_wrap, /// `lhs -%= rhs`. main_token is op. assign_sub_wrap, /// `lhs *|= rhs`. main_token is op. assign_mul_sat, /// `lhs +|= rhs`. main_token is op. assign_add_sat, /// `lhs -|= rhs`. main_token is op. assign_sub_sat, /// `lhs = rhs`. main_token is op. assign, /// `lhs || rhs`. main_token is the `||`. merge_error_sets, /// `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 `**`. array_mult, /// `lhs *% rhs`. main_token is the `*%`. mul_wrap, /// `lhs *| rhs`. main_token is the `*|`. mul_sat, /// `lhs + rhs`. main_token is the `+`. add, /// `lhs - rhs`. main_token is the `-`. sub, /// `lhs ++ rhs`. main_token is the `++`. array_cat, /// `lhs +% rhs`. main_token is the `+%`. add_wrap, /// `lhs -% rhs`. main_token is the `-%`. sub_wrap, /// `lhs +| rhs`. main_token is the `+|`. add_sat, /// `lhs -| rhs`. main_token is the `-|`. sub_sat, /// `lhs << rhs`. main_token is the `<<`. shl, /// `lhs <<| rhs`. main_token is the `<<|`. shl_sat, /// `lhs >> rhs`. main_token is the `>>`. shr, /// `lhs & rhs`. main_token is the `&`. bit_and, /// `lhs ^ rhs`. main_token is the `^`. bit_xor, /// `lhs | rhs`. main_token is the `|`. bit_or, /// `lhs orelse rhs`. main_token is the `orelse`. @"orelse", /// `lhs and rhs`. main_token is the `and`. bool_and, /// `lhs or rhs`. main_token is the `or`. bool_or, /// `op lhs`. rhs unused. main_token is op. bool_not, /// `op lhs`. rhs unused. main_token is op. negation, /// `op lhs`. rhs unused. main_token is op. bit_not, /// `op lhs`. rhs unused. main_token is op. negation_wrap, /// `op lhs`. rhs unused. main_token is op. address_of, /// `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 `?`. optional_type, /// `[lhs]rhs`. array_type, /// `[lhs:a]b`. `ArrayTypeSentinel[rhs]`. array_type_sentinel, /// `[*]align(lhs) rhs`. lhs can be omitted. /// `*align(lhs) rhs`. lhs can be omitted. /// `[]rhs`. /// main_token is the asterisk if a pointer or the lbracket if a slice /// main_token might be a ** token, which is shared with a parent/child /// pointer type and may require special handling. ptr_type_aligned, /// `[*:lhs]rhs`. lhs can be omitted. /// `*rhs`. /// `[:lhs]rhs`. /// main_token is the asterisk if a pointer or the lbracket if a slice /// main_token might be a ** token, which is shared with a parent/child /// pointer type and may require special handling. ptr_type_sentinel, /// lhs is index into ptr_type. rhs is the element type expression. /// main_token is the asterisk if a pointer or the lbracket if a slice /// main_token might be a ** token, which is shared with a parent/child /// pointer type and may require special handling. ptr_type, /// lhs is index into ptr_type_bit_range. rhs is the element type expression. /// main_token is the asterisk if a pointer or the lbracket if a slice /// main_token might be a ** token, which is shared with a parent/child /// pointer type and may require special handling. ptr_type_bit_range, /// `lhs[rhs..]` /// main_token is the lbracket. slice_open, /// `lhs[b..c]`. rhs is index into Slice /// main_token is the lbracket. slice, /// `lhs[b..c :d]`. rhs is index into SliceSentinel /// main_token is the lbracket. slice_sentinel, /// `lhs.*`. rhs is unused. deref, /// `lhs[rhs]`. array_access, /// `lhs{rhs}`. rhs can be omitted. array_init_one, /// `lhs{rhs,}`. rhs can *not* be omitted array_init_one_comma, /// `.{lhs, rhs}`. lhs and rhs can be omitted. array_init_dot_two, /// Same as `array_init_dot_two` except there is known to be a trailing comma /// before the final rbrace. array_init_dot_two_comma, /// `.{a, b}`. `sub_list[lhs..rhs]`. array_init_dot, /// Same as `array_init_dot` except there is known to be a trailing comma /// before the final rbrace. array_init_dot_comma, /// `lhs{a, b}`. `sub_range_list[rhs]`. lhs can be omitted which means `.{a, b}`. array_init, /// Same as `array_init` except there is known to be a trailing comma /// before the final rbrace. array_init_comma, /// `lhs{.a = rhs}`. rhs can be omitted making it empty. /// main_token is the lbrace. struct_init_one, /// `lhs{.a = rhs,}`. rhs can *not* be omitted. /// main_token is the lbrace. struct_init_one_comma, /// `.{.a = lhs, .b = rhs}`. lhs and rhs can be omitted. /// main_token is the lbrace. /// No trailing comma before the rbrace. struct_init_dot_two, /// Same as `struct_init_dot_two` except there is known to be a trailing comma /// before the final rbrace. struct_init_dot_two_comma, /// `.{.a = b, .c = d}`. `sub_list[lhs..rhs]`. /// main_token is the lbrace. struct_init_dot, /// Same as `struct_init_dot` except there is known to be a trailing comma /// before the final rbrace. struct_init_dot_comma, /// `lhs{.a = b, .c = d}`. `sub_range_list[rhs]`. /// lhs can be omitted which means `.{.a = b, .c = d}`. /// main_token is the lbrace. struct_init, /// Same as `struct_init` except there is known to be a trailing comma /// before the final rbrace. struct_init_comma, /// `lhs(rhs)`. rhs can be omitted. /// main_token is the lparen. call_one, /// `lhs(rhs,)`. rhs can be omitted. /// main_token is the lparen. call_one_comma, /// `async lhs(rhs)`. rhs can be omitted. async_call_one, /// `async lhs(rhs,)`. async_call_one_comma, /// `lhs(a, b, c)`. `SubRange[rhs]`. /// main_token is the `(`. call, /// `lhs(a, b, c,)`. `SubRange[rhs]`. /// main_token is the `(`. call_comma, /// `async lhs(a, b, c)`. `SubRange[rhs]`. /// main_token is the `(`. async_call, /// `async lhs(a, b, c,)`. `SubRange[rhs]`. /// main_token is the `(`. async_call_comma, /// `switch(lhs) {}`. `SubRange[rhs]`. @"switch", /// Same as switch except there is known to be a trailing comma /// before the final rbrace switch_comma, /// `lhs => rhs`. If lhs is omitted it means `else`. /// main_token is the `=>` switch_case_one, /// Same ast `switch_case_one` but the case is inline switch_case_inline_one, /// `a, b, c => rhs`. `SubRange[lhs]`. /// main_token is the `=>` switch_case, /// Same ast `switch_case` but the case is inline switch_case_inline, /// `lhs...rhs`. switch_range, /// `while (lhs) rhs`. /// `while (lhs) |x| rhs`. while_simple, /// `while (lhs) : (a) b`. `WhileCont[rhs]`. /// `while (lhs) : (a) b`. `WhileCont[rhs]`. while_cont, /// `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`. for_simple, /// `for (lhs[0..inputs]) lhs[inputs + 1] else lhs[inputs + 2]`. `For[rhs]`. @"for", /// `lhs..rhs`. for_range, /// `if (lhs) rhs`. /// `if (lhs) |a| rhs`. if_simple, /// `if (lhs) a else b`. `If[rhs]`. /// `if (lhs) |x| a else b`. `If[rhs]`. /// `if (lhs) |x| a else |y| b`. `If[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 :lhs rhs` /// both lhs and rhs may be omitted. @"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. /// main_token is the `fn` keyword. /// extern function declarations use this tag. fn_proto_simple, /// `fn(a: b, c: d) rhs`. `sub_range_list[lhs]`. /// anytype and ... parameters are omitted from the AST tree. /// main_token is the `fn` keyword. /// extern function declarations use this tag. fn_proto_multi, /// `fn(a: b) rhs addrspace(e) linksection(f) callconv(g)`. `FnProtoOne[lhs]`. /// zero or one parameters. /// anytype and ... parameters are omitted from the AST tree. /// main_token is the `fn` keyword. /// extern function declarations use this tag. fn_proto_one, /// `fn(a: b, c: d) rhs addrspace(e) linksection(f) callconv(g)`. `FnProto[lhs]`. /// anytype and ... parameters are omitted from the AST tree. /// main_token is the `fn` keyword. /// extern function declarations use this tag. fn_proto, /// lhs is the fn_proto. /// rhs is the function body block. /// Note that extern function declarations use the fn_proto tags rather /// than this one. fn_decl, /// `anyframe->rhs`. main_token is `anyframe`. `lhs` is arrow token index. anyframe_type, /// Both lhs and rhs unused. anyframe_literal, /// Both lhs and rhs unused. char_literal, /// Both lhs and rhs unused. number_literal, /// Both lhs and rhs unused. unreachable_literal, /// 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. enum_literal, /// main_token is the string literal token /// Both lhs and rhs unused. string_literal, /// 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 multiline_string_literal_line tokens, or a single /// string_literal token. multiline_string_literal, /// `(lhs)`. main_token is the `(`; rhs is the token index of the `)`. grouped_expression, /// `@a(lhs, rhs)`. lhs and rhs may be omitted. /// main_token is the builtin token. builtin_call_two, /// Same as builtin_call_two but there is known to be a trailing comma before the rparen. builtin_call_two_comma, /// `@a(b, c)`. `sub_list[lhs..rhs]`. /// main_token is the builtin token. builtin_call, /// Same as builtin_call but there is known to be a trailing comma before the rparen. builtin_call_comma, /// `error{a, b}`. /// rhs is the rbrace, lhs is unused. error_set_decl, /// `struct {}`, `union {}`, `opaque {}`, `enum {}`. `extra_data[lhs..rhs]`. /// main_token is `struct`, `union`, `opaque`, `enum` keyword. container_decl, /// Same as ContainerDecl but there is known to be a trailing comma /// or semicolon before the rbrace. container_decl_trailing, /// `struct {lhs, rhs}`, `union {lhs, rhs}`, `opaque {lhs, rhs}`, `enum {lhs, rhs}`. /// lhs or rhs can be omitted. /// main_token is `struct`, `union`, `opaque`, `enum` keyword. container_decl_two, /// Same as ContainerDeclTwo except there is known to be a trailing comma /// or semicolon before the rbrace. container_decl_two_trailing, /// `struct(lhs)` / `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`. container_decl_arg, /// Same as container_decl_arg but there is known to be a trailing /// comma or semicolon before the rbrace. container_decl_arg_trailing, /// `union(enum) {}`. `sub_list[lhs..rhs]`. /// Note that tagged unions with explicitly provided enums are represented /// by `container_decl_arg`. tagged_union, /// Same as tagged_union but there is known to be a trailing comma /// or semicolon before the rbrace. tagged_union_trailing, /// `union(enum) {lhs, rhs}`. lhs or rhs may be omitted. /// Note that tagged unions with explicitly provided enums are represented /// by `container_decl_arg`. tagged_union_two, /// Same as tagged_union_two but there is known to be a trailing comma /// or semicolon before the rbrace. tagged_union_two_trailing, /// `union(enum(lhs)) {}`. `SubRange[rhs]`. tagged_union_enum_tag, /// Same as tagged_union_enum_tag but there is known to be a trailing comma /// or semicolon before the rbrace. tagged_union_enum_tag_trailing, /// `a: lhs = rhs,`. lhs and rhs can be omitted. /// main_token is the field name identifier. /// lastToken() does not include the possible trailing comma. container_field_init, /// `a: lhs align(rhs),`. rhs can be omitted. /// main_token is the field name identifier. /// lastToken() does not include the possible trailing comma. container_field_align, /// `a: lhs align(c) = d,`. `container_field_list[rhs]`. /// main_token is the field name identifier. /// lastToken() does not include the possible trailing comma. container_field, /// `comptime lhs`. rhs unused. @"comptime", /// `nosuspend lhs`. rhs unused. @"nosuspend", /// `{lhs rhs}`. rhs or lhs can be omitted. /// main_token points at the lbrace. block_two, /// Same as block_two but there is known to be a semicolon before the rbrace. block_two_semicolon, /// `{}`. `sub_list[lhs..rhs]`. /// main_token points at the lbrace. block, /// Same as block but there is known to be a semicolon before the rbrace. block_semicolon, /// `asm(lhs)`. rhs is the token index of the rparen. asm_simple, /// `asm(lhs, a)`. `Asm[rhs]`. @"asm", /// `[a] "b" (c)`. lhs is 0, rhs is token index of the rparen. /// `[a] "b" (-> lhs)`. rhs is token index of the rparen. /// main_token is `a`. asm_output, /// `[a] "b" (lhs)`. rhs is token index of the rparen. /// main_token is `a`. asm_input, /// `error.a`. lhs is token index of `.`. rhs is token index of `a`. error_value, /// `lhs!rhs`. main_token is the `!`. error_union, pub fn isContainerField(tag: Tag) bool { return switch (tag) { .container_field_init, .container_field_align, .container_field, => true, else => false, }; } }; 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, addrspace_node: Index, }; pub const PtrTypeBitRange = struct { sentinel: Index, align_node: Index, addrspace_node: Index, bit_range_start: Index, bit_range_end: 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 { /// Populated if there is an explicit type ascription. type_node: Index, /// Populated if align(A) is present. align_node: Index, /// Populated if addrspace(A) is present. addrspace_node: Index, /// Populated if linksection(A) is present. section_node: Index, }; pub const Slice = struct { start: Index, end: Index, }; pub const SliceSentinel = struct { start: Index, /// May be 0 if the slice is "open" end: Index, sentinel: Index, }; pub const While = struct { cont_expr: Index, then_expr: Index, else_expr: Index, }; pub const WhileCont = struct { cont_expr: Index, then_expr: Index, }; pub const For = packed struct(u32) { inputs: u31, has_else: bool, }; 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 addrspace(A) is present. addrspace_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 addrspace(A) is present. addrspace_expr: Index, /// Populated if linksection(A) is present. section_expr: Index, /// Populated if callconv(A) is present. callconv_expr: Index, }; pub const Asm = struct { items_start: Index, items_end: Index, /// Needed to make lastToken() work. rparen: TokenIndex, }; }; const std = @import("../std.zig"); const assert = std.debug.assert; const testing = std.testing; const mem = std.mem; const Token = std.zig.Token; const Ast = @This(); const Allocator = std.mem.Allocator; const Parse = @import("Parse.zig"); test { testing.refAllDecls(@This()); } |
Generated by zstd-live on 2025-08-13 02:35:14 UTC. |