Created
February 5, 2017 13:47
-
-
Save eyelash/835a5bec9ca03badeb1dac2e4d254a0e to your computer and use it in GitHub Desktop.
This file contains 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
trait Peg { | |
fn p(&self, s: &mut &str) -> bool; | |
} | |
struct Wrap<T>(T); | |
impl<T: Peg> Peg for Wrap<T> { | |
fn p(&self, s: &mut &str) -> bool { | |
self.0.p(s) | |
} | |
} | |
impl<'a> Peg for &'a str { | |
fn p(&self, s: &mut &str) -> bool { | |
if s.starts_with(self) { | |
*s = &s[self.len()..]; | |
return true; | |
} | |
false | |
} | |
} | |
impl<F: Fn(char) -> bool> Peg for F { | |
fn p(&self, s: &mut &str) -> bool { | |
if let Some(c) = s.chars().next() { | |
if self(c) { | |
*s = &s[c.len_utf8()..]; | |
return true; | |
} | |
} | |
false | |
} | |
} | |
impl Peg for char { | |
fn p(&self, s: &mut &str) -> bool { | |
(|c| c == *self).p(s) | |
} | |
} | |
// note that ranges are inclusive here | |
impl Peg for std::ops::Range<char> { | |
fn p(&self, s: &mut &str) -> bool { | |
(|c| c >= self.start && c <= self.end).p(s) | |
} | |
} | |
struct Nothing(); | |
impl Peg for Nothing { | |
fn p(&self, s: &mut &str) -> bool { | |
true | |
} | |
} | |
// sequence | |
struct Seq<P1, P2>(P1, P2); | |
impl<P1: Peg, P2: Peg> Peg for Seq<P1, P2> { | |
fn p(&self, s: &mut &str) -> bool { | |
let original = *s; | |
if !self.0.p(s) { | |
return false; | |
} | |
if !self.1.p(s) { | |
*s = original; | |
return false; | |
} | |
true | |
} | |
} | |
impl<P1: Peg, P2: Peg> std::ops::Add<Wrap<P2>> for Wrap<P1> { | |
type Output = Wrap<Seq<P1, P2>>; | |
fn add(self, rhs: Wrap<P2>) -> Wrap<Seq<P1, P2>> { | |
Wrap(Seq(self.0, rhs.0)) | |
} | |
} | |
// ordered choice | |
struct Choice<P1, P2>(P1, P2); | |
impl<P1: Peg, P2: Peg> Peg for Choice<P1, P2> { | |
fn p(&self, s: &mut &str) -> bool { | |
self.0.p(s) || self.1.p(s) | |
} | |
} | |
impl<P1: Peg, P2: Peg> std::ops::BitOr<Wrap<P2>> for Wrap<P1> { | |
type Output = Wrap<Choice<P1, P2>>; | |
fn bitor(self, rhs: Wrap<P2>) -> Wrap<Choice<P1, P2>> { | |
Wrap(Choice(self.0, rhs.0)) | |
} | |
} | |
struct ZeroOrMore<P>(P); | |
impl<P: Peg> Peg for ZeroOrMore<P> { | |
fn p(&self, s: &mut &str) -> bool { | |
while self.0.p(s) { | |
} | |
true | |
} | |
} | |
fn zero_or_more<P: Peg>(p: P) -> Wrap<ZeroOrMore<P>> { | |
Wrap(ZeroOrMore(p)) | |
} | |
struct OneOrMore<P>(P); | |
impl<P: Peg> Peg for OneOrMore<P> { | |
fn p(&self, s: &mut &str) -> bool { | |
if !self.0.p(s) { | |
return false | |
} | |
while self.0.p(s) { | |
} | |
true | |
} | |
} | |
fn one_or_more<P: Peg>(p: P) -> Wrap<OneOrMore<P>> { | |
Wrap(OneOrMore(p)) | |
} | |
fn optional<P: Peg>(p: P) -> Wrap<Choice<P, Nothing>> { | |
Wrap(Choice(p, Nothing())) | |
} | |
struct End(); | |
impl Peg for End { | |
fn p(&self, s: &mut &str) -> bool { | |
s.is_empty() | |
} | |
} | |
fn end() -> Wrap<End> { | |
Wrap(End()) | |
} | |
fn main() { | |
let p = | |
optional('-') + | |
(Wrap('0') | one_or_more('0'..'9')) + | |
optional(Wrap('.') + one_or_more(char::is_numeric)) + | |
optional((Wrap('e') | Wrap('E')) + optional(Wrap('+') | Wrap('-')) + one_or_more(char::is_numeric)) + | |
end(); | |
assert!(p.p(&mut "9.81")); | |
assert!(p.p(&mut "6.022e23")); | |
assert!(!p.p(&mut "0815")); | |
} |
I've spotted the error, I was trying to pass char::is_numeric as a parse function. What I'll have to do is wrap the function in one that returns an Option<&str>. Thanks again for sharing this. I think its a nice bit of code.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you for sharing this. I followed a reddit link to here while reading about various parsing techniques in rust. I'm tinkering with making a monadic style parser, (similar to parsec and maybe chomp), where parse functions return the result of the parse along with the remaining string. I see you have mostly done that here, returning bools along with the remaining string.
I'm been making some changes, trying to change from returning bools to returning Option<&str> as the result of the parse. I want to make some way of returning the parse results. I know its been quite a while since you posted it, but I'm wondering if you care to look at my refactor in progress where I can't seem to make the std::ops::Add and std::ops::BitOr trait impls working correctly. It seems to be close to compiling other than this.
I'm certain there are other issues which I haven't been able to debug yet too. Thought you might be able to spot something or offer some advice as its an adaptation of your original. Or maybe you see a better way to keep track of the results? Thanks for any input you might have.
Rust Playground
My Fork