aboutsummaryrefslogtreecommitdiff
path: root/src/non-terminal.zig
blob: 097c603980e32237bd74d56d52286186b633a371 (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
92
93
94
95
const std = @import("std");
const CharSet = @import("char-set.zig");
const Character = @import("character.zig").Character;

const Self = @This();
pub const Rule = []Character;

name: u8,
rule_list: std.ArrayList(Rule),
first: CharSet,
follows: CharSet,

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

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

	self.rule_list.deinit();
}

pub inline fn rules(self: *Self) []Rule {
	return self.rule_list.items;
}

pub fn add_rule(self: *Self, rule: Rule) !void {
	try self.rule_list.append(rule);
}

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

	var rule = std.ArrayList(Character).init(allocator);
	errdefer rule.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 rule.append(Character.from_u8(token[0]));
	}

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

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

pub inline fn is_empty(self: *const Self) bool {
	return self.rules().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.rules().len > 0) {
		for (self.rules()[0]) |c| {
			try writer.print("{}", .{c});
		}

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

	try writer.writeByte(']');
}