Last active
February 28, 2019 04:31
-
-
Save d-plaindoux/bfa20cebca9c5fc4c3b6ff7a641582d2 to your computer and use it in GitHub Desktop.
Basic Parsers and incomplete string recogniser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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