-
-
Save travisstaloch/0ce44a4fd5060dff7f2f1482765acdec 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
#![feature(ptr_offset_from)] | |
type ParseResult<'a> = Option<&'a str>; | |
trait Peg<'a> { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a>; | |
} | |
struct Wrap<T>(T); | |
impl<'a, T: Peg<'a>> Peg<'a> for Wrap<T> { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
self.0.p(s) | |
} | |
} | |
impl<'a> Peg<'a> for &'a str { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
if s.starts_with(self) { | |
*s = &s[self.len()..]; | |
return Some(&s[..self.len() - 1]); | |
} | |
None | |
} | |
} | |
impl<'a, F: Fn(char) -> ParseResult<'a>> Peg<'a> for F { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
if let Some(c) = s.chars().next() { | |
if self(c).is_some() { | |
*s = &s[c.len_utf8()..]; | |
return Some(&s[..c.len_utf8() - 1]); | |
} | |
} | |
None | |
} | |
} | |
impl<'a> Peg<'a> for char { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
(|c| if c == *self { Some(&s[..1]) } else { None }).p(&mut &s[1..]) | |
} | |
} | |
// note that ranges are inclusive here | |
impl<'a> Peg<'a> for std::ops::Range<char> { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
(|c| { | |
if c >= self.start && c <= self.end { | |
Some(&s[..1]) | |
} else { | |
None | |
} | |
}).p(&mut &s[1..]) | |
} | |
} | |
struct Nothing(); | |
impl<'a> Peg<'a> for Nothing { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
Some("") | |
} | |
} | |
// sequence | |
struct Seq<P1, P2>(P1, P2); | |
impl<'a, P1: Peg<'a>, P2: Peg<'a>> Peg<'a> for Seq<P1, P2> { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
let original = *s; | |
if self.0.p(s).is_none() { | |
return None; | |
} | |
if self.1.p(s).is_none() { | |
*s = original; | |
return None; | |
} | |
Some(&s[..ptr_offset(original, s)]) | |
} | |
} | |
impl<'a, P1: Peg<'a>, P2: Peg<'a>> 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<'a, P1: Peg<'a>, P2: Peg<'a>> Peg<'a> for Choice<P1, P2> { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
let original = *s; | |
if self.0.p(s).is_some() || self.1.p(s).is_some() { | |
Some(&s[..ptr_offset(original, s)]) | |
} else { | |
None | |
} | |
} | |
} | |
impl<'a, P1: Peg<'a>, P2: Peg<'a>> 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<'a, P: Peg<'a>> Peg<'a> for ZeroOrMore<P> { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
let original = *s; | |
while self.0.p(s).is_some() {} | |
Some(&s[..ptr_offset(original, s)]) | |
} | |
} | |
fn zero_or_more<'a, P: Peg<'a>>(p: P) -> Wrap<ZeroOrMore<P>> { | |
Wrap(ZeroOrMore(p)) | |
} | |
struct OneOrMore<P>(P); | |
impl<'a, P: Peg<'a>> Peg<'a> for OneOrMore<P> { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
if self.0.p(s).is_none() { | |
return None; | |
} | |
let original = *s; | |
while self.0.p(s).is_some() {} | |
Some(&s[..ptr_offset(original, s)]) | |
} | |
} | |
fn one_or_more<'a, P: Peg<'a>>(p: P) -> Wrap<OneOrMore<P>> { | |
Wrap(OneOrMore(p)) | |
} | |
fn optional<'a, P: Peg<'a>>(p: P) -> Wrap<Choice<P, Nothing>> { | |
Wrap(Choice(p, Nothing())) | |
} | |
struct End(); | |
impl<'a> Peg<'a> for End { | |
fn p(&self, s: &mut &'a str) -> ParseResult<'a> { | |
if s.is_empty() { | |
None | |
} else { | |
Some(s) | |
} | |
} | |
} | |
fn end() -> Wrap<End> { | |
Wrap(End()) | |
} | |
fn ptr_offset(orig: &str, s: &str) -> usize { | |
let ptro: *const u8 = orig.as_ptr(); | |
let ptrs: *const u8 = s.as_ptr(); | |
(unsafe { ptrs.offset_from(ptro) }) as usize | |
} | |
#[test] | |
fn itworks() { | |
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