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