Skip to content

Instantly share code, notes, and snippets.

@slimsag
Created March 10, 2021 20:29
Show Gist options
  • Save slimsag/db2dd2c49aa038e23b654120e70c9b00 to your computer and use it in GitHub Desktop.
Save slimsag/db2dd2c49aa038e23b654120e70c9b00 to your computer and use it in GitHub Desktop.
const std = @import("std");
const Allocator = std.mem.Allocator;
pub const Error = error{
EndOfStream,
Utf8InvalidStartByte,
} || std.fs.File.ReadError || std.fs.File.SeekError || std.mem.Allocator.Error;
pub fn Parser(comptime Value: type, comptime Reader: type) type {
return struct {
const Self = @This();
_parse: fn(self: *Self, allocator: *Allocator, src: *Reader) callconv(.Inline) Error!?Value,
pub fn parse(self: *Self, allocator: *Allocator, src: *Reader) callconv(.Inline) Error!?Value {
return self._parse(self, allocator, src);
}
};
}
pub fn OneOf(comptime Value: type, comptime Reader: type) type {
return struct {
parser: Parser(Value, Reader) = .{
._parse = parse,
},
parsers: []*Parser(Value, Reader),
const Self = @This();
// `parsers` slice must stay alive for as long as the parser will be
// used.
pub fn init(parsers: []*Parser(Value, Reader)) Self {
return Self{
.parsers = parsers,
};
}
// Caller is responsible for freeing the value, if any.
fn parse(parser: *Parser(Value, Reader), allocator: *Allocator, src: *Reader) callconv(.Inline) Error!?Value {
const self = @fieldParentPtr(Self, "parser", parser);
for (self.parsers) | one_of_parser | {
const result = try one_of_parser.parse(allocator, src);
if (result != null) {
return result;
}
}
return null;
}
};
}
const std = @import("std");
const Allocator = std.mem.Allocator;
const Parser = @import("parser.zig").Parser;
const OneOf = @import("parser.zig").OneOf;
const Error = @import("parser.zig").Error;
pub fn Literal(comptime Reader: type) type {
return struct {
parser: Parser([]u8, Reader) = .{
._parse = parse,
},
want: []const u8,
const Self = @This();
pub fn init(want: []const u8) Self {
return Self{
.want = want
};
}
fn parse(parser: *Parser([]u8, Reader), allocator: *Allocator, src: *Reader) callconv(.Inline) Error!?[]u8 {
const self = @fieldParentPtr(Self, "parser", parser);
const buf = try allocator.alloc(u8, self.want.len);
const read = try src.reader().readAll(buf);
if (read < self.want.len or !std.mem.eql(u8, buf, self.want)) {
try src.seekableStream().seekBy(-@intCast(i64, read));
allocator.free(buf);
return null;
}
return buf;
}
};
}
test "literal" {
const allocator = std.testing.allocator;
var reader = std.io.fixedBufferStream("abcdef");
var want: []const u8 = "abc";
var literal = Literal(@TypeOf(reader)).init(want);
const p = &literal.parser;
var result = try p.parse(allocator, &reader);
std.testing.expectEqualStrings(want, result.?);
if (result) |r| {
allocator.free(r);
}
}
test "oneof_literal" {
const allocator = std.testing.allocator;
var reader = std.io.fixedBufferStream("catdogsheep");
// Define our parser.
var one_of = OneOf([]u8, @TypeOf(reader)).init(&.{
&Literal(@TypeOf(reader)).init("dog").parser,
&Literal(@TypeOf(reader)).init("sheep").parser,
&Literal(@TypeOf(reader)).init("cat").parser,
});
var p = &one_of.parser;
// Parse!
var result = try p.parse(allocator, &reader);
std.testing.expectEqualStrings("cat", result.?);
if (result) |r| {
allocator.free(r);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment