feature. See also
. The project being documented here (as the example) is the Zig library itself.
ast.renderNode
fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex
File
Code
fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
switch (node.tag()) {
.declaration => unreachable,
.warning => unreachable,
.discard => {
const payload = node.castTag(.discard).?.data;
std.debug.assert(!payload.should_skip);
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) {
var addr_of_pl: Payload.UnOp = .{
.base = .{ .tag = .address_of },
.data = payload.value,
};
const addr_of: Node = .{ .ptr_otherwise = &addr_of_pl.base };
return try c.addNode(.{
.tag = .assign,
.main_token = main_token,
.data = .{ .node_and_node = .{
lhs, try renderNode(c, addr_of),
} },
});
} else {
return try c.addNode(.{
.tag = .assign,
.main_token = main_token,
.data = .{ .node_and_node = .{
lhs, try renderNode(c, payload.value),
} },
});
}
},
.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 });
},
.vector => {
const payload = node.castTag(.vector).?.data;
return renderBuiltinCall(c, "@Vector", &.{ payload.lhs, payload.rhs });
},
.call => {
const payload = node.castTag(.call).?.data;
const lhs = 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,
}),
.@"unreachable" => return c.addNode(.{
.tag = .unreachable_literal,
.main_token = try c.addToken(.keyword_unreachable, "unreachable"),
.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 = .{ .opt_token_and_opt_node = .{
.none, .none,
} },
}),
.return_void => return c.addNode(.{
.tag = .@"return",
.main_token = try c.addToken(.keyword_return, "return"),
.data = .{ .opt_node = .none },
}),
.@"break" => return c.addNode(.{
.tag = .@"break",
.main_token = try c.addToken(.keyword_break, "break"),
.data = .{ .opt_token_and_opt_node = .{
.none, .none,
} },
}),
.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 = .{ .opt_token_and_opt_node = .{
.fromToken(break_label), (try renderNode(c, payload.val)).toOptional(),
} },
});
},
.@"return" => {
const payload = node.castTag(.@"return").?.data;
return c.addNode(.{
.tag = .@"return",
.main_token = try c.addToken(.keyword_return, "return"),
.data = .{ .opt_node = (try renderNode(c, payload)).toOptional() },
});
},
.@"comptime" => {
const payload = node.castTag(.@"comptime").?.data;
return c.addNode(.{
.tag = .@"comptime",
.main_token = try c.addToken(.keyword_comptime, "comptime"),
.data = .{
.node = try renderNode(c, payload),
},
});
},
.@"defer" => {
const payload = node.castTag(.@"defer").?.data;
return c.addNode(.{
.tag = .@"defer",
.main_token = try c.addToken(.keyword_defer, "defer"),
.data = .{
.node = 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 = .{ .node_and_token = .{
try renderNode(c, payload),
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,
});
},
.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,
});
},
.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 = .{ .node_and_extra = .{
string,
try c.addExtra(std.zig.Ast.Node.Slice{
.start = start,
.end = end,
}),
} },
});
},
.fail_decl => {
const payload = node.castTag(.fail_decl).?.data;
if (!payload.local) _ = try c.addToken(.keyword_pub, "pub");
const const_tok = try c.addToken(.keyword_const, "const");
_ = try c.addIdentifier(payload.actual);
_ = try c.addToken(.equal, "=");
var if_tok: TokenIndex = undefined;
var true_node: NodeIndex = undefined;
if (payload.local) {
if_tok = try c.addToken(.keyword_if, "if");
_ = try c.addToken(.l_paren, "(");
true_node = try c.addNode(.{
.tag = .identifier,
.main_token = try c.addToken(.identifier, "true"),
.data = undefined,
});
_ = try c.addToken(.r_paren, ")");
}
const compile_error_tok = try c.addToken(.builtin, "@compileError");
_ = try c.addToken(.l_paren, "(");
const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(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 = .{ .opt_node_and_opt_node = .{
err_msg.toOptional(), .none,
} },
});
_ = try c.addToken(.semicolon, ";");
return c.addNode(.{
.tag = .simple_var_decl,
.main_token = const_tok,
.data = .{
.opt_node_and_opt_node = .{
.none,
if (payload.local)
(try c.addNode(.{
.tag = .if_simple,
.main_token = if_tok,
.data = .{ .node_and_node = .{
true_node, compile_error,
} },
})).toOptional()
else
compile_error.toOptional(),
},
},
});
},
.pub_var_simple, .var_simple => {
const payload = @as(*Payload.SimpleVarDecl, @alignCast(@fieldParentPtr("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 = .{
.opt_node_and_opt_node = .{
.none,
init.toOptional(),
},
},
});
},
.wrapped_local => {
const payload = node.castTag(.wrapped_local).?.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 = .{ .opt_node_and_opt_node = .{
(try renderNode(c, payload.init)).toOptional(), .none,
} },
});
_ = try c.addToken(.r_brace, "}");
_ = try c.addToken(.semicolon, ";");
return c.addNode(.{
.tag = .simple_var_decl,
.main_token = const_tok,
.data = .{
.opt_node_and_opt_node = .{
.none,
container_def.toOptional(),
},
},
});
},
.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 = .{
.node = try renderNodeGrouped(c, payload.init),
},
.main_token = try c.addToken(.period_asterisk, ".*"),
});
_ = try c.addToken(.semicolon, ";");
return c.addNode(.{
.tag = .simple_var_decl,
.main_token = var_tok,
.data = .{
.opt_node_and_opt_node = .{
.none,
deref.toOptional(),
},
},
});
},
.var_decl => return renderVar(c, node),
.arg_redecl, .alias => {
const payload = @as(*Payload.ArgRedecl, @alignCast(@fieldParentPtr("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 = .{
.opt_node_and_opt_node = .{
.none,
init.toOptional(),
},
},
});
},
.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});
},
.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 },
});
},
.helper_call => {
const payload = node.castTag(.helper_call).?.data;
const helpers_tok = try c.addNode(.{
.tag = .identifier,
.main_token = try c.addIdentifier("__helpers"),
.data = undefined,
});
const func = try renderFieldAccess(c, helpers_tok, payload.name);
return renderCall(c, func, payload.args);
},
.helper_ref => {
const payload = node.castTag(.helper_ref).?.data;
const helpers_tok = try c.addNode(.{
.tag = .identifier,
.main_token = try c.addIdentifier("__helpers"),
.data = undefined,
});
return renderFieldAccess(c, helpers_tok, payload);
},
.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});
},
.byte_swap => {
const payload = node.castTag(.byte_swap).?.data;
return renderBuiltinCall(c, "@byteSwap", &.{payload});
},
.ceil => {
const payload = node.castTag(.ceil).?.data;
return renderBuiltinCall(c, "@ceil", &.{payload});
},
.cos => {
const payload = node.castTag(.cos).?.data;
return renderBuiltinCall(c, "@cos", &.{payload});
},
.sin => {
const payload = node.castTag(.sin).?.data;
return renderBuiltinCall(c, "@sin", &.{payload});
},
.exp => {
const payload = node.castTag(.exp).?.data;
return renderBuiltinCall(c, "@exp", &.{payload});
},
.exp2 => {
const payload = node.castTag(.exp2).?.data;
return renderBuiltinCall(c, "@exp2", &.{payload});
},
.exp10 => {
const payload = node.castTag(.exp10).?.data;
return renderBuiltinCall(c, "@exp10", &.{payload});
},
.abs => {
const payload = node.castTag(.abs).?.data;
return renderBuiltinCall(c, "@abs", &.{payload});
},
.log => {
const payload = node.castTag(.log).?.data;
return renderBuiltinCall(c, "@log", &.{payload});
},
.log2 => {
const payload = node.castTag(.log2).?.data;
return renderBuiltinCall(c, "@log2", &.{payload});
},
.log10 => {
const payload = node.castTag(.log10).?.data;
return renderBuiltinCall(c, "@log10", &.{payload});
},
.round => {
const payload = node.castTag(.round).?.data;
return renderBuiltinCall(c, "@round", &.{payload});
},
.sqrt => {
const payload = node.castTag(.sqrt).?.data;
return renderBuiltinCall(c, "@sqrt", &.{payload});
},
.trunc => {
const payload = node.castTag(.trunc).?.data;
return renderBuiltinCall(c, "@trunc", &.{payload});
},
.floor => {
const payload = node.castTag(.floor).?.data;
return renderBuiltinCall(c, "@floor", &.{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 = try renderNodeGrouped(c, payload);
return c.addNode(.{
.tag = .address_of,
.main_token = ampersand,
.data = .{
.node = base,
},
});
},
.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 = .{
.node = operand,
},
});
},
.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 = .{ .node_and_token = .{
operand, question_mark,
} },
});
},
.c_pointer, .single_pointer => {
const payload = @as(*Payload.Pointer, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
const main_token = if (node.tag() == .single_pointer)
try c.addToken(.asterisk, "*")
else blk: {
const res = try c.addToken(.l_bracket, "[");
_ = 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");
if (payload.is_allowzero) _ = try c.addToken(.keyword_allowzero, "allowzero");
const elem_type = try renderNodeGrouped(c, payload.elem_type);
return c.addNode(.{
.tag = .ptr_type_aligned,
.main_token = main_token,
.data = .{
.opt_node_and_node = .{
.none,
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 = .{ .opt_node_and_opt_node = .{
.none, .none,
} },
});
},
.block_single => {
const payload = node.castTag(.block_single).?.data;
const l_brace = try c.addToken(.l_brace, "{");
const stmt = (try renderNodeOpt(c, payload)) orelse {
_ = try c.addToken(.r_brace, "}");
return c.addNode(.{
.tag = .block_two,
.main_token = l_brace,
.data = .{ .opt_node_and_opt_node = .{
.none, .none,
} },
});
};
try addSemicolonIfNeeded(c, payload);
_ = try c.addToken(.r_brace, "}");
return c.addNode(.{
.tag = .block_two_semicolon,
.main_token = l_brace,
.data = .{ .opt_node_and_opt_node = .{
stmt.toOptional(), .none,
} },
});
},
.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) = .empty;
defer stmts.deinit(c.gpa);
for (payload.stmts) |stmt| {
const res = (try renderNodeOpt(c, stmt)) orelse continue;
try addSemicolonIfNeeded(c, stmt);
try stmts.append(c.gpa, 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 = .{ .extra_range = span },
});
},
.func => return renderFunc(c, node),
.pub_inline_fn => return renderMacroFunc(c, node),
.@"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_opt = 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 null;
const body = try renderNode(c, payload.body);
if (cont_expr_opt) |cont_expr| {
return c.addNode(.{
.tag = .while_cont,
.main_token = while_tok,
.data = .{ .node_and_extra = .{
cond,
try c.addExtra(std.zig.Ast.Node.WhileCont{
.cont_expr = cont_expr,
.then_expr = body,
}),
} },
});
} else {
return c.addNode(.{
.tag = .while_simple,
.main_token = while_tok,
.data = .{ .node_and_node = .{
cond, 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 = .{ .node_and_node = .{
cond, 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 = .{ .node_and_node = .{
cond, 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 = .{ .node_and_extra = .{
cond,
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 = .{
.node = try renderNodeGrouped(c, payload),
},
});
_ = try c.addToken(.r_paren, ")");
const then_expr = try c.addNode(.{
.tag = .@"break",
.main_token = try c.addToken(.keyword_break, "break"),
.data = .{ .opt_token_and_opt_node = .{
.none, .none,
} },
});
return c.addNode(.{
.tag = .if_simple,
.main_token = if_tok,
.data = .{ .node_and_node = .{
cond, 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 = .{ .node_and_extra = .{
cond,
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 = .{ .opt_node_and_node = .{
.none, try renderNode(c, payload),
} },
});
},
.switch_prong => {
const payload = node.castTag(.switch_prong).?.data;
var items = try c.gpa.alloc(NodeIndex, payload.cases.len);
defer c.gpa.free(items);
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 = .{ .opt_node_and_node = .{
if (payload.cases.len == 1) items[0].toOptional() else .none,
try renderNode(c, payload.cond),
} },
});
} else {
return c.addNode(.{
.tag = .switch_case,
.main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
.data = .{ .extra_and_node = .{
try c.addExtra(try c.listToSpan(items)),
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 = .{ .opt_node_and_opt_node = .{
.none, .none,
} },
});
},
.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 = .{ .node_and_node = .{
lhs, 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 as_tok = try c.addToken(.builtin, "@as");
_ = try c.addToken(.l_paren, "(");
const type_node = try renderArrayType(c, payload.count, payload.type);
_ = try c.addToken(.comma, ",");
const splat_node = try renderBuiltinCall(c, "@splat", &.{payload.filler});
_ = try c.addToken(.r_paren, ")");
return c.addNode(.{
.tag = .builtin_call_two,
.main_token = as_tok,
.data = .{ .opt_node_and_opt_node = .{
.fromOptional(type_node), .fromOptional(splat_node),
} },
});
},
.empty_array => {
const payload = node.castTag(.empty_array).?.data;
const type_expr = try renderNode(c, 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);
},
.field_builtin => {
const payload = node.castTag(.field_builtin).?.data;
return renderBuiltinCall(c, "@field", &.{ payload.lhs, payload.rhs });
},
.@"struct", .@"union", .@"opaque" => return renderContainer(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_opt = if (payload.type) |enum_const_type| blk: {
_ = try c.addToken(.colon, ":");
break :blk try renderNode(c, enum_const_type);
} else null;
_ = 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 = .{ .opt_node_and_opt_node = .{
.fromOptional(type_node_opt),
init_node.toOptional(),
} },
});
},
.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, payload.len);
defer c.gpa.free(inits);
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 = .{ .opt_node_and_opt_node = .{
if (inits.len >= 1) inits[0].toOptional() else .none,
if (inits.len >= 2) inits[1].toOptional() else .none,
} },
});
} else {
return c.addNode(.{
.tag = .array_init_dot,
.main_token = l_brace,
.data = .{ .extra_range = try c.listToSpan(inits) },
});
}
},
.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, payload.len);
defer c.gpa.free(inits);
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 = .{ .opt_node_and_opt_node = .{
if (inits.len >= 1) inits[0].toOptional() else .none,
if (inits.len >= 2) inits[1].toOptional() else .none,
} },
});
} else {
return c.addNode(.{
.tag = .struct_init_dot_comma,
.main_token = l_brace,
.data = .{ .extra_range = try c.listToSpan(inits) },
});
}
},
.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, payload.inits.len);
defer c.gpa.free(inits);
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, "}");
switch (inits.len) {
0 => return c.addNode(.{
.tag = .struct_init_one,
.main_token = l_brace,
.data = .{ .node_and_opt_node = .{
lhs, .none,
} },
}),
1 => return c.addNode(.{
.tag = .struct_init_one_comma,
.main_token = l_brace,
.data = .{ .node_and_opt_node = .{
lhs, inits[0].toOptional(),
} },
}),
else => return c.addNode(.{
.tag = .struct_init_comma,
.main_token = l_brace,
.data = .{ .node_and_extra = .{
lhs,
try c.addExtra(try c.listToSpan(inits)),
} },
}),
}
},
.static_assert => {
const payload = node.castTag(.static_assert).?.data;
const comptime_tok = try c.addToken(.keyword_comptime, "comptime");
const l_brace = try c.addToken(.l_brace, "{");
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 = .{
.node = try renderNodeGrouped(c, payload.lhs),
},
});
_ = try c.addToken(.r_paren, ")");
const compile_error_tok = try c.addToken(.builtin, "@compileError");
_ = try c.addToken(.l_paren, "(");
const err_msg = try renderNode(c, payload.rhs);
_ = try c.addToken(.r_paren, ")");
const compile_error = try c.addNode(.{
.tag = .builtin_call_two,
.main_token = compile_error_tok,
.data = .{ .opt_node_and_opt_node = .{
err_msg.toOptional(), .none,
} },
});
const if_node = try c.addNode(.{
.tag = .if_simple,
.main_token = if_tok,
.data = .{ .node_and_node = .{
cond, compile_error,
} },
});
_ = try c.addToken(.semicolon, ";");
_ = try c.addToken(.r_brace, "}");
const block_node = try c.addNode(.{
.tag = .block_two_semicolon,
.main_token = l_brace,
.data = .{ .opt_node_and_opt_node = .{
if_node.toOptional(), .none,
} },
});
return c.addNode(.{
.tag = .@"comptime",
.main_token = comptime_tok,
.data = .{
.node = block_node,
},
});
},
.@"anytype" => unreachable,
.root_ref => {
const payload = node.castTag(.root_ref).?.data;
const root_tok = try c.addNode(.{
.tag = .identifier,
.main_token = try c.addIdentifier("__root"),
.data = undefined,
});
return renderFieldAccess(c, root_tok, payload);
},
}
}