const std = @import("std"); pub fn init(s: []const u8) Parser { return .init(s); } pub const Parser = struct { original: []const u8, rest: []const u8, end_idx: usize, // Initializes a Parser for s. pub fn init(s: []const u8) Parser { return .{ .original = s, .end_idx = 0, .rest = s, }; } // Seek the parser window of the text forward skip bytes and return a new Parser. pub fn seek(self: *const Parser, skip: usize) Parser { return .{ .original = self.original, .rest = self.rest[skip..], .end_idx = self.end_idx + skip }; } // Attempts to consume at least one whitespace character from the input text. pub fn consume_space(self: *const Parser) ?Parser { if (!std.ascii.isWhitespace(self.rest[0])) { return null; } for (self.rest[1..], 1..) |c, idx| { if (!std.ascii.isWhitespace(c)) { return self.seek(idx); } } return self.seek(self.rest.len); } // Attempts to consume a character c. pub fn consume_char(self: *const Parser, c: u8) ?Parser { if (self.rest[0] != c) { return null; } return self.seek(1); } // Attempts to consume a string s. pub fn consume_str(self: *const Parser, s: []const u8) ?Parser { const len = s.len; if (self.rest.len < len) { return null; } if (!std.mem.eql(u8, self.rest[0..len], s)) { return null; } return self.seek(len); } // Finds the next occurrence of c (idx) in the current parser // window and extracts it. // // Returns a new parser window that starts after idx and the // extracted byte slice. pub fn take_until_char(self: *const Parser, c: u8) ?struct { Parser, []const u8 } { if (std.mem.indexOfScalar(u8, self.rest, c)) |idx| { return .{ self.seek(idx), self.rest[0..idx] }; } return null; } // Take the current character and advance the parser one step. pub fn take_char(self: *const Parser) struct { Parser, u8 } { return .{ self.seek(1), self.rest[0] }; } // Return the currently accepted text. pub fn parsed(self: *const Parser) []const u8 { return self.original[0..self.end_idx]; } }; test "parser can skip whitespace" { var parser = init("Hello, World"); parser = parser.consume_str("Hello,").?; parser = parser.consume_space().?; parser = parser.consume_str("World").?; try std.testing.expectEqual("Hello, World", parser.parsed()); }