Skip to content

Instantly share code, notes, and snippets.

@d-plaindoux
Last active February 28, 2019 04:31
Show Gist options
  • Save d-plaindoux/bfa20cebca9c5fc4c3b6ff7a641582d2 to your computer and use it in GitHub Desktop.
Save d-plaindoux/bfa20cebca9c5fc4c3b6ff7a641582d2 to your computer and use it in GitHub Desktop.
Basic Parsers and incomplete string recogniser
#![allow(dead_code)]
use std::marker::PhantomData;
// -----------------------------------------------------------------------------
// Response definition
// -----------------------------------------------------------------------------
pub enum Response<A> {
Success(A, usize),
Reject,
}
// -----------------------------------------------------------------------------
// Separate type from behaviors
// -----------------------------------------------------------------------------
pub trait Parser<A> {}
pub trait Executable<'a, A> {
fn parse(&self, s: &'a [u8], o: usize) -> Response<A>;
}
// -----------------------------------------------------------------------------
// The Satisfy parser
// -----------------------------------------------------------------------------
impl<E> Parser<char> for E where E: Fn(char) -> bool {}
impl<'a, E> Executable<'a, char> for E where E: Fn(char) -> bool {
fn parse(&self, s: &'a [u8], o: usize) -> Response<char> {
if o < s.len() {
let c = s[o] as char; // Simplified approach
if self(c) {
return Response::Success(c, o + 1);
}
}
Response::Reject
}
}
fn any() -> impl Fn(char) -> bool {
|_| true
}
fn char(c: char) -> impl Fn(char) -> bool {
move |v| v == c
}
fn not(c: char) -> impl Fn(char) -> bool {
move |v| v != c
}
// -----------------------------------------------------------------------------
// The And parser
// -----------------------------------------------------------------------------
struct And<L, R, A, B> (L, R, PhantomData<A>, PhantomData<B>)
where L: Parser<A>,
R: Parser<B>;
macro_rules! and {
( $ a: expr, $ b: expr) => { And( $ a, $ b, PhantomData, PhantomData) };
}
impl<L, R, A, B> Parser<(A, B)> for And<L, R, A, B>
where L: Parser<A>,
R: Parser<B>
{}
impl<'a, L, R, A, B> Executable<'a, (A, B)> for And<L, R, A, B>
where L: Executable<'a, A> + Parser<A>,
R: Executable<'a, B> + Parser<B>
{
fn parse(&self, s: &'a [u8], o: usize) -> Response<(A, B)> {
let And(left, right, _, _) = self;
match left.parse(s, o) {
Response::Success(v1, s1) => {
match right.parse(s, s1) {
Response::Success(v2, s2) => Response::Success((v1, v2), s2),
Response::Reject => Response::Reject
}
}
Response::Reject => Response::Reject
}
}
}
// ----------------------------------------------------------------------------
// The Repeatable parser
// -----------------------------------------------------------------------------
pub struct Repeat<P, A> (pub bool, pub P, pub PhantomData<A>)
where P: Parser<A>;
#[macro_export]
macro_rules! rep {
( $ a: expr) => { Repeat(false, $ a, PhantomData) };
}
macro_rules! optrep {
( $ a: expr) => { Repeat(true, $ a, PhantomData) };
}
impl<P, A> Parser<Vec<A>> for Repeat<P, A>
where P: Parser<A>
{}
impl<'a, P, A> Executable<'a, Vec<A>> for Repeat<P, A>
where P: Executable<'a, A> + Parser<A>
{
fn parse(&self, s: &'a [u8], o: usize) -> Response<Vec<A>> {
let Repeat(opt, p, _) = self;
let mut values: Vec<A> = Vec::with_capacity(if *opt { 0 } else { 1 });
let mut offset = o;
loop {
let result = p.parse(s, offset);
match result {
Response::Success(a, s) => {
offset = s;
values.push(a);
}
_ => {
if !*opt & &values.is_empty() {
return Response::Reject;
}
return Response::Success(values, offset);
}
}
}
}
}
// ----------------------------------------------------------------------------
// Example examples
// ----------------------------------------------------------------------------
pub struct Delimited;
impl<'a> Parser<(&'a [u8], usize, usize)> for Delimited {}
impl<'a> Executable<'a, (&'a [u8], usize, usize)> for Delimited {
fn parse(&self, s: &'a [u8], o: usize) -> Response<(&'a [u8], usize, usize)> {
let sep = '"';
let response =
and!(char(sep), and!(optrep!(not(sep)), char(sep))).parse(s, o);
match response {
Response::Success(_, no) => Response::Success((s, o + 1, no - 1), no),
Response::Reject => Response::Reject
}
}
}
pub fn delimited_string() -> Delimited {
Delimited
}
#[cfg(test)]
mod tests_delimited_string {
use crate::delimited_string;
use crate::Executable;
impl<A> crate::Response<A> {
pub fn fold<FS, FR, B>(self, success: FS, reject: FR) -> B
where FS: Fn(A, usize) -> B,
FR: Fn() -> B
{
use crate::Response::{Success, Reject};
match self {
Success(a, s) => success(a, s),
Reject => reject()
}
}
}
#[test]
fn it_parse_a_three_characters_string() {
let response = delimited_string().parse(b"\"aaa\"", 0);
assert_eq!(response.fold(|(_, s, e), _| (e - s) == 3, || false), true);
}
#[test]
fn it_parse_an_empty_string() {
let response = delimited_string().parse(b"\"\"", 0);
assert_eq!(response.fold(|(_, s, e), _| (e - s) == 0, || false), true);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment