From 71aa44f7071ba3b2c17dbb4511a4381d2ad0e04b Mon Sep 17 00:00:00 2001 From: Chris Mounce Date: Thu, 1 May 2025 01:38:54 -0700 Subject: [PATCH] Start an alternative combinator-based parser --- src/preprocess/peg.rs | 116 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/preprocess/peg.rs b/src/preprocess/peg.rs index bb77ade..e7c7411 100644 --- a/src/preprocess/peg.rs +++ b/src/preprocess/peg.rs @@ -81,6 +81,97 @@ impl Parser { } } +trait Rule { + fn parse(&self, p: &mut Parser) -> Result<()>; +} + +struct Ref(T); + +impl Rule for Ref<&T> +where + T: Rule, +{ + fn parse(&self, p: &mut Parser) -> Result<()> { + self.0.parse(p) + } +} + +impl Rule for F +where + R: Rule, + F: Fn() -> R, +{ + fn parse(&self, p: &mut Parser) -> Result<()> { + self().parse(p) + } +} + +impl Rule for &str { + fn parse(&self, p: &mut Parser) -> Result<()> { + if p.input[p.offset..].starts_with(self) { + p.offset += self.len(); + Ok(()) + } else { + bail!("No match") + } + } +} + +macro_rules! impl_rule_for_tuple { + ($($x:ident)+) => { + impl<$($x),+> Rule for ($($x),+,) where $($x: Rule),+, { + fn parse(&self, p: &mut Parser) -> Result<()> { + let save = p.save(); + let mut lambda = || { + #[allow(non_snake_case)] + let ($($x),+,) = self; + $($x.parse(p)?;)+ + Ok(()) + }; + match lambda() { + Ok(x) => Ok(x), + Err(e) => { + p.restore(save); + Err(e) + } + } + } + } + }; +} + +struct Alt(T); + +macro_rules! impl_rule_for_alt { + ($($x:ident)+) => { + impl<$($x),+> Rule for Alt<($($x),+,)> where $($x: Rule),+ { + fn parse(&self, p: &mut Parser) -> Result<()> { + let save = p.save(); + #[allow(non_snake_case)] + let ($($x),+,) = &self.0; + $( + if $x.parse(p).is_ok() { + return Ok(()) + } + p.restore(save); + )+ + bail!("No match") + } + } + }; +} + +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); + mod test { use super::*; @@ -90,4 +181,29 @@ mod test { p.literal("f").unwrap(); p.star(|p| p.literal("o")).unwrap(); } + + #[test] + fn test_combinator_tuples() { + let foo = ("f", "o", "o"); + let bar = "bar"; + let rule = (foo, " ", bar); + let mut p = Parser::new("foo bar"); + rule.parse(&mut p).unwrap(); + } + + #[test] + fn test_combinator_fn_wrapper() { + let foo = || "foo"; + let bar = (foo, " ", foo); + let mut p = Parser::new("foo foo"); + bar.parse(&mut p).unwrap(); + } + + #[test] + fn test_combinator_alt() { + let item = Alt(("foo", "bar", "baz")); + let rule = (Ref(&item), ", ", Ref(&item)); + let mut p = Parser::new("foo, bar"); + rule.parse(&mut p).unwrap() + } }