aboutsummaryrefslogtreecommitdiff
path: root/src/pex/block.zig
blob: 3ae690e65d52aef9046f15c9b2250e4c0a720c2b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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;
}