#Need to call a init function on an unrelated module to solve circular dependency

1 messages · Page 1 of 1 (latest)

hot dune
#

I have this code

test "struct conversions" {
    const S = struct {
        member_1: i32,
        member_2: []const u8,

        const Self = @This();

        pub fn clone(self: Self, _: std.mem.Allocator) Self {
            // placeholder for test
            return .{
                .member_1 = self.member_1,
                .member_2 = self.member_2,
            };
        }
    };

    var gpa = std.heap.DebugAllocator(.{}){};
    const allocator = gpa.allocator();

    var map = std.StringHashMapUnmanaged(LispType).empty;
    defer map.deinit(allocator);

    // Insert values (simulate parsed data)
    try map.put(allocator, "member_1", .{ .int = 42 });
    try map.put(allocator, "member_2", LispType.String.initString(allocator, "hello"));

    const v = try LispType.Record.fromHashMap(S, allocator, map);
    const s = try v.cast(S, allocator);

    try std.testing.expect(s.member_1 == map.get("member_1").?.int);
    try std.testing.expect(std.mem.eql(u8, s.member_2, map.get("member_2").?.string.getItems()));
}

This code does not compile, it gives me the error

src/types.zig:323:26: error: union 'types.LispType.Function' depends on itself
    pub const Function = union(enum) {

However, the same code compiles just fine on my main.zig. I discovered that adding this line

    _ = Interpreter.init(allocator);

For some reason makes the code compile (it seems like that's why the code compiles when running zig build run).

#

Here is the Function union

    pub const Function = union(enum) {
        fn_: Fn,
        builtin: BuiltinFunc,

        pub fn clone(self: Function, allocator: std.mem.Allocator) LispType {
            return switch (self) {
                .fn_ => |fn_| fn_.clone(allocator),
                .builtin => .{ .function = self },
            };
        }

        pub fn deinit(self: *Function, allocator: std.mem.Allocator) void {
            switch (self.*) {
                .fn_ => |*fn_| fn_.deinit(allocator),
                .builtin => {},
            }
        }
    };

    pub const BuiltinFunc = *const fn (
        allocator: std.mem.Allocator,
        args: []LispType,
        env: *Env,
        err_ctx: *errors.Context,
    ) LispError!LispType;
#
    pub const Fn = struct {
        ast: *LispType,
        args: [][]const u8,
        env: *Env,
        is_macro: bool = false,

        pub fn init(
            allocator: std.mem.Allocator,
            val: LispType,
            args: [][]const u8,
            closure_names: [][]const u8,
            closure_vals: []LispType,
            base_env: *Env,
        ) LispType {
            var env = Env.initFromParent(base_env.getRoot());
            for (closure_vals, closure_names) |v, name| {
                _ = env.putClone(name, v);
            }

            const ast = allocator.create(LispType) catch outOfMemory();
            ast.* = val.clone(allocator);

            var args_owned = allocator.alloc([]const u8, args.len) catch outOfMemory();
            for (args, 0..) |arg, i| {
                args_owned[i] = allocator.dupe(u8, arg) catch outOfMemory();
            }

            const m_fn = Fn{
                .ast = ast,
                .args = args_owned,
                .env = env,
            };
            return .{ .function = .{ .fn_ = m_fn } };
        }

        pub fn clone(self: Fn, allocator: std.mem.Allocator) LispType {
            var fn_ = init(allocator, self.ast.*, self.args, &[0][]u8{}, &[0]LispType{}, self.env.getRoot());
            fn_.function.fn_.is_macro = self.is_macro;

            var iter = self.env.mapping.iterator();
            while (iter.next()) |entry| {
                _ = fn_.function.fn_.env.putClone(entry.key_ptr.*, entry.value_ptr.*);
            }

            return fn_;
        }

        pub fn deinit(self: *Fn, allocator: std.mem.Allocator) void {
            self.ast.deinit(allocator);
            allocator.destroy(self.ast);
            self.env.deinit();
        }
    };
#

And here is the interpreter

pub const Interpreter = struct {
    arena: std.heap.ArenaAllocator,
    eval_arena: std.heap.ArenaAllocator,
    print_arena: std.heap.ArenaAllocator,
    env: *Env,
    err_ctx: errors.Context,

    const Self = @This();

    pub fn init(base_allocator: std.mem.Allocator) Self {
        const arena = std.heap.ArenaAllocator.init(base_allocator);
        const eval_arena = std.heap.ArenaAllocator.init(base_allocator);
        const print_arena = std.heap.ArenaAllocator.init(base_allocator);

        const err_ctx = errors.Context.init(base_allocator);
        const env = Env.init(base_allocator).setFunctions();
        return .{
            .arena = arena,
            .eval_arena = eval_arena,
            .print_arena = print_arena,
            .err_ctx = err_ctx,
            .env = env,
        };
    }

    pub fn deinit(self: *Self) void {
        self.err_ctx.deinit();
        self.env.deinit();
        self.arena.deinit();
        self.eval_arena.deinit();
        self.print_arena.deinit();
    }

    pub fn run(self: *Self, allocator: std.mem.Allocator, value: LispType) LispError!LispType {
        defer _ = self.eval_arena.reset(.retain_capacity);
        const s = try eval(self.eval_arena.allocator(), value, self.env, &self.err_ctx);
        return s.clone(allocator);
    }

    pub fn print(self: *Self, value: LispType) []const u8 {
        _ = self.print_arena.reset(.retain_capacity);
        return value.toStringFull(self.print_arena.allocator()) catch outOfMemory();
    }
#
    pub fn re(self: *Self, text: []const u8) !LispType {
        _ = self.arena.reset(.retain_capacity);
        const allocator = self.arena.allocator();
        const val = try Reader.readStr(allocator, text);
        return self.run(allocator, val);
    }

    pub fn rep(self: *Self, text: []const u8) ![]const u8 {
        const allocator = self.arena.allocator();
        const ret = self.re(text) catch blk: {
            const err_str = std.fmt.allocPrint(
                allocator,
                "ERROR: {s}\n",
                .{self.err_ctx.buffer.items},
            ) catch outOfMemory();
            break :blk LispType.String.initString(allocator, err_str);
        };
        return self.print(ret);
    }
};
#

Need to call a init function on an unrelated module to solve circular dependency