aboutsummaryrefslogtreecommitdiff
path: root/src/pex/block.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/pex/block.zig')
-rw-r--r--src/pex/block.zig90
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;
+}