diff options
Diffstat (limited to 'src/pex/block.zig')
| -rw-r--r-- | src/pex/block.zig | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/src/pex/block.zig b/src/pex/block.zig new file mode 100644 index 0000000..3ae690e --- /dev/null +++ b/src/pex/block.zig @@ -0,0 +1,90 @@ +const std = @import("std"); +const NonTerminal = @import("../non-terminal.zig"); +const Rule = @import("../rule.zig"); +const Node = @import("node.zig"); + +const Self = @This(); + +id: usize, +heads: std.ArrayListUnmanaged(*Node) = .empty, + +pub fn compile( + non_terminal: *NonTerminal, + node_pool: *std.heap.MemoryPool(Node), + allocator: std.mem.Allocator +) !Self { + var self: Self = .{ .id = non_terminal.id }; + + for (non_terminal.rules()) |*rule| { + try self.heads.append(allocator, try compile_variant(rule, node_pool)); + } + + return self; +} + +pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.heads.items) |head| { + head.deinit(); + } + self.heads.deinit(allocator); + self.* = undefined; +} + +pub fn clone( + self: *const Self, + allocator: std.mem.Allocator, + node_pool: *std.heap.MemoryPool(Node), +) !Self { + var self_clone = Self{ + .id = self.id, + .heads = try .initCapacity(allocator, self.heads.items.len), + }; + errdefer self_clone.deinit(allocator); + + for (self.heads.items) |head| { + try self_clone.heads.append(allocator, try head.clone(node_pool)); + } + + return self_clone; +} + +fn compile_variant( + rule: *Rule, + node_pool: *std.heap.MemoryPool(Node), +) !*Node { + const first = blk: { + for (rule.items, 0..) |item, index| { + switch (item) { + .terminal, .non_terminal => break :blk index, + .epsilon => {}, + } + } + + break :blk rule.items.len; + }; + + if (first == rule.items.len) { + return try Node.create(.{ .@"return" = void{} }, node_pool); + } + + const head = switch (rule.items[0]) { + .terminal => |c| try Node.create(.{ .@"check" = c }, node_pool), + .non_terminal => |id| try Node.create(.{ .@"call" = id }, node_pool), + .epsilon => unreachable, + }; + + var current = head; + for (rule.items[1..]) |item| { + const node = switch (item) { + .terminal => |c| try Node.create(.{ .@"check" = c }, node_pool), + .non_terminal => |id| try Node.create(.{ .@"call" = id }, node_pool), + .epsilon => continue, + }; + current.next = node; + current = node; + } + + current.next = try Node.create(.{ .@"return" = void{} }, node_pool); + + return head; +} |