treesummaryrefslogcommitdiff
path: root/src/refl.zig
blob: 5cfcc52615c1ae2a186e4b9edd118397b97adc35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
const std = @import("std");

fn Args(params: []const std.builtin.Type.Fn.Param) type {
    var argTypes: [params.len]type = undefined;

    for (params, 0..) |p, i| {
        argTypes[i] = p.type.?;
    }

    return @Tuple(&argTypes);
}

fn Val(comptime T: type) type {
    const ti = @typeInfo(T).@"struct";

    const fieldCount = ti.fields.len + ti.decls.len + 1;

    var names: [fieldCount][]const u8 = undefined;
    var types: [fieldCount]type = undefined;
    var attrs: [fieldCount]std.builtin.Type.StructField.Attributes = undefined;

    inline for (ti.fields, 0..) |field, idx| {
        if (field.is_comptime)
            @compileError("implement skipping comptime fields");

        const fi = @typeInfo(field.type);
        switch (fi) {
            .@"struct" => {
                names[idx] = field.name;
                const NT = Val(field.type);
                types[idx] = NT;
                attrs[idx] = .{
                    .default_value_ptr = &NT{},
                };
            },
            else => {
                names[idx] = field.name;
                const NT = struct {
                    pub fn get(self: *@This()) field.type {
                        std.debug.print("getting {s}\n", .{field.name});
                        const parent = self.valuePtr();
                        return @field(parent.value, field.name);
                    }
                    pub fn set(self: *@This(), value: field.type) void {
                        std.debug.print("setting {s}\n", .{field.name});
                        const parent = self.valuePtr();
                        @field(parent.value, field.name) = value;
                    }
                    fn valuePtr(self: *@This()) *align(@alignOf(@This())) Val(T) {
                        const parent: *align(@alignOf(@This())) Val(T) = @fieldParentPtr(field.name, self);
                        return parent;
                    }
                };
                types[idx] = NT;
                attrs[idx] = .{
                    .default_value_ptr = &NT{},
                };
            },
        }
    }
    inline for (ti.decls, 0..) |decl, idx| {
        names[ti.fields.len + idx] = decl.name;
        const fi = @typeInfo(@TypeOf(@field(T, decl.name))).@"fn";

        const NT = struct {
            pub fn call(self: @This(), args: Args(fi.params)) (fi.return_type orelse void) {
                _ = self;
                std.debug.print("calling {s}\n", .{decl.name});
                @call(.auto, @field(T, decl.name), args);
                std.debug.print("done calling {s}\n", .{decl.name});
            }
        };
        types[ti.fields.len + idx] = NT;
        attrs[ti.fields.len + idx] = .{ .default_value_ptr = &NT{} };
    }
    names[fieldCount - 1] = "value";
    types[fieldCount - 1] = T;
    attrs[fieldCount - 1] = .{
        .@"comptime" = false,
        .@"align" = @alignOf(T),
        .default_value_ptr = &T{},
    };

    return @Struct(.auto, null, &names, &types, &attrs);
}

pub fn main() !void {
    var a = Val(struct {
        i: i32 = 123,
        j: i32 = 456,
        s: struct {
            i: i32 = 789,
        } = .{},
        pub fn b() void {
            std.debug.print("B!\n", .{});
        }
        pub fn c(self: @This()) void {
            std.debug.print("C! ({})\n", .{self});
        }
        pub fn d(self: @This(), i: i32) void {
            self.i += i;
            std.debug.print("D! ({})\n", .{self});
        }
    }){};
    std.debug.print("[{}] {}\n{}\n", .{ @TypeOf(a), a, a.value });
    // a.b.call(.{});
    // a.c.call(.{a});
    // a.c.call(.{a, 5});
    std.debug.print("{}\n", .{a.i.get()});
    std.debug.print("{}\n", .{a.j.get()});
    a.j.set(9);
    std.debug.print("{}\n", .{a.j.get()});
    std.debug.print("{}\n", .{a.s.i.get()});
}