aboutsummaryrefslogtreecommitdiff
path: root/src/rule-list.zig
blob: a43875053cc4bcb1137dc0f8022a5a8723adf68a (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
91
const std = @import("std");
const CharSet = @import("char-set.zig");
const Character = @import("character.zig").Character;

const Self = @This();
const Rhs = []Character;

name: u8,
rhs: std.ArrayList(Rhs),
first: CharSet,
follows: CharSet,

pub fn init(name: u8, allocator: std.mem.Allocator) Self {
	return Self {
		.name = name,
		.rhs = std.ArrayList(Rhs).init(allocator),
		.first = CharSet {},
		.follows = CharSet {},
	};
}

pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
	for (self.rhs.items) |item| {
		allocator.free(item);
	}

	self.rhs.deinit();
}

pub fn add_rule(self: *Self, rhs: Rhs) !void {
	try self.rhs.append(rhs);
}

pub fn parse_rule(
	buffer: []const u8,
	allocator: std.mem.Allocator
) !struct { u8, Rhs } {

	var rhs = std.ArrayList(Character).init(allocator);
	errdefer rhs.deinit();

	var tokens = std.mem.tokenizeAny(u8, buffer, &std.ascii.whitespace);

	const lhs = tokens.next() orelse return error.MissingLhs;
	if (lhs.len > 1) return error.InvalidLhs;

	const arrow = tokens.next() orelse return error.MissingArrow;
	if (!std.mem.eql(u8, arrow, "->")) return error.InvalidArrow;

	while (tokens.next()) |token| {
		if (token.len > 1) return error.InvalidCharacter;
		try rhs.append(Character.from_u8(token[0]));
	}

	if (rhs.items.len == 0) {
		return error.EmptyProduction;
	}

	return .{ lhs[0], try rhs.toOwnedSlice() };
}

pub inline fn is_empty(self: *const Self) bool {
	return self.rhs.items.len == 0;
}

pub fn format(
	self: *const Self,
	comptime fmt: []const u8,
	options: std.fmt.FormatOptions,
	writer: anytype,
) !void {
	_ = fmt;
	_ = options;

	try writer.print("[{c} -> ", .{ self.name });

	if (self.rhs.items.len > 0) {
		for (self.rhs.items[0]) |c| {
			try writer.print("{}", .{c});
		}

		for (self.rhs.items[1..]) |r| {
			try writer.print(" | ", .{});
			for (r) |c| {
				try writer.print("{}", .{c});
			}
		}
	}

	try writer.writeByte(']');
}