mirror of
https://github.com/ziglang/zig.git
synced 2025-12-07 06:43:07 +00:00
Part of #19063. Primarily, this moves Aro from deps/ to lib/compiler/ so that it can be lazily compiled from source. src/aro_translate_c.zig is moved to lib/compiler/aro_translate_c.zig and some of Zig CLI logic moved to a main() function there. aro_translate_c.zig becomes the "common" import for clang-based translate-c. Not all of the compiler was able to be detangled from Aro, however, so it still, for now, remains being compiled with the main compiler sources due to the clang-based translate-c depending on it. Once aro-based translate-c achieves feature parity with the clang-based translate-c implementation, the clang-based one can be removed from Zig. Aro made it unnecessarily difficult to depend on with these .def files and all these Zig module requirements. I looked at the .def files and made these observations: - The canonical source is llvm .def files. - Therefore there is an update process to sync with llvm that involves regenerating the .def files in Aro. - Therefore you might as well just regenerate the .zig files directly and check those into Aro. - Also with a small amount of tinkering, the file size on disk of these generated .zig files can be made many times smaller, without compromising type safety in the usage of the data. This would make things much easier on Zig as downstream project, particularly we could remove those pesky stubs when bootstrapping. I have gone ahead with these changes since they unblock me and I will have a chat with Vexu to see what he thinks.
2942 lines
97 KiB
Zig
2942 lines
97 KiB
Zig
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
pub const Node = extern union {
|
|
/// If the tag value is less than Tag.no_payload_count, then no pointer
|
|
/// dereference is needed.
|
|
tag_if_small_enough: usize,
|
|
ptr_otherwise: *Payload,
|
|
|
|
pub const Tag = enum {
|
|
/// Declarations add themselves to the correct scopes and should not be emitted as this tag.
|
|
declaration,
|
|
null_literal,
|
|
undefined_literal,
|
|
/// opaque {}
|
|
opaque_literal,
|
|
true_literal,
|
|
false_literal,
|
|
empty_block,
|
|
return_void,
|
|
zero_literal,
|
|
one_literal,
|
|
void_type,
|
|
noreturn_type,
|
|
@"anytype",
|
|
@"continue",
|
|
@"break",
|
|
// After this, the tag requires a payload.
|
|
|
|
integer_literal,
|
|
float_literal,
|
|
string_literal,
|
|
char_literal,
|
|
enum_literal,
|
|
/// "string"[0..end]
|
|
string_slice,
|
|
identifier,
|
|
fn_identifier,
|
|
@"if",
|
|
/// if (!operand) break;
|
|
if_not_break,
|
|
@"while",
|
|
/// while (true) operand
|
|
while_true,
|
|
@"switch",
|
|
/// else => operand,
|
|
switch_else,
|
|
/// items => body,
|
|
switch_prong,
|
|
break_val,
|
|
@"return",
|
|
field_access,
|
|
array_access,
|
|
call,
|
|
var_decl,
|
|
/// const name = struct { init }
|
|
static_local_var,
|
|
/// var name = init.*
|
|
mut_str,
|
|
func,
|
|
warning,
|
|
@"struct",
|
|
@"union",
|
|
@"comptime",
|
|
@"defer",
|
|
array_init,
|
|
tuple,
|
|
container_init,
|
|
container_init_dot,
|
|
helpers_cast,
|
|
/// _ = operand;
|
|
discard,
|
|
|
|
// a + b
|
|
add,
|
|
// a = b
|
|
add_assign,
|
|
// c = (a = b)
|
|
add_wrap,
|
|
add_wrap_assign,
|
|
sub,
|
|
sub_assign,
|
|
sub_wrap,
|
|
sub_wrap_assign,
|
|
mul,
|
|
mul_assign,
|
|
mul_wrap,
|
|
mul_wrap_assign,
|
|
div,
|
|
div_assign,
|
|
shl,
|
|
shl_assign,
|
|
shr,
|
|
shr_assign,
|
|
mod,
|
|
mod_assign,
|
|
@"and",
|
|
@"or",
|
|
less_than,
|
|
less_than_equal,
|
|
greater_than,
|
|
greater_than_equal,
|
|
equal,
|
|
not_equal,
|
|
bit_and,
|
|
bit_and_assign,
|
|
bit_or,
|
|
bit_or_assign,
|
|
bit_xor,
|
|
bit_xor_assign,
|
|
array_cat,
|
|
ellipsis3,
|
|
assign,
|
|
|
|
/// @import("std").zig.c_builtins.<name>
|
|
import_c_builtin,
|
|
/// @intCast(operand)
|
|
int_cast,
|
|
/// @constCast(operand)
|
|
const_cast,
|
|
/// @volatileCast(operand)
|
|
volatile_cast,
|
|
/// @import("std").zig.c_translation.promoteIntLiteral(value, type, base)
|
|
helpers_promoteIntLiteral,
|
|
/// @import("std").zig.c_translation.signedRemainder(lhs, rhs)
|
|
signed_remainder,
|
|
/// @divTrunc(lhs, rhs)
|
|
div_trunc,
|
|
/// @intFromBool(operand)
|
|
int_from_bool,
|
|
/// @as(lhs, rhs)
|
|
as,
|
|
/// @truncate(operand)
|
|
truncate,
|
|
/// @bitCast(operand)
|
|
bit_cast,
|
|
/// @floatCast(operand)
|
|
float_cast,
|
|
/// @intFromFloat(operand)
|
|
int_from_float,
|
|
/// @floatFromInt(operand)
|
|
float_from_int,
|
|
/// @ptrFromInt(operand)
|
|
ptr_from_int,
|
|
/// @intFromPtr(operand)
|
|
int_from_ptr,
|
|
/// @alignCast(operand)
|
|
align_cast,
|
|
/// @ptrCast(operand)
|
|
ptr_cast,
|
|
/// @divExact(lhs, rhs)
|
|
div_exact,
|
|
/// @offsetOf(lhs, rhs)
|
|
offset_of,
|
|
/// @splat(operand)
|
|
vector_zero_init,
|
|
/// @shuffle(type, a, b, mask)
|
|
shuffle,
|
|
/// @extern(ty, .{ .name = n })
|
|
builtin_extern,
|
|
|
|
/// @import("std").zig.c_translation.MacroArithmetic.<op>(lhs, rhs)
|
|
macro_arithmetic,
|
|
|
|
asm_simple,
|
|
|
|
negate,
|
|
negate_wrap,
|
|
bit_not,
|
|
not,
|
|
address_of,
|
|
/// .?
|
|
unwrap,
|
|
/// .*
|
|
deref,
|
|
|
|
block,
|
|
/// { operand }
|
|
block_single,
|
|
|
|
sizeof,
|
|
alignof,
|
|
typeof,
|
|
typeinfo,
|
|
type,
|
|
|
|
optional_type,
|
|
c_pointer,
|
|
single_pointer,
|
|
array_type,
|
|
null_sentinel_array_type,
|
|
|
|
/// @import("std").zig.c_translation.sizeof(operand)
|
|
helpers_sizeof,
|
|
/// @import("std").zig.c_translation.FlexibleArrayType(lhs, rhs)
|
|
helpers_flexible_array_type,
|
|
/// @import("std").zig.c_translation.shuffleVectorIndex(lhs, rhs)
|
|
helpers_shuffle_vector_index,
|
|
/// @import("std").zig.c_translation.Macro.<operand>
|
|
helpers_macro,
|
|
/// @Vector(lhs, rhs)
|
|
vector,
|
|
/// @import("std").mem.zeroes(operand)
|
|
std_mem_zeroes,
|
|
/// @import("std").mem.zeroInit(lhs, rhs)
|
|
std_mem_zeroinit,
|
|
// pub const name = @compileError(msg);
|
|
fail_decl,
|
|
// var actual = mangled;
|
|
arg_redecl,
|
|
/// pub const alias = actual;
|
|
alias,
|
|
/// const name = init;
|
|
var_simple,
|
|
/// pub const name = init;
|
|
pub_var_simple,
|
|
/// pub? const name (: type)? = value
|
|
enum_constant,
|
|
|
|
/// pub inline fn name(params) return_type body
|
|
pub_inline_fn,
|
|
|
|
/// [0]type{}
|
|
empty_array,
|
|
/// [1]type{val} ** count
|
|
array_filler,
|
|
|
|
pub const last_no_payload_tag = Tag.@"break";
|
|
pub const no_payload_count = @intFromEnum(last_no_payload_tag) + 1;
|
|
|
|
pub fn Type(comptime t: Tag) type {
|
|
return switch (t) {
|
|
.declaration,
|
|
.null_literal,
|
|
.undefined_literal,
|
|
.opaque_literal,
|
|
.true_literal,
|
|
.false_literal,
|
|
.empty_block,
|
|
.return_void,
|
|
.zero_literal,
|
|
.one_literal,
|
|
.void_type,
|
|
.noreturn_type,
|
|
.@"anytype",
|
|
.@"continue",
|
|
.@"break",
|
|
=> @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
|
|
|
|
.std_mem_zeroes,
|
|
.@"return",
|
|
.@"comptime",
|
|
.@"defer",
|
|
.asm_simple,
|
|
.negate,
|
|
.negate_wrap,
|
|
.bit_not,
|
|
.not,
|
|
.optional_type,
|
|
.address_of,
|
|
.unwrap,
|
|
.deref,
|
|
.int_from_ptr,
|
|
.empty_array,
|
|
.while_true,
|
|
.if_not_break,
|
|
.switch_else,
|
|
.block_single,
|
|
.helpers_sizeof,
|
|
.int_from_bool,
|
|
.sizeof,
|
|
.alignof,
|
|
.typeof,
|
|
.typeinfo,
|
|
.align_cast,
|
|
.truncate,
|
|
.bit_cast,
|
|
.float_cast,
|
|
.int_from_float,
|
|
.float_from_int,
|
|
.ptr_from_int,
|
|
.ptr_cast,
|
|
.int_cast,
|
|
.const_cast,
|
|
.volatile_cast,
|
|
.vector_zero_init,
|
|
=> Payload.UnOp,
|
|
|
|
.add,
|
|
.add_assign,
|
|
.add_wrap,
|
|
.add_wrap_assign,
|
|
.sub,
|
|
.sub_assign,
|
|
.sub_wrap,
|
|
.sub_wrap_assign,
|
|
.mul,
|
|
.mul_assign,
|
|
.mul_wrap,
|
|
.mul_wrap_assign,
|
|
.div,
|
|
.div_assign,
|
|
.shl,
|
|
.shl_assign,
|
|
.shr,
|
|
.shr_assign,
|
|
.mod,
|
|
.mod_assign,
|
|
.@"and",
|
|
.@"or",
|
|
.less_than,
|
|
.less_than_equal,
|
|
.greater_than,
|
|
.greater_than_equal,
|
|
.equal,
|
|
.not_equal,
|
|
.bit_and,
|
|
.bit_and_assign,
|
|
.bit_or,
|
|
.bit_or_assign,
|
|
.bit_xor,
|
|
.bit_xor_assign,
|
|
.div_trunc,
|
|
.signed_remainder,
|
|
.as,
|
|
.array_cat,
|
|
.ellipsis3,
|
|
.assign,
|
|
.array_access,
|
|
.std_mem_zeroinit,
|
|
.helpers_flexible_array_type,
|
|
.helpers_shuffle_vector_index,
|
|
.vector,
|
|
.div_exact,
|
|
.offset_of,
|
|
.helpers_cast,
|
|
=> Payload.BinOp,
|
|
|
|
.integer_literal,
|
|
.float_literal,
|
|
.string_literal,
|
|
.char_literal,
|
|
.enum_literal,
|
|
.identifier,
|
|
.fn_identifier,
|
|
.warning,
|
|
.type,
|
|
.helpers_macro,
|
|
.import_c_builtin,
|
|
=> Payload.Value,
|
|
.discard => Payload.Discard,
|
|
.@"if" => Payload.If,
|
|
.@"while" => Payload.While,
|
|
.@"switch", .array_init, .switch_prong => Payload.Switch,
|
|
.break_val => Payload.BreakVal,
|
|
.call => Payload.Call,
|
|
.var_decl => Payload.VarDecl,
|
|
.func => Payload.Func,
|
|
.@"struct", .@"union" => Payload.Record,
|
|
.tuple => Payload.TupleInit,
|
|
.container_init => Payload.ContainerInit,
|
|
.container_init_dot => Payload.ContainerInitDot,
|
|
.helpers_promoteIntLiteral => Payload.PromoteIntLiteral,
|
|
.block => Payload.Block,
|
|
.c_pointer, .single_pointer => Payload.Pointer,
|
|
.array_type, .null_sentinel_array_type => Payload.Array,
|
|
.arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
|
|
.var_simple, .pub_var_simple, .static_local_var, .mut_str => Payload.SimpleVarDecl,
|
|
.enum_constant => Payload.EnumConstant,
|
|
.array_filler => Payload.ArrayFiller,
|
|
.pub_inline_fn => Payload.PubInlineFn,
|
|
.field_access => Payload.FieldAccess,
|
|
.string_slice => Payload.StringSlice,
|
|
.shuffle => Payload.Shuffle,
|
|
.builtin_extern => Payload.Extern,
|
|
.macro_arithmetic => Payload.MacroArithmetic,
|
|
};
|
|
}
|
|
|
|
pub fn init(comptime t: Tag) Node {
|
|
comptime std.debug.assert(@intFromEnum(t) < Tag.no_payload_count);
|
|
return .{ .tag_if_small_enough = @intFromEnum(t) };
|
|
}
|
|
|
|
pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Node {
|
|
const ptr = try ally.create(t.Type());
|
|
ptr.* = .{
|
|
.base = .{ .tag = t },
|
|
.data = data,
|
|
};
|
|
return Node{ .ptr_otherwise = &ptr.base };
|
|
}
|
|
|
|
pub fn Data(comptime t: Tag) type {
|
|
return std.meta.fieldInfo(t.Type(), .data).type;
|
|
}
|
|
};
|
|
|
|
pub fn tag(self: Node) Tag {
|
|
if (self.tag_if_small_enough < Tag.no_payload_count) {
|
|
return @as(Tag, @enumFromInt(@as(std.meta.Tag(Tag), @intCast(self.tag_if_small_enough))));
|
|
} else {
|
|
return self.ptr_otherwise.tag;
|
|
}
|
|
}
|
|
|
|
pub fn castTag(self: Node, comptime t: Tag) ?*t.Type() {
|
|
if (self.tag_if_small_enough < Tag.no_payload_count)
|
|
return null;
|
|
|
|
if (self.ptr_otherwise.tag == t)
|
|
return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise);
|
|
|
|
return null;
|
|
}
|
|
|
|
pub fn initPayload(payload: *Payload) Node {
|
|
std.debug.assert(@intFromEnum(payload.tag) >= Tag.no_payload_count);
|
|
return .{ .ptr_otherwise = payload };
|
|
}
|
|
|
|
pub fn isNoreturn(node: Node, break_counts: bool) bool {
|
|
switch (node.tag()) {
|
|
.block => {
|
|
const block_node = node.castTag(.block).?;
|
|
if (block_node.data.stmts.len == 0) return false;
|
|
|
|
const last = block_node.data.stmts[block_node.data.stmts.len - 1];
|
|
return last.isNoreturn(break_counts);
|
|
},
|
|
.@"switch" => {
|
|
const switch_node = node.castTag(.@"switch").?;
|
|
|
|
for (switch_node.data.cases) |case| {
|
|
const body = if (case.castTag(.switch_else)) |some|
|
|
some.data
|
|
else if (case.castTag(.switch_prong)) |some|
|
|
some.data.cond
|
|
else
|
|
unreachable;
|
|
|
|
if (!body.isNoreturn(break_counts)) return false;
|
|
}
|
|
return true;
|
|
},
|
|
.@"return", .return_void => return true,
|
|
.@"break" => if (break_counts) return true,
|
|
else => {},
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
pub const Payload = struct {
|
|
tag: Node.Tag,
|
|
|
|
pub const Value = struct {
|
|
base: Payload,
|
|
data: []const u8,
|
|
};
|
|
|
|
pub const UnOp = struct {
|
|
base: Payload,
|
|
data: Node,
|
|
};
|
|
|
|
pub const BinOp = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
lhs: Node,
|
|
rhs: Node,
|
|
},
|
|
};
|
|
|
|
pub const Discard = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
should_skip: bool,
|
|
value: Node,
|
|
},
|
|
};
|
|
|
|
pub const If = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
cond: Node,
|
|
then: Node,
|
|
@"else": ?Node,
|
|
},
|
|
};
|
|
|
|
pub const While = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
cond: Node,
|
|
body: Node,
|
|
cont_expr: ?Node,
|
|
},
|
|
};
|
|
|
|
pub const Switch = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
cond: Node,
|
|
cases: []Node,
|
|
},
|
|
};
|
|
|
|
pub const BreakVal = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
label: ?[]const u8,
|
|
val: Node,
|
|
},
|
|
};
|
|
|
|
pub const Call = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
lhs: Node,
|
|
args: []Node,
|
|
},
|
|
};
|
|
|
|
pub const VarDecl = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
is_pub: bool,
|
|
is_const: bool,
|
|
is_extern: bool,
|
|
is_export: bool,
|
|
is_threadlocal: bool,
|
|
alignment: ?c_uint,
|
|
linksection_string: ?[]const u8,
|
|
name: []const u8,
|
|
type: Node,
|
|
init: ?Node,
|
|
},
|
|
};
|
|
|
|
pub const Func = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
is_pub: bool,
|
|
is_extern: bool,
|
|
is_export: bool,
|
|
is_inline: bool,
|
|
is_var_args: bool,
|
|
name: ?[]const u8,
|
|
linksection_string: ?[]const u8,
|
|
explicit_callconv: ?std.builtin.CallingConvention,
|
|
params: []Param,
|
|
return_type: Node,
|
|
body: ?Node,
|
|
alignment: ?c_uint,
|
|
},
|
|
};
|
|
|
|
pub const Param = struct {
|
|
is_noalias: bool,
|
|
name: ?[]const u8,
|
|
type: Node,
|
|
};
|
|
|
|
pub const Record = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
layout: enum { @"packed", @"extern", none },
|
|
fields: []Field,
|
|
functions: []Node,
|
|
variables: []Node,
|
|
},
|
|
|
|
pub const Field = struct {
|
|
name: []const u8,
|
|
type: Node,
|
|
alignment: ?c_uint,
|
|
default_value: ?Node,
|
|
};
|
|
};
|
|
|
|
pub const TupleInit = struct {
|
|
base: Payload,
|
|
data: []Node,
|
|
};
|
|
|
|
pub const ContainerInit = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
lhs: Node,
|
|
inits: []Initializer,
|
|
},
|
|
|
|
pub const Initializer = struct {
|
|
name: []const u8,
|
|
value: Node,
|
|
};
|
|
};
|
|
|
|
pub const ContainerInitDot = struct {
|
|
base: Payload,
|
|
data: []Initializer,
|
|
|
|
pub const Initializer = struct {
|
|
name: []const u8,
|
|
value: Node,
|
|
};
|
|
};
|
|
|
|
pub const Block = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
label: ?[]const u8,
|
|
stmts: []Node,
|
|
},
|
|
};
|
|
|
|
pub const Array = struct {
|
|
base: Payload,
|
|
data: ArrayTypeInfo,
|
|
|
|
pub const ArrayTypeInfo = struct {
|
|
elem_type: Node,
|
|
len: usize,
|
|
};
|
|
};
|
|
|
|
pub const Pointer = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
elem_type: Node,
|
|
is_const: bool,
|
|
is_volatile: bool,
|
|
},
|
|
};
|
|
|
|
pub const ArgRedecl = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
actual: []const u8,
|
|
mangled: []const u8,
|
|
},
|
|
};
|
|
|
|
pub const SimpleVarDecl = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
name: []const u8,
|
|
init: Node,
|
|
},
|
|
};
|
|
|
|
pub const EnumConstant = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
name: []const u8,
|
|
is_public: bool,
|
|
type: ?Node,
|
|
value: Node,
|
|
},
|
|
};
|
|
|
|
pub const ArrayFiller = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
type: Node,
|
|
filler: Node,
|
|
count: usize,
|
|
},
|
|
};
|
|
|
|
pub const PubInlineFn = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
name: []const u8,
|
|
params: []Param,
|
|
return_type: Node,
|
|
body: Node,
|
|
},
|
|
};
|
|
|
|
pub const FieldAccess = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
lhs: Node,
|
|
field_name: []const u8,
|
|
},
|
|
};
|
|
|
|
pub const PromoteIntLiteral = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
value: Node,
|
|
type: Node,
|
|
base: Node,
|
|
},
|
|
};
|
|
|
|
pub const StringSlice = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
string: Node,
|
|
end: usize,
|
|
},
|
|
};
|
|
|
|
pub const Shuffle = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
element_type: Node,
|
|
a: Node,
|
|
b: Node,
|
|
mask_vector: Node,
|
|
},
|
|
};
|
|
|
|
pub const Extern = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
type: Node,
|
|
name: Node,
|
|
},
|
|
};
|
|
|
|
pub const MacroArithmetic = struct {
|
|
base: Payload,
|
|
data: struct {
|
|
op: Operator,
|
|
lhs: Node,
|
|
rhs: Node,
|
|
},
|
|
|
|
pub const Operator = enum { div, rem };
|
|
};
|
|
};
|
|
|
|
/// Converts the nodes into a Zig Ast.
|
|
/// Caller must free the source slice.
|
|
pub fn render(gpa: Allocator, nodes: []const Node) !std.zig.Ast {
|
|
var ctx = Context{
|
|
.gpa = gpa,
|
|
.buf = std.ArrayList(u8).init(gpa),
|
|
};
|
|
defer ctx.buf.deinit();
|
|
defer ctx.nodes.deinit(gpa);
|
|
defer ctx.extra_data.deinit(gpa);
|
|
defer ctx.tokens.deinit(gpa);
|
|
|
|
// Estimate that each top level node has 10 child nodes.
|
|
const estimated_node_count = nodes.len * 10;
|
|
try ctx.nodes.ensureTotalCapacity(gpa, estimated_node_count);
|
|
// Estimate that each each node has 2 tokens.
|
|
const estimated_tokens_count = estimated_node_count * 2;
|
|
try ctx.tokens.ensureTotalCapacity(gpa, estimated_tokens_count);
|
|
// Estimate that each each token is 3 bytes long.
|
|
const estimated_buf_len = estimated_tokens_count * 3;
|
|
try ctx.buf.ensureTotalCapacity(estimated_buf_len);
|
|
|
|
ctx.nodes.appendAssumeCapacity(.{
|
|
.tag = .root,
|
|
.main_token = 0,
|
|
.data = .{
|
|
.lhs = undefined,
|
|
.rhs = undefined,
|
|
},
|
|
});
|
|
|
|
const root_members = blk: {
|
|
var result = std.ArrayList(NodeIndex).init(gpa);
|
|
defer result.deinit();
|
|
|
|
for (nodes) |node| {
|
|
const res = try renderNode(&ctx, node);
|
|
if (node.tag() == .warning) continue;
|
|
try result.append(res);
|
|
}
|
|
break :blk try ctx.listToSpan(result.items);
|
|
};
|
|
|
|
ctx.nodes.items(.data)[0] = .{
|
|
.lhs = root_members.start,
|
|
.rhs = root_members.end,
|
|
};
|
|
|
|
try ctx.tokens.append(gpa, .{
|
|
.tag = .eof,
|
|
.start = @as(u32, @intCast(ctx.buf.items.len)),
|
|
});
|
|
|
|
return std.zig.Ast{
|
|
.source = try ctx.buf.toOwnedSliceSentinel(0),
|
|
.tokens = ctx.tokens.toOwnedSlice(),
|
|
.nodes = ctx.nodes.toOwnedSlice(),
|
|
.extra_data = try ctx.extra_data.toOwnedSlice(gpa),
|
|
.errors = &.{},
|
|
.mode = .zig,
|
|
};
|
|
}
|
|
|
|
const NodeIndex = std.zig.Ast.Node.Index;
|
|
const NodeSubRange = std.zig.Ast.Node.SubRange;
|
|
const TokenIndex = std.zig.Ast.TokenIndex;
|
|
const TokenTag = std.zig.Token.Tag;
|
|
|
|
const Context = struct {
|
|
gpa: Allocator,
|
|
buf: std.ArrayList(u8),
|
|
nodes: std.zig.Ast.NodeList = .{},
|
|
extra_data: std.ArrayListUnmanaged(std.zig.Ast.Node.Index) = .{},
|
|
tokens: std.zig.Ast.TokenList = .{},
|
|
|
|
fn addTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex {
|
|
const start_index = c.buf.items.len;
|
|
try c.buf.writer().print(format ++ " ", args);
|
|
|
|
try c.tokens.append(c.gpa, .{
|
|
.tag = tag,
|
|
.start = @as(u32, @intCast(start_index)),
|
|
});
|
|
|
|
return @as(u32, @intCast(c.tokens.len - 1));
|
|
}
|
|
|
|
fn addToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex {
|
|
return c.addTokenFmt(tag, "{s}", .{bytes});
|
|
}
|
|
|
|
fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
|
|
if (std.zig.primitives.isPrimitive(bytes))
|
|
return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});
|
|
return c.addTokenFmt(.identifier, "{s}", .{std.zig.fmtId(bytes)});
|
|
}
|
|
|
|
fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange {
|
|
try c.extra_data.appendSlice(c.gpa, list);
|
|
return NodeSubRange{
|
|
.start = @as(NodeIndex, @intCast(c.extra_data.items.len - list.len)),
|
|
.end = @as(NodeIndex, @intCast(c.extra_data.items.len)),
|
|
};
|
|
}
|
|
|
|
fn addNode(c: *Context, elem: std.zig.Ast.Node) Allocator.Error!NodeIndex {
|
|
const result = @as(NodeIndex, @intCast(c.nodes.len));
|
|
try c.nodes.append(c.gpa, elem);
|
|
return result;
|
|
}
|
|
|
|
fn addExtra(c: *Context, extra: anytype) Allocator.Error!NodeIndex {
|
|
const fields = std.meta.fields(@TypeOf(extra));
|
|
try c.extra_data.ensureUnusedCapacity(c.gpa, fields.len);
|
|
const result = @as(u32, @intCast(c.extra_data.items.len));
|
|
inline for (fields) |field| {
|
|
comptime std.debug.assert(field.type == NodeIndex);
|
|
c.extra_data.appendAssumeCapacity(@field(extra, field.name));
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
|
|
fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange {
|
|
var result = std.ArrayList(NodeIndex).init(c.gpa);
|
|
defer result.deinit();
|
|
|
|
for (nodes) |node| {
|
|
const res = try renderNode(c, node);
|
|
if (node.tag() == .warning) continue;
|
|
try result.append(res);
|
|
}
|
|
|
|
return try c.listToSpan(result.items);
|
|
}
|
|
|
|
fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
|
|
switch (node.tag()) {
|
|
.declaration => unreachable,
|
|
.warning => {
|
|
const payload = node.castTag(.warning).?.data;
|
|
try c.buf.appendSlice(payload);
|
|
try c.buf.append('\n');
|
|
return @as(NodeIndex, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32'
|
|
},
|
|
.helpers_cast => {
|
|
const payload = node.castTag(.helpers_cast).?.data;
|
|
const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "cast" });
|
|
return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.helpers_promoteIntLiteral => {
|
|
const payload = node.castTag(.helpers_promoteIntLiteral).?.data;
|
|
const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "promoteIntLiteral" });
|
|
return renderCall(c, import_node, &.{ payload.type, payload.value, payload.base });
|
|
},
|
|
.helpers_sizeof => {
|
|
const payload = node.castTag(.helpers_sizeof).?.data;
|
|
const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "sizeof" });
|
|
return renderCall(c, import_node, &.{payload});
|
|
},
|
|
.std_mem_zeroes => {
|
|
const payload = node.castTag(.std_mem_zeroes).?.data;
|
|
const import_node = try renderStdImport(c, &.{ "mem", "zeroes" });
|
|
return renderCall(c, import_node, &.{payload});
|
|
},
|
|
.std_mem_zeroinit => {
|
|
const payload = node.castTag(.std_mem_zeroinit).?.data;
|
|
const import_node = try renderStdImport(c, &.{ "mem", "zeroInit" });
|
|
return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.helpers_flexible_array_type => {
|
|
const payload = node.castTag(.helpers_flexible_array_type).?.data;
|
|
const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "FlexibleArrayType" });
|
|
return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.helpers_shuffle_vector_index => {
|
|
const payload = node.castTag(.helpers_shuffle_vector_index).?.data;
|
|
const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "shuffleVectorIndex" });
|
|
return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.vector => {
|
|
const payload = node.castTag(.vector).?.data;
|
|
return renderBuiltinCall(c, "@Vector", &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.call => {
|
|
const payload = node.castTag(.call).?.data;
|
|
// Cosmetic: avoids an unnecesary address_of on most function calls.
|
|
const lhs = if (payload.lhs.tag() == .fn_identifier)
|
|
try c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addIdentifier(payload.lhs.castTag(.fn_identifier).?.data),
|
|
.data = undefined,
|
|
})
|
|
else
|
|
try renderNodeGrouped(c, payload.lhs);
|
|
return renderCall(c, lhs, payload.args);
|
|
},
|
|
.null_literal => return c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addToken(.identifier, "null"),
|
|
.data = undefined,
|
|
}),
|
|
.undefined_literal => return c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addToken(.identifier, "undefined"),
|
|
.data = undefined,
|
|
}),
|
|
.true_literal => return c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addToken(.identifier, "true"),
|
|
.data = undefined,
|
|
}),
|
|
.false_literal => return c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addToken(.identifier, "false"),
|
|
.data = undefined,
|
|
}),
|
|
.zero_literal => return c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addToken(.number_literal, "0"),
|
|
.data = undefined,
|
|
}),
|
|
.one_literal => return c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addToken(.number_literal, "1"),
|
|
.data = undefined,
|
|
}),
|
|
.void_type => return c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addToken(.identifier, "void"),
|
|
.data = undefined,
|
|
}),
|
|
.noreturn_type => return c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addToken(.identifier, "noreturn"),
|
|
.data = undefined,
|
|
}),
|
|
.@"continue" => return c.addNode(.{
|
|
.tag = .@"continue",
|
|
.main_token = try c.addToken(.keyword_continue, "continue"),
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = undefined,
|
|
},
|
|
}),
|
|
.return_void => return c.addNode(.{
|
|
.tag = .@"return",
|
|
.main_token = try c.addToken(.keyword_return, "return"),
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = undefined,
|
|
},
|
|
}),
|
|
.@"break" => return c.addNode(.{
|
|
.tag = .@"break",
|
|
.main_token = try c.addToken(.keyword_break, "break"),
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = 0,
|
|
},
|
|
}),
|
|
.break_val => {
|
|
const payload = node.castTag(.break_val).?.data;
|
|
const tok = try c.addToken(.keyword_break, "break");
|
|
const break_label = if (payload.label) |some| blk: {
|
|
_ = try c.addToken(.colon, ":");
|
|
break :blk try c.addIdentifier(some);
|
|
} else 0;
|
|
return c.addNode(.{
|
|
.tag = .@"break",
|
|
.main_token = tok,
|
|
.data = .{
|
|
.lhs = break_label,
|
|
.rhs = try renderNode(c, payload.val),
|
|
},
|
|
});
|
|
},
|
|
.@"return" => {
|
|
const payload = node.castTag(.@"return").?.data;
|
|
return c.addNode(.{
|
|
.tag = .@"return",
|
|
.main_token = try c.addToken(.keyword_return, "return"),
|
|
.data = .{
|
|
.lhs = try renderNode(c, payload),
|
|
.rhs = undefined,
|
|
},
|
|
});
|
|
},
|
|
.@"comptime" => {
|
|
const payload = node.castTag(.@"comptime").?.data;
|
|
return c.addNode(.{
|
|
.tag = .@"comptime",
|
|
.main_token = try c.addToken(.keyword_comptime, "comptime"),
|
|
.data = .{
|
|
.lhs = try renderNode(c, payload),
|
|
.rhs = undefined,
|
|
},
|
|
});
|
|
},
|
|
.@"defer" => {
|
|
const payload = node.castTag(.@"defer").?.data;
|
|
return c.addNode(.{
|
|
.tag = .@"defer",
|
|
.main_token = try c.addToken(.keyword_defer, "defer"),
|
|
.data = .{
|
|
.lhs = undefined,
|
|
.rhs = try renderNode(c, payload),
|
|
},
|
|
});
|
|
},
|
|
.asm_simple => {
|
|
const payload = node.castTag(.asm_simple).?.data;
|
|
const asm_token = try c.addToken(.keyword_asm, "asm");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
return c.addNode(.{
|
|
.tag = .asm_simple,
|
|
.main_token = asm_token,
|
|
.data = .{
|
|
.lhs = try renderNode(c, payload),
|
|
.rhs = try c.addToken(.r_paren, ")"),
|
|
},
|
|
});
|
|
},
|
|
.type => {
|
|
const payload = node.castTag(.type).?.data;
|
|
return c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addToken(.identifier, payload),
|
|
.data = undefined,
|
|
});
|
|
},
|
|
.identifier => {
|
|
const payload = node.castTag(.identifier).?.data;
|
|
return c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addIdentifier(payload),
|
|
.data = undefined,
|
|
});
|
|
},
|
|
.fn_identifier => {
|
|
// C semantics are that a function identifier has address
|
|
// value (implicit in stage1, explicit in stage2), except in
|
|
// the context of an address_of, which is handled there.
|
|
const payload = node.castTag(.fn_identifier).?.data;
|
|
const tok = try c.addToken(.ampersand, "&");
|
|
const arg = try c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addIdentifier(payload),
|
|
.data = undefined,
|
|
});
|
|
return c.addNode(.{
|
|
.tag = .address_of,
|
|
.main_token = tok,
|
|
.data = .{
|
|
.lhs = arg,
|
|
.rhs = undefined,
|
|
},
|
|
});
|
|
},
|
|
.float_literal => {
|
|
const payload = node.castTag(.float_literal).?.data;
|
|
return c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addToken(.number_literal, payload),
|
|
.data = undefined,
|
|
});
|
|
},
|
|
.integer_literal => {
|
|
const payload = node.castTag(.integer_literal).?.data;
|
|
return c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addToken(.number_literal, payload),
|
|
.data = undefined,
|
|
});
|
|
},
|
|
.string_literal => {
|
|
const payload = node.castTag(.string_literal).?.data;
|
|
return c.addNode(.{
|
|
.tag = .string_literal,
|
|
.main_token = try c.addToken(.string_literal, payload),
|
|
.data = undefined,
|
|
});
|
|
},
|
|
.char_literal => {
|
|
const payload = node.castTag(.char_literal).?.data;
|
|
return c.addNode(.{
|
|
.tag = .char_literal,
|
|
.main_token = try c.addToken(.char_literal, payload),
|
|
.data = undefined,
|
|
});
|
|
},
|
|
.enum_literal => {
|
|
const payload = node.castTag(.enum_literal).?.data;
|
|
_ = try c.addToken(.period, ".");
|
|
return c.addNode(.{
|
|
.tag = .enum_literal,
|
|
.main_token = try c.addToken(.identifier, payload),
|
|
.data = undefined,
|
|
});
|
|
},
|
|
.helpers_macro => {
|
|
const payload = node.castTag(.helpers_macro).?.data;
|
|
const chain = [_][]const u8{
|
|
"zig",
|
|
"c_translation",
|
|
"Macros",
|
|
payload,
|
|
};
|
|
return renderStdImport(c, &chain);
|
|
},
|
|
.import_c_builtin => {
|
|
const payload = node.castTag(.import_c_builtin).?.data;
|
|
const chain = [_][]const u8{
|
|
"zig",
|
|
"c_builtins",
|
|
payload,
|
|
};
|
|
return renderStdImport(c, &chain);
|
|
},
|
|
.string_slice => {
|
|
const payload = node.castTag(.string_slice).?.data;
|
|
|
|
const string = try renderNode(c, payload.string);
|
|
const l_bracket = try c.addToken(.l_bracket, "[");
|
|
const start = try c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addToken(.number_literal, "0"),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.ellipsis2, "..");
|
|
const end = try c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addTokenFmt(.number_literal, "{d}", .{payload.end}),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_bracket, "]");
|
|
|
|
return c.addNode(.{
|
|
.tag = .slice,
|
|
.main_token = l_bracket,
|
|
.data = .{
|
|
.lhs = string,
|
|
.rhs = try c.addExtra(std.zig.Ast.Node.Slice{
|
|
.start = start,
|
|
.end = end,
|
|
}),
|
|
},
|
|
});
|
|
},
|
|
.fail_decl => {
|
|
const payload = node.castTag(.fail_decl).?.data;
|
|
// pub const name = @compileError(msg);
|
|
_ = try c.addToken(.keyword_pub, "pub");
|
|
const const_tok = try c.addToken(.keyword_const, "const");
|
|
_ = try c.addIdentifier(payload.actual);
|
|
_ = try c.addToken(.equal, "=");
|
|
|
|
const compile_error_tok = try c.addToken(.builtin, "@compileError");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(payload.mangled)});
|
|
const err_msg = try c.addNode(.{
|
|
.tag = .string_literal,
|
|
.main_token = err_msg_tok,
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_paren, ")");
|
|
const compile_error = try c.addNode(.{
|
|
.tag = .builtin_call_two,
|
|
.main_token = compile_error_tok,
|
|
.data = .{
|
|
.lhs = err_msg,
|
|
.rhs = 0,
|
|
},
|
|
});
|
|
_ = try c.addToken(.semicolon, ";");
|
|
|
|
return c.addNode(.{
|
|
.tag = .simple_var_decl,
|
|
.main_token = const_tok,
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = compile_error,
|
|
},
|
|
});
|
|
},
|
|
.pub_var_simple, .var_simple => {
|
|
const payload = @fieldParentPtr(Payload.SimpleVarDecl, "base", node.ptr_otherwise).data;
|
|
if (node.tag() == .pub_var_simple) _ = try c.addToken(.keyword_pub, "pub");
|
|
const const_tok = try c.addToken(.keyword_const, "const");
|
|
_ = try c.addIdentifier(payload.name);
|
|
_ = try c.addToken(.equal, "=");
|
|
|
|
const init = try renderNode(c, payload.init);
|
|
_ = try c.addToken(.semicolon, ";");
|
|
|
|
return c.addNode(.{
|
|
.tag = .simple_var_decl,
|
|
.main_token = const_tok,
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = init,
|
|
},
|
|
});
|
|
},
|
|
.static_local_var => {
|
|
const payload = node.castTag(.static_local_var).?.data;
|
|
|
|
const const_tok = try c.addToken(.keyword_const, "const");
|
|
_ = try c.addIdentifier(payload.name);
|
|
_ = try c.addToken(.equal, "=");
|
|
|
|
const kind_tok = try c.addToken(.keyword_struct, "struct");
|
|
_ = try c.addToken(.l_brace, "{");
|
|
|
|
const container_def = try c.addNode(.{
|
|
.tag = .container_decl_two_trailing,
|
|
.main_token = kind_tok,
|
|
.data = .{
|
|
.lhs = try renderNode(c, payload.init),
|
|
.rhs = 0,
|
|
},
|
|
});
|
|
_ = try c.addToken(.r_brace, "}");
|
|
_ = try c.addToken(.semicolon, ";");
|
|
|
|
return c.addNode(.{
|
|
.tag = .simple_var_decl,
|
|
.main_token = const_tok,
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = container_def,
|
|
},
|
|
});
|
|
},
|
|
.mut_str => {
|
|
const payload = node.castTag(.mut_str).?.data;
|
|
|
|
const var_tok = try c.addToken(.keyword_var, "var");
|
|
_ = try c.addIdentifier(payload.name);
|
|
_ = try c.addToken(.equal, "=");
|
|
|
|
const deref = try c.addNode(.{
|
|
.tag = .deref,
|
|
.data = .{
|
|
.lhs = try renderNodeGrouped(c, payload.init),
|
|
.rhs = undefined,
|
|
},
|
|
.main_token = try c.addToken(.period_asterisk, ".*"),
|
|
});
|
|
_ = try c.addToken(.semicolon, ";");
|
|
|
|
return c.addNode(.{
|
|
.tag = .simple_var_decl,
|
|
.main_token = var_tok,
|
|
.data = .{ .lhs = 0, .rhs = deref },
|
|
});
|
|
},
|
|
.var_decl => return renderVar(c, node),
|
|
.arg_redecl, .alias => {
|
|
const payload = @fieldParentPtr(Payload.ArgRedecl, "base", node.ptr_otherwise).data;
|
|
if (node.tag() == .alias) _ = try c.addToken(.keyword_pub, "pub");
|
|
const mut_tok = if (node.tag() == .alias)
|
|
try c.addToken(.keyword_const, "const")
|
|
else
|
|
try c.addToken(.keyword_var, "var");
|
|
_ = try c.addIdentifier(payload.actual);
|
|
_ = try c.addToken(.equal, "=");
|
|
|
|
const init = try c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addIdentifier(payload.mangled),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.semicolon, ";");
|
|
|
|
return c.addNode(.{
|
|
.tag = .simple_var_decl,
|
|
.main_token = mut_tok,
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = init,
|
|
},
|
|
});
|
|
},
|
|
.int_cast => {
|
|
const payload = node.castTag(.int_cast).?.data;
|
|
return renderBuiltinCall(c, "@intCast", &.{payload});
|
|
},
|
|
.const_cast => {
|
|
const payload = node.castTag(.const_cast).?.data;
|
|
return renderBuiltinCall(c, "@constCast", &.{payload});
|
|
},
|
|
.volatile_cast => {
|
|
const payload = node.castTag(.volatile_cast).?.data;
|
|
return renderBuiltinCall(c, "@volatileCast", &.{payload});
|
|
},
|
|
.signed_remainder => {
|
|
const payload = node.castTag(.signed_remainder).?.data;
|
|
const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "signedRemainder" });
|
|
return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.div_trunc => {
|
|
const payload = node.castTag(.div_trunc).?.data;
|
|
return renderBuiltinCall(c, "@divTrunc", &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.int_from_bool => {
|
|
const payload = node.castTag(.int_from_bool).?.data;
|
|
return renderBuiltinCall(c, "@intFromBool", &.{payload});
|
|
},
|
|
.as => {
|
|
const payload = node.castTag(.as).?.data;
|
|
return renderBuiltinCall(c, "@as", &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.truncate => {
|
|
const payload = node.castTag(.truncate).?.data;
|
|
return renderBuiltinCall(c, "@truncate", &.{payload});
|
|
},
|
|
.bit_cast => {
|
|
const payload = node.castTag(.bit_cast).?.data;
|
|
return renderBuiltinCall(c, "@bitCast", &.{payload});
|
|
},
|
|
.float_cast => {
|
|
const payload = node.castTag(.float_cast).?.data;
|
|
return renderBuiltinCall(c, "@floatCast", &.{payload});
|
|
},
|
|
.int_from_float => {
|
|
const payload = node.castTag(.int_from_float).?.data;
|
|
return renderBuiltinCall(c, "@intFromFloat", &.{payload});
|
|
},
|
|
.float_from_int => {
|
|
const payload = node.castTag(.float_from_int).?.data;
|
|
return renderBuiltinCall(c, "@floatFromInt", &.{payload});
|
|
},
|
|
.ptr_from_int => {
|
|
const payload = node.castTag(.ptr_from_int).?.data;
|
|
return renderBuiltinCall(c, "@ptrFromInt", &.{payload});
|
|
},
|
|
.int_from_ptr => {
|
|
const payload = node.castTag(.int_from_ptr).?.data;
|
|
return renderBuiltinCall(c, "@intFromPtr", &.{payload});
|
|
},
|
|
.align_cast => {
|
|
const payload = node.castTag(.align_cast).?.data;
|
|
return renderBuiltinCall(c, "@alignCast", &.{payload});
|
|
},
|
|
.ptr_cast => {
|
|
const payload = node.castTag(.ptr_cast).?.data;
|
|
return renderBuiltinCall(c, "@ptrCast", &.{payload});
|
|
},
|
|
.div_exact => {
|
|
const payload = node.castTag(.div_exact).?.data;
|
|
return renderBuiltinCall(c, "@divExact", &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.offset_of => {
|
|
const payload = node.castTag(.offset_of).?.data;
|
|
return renderBuiltinCall(c, "@offsetOf", &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.sizeof => {
|
|
const payload = node.castTag(.sizeof).?.data;
|
|
return renderBuiltinCall(c, "@sizeOf", &.{payload});
|
|
},
|
|
.shuffle => {
|
|
const payload = node.castTag(.shuffle).?.data;
|
|
return renderBuiltinCall(c, "@shuffle", &.{
|
|
payload.element_type,
|
|
payload.a,
|
|
payload.b,
|
|
payload.mask_vector,
|
|
});
|
|
},
|
|
.builtin_extern => {
|
|
const payload = node.castTag(.builtin_extern).?.data;
|
|
|
|
var info_inits: [1]Payload.ContainerInitDot.Initializer = .{
|
|
.{ .name = "name", .value = payload.name },
|
|
};
|
|
var info_payload: Payload.ContainerInitDot = .{
|
|
.base = .{ .tag = .container_init_dot },
|
|
.data = &info_inits,
|
|
};
|
|
|
|
return renderBuiltinCall(c, "@extern", &.{
|
|
payload.type,
|
|
.{ .ptr_otherwise = &info_payload.base },
|
|
});
|
|
},
|
|
.macro_arithmetic => {
|
|
const payload = node.castTag(.macro_arithmetic).?.data;
|
|
const op = @tagName(payload.op);
|
|
const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "MacroArithmetic", op });
|
|
return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
|
|
},
|
|
.alignof => {
|
|
const payload = node.castTag(.alignof).?.data;
|
|
return renderBuiltinCall(c, "@alignOf", &.{payload});
|
|
},
|
|
.typeof => {
|
|
const payload = node.castTag(.typeof).?.data;
|
|
return renderBuiltinCall(c, "@TypeOf", &.{payload});
|
|
},
|
|
.typeinfo => {
|
|
const payload = node.castTag(.typeinfo).?.data;
|
|
return renderBuiltinCall(c, "@typeInfo", &.{payload});
|
|
},
|
|
.negate => return renderPrefixOp(c, node, .negation, .minus, "-"),
|
|
.negate_wrap => return renderPrefixOp(c, node, .negation_wrap, .minus_percent, "-%"),
|
|
.bit_not => return renderPrefixOp(c, node, .bit_not, .tilde, "~"),
|
|
.not => return renderPrefixOp(c, node, .bool_not, .bang, "!"),
|
|
.optional_type => return renderPrefixOp(c, node, .optional_type, .question_mark, "?"),
|
|
.address_of => {
|
|
const payload = node.castTag(.address_of).?.data;
|
|
|
|
const ampersand = try c.addToken(.ampersand, "&");
|
|
const base = if (payload.tag() == .fn_identifier)
|
|
try c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addIdentifier(payload.castTag(.fn_identifier).?.data),
|
|
.data = undefined,
|
|
})
|
|
else
|
|
try renderNodeGrouped(c, payload);
|
|
return c.addNode(.{
|
|
.tag = .address_of,
|
|
.main_token = ampersand,
|
|
.data = .{
|
|
.lhs = base,
|
|
.rhs = undefined,
|
|
},
|
|
});
|
|
},
|
|
.deref => {
|
|
const payload = node.castTag(.deref).?.data;
|
|
const operand = try renderNodeGrouped(c, payload);
|
|
const deref_tok = try c.addToken(.period_asterisk, ".*");
|
|
return c.addNode(.{
|
|
.tag = .deref,
|
|
.main_token = deref_tok,
|
|
.data = .{
|
|
.lhs = operand,
|
|
.rhs = undefined,
|
|
},
|
|
});
|
|
},
|
|
.unwrap => {
|
|
const payload = node.castTag(.unwrap).?.data;
|
|
const operand = try renderNodeGrouped(c, payload);
|
|
const period = try c.addToken(.period, ".");
|
|
const question_mark = try c.addToken(.question_mark, "?");
|
|
return c.addNode(.{
|
|
.tag = .unwrap_optional,
|
|
.main_token = period,
|
|
.data = .{
|
|
.lhs = operand,
|
|
.rhs = question_mark,
|
|
},
|
|
});
|
|
},
|
|
.c_pointer, .single_pointer => {
|
|
const payload = @fieldParentPtr(Payload.Pointer, "base", node.ptr_otherwise).data;
|
|
|
|
const asterisk = if (node.tag() == .single_pointer)
|
|
try c.addToken(.asterisk, "*")
|
|
else blk: {
|
|
_ = try c.addToken(.l_bracket, "[");
|
|
const res = try c.addToken(.asterisk, "*");
|
|
_ = try c.addIdentifier("c");
|
|
_ = try c.addToken(.r_bracket, "]");
|
|
break :blk res;
|
|
};
|
|
if (payload.is_const) _ = try c.addToken(.keyword_const, "const");
|
|
if (payload.is_volatile) _ = try c.addToken(.keyword_volatile, "volatile");
|
|
const elem_type = try renderNodeGrouped(c, payload.elem_type);
|
|
|
|
return c.addNode(.{
|
|
.tag = .ptr_type_aligned,
|
|
.main_token = asterisk,
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = elem_type,
|
|
},
|
|
});
|
|
},
|
|
.add => return renderBinOpGrouped(c, node, .add, .plus, "+"),
|
|
.add_assign => return renderBinOp(c, node, .assign_add, .plus_equal, "+="),
|
|
.add_wrap => return renderBinOpGrouped(c, node, .add_wrap, .plus_percent, "+%"),
|
|
.add_wrap_assign => return renderBinOp(c, node, .assign_add_wrap, .plus_percent_equal, "+%="),
|
|
.sub => return renderBinOpGrouped(c, node, .sub, .minus, "-"),
|
|
.sub_assign => return renderBinOp(c, node, .assign_sub, .minus_equal, "-="),
|
|
.sub_wrap => return renderBinOpGrouped(c, node, .sub_wrap, .minus_percent, "-%"),
|
|
.sub_wrap_assign => return renderBinOp(c, node, .assign_sub_wrap, .minus_percent_equal, "-%="),
|
|
.mul => return renderBinOpGrouped(c, node, .mul, .asterisk, "*"),
|
|
.mul_assign => return renderBinOp(c, node, .assign_mul, .asterisk_equal, "*="),
|
|
.mul_wrap => return renderBinOpGrouped(c, node, .mul_wrap, .asterisk_percent, "*%"),
|
|
.mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
|
|
.div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
|
|
.div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),
|
|
.shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"),
|
|
.shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="),
|
|
.shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"),
|
|
.shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="),
|
|
.mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
|
|
.mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
|
|
.@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
|
|
.@"or" => return renderBinOpGrouped(c, node, .bool_or, .keyword_or, "or"),
|
|
.less_than => return renderBinOpGrouped(c, node, .less_than, .angle_bracket_left, "<"),
|
|
.less_than_equal => return renderBinOpGrouped(c, node, .less_or_equal, .angle_bracket_left_equal, "<="),
|
|
.greater_than => return renderBinOpGrouped(c, node, .greater_than, .angle_bracket_right, ">="),
|
|
.greater_than_equal => return renderBinOpGrouped(c, node, .greater_or_equal, .angle_bracket_right_equal, ">="),
|
|
.equal => return renderBinOpGrouped(c, node, .equal_equal, .equal_equal, "=="),
|
|
.not_equal => return renderBinOpGrouped(c, node, .bang_equal, .bang_equal, "!="),
|
|
.bit_and => return renderBinOpGrouped(c, node, .bit_and, .ampersand, "&"),
|
|
.bit_and_assign => return renderBinOp(c, node, .assign_bit_and, .ampersand_equal, "&="),
|
|
.bit_or => return renderBinOpGrouped(c, node, .bit_or, .pipe, "|"),
|
|
.bit_or_assign => return renderBinOp(c, node, .assign_bit_or, .pipe_equal, "|="),
|
|
.bit_xor => return renderBinOpGrouped(c, node, .bit_xor, .caret, "^"),
|
|
.bit_xor_assign => return renderBinOp(c, node, .assign_bit_xor, .caret_equal, "^="),
|
|
.array_cat => return renderBinOp(c, node, .array_cat, .plus_plus, "++"),
|
|
.ellipsis3 => return renderBinOpGrouped(c, node, .switch_range, .ellipsis3, "..."),
|
|
.assign => return renderBinOp(c, node, .assign, .equal, "="),
|
|
.empty_block => {
|
|
const l_brace = try c.addToken(.l_brace, "{");
|
|
_ = try c.addToken(.r_brace, "}");
|
|
return c.addNode(.{
|
|
.tag = .block_two,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = 0,
|
|
},
|
|
});
|
|
},
|
|
.block_single => {
|
|
const payload = node.castTag(.block_single).?.data;
|
|
const l_brace = try c.addToken(.l_brace, "{");
|
|
|
|
const stmt = try renderNode(c, payload);
|
|
try addSemicolonIfNeeded(c, payload);
|
|
|
|
_ = try c.addToken(.r_brace, "}");
|
|
return c.addNode(.{
|
|
.tag = .block_two_semicolon,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = stmt,
|
|
.rhs = 0,
|
|
},
|
|
});
|
|
},
|
|
.block => {
|
|
const payload = node.castTag(.block).?.data;
|
|
if (payload.label) |some| {
|
|
_ = try c.addIdentifier(some);
|
|
_ = try c.addToken(.colon, ":");
|
|
}
|
|
const l_brace = try c.addToken(.l_brace, "{");
|
|
|
|
var stmts = std.ArrayList(NodeIndex).init(c.gpa);
|
|
defer stmts.deinit();
|
|
for (payload.stmts) |stmt| {
|
|
const res = try renderNode(c, stmt);
|
|
if (res == 0) continue;
|
|
try addSemicolonIfNeeded(c, stmt);
|
|
try stmts.append(res);
|
|
}
|
|
const span = try c.listToSpan(stmts.items);
|
|
_ = try c.addToken(.r_brace, "}");
|
|
|
|
const semicolon = c.tokens.items(.tag)[c.tokens.len - 2] == .semicolon;
|
|
return c.addNode(.{
|
|
.tag = if (semicolon) .block_semicolon else .block,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = span.start,
|
|
.rhs = span.end,
|
|
},
|
|
});
|
|
},
|
|
.func => return renderFunc(c, node),
|
|
.pub_inline_fn => return renderMacroFunc(c, node),
|
|
.discard => {
|
|
const payload = node.castTag(.discard).?.data;
|
|
if (payload.should_skip) return @as(NodeIndex, 0);
|
|
|
|
const lhs = try c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addToken(.identifier, "_"),
|
|
.data = undefined,
|
|
});
|
|
const main_token = try c.addToken(.equal, "=");
|
|
if (payload.value.tag() == .identifier) {
|
|
// Render as `_ = &foo;` to avoid tripping "pointless discard" and "local variable never mutated" errors.
|
|
var addr_of_pl: Payload.UnOp = .{
|
|
.base = .{ .tag = .address_of },
|
|
.data = payload.value,
|
|
};
|
|
const addr_of: Node = .{ .ptr_otherwise = &addr_of_pl.base };
|
|
return c.addNode(.{
|
|
.tag = .assign,
|
|
.main_token = main_token,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = try renderNode(c, addr_of),
|
|
},
|
|
});
|
|
} else {
|
|
return c.addNode(.{
|
|
.tag = .assign,
|
|
.main_token = main_token,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = try renderNode(c, payload.value),
|
|
},
|
|
});
|
|
}
|
|
},
|
|
.@"while" => {
|
|
const payload = node.castTag(.@"while").?.data;
|
|
const while_tok = try c.addToken(.keyword_while, "while");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const cond = try renderNode(c, payload.cond);
|
|
_ = try c.addToken(.r_paren, ")");
|
|
|
|
const cont_expr = if (payload.cont_expr) |some| blk: {
|
|
_ = try c.addToken(.colon, ":");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const res = try renderNode(c, some);
|
|
_ = try c.addToken(.r_paren, ")");
|
|
break :blk res;
|
|
} else 0;
|
|
const body = try renderNode(c, payload.body);
|
|
|
|
if (cont_expr == 0) {
|
|
return c.addNode(.{
|
|
.tag = .while_simple,
|
|
.main_token = while_tok,
|
|
.data = .{
|
|
.lhs = cond,
|
|
.rhs = body,
|
|
},
|
|
});
|
|
} else {
|
|
return c.addNode(.{
|
|
.tag = .while_cont,
|
|
.main_token = while_tok,
|
|
.data = .{
|
|
.lhs = cond,
|
|
.rhs = try c.addExtra(std.zig.Ast.Node.WhileCont{
|
|
.cont_expr = cont_expr,
|
|
.then_expr = body,
|
|
}),
|
|
},
|
|
});
|
|
}
|
|
},
|
|
.while_true => {
|
|
const payload = node.castTag(.while_true).?.data;
|
|
const while_tok = try c.addToken(.keyword_while, "while");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const cond = try c.addNode(.{
|
|
.tag = .identifier,
|
|
.main_token = try c.addToken(.identifier, "true"),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_paren, ")");
|
|
const body = try renderNode(c, payload);
|
|
|
|
return c.addNode(.{
|
|
.tag = .while_simple,
|
|
.main_token = while_tok,
|
|
.data = .{
|
|
.lhs = cond,
|
|
.rhs = body,
|
|
},
|
|
});
|
|
},
|
|
.@"if" => {
|
|
const payload = node.castTag(.@"if").?.data;
|
|
const if_tok = try c.addToken(.keyword_if, "if");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const cond = try renderNode(c, payload.cond);
|
|
_ = try c.addToken(.r_paren, ")");
|
|
|
|
const then_expr = try renderNode(c, payload.then);
|
|
const else_node = payload.@"else" orelse return c.addNode(.{
|
|
.tag = .if_simple,
|
|
.main_token = if_tok,
|
|
.data = .{
|
|
.lhs = cond,
|
|
.rhs = then_expr,
|
|
},
|
|
});
|
|
_ = try c.addToken(.keyword_else, "else");
|
|
const else_expr = try renderNode(c, else_node);
|
|
|
|
return c.addNode(.{
|
|
.tag = .@"if",
|
|
.main_token = if_tok,
|
|
.data = .{
|
|
.lhs = cond,
|
|
.rhs = try c.addExtra(std.zig.Ast.Node.If{
|
|
.then_expr = then_expr,
|
|
.else_expr = else_expr,
|
|
}),
|
|
},
|
|
});
|
|
},
|
|
.if_not_break => {
|
|
const payload = node.castTag(.if_not_break).?.data;
|
|
const if_tok = try c.addToken(.keyword_if, "if");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const cond = try c.addNode(.{
|
|
.tag = .bool_not,
|
|
.main_token = try c.addToken(.bang, "!"),
|
|
.data = .{
|
|
.lhs = try renderNodeGrouped(c, payload),
|
|
.rhs = undefined,
|
|
},
|
|
});
|
|
_ = try c.addToken(.r_paren, ")");
|
|
const then_expr = try c.addNode(.{
|
|
.tag = .@"break",
|
|
.main_token = try c.addToken(.keyword_break, "break"),
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = 0,
|
|
},
|
|
});
|
|
|
|
return c.addNode(.{
|
|
.tag = .if_simple,
|
|
.main_token = if_tok,
|
|
.data = .{
|
|
.lhs = cond,
|
|
.rhs = then_expr,
|
|
},
|
|
});
|
|
},
|
|
.@"switch" => {
|
|
const payload = node.castTag(.@"switch").?.data;
|
|
const switch_tok = try c.addToken(.keyword_switch, "switch");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const cond = try renderNode(c, payload.cond);
|
|
_ = try c.addToken(.r_paren, ")");
|
|
|
|
_ = try c.addToken(.l_brace, "{");
|
|
var cases = try c.gpa.alloc(NodeIndex, payload.cases.len);
|
|
defer c.gpa.free(cases);
|
|
for (payload.cases, 0..) |case, i| {
|
|
cases[i] = try renderNode(c, case);
|
|
_ = try c.addToken(.comma, ",");
|
|
}
|
|
const span = try c.listToSpan(cases);
|
|
_ = try c.addToken(.r_brace, "}");
|
|
return c.addNode(.{
|
|
.tag = .switch_comma,
|
|
.main_token = switch_tok,
|
|
.data = .{
|
|
.lhs = cond,
|
|
.rhs = try c.addExtra(NodeSubRange{
|
|
.start = span.start,
|
|
.end = span.end,
|
|
}),
|
|
},
|
|
});
|
|
},
|
|
.switch_else => {
|
|
const payload = node.castTag(.switch_else).?.data;
|
|
_ = try c.addToken(.keyword_else, "else");
|
|
return c.addNode(.{
|
|
.tag = .switch_case_one,
|
|
.main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = try renderNode(c, payload),
|
|
},
|
|
});
|
|
},
|
|
.switch_prong => {
|
|
const payload = node.castTag(.switch_prong).?.data;
|
|
var items = try c.gpa.alloc(NodeIndex, @max(payload.cases.len, 1));
|
|
defer c.gpa.free(items);
|
|
items[0] = 0;
|
|
for (payload.cases, 0..) |item, i| {
|
|
if (i != 0) _ = try c.addToken(.comma, ",");
|
|
items[i] = try renderNode(c, item);
|
|
}
|
|
_ = try c.addToken(.r_brace, "}");
|
|
if (items.len < 2) {
|
|
return c.addNode(.{
|
|
.tag = .switch_case_one,
|
|
.main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
|
|
.data = .{
|
|
.lhs = items[0],
|
|
.rhs = try renderNode(c, payload.cond),
|
|
},
|
|
});
|
|
} else {
|
|
const span = try c.listToSpan(items);
|
|
return c.addNode(.{
|
|
.tag = .switch_case,
|
|
.main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
|
|
.data = .{
|
|
.lhs = try c.addExtra(NodeSubRange{
|
|
.start = span.start,
|
|
.end = span.end,
|
|
}),
|
|
.rhs = try renderNode(c, payload.cond),
|
|
},
|
|
});
|
|
}
|
|
},
|
|
.opaque_literal => {
|
|
const opaque_tok = try c.addToken(.keyword_opaque, "opaque");
|
|
_ = try c.addToken(.l_brace, "{");
|
|
_ = try c.addToken(.r_brace, "}");
|
|
|
|
return c.addNode(.{
|
|
.tag = .container_decl_two,
|
|
.main_token = opaque_tok,
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = 0,
|
|
},
|
|
});
|
|
},
|
|
.array_access => {
|
|
const payload = node.castTag(.array_access).?.data;
|
|
const lhs = try renderNodeGrouped(c, payload.lhs);
|
|
const l_bracket = try c.addToken(.l_bracket, "[");
|
|
const index_expr = try renderNode(c, payload.rhs);
|
|
_ = try c.addToken(.r_bracket, "]");
|
|
return c.addNode(.{
|
|
.tag = .array_access,
|
|
.main_token = l_bracket,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = index_expr,
|
|
},
|
|
});
|
|
},
|
|
.array_type => {
|
|
const payload = node.castTag(.array_type).?.data;
|
|
return renderArrayType(c, payload.len, payload.elem_type);
|
|
},
|
|
.null_sentinel_array_type => {
|
|
const payload = node.castTag(.null_sentinel_array_type).?.data;
|
|
return renderNullSentinelArrayType(c, payload.len, payload.elem_type);
|
|
},
|
|
.array_filler => {
|
|
const payload = node.castTag(.array_filler).?.data;
|
|
|
|
const type_expr = try renderArrayType(c, 1, payload.type);
|
|
const l_brace = try c.addToken(.l_brace, "{");
|
|
const val = try renderNode(c, payload.filler);
|
|
_ = try c.addToken(.r_brace, "}");
|
|
|
|
const init = try c.addNode(.{
|
|
.tag = .array_init_one,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = type_expr,
|
|
.rhs = val,
|
|
},
|
|
});
|
|
return c.addNode(.{
|
|
.tag = .array_cat,
|
|
.main_token = try c.addToken(.asterisk_asterisk, "**"),
|
|
.data = .{
|
|
.lhs = init,
|
|
.rhs = try c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addTokenFmt(.number_literal, "{d}", .{payload.count}),
|
|
.data = undefined,
|
|
}),
|
|
},
|
|
});
|
|
},
|
|
.empty_array => {
|
|
const payload = node.castTag(.empty_array).?.data;
|
|
|
|
const type_expr = try renderArrayType(c, 0, payload);
|
|
return renderArrayInit(c, type_expr, &.{});
|
|
},
|
|
.array_init => {
|
|
const payload = node.castTag(.array_init).?.data;
|
|
const type_expr = try renderNode(c, payload.cond);
|
|
return renderArrayInit(c, type_expr, payload.cases);
|
|
},
|
|
.vector_zero_init => {
|
|
const payload = node.castTag(.vector_zero_init).?.data;
|
|
return renderBuiltinCall(c, "@splat", &.{payload});
|
|
},
|
|
.field_access => {
|
|
const payload = node.castTag(.field_access).?.data;
|
|
const lhs = try renderNodeGrouped(c, payload.lhs);
|
|
return renderFieldAccess(c, lhs, payload.field_name);
|
|
},
|
|
.@"struct", .@"union" => return renderRecord(c, node),
|
|
.enum_constant => {
|
|
const payload = node.castTag(.enum_constant).?.data;
|
|
|
|
if (payload.is_public) _ = try c.addToken(.keyword_pub, "pub");
|
|
const const_tok = try c.addToken(.keyword_const, "const");
|
|
_ = try c.addIdentifier(payload.name);
|
|
|
|
const type_node = if (payload.type) |enum_const_type| blk: {
|
|
_ = try c.addToken(.colon, ":");
|
|
break :blk try renderNode(c, enum_const_type);
|
|
} else 0;
|
|
|
|
_ = try c.addToken(.equal, "=");
|
|
|
|
const init_node = try renderNode(c, payload.value);
|
|
_ = try c.addToken(.semicolon, ";");
|
|
|
|
return c.addNode(.{
|
|
.tag = .simple_var_decl,
|
|
.main_token = const_tok,
|
|
.data = .{
|
|
.lhs = type_node,
|
|
.rhs = init_node,
|
|
},
|
|
});
|
|
},
|
|
.tuple => {
|
|
const payload = node.castTag(.tuple).?.data;
|
|
_ = try c.addToken(.period, ".");
|
|
const l_brace = try c.addToken(.l_brace, "{");
|
|
var inits = try c.gpa.alloc(NodeIndex, @max(payload.len, 2));
|
|
defer c.gpa.free(inits);
|
|
inits[0] = 0;
|
|
inits[1] = 0;
|
|
for (payload, 0..) |init, i| {
|
|
if (i != 0) _ = try c.addToken(.comma, ",");
|
|
inits[i] = try renderNode(c, init);
|
|
}
|
|
_ = try c.addToken(.r_brace, "}");
|
|
if (payload.len < 3) {
|
|
return c.addNode(.{
|
|
.tag = .array_init_dot_two,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = inits[0],
|
|
.rhs = inits[1],
|
|
},
|
|
});
|
|
} else {
|
|
const span = try c.listToSpan(inits);
|
|
return c.addNode(.{
|
|
.tag = .array_init_dot,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = span.start,
|
|
.rhs = span.end,
|
|
},
|
|
});
|
|
}
|
|
},
|
|
.container_init_dot => {
|
|
const payload = node.castTag(.container_init_dot).?.data;
|
|
_ = try c.addToken(.period, ".");
|
|
const l_brace = try c.addToken(.l_brace, "{");
|
|
var inits = try c.gpa.alloc(NodeIndex, @max(payload.len, 2));
|
|
defer c.gpa.free(inits);
|
|
inits[0] = 0;
|
|
inits[1] = 0;
|
|
for (payload, 0..) |init, i| {
|
|
_ = try c.addToken(.period, ".");
|
|
_ = try c.addIdentifier(init.name);
|
|
_ = try c.addToken(.equal, "=");
|
|
inits[i] = try renderNode(c, init.value);
|
|
_ = try c.addToken(.comma, ",");
|
|
}
|
|
_ = try c.addToken(.r_brace, "}");
|
|
|
|
if (payload.len < 3) {
|
|
return c.addNode(.{
|
|
.tag = .struct_init_dot_two_comma,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = inits[0],
|
|
.rhs = inits[1],
|
|
},
|
|
});
|
|
} else {
|
|
const span = try c.listToSpan(inits);
|
|
return c.addNode(.{
|
|
.tag = .struct_init_dot_comma,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = span.start,
|
|
.rhs = span.end,
|
|
},
|
|
});
|
|
}
|
|
},
|
|
.container_init => {
|
|
const payload = node.castTag(.container_init).?.data;
|
|
const lhs = try renderNode(c, payload.lhs);
|
|
|
|
const l_brace = try c.addToken(.l_brace, "{");
|
|
var inits = try c.gpa.alloc(NodeIndex, @max(payload.inits.len, 1));
|
|
defer c.gpa.free(inits);
|
|
inits[0] = 0;
|
|
for (payload.inits, 0..) |init, i| {
|
|
_ = try c.addToken(.period, ".");
|
|
_ = try c.addIdentifier(init.name);
|
|
_ = try c.addToken(.equal, "=");
|
|
inits[i] = try renderNode(c, init.value);
|
|
_ = try c.addToken(.comma, ",");
|
|
}
|
|
_ = try c.addToken(.r_brace, "}");
|
|
|
|
return switch (payload.inits.len) {
|
|
0 => c.addNode(.{
|
|
.tag = .struct_init_one,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = 0,
|
|
},
|
|
}),
|
|
1 => c.addNode(.{
|
|
.tag = .struct_init_one_comma,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = inits[0],
|
|
},
|
|
}),
|
|
else => blk: {
|
|
const span = try c.listToSpan(inits);
|
|
break :blk c.addNode(.{
|
|
.tag = .struct_init_comma,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = try c.addExtra(NodeSubRange{
|
|
.start = span.start,
|
|
.end = span.end,
|
|
}),
|
|
},
|
|
});
|
|
},
|
|
};
|
|
},
|
|
.@"anytype" => unreachable, // Handled in renderParams
|
|
}
|
|
}
|
|
|
|
fn renderRecord(c: *Context, node: Node) !NodeIndex {
|
|
const payload = @fieldParentPtr(Payload.Record, "base", node.ptr_otherwise).data;
|
|
if (payload.layout == .@"packed")
|
|
_ = try c.addToken(.keyword_packed, "packed")
|
|
else if (payload.layout == .@"extern")
|
|
_ = try c.addToken(.keyword_extern, "extern");
|
|
const kind_tok = if (node.tag() == .@"struct")
|
|
try c.addToken(.keyword_struct, "struct")
|
|
else
|
|
try c.addToken(.keyword_union, "union");
|
|
|
|
_ = try c.addToken(.l_brace, "{");
|
|
|
|
const num_vars = payload.variables.len;
|
|
const num_funcs = payload.functions.len;
|
|
const total_members = payload.fields.len + num_vars + num_funcs;
|
|
const members = try c.gpa.alloc(NodeIndex, @max(total_members, 2));
|
|
defer c.gpa.free(members);
|
|
members[0] = 0;
|
|
members[1] = 0;
|
|
|
|
for (payload.fields, 0..) |field, i| {
|
|
const name_tok = try c.addTokenFmt(.identifier, "{s}", .{std.zig.fmtId(field.name)});
|
|
_ = try c.addToken(.colon, ":");
|
|
const type_expr = try renderNode(c, field.type);
|
|
|
|
const align_expr = if (field.alignment) |alignment| blk: {
|
|
_ = try c.addToken(.keyword_align, "align");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const align_expr = try c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addTokenFmt(.number_literal, "{d}", .{alignment}),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_paren, ")");
|
|
break :blk align_expr;
|
|
} else 0;
|
|
|
|
const value_expr = if (field.default_value) |value| blk: {
|
|
_ = try c.addToken(.equal, "=");
|
|
break :blk try renderNode(c, value);
|
|
} else 0;
|
|
|
|
members[i] = try c.addNode(if (align_expr == 0) .{
|
|
.tag = .container_field_init,
|
|
.main_token = name_tok,
|
|
.data = .{
|
|
.lhs = type_expr,
|
|
.rhs = value_expr,
|
|
},
|
|
} else if (value_expr == 0) .{
|
|
.tag = .container_field_align,
|
|
.main_token = name_tok,
|
|
.data = .{
|
|
.lhs = type_expr,
|
|
.rhs = align_expr,
|
|
},
|
|
} else .{
|
|
.tag = .container_field,
|
|
.main_token = name_tok,
|
|
.data = .{
|
|
.lhs = type_expr,
|
|
.rhs = try c.addExtra(std.zig.Ast.Node.ContainerField{
|
|
.align_expr = align_expr,
|
|
.value_expr = value_expr,
|
|
}),
|
|
},
|
|
});
|
|
_ = try c.addToken(.comma, ",");
|
|
}
|
|
for (payload.variables, 0..) |variable, i| {
|
|
members[payload.fields.len + i] = try renderNode(c, variable);
|
|
}
|
|
for (payload.functions, 0..) |function, i| {
|
|
members[payload.fields.len + num_vars + i] = try renderNode(c, function);
|
|
}
|
|
_ = try c.addToken(.r_brace, "}");
|
|
|
|
if (total_members == 0) {
|
|
return c.addNode(.{
|
|
.tag = .container_decl_two,
|
|
.main_token = kind_tok,
|
|
.data = .{
|
|
.lhs = 0,
|
|
.rhs = 0,
|
|
},
|
|
});
|
|
} else if (total_members <= 2) {
|
|
return c.addNode(.{
|
|
.tag = if (num_funcs == 0) .container_decl_two_trailing else .container_decl_two,
|
|
.main_token = kind_tok,
|
|
.data = .{
|
|
.lhs = members[0],
|
|
.rhs = members[1],
|
|
},
|
|
});
|
|
} else {
|
|
const span = try c.listToSpan(members);
|
|
return c.addNode(.{
|
|
.tag = if (num_funcs == 0) .container_decl_trailing else .container_decl,
|
|
.main_token = kind_tok,
|
|
.data = .{
|
|
.lhs = span.start,
|
|
.rhs = span.end,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeIndex {
|
|
return c.addNode(.{
|
|
.tag = .field_access,
|
|
.main_token = try c.addToken(.period, "."),
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = try c.addTokenFmt(.identifier, "{s}", .{std.zig.fmtId(field_name)}),
|
|
},
|
|
});
|
|
}
|
|
|
|
fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex {
|
|
const l_brace = try c.addToken(.l_brace, "{");
|
|
var rendered = try c.gpa.alloc(NodeIndex, @max(inits.len, 1));
|
|
defer c.gpa.free(rendered);
|
|
rendered[0] = 0;
|
|
for (inits, 0..) |init, i| {
|
|
rendered[i] = try renderNode(c, init);
|
|
_ = try c.addToken(.comma, ",");
|
|
}
|
|
_ = try c.addToken(.r_brace, "}");
|
|
if (inits.len < 2) {
|
|
return c.addNode(.{
|
|
.tag = .array_init_one_comma,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = rendered[0],
|
|
},
|
|
});
|
|
} else {
|
|
const span = try c.listToSpan(rendered);
|
|
return c.addNode(.{
|
|
.tag = .array_init_comma,
|
|
.main_token = l_brace,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = try c.addExtra(NodeSubRange{
|
|
.start = span.start,
|
|
.end = span.end,
|
|
}),
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
|
|
const l_bracket = try c.addToken(.l_bracket, "[");
|
|
const len_expr = try c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addTokenFmt(.number_literal, "{d}", .{len}),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_bracket, "]");
|
|
const elem_type_expr = try renderNode(c, elem_type);
|
|
return c.addNode(.{
|
|
.tag = .array_type,
|
|
.main_token = l_bracket,
|
|
.data = .{
|
|
.lhs = len_expr,
|
|
.rhs = elem_type_expr,
|
|
},
|
|
});
|
|
}
|
|
|
|
fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
|
|
const l_bracket = try c.addToken(.l_bracket, "[");
|
|
const len_expr = try c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addTokenFmt(.number_literal, "{d}", .{len}),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.colon, ":");
|
|
|
|
const sentinel_expr = try c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addToken(.number_literal, "0"),
|
|
.data = undefined,
|
|
});
|
|
|
|
_ = try c.addToken(.r_bracket, "]");
|
|
const elem_type_expr = try renderNode(c, elem_type);
|
|
return c.addNode(.{
|
|
.tag = .array_type_sentinel,
|
|
.main_token = l_bracket,
|
|
.data = .{
|
|
.lhs = len_expr,
|
|
.rhs = try c.addExtra(std.zig.Ast.Node.ArrayTypeSentinel{
|
|
.sentinel = sentinel_expr,
|
|
.elem_type = elem_type_expr,
|
|
}),
|
|
},
|
|
});
|
|
}
|
|
|
|
fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
|
|
switch (node.tag()) {
|
|
.warning => unreachable,
|
|
.var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .mut_str => {},
|
|
.while_true => {
|
|
const payload = node.castTag(.while_true).?.data;
|
|
return addSemicolonIfNotBlock(c, payload);
|
|
},
|
|
.@"while" => {
|
|
const payload = node.castTag(.@"while").?.data;
|
|
return addSemicolonIfNotBlock(c, payload.body);
|
|
},
|
|
.@"if" => {
|
|
const payload = node.castTag(.@"if").?.data;
|
|
if (payload.@"else") |some|
|
|
return addSemicolonIfNeeded(c, some);
|
|
return addSemicolonIfNotBlock(c, payload.then);
|
|
},
|
|
else => _ = try c.addToken(.semicolon, ";"),
|
|
}
|
|
}
|
|
|
|
fn addSemicolonIfNotBlock(c: *Context, node: Node) !void {
|
|
switch (node.tag()) {
|
|
.block, .empty_block, .block_single => {},
|
|
else => _ = try c.addToken(.semicolon, ";"),
|
|
}
|
|
}
|
|
|
|
fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
|
|
switch (node.tag()) {
|
|
.declaration => unreachable,
|
|
.null_literal,
|
|
.undefined_literal,
|
|
.true_literal,
|
|
.false_literal,
|
|
.return_void,
|
|
.zero_literal,
|
|
.one_literal,
|
|
.void_type,
|
|
.noreturn_type,
|
|
.@"anytype",
|
|
.div_trunc,
|
|
.signed_remainder,
|
|
.int_cast,
|
|
.const_cast,
|
|
.volatile_cast,
|
|
.as,
|
|
.truncate,
|
|
.bit_cast,
|
|
.float_cast,
|
|
.int_from_float,
|
|
.float_from_int,
|
|
.ptr_from_int,
|
|
.std_mem_zeroes,
|
|
.int_from_ptr,
|
|
.sizeof,
|
|
.alignof,
|
|
.typeof,
|
|
.typeinfo,
|
|
.vector,
|
|
.helpers_sizeof,
|
|
.helpers_cast,
|
|
.helpers_promoteIntLiteral,
|
|
.helpers_shuffle_vector_index,
|
|
.helpers_flexible_array_type,
|
|
.std_mem_zeroinit,
|
|
.integer_literal,
|
|
.float_literal,
|
|
.string_literal,
|
|
.string_slice,
|
|
.char_literal,
|
|
.enum_literal,
|
|
.identifier,
|
|
.fn_identifier,
|
|
.field_access,
|
|
.ptr_cast,
|
|
.type,
|
|
.array_access,
|
|
.align_cast,
|
|
.optional_type,
|
|
.c_pointer,
|
|
.single_pointer,
|
|
.unwrap,
|
|
.deref,
|
|
.not,
|
|
.negate,
|
|
.negate_wrap,
|
|
.bit_not,
|
|
.func,
|
|
.call,
|
|
.array_type,
|
|
.null_sentinel_array_type,
|
|
.int_from_bool,
|
|
.div_exact,
|
|
.offset_of,
|
|
.shuffle,
|
|
.builtin_extern,
|
|
.static_local_var,
|
|
.mut_str,
|
|
.macro_arithmetic,
|
|
=> {
|
|
// no grouping needed
|
|
return renderNode(c, node);
|
|
},
|
|
|
|
.opaque_literal,
|
|
.empty_array,
|
|
.block_single,
|
|
.add,
|
|
.add_wrap,
|
|
.sub,
|
|
.sub_wrap,
|
|
.mul,
|
|
.mul_wrap,
|
|
.div,
|
|
.shl,
|
|
.shr,
|
|
.mod,
|
|
.@"and",
|
|
.@"or",
|
|
.less_than,
|
|
.less_than_equal,
|
|
.greater_than,
|
|
.greater_than_equal,
|
|
.equal,
|
|
.not_equal,
|
|
.bit_and,
|
|
.bit_or,
|
|
.bit_xor,
|
|
.empty_block,
|
|
.array_cat,
|
|
.array_filler,
|
|
.@"if",
|
|
.@"struct",
|
|
.@"union",
|
|
.array_init,
|
|
.vector_zero_init,
|
|
.tuple,
|
|
.container_init,
|
|
.container_init_dot,
|
|
.block,
|
|
.address_of,
|
|
=> return c.addNode(.{
|
|
.tag = .grouped_expression,
|
|
.main_token = try c.addToken(.l_paren, "("),
|
|
.data = .{
|
|
.lhs = try renderNode(c, node),
|
|
.rhs = try c.addToken(.r_paren, ")"),
|
|
},
|
|
}),
|
|
.ellipsis3,
|
|
.switch_prong,
|
|
.warning,
|
|
.var_decl,
|
|
.fail_decl,
|
|
.arg_redecl,
|
|
.alias,
|
|
.var_simple,
|
|
.pub_var_simple,
|
|
.enum_constant,
|
|
.@"while",
|
|
.@"switch",
|
|
.@"break",
|
|
.break_val,
|
|
.pub_inline_fn,
|
|
.discard,
|
|
.@"continue",
|
|
.@"return",
|
|
.@"comptime",
|
|
.@"defer",
|
|
.asm_simple,
|
|
.while_true,
|
|
.if_not_break,
|
|
.switch_else,
|
|
.add_assign,
|
|
.add_wrap_assign,
|
|
.sub_assign,
|
|
.sub_wrap_assign,
|
|
.mul_assign,
|
|
.mul_wrap_assign,
|
|
.div_assign,
|
|
.shl_assign,
|
|
.shr_assign,
|
|
.mod_assign,
|
|
.bit_and_assign,
|
|
.bit_or_assign,
|
|
.bit_xor_assign,
|
|
.assign,
|
|
.helpers_macro,
|
|
.import_c_builtin,
|
|
=> {
|
|
// these should never appear in places where grouping might be needed.
|
|
unreachable;
|
|
},
|
|
}
|
|
}
|
|
|
|
fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
|
|
const payload = @fieldParentPtr(Payload.UnOp, "base", node.ptr_otherwise).data;
|
|
return c.addNode(.{
|
|
.tag = tag,
|
|
.main_token = try c.addToken(tok_tag, bytes),
|
|
.data = .{
|
|
.lhs = try renderNodeGrouped(c, payload),
|
|
.rhs = undefined,
|
|
},
|
|
});
|
|
}
|
|
|
|
fn renderBinOpGrouped(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
|
|
const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data;
|
|
const lhs = try renderNodeGrouped(c, payload.lhs);
|
|
return c.addNode(.{
|
|
.tag = tag,
|
|
.main_token = try c.addToken(tok_tag, bytes),
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = try renderNodeGrouped(c, payload.rhs),
|
|
},
|
|
});
|
|
}
|
|
|
|
fn renderBinOp(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
|
|
const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data;
|
|
const lhs = try renderNode(c, payload.lhs);
|
|
return c.addNode(.{
|
|
.tag = tag,
|
|
.main_token = try c.addToken(tok_tag, bytes),
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = try renderNode(c, payload.rhs),
|
|
},
|
|
});
|
|
}
|
|
|
|
fn renderStdImport(c: *Context, parts: []const []const u8) !NodeIndex {
|
|
const import_tok = try c.addToken(.builtin, "@import");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const std_tok = try c.addToken(.string_literal, "\"std\"");
|
|
const std_node = try c.addNode(.{
|
|
.tag = .string_literal,
|
|
.main_token = std_tok,
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_paren, ")");
|
|
|
|
const import_node = try c.addNode(.{
|
|
.tag = .builtin_call_two,
|
|
.main_token = import_tok,
|
|
.data = .{
|
|
.lhs = std_node,
|
|
.rhs = 0,
|
|
},
|
|
});
|
|
|
|
var access_chain = import_node;
|
|
for (parts) |part| {
|
|
access_chain = try renderFieldAccess(c, access_chain, part);
|
|
}
|
|
return access_chain;
|
|
}
|
|
|
|
fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex {
|
|
const lparen = try c.addToken(.l_paren, "(");
|
|
const res = switch (args.len) {
|
|
0 => try c.addNode(.{
|
|
.tag = .call_one,
|
|
.main_token = lparen,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = 0,
|
|
},
|
|
}),
|
|
1 => blk: {
|
|
const arg = try renderNode(c, args[0]);
|
|
break :blk try c.addNode(.{
|
|
.tag = .call_one,
|
|
.main_token = lparen,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = arg,
|
|
},
|
|
});
|
|
},
|
|
else => blk: {
|
|
var rendered = try c.gpa.alloc(NodeIndex, args.len);
|
|
defer c.gpa.free(rendered);
|
|
|
|
for (args, 0..) |arg, i| {
|
|
if (i != 0) _ = try c.addToken(.comma, ",");
|
|
rendered[i] = try renderNode(c, arg);
|
|
}
|
|
const span = try c.listToSpan(rendered);
|
|
break :blk try c.addNode(.{
|
|
.tag = .call,
|
|
.main_token = lparen,
|
|
.data = .{
|
|
.lhs = lhs,
|
|
.rhs = try c.addExtra(NodeSubRange{
|
|
.start = span.start,
|
|
.end = span.end,
|
|
}),
|
|
},
|
|
});
|
|
},
|
|
};
|
|
_ = try c.addToken(.r_paren, ")");
|
|
return res;
|
|
}
|
|
|
|
fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !NodeIndex {
|
|
const builtin_tok = try c.addToken(.builtin, builtin);
|
|
_ = try c.addToken(.l_paren, "(");
|
|
var arg_1: NodeIndex = 0;
|
|
var arg_2: NodeIndex = 0;
|
|
var arg_3: NodeIndex = 0;
|
|
var arg_4: NodeIndex = 0;
|
|
switch (args.len) {
|
|
0 => {},
|
|
1 => {
|
|
arg_1 = try renderNode(c, args[0]);
|
|
},
|
|
2 => {
|
|
arg_1 = try renderNode(c, args[0]);
|
|
_ = try c.addToken(.comma, ",");
|
|
arg_2 = try renderNode(c, args[1]);
|
|
},
|
|
4 => {
|
|
arg_1 = try renderNode(c, args[0]);
|
|
_ = try c.addToken(.comma, ",");
|
|
arg_2 = try renderNode(c, args[1]);
|
|
_ = try c.addToken(.comma, ",");
|
|
arg_3 = try renderNode(c, args[2]);
|
|
_ = try c.addToken(.comma, ",");
|
|
arg_4 = try renderNode(c, args[3]);
|
|
},
|
|
else => unreachable, // expand this function as needed.
|
|
}
|
|
|
|
_ = try c.addToken(.r_paren, ")");
|
|
if (args.len <= 2) {
|
|
return c.addNode(.{
|
|
.tag = .builtin_call_two,
|
|
.main_token = builtin_tok,
|
|
.data = .{
|
|
.lhs = arg_1,
|
|
.rhs = arg_2,
|
|
},
|
|
});
|
|
} else {
|
|
std.debug.assert(args.len == 4);
|
|
|
|
const params = try c.listToSpan(&.{ arg_1, arg_2, arg_3, arg_4 });
|
|
return c.addNode(.{
|
|
.tag = .builtin_call,
|
|
.main_token = builtin_tok,
|
|
.data = .{
|
|
.lhs = params.start,
|
|
.rhs = params.end,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
fn renderVar(c: *Context, node: Node) !NodeIndex {
|
|
const payload = node.castTag(.var_decl).?.data;
|
|
if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
|
|
if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
|
|
if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
|
|
if (payload.is_threadlocal) _ = try c.addToken(.keyword_threadlocal, "threadlocal");
|
|
const mut_tok = if (payload.is_const)
|
|
try c.addToken(.keyword_const, "const")
|
|
else
|
|
try c.addToken(.keyword_var, "var");
|
|
_ = try c.addIdentifier(payload.name);
|
|
_ = try c.addToken(.colon, ":");
|
|
const type_node = try renderNode(c, payload.type);
|
|
|
|
const align_node = if (payload.alignment) |some| blk: {
|
|
_ = try c.addToken(.keyword_align, "align");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const res = try c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addTokenFmt(.number_literal, "{d}", .{some}),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_paren, ")");
|
|
break :blk res;
|
|
} else 0;
|
|
|
|
const section_node = if (payload.linksection_string) |some| blk: {
|
|
_ = try c.addToken(.keyword_linksection, "linksection");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const res = try c.addNode(.{
|
|
.tag = .string_literal,
|
|
.main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_paren, ")");
|
|
break :blk res;
|
|
} else 0;
|
|
|
|
const init_node = if (payload.init) |some| blk: {
|
|
_ = try c.addToken(.equal, "=");
|
|
break :blk try renderNode(c, some);
|
|
} else 0;
|
|
_ = try c.addToken(.semicolon, ";");
|
|
|
|
if (section_node == 0) {
|
|
if (align_node == 0) {
|
|
return c.addNode(.{
|
|
.tag = .simple_var_decl,
|
|
.main_token = mut_tok,
|
|
.data = .{
|
|
.lhs = type_node,
|
|
.rhs = init_node,
|
|
},
|
|
});
|
|
} else {
|
|
return c.addNode(.{
|
|
.tag = .local_var_decl,
|
|
.main_token = mut_tok,
|
|
.data = .{
|
|
.lhs = try c.addExtra(std.zig.Ast.Node.LocalVarDecl{
|
|
.type_node = type_node,
|
|
.align_node = align_node,
|
|
}),
|
|
.rhs = init_node,
|
|
},
|
|
});
|
|
}
|
|
} else {
|
|
return c.addNode(.{
|
|
.tag = .global_var_decl,
|
|
.main_token = mut_tok,
|
|
.data = .{
|
|
.lhs = try c.addExtra(std.zig.Ast.Node.GlobalVarDecl{
|
|
.type_node = type_node,
|
|
.align_node = align_node,
|
|
.section_node = section_node,
|
|
.addrspace_node = 0,
|
|
}),
|
|
.rhs = init_node,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
fn renderFunc(c: *Context, node: Node) !NodeIndex {
|
|
const payload = node.castTag(.func).?.data;
|
|
if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
|
|
if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
|
|
if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
|
|
if (payload.is_inline) _ = try c.addToken(.keyword_inline, "inline");
|
|
const fn_token = try c.addToken(.keyword_fn, "fn");
|
|
if (payload.name) |some| _ = try c.addIdentifier(some);
|
|
|
|
const params = try renderParams(c, payload.params, payload.is_var_args);
|
|
defer params.deinit();
|
|
var span: NodeSubRange = undefined;
|
|
if (params.items.len > 1) span = try c.listToSpan(params.items);
|
|
|
|
const align_expr = if (payload.alignment) |some| blk: {
|
|
_ = try c.addToken(.keyword_align, "align");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const res = try c.addNode(.{
|
|
.tag = .number_literal,
|
|
.main_token = try c.addTokenFmt(.number_literal, "{d}", .{some}),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_paren, ")");
|
|
break :blk res;
|
|
} else 0;
|
|
|
|
const section_expr = if (payload.linksection_string) |some| blk: {
|
|
_ = try c.addToken(.keyword_linksection, "linksection");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
const res = try c.addNode(.{
|
|
.tag = .string_literal,
|
|
.main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_paren, ")");
|
|
break :blk res;
|
|
} else 0;
|
|
|
|
const callconv_expr = if (payload.explicit_callconv) |some| blk: {
|
|
_ = try c.addToken(.keyword_callconv, "callconv");
|
|
_ = try c.addToken(.l_paren, "(");
|
|
_ = try c.addToken(.period, ".");
|
|
const res = try c.addNode(.{
|
|
.tag = .enum_literal,
|
|
.main_token = try c.addTokenFmt(.identifier, "{s}", .{@tagName(some)}),
|
|
.data = undefined,
|
|
});
|
|
_ = try c.addToken(.r_paren, ")");
|
|
break :blk res;
|
|
} else 0;
|
|
|
|
const return_type_expr = try renderNode(c, payload.return_type);
|
|
|
|
const fn_proto = try blk: {
|
|
if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
|
|
if (params.items.len < 2)
|
|
break :blk c.addNode(.{
|
|
.tag = .fn_proto_simple,
|
|
.main_token = fn_token,
|
|
.data = .{
|
|
.lhs = params.items[0],
|
|
.rhs = return_type_expr,
|
|
},
|
|
})
|
|
else
|
|
break :blk c.addNode(.{
|
|
.tag = .fn_proto_multi,
|
|
.main_token = fn_token,
|
|
.data = .{
|
|
.lhs = try c.addExtra(NodeSubRange{
|
|
.start = span.start,
|
|
.end = span.end,
|
|
}),
|
|
.rhs = return_type_expr,
|
|
},
|
|
});
|
|
}
|
|
if (params.items.len < 2)
|
|
break :blk c.addNode(.{
|
|
.tag = .fn_proto_one,
|
|
.main_token = fn_token,
|
|
.data = .{
|
|
.lhs = try c.addExtra(std.zig.Ast.Node.FnProtoOne{
|
|
.param = params.items[0],
|
|
.align_expr = align_expr,
|
|
.addrspace_expr = 0, // TODO
|
|
.section_expr = section_expr,
|
|
.callconv_expr = callconv_expr,
|
|
}),
|
|
.rhs = return_type_expr,
|
|
},
|
|
})
|
|
else
|
|
break :blk c.addNode(.{
|
|
.tag = .fn_proto,
|
|
.main_token = fn_token,
|
|
.data = .{
|
|
.lhs = try c.addExtra(std.zig.Ast.Node.FnProto{
|
|
.params_start = span.start,
|
|
.params_end = span.end,
|
|
.align_expr = align_expr,
|
|
.addrspace_expr = 0, // TODO
|
|
.section_expr = section_expr,
|
|
.callconv_expr = callconv_expr,
|
|
}),
|
|
.rhs = return_type_expr,
|
|
},
|
|
});
|
|
};
|
|
|
|
const payload_body = payload.body orelse {
|
|
if (payload.is_extern) {
|
|
_ = try c.addToken(.semicolon, ";");
|
|
}
|
|
return fn_proto;
|
|
};
|
|
const body = try renderNode(c, payload_body);
|
|
return c.addNode(.{
|
|
.tag = .fn_decl,
|
|
.main_token = fn_token,
|
|
.data = .{
|
|
.lhs = fn_proto,
|
|
.rhs = body,
|
|
},
|
|
});
|
|
}
|
|
|
|
fn renderMacroFunc(c: *Context, node: Node) !NodeIndex {
|
|
const payload = node.castTag(.pub_inline_fn).?.data;
|
|
_ = try c.addToken(.keyword_pub, "pub");
|
|
_ = try c.addToken(.keyword_inline, "inline");
|
|
const fn_token = try c.addToken(.keyword_fn, "fn");
|
|
_ = try c.addIdentifier(payload.name);
|
|
|
|
const params = try renderParams(c, payload.params, false);
|
|
defer params.deinit();
|
|
var span: NodeSubRange = undefined;
|
|
if (params.items.len > 1) span = try c.listToSpan(params.items);
|
|
|
|
const return_type_expr = try renderNodeGrouped(c, payload.return_type);
|
|
|
|
const fn_proto = blk: {
|
|
if (params.items.len < 2) {
|
|
break :blk try c.addNode(.{
|
|
.tag = .fn_proto_simple,
|
|
.main_token = fn_token,
|
|
.data = .{
|
|
.lhs = params.items[0],
|
|
.rhs = return_type_expr,
|
|
},
|
|
});
|
|
} else {
|
|
break :blk try c.addNode(.{
|
|
.tag = .fn_proto_multi,
|
|
.main_token = fn_token,
|
|
.data = .{
|
|
.lhs = try c.addExtra(std.zig.Ast.Node.SubRange{
|
|
.start = span.start,
|
|
.end = span.end,
|
|
}),
|
|
.rhs = return_type_expr,
|
|
},
|
|
});
|
|
}
|
|
};
|
|
return c.addNode(.{
|
|
.tag = .fn_decl,
|
|
.main_token = fn_token,
|
|
.data = .{
|
|
.lhs = fn_proto,
|
|
.rhs = try renderNode(c, payload.body),
|
|
},
|
|
});
|
|
}
|
|
|
|
fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.ArrayList(NodeIndex) {
|
|
_ = try c.addToken(.l_paren, "(");
|
|
var rendered = try std.ArrayList(NodeIndex).initCapacity(c.gpa, @max(params.len, 1));
|
|
errdefer rendered.deinit();
|
|
|
|
for (params, 0..) |param, i| {
|
|
if (i != 0) _ = try c.addToken(.comma, ",");
|
|
if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias");
|
|
if (param.name) |some| {
|
|
_ = try c.addIdentifier(some);
|
|
_ = try c.addToken(.colon, ":");
|
|
}
|
|
if (param.type.tag() == .@"anytype") {
|
|
_ = try c.addToken(.keyword_anytype, "anytype");
|
|
continue;
|
|
}
|
|
rendered.appendAssumeCapacity(try renderNode(c, param.type));
|
|
}
|
|
if (is_var_args) {
|
|
if (params.len != 0) _ = try c.addToken(.comma, ",");
|
|
_ = try c.addToken(.ellipsis3, "...");
|
|
}
|
|
_ = try c.addToken(.r_paren, ")");
|
|
|
|
if (rendered.items.len == 0) rendered.appendAssumeCapacity(0);
|
|
return rendered;
|
|
}
|