mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
1315 lines
45 KiB
Zig
Vendored
1315 lines
45 KiB
Zig
Vendored
const std = @import("std");
|
|
const Type = @import("Type.zig");
|
|
const Tokenizer = @import("Tokenizer.zig");
|
|
const Compilation = @import("Compilation.zig");
|
|
const Source = @import("Source.zig");
|
|
const Attribute = @import("Attribute.zig");
|
|
const Value = @import("Value.zig");
|
|
const StringInterner = @import("StringInterner.zig");
|
|
|
|
const Tree = @This();
|
|
|
|
pub const Token = struct {
|
|
id: Id,
|
|
flags: packed struct {
|
|
expansion_disabled: bool = false,
|
|
is_macro_arg: bool = false,
|
|
} = .{},
|
|
/// This location contains the actual token slice which might be generated.
|
|
/// If it is generated then there is guaranteed to be at least one
|
|
/// expansion location.
|
|
loc: Source.Location,
|
|
expansion_locs: ?[*]Source.Location = null,
|
|
|
|
pub fn expansionSlice(tok: Token) []const Source.Location {
|
|
const locs = tok.expansion_locs orelse return &[0]Source.Location{};
|
|
var i: usize = 0;
|
|
while (locs[i].id != .unused) : (i += 1) {}
|
|
return locs[0..i];
|
|
}
|
|
|
|
pub fn addExpansionLocation(tok: *Token, gpa: std.mem.Allocator, new: []const Source.Location) !void {
|
|
if (new.len == 0 or tok.id == .whitespace) return;
|
|
var list = std.ArrayList(Source.Location).init(gpa);
|
|
defer {
|
|
@memset(list.items.ptr[list.items.len..list.capacity], .{});
|
|
// Add a sentinel to indicate the end of the list since
|
|
// the ArrayList's capacity isn't guaranteed to be exactly
|
|
// what we ask for.
|
|
if (list.capacity > 0) {
|
|
list.items.ptr[list.capacity - 1].byte_offset = 1;
|
|
}
|
|
tok.expansion_locs = list.items.ptr;
|
|
}
|
|
|
|
if (tok.expansion_locs) |locs| {
|
|
var i: usize = 0;
|
|
while (locs[i].id != .unused) : (i += 1) {}
|
|
list.items = locs[0..i];
|
|
while (locs[i].byte_offset != 1) : (i += 1) {}
|
|
list.capacity = i + 1;
|
|
}
|
|
|
|
const min_len = @max(list.items.len + new.len + 1, 4);
|
|
const wanted_len = std.math.ceilPowerOfTwo(usize, min_len) catch
|
|
return error.OutOfMemory;
|
|
try list.ensureTotalCapacity(wanted_len);
|
|
|
|
for (new) |new_loc| {
|
|
if (new_loc.id == .generated) continue;
|
|
list.appendAssumeCapacity(new_loc);
|
|
}
|
|
}
|
|
|
|
pub fn free(expansion_locs: ?[*]Source.Location, gpa: std.mem.Allocator) void {
|
|
const locs = expansion_locs orelse return;
|
|
var i: usize = 0;
|
|
while (locs[i].id != .unused) : (i += 1) {}
|
|
while (locs[i].byte_offset != 1) : (i += 1) {}
|
|
gpa.free(locs[0 .. i + 1]);
|
|
}
|
|
|
|
pub fn dupe(tok: Token, gpa: std.mem.Allocator) !Token {
|
|
var copy = tok;
|
|
copy.expansion_locs = null;
|
|
try copy.addExpansionLocation(gpa, tok.expansionSlice());
|
|
return copy;
|
|
}
|
|
|
|
pub fn checkMsEof(tok: Token, source: Source, comp: *Compilation) !void {
|
|
std.debug.assert(tok.id == .eof);
|
|
if (source.buf.len > tok.loc.byte_offset and source.buf[tok.loc.byte_offset] == 0x1A) {
|
|
try comp.diag.add(.{
|
|
.tag = .ctrl_z_eof,
|
|
.loc = .{
|
|
.id = source.id,
|
|
.byte_offset = tok.loc.byte_offset,
|
|
.line = tok.loc.line,
|
|
},
|
|
}, &.{});
|
|
}
|
|
}
|
|
|
|
pub const List = std.MultiArrayList(Token);
|
|
pub const Id = Tokenizer.Token.Id;
|
|
};
|
|
|
|
pub const TokenIndex = u32;
|
|
pub const NodeIndex = enum(u32) { none, _ };
|
|
pub const ValueMap = std.AutoHashMap(NodeIndex, Value);
|
|
|
|
comp: *Compilation,
|
|
arena: std.heap.ArenaAllocator,
|
|
generated: []const u8,
|
|
tokens: Token.List.Slice,
|
|
nodes: Node.List.Slice,
|
|
data: []const NodeIndex,
|
|
root_decls: []const NodeIndex,
|
|
strings: []const u8,
|
|
value_map: ValueMap,
|
|
|
|
pub fn deinit(tree: *Tree) void {
|
|
tree.comp.gpa.free(tree.root_decls);
|
|
tree.comp.gpa.free(tree.data);
|
|
tree.comp.gpa.free(tree.strings);
|
|
tree.nodes.deinit(tree.comp.gpa);
|
|
tree.arena.deinit();
|
|
tree.value_map.deinit();
|
|
}
|
|
|
|
pub const GNUAssemblyQualifiers = struct {
|
|
@"volatile": bool = false,
|
|
@"inline": bool = false,
|
|
goto: bool = false,
|
|
};
|
|
|
|
pub const Node = struct {
|
|
tag: Tag,
|
|
ty: Type = .{ .specifier = .void },
|
|
data: Data,
|
|
|
|
pub const Range = struct { start: u32, end: u32 };
|
|
|
|
pub const Data = union {
|
|
decl: struct {
|
|
name: TokenIndex,
|
|
node: NodeIndex = .none,
|
|
},
|
|
decl_ref: TokenIndex,
|
|
range: Range,
|
|
if3: struct {
|
|
cond: NodeIndex,
|
|
body: u32,
|
|
},
|
|
un: NodeIndex,
|
|
bin: struct {
|
|
lhs: NodeIndex,
|
|
rhs: NodeIndex,
|
|
},
|
|
member: struct {
|
|
lhs: NodeIndex,
|
|
index: u32,
|
|
},
|
|
union_init: struct {
|
|
field_index: u32,
|
|
node: NodeIndex,
|
|
},
|
|
cast: struct {
|
|
operand: NodeIndex,
|
|
kind: CastKind,
|
|
},
|
|
int: u64,
|
|
return_zero: bool,
|
|
|
|
pub fn forDecl(data: Data, tree: Tree) struct {
|
|
decls: []const NodeIndex,
|
|
cond: NodeIndex,
|
|
incr: NodeIndex,
|
|
body: NodeIndex,
|
|
} {
|
|
const items = tree.data[data.range.start..data.range.end];
|
|
const decls = items[0 .. items.len - 3];
|
|
|
|
return .{
|
|
.decls = decls,
|
|
.cond = items[items.len - 3],
|
|
.incr = items[items.len - 2],
|
|
.body = items[items.len - 1],
|
|
};
|
|
}
|
|
|
|
pub fn forStmt(data: Data, tree: Tree) struct {
|
|
init: NodeIndex,
|
|
cond: NodeIndex,
|
|
incr: NodeIndex,
|
|
body: NodeIndex,
|
|
} {
|
|
const items = tree.data[data.if3.body..];
|
|
|
|
return .{
|
|
.init = items[0],
|
|
.cond = items[1],
|
|
.incr = items[2],
|
|
.body = data.if3.cond,
|
|
};
|
|
}
|
|
};
|
|
|
|
pub const List = std.MultiArrayList(Node);
|
|
};
|
|
|
|
pub const CastKind = enum(u8) {
|
|
/// Does nothing except possibly add qualifiers
|
|
no_op,
|
|
/// Interpret one bit pattern as another. Used for operands which have the same
|
|
/// size and unrelated types, e.g. casting one pointer type to another
|
|
bitcast,
|
|
/// Convert T[] to T *
|
|
array_to_pointer,
|
|
/// Converts an lvalue to an rvalue
|
|
lval_to_rval,
|
|
/// Convert a function type to a pointer to a function
|
|
function_to_pointer,
|
|
/// Convert a pointer type to a _Bool
|
|
pointer_to_bool,
|
|
/// Convert a pointer type to an integer type
|
|
pointer_to_int,
|
|
/// Convert _Bool to an integer type
|
|
bool_to_int,
|
|
/// Convert _Bool to a floating type
|
|
bool_to_float,
|
|
/// Convert a _Bool to a pointer; will cause a warning
|
|
bool_to_pointer,
|
|
/// Convert an integer type to _Bool
|
|
int_to_bool,
|
|
/// Convert an integer to a floating type
|
|
int_to_float,
|
|
/// Convert a complex integer to a complex floating type
|
|
complex_int_to_complex_float,
|
|
/// Convert an integer type to a pointer type
|
|
int_to_pointer,
|
|
/// Convert a floating type to a _Bool
|
|
float_to_bool,
|
|
/// Convert a floating type to an integer
|
|
float_to_int,
|
|
/// Convert a complex floating type to a complex integer
|
|
complex_float_to_complex_int,
|
|
/// Convert one integer type to another
|
|
int_cast,
|
|
/// Convert one complex integer type to another
|
|
complex_int_cast,
|
|
/// Convert real part of complex integer to a integer
|
|
complex_int_to_real,
|
|
/// Create a complex integer type using operand as the real part
|
|
real_to_complex_int,
|
|
/// Convert one floating type to another
|
|
float_cast,
|
|
/// Convert one complex floating type to another
|
|
complex_float_cast,
|
|
/// Convert real part of complex float to a float
|
|
complex_float_to_real,
|
|
/// Create a complex floating type using operand as the real part
|
|
real_to_complex_float,
|
|
/// Convert type to void
|
|
to_void,
|
|
/// Convert a literal 0 to a null pointer
|
|
null_to_pointer,
|
|
/// GNU cast-to-union extension
|
|
union_cast,
|
|
/// Create vector where each value is same as the input scalar.
|
|
vector_splat,
|
|
};
|
|
|
|
pub const Tag = enum(u8) {
|
|
/// Must appear at index 0. Also used as the tag for __builtin_types_compatible_p arguments, since the arguments are types
|
|
/// Reaching it is always the result of a bug.
|
|
invalid,
|
|
|
|
// ====== Decl ======
|
|
|
|
// _Static_assert
|
|
static_assert,
|
|
|
|
// function prototype
|
|
fn_proto,
|
|
static_fn_proto,
|
|
inline_fn_proto,
|
|
inline_static_fn_proto,
|
|
|
|
// function definition
|
|
fn_def,
|
|
static_fn_def,
|
|
inline_fn_def,
|
|
inline_static_fn_def,
|
|
|
|
// variable declaration
|
|
@"var",
|
|
extern_var,
|
|
static_var,
|
|
// same as static_var, used for __func__, __FUNCTION__ and __PRETTY_FUNCTION__
|
|
implicit_static_var,
|
|
threadlocal_var,
|
|
threadlocal_extern_var,
|
|
threadlocal_static_var,
|
|
|
|
/// __asm__("...") at file scope
|
|
file_scope_asm,
|
|
|
|
// typedef declaration
|
|
typedef,
|
|
|
|
// container declarations
|
|
/// { lhs; rhs; }
|
|
struct_decl_two,
|
|
/// { lhs; rhs; }
|
|
union_decl_two,
|
|
/// { lhs, rhs, }
|
|
enum_decl_two,
|
|
/// { range }
|
|
struct_decl,
|
|
/// { range }
|
|
union_decl,
|
|
/// { range }
|
|
enum_decl,
|
|
/// struct decl_ref;
|
|
struct_forward_decl,
|
|
/// union decl_ref;
|
|
union_forward_decl,
|
|
/// enum decl_ref;
|
|
enum_forward_decl,
|
|
|
|
/// name = node
|
|
enum_field_decl,
|
|
/// ty name : node
|
|
/// name == 0 means unnamed
|
|
record_field_decl,
|
|
/// Used when a record has an unnamed record as a field
|
|
indirect_record_field_decl,
|
|
|
|
// ====== Stmt ======
|
|
|
|
labeled_stmt,
|
|
/// { first; second; } first and second may be null
|
|
compound_stmt_two,
|
|
/// { data }
|
|
compound_stmt,
|
|
/// if (first) data[second] else data[second+1];
|
|
if_then_else_stmt,
|
|
/// if (first) second; second may be null
|
|
if_then_stmt,
|
|
/// switch (first) second
|
|
switch_stmt,
|
|
/// case first: second
|
|
case_stmt,
|
|
/// case data[body]...data[body+1]: cond
|
|
case_range_stmt,
|
|
/// default: first
|
|
default_stmt,
|
|
/// while (first) second
|
|
while_stmt,
|
|
/// do second while(first);
|
|
do_while_stmt,
|
|
/// for (data[..]; data[len-3]; data[len-2]) data[len-1]
|
|
for_decl_stmt,
|
|
/// for (;;;) first
|
|
forever_stmt,
|
|
/// for (data[first]; data[first+1]; data[first+2]) second
|
|
for_stmt,
|
|
/// goto first;
|
|
goto_stmt,
|
|
/// goto *un;
|
|
computed_goto_stmt,
|
|
// continue; first and second unused
|
|
continue_stmt,
|
|
// break; first and second unused
|
|
break_stmt,
|
|
// null statement (just a semicolon); first and second unused
|
|
null_stmt,
|
|
/// return first; first may be null
|
|
return_stmt,
|
|
/// Assembly statement of the form __asm__("string literal")
|
|
gnu_asm_simple,
|
|
|
|
// ====== Expr ======
|
|
|
|
/// lhs , rhs
|
|
comma_expr,
|
|
/// lhs ? data[0] : data[1]
|
|
binary_cond_expr,
|
|
/// Used as the base for casts of the lhs in `binary_cond_expr`.
|
|
cond_dummy_expr,
|
|
/// lhs ? data[0] : data[1]
|
|
cond_expr,
|
|
/// lhs = rhs
|
|
assign_expr,
|
|
/// lhs *= rhs
|
|
mul_assign_expr,
|
|
/// lhs /= rhs
|
|
div_assign_expr,
|
|
/// lhs %= rhs
|
|
mod_assign_expr,
|
|
/// lhs += rhs
|
|
add_assign_expr,
|
|
/// lhs -= rhs
|
|
sub_assign_expr,
|
|
/// lhs <<= rhs
|
|
shl_assign_expr,
|
|
/// lhs >>= rhs
|
|
shr_assign_expr,
|
|
/// lhs &= rhs
|
|
bit_and_assign_expr,
|
|
/// lhs ^= rhs
|
|
bit_xor_assign_expr,
|
|
/// lhs |= rhs
|
|
bit_or_assign_expr,
|
|
/// lhs || rhs
|
|
bool_or_expr,
|
|
/// lhs && rhs
|
|
bool_and_expr,
|
|
/// lhs | rhs
|
|
bit_or_expr,
|
|
/// lhs ^ rhs
|
|
bit_xor_expr,
|
|
/// lhs & rhs
|
|
bit_and_expr,
|
|
/// lhs == rhs
|
|
equal_expr,
|
|
/// lhs != rhs
|
|
not_equal_expr,
|
|
/// lhs < rhs
|
|
less_than_expr,
|
|
/// lhs <= rhs
|
|
less_than_equal_expr,
|
|
/// lhs > rhs
|
|
greater_than_expr,
|
|
/// lhs >= rhs
|
|
greater_than_equal_expr,
|
|
/// lhs << rhs
|
|
shl_expr,
|
|
/// lhs >> rhs
|
|
shr_expr,
|
|
/// lhs + rhs
|
|
add_expr,
|
|
/// lhs - rhs
|
|
sub_expr,
|
|
/// lhs * rhs
|
|
mul_expr,
|
|
/// lhs / rhs
|
|
div_expr,
|
|
/// lhs % rhs
|
|
mod_expr,
|
|
/// Explicit: (type) cast
|
|
explicit_cast,
|
|
/// Implicit: cast
|
|
implicit_cast,
|
|
/// &un
|
|
addr_of_expr,
|
|
/// &&decl_ref
|
|
addr_of_label,
|
|
/// *un
|
|
deref_expr,
|
|
/// +un
|
|
plus_expr,
|
|
/// -un
|
|
negate_expr,
|
|
/// ~un
|
|
bit_not_expr,
|
|
/// !un
|
|
bool_not_expr,
|
|
/// ++un
|
|
pre_inc_expr,
|
|
/// --un
|
|
pre_dec_expr,
|
|
/// __imag un
|
|
imag_expr,
|
|
/// __real un
|
|
real_expr,
|
|
/// lhs[rhs] lhs is pointer/array type, rhs is integer type
|
|
array_access_expr,
|
|
/// first(second) second may be 0
|
|
call_expr_one,
|
|
/// data[0](data[1..])
|
|
call_expr,
|
|
/// decl
|
|
builtin_call_expr_one,
|
|
builtin_call_expr,
|
|
/// lhs.member
|
|
member_access_expr,
|
|
/// lhs->member
|
|
member_access_ptr_expr,
|
|
/// un++
|
|
post_inc_expr,
|
|
/// un--
|
|
post_dec_expr,
|
|
/// (un)
|
|
paren_expr,
|
|
/// decl_ref
|
|
decl_ref_expr,
|
|
/// decl_ref
|
|
enumeration_ref,
|
|
/// C23 bool literal `true` / `false`
|
|
bool_literal,
|
|
/// C23 nullptr literal
|
|
nullptr_literal,
|
|
/// integer literal, always unsigned
|
|
int_literal,
|
|
/// Same as int_literal, but originates from a char literal
|
|
char_literal,
|
|
/// _Float16 literal
|
|
float16_literal,
|
|
/// f32 literal
|
|
float_literal,
|
|
/// f64 literal
|
|
double_literal,
|
|
/// wraps a float or double literal: un
|
|
imaginary_literal,
|
|
/// tree.str[index..][0..len]
|
|
string_literal_expr,
|
|
/// sizeof(un?)
|
|
sizeof_expr,
|
|
/// _Alignof(un?)
|
|
alignof_expr,
|
|
/// _Generic(controlling lhs, chosen rhs)
|
|
generic_expr_one,
|
|
/// _Generic(controlling range[0], chosen range[1], rest range[2..])
|
|
generic_expr,
|
|
/// ty: un
|
|
generic_association_expr,
|
|
// default: un
|
|
generic_default_expr,
|
|
/// __builtin_choose_expr(lhs, data[0], data[1])
|
|
builtin_choose_expr,
|
|
/// __builtin_types_compatible_p(lhs, rhs)
|
|
builtin_types_compatible_p,
|
|
/// decl - special builtins require custom parsing
|
|
special_builtin_call_one,
|
|
/// ({ un })
|
|
stmt_expr,
|
|
|
|
// ====== Initializer expressions ======
|
|
|
|
/// { lhs, rhs }
|
|
array_init_expr_two,
|
|
/// { range }
|
|
array_init_expr,
|
|
/// { lhs, rhs }
|
|
struct_init_expr_two,
|
|
/// { range }
|
|
struct_init_expr,
|
|
/// { union_init }
|
|
union_init_expr,
|
|
/// (ty){ un }
|
|
compound_literal_expr,
|
|
|
|
/// Inserted at the end of a function body if no return stmt is found.
|
|
/// ty is the functions return type
|
|
/// data is return_zero which is true if the function is called "main" and ty is compatible with int
|
|
implicit_return,
|
|
|
|
/// Inserted in array_init_expr to represent unspecified elements.
|
|
/// data.int contains the amount of elements.
|
|
array_filler_expr,
|
|
/// Inserted in record and scalar initializers for unspecified elements.
|
|
default_init_expr,
|
|
|
|
pub fn isImplicit(tag: Tag) bool {
|
|
return switch (tag) {
|
|
.implicit_cast,
|
|
.implicit_return,
|
|
.array_filler_expr,
|
|
.default_init_expr,
|
|
.implicit_static_var,
|
|
.cond_dummy_expr,
|
|
=> true,
|
|
else => false,
|
|
};
|
|
}
|
|
};
|
|
|
|
pub fn isBitfield(nodes: Node.List.Slice, node: NodeIndex) bool {
|
|
return bitfieldWidth(nodes, node, false) != null;
|
|
}
|
|
|
|
/// Returns null if node is not a bitfield. If inspect_lval is true, this function will
|
|
/// recurse into implicit lval_to_rval casts (useful for arithmetic conversions)
|
|
pub fn bitfieldWidth(nodes: Node.List.Slice, node: NodeIndex, inspect_lval: bool) ?u32 {
|
|
if (node == .none) return null;
|
|
switch (nodes.items(.tag)[@intFromEnum(node)]) {
|
|
.member_access_expr, .member_access_ptr_expr => {
|
|
const member = nodes.items(.data)[@intFromEnum(node)].member;
|
|
var ty = nodes.items(.ty)[@intFromEnum(member.lhs)];
|
|
if (ty.isPtr()) ty = ty.elemType();
|
|
const record_ty = ty.get(.@"struct") orelse ty.get(.@"union") orelse return null;
|
|
const field = record_ty.data.record.fields[member.index];
|
|
return field.bit_width;
|
|
},
|
|
.implicit_cast => {
|
|
if (!inspect_lval) return null;
|
|
|
|
const data = nodes.items(.data)[@intFromEnum(node)];
|
|
return switch (data.cast.kind) {
|
|
.lval_to_rval => bitfieldWidth(nodes, data.cast.operand, false),
|
|
else => null,
|
|
};
|
|
},
|
|
else => return null,
|
|
}
|
|
}
|
|
|
|
pub fn isLval(nodes: Node.List.Slice, extra: []const NodeIndex, value_map: ValueMap, node: NodeIndex) bool {
|
|
var is_const: bool = undefined;
|
|
return isLvalExtra(nodes, extra, value_map, node, &is_const);
|
|
}
|
|
|
|
pub fn isLvalExtra(nodes: Node.List.Slice, extra: []const NodeIndex, value_map: ValueMap, node: NodeIndex, is_const: *bool) bool {
|
|
is_const.* = false;
|
|
switch (nodes.items(.tag)[@intFromEnum(node)]) {
|
|
.compound_literal_expr => {
|
|
is_const.* = nodes.items(.ty)[@intFromEnum(node)].isConst();
|
|
return true;
|
|
},
|
|
.string_literal_expr => return true,
|
|
.member_access_ptr_expr => {
|
|
const lhs_expr = nodes.items(.data)[@intFromEnum(node)].member.lhs;
|
|
const ptr_ty = nodes.items(.ty)[@intFromEnum(lhs_expr)];
|
|
if (ptr_ty.isPtr()) is_const.* = ptr_ty.elemType().isConst();
|
|
return true;
|
|
},
|
|
.array_access_expr => {
|
|
const lhs_expr = nodes.items(.data)[@intFromEnum(node)].bin.lhs;
|
|
if (lhs_expr != .none) {
|
|
const array_ty = nodes.items(.ty)[@intFromEnum(lhs_expr)];
|
|
if (array_ty.isPtr() or array_ty.isArray()) is_const.* = array_ty.elemType().isConst();
|
|
}
|
|
return true;
|
|
},
|
|
.decl_ref_expr => {
|
|
const decl_ty = nodes.items(.ty)[@intFromEnum(node)];
|
|
is_const.* = decl_ty.isConst();
|
|
return true;
|
|
},
|
|
.deref_expr => {
|
|
const data = nodes.items(.data)[@intFromEnum(node)];
|
|
const operand_ty = nodes.items(.ty)[@intFromEnum(data.un)];
|
|
if (operand_ty.isFunc()) return false;
|
|
if (operand_ty.isPtr() or operand_ty.isArray()) is_const.* = operand_ty.elemType().isConst();
|
|
return true;
|
|
},
|
|
.member_access_expr => {
|
|
const data = nodes.items(.data)[@intFromEnum(node)];
|
|
return isLvalExtra(nodes, extra, value_map, data.member.lhs, is_const);
|
|
},
|
|
.paren_expr => {
|
|
const data = nodes.items(.data)[@intFromEnum(node)];
|
|
return isLvalExtra(nodes, extra, value_map, data.un, is_const);
|
|
},
|
|
.builtin_choose_expr => {
|
|
const data = nodes.items(.data)[@intFromEnum(node)];
|
|
|
|
if (value_map.get(data.if3.cond)) |val| {
|
|
const offset = @intFromBool(val.isZero());
|
|
return isLvalExtra(nodes, extra, value_map, extra[data.if3.body + offset], is_const);
|
|
}
|
|
return false;
|
|
},
|
|
else => return false,
|
|
}
|
|
}
|
|
|
|
pub fn tokSlice(tree: Tree, tok_i: TokenIndex) []const u8 {
|
|
if (tree.tokens.items(.id)[tok_i].lexeme()) |some| return some;
|
|
const loc = tree.tokens.items(.loc)[tok_i];
|
|
var tmp_tokenizer = Tokenizer{
|
|
.buf = tree.comp.getSource(loc.id).buf,
|
|
.comp = tree.comp,
|
|
.index = loc.byte_offset,
|
|
.source = .generated,
|
|
};
|
|
const tok = tmp_tokenizer.next();
|
|
return tmp_tokenizer.buf[tok.start..tok.end];
|
|
}
|
|
|
|
pub fn dump(tree: Tree, color: bool, writer: anytype) @TypeOf(writer).Error!void {
|
|
const mapper = tree.comp.string_interner.getFastTypeMapper(tree.comp.gpa) catch tree.comp.string_interner.getSlowTypeMapper();
|
|
defer mapper.deinit(tree.comp.gpa);
|
|
|
|
for (tree.root_decls) |i| {
|
|
try tree.dumpNode(i, 0, mapper, color, writer);
|
|
try writer.writeByte('\n');
|
|
}
|
|
}
|
|
|
|
fn dumpFieldAttributes(attributes: []const Attribute, level: u32, strings: []const u8, writer: anytype) !void {
|
|
for (attributes) |attr| {
|
|
try writer.writeByteNTimes(' ', level);
|
|
try writer.print("field attr: {s}", .{@tagName(attr.tag)});
|
|
try dumpAttribute(attr, strings, writer);
|
|
}
|
|
}
|
|
|
|
fn dumpAttribute(attr: Attribute, strings: []const u8, writer: anytype) !void {
|
|
switch (attr.tag) {
|
|
inline else => |tag| {
|
|
const args = @field(attr.args, @tagName(tag));
|
|
const fields = @typeInfo(@TypeOf(args)).Struct.fields;
|
|
if (fields.len == 0) {
|
|
try writer.writeByte('\n');
|
|
return;
|
|
}
|
|
try writer.writeByte(' ');
|
|
inline for (fields, 0..) |f, i| {
|
|
if (comptime std.mem.eql(u8, f.name, "__name_tok")) continue;
|
|
if (i != 0) {
|
|
try writer.writeAll(", ");
|
|
}
|
|
try writer.writeAll(f.name);
|
|
try writer.writeAll(": ");
|
|
switch (f.type) {
|
|
Value.ByteRange => try writer.print("\"{s}\"", .{@field(args, f.name).slice(strings, .@"1")}),
|
|
?Value.ByteRange => try writer.print("\"{?s}\"", .{if (@field(args, f.name)) |range| range.slice(strings, .@"1") else null}),
|
|
else => switch (@typeInfo(f.type)) {
|
|
.Enum => try writer.writeAll(@tagName(@field(args, f.name))),
|
|
else => try writer.print("{any}", .{@field(args, f.name)}),
|
|
},
|
|
}
|
|
}
|
|
try writer.writeByte('\n');
|
|
return;
|
|
},
|
|
}
|
|
}
|
|
|
|
fn dumpNode(tree: Tree, node: NodeIndex, level: u32, mapper: StringInterner.TypeMapper, color: bool, w: anytype) @TypeOf(w).Error!void {
|
|
const delta = 2;
|
|
const half = delta / 2;
|
|
const util = @import("util.zig");
|
|
const TYPE = util.Color.purple;
|
|
const TAG = util.Color.cyan;
|
|
const IMPLICIT = util.Color.blue;
|
|
const NAME = util.Color.red;
|
|
const LITERAL = util.Color.green;
|
|
const ATTRIBUTE = util.Color.yellow;
|
|
std.debug.assert(node != .none);
|
|
|
|
const tag = tree.nodes.items(.tag)[@intFromEnum(node)];
|
|
const data = tree.nodes.items(.data)[@intFromEnum(node)];
|
|
const ty = tree.nodes.items(.ty)[@intFromEnum(node)];
|
|
try w.writeByteNTimes(' ', level);
|
|
|
|
if (color) util.setColor(if (tag.isImplicit()) IMPLICIT else TAG, w);
|
|
try w.print("{s}: ", .{@tagName(tag)});
|
|
if (tag == .implicit_cast or tag == .explicit_cast) {
|
|
if (color) util.setColor(.white, w);
|
|
try w.print("({s}) ", .{@tagName(data.cast.kind)});
|
|
}
|
|
if (color) util.setColor(TYPE, w);
|
|
try w.writeByte('\'');
|
|
try ty.dump(mapper, tree.comp.langopts, w);
|
|
try w.writeByte('\'');
|
|
|
|
if (isLval(tree.nodes, tree.data, tree.value_map, node)) {
|
|
if (color) util.setColor(ATTRIBUTE, w);
|
|
try w.writeAll(" lvalue");
|
|
}
|
|
if (isBitfield(tree.nodes, node)) {
|
|
if (color) util.setColor(ATTRIBUTE, w);
|
|
try w.writeAll(" bitfield");
|
|
}
|
|
if (tree.value_map.get(node)) |val| {
|
|
if (color) util.setColor(LITERAL, w);
|
|
try w.writeAll(" (value: ");
|
|
try val.dump(ty, tree.comp, tree.strings, w);
|
|
try w.writeByte(')');
|
|
}
|
|
if (tag == .implicit_return and data.return_zero) {
|
|
if (color) util.setColor(IMPLICIT, w);
|
|
try w.writeAll(" (value: 0)");
|
|
if (color) util.setColor(.reset, w);
|
|
}
|
|
|
|
try w.writeAll("\n");
|
|
if (color) util.setColor(.reset, w);
|
|
|
|
if (ty.specifier == .attributed) {
|
|
if (color) util.setColor(ATTRIBUTE, w);
|
|
for (ty.data.attributed.attributes) |attr| {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.print("attr: {s}", .{@tagName(attr.tag)});
|
|
try dumpAttribute(attr, tree.strings, w);
|
|
}
|
|
if (color) util.setColor(.reset, w);
|
|
}
|
|
|
|
switch (tag) {
|
|
.invalid => unreachable,
|
|
.file_scope_asm => {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try tree.dumpNode(data.decl.node, level + delta, mapper, color, w);
|
|
},
|
|
.gnu_asm_simple => {
|
|
try w.writeByteNTimes(' ', level);
|
|
try tree.dumpNode(data.un, level, mapper, color, w);
|
|
},
|
|
.static_assert => {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("condition:\n");
|
|
try tree.dumpNode(data.bin.lhs, level + delta, mapper, color, w);
|
|
if (data.bin.rhs != .none) {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("diagnostic:\n");
|
|
try tree.dumpNode(data.bin.rhs, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.fn_proto,
|
|
.static_fn_proto,
|
|
.inline_fn_proto,
|
|
.inline_static_fn_proto,
|
|
=> {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl.name)});
|
|
if (color) util.setColor(.reset, w);
|
|
},
|
|
.fn_def,
|
|
.static_fn_def,
|
|
.inline_fn_def,
|
|
.inline_static_fn_def,
|
|
=> {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl.name)});
|
|
if (color) util.setColor(.reset, w);
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("body:\n");
|
|
try tree.dumpNode(data.decl.node, level + delta, mapper, color, w);
|
|
},
|
|
.typedef,
|
|
.@"var",
|
|
.extern_var,
|
|
.static_var,
|
|
.implicit_static_var,
|
|
.threadlocal_var,
|
|
.threadlocal_extern_var,
|
|
.threadlocal_static_var,
|
|
=> {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl.name)});
|
|
if (color) util.setColor(.reset, w);
|
|
if (data.decl.node != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("init:\n");
|
|
try tree.dumpNode(data.decl.node, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.enum_field_decl => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl.name)});
|
|
if (color) util.setColor(.reset, w);
|
|
if (data.decl.node != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("value:\n");
|
|
try tree.dumpNode(data.decl.node, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.record_field_decl => {
|
|
if (data.decl.name != 0) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl.name)});
|
|
if (color) util.setColor(.reset, w);
|
|
}
|
|
if (data.decl.node != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("bits:\n");
|
|
try tree.dumpNode(data.decl.node, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.indirect_record_field_decl => {},
|
|
.compound_stmt,
|
|
.array_init_expr,
|
|
.struct_init_expr,
|
|
.enum_decl,
|
|
.struct_decl,
|
|
.union_decl,
|
|
=> {
|
|
const maybe_field_attributes = if (ty.getRecord()) |record| record.field_attributes else null;
|
|
for (tree.data[data.range.start..data.range.end], 0..) |stmt, i| {
|
|
if (i != 0) try w.writeByte('\n');
|
|
try tree.dumpNode(stmt, level + delta, mapper, color, w);
|
|
if (maybe_field_attributes) |field_attributes| {
|
|
if (field_attributes[i].len == 0) continue;
|
|
|
|
if (color) util.setColor(ATTRIBUTE, w);
|
|
try dumpFieldAttributes(field_attributes[i], level + delta + half, tree.strings, w);
|
|
if (color) util.setColor(.reset, w);
|
|
}
|
|
}
|
|
},
|
|
.compound_stmt_two,
|
|
.array_init_expr_two,
|
|
.struct_init_expr_two,
|
|
.enum_decl_two,
|
|
.struct_decl_two,
|
|
.union_decl_two,
|
|
=> {
|
|
var attr_array = [2][]const Attribute{ &.{}, &.{} };
|
|
const empty: [][]const Attribute = &attr_array;
|
|
const field_attributes = if (ty.getRecord()) |record| (record.field_attributes orelse empty.ptr) else empty.ptr;
|
|
if (data.bin.lhs != .none) {
|
|
try tree.dumpNode(data.bin.lhs, level + delta, mapper, color, w);
|
|
if (field_attributes[0].len > 0) {
|
|
if (color) util.setColor(ATTRIBUTE, w);
|
|
try dumpFieldAttributes(field_attributes[0], level + delta + half, tree.strings, w);
|
|
if (color) util.setColor(.reset, w);
|
|
}
|
|
}
|
|
if (data.bin.rhs != .none) {
|
|
try tree.dumpNode(data.bin.rhs, level + delta, mapper, color, w);
|
|
if (field_attributes[1].len > 0) {
|
|
if (color) util.setColor(ATTRIBUTE, w);
|
|
try dumpFieldAttributes(field_attributes[1], level + delta + half, tree.strings, w);
|
|
if (color) util.setColor(.reset, w);
|
|
}
|
|
}
|
|
},
|
|
.union_init_expr => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("field index: ");
|
|
if (color) util.setColor(LITERAL, w);
|
|
try w.print("{d}\n", .{data.union_init.field_index});
|
|
if (color) util.setColor(.reset, w);
|
|
if (data.union_init.node != .none) {
|
|
try tree.dumpNode(data.union_init.node, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.compound_literal_expr => {
|
|
try tree.dumpNode(data.un, level + half, mapper, color, w);
|
|
},
|
|
.labeled_stmt => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("label: ");
|
|
if (color) util.setColor(LITERAL, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl.name)});
|
|
if (color) util.setColor(.reset, w);
|
|
if (data.decl.node != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("stmt:\n");
|
|
try tree.dumpNode(data.decl.node, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.case_stmt => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("value:\n");
|
|
try tree.dumpNode(data.bin.lhs, level + delta, mapper, color, w);
|
|
if (data.bin.rhs != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("stmt:\n");
|
|
try tree.dumpNode(data.bin.rhs, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.case_range_stmt => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("range start:\n");
|
|
try tree.dumpNode(tree.data[data.if3.body], level + delta, mapper, color, w);
|
|
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("range end:\n");
|
|
try tree.dumpNode(tree.data[data.if3.body + 1], level + delta, mapper, color, w);
|
|
|
|
if (data.if3.cond != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("stmt:\n");
|
|
try tree.dumpNode(data.if3.cond, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.default_stmt => {
|
|
if (data.un != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("stmt:\n");
|
|
try tree.dumpNode(data.un, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.binary_cond_expr, .cond_expr, .if_then_else_stmt, .builtin_choose_expr => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("cond:\n");
|
|
try tree.dumpNode(data.if3.cond, level + delta, mapper, color, w);
|
|
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("then:\n");
|
|
try tree.dumpNode(tree.data[data.if3.body], level + delta, mapper, color, w);
|
|
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("else:\n");
|
|
try tree.dumpNode(tree.data[data.if3.body + 1], level + delta, mapper, color, w);
|
|
},
|
|
.builtin_types_compatible_p => {
|
|
std.debug.assert(tree.nodes.items(.tag)[@intFromEnum(data.bin.lhs)] == .invalid);
|
|
std.debug.assert(tree.nodes.items(.tag)[@intFromEnum(data.bin.rhs)] == .invalid);
|
|
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("lhs: ");
|
|
|
|
const lhs_ty = tree.nodes.items(.ty)[@intFromEnum(data.bin.lhs)];
|
|
if (color) util.setColor(TYPE, w);
|
|
try lhs_ty.dump(mapper, tree.comp.langopts, w);
|
|
if (color) util.setColor(.reset, w);
|
|
try w.writeByte('\n');
|
|
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("rhs: ");
|
|
|
|
const rhs_ty = tree.nodes.items(.ty)[@intFromEnum(data.bin.rhs)];
|
|
if (color) util.setColor(TYPE, w);
|
|
try rhs_ty.dump(mapper, tree.comp.langopts, w);
|
|
if (color) util.setColor(.reset, w);
|
|
try w.writeByte('\n');
|
|
},
|
|
.if_then_stmt => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("cond:\n");
|
|
try tree.dumpNode(data.bin.lhs, level + delta, mapper, color, w);
|
|
|
|
if (data.bin.rhs != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("then:\n");
|
|
try tree.dumpNode(data.bin.rhs, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.switch_stmt, .while_stmt, .do_while_stmt => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("cond:\n");
|
|
try tree.dumpNode(data.bin.lhs, level + delta, mapper, color, w);
|
|
|
|
if (data.bin.rhs != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("body:\n");
|
|
try tree.dumpNode(data.bin.rhs, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.for_decl_stmt => {
|
|
const for_decl = data.forDecl(tree);
|
|
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("decl:\n");
|
|
for (for_decl.decls) |decl| {
|
|
try tree.dumpNode(decl, level + delta, mapper, color, w);
|
|
try w.writeByte('\n');
|
|
}
|
|
if (for_decl.cond != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("cond:\n");
|
|
try tree.dumpNode(for_decl.cond, level + delta, mapper, color, w);
|
|
}
|
|
if (for_decl.incr != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("incr:\n");
|
|
try tree.dumpNode(for_decl.incr, level + delta, mapper, color, w);
|
|
}
|
|
if (for_decl.body != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("body:\n");
|
|
try tree.dumpNode(for_decl.body, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.forever_stmt => {
|
|
if (data.un != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("body:\n");
|
|
try tree.dumpNode(data.un, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.for_stmt => {
|
|
const for_stmt = data.forStmt(tree);
|
|
|
|
if (for_stmt.init != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("init:\n");
|
|
try tree.dumpNode(for_stmt.init, level + delta, mapper, color, w);
|
|
}
|
|
if (for_stmt.cond != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("cond:\n");
|
|
try tree.dumpNode(for_stmt.cond, level + delta, mapper, color, w);
|
|
}
|
|
if (for_stmt.incr != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("incr:\n");
|
|
try tree.dumpNode(for_stmt.incr, level + delta, mapper, color, w);
|
|
}
|
|
if (for_stmt.body != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("body:\n");
|
|
try tree.dumpNode(for_stmt.body, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.goto_stmt, .addr_of_label => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("label: ");
|
|
if (color) util.setColor(LITERAL, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl_ref)});
|
|
if (color) util.setColor(.reset, w);
|
|
},
|
|
.continue_stmt, .break_stmt, .implicit_return, .null_stmt => {},
|
|
.return_stmt => {
|
|
if (data.un != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("expr:\n");
|
|
try tree.dumpNode(data.un, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.call_expr => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("lhs:\n");
|
|
try tree.dumpNode(tree.data[data.range.start], level + delta, mapper, color, w);
|
|
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("args:\n");
|
|
for (tree.data[data.range.start + 1 .. data.range.end]) |arg| try tree.dumpNode(arg, level + delta, mapper, color, w);
|
|
},
|
|
.call_expr_one => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("lhs:\n");
|
|
try tree.dumpNode(data.bin.lhs, level + delta, mapper, color, w);
|
|
if (data.bin.rhs != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("arg:\n");
|
|
try tree.dumpNode(data.bin.rhs, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.builtin_call_expr => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(@intFromEnum(tree.data[data.range.start]))});
|
|
if (color) util.setColor(.reset, w);
|
|
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("args:\n");
|
|
for (tree.data[data.range.start + 1 .. data.range.end]) |arg| try tree.dumpNode(arg, level + delta, mapper, color, w);
|
|
},
|
|
.builtin_call_expr_one => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl.name)});
|
|
if (color) util.setColor(.reset, w);
|
|
if (data.decl.node != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("arg:\n");
|
|
try tree.dumpNode(data.decl.node, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.special_builtin_call_one => {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl.name)});
|
|
if (color) util.setColor(.reset, w);
|
|
if (data.decl.node != .none) {
|
|
try w.writeByteNTimes(' ', level + half);
|
|
try w.writeAll("arg:\n");
|
|
try tree.dumpNode(data.decl.node, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.comma_expr,
|
|
.assign_expr,
|
|
.mul_assign_expr,
|
|
.div_assign_expr,
|
|
.mod_assign_expr,
|
|
.add_assign_expr,
|
|
.sub_assign_expr,
|
|
.shl_assign_expr,
|
|
.shr_assign_expr,
|
|
.bit_and_assign_expr,
|
|
.bit_xor_assign_expr,
|
|
.bit_or_assign_expr,
|
|
.bool_or_expr,
|
|
.bool_and_expr,
|
|
.bit_or_expr,
|
|
.bit_xor_expr,
|
|
.bit_and_expr,
|
|
.equal_expr,
|
|
.not_equal_expr,
|
|
.less_than_expr,
|
|
.less_than_equal_expr,
|
|
.greater_than_expr,
|
|
.greater_than_equal_expr,
|
|
.shl_expr,
|
|
.shr_expr,
|
|
.add_expr,
|
|
.sub_expr,
|
|
.mul_expr,
|
|
.div_expr,
|
|
.mod_expr,
|
|
=> {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("lhs:\n");
|
|
try tree.dumpNode(data.bin.lhs, level + delta, mapper, color, w);
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("rhs:\n");
|
|
try tree.dumpNode(data.bin.rhs, level + delta, mapper, color, w);
|
|
},
|
|
.explicit_cast, .implicit_cast => try tree.dumpNode(data.cast.operand, level + delta, mapper, color, w),
|
|
.addr_of_expr,
|
|
.computed_goto_stmt,
|
|
.deref_expr,
|
|
.plus_expr,
|
|
.negate_expr,
|
|
.bit_not_expr,
|
|
.bool_not_expr,
|
|
.pre_inc_expr,
|
|
.pre_dec_expr,
|
|
.imag_expr,
|
|
.real_expr,
|
|
.post_inc_expr,
|
|
.post_dec_expr,
|
|
.paren_expr,
|
|
=> {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("operand:\n");
|
|
try tree.dumpNode(data.un, level + delta, mapper, color, w);
|
|
},
|
|
.decl_ref_expr => {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl_ref)});
|
|
if (color) util.setColor(.reset, w);
|
|
},
|
|
.enumeration_ref => {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{tree.tokSlice(data.decl_ref)});
|
|
if (color) util.setColor(.reset, w);
|
|
},
|
|
.bool_literal,
|
|
.nullptr_literal,
|
|
.int_literal,
|
|
.char_literal,
|
|
.float16_literal,
|
|
.float_literal,
|
|
.double_literal,
|
|
.string_literal_expr,
|
|
=> {},
|
|
.member_access_expr, .member_access_ptr_expr => {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("lhs:\n");
|
|
try tree.dumpNode(data.member.lhs, level + delta, mapper, color, w);
|
|
|
|
var lhs_ty = tree.nodes.items(.ty)[@intFromEnum(data.member.lhs)];
|
|
if (lhs_ty.isPtr()) lhs_ty = lhs_ty.elemType();
|
|
lhs_ty = lhs_ty.canonicalize(.standard);
|
|
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("name: ");
|
|
if (color) util.setColor(NAME, w);
|
|
try w.print("{s}\n", .{mapper.lookup(lhs_ty.data.record.fields[data.member.index].name)});
|
|
if (color) util.setColor(.reset, w);
|
|
},
|
|
.array_access_expr => {
|
|
if (data.bin.lhs != .none) {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("lhs:\n");
|
|
try tree.dumpNode(data.bin.lhs, level + delta, mapper, color, w);
|
|
}
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("index:\n");
|
|
try tree.dumpNode(data.bin.rhs, level + delta, mapper, color, w);
|
|
},
|
|
.sizeof_expr, .alignof_expr => {
|
|
if (data.un != .none) {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("expr:\n");
|
|
try tree.dumpNode(data.un, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.generic_expr_one => {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("controlling:\n");
|
|
try tree.dumpNode(data.bin.lhs, level + delta, mapper, color, w);
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
if (data.bin.rhs != .none) {
|
|
try w.writeAll("chosen:\n");
|
|
try tree.dumpNode(data.bin.rhs, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.generic_expr => {
|
|
const nodes = tree.data[data.range.start..data.range.end];
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("controlling:\n");
|
|
try tree.dumpNode(nodes[0], level + delta, mapper, color, w);
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("chosen:\n");
|
|
try tree.dumpNode(nodes[1], level + delta, mapper, color, w);
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("rest:\n");
|
|
for (nodes[2..]) |expr| {
|
|
try tree.dumpNode(expr, level + delta, mapper, color, w);
|
|
}
|
|
},
|
|
.generic_association_expr, .generic_default_expr, .stmt_expr, .imaginary_literal => {
|
|
try tree.dumpNode(data.un, level + delta, mapper, color, w);
|
|
},
|
|
.array_filler_expr => {
|
|
try w.writeByteNTimes(' ', level + 1);
|
|
try w.writeAll("count: ");
|
|
if (color) util.setColor(LITERAL, w);
|
|
try w.print("{d}\n", .{data.int});
|
|
if (color) util.setColor(.reset, w);
|
|
},
|
|
.struct_forward_decl,
|
|
.union_forward_decl,
|
|
.enum_forward_decl,
|
|
.default_init_expr,
|
|
.cond_dummy_expr,
|
|
=> {},
|
|
}
|
|
}
|