tricu

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

tree.zig (5990B)


      1 const std = @import("std");
      2 
      3 pub const NodeTag = enum(u8) {
      4     leaf = 0,
      5     stem = 1,
      6     fork = 2,
      7     app = 3,
      8 };
      9 
     10 pub const Node = union(NodeTag) {
     11     leaf,
     12     stem: struct { child: u32 },
     13     fork: struct { left: u32, right: u32 },
     14     app: struct { func: u32, arg: u32 },
     15 
     16     pub fn leafNode() Node {
     17         return .leaf;
     18     }
     19 
     20     pub fn stemNode(child: u32) Node {
     21         return .{ .stem = .{ .child = child } };
     22     }
     23 
     24     pub fn forkNode(left: u32, right: u32) Node {
     25         return .{ .fork = .{ .left = left, .right = right } };
     26     }
     27 
     28     pub fn appNode(func: u32, arg: u32) Node {
     29         return .{ .app = .{ .func = func, .arg = arg } };
     30     }
     31 };
     32 
     33 pub const NodePool = struct {
     34     allocator: std.mem.Allocator,
     35     nodes: std.ArrayList(Node),
     36 
     37     pub fn init(allocator: std.mem.Allocator) NodePool {
     38         return .{
     39             .allocator = allocator,
     40             .nodes = .empty,
     41         };
     42     }
     43 
     44     pub fn deinit(self: *NodePool) void {
     45         self.nodes.deinit(self.allocator);
     46     }
     47 
     48     pub fn push(self: *NodePool, node: Node) !u32 {
     49         const idx: u32 = @intCast(self.nodes.items.len);
     50         try self.nodes.append(self.allocator, node);
     51         return idx;
     52     }
     53 
     54     pub fn get(self: *NodePool, idx: u32) *Node {
     55         return &self.nodes.items[idx];
     56     }
     57 
     58     pub fn len(self: *const NodePool) u32 {
     59         return @intCast(self.nodes.items.len);
     60     }
     61 };
     62 
     63 pub fn sameTree(pool: anytype, a: u32, b: u32) bool {
     64     if (a == b) return true;
     65     const na = pool.nodes.items[a];
     66     const nb = pool.nodes.items[b];
     67     if (@intFromEnum(na) != @intFromEnum(nb)) return false;
     68     return switch (na) {
     69         .leaf => true,
     70         .stem => |sa| sameTree(pool, sa.child, nb.stem.child),
     71         .fork => |fa| sameTree(pool, fa.left, nb.fork.left) and sameTree(pool, fa.right, nb.fork.right),
     72         .app => |aa| sameTree(pool, aa.func, nb.app.func) and sameTree(pool, aa.arg, nb.app.arg),
     73     };
     74 }
     75 
     76 const DstArena = @import("arena.zig").Arena;
     77 
     78 /// Iterative deep-copy of a DAG from `src` into `dst`.  Uses an explicit
     79 /// heap-allocated stack so that very deep (e.g. long list) trees do not
     80 /// blow the native C stack.  Shared sub-graphs are copied once and
     81 /// re-used (the copy preserves sharing).
     82 pub fn copyTree(src: []const Node, dst: *DstArena, root: u32) !u32 {
     83     const Frame = struct {
     84         src: u32,
     85         state: u2, // 0 = discover children, 1 = allocate after children are mapped
     86     };
     87 
     88     var map = try dst.allocator.alloc(u32, src.len);
     89     defer dst.allocator.free(map);
     90     @memset(std.mem.sliceAsBytes(map), 0xFF);
     91 
     92     var stack: std.ArrayList(Frame) = .empty;
     93     defer stack.deinit(dst.allocator);
     94     try stack.append(dst.allocator, .{ .src = root, .state = 0 });
     95 
     96     while (stack.items.len > 0) {
     97         const frame = &stack.items[stack.items.len - 1];
     98         const src_idx = frame.src;
     99 
    100         if (map[src_idx] != 0xFFFFFFFF) {
    101             _ = stack.pop();
    102             continue;
    103         }
    104 
    105         if (frame.state == 0) {
    106             frame.state = 1;
    107             const node = src[src_idx];
    108             switch (node) {
    109                 .leaf => {}, // no children, fall through to allocation next iteration
    110                 .stem => |s| {
    111                     if (map[s.child] == 0xFFFFFFFF) {
    112                         try stack.append(dst.allocator, .{ .src = s.child, .state = 0 });
    113                     }
    114                 },
    115                 .fork => |f| {
    116                     const need_left = map[f.left] == 0xFFFFFFFF;
    117                     const need_right = map[f.right] == 0xFFFFFFFF;
    118                     if (need_right) {
    119                         try stack.append(dst.allocator, .{ .src = f.right, .state = 0 });
    120                     }
    121                     if (need_left) {
    122                         try stack.append(dst.allocator, .{ .src = f.left, .state = 0 });
    123                     }
    124                 },
    125                 .app => |a| {
    126                     const need_func = map[a.func] == 0xFFFFFFFF;
    127                     const need_arg = map[a.arg] == 0xFFFFFFFF;
    128                     if (need_arg) {
    129                         try stack.append(dst.allocator, .{ .src = a.arg, .state = 0 });
    130                     }
    131                     if (need_func) {
    132                         try stack.append(dst.allocator, .{ .src = a.func, .state = 0 });
    133                     }
    134                 },
    135             }
    136         } else {
    137             // All children mapped; allocate this node in dst.
    138             const node = src[src_idx];
    139             const dst_idx = switch (node) {
    140                 .leaf => try dst.alloc(.leaf),
    141                 .stem => |s| try dst.alloc(.{ .stem = .{ .child = map[s.child] } }),
    142                 .fork => |f| try dst.alloc(.{ .fork = .{ .left = map[f.left], .right = map[f.right] } }),
    143                 .app => |a| try dst.alloc(.{ .app = .{ .func = map[a.func], .arg = map[a.arg] } }),
    144             };
    145             map[src_idx] = dst_idx;
    146             _ = stack.pop();
    147         }
    148     }
    149 
    150     return map[root];
    151 }
    152 
    153 pub fn formatTree(writer: anytype, pool: anytype, idx: u32, depth: usize) !void {
    154     if (depth > 200) {
    155         try writer.writeAll("...");
    156         return;
    157     }
    158     const node = pool.nodes.items[idx];
    159     switch (node) {
    160         .leaf => try writer.writeAll("Leaf"),
    161         .stem => |s| {
    162             try writer.writeAll("Stem(");
    163             try formatTree(writer, pool, s.child, depth + 1);
    164             try writer.writeAll(")");
    165         },
    166         .fork => |f| {
    167             try writer.writeAll("Fork(");
    168             try formatTree(writer, pool, f.left, depth + 1);
    169             try writer.writeAll(", ");
    170             try formatTree(writer, pool, f.right, depth + 1);
    171             try writer.writeAll(")");
    172         },
    173         .app => |a| {
    174             try writer.writeAll("App(");
    175             try formatTree(writer, pool, a.func, depth + 1);
    176             try writer.writeAll(", ");
    177             try formatTree(writer, pool, a.arg, depth + 1);
    178             try writer.writeAll(")");
    179         },
    180     }
    181 }