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(']'); }