feature. See also
. The project being documented here (as the example) is the Zig library itself.
ZonGen.expr
fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator.Error!void
File
Code
fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator.Error!void {
const gpa = zg.gpa;
const tree = zg.tree;
switch (tree.nodeTag(node)) {
.root => unreachable,
.test_decl => unreachable,
.container_field_init => unreachable,
.container_field_align => unreachable,
.container_field => unreachable,
.fn_decl => unreachable,
.global_var_decl => unreachable,
.local_var_decl => unreachable,
.simple_var_decl => unreachable,
.aligned_var_decl => unreachable,
.@"defer" => unreachable,
.@"errdefer" => unreachable,
.switch_case => unreachable,
.switch_case_inline => unreachable,
.switch_case_one => unreachable,
.switch_case_inline_one => unreachable,
.switch_range => unreachable,
.asm_output => unreachable,
.asm_input => unreachable,
.for_range => unreachable,
.assign => unreachable,
.assign_destructure => unreachable,
.assign_shl => unreachable,
.assign_shl_sat => unreachable,
.assign_shr => unreachable,
.assign_bit_and => unreachable,
.assign_bit_or => unreachable,
.assign_bit_xor => unreachable,
.assign_div => unreachable,
.assign_sub => unreachable,
.assign_sub_wrap => unreachable,
.assign_sub_sat => unreachable,
.assign_mod => unreachable,
.assign_add => unreachable,
.assign_add_wrap => unreachable,
.assign_add_sat => unreachable,
.assign_mul => unreachable,
.assign_mul_wrap => unreachable,
.assign_mul_sat => unreachable,
.shl,
.shr,
.add,
.add_wrap,
.add_sat,
.sub,
.sub_wrap,
.sub_sat,
.mul,
.mul_wrap,
.mul_sat,
.div,
.mod,
.shl_sat,
.bit_and,
.bit_or,
.bit_xor,
.bang_equal,
.equal_equal,
.greater_than,
.greater_or_equal,
.less_than,
.less_or_equal,
.array_cat,
.bool_and,
.bool_or,
.bool_not,
.bit_not,
.negation_wrap,
=> try zg.addErrorTok(tree.nodeMainToken(node), "operator '{s}' is not allowed in ZON", .{tree.tokenSlice(tree.nodeMainToken(node))}),
.error_union,
.merge_error_sets,
.optional_type,
.anyframe_literal,
.anyframe_type,
.ptr_type_aligned,
.ptr_type_sentinel,
.ptr_type,
.ptr_type_bit_range,
.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,
.array_type,
.array_type_sentinel,
.error_set_decl,
.fn_proto_simple,
.fn_proto_multi,
.fn_proto_one,
.fn_proto,
=> try zg.addErrorNode(node, "types are not available in ZON", .{}),
.call_one,
.call_one_comma,
.call,
.call_comma,
.@"return",
.if_simple,
.@"if",
.while_simple,
.while_cont,
.@"while",
.for_simple,
.@"for",
.@"catch",
.@"orelse",
.@"break",
.@"continue",
.@"switch",
.switch_comma,
.@"nosuspend",
.@"suspend",
.@"resume",
.@"try",
.unreachable_literal,
=> try zg.addErrorNode(node, "control flow is not allowed in ZON", .{}),
.@"comptime" => try zg.addErrorNode(node, "keyword 'comptime' is not allowed in ZON", .{}),
.asm_simple, .@"asm" => try zg.addErrorNode(node, "inline asm is not allowed in ZON", .{}),
.builtin_call_two,
.builtin_call_two_comma,
.builtin_call,
.builtin_call_comma,
=> try zg.addErrorNode(node, "builtin function calls are not allowed in ZON", .{}),
.field_access => try zg.addErrorNode(node, "field accesses are not allowed in ZON", .{}),
.slice_open,
.slice,
.slice_sentinel,
=> try zg.addErrorNode(node, "slice operator is not allowed in ZON", .{}),
.deref, .address_of => try zg.addErrorTok(tree.nodeMainToken(node), "pointers are not available in ZON", .{}),
.unwrap_optional => try zg.addErrorTok(tree.nodeMainToken(node), "optionals are not available in ZON", .{}),
.error_value => try zg.addErrorNode(node, "errors are not available in ZON", .{}),
.array_access => try zg.addErrorNode(node, "array indexing is not allowed in ZON", .{}),
.block_two,
.block_two_semicolon,
.block,
.block_semicolon,
=> {
var buffer: [2]Ast.Node.Index = undefined;
const statements = tree.blockStatements(&buffer, node).?;
if (statements.len == 0) {
try zg.addErrorNodeNotes(node, "void literals are not available in ZON", .{}, &.{
try zg.errNoteNode(node, "void union payloads can be represented by enum literals", .{}),
});
} else {
try zg.addErrorNode(node, "blocks are not allowed in ZON", .{});
}
},
.array_init_one,
.array_init_one_comma,
.array_init,
.array_init_comma,
.struct_init_one,
.struct_init_one_comma,
.struct_init,
.struct_init_comma,
=> {
var buf: [2]Ast.Node.Index = undefined;
const type_node = if (tree.fullArrayInit(&buf, node)) |full|
full.ast.type_expr.unwrap().?
else if (tree.fullStructInit(&buf, node)) |full|
full.ast.type_expr.unwrap().?
else
unreachable;
try zg.addErrorNodeNotes(type_node, "types are not available in ZON", .{}, &.{
try zg.errNoteNode(type_node, "replace the type with '.'", .{}),
});
},
.grouped_expression => {
try zg.addErrorTokNotes(tree.nodeMainToken(node), "expression grouping is not allowed in ZON", .{}, &.{
try zg.errNoteTok(tree.nodeMainToken(node), "these parentheses are always redundant", .{}),
});
return zg.expr(tree.nodeData(node).node_and_token[0], dest_node);
},
.negation => {
const child_node = tree.nodeData(node).node;
switch (tree.nodeTag(child_node)) {
.number_literal => return zg.numberLiteral(child_node, node, dest_node, .negative),
.identifier => {
const child_ident = tree.tokenSlice(tree.nodeMainToken(child_node));
if (mem.eql(u8, child_ident, "inf")) {
zg.setNode(dest_node, .{
.tag = .neg_inf,
.data = 0,
.ast_node = node,
});
return;
}
},
else => {},
}
try zg.addErrorTok(tree.nodeMainToken(node), "expected number or 'inf' after '-'", .{});
},
.number_literal => try zg.numberLiteral(node, node, dest_node, .positive),
.char_literal => try zg.charLiteral(node, dest_node),
.identifier => try zg.identifier(node, dest_node),
.enum_literal => {
const str_index = zg.identAsString(tree.nodeMainToken(node)) catch |err| switch (err) {
error.BadString => undefined,
error.OutOfMemory => |e| return e,
};
zg.setNode(dest_node, .{
.tag = .enum_literal,
.data = @backingInt(str_index),
.ast_node = node,
});
},
.string_literal, .multiline_string_literal => if (zg.strLitAsString(node)) |result| switch (result) {
.nts => |nts| zg.setNode(dest_node, .{
.tag = .string_literal_null,
.data = @backingInt(nts),
.ast_node = node,
}),
.slice => |slice| {
const extra_index: u32 = @intCast(zg.extra.items.len);
try zg.extra.appendSlice(zg.gpa, &.{ slice.start, slice.len });
zg.setNode(dest_node, .{
.tag = .string_literal,
.data = extra_index,
.ast_node = node,
});
},
} else |err| switch (err) {
error.BadString => {},
error.OutOfMemory => |e| return e,
},
.array_init_dot_two,
.array_init_dot_two_comma,
.array_init_dot,
.array_init_dot_comma,
=> {
var buf: [2]Ast.Node.Index = undefined;
const full = tree.fullArrayInit(&buf, node).?;
assert(full.ast.elements.len != 0);
assert(full.ast.type_expr == .none);
const first_elem: u32 = @intCast(zg.nodes.len);
try zg.nodes.resize(gpa, zg.nodes.len + full.ast.elements.len);
const extra_index: u32 = @intCast(zg.extra.items.len);
try zg.extra.appendSlice(gpa, &.{
@intCast(full.ast.elements.len),
first_elem,
});
zg.setNode(dest_node, .{
.tag = .array_literal,
.data = extra_index,
.ast_node = node,
});
for (full.ast.elements, first_elem..) |elem_node, elem_dest_node| {
try zg.expr(elem_node, @fromBackingInt(@intCast(elem_dest_node)));
}
},
.struct_init_dot_two,
.struct_init_dot_two_comma,
.struct_init_dot,
.struct_init_dot_comma,
=> {
var buf: [2]Ast.Node.Index = undefined;
const full = tree.fullStructInit(&buf, node).?;
assert(full.ast.type_expr == .none);
if (full.ast.fields.len == 0) {
zg.setNode(dest_node, .{
.tag = .empty_literal,
.data = 0,
.ast_node = node,
});
return;
}
const first_elem: u32 = @intCast(zg.nodes.len);
try zg.nodes.resize(gpa, zg.nodes.len + full.ast.fields.len);
const extra_index: u32 = @intCast(zg.extra.items.len);
try zg.extra.ensureUnusedCapacity(gpa, 2 + full.ast.fields.len);
zg.extra.appendSliceAssumeCapacity(&.{
@intCast(full.ast.fields.len),
first_elem,
});
const names_start = extra_index + 2;
zg.extra.appendNTimesAssumeCapacity(undefined, full.ast.fields.len);
zg.setNode(dest_node, .{
.tag = .struct_literal,
.data = extra_index,
.ast_node = node,
});
var bfa_buf: [256]u8 = undefined;
var bfa_state: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);
const bfa = bfa_state.allocator();
var field_names: std.AutoHashMapUnmanaged(Zoir.NullTerminatedString, Ast.TokenIndex) = .empty;
defer field_names.deinit(bfa);
var reported_any_duplicate = false;
for (full.ast.fields, names_start.., first_elem..) |elem_node, extra_name_idx, elem_dest_node| {
const name_token = tree.firstToken(elem_node) - 2;
if (zg.identAsString(name_token)) |name_str| {
zg.extra.items[extra_name_idx] = @backingInt(name_str);
const gop = try field_names.getOrPut(bfa, name_str);
if (gop.found_existing and !reported_any_duplicate) {
reported_any_duplicate = true;
const earlier_token = gop.value_ptr.*;
try zg.addErrorTokNotes(earlier_token, "duplicate struct field name", .{}, &.{
try zg.errNoteTok(name_token, "duplicate name here", .{}),
});
}
gop.value_ptr.* = name_token;
} else |err| switch (err) {
error.BadString => {},
error.OutOfMemory => |e| return e,
}
try zg.expr(elem_node, @fromBackingInt(@intCast(elem_dest_node)));
}
},
}
}