summaryrefslogtreecommitdiff
path: root/src/estd/parser/root.zig
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2025-02-01 12:47:35 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2025-02-01 12:47:35 +0100
commit85bcada8cf78bdf2bfb3be583289686026e0f25e (patch)
tree0ce404c4840432db9b6d3addd3947a736d103382 /src/estd/parser/root.zig
parent2ce14aec655589f00442ab469b9d877a143eeefd (diff)
screen: start drm implementation
Diffstat (limited to 'src/estd/parser/root.zig')
-rw-r--r--src/estd/parser/root.zig284
1 files changed, 284 insertions, 0 deletions
diff --git a/src/estd/parser/root.zig b/src/estd/parser/root.zig
new file mode 100644
index 0000000..b168a88
--- /dev/null
+++ b/src/estd/parser/root.zig
@@ -0,0 +1,284 @@
+const std = @import("std");
+const Cursor = @import("../cursor.zig").Cursor;
+pub const common = @import("common.zig");
+pub const Context = @import("context.zig").Context;
+
+
+pub fn ParseFn(comptime I: type, comptime O: type) type {
+ return *const fn (ctx: *Context(I)) anyerror!O;
+}
+
+pub fn Parser(comptime I: type, comptime O: type, comptime parse_fn: ParseFn(I, O)) type {
+ return struct {
+ const Self = @This();
+
+ pub fn parse(ctx: *Context(I)) !O {
+ return try parse_fn(ctx);
+ }
+
+ pub fn or_else(comptime other: ParseFn(I, O)) type {
+ return Parser(I, O, struct {
+ pub fn parse(ctx: *Context(I)) !O {
+ return parse_fn(ctx) catch other(ctx);
+ }
+ }.parse);
+ }
+
+ pub fn and_skip(comptime skip: anytype) type {
+ return Parser(I, O, struct {
+ pub fn parse(ctx: *Context(I)) !O {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ _ = try skip(ctx);
+ return try parse_fn(ctx);
+ }
+ }.parse);
+ }
+
+ pub fn then_skip(comptime skip: anytype) type {
+ return Parser(I, O, struct {
+ pub fn parse(ctx: *Context(I)) !O {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+ const value = try parse_fn(ctx);
+ _ = try skip(ctx);
+ return value;
+ }
+ }.parse);
+ }
+
+ pub fn then(comptime O2: type, comptime other: ParseFn(I, O2)) type {
+ const CombinedOutput = struct { O, O2 };
+
+ return Parser(I, CombinedOutput, struct {
+ pub fn parse(ctx: *Context(I)) !CombinedOutput {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ return .{ try parse_fn(ctx), try other(ctx) };
+ }
+ }.parse);
+ }
+
+ pub fn zero_or_more() type {
+ return Parser(I, std.ArrayList(O), struct {
+ pub fn parse(ctx: *Context(I)) !std.ArrayList(O) {
+ var values = std.ArrayList(O).init(ctx.allocator);
+ errdefer values.deinit();
+
+ while (parse_fn(ctx)) |value| {
+ try values.append(value);
+ } else |_| {}
+
+ return values;
+ }
+ }.parse);
+ }
+
+ pub fn repeat(n: usize) type {
+ return Parser(I, [n]O, struct {
+ pub fn parse(ctx: *Context(I)) ![n]O {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ var values: [n]O = undefined;
+
+ for (&values) |*value| {
+ value.* = try parse_fn(ctx);
+ }
+
+ return values;
+ }
+ }.parse);
+ }
+
+ pub fn map(M: type, map_fn: *const fn (value: O) M) type {
+ return Parser(I, M, struct {
+ pub fn parse(ctx: *Context(I)) !M {
+ return map_fn(try parse_fn(ctx));
+ }
+ }.parse);
+ }
+
+ pub fn end() type {
+ return Parser(I, O, struct {
+ pub fn parse(ctx: *Context(I)) !O {
+ const value = try parse_fn(ctx);
+ _ = ctx.cursor.peek(1) catch return value;
+ return error.NotEnd;
+ }
+ }.parse);
+ }
+ };
+}
+
+fn parse_zero(ctx: *Context(u8)) !u8 {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ const value = try ctx.cursor.consume(1);
+
+ if (value[0] == '0') {
+ return 0;
+ }
+
+ return error.NotZero;
+}
+
+fn parse_one(ctx: *Context(u8)) !u8 {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ const value = try ctx.cursor.consume(1);
+
+ if (value[0] == '1') {
+ return 1;
+ }
+
+ return error.NotOne;
+}
+
+
+test "direct" {
+ const ZeroParser = Parser(u8, u8, parse_zero);
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("0"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectEqual(0, try ZeroParser.parse(&ctx));
+
+ ctx.cursor = Cursor(u8).init("1");
+ try std.testing.expectError(error.NotZero, ZeroParser.parse(&ctx));
+}
+
+test "or_else" {
+ const ZeroParser = Parser(u8, u8, parse_zero);
+ const ZeroAndOneParser = ZeroParser.or_else(parse_one);
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("0"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectEqual(0, try ZeroAndOneParser.parse(&ctx));
+
+ ctx.cursor = Cursor(u8).init("1");
+ try std.testing.expectEqual(1, try ZeroAndOneParser.parse(&ctx));
+
+ ctx.cursor = Cursor(u8).init("2");
+ try std.testing.expectError(error.NotZero, ZeroParser.parse(&ctx));
+}
+
+test "then" {
+ const ZeroParser = Parser(u8, u8, parse_zero);
+ const ZeroThenOneParser = ZeroParser.then(u8, parse_one);
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("01"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectEqual(.{ 0, 1 }, try ZeroThenOneParser.parse(&ctx));
+
+ ctx.cursor = Cursor(u8).init("11");
+ try std.testing.expectError(error.NotZero, ZeroThenOneParser.parse(&ctx));
+
+ ctx.cursor = Cursor(u8).init("00");
+ try std.testing.expectError(error.NotOne, ZeroThenOneParser.parse(&ctx));
+}
+
+test "zero_or_more" {
+ const ZeroParser = Parser(u8, u8, parse_zero);
+ const ZeroWildcardParser = ZeroParser.zero_or_more();
+ const ZeroOrOneWildcardParser = ZeroParser.or_else(parse_one).zero_or_more();
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("0000"),
+ .allocator = std.testing.allocator,
+ };
+ var result = try ZeroWildcardParser.parse(&ctx);
+ try std.testing.expectEqualSlices(u8, &[_]u8{0, 0, 0, 0}, result.items);
+ result.deinit();
+
+ ctx.cursor = Cursor(u8).init("00001");
+ result = try ZeroWildcardParser.parse(&ctx);
+ try std.testing.expectEqualSlices(u8, &[_]u8{0, 0, 0, 0}, result.items);
+ result.deinit();
+
+ ctx.cursor = Cursor(u8).init("0101110");
+ result = try ZeroOrOneWildcardParser.parse(&ctx);
+ try std.testing.expectEqualSlices(u8, &[_]u8{0, 1, 0, 1, 1, 1, 0}, result.items);
+ result.deinit();
+
+ ctx.cursor = Cursor(u8).init("0101110abc");
+ result = try ZeroOrOneWildcardParser.parse(&ctx);
+ try std.testing.expectEqualSlices(u8, &[_]u8{0, 1, 0, 1, 1, 1, 0}, result.items);
+ result.deinit();
+}
+
+test "repeat" {
+ const ZeroParser = Parser(u8, u8, parse_zero);
+ const FourZerosParser = ZeroParser.repeat(4);
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("00001"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectEqualSlices(
+ u8,
+ &[_]u8{0, 0, 0, 0},
+ &(try FourZerosParser.parse(&ctx))
+ );
+
+ ctx.cursor = Cursor(u8).init("0001");
+ try std.testing.expectError(
+ error.NotZero,
+ FourZerosParser.parse(&ctx)
+ );
+}
+
+test "map" {
+ const ZeroOrOneParser = Parser(u8, u8, parse_zero).or_else(parse_one);
+ const ZeroOrOneMappedParser = ZeroOrOneParser.map(u8, struct {
+ pub fn map(i: u8) u8 {
+ return i * 2;
+ }
+ }.map).zero_or_more();
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("00110101"),
+ .allocator = std.testing.allocator,
+ };
+ const result = try ZeroOrOneMappedParser.parse(&ctx);
+ try std.testing.expectEqualSlices(u8, &[_]u8{ 0, 0, 2, 2, 0, 2, 0, 2 }, result.items);
+ defer result.deinit();
+}
+
+test "cursor rollback" {
+ const DoubleZeroOrOneParser = Parser(u8, u8, parse_zero).repeat(3).or_else(
+ Parser(u8, u8, parse_zero).repeat(2).then(u8, parse_one).map([3]u8, struct {
+ pub fn map(r: struct { [2]u8, u8 }) [3]u8 {
+ const array, const last = r;
+ return array ++ .{ last };
+ }
+ }.map).parse
+ );
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("0001"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectEqualSlices(u8, &[_]u8{ 0, 0, 0 }, &(try DoubleZeroOrOneParser.parse(&ctx)));
+
+ ctx.cursor = Cursor(u8).init("0010");
+ try std.testing.expectEqualSlices(u8, &[_]u8{ 0, 0, 1 }, &(try DoubleZeroOrOneParser.parse(&ctx)));
+}
+
+test "cursor error touched slice" {
+ const OneThenDoubleZeroParser = Parser(u8, u8, parse_one).then([2]u8, Parser(u8, u8, parse_zero).repeat(2).parse);
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("10110"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectError(error.NotZero, OneThenDoubleZeroParser.parse(&ctx));
+ try std.testing.expectEqualSlices(u8, "101", ctx.cursor.touched_slice());
+}