const std = @import("std"); const mod = @import("root.zig"); const Result = mod.Result; pub fn Parser(T: type) type { const R = Result(T); return struct { parse: *const fn ([]const u8) anyerror!R, pub fn parseAll(self: *const @This(), buffer: []const u8) !T { const value, const next = try self.parse(buffer); if (next.len > 0) { return error.NotExhaustive; } return value; } pub fn fromType(P: type) @This() { return .{ .parse = P.parse }; } pub fn variants(V: type) @This() { if (@typeInfo(T) != .@"union") { @compileError("variants is only allowed with an union as result type"); } return .{ .parse = struct { fn parse(buffer: []const u8) !R { inline for (std.meta.fields(T)) |field| { if (@field(V, field.name).parse(buffer)) |result| { const value, const next = result; return .{ @unionInit(T, field.name, value), next, }; } else |_| {} } return error.Variants; } }.parse, }; } pub fn any(P: type, comptime parsers: []const struct { Parser(P), T }) @This() { return .{ .parse = struct { fn parse(buffer: []const u8) !R { inline for (parsers) |pair| { const parser, const value = pair; if (parser.parse(buffer)) |result| { _, const next = result; return .{ value, next }; } else |_| {} } return error.Any; } }.parse, }; } }; } test "variants" { const Char = union(enum) { upper: u8, lower: u8, space: void, }; const parser: Parser(Char) = .variants(struct { const upper: Parser(u8) = .any(void, &.{ .{ mod.builtin.literal("A"), 'A' }, .{ mod.builtin.literal("B"), 'B' }, }); const lower: Parser(u8) = .any(void, &.{ .{ mod.builtin.literal("a"), 'a' }, .{ mod.builtin.literal("b"), 'b' }, }); const space: Parser(void) = mod.builtin.literal(" "); }); { const result = try parser.parseAll("a"); try std.testing.expect(result.lower == 'a'); } { const result = try parser.parseAll("b"); try std.testing.expect(result.lower == 'b'); } { const result = try parser.parseAll("A"); try std.testing.expect(result.upper == 'A'); } { const result = try parser.parseAll("B"); try std.testing.expect(result.upper == 'B'); } { const result = try parser.parseAll(" "); try std.testing.expect(result == .space); } try std.testing.expect(parser.parseAll("c") == error.Variants); }