tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

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 }