tricu

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

codecs.zig (6857B)


      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 
      6 // ---------------------------------------------------------------------------
      7 // Number encoding/decoding
      8 // ---------------------------------------------------------------------------
      9 
     10 pub fn ofNumber(arena: *Arena, n: u64) !u32 {
     11     if (n == 0) {
     12         return try arena.alloc(.leaf);
     13     }
     14     const bit = if (n % 2 == 1) try arena.alloc(.{ .stem = .{ .child = try arena.alloc(.leaf) } }) else try arena.alloc(.leaf);
     15     const rest = try ofNumber(arena, n / 2);
     16     return try arena.alloc(.{ .fork = .{ .left = bit, .right = rest } });
     17 }
     18 
     19 pub fn toNumber(arena: *Arena, idx: u32) !?u64 {
     20     const node = try reduce.reduce(idx, arena, 10_000);
     21     const n = arena.get(node);
     22     return switch (n.*) {
     23         .leaf => 0,
     24         .stem => return null,
     25         .fork => |f| blk: {
     26             const bit_node = try reduce.reduce(f.left, arena, 10_000);
     27             const bit = arena.get(bit_node);
     28             const bit_val: u64 = switch (bit.*) {
     29                 .leaf => 0,
     30                 .stem => |s| if (arena.get(s.child).* == .leaf) 1 else return null,
     31                 else => return null,
     32             };
     33             const rest = try toNumber(arena, f.right) orelse return null;
     34             break :blk bit_val + 2 * rest;
     35         },
     36         .app => return null,
     37     };
     38 }
     39 
     40 // ---------------------------------------------------------------------------
     41 // List encoding/decoding
     42 // ---------------------------------------------------------------------------
     43 
     44 pub fn ofList(arena: *Arena, items: []const u32) !u32 {
     45     var result = try arena.alloc(.leaf);
     46     var i: usize = items.len;
     47     while (i > 0) {
     48         i -= 1;
     49         result = try arena.alloc(.{ .fork = .{ .left = items[i], .right = result } });
     50     }
     51     return result;
     52 }
     53 
     54 pub fn toList(arena: *Arena, idx: u32) !?std.ArrayList(u32) {
     55     var result = std.ArrayList(u32).empty;
     56     errdefer result.deinit(arena.allocator);
     57 
     58     var current = idx;
     59     while (true) {
     60         const node = try reduce.reduce(current, arena, 10_000);
     61         const n = arena.get(node);
     62         switch (n.*) {
     63             .leaf => return result,
     64             .stem => return null,
     65             .fork => |f| {
     66                 try result.append(arena.allocator, f.left);
     67                 current = f.right;
     68             },
     69             .app => return null,
     70         }
     71     }
     72 }
     73 
     74 // ---------------------------------------------------------------------------
     75 // String / Bytes encoding/decoding
     76 // Strings are lists of byte values (each character encoded as a number tree).
     77 // ---------------------------------------------------------------------------
     78 
     79 pub fn ofString(arena: *Arena, s: []const u8) !u32 {
     80     var bytes = try arena.allocator.alloc(u32, s.len);
     81     defer arena.allocator.free(bytes);
     82     for (s, 0..) |c, i| {
     83         bytes[i] = try ofNumber(arena, c);
     84     }
     85     return try ofList(arena, bytes);
     86 }
     87 
     88 pub fn toString(arena: *Arena, idx: u32) !?[]u8 {
     89     var list = try toList(arena, idx) orelse return null;
     90     defer list.deinit(arena.allocator);
     91     var result = try arena.allocator.alloc(u8, list.items.len);
     92     errdefer arena.allocator.free(result);
     93     for (list.items, 0..) |elem_idx, i| {
     94         const num = try toNumber(arena, elem_idx) orelse {
     95             arena.allocator.free(result);
     96             return null;
     97         };
     98         if (num > 255) {
     99             arena.allocator.free(result);
    100             return null;
    101         }
    102         result[i] = @intCast(num);
    103     }
    104     return result;
    105 }
    106 
    107 pub fn ofBytes(arena: *Arena, bytes: []const u8) !u32 {
    108     return try ofString(arena, bytes);
    109 }
    110 
    111 pub fn toBytes(arena: *Arena, idx: u32) !?[]u8 {
    112     return try toString(arena, idx);
    113 }
    114 
    115 // ---------------------------------------------------------------------------
    116 // Result unwrapping (ok/err protocol)
    117 // ok value rest = pair true (pair value rest)
    118 // err code rest = pair false (pair code rest)
    119 // ---------------------------------------------------------------------------
    120 
    121 pub const UnwrapResult = struct {
    122     ok: bool,
    123     value: u32,
    124     rest: u32,
    125 };
    126 
    127 pub fn unwrapResult(arena: *Arena, idx: u32) !?UnwrapResult {
    128     const node = try reduce.reduce(idx, arena, 10_000);
    129     const n = arena.get(node);
    130     switch (n.*) {
    131         .fork => |f| {
    132             const tag = try reduce.reduce(f.left, arena, 10_000);
    133             const rest_pair = try reduce.reduce(f.right, arena, 10_000);
    134             const rp = arena.get(rest_pair);
    135             switch (rp.*) {
    136                 .fork => |rf| {
    137                     const is_ok = tree.sameTree(arena, tag, try arena.alloc(.{ .stem = .{ .child = try arena.alloc(.leaf) } }));
    138                     return UnwrapResult{
    139                         .ok = is_ok,
    140                         .value = rf.left,
    141                         .rest = rf.right,
    142                     };
    143                 },
    144                 else => return null,
    145             }
    146         },
    147         else => return null,
    148     }
    149 }
    150 
    151 // ---------------------------------------------------------------------------
    152 // Host ABI value unwrapping
    153 // A host ABI value is: pair tag payload
    154 // ---------------------------------------------------------------------------
    155 
    156 pub const HostValue = struct {
    157     tag: u64,
    158     payload: u32,
    159 };
    160 
    161 pub fn unwrapHostValue(arena: *Arena, idx: u32) !?HostValue {
    162     const node = try reduce.reduce(idx, arena, 10_000);
    163     const n = arena.get(node);
    164     switch (n.*) {
    165         .fork => |f| {
    166             const tag_num = try toNumber(arena, f.left) orelse return null;
    167             return HostValue{ .tag = tag_num, .payload = f.right };
    168         },
    169         else => return null,
    170     }
    171 }
    172 
    173 /// Returns true if the tree is a valid boolean (Leaf=false, Stem Leaf=true).
    174 pub fn isBool(arena: *Arena, idx: u32) !bool {
    175     const node = try reduce.reduce(idx, arena, 10_000);
    176     const n = arena.get(node);
    177     return switch (n.*) {
    178         .leaf => true,
    179         .stem => |s| arena.get(s.child).* == .leaf,
    180         else => false,
    181     };
    182 }
    183 
    184 /// Extract the boolean value: false for Leaf, true for Stem Leaf.
    185 /// Returns null if the tree is not a valid boolean.
    186 pub fn toBool(arena: *Arena, idx: u32) !?bool {
    187     const node = try reduce.reduce(idx, arena, 10_000);
    188     const n = arena.get(node);
    189     return switch (n.*) {
    190         .leaf => false,
    191         .stem => |s| if (arena.get(s.child).* == .leaf) true else null,
    192         else => null,
    193     };
    194 }
    195 
    196 // ---------------------------------------------------------------------------
    197 // Host ABI tag constants
    198 // ---------------------------------------------------------------------------
    199 
    200 pub const HOST_TREE_TAG: u64 = 0;
    201 pub const HOST_STRING_TAG: u64 = 1;
    202 pub const HOST_NUMBER_TAG: u64 = 2;
    203 pub const HOST_BOOL_TAG: u64 = 3;
    204 pub const HOST_LIST_TAG: u64 = 4;
    205 pub const HOST_BYTES_TAG: u64 = 5;