Files
marzipan/src/preprocess/peg.rs
T
cmounce 6d33bd5838 Add a very basic capture group iterator
- Add a basic iterator over Event
- Use single struct per capture group to store capture info
2025-05-10 17:43:17 -07:00

418 lines
8.8 KiB
Rust

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<usize>,
sibling_index: usize,
}
pub struct Parser {
input: String,
offset: usize,
output: Vec<CaptureInfo>,
}
#[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<Self::Item> {
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<T>(pub T);
impl<T> Rule for Ref<&T>
where
T: Rule,
{
fn parse(&self, p: &mut Parser) -> bool {
self.0.parse(p)
}
}
impl<R, F> 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<char> {
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<T>(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<T>(pub T);
impl<T> Rule for And<T>
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<T>(pub T);
impl<T> Rule for Not<T>
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<T>(pub T);
impl<T> Rule for Opt<T>
where
T: Rule,
{
fn parse(&self, p: &mut Parser) -> bool {
let _ = self.0.parse(p);
true
}
}
pub struct Star<T>(pub T);
impl<T> Rule for Star<T>
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<T>(pub &'static str, pub T);
impl<T> Rule for Tag<T>
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<T: Rule>(rule: &T, input: &str) {
let mut p = Parser::new(input);
let rule = (Ref(rule), EOF);
assert!(rule.parse(&mut p));
}
fn parse_err<T: Rule>(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",
]
"#);
}
}