From 77642e5081b4ed2516dc08b3ef611c3c444ec6c5 Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Sat, 1 Aug 2026 20:10:10 +0200 Subject: restructure parser --- src/z/parser/parser.zig | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 src/z/parser/parser.zig (limited to 'src/z/parser/parser.zig') diff --git a/src/z/parser/parser.zig b/src/z/parser/parser.zig new file mode 100644 index 0000000..b73d895 --- /dev/null +++ b/src/z/parser/parser.zig @@ -0,0 +1,117 @@ +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); +} -- cgit v1.2.3-70-g09d2