Skip to content

Instantly share code, notes, and snippets.

@eyelash
Created February 5, 2017 13:47
Show Gist options
  • Save eyelash/835a5bec9ca03badeb1dac2e4d254a0e to your computer and use it in GitHub Desktop.
Save eyelash/835a5bec9ca03badeb1dac2e4d254a0e to your computer and use it in GitHub Desktop.
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"));
}
@travisstaloch
Copy link

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

@travisstaloch
Copy link

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