renderExpression
Function parameters
Parameters
Type definitions in this namespace
Types
Functions in this namespace
Functions
Error sets in this namespace
Error Sets
Source
Implementation
fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
const tree = r.tree;
const ais = r.ais;
if (r.fixups.replace_nodes_with_string.get(node)) |replacement| {
try ais.writeAll(replacement);
try renderOnlySpace(r, space);
return;
} else if (r.fixups.replace_nodes_with_node.get(node)) |replacement| {
return renderExpression(r, replacement, space);
}
switch (tree.nodeTag(node)) {
.identifier => {
const token_index = tree.nodeMainToken(node);
return renderIdentifier(r, token_index, space, .preserve_when_shadowing);
},
.number_literal,
.char_literal,
.unreachable_literal,
.anyframe_literal,
.string_literal,
=> return renderToken(r, tree.nodeMainToken(node), space),
.multiline_string_literal => {
try ais.maybeInsertNewline();
const first_tok, const last_tok = tree.nodeData(node).token_and_token;
for (first_tok..last_tok + 1) |i| {
try renderToken(r, @intCast(i), .newline);
}
const next_token = last_tok + 1;
const next_token_tag = tree.tokenTag(next_token);
// dedent the next thing that comes after a multiline string literal
if (!ais.indentStackEmpty() and
next_token_tag != .colon and
((next_token_tag != .semicolon and next_token_tag != .comma) or
ais.lastSpaceModeIndent() < ais.currentIndent()))
{
ais.popIndent();
try ais.pushIndent(.normal);
}
switch (space) {
.none, .space, .newline, .skip => {},
.semicolon => if (next_token_tag == .semicolon) try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),
.comma => if (next_token_tag == .comma) try renderTokenOverrideSpaceMode(r, next_token, .newline, .comma),
.comma_space => if (next_token_tag == .comma) try renderToken(r, next_token, .space),
}
},
.error_value => {
const main_token = tree.nodeMainToken(node);
try renderToken(r, main_token, .none);
try renderToken(r, main_token + 1, .none);
return renderIdentifier(r, main_token + 2, space, .eagerly_unquote);
},
.block_two,
.block_two_semicolon,
.block,
.block_semicolon,
=> {
var buf: [2]Ast.Node.Index = undefined;
const statements = tree.blockStatements(&buf, node).?;
return renderBlock(r, node, statements, space);
},
.@"errdefer" => {
const defer_token = tree.nodeMainToken(node);
const maybe_payload_token, const expr = tree.nodeData(node).opt_token_and_node;
try renderToken(r, defer_token, .space);
if (maybe_payload_token.unwrap()) |payload_token| {
try renderToken(r, payload_token - 1, .none); // |
try renderIdentifier(r, payload_token, .none, .preserve_when_shadowing); // identifier
try renderToken(r, payload_token + 1, .space); // |
}
return renderExpression(r, expr, space);
},
.@"defer",
.@"comptime",
.@"nosuspend",
.@"suspend",
=> {
const main_token = tree.nodeMainToken(node);
const item = tree.nodeData(node).node;
try renderToken(r, main_token, .space);
return renderExpression(r, item, space);
},
.@"catch" => {
const main_token = tree.nodeMainToken(node);
const lhs, const rhs = tree.nodeData(node).node_and_node;
const fallback_first = tree.firstToken(rhs);
const same_line = tree.tokensOnSameLine(main_token, fallback_first);
const after_op_space = if (same_line) Space.space else Space.newline;
try renderExpression(r, lhs, .space); // target
try ais.pushIndent(.normal);
if (tree.tokenTag(fallback_first - 1) == .pipe) {
try renderToken(r, main_token, .space); // catch keyword
try renderToken(r, main_token + 1, .none); // pipe
try renderIdentifier(r, main_token + 2, .none, .preserve_when_shadowing); // payload identifier
try renderToken(r, main_token + 3, after_op_space); // pipe
} else {
assert(tree.tokenTag(fallback_first - 1) == .keyword_catch);
try renderToken(r, main_token, after_op_space); // catch keyword
}
try renderExpression(r, rhs, space); // fallback
ais.popIndent();
},
.field_access => {
const lhs, const name_token = tree.nodeData(node).node_and_token;
const dot_token = name_token - 1;
try ais.pushIndent(.field_access);
try renderExpression(r, lhs, .none);
// Allow a line break between the lhs and the dot if the lhs and rhs
// are on different lines.
const lhs_last_token = tree.lastToken(lhs);
const same_line = tree.tokensOnSameLine(lhs_last_token, name_token);
if (!same_line and !hasComment(tree, lhs_last_token, dot_token)) try ais.insertNewline();
try renderToken(r, dot_token, .none);
try renderIdentifier(r, name_token, space, .eagerly_unquote); // field
ais.popIndent();
},
.error_union,
.switch_range,
=> {
const lhs, const rhs = tree.nodeData(node).node_and_node;
try renderExpression(r, lhs, .none);
try renderToken(r, tree.nodeMainToken(node), .none);
return renderExpression(r, rhs, space);
},
.for_range => {
const start, const opt_end = tree.nodeData(node).node_and_opt_node;
try renderExpression(r, start, .none);
if (opt_end.unwrap()) |end| {
try renderToken(r, tree.nodeMainToken(node), .none);
return renderExpression(r, end, space);
} else {
return renderToken(r, tree.nodeMainToken(node), space);
}
},
.assign,
.assign_bit_and,
.assign_bit_or,
.assign_shl,
.assign_shl_sat,
.assign_shr,
.assign_bit_xor,
.assign_div,
.assign_sub,
.assign_sub_wrap,
.assign_sub_sat,
.assign_mod,
.assign_add,
.assign_add_wrap,
.assign_add_sat,
.assign_mul,
.assign_mul_wrap,
.assign_mul_sat,
=> {
const lhs, const rhs = tree.nodeData(node).node_and_node;
try renderExpression(r, lhs, .space);
const op_token = tree.nodeMainToken(node);
try ais.pushIndent(.after_equals);
if (tree.tokensOnSameLine(op_token, op_token + 1)) {
try renderToken(r, op_token, .space);
} else {
try renderToken(r, op_token, .newline);
}
try renderExpression(r, rhs, space);
ais.popIndent();
},
.add,
.add_wrap,
.add_sat,
.array_cat,
.array_mult,
.bang_equal,
.bit_and,
.bit_or,
.shl,
.shl_sat,
.shr,
.bit_xor,
.bool_and,
.bool_or,
.div,
.equal_equal,
.greater_or_equal,
.greater_than,
.less_or_equal,
.less_than,
.merge_error_sets,
.mod,
.mul,
.mul_wrap,
.mul_sat,
.sub,
.sub_wrap,
.sub_sat,
.@"orelse",
=> {
const lhs, const rhs = tree.nodeData(node).node_and_node;
try renderExpression(r, lhs, .space);
const op_token = tree.nodeMainToken(node);
try ais.pushIndent(.binop);
if (tree.tokensOnSameLine(op_token, op_token + 1)) {
try renderToken(r, op_token, .space);
} else {
try renderToken(r, op_token, .newline);
}
try renderExpression(r, rhs, space);
ais.popIndent();
},
.assign_destructure => {
const full = tree.assignDestructure(node);
if (full.comptime_token) |comptime_token| {
try renderToken(r, comptime_token, .space);
}
for (full.ast.variables, 0..) |variable_node, i| {
const variable_space: Space = if (i == full.ast.variables.len - 1) .space else .comma_space;
switch (tree.nodeTag(variable_node)) {
.global_var_decl,
.local_var_decl,
.simple_var_decl,
.aligned_var_decl,
=> {
try renderVarDecl(r, tree.fullVarDecl(variable_node).?, true, variable_space);
},
else => try renderExpression(r, variable_node, variable_space),
}
}
try ais.pushIndent(.after_equals);
if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) {
try renderToken(r, full.ast.equal_token, .space);
} else {
try renderToken(r, full.ast.equal_token, .newline);
}
try renderExpression(r, full.ast.value_expr, space);
ais.popIndent();
},
.bit_not,
.bool_not,
.negation,
.negation_wrap,
.optional_type,
.address_of,
=> {
try renderToken(r, tree.nodeMainToken(node), .none);
return renderExpression(r, tree.nodeData(node).node, space);
},
.@"try",
.@"resume",
=> {
try renderToken(r, tree.nodeMainToken(node), .space);
return renderExpression(r, tree.nodeData(node).node, space);
},
.array_type,
.array_type_sentinel,
=> return renderArrayType(r, tree.fullArrayType(node).?, space),
.ptr_type_aligned,
.ptr_type_sentinel,
.ptr_type,
.ptr_type_bit_range,
=> return renderPtrType(r, tree.fullPtrType(node).?, space),
.array_init_one,
.array_init_one_comma,
.array_init_dot_two,
.array_init_dot_two_comma,
.array_init_dot,
.array_init_dot_comma,
.array_init,
.array_init_comma,
=> {
var elements: [2]Ast.Node.Index = undefined;
return renderArrayInit(r, tree.fullArrayInit(&elements, node).?, space);
},
.struct_init_one,
.struct_init_one_comma,
.struct_init_dot_two,
.struct_init_dot_two_comma,
.struct_init_dot,
.struct_init_dot_comma,
.struct_init,
.struct_init_comma,
=> {
var buf: [2]Ast.Node.Index = undefined;
return renderStructInit(r, node, tree.fullStructInit(&buf, node).?, space);
},
.call_one,
.call_one_comma,
.call,
.call_comma,
=> {
var buf: [1]Ast.Node.Index = undefined;
return renderCall(r, tree.fullCall(&buf, node).?, space);
},
.array_access => {
const lhs, const rhs = tree.nodeData(node).node_and_node;
const lbracket = tree.firstToken(rhs) - 1;
const rbracket = tree.lastToken(rhs) + 1;
const one_line = tree.tokensOnSameLine(lbracket, rbracket);
const inner_space = if (one_line) Space.none else Space.newline;
try renderExpression(r, lhs, .none);
try ais.pushIndent(.normal);
try renderToken(r, lbracket, inner_space); // [
try renderExpression(r, rhs, inner_space);
ais.popIndent();
return renderToken(r, rbracket, space); // ]
},
.slice_open,
.slice,
.slice_sentinel,
=> return renderSlice(r, node, tree.fullSlice(node).?, space),
.deref => {
try renderExpression(r, tree.nodeData(node).node, .none);
return renderToken(r, tree.nodeMainToken(node), space);
},
.unwrap_optional => {
const lhs, const question_mark = tree.nodeData(node).node_and_token;
const dot_token = question_mark - 1;
try renderExpression(r, lhs, .none);
try renderToken(r, dot_token, .none);
return renderToken(r, question_mark, space);
},
.@"break", .@"continue" => {
const main_token = tree.nodeMainToken(node);
const opt_label_token, const opt_target = tree.nodeData(node).opt_token_and_opt_node;
if (opt_label_token == .none and opt_target == .none) {
try renderToken(r, main_token, space); // break/continue
} else if (opt_label_token == .none and opt_target != .none) {
const target = opt_target.unwrap().?;
try renderToken(r, main_token, .space); // break/continue
try renderExpression(r, target, space);
} else if (opt_label_token != .none and opt_target == .none) {
const label_token = opt_label_token.unwrap().?;
try renderToken(r, main_token, .space); // break/continue
try renderToken(r, label_token - 1, .none); // :
try renderIdentifier(r, label_token, space, .eagerly_unquote); // identifier
} else if (opt_label_token != .none and opt_target != .none) {
const label_token = opt_label_token.unwrap().?;
const target = opt_target.unwrap().?;
try renderToken(r, main_token, .space); // break/continue
try renderToken(r, label_token - 1, .none); // :
try renderIdentifier(r, label_token, .space, .eagerly_unquote); // identifier
try renderExpression(r, target, space);
} else unreachable;
},
.@"return" => {
if (tree.nodeData(node).opt_node.unwrap()) |expr| {
try renderToken(r, tree.nodeMainToken(node), .space);
try renderExpression(r, expr, space);
} else {
try renderToken(r, tree.nodeMainToken(node), space);
}
},
.grouped_expression => {
const expr, const rparen = tree.nodeData(node).node_and_token;
try ais.pushIndent(.normal);
try renderToken(r, tree.nodeMainToken(node), .none); // lparen
try renderExpression(r, expr, .none);
ais.popIndent();
return renderToken(r, rparen, space);
},
.container_decl,
.container_decl_trailing,
.container_decl_arg,
.container_decl_arg_trailing,
.container_decl_two,
.container_decl_two_trailing,
.tagged_union,
.tagged_union_trailing,
.tagged_union_enum_tag,
.tagged_union_enum_tag_trailing,
.tagged_union_two,
.tagged_union_two_trailing,
=> {
var buf: [2]Ast.Node.Index = undefined;
return renderContainerDecl(r, node, tree.fullContainerDecl(&buf, node).?, space);
},
.error_set_decl => {
const error_token = tree.nodeMainToken(node);
const lbrace, const rbrace = tree.nodeData(node).token_and_token;
try renderToken(r, error_token, .none);
if (lbrace + 1 == rbrace) {
// There is nothing between the braces so render condensed: `error{}`
try renderToken(r, lbrace, .none);
return renderToken(r, rbrace, space);
} else if (lbrace + 2 == rbrace and tree.tokenTag(lbrace + 1) == .identifier) {
// There is exactly one member and no trailing comma or
// comments, so render without surrounding spaces: `error{Foo}`
try renderToken(r, lbrace, .none);
try renderIdentifier(r, lbrace + 1, .none, .eagerly_unquote); // identifier
return renderToken(r, rbrace, space);
} else if (tree.tokenTag(rbrace - 1) == .comma) {
// There is a trailing comma so render each member on a new line.
try ais.pushIndent(.normal);
try renderToken(r, lbrace, .newline);
var i = lbrace + 1;
while (i < rbrace) : (i += 1) {
if (i > lbrace + 1) try renderExtraNewlineToken(r, i);
switch (tree.tokenTag(i)) {
.doc_comment => try renderToken(r, i, .newline),
.identifier => {
try ais.pushSpace(.comma);
try renderIdentifier(r, i, .comma, .eagerly_unquote);
ais.popSpace();
},
.comma => {},
else => unreachable,
}
}
ais.popIndent();
return renderToken(r, rbrace, space);
} else {
// There is no trailing comma so render everything on one line.
try renderToken(r, lbrace, .space);
var i = lbrace + 1;
while (i < rbrace) : (i += 1) {
switch (tree.tokenTag(i)) {
.doc_comment => unreachable, // TODO
.identifier => try renderIdentifier(r, i, .comma_space, .eagerly_unquote),
.comma => {},
else => unreachable,
}
}
return renderToken(r, rbrace, space);
}
},
.builtin_call_two,
.builtin_call_two_comma,
.builtin_call,
.builtin_call_comma,
=> {
var buf: [2]Ast.Node.Index = undefined;
const params = tree.builtinCallParams(&buf, node).?;
var builtin_token = tree.nodeMainToken(node);
canonicalize: {
if (params.len != 1) break :canonicalize;
const CastKind = enum {
ptrCast,
alignCast,
addrSpaceCast,
constCast,
volatileCast,
};
const kind = meta.stringToEnum(CastKind, tree.tokenSlice(builtin_token)[1..]) orelse break :canonicalize;
var cast_map = std.EnumMap(CastKind, Ast.TokenIndex).init(.{});
cast_map.put(kind, builtin_token);
var casts_before: usize = 0;
if (builtin_token >= 2) {
var prev_builtin_token = builtin_token - 2;
while (tree.tokenTag(prev_builtin_token) == .builtin) : (prev_builtin_token -= 2) {
const prev_kind = meta.stringToEnum(CastKind, tree.tokenSlice(prev_builtin_token)[1..]) orelse break;
if (cast_map.contains(prev_kind)) break :canonicalize;
cast_map.put(prev_kind, prev_builtin_token);
casts_before += 1;
}
}
var next_builtin_token = builtin_token + 2;
while (tree.tokenTag(next_builtin_token) == .builtin) : (next_builtin_token += 2) {
const next_kind = meta.stringToEnum(CastKind, tree.tokenSlice(next_builtin_token)[1..]) orelse break;
if (cast_map.contains(next_kind)) break :canonicalize;
cast_map.put(next_kind, next_builtin_token);
}
var it = cast_map.iterator();
builtin_token = it.next().?.value.*;
while (casts_before > 0) : (casts_before -= 1) {
builtin_token = it.next().?.value.*;
}
}
return renderBuiltinCall(r, builtin_token, params, space);
},
.fn_proto_simple,
.fn_proto_multi,
.fn_proto_one,
.fn_proto,
=> {
var buf: [1]Ast.Node.Index = undefined;
return renderFnProto(r, tree.fullFnProto(&buf, node).?, space);
},
.anyframe_type => {
const main_token = tree.nodeMainToken(node);
try renderToken(r, main_token, .none); // anyframe
try renderToken(r, main_token + 1, .none); // ->
return renderExpression(r, tree.nodeData(node).token_and_node[1], space);
},
.@"switch",
.switch_comma,
=> {
const full = tree.switchFull(node);
if (full.label_token) |label_token| {
try renderIdentifier(r, label_token, .none, .eagerly_unquote); // label
try renderToken(r, label_token + 1, .space); // :
}
const rparen = tree.lastToken(full.ast.condition) + 1;
try renderToken(r, full.ast.switch_token, .space); // switch
try renderToken(r, full.ast.switch_token + 1, .none); // (
try renderExpression(r, full.ast.condition, .none); // condition expression
try renderToken(r, rparen, .space); // )
try ais.pushIndent(.normal);
if (full.ast.cases.len == 0) {
try renderToken(r, rparen + 1, .none); // {
} else {
try renderToken(r, rparen + 1, .newline); // {
try ais.pushSpace(.comma);
try renderExpressions(r, full.ast.cases, .comma);
ais.popSpace();
}
ais.popIndent();
return renderToken(r, tree.lastToken(node), space); // }
},
.switch_case_one,
.switch_case_inline_one,
.switch_case,
.switch_case_inline,
=> return renderSwitchCase(r, tree.fullSwitchCase(node).?, space),
.while_simple,
.while_cont,
.@"while",
=> return renderWhile(r, tree.fullWhile(node).?, space),
.for_simple,
.@"for",
=> return renderFor(r, tree.fullFor(node).?, space),
.if_simple,
.@"if",
=> return renderIf(r, tree.fullIf(node).?, space),
.asm_simple,
.@"asm",
=> return renderAsm(r, tree.fullAsm(node).?, space),
// To be removed after 0.15.0 is tagged
.asm_legacy => return renderAsmLegacy(r, tree.legacyAsm(node).?, space),
.enum_literal => {
try renderToken(r, tree.nodeMainToken(node) - 1, .none); // .
return renderIdentifier(r, tree.nodeMainToken(node), space, .eagerly_unquote); // name
},
.fn_decl => unreachable,
.container_field => unreachable,
.container_field_init => unreachable,
.container_field_align => unreachable,
.root => unreachable,
.global_var_decl => unreachable,
.local_var_decl => unreachable,
.simple_var_decl => unreachable,
.aligned_var_decl => unreachable,
.test_decl => unreachable,
.asm_output => unreachable,
.asm_input => unreachable,
}
}