tree.zig (6188B)
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 /// Deep-copy a term from a source node slice into a destination Arena, returning the new index. 77 /// Uses recursion; assumes the tree is finite and well-formed. 78 const DstArena = @import("arena.zig").Arena; 79 80 /// Iterative deep-copy of a DAG from `src` into `dst`. Uses an explicit 81 /// heap-allocated stack so that very deep (e.g. long list) trees do not 82 /// blow the native C stack. Shared sub-graphs are copied once and 83 /// re-used (the copy preserves sharing). 84 pub fn copyTree(src: []const Node, dst: *DstArena, root: u32) !u32 { 85 const Frame = struct { 86 src: u32, 87 state: u2, // 0 = discover children, 1 = allocate after children are mapped 88 }; 89 90 var map = try dst.allocator.alloc(u32, src.len); 91 defer dst.allocator.free(map); 92 @memset(std.mem.sliceAsBytes(map), 0xFF); 93 94 var stack = try dst.allocator.alloc(Frame, src.len); 95 defer dst.allocator.free(stack); 96 var sp: usize = 0; 97 98 stack[sp] = .{ .src = root, .state = 0 }; 99 sp += 1; 100 101 while (sp > 0) { 102 const frame = &stack[sp - 1]; 103 const src_idx = frame.src; 104 105 if (map[src_idx] != 0xFFFFFFFF) { 106 sp -= 1; 107 continue; 108 } 109 110 if (frame.state == 0) { 111 frame.state = 1; 112 const node = src[src_idx]; 113 switch (node) { 114 .leaf => {}, // no children, fall through to allocation next iteration 115 .stem => |s| { 116 if (map[s.child] == 0xFFFFFFFF) { 117 stack[sp] = .{ .src = s.child, .state = 0 }; 118 sp += 1; 119 } 120 }, 121 .fork => |f| { 122 const need_left = map[f.left] == 0xFFFFFFFF; 123 const need_right = map[f.right] == 0xFFFFFFFF; 124 if (need_right) { 125 stack[sp] = .{ .src = f.right, .state = 0 }; 126 sp += 1; 127 } 128 if (need_left) { 129 stack[sp] = .{ .src = f.left, .state = 0 }; 130 sp += 1; 131 } 132 }, 133 .app => |a| { 134 const need_func = map[a.func] == 0xFFFFFFFF; 135 const need_arg = map[a.arg] == 0xFFFFFFFF; 136 if (need_arg) { 137 stack[sp] = .{ .src = a.arg, .state = 0 }; 138 sp += 1; 139 } 140 if (need_func) { 141 stack[sp] = .{ .src = a.func, .state = 0 }; 142 sp += 1; 143 } 144 }, 145 } 146 } else { 147 // All children mapped; allocate this node in dst. 148 const node = src[src_idx]; 149 const dst_idx = switch (node) { 150 .leaf => try dst.alloc(.leaf), 151 .stem => |s| try dst.alloc(.{ .stem = .{ .child = map[s.child] } }), 152 .fork => |f| try dst.alloc(.{ .fork = .{ .left = map[f.left], .right = map[f.right] } }), 153 .app => |a| try dst.alloc(.{ .app = .{ .func = map[a.func], .arg = map[a.arg] } }), 154 }; 155 map[src_idx] = dst_idx; 156 sp -= 1; 157 } 158 } 159 160 return map[root]; 161 } 162 163 pub fn formatTree(writer: anytype, pool: anytype, idx: u32, depth: usize) !void { 164 if (depth > 200) { 165 try writer.writeAll("..."); 166 return; 167 } 168 const node = pool.nodes.items[idx]; 169 switch (node) { 170 .leaf => try writer.writeAll("Leaf"), 171 .stem => |s| { 172 try writer.writeAll("Stem("); 173 try formatTree(writer, pool, s.child, depth + 1); 174 try writer.writeAll(")"); 175 }, 176 .fork => |f| { 177 try writer.writeAll("Fork("); 178 try formatTree(writer, pool, f.left, depth + 1); 179 try writer.writeAll(", "); 180 try formatTree(writer, pool, f.right, depth + 1); 181 try writer.writeAll(")"); 182 }, 183 .app => |a| { 184 try writer.writeAll("App("); 185 try formatTree(writer, pool, a.func, depth + 1); 186 try writer.writeAll(", "); 187 try formatTree(writer, pool, a.arg, depth + 1); 188 try writer.writeAll(")"); 189 }, 190 } 191 }