treesummaryrefslogcommitdiff
diff options
context:
space:
mode:
authorPatrick2026-05-17 17:44:09 +0000
committerPatrick2026-05-17 17:44:09 +0000
commitfbe506286ace10bb7d9fbd8114a34ae651b7e770 (patch)
tree6ccbf9028077eb10337ccf10d4862534d940c084
parent5a9132af42bdcae15a7ceabb171aac10a5ee9a3a (diff)
downloadreactive-zig-fbe506286ace10bb7d9fbd8114a34ae651b7e770.tar.gz
reactive-zig-fbe506286ace10bb7d9fbd8114a34ae651b7e770.zip
update codeHEADmain
-rw-r--r--src/refl.zig245
1 files changed, 198 insertions, 47 deletions
diff --git a/src/refl.zig b/src/refl.zig
index 5cfcc52..1a34aad 100644
--- a/src/refl.zig
+++ b/src/refl.zig
@@ -1,9 +1,19 @@
const std = @import("std");
-fn Args(params: []const std.builtin.Type.Fn.Param) type {
- var argTypes: [params.len]type = undefined;
+const List = std.ArrayList;
- for (params, 0..) |p, i| {
+fn Args(comptime T: type, params: []const std.builtin.Type.Fn.Param) type {
+ const method = params.len > 0 and switch (@typeInfo(params[0].type.?)) {
+ .pointer => |p| p.child == T,
+ else => params[0].type == T,
+ };
+
+ const argCount = if (method) params.len - 1 else params.len;
+
+ var argTypes: [argCount]type = undefined;
+
+ for (0..argCount) |i| {
+ const p = if (method) params[i + 1] else params[i];
argTypes[i] = p.type.?;
}
@@ -11,14 +21,65 @@ fn Args(params: []const std.builtin.Type.Fn.Param) type {
}
fn Val(comptime T: type) type {
- const ti = @typeInfo(T).@"struct";
+ const ti: std.builtin.Type.Struct = switch (@typeInfo(T)) {
+ .@"struct" => |s| s,
+ .pointer => |p| @typeInfo(p.child).@"struct",
+ else => @compileError("only struct types allowed"),
+ };
- const fieldCount = ti.fields.len + ti.decls.len + 1;
+ const fieldCount = ti.fields.len + ti.decls.len + 1; // + 1 for value
var names: [fieldCount][]const u8 = undefined;
var types: [fieldCount]type = undefined;
var attrs: [fieldCount]std.builtin.Type.StructField.Attributes = undefined;
+ // value
+ {
+ names[fieldCount - 1] = "value";
+ const NT = struct {
+ value: T = undefined,
+
+ pub fn init(self: *@This(), t: T) void {
+ self.value = t;
+
+ inline for (ti.fields) |field| {
+ const parent: *Val(T) = @alignCast(@fieldParentPtr("value", self));
+ @field(parent, field.name).value.init(&@field(self.value, field.name));
+ }
+ inline for (ti.decls) |decl| {
+ const parent: *Val(T) = @alignCast(@fieldParentPtr("value", self));
+ @field(parent, decl.name).init();
+ }
+ }
+
+ pub fn deinit(self: *@This(), alloc: std.mem.Allocator) void {
+ inline for (ti.fields) |field| {
+ const parent: *Val(T) = @alignCast(@fieldParentPtr("value", self));
+ @field(parent, field.name).value.deinit(alloc);
+ }
+ inline for (ti.decls) |decl| {
+ const parent: *Val(T) = @alignCast(@fieldParentPtr("value", self));
+ @field(parent, decl.name).deinit(alloc);
+ }
+ }
+
+ pub fn get(self: *@This()) if (@typeInfo(T) == .pointer) T else *T {
+ if (@typeInfo(T) == .pointer) {
+ return self.value;
+ } else {
+ return &self.value;
+ }
+ }
+ };
+ types[fieldCount - 1] = NT;
+ attrs[fieldCount - 1] = .{
+ .@"comptime" = false,
+ .@"align" = @alignOf(NT),
+ .default_value_ptr = &NT{},
+ };
+ }
+
+ // fields
inline for (ti.fields, 0..) |field, idx| {
if (field.is_comptime)
@compileError("implement skipping comptime fields");
@@ -27,69 +88,122 @@ fn Val(comptime T: type) type {
switch (fi) {
.@"struct" => {
names[idx] = field.name;
- const NT = Val(field.type);
+ const NT = Val(*field.type);
types[idx] = NT;
- attrs[idx] = .{
- .default_value_ptr = &NT{},
- };
+ attrs[idx] = .{};
},
else => {
names[idx] = field.name;
const NT = struct {
+ const _NT = @This();
+
+ value: struct {
+ value: *field.type = undefined,
+
+ pub fn init(self: *@This(), value: *field.type) void {
+ self.value = value;
+ var parent: *_NT = @fieldParentPtr("value", self);
+ parent.on_get = .empty;
+ parent.on_set = .empty;
+ }
+
+ pub fn deinit(self: *@This(), alloc: std.mem.Allocator) void {
+ var parent: *_NT = @fieldParentPtr("value", self);
+ parent.on_get.clearAndFree(alloc);
+ parent.on_set.clearAndFree(alloc);
+ }
+ },
+ on_get: List(*const fn (field.type) void) = .empty,
+ on_set: List(*const fn (field.type) void) = .empty,
+
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);
+ for (self.on_get.items) |cb| cb(self.value.value.*);
+ return self.value.value.*;
}
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;
+ for (self.on_set.items) |cb| cb(self.value.value.*);
+ self.value.value.* = value;
}
};
types[idx] = NT;
- attrs[idx] = .{
- .default_value_ptr = &NT{},
- };
+ attrs[idx] = .{};
},
}
}
- inline for (ti.decls, 0..) |decl, idx| {
- names[ti.fields.len + idx] = decl.name;
- const fi = @typeInfo(@TypeOf(@field(T, decl.name))).@"fn";
+
+ // decls
+ inline for (ti.decls, ti.fields.len..) |decl, idx| {
+ names[idx] = decl.name;
+ const T2 = if (@typeInfo(T) == .pointer) @typeInfo(T).pointer.child else T;
+ const F = @TypeOf(@field(T2, decl.name));
+ const fi = @typeInfo(F).@"fn";
+
+ const method = fi.params.len > 0 and switch (@typeInfo(fi.params[0].type.?)) {
+ .pointer => |p| p.child == T,
+ else => fi.params[0].type == T,
+ };
+ const modifying = method and switch (@typeInfo(fi.params[0].type.?)) {
+ .pointer => |p| !p.is_const,
+ else => false,
+ };
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});
+ on_call: List(*const F) = .empty,
+ on_modify: List(*const F) = .empty,
+
+ pub fn init(self: *@This()) void {
+ self.on_call = .empty;
+ self.on_modify = .empty;
+ }
+
+ pub fn deinit(self: *@This(), alloc: std.mem.Allocator) void {
+ self.on_call.clearAndFree(alloc);
+ self.on_modify.clearAndFree(alloc);
+ }
+
+ pub fn call(self: *@This(), args: Args(T, fi.params)) (fi.return_type orelse void) {
+ const allArgs = blk: {
+ if (method) {
+ const parent: *Val(T) = @alignCast(@fieldParentPtr(decl.name, self));
+ const tiv = @typeInfo(@TypeOf(parent.value.value));
+ const tip = @typeInfo(fi.params[0].type.?);
+ const arg0 =
+ if (tiv == .pointer and tip != .pointer) parent.value.value.* else if (tiv != .pointer and tip == .pointer) &parent.value.value else parent.value.value;
+ break :blk .{arg0} ++ args;
+ } else {
+ break :blk args;
+ }
+ };
+
+ @call(.auto, @field(T2, decl.name), allArgs);
+
+ for (self.on_call.items) |cb| @call(.auto, cb, allArgs);
+ if (modifying)
+ for (self.on_modify.items) |cb| @call(.auto, cb, allArgs);
}
};
- types[ti.fields.len + idx] = NT;
- attrs[ti.fields.len + idx] = .{ .default_value_ptr = &NT{} };
+ types[idx] = NT;
+ attrs[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 {
+pub fn main(init: std.process.Init) !void {
+ const A = struct {
i: i32 = 123,
j: i32 = 456,
s: struct {
i: i32 = 789,
+ t: struct {
+ j: i32 = 341,
+ pub fn yo() void {
+ std.debug.print("t.yo!\n", .{});
+ }
+ } = .{},
+ pub fn jo() void {
+ std.debug.print("s.jo!\n", .{});
+ }
} = .{},
pub fn b() void {
std.debug.print("B!\n", .{});
@@ -97,18 +211,55 @@ pub fn main() !void {
pub fn c(self: @This()) void {
std.debug.print("C! ({})\n", .{self});
}
- pub fn d(self: @This(), i: i32) void {
+ 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});
+ };
+
+ var a: Val(A) = undefined;
+ a.value.init(.{});
+ defer a.value.deinit(init.gpa);
+
+ const callbacks = struct {
+ fn set1(i: i32) void {
+ std.debug.print("{} was set\n", .{i});
+ }
+ fn get1(i: i32) void {
+ std.debug.print("{} was get\n", .{i});
+ }
+ fn f0() void {
+ std.debug.print("called!\n", .{});
+ }
+ fn f1(self: A) void {
+ std.debug.print("called ({})!\n", .{self});
+ }
+ fn f2(self: *A, i: i32) void {
+ std.debug.print("called ({}, {})!\n", .{ self, i });
+ }
+ };
+
+ try a.j.on_get.append(init.gpa, callbacks.get1);
+ try a.j.on_set.append(init.gpa, callbacks.set1);
+ try a.b.on_call.append(init.gpa, callbacks.f0);
+ try a.c.on_call.append(init.gpa, callbacks.f1);
+ try a.d.on_call.append(init.gpa, callbacks.f2);
+ try a.s.jo.on_call.append(init.gpa, callbacks.f0);
+ try a.s.t.yo.on_call.append(init.gpa, callbacks.f0);
+
+ std.debug.print("{}\n{}\n", .{ a, a.value });
+ a.b.call(.{});
+ std.debug.print("{}\n", .{a.value.get()});
+ std.debug.print("{}\n", .{a.s.value.get()});
+ a.c.call(.{});
+ a.d.call(.{5});
+ a.s.jo.call(.{});
+ a.s.t.yo.call(.{});
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.j.get()});
+ std.debug.print("{}\n", .{a.j.get()});
std.debug.print("{}\n", .{a.s.i.get()});
}