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")); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.