c_abi.zig (9297B)
1 const std = @import("std"); 2 const tree = @import("tree.zig"); 3 const Arena = @import("arena.zig").Arena; 4 const reduce = @import("reduce.zig"); 5 const codecs = @import("codecs.zig"); 6 const kernel = @import("kernel.zig"); 7 const bundle = @import("bundle.zig"); 8 const io_driver = @import("io_driver.zig"); 9 10 /// Opaque handle for the C API. Layout is not exposed to C. 11 /// Holds a persistent arena for user-built terms and the kernel. 12 pub const ArbCtx = struct { 13 gpa: std.mem.Allocator, 14 arena: Arena, 15 kernel_root: u32, 16 }; 17 18 // --------------------------------------------------------------------------- 19 // Context lifecycle 20 // --------------------------------------------------------------------------- 21 22 export fn arboricx_init() ?*ArbCtx { 23 const ptr = std.heap.smp_allocator.create(ArbCtx) catch return null; 24 ptr.gpa = std.heap.smp_allocator; 25 ptr.arena = Arena.init(std.heap.smp_allocator); 26 ptr.kernel_root = kernel.loadKernel(&ptr.arena) catch { 27 ptr.arena.deinit(); 28 std.heap.smp_allocator.destroy(ptr); 29 return null; 30 }; 31 return ptr; 32 } 33 34 export fn arboricx_free(ctx: *ArbCtx) void { 35 ctx.arena.deinit(); 36 ctx.gpa.destroy(ctx); 37 } 38 39 export fn arboricx_free_buf(_: *ArbCtx, ptr: [*]u8, len: usize) void { 40 std.heap.smp_allocator.free(ptr[0..len]); 41 } 42 43 // --------------------------------------------------------------------------- 44 // Tree construction (all write into the persistent arena) 45 // --------------------------------------------------------------------------- 46 47 export fn arb_leaf(ctx: *ArbCtx) u32 { 48 return ctx.arena.alloc(.leaf) catch 0; 49 } 50 51 export fn arb_stem(ctx: *ArbCtx, child: u32) u32 { 52 return ctx.arena.alloc(.{ .stem = .{ .child = child } }) catch 0; 53 } 54 55 export fn arb_fork(ctx: *ArbCtx, left: u32, right: u32) u32 { 56 return ctx.arena.alloc(.{ .fork = .{ .left = left, .right = right } }) catch 0; 57 } 58 59 export fn arb_app(ctx: *ArbCtx, func: u32, arg: u32) u32 { 60 return ctx.arena.alloc(.{ .app = .{ .func = func, .arg = arg } }) catch 0; 61 } 62 63 // --------------------------------------------------------------------------- 64 // Tree inspection (Layer 1 — for custom IO drivers and non-POSIX hosts) 65 // All return 1 on success / true, 0 on failure / false. 66 // --------------------------------------------------------------------------- 67 68 export fn arb_is_leaf(ctx: *ArbCtx, root: u32) c_int { 69 if (root >= ctx.arena.len()) return 0; 70 return if (ctx.arena.nodes.items[root] == .leaf) 1 else 0; 71 } 72 73 export fn arb_is_stem(ctx: *ArbCtx, root: u32) c_int { 74 if (root >= ctx.arena.len()) return 0; 75 return if (ctx.arena.nodes.items[root] == .stem) 1 else 0; 76 } 77 78 export fn arb_is_fork(ctx: *ArbCtx, root: u32) c_int { 79 if (root >= ctx.arena.len()) return 0; 80 return if (ctx.arena.nodes.items[root] == .fork) 1 else 0; 81 } 82 83 export fn arb_is_app(ctx: *ArbCtx, root: u32) c_int { 84 if (root >= ctx.arena.len()) return 0; 85 return if (ctx.arena.nodes.items[root] == .app) 1 else 0; 86 } 87 88 export fn arb_get_stem_child(ctx: *ArbCtx, root: u32, out: *u32) c_int { 89 if (root >= ctx.arena.len()) return 0; 90 const node = ctx.arena.nodes.items[root]; 91 if (node != .stem) return 0; 92 out.* = node.stem.child; 93 return 1; 94 } 95 96 export fn arb_get_fork_children(ctx: *ArbCtx, root: u32, out_left: *u32, out_right: *u32) c_int { 97 if (root >= ctx.arena.len()) return 0; 98 const node = ctx.arena.nodes.items[root]; 99 if (node != .fork) return 0; 100 out_left.* = node.fork.left; 101 out_right.* = node.fork.right; 102 return 1; 103 } 104 105 export fn arb_get_app_func_arg(ctx: *ArbCtx, root: u32, out_func: *u32, out_arg: *u32) c_int { 106 if (root >= ctx.arena.len()) return 0; 107 const node = ctx.arena.nodes.items[root]; 108 if (node != .app) return 0; 109 out_func.* = node.app.func; 110 out_arg.* = node.app.arg; 111 return 1; 112 } 113 114 // --------------------------------------------------------------------------- 115 // Reduction 116 // --------------------------------------------------------------------------- 117 /// Reduces `root` in a *fresh* scratch arena so that garbage from previous 118 /// reductions never accumulates. The kernel and term are deep-copied into 119 /// the scratch arena, reduced there, and the result is copied back into the 120 /// persistent arena. 121 // --------------------------------------------------------------------------- 122 123 export fn arb_reduce(ctx: *ArbCtx, root: u32, fuel: u64) u32 { 124 // 1. Fresh scratch arena 125 var scratch = Arena.init(ctx.gpa); 126 defer scratch.deinit(); 127 128 // 2. Deep-copy the term (which may reference kernel nodes) into scratch 129 const scratch_root = tree.copyTree(ctx.arena.nodes.items, &scratch, root) catch return 0; 130 131 // 3. Reduce in scratch 132 const scratch_result = reduce.reduce(scratch_root, &scratch, fuel) catch return 0; 133 134 // 4. Copy the result back to the persistent arena 135 return tree.copyTree(scratch.nodes.items, &ctx.arena, scratch_result) catch 0; 136 } 137 138 // --------------------------------------------------------------------------- 139 // Codec constructors 140 // --------------------------------------------------------------------------- 141 142 export fn arb_of_number(ctx: *ArbCtx, n: u64) u32 { 143 return codecs.ofNumber(&ctx.arena, n) catch 0; 144 } 145 146 export fn arb_of_string(ctx: *ArbCtx, s: [*:0]const u8) u32 { 147 const slice = std.mem.sliceTo(s, 0); 148 return codecs.ofString(&ctx.arena, slice) catch 0; 149 } 150 151 export fn arb_of_bytes(ctx: *ArbCtx, bytes: [*]const u8, len: usize) u32 { 152 return codecs.ofBytes(&ctx.arena, bytes[0..len]) catch 0; 153 } 154 155 export fn arb_of_list(ctx: *ArbCtx, items: [*]const u32, len: usize) u32 { 156 return codecs.ofList(&ctx.arena, items[0..len]) catch 0; 157 } 158 159 // --------------------------------------------------------------------------- 160 // Codec destructors 161 // Return 1 on success, 0 on failure. 162 // --------------------------------------------------------------------------- 163 164 export fn arb_to_number(ctx: *ArbCtx, root: u32, out: *u64) c_int { 165 const n = codecs.toNumber(&ctx.arena, root) catch return 0; 166 if (n == null) return 0; 167 out.* = n.?; 168 return 1; 169 } 170 171 export fn arb_to_string(ctx: *ArbCtx, root: u32, out_ptr: **u8, out_len: *usize) c_int { 172 const s = codecs.toString(&ctx.arena, root) catch return 0; 173 if (s == null) return 0; 174 out_ptr.* = @ptrCast(s.?.ptr); 175 out_len.* = s.?.len; 176 return 1; 177 } 178 179 export fn arb_to_bytes(ctx: *ArbCtx, root: u32, out_ptr: **u8, out_len: *usize) c_int { 180 return arb_to_string(ctx, root, out_ptr, out_len); 181 } 182 183 export fn arb_to_bool(ctx: *ArbCtx, root: u32, out: *c_int) c_int { 184 const b = codecs.toBool(&ctx.arena, root) catch return 0; 185 if (b == null) return 0; 186 out.* = if (b.?) 1 else 0; 187 return 1; 188 } 189 190 // --------------------------------------------------------------------------- 191 // Result unwrapping 192 // Return 1 on success, 0 on failure. 193 // --------------------------------------------------------------------------- 194 195 export fn arb_unwrap_result(ctx: *ArbCtx, root: u32, out_ok: *c_int, out_value: *u32, out_rest: *u32) c_int { 196 const r = codecs.unwrapResult(&ctx.arena, root) catch return 0; 197 if (r == null) return 0; 198 out_ok.* = if (r.?.ok) 1 else 0; 199 out_value.* = r.?.value; 200 out_rest.* = r.?.rest; 201 return 1; 202 } 203 204 export fn arb_unwrap_host_value(ctx: *ArbCtx, root: u32, out_tag: *u64, out_payload: *u32) c_int { 205 const hv = codecs.unwrapHostValue(&ctx.arena, root) catch return 0; 206 if (hv == null) return 0; 207 out_tag.* = hv.?.tag; 208 out_payload.* = hv.?.payload; 209 return 1; 210 } 211 212 // --------------------------------------------------------------------------- 213 // IO driver (Layer 2 — POSIX interaction-tree runtime) 214 // --------------------------------------------------------------------------- 215 216 pub const arb_io_perms_t = extern struct { 217 allow_read_all: c_int, 218 allow_write_all: c_int, 219 }; 220 221 export fn arb_run_io(ctx: *ArbCtx, program: u32, perms: ?*const arb_io_perms_t) u32 { 222 const zig_perms = if (perms) |p| io_driver.IOPerms{ 223 .allow_read_all = p.allow_read_all != 0, 224 .allow_write_all = p.allow_write_all != 0, 225 } else io_driver.IOPerms{}; 226 return io_driver.runIO(ctx.gpa, &ctx.arena, program, zig_perms) catch 0; 227 } 228 229 // --------------------------------------------------------------------------- 230 // Kernel entrypoints 231 // --------------------------------------------------------------------------- 232 233 export fn arb_kernel_root(ctx: *ArbCtx) u32 { 234 return ctx.kernel_root; 235 } 236 237 // --------------------------------------------------------------------------- 238 // Native bundle loading (fast path — bypasses the Tricu kernel) 239 // --------------------------------------------------------------------------- 240 241 /// Load a named export from an Arboricx bundle directly into the arena. 242 /// Returns the arena index of the exported term, or 0 on error. 243 export fn arb_load_bundle(ctx: *ArbCtx, bytes: [*]const u8, len: usize, name: [*:0]const u8) u32 { 244 const name_slice = std.mem.sliceTo(name, 0); 245 return bundle.loadBundleExport(&ctx.arena, bytes[0..len], name_slice) catch 0; 246 } 247 248 /// Load the default root from an Arboricx bundle directly into the arena. 249 /// Returns the arena index of the root term, or 0 on error. 250 export fn arb_load_bundle_default(ctx: *ArbCtx, bytes: [*]const u8, len: usize) u32 { 251 return bundle.loadBundleDefaultRoot(&ctx.arena, bytes[0..len]) catch 0; 252 }