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;
}
|