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 }