diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2026-07-30 07:46:37 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2026-07-30 07:46:37 +0200 |
| commit | c8744f9f6a28a23b83603435a3ad987f0d9f181b (patch) | |
| tree | e0257c54661e9f34b6d2ce8658346f36aa0ff9e3 /src/z/js | |
| parent | d1776161dcc57b600ce7b18c5e7179757491fbc2 (diff) | |
temporal commit
Diffstat (limited to 'src/z/js')
| -rw-r--r-- | src/z/js/parser/builtin.zig | 17 | ||||
| -rw-r--r-- | src/z/js/parser/lexical_grammar.zig | 3 | ||||
| -rw-r--r-- | src/z/js/parser/parser.zig | 117 | ||||
| -rw-r--r-- | src/z/js/parser/result.zig | 4 | ||||
| -rw-r--r-- | src/z/js/parser/root.zig | 11 |
5 files changed, 152 insertions, 0 deletions
diff --git a/src/z/js/parser/builtin.zig b/src/z/js/parser/builtin.zig new file mode 100644 index 0000000..fc96fc3 --- /dev/null +++ b/src/z/js/parser/builtin.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +const parser = @import("root.zig"); +const Parser = parser.Parser; +const Result = parser.Result; + +pub fn literal(lit: []const u8) Parser(void) { + return .fromType(struct { + pub fn parse(buffer: []const u8) !Result(void) { + if (std.mem.startsWith(u8, buffer, lit)) { + return .{ void{}, buffer[lit.len..] }; + } + + return error.Literal; + } + }); +} diff --git a/src/z/js/parser/lexical_grammar.zig b/src/z/js/parser/lexical_grammar.zig new file mode 100644 index 0000000..7060766 --- /dev/null +++ b/src/z/js/parser/lexical_grammar.zig @@ -0,0 +1,3 @@ +const parser = @import("root.zig"); +const Result = parser.Result; + diff --git a/src/z/js/parser/parser.zig b/src/z/js/parser/parser.zig new file mode 100644 index 0000000..b73d895 --- /dev/null +++ b/src/z/js/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); +} diff --git a/src/z/js/parser/result.zig b/src/z/js/parser/result.zig new file mode 100644 index 0000000..5cc5878 --- /dev/null +++ b/src/z/js/parser/result.zig @@ -0,0 +1,4 @@ + +pub fn Result(T: type) type { + return struct { T, []const u8 }; +} diff --git a/src/z/js/parser/root.zig b/src/z/js/parser/root.zig new file mode 100644 index 0000000..a6be9ca --- /dev/null +++ b/src/z/js/parser/root.zig @@ -0,0 +1,11 @@ +const std = @import("std"); + +pub const lexical_grammar = @import("lexical_grammar.zig"); + +pub const Result = @import("result.zig").Result; +pub const Parser = @import("parser.zig").Parser; +pub const builtin = @import("builtin.zig"); + +test { + _ = std.testing.refAllDecls(@This()); +} |