lib/accy/src/kernel/interpret/spec.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const dispatch = @import("layer.zig");
2 const policy = @import("policy.zig");
3 const program = @import("../program/root.zig");
4
5 pub const Graph = struct {};
6
7 pub fn graph() With(Graph) {
8 return with(Graph{});
9 }
10
11 pub fn with(impl: anytype) With(@TypeOf(impl)) {
12 return .{ .impl = impl };
13 }
14
15 pub fn InterpretSpec(comptime Initial: type) type {
16 if (comptime @hasDecl(Initial, "attach")) return Initial;
17 return With(Initial);
18 }
19
20 pub fn asSpec(initial: anytype) InterpretSpec(@TypeOf(initial)) {
21 if (comptime @hasDecl(@TypeOf(initial), "attach")) return initial;
22 return with(initial);
23 }
24
25 pub fn deinitIfPresent(value: anytype) void {
26 const Value = @typeInfo(@TypeOf(value)).pointer.child;
27 switch (@typeInfo(Value)) {
28 .@"struct", .@"union", .@"enum", .@"opaque" => if (comptime @hasDecl(Value, "deinit")) value.deinit(),
29 else => {},
30 }
31 }
32
33 pub fn With(comptime Impl: type) type {
34 return WithPolicy(Impl, policy.implUnhandled(Impl, policy.lower));
35 }
36
37 pub fn bind(impl: anytype) WithPolicy(@TypeOf(impl), policy.transparent) {
38 return .{ .impl = impl };
39 }
40
41 pub fn bindIndexed(impl: anytype) WithPolicy(@TypeOf(impl), policy.indexed) {
42 return .{ .impl = impl };
43 }
44
45 pub fn WithPolicy(comptime Impl: type, comptime unhandled: policy.Unhandled) type {
46 return struct {
47 impl: Impl,
48
49 pub fn attach(self: @This(), next: anytype) Layer(@TypeOf(next), Impl, unhandled) {
50 return .{
51 .next = next,
52 .impl = self.impl,
53 };
54 }
55 };
56 }
57
58 pub fn stack(specs: anytype) Stack(@TypeOf(specs)) {
59 return .{ .specs = specs };
60 }
61
62 pub fn Stack(comptime Specs: type) type {
63 _ = stackLength(Specs);
64 return struct {
65 specs: Specs,
66
67 pub fn attach(self: @This(), next: anytype) StackAttach(0, Specs, @TypeOf(next)) {
68 return attachFrom(0, self.specs, next);
69 }
70 };
71 }
72
73 pub fn layer(next: anytype, impl: anytype) Layer(@TypeOf(next), @TypeOf(impl), policy.implUnhandled(@TypeOf(impl), policy.lower)) {
74 return .{
75 .next = next,
76 .impl = impl,
77 };
78 }
79
80 pub fn State(comptime Spec: type) type {
81 return @TypeOf(@as(Spec, undefined).attach(@as(*program.Builder, undefined)));
82 }
83
84 pub fn Result(comptime Spec: type) type {
85 return State(Spec).Result;
86 }
87
88 pub fn Layer(comptime Next: type, comptime Impl: type, comptime unhandled: policy.Unhandled) type {
89 return dispatch.Layer(Next, Impl, unhandled);
90 }
91
92 fn stackLength(comptime Specs: type) comptime_int {
93 const info = @typeInfo(Specs);
94 if (info != .@"struct" or !info.@"struct".is_tuple) {
95 @compileError("kernel.interpret.stack expects a tuple of interpreter specs");
96 }
97 if (info.@"struct".field_names.len == 0) {
98 @compileError("kernel.interpret.stack expects at least one interpreter spec");
99 }
100 return info.@"struct".field_names.len;
101 }
102
103 fn StackAttach(comptime index: usize, comptime Specs: type, comptime Next: type) type {
104 const len = stackLength(Specs);
105 if (index == len) return Next;
106 const Spec = @typeInfo(Specs).@"struct".field_types[index];
107 return @TypeOf(@as(Spec, undefined).attach(@as(StackAttach(index + 1, Specs, Next), undefined)));
108 }
109
110 fn attachFrom(comptime index: usize, specs: anytype, next: anytype) StackAttach(index, @TypeOf(specs), @TypeOf(next)) {
111 if (comptime index == stackLength(@TypeOf(specs))) return next;
112 const inner = attachFrom(index + 1, specs, next);
113 return @field(specs, @typeInfo(@TypeOf(specs)).@"struct".field_names[index]).attach(inner);
114 }