Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

expr

ZonGen.expr
fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator.Error!void

File

lib/std/zig/ZonGen.zig:100

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, // ignored
                            .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, // doesn't matter, there's an error
                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); // Otherwise it would be a struct init
            assert(full.ast.type_expr == .none); // The tag was `array_init_dot_*`

            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); // The tag was `struct_init_dot_*`

            if (full.ast.fields.len == 0) {
                zg.setNode(dest_node, .{
                    .tag = .empty_literal,
                    .data = 0, // ignored
                    .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,
            });

            // For short initializers, track the names on the stack rather than going through gpa.
            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 => {}, // there's an error, so it's fine to not populate `zg.extra`
                    error.OutOfMemory => |e| return e,
                }
                try zg.expr(elem_node, @fromBackingInt(@intCast(elem_dest_node)));
            }
        },
    }
}