use std::ops::{Range, RangeInclusive}; #[derive(Debug)] pub enum Event { Open { kind: &'static str, offset: usize }, Close { offset: usize }, } #[derive(Debug)] pub struct CaptureInfo { pub kind: &'static str, pub span: Range, sibling_index: usize, } pub struct Parser { input: String, offset: usize, output: Vec, } #[derive(Clone, Copy)] pub struct Savepoint { offset: usize, num_tags: usize, } impl Parser { pub fn new(input: &str) -> Self { Self { input: input.into(), offset: 0, output: Vec::new(), } } pub fn save(&self) -> Savepoint { Savepoint { offset: self.offset, num_tags: self.output.len(), } } pub fn restore(&mut self, sp: Savepoint) { self.offset = sp.offset; self.output.truncate(sp.num_tags); } pub fn iter(&self) -> CaptureIterator { CaptureIterator { events: &self.output, index: 0, } } } pub struct CaptureIterator<'a> { events: &'a [CaptureInfo], index: usize, } pub struct CaptureGroup<'a> { pub event: &'a CaptureInfo, } impl<'a> Iterator for CaptureIterator<'a> { type Item = CaptureGroup<'a>; fn next(&mut self) -> Option { while let Some(x) = self.events.get(self.index) { self.index = x.sibling_index; return Some(CaptureGroup { event: x }); } None } } pub trait Rule { fn parse(&self, p: &mut Parser) -> bool; } pub struct Ref(pub T); impl Rule for Ref<&T> where T: Rule, { fn parse(&self, p: &mut Parser) -> bool { self.0.parse(p) } } impl Rule for F where R: Rule, F: Fn() -> R, { fn parse(&self, p: &mut Parser) -> bool { self().parse(p) } } impl Rule for &str { fn parse(&self, p: &mut Parser) -> bool { if p.input[p.offset..].starts_with(self) { p.offset += self.len(); true } else { false } } } impl Rule for RangeInclusive { fn parse(&self, p: &mut Parser) -> bool { if let Some(c) = p.input[p.offset..].chars().next() { if self.contains(&c) { p.offset += c.len_utf8(); return true; } } false } } macro_rules! impl_rule_for_tuple { ($($x:ident)+) => { impl<$($x),+> Rule for ($($x),+,) where $($x: Rule),+, { fn parse(&self, p: &mut Parser) -> bool { let save = p.save(); let mut lambda = || { #[allow(non_snake_case)] let ($($x),+,) = self; $(if !$x.parse(p) {return false; })+ true }; if lambda() { true } else { p.restore(save); false } } } }; } pub struct Alt(pub T); macro_rules! impl_rule_for_alt { ($($x:ident)+) => { impl<$($x),+> Rule for Alt<($($x),+,)> where $($x: Rule),+ { fn parse(&self, p: &mut Parser) -> bool { let save = p.save(); #[allow(non_snake_case)] let ($($x),+,) = &self.0; $( if $x.parse(p) { return true } p.restore(save); )+ false } } }; } pub struct And(pub T); impl Rule for And where T: Rule, { fn parse(&self, p: &mut Parser) -> bool { let save = p.save(); let result = self.0.parse(p); p.restore(save); result } } pub struct Dot; impl Rule for Dot { fn parse(&self, p: &mut Parser) -> bool { if let Some(c) = p.input[p.offset..].chars().next() { p.offset += c.len_utf8(); true } else { false } } } pub struct Not(pub T); impl Rule for Not where T: Rule, { fn parse(&self, p: &mut Parser) -> bool { let save = p.save(); if self.0.parse(p) { p.restore(save); false } else { true } } } pub struct Opt(pub T); impl Rule for Opt where T: Rule, { fn parse(&self, p: &mut Parser) -> bool { let _ = self.0.parse(p); true } } pub struct Star(pub T); impl Rule for Star where T: Rule, { fn parse(&self, p: &mut Parser) -> bool { while self.0.parse(p) {} true } } #[macro_export] macro_rules! star { ($($item:expr),+ $(,)?) => { Star(( $($item),+, )) }; } pub struct Tag(pub &'static str, pub T); impl Rule for Tag where T: Rule, { fn parse(&self, p: &mut Parser) -> bool { let save = p.save(); let index = p.output.len(); let start_offset = p.offset; p.output.push(CaptureInfo { kind: self.0, span: 0..0, sibling_index: 0, }); if self.1.parse(p) { p.output[index].span = start_offset..p.offset; p.output[index].sibling_index = p.output.len(); true } else { p.restore(save); false } } } pub struct EOF; impl Rule for EOF { fn parse(&self, p: &mut Parser) -> bool { p.offset >= p.input.len() } } macro_rules! impl_rule_for_many { () => {}; ($head:ident $($tail:ident)*) => { impl_rule_for_alt!($head $($tail)*); impl_rule_for_tuple!($head $($tail)*); impl_rule_for_many!($($tail)*); } } impl_rule_for_many!(A B C D E F G H I J); #[cfg(test)] mod test { use insta::assert_debug_snapshot; use super::*; fn parse(rule: &T, input: &str) { let mut p = Parser::new(input); let rule = (Ref(rule), EOF); assert!(rule.parse(&mut p)); } fn parse_err(rule: &T, input: &str) { let mut p = Parser::new(input); let rule = (Ref(rule), EOF); assert!(!rule.parse(&mut p)); } #[test] fn test_char_range() { let num = '0'..='9'; let rule = (Ref(&num), star!(Ref(&num))); parse(&rule, "0"); parse(&rule, "123"); parse(&rule, "9"); } #[test] fn test_combinator_tuples() { let foo = ("f", "o", "o"); let bar = "bar"; let rule = (foo, " ", bar); parse(&rule, "foo bar"); } #[test] fn test_combinator_fn_wrapper() { let foo = || "foo"; let bar = (foo, " ", foo); parse(&bar, "foo foo"); } #[test] fn test_combinator_alt() { let item = Alt(("foo", "bar", "baz")); let rule = (Ref(&item), ", ", Ref(&item)); parse(&rule, "foo, bar"); parse(&rule, "bar, baz"); parse(&rule, "baz, foo"); } #[test] fn test_combinator_and() { let has = |x| (star!(Not(x), Dot), x); let foo_bar = (And(has("foo")), And(has("bar")), star!(Dot)); parse(&foo_bar, "foo bar"); parse(&foo_bar, "bar foo"); parse_err(&foo_bar, "foo foo"); parse_err(&foo_bar, "bar bar"); } #[test] fn test_combinator_dot() { let rule = ("foo", Dot, "bar"); parse(&rule, "foo bar"); parse(&rule, "foodbar"); parse(&rule, "foo\nbar"); } #[test] fn test_combinator_not() { let rule = (Not("foo"), star!(Dot)); parse(&rule, "bar"); parse(&rule, "barfoo"); parse_err(&rule, "foobar"); } #[test] fn test_combinator_opt() { let rule = ("foo", Opt(" "), "bar"); parse(&rule, "foobar"); parse(&rule, "foo bar"); parse_err(&rule, "foo bar"); } #[test] fn test_combinator_star() { let item = "foo"; let csv = (item, star!(", ", item)); parse(&csv, "foo"); parse(&csv, "foo, foo"); parse(&csv, "foo, foo, foo"); } #[test] fn test_captures() { let num = Tag("num", ('0'..='9', star!('0'..='9'))); let rule = star!(Alt((num, Dot))); let mut p = Parser::new("I have 123 gems and 45 torches."); assert!(rule.parse(&mut p)); assert_debug_snapshot!(p.output, @r#" [ CaptureInfo { kind: "num", span: 7..10, sibling_index: 1, }, CaptureInfo { kind: "num", span: 20..22, sibling_index: 2, }, ] "#); let result: Vec<_> = p .iter() .map(|x| &p.input[x.event.span.start..x.event.span.end]) .collect(); assert_debug_snapshot!(result, @r#" [ "123", "45", ] "#); } }