diff --git a/peg_macro/src/lib.rs b/peg_macro/src/lib.rs index f0dba6a..dcc6ccd 100644 --- a/peg_macro/src/lib.rs +++ b/peg_macro/src/lib.rs @@ -310,7 +310,7 @@ pub fn grammar(ts: TokenStream) -> TokenStream { let fn_name = &r.name; let generated = r.definition.generate_code(); quote! { - fn #fn_name(p: &mut crate::peg::ParseState) -> bool { + fn #fn_name(p: &mut crate::peg::ParseState<()>) -> bool { #generated } } diff --git a/src/peg/peg.rs b/src/peg/peg.rs index 862db47..8f6a14d 100644 --- a/src/peg/peg.rs +++ b/src/peg/peg.rs @@ -1,19 +1,42 @@ -use std::ops::RangeInclusive; +use std::{ + num::NonZero, + ops::{Range, RangeInclusive}, +}; -pub struct ParseState { +pub struct ParseState { pub input: String, pub offset: usize, + captures: Vec>, } pub struct Savepoint { offset: usize, } -impl ParseState { +pub struct Captures<'a, T: Clone> { + input: &'a str, + raw: &'a [RawCapture], + index: usize, +} + +pub struct Capture<'a, T: Clone> { + input: &'a str, + raw: &'a [RawCapture], +} + +#[derive(Debug)] +struct RawCapture { + kind: T, + span: Range, + subtree_len: Option>, +} + +impl ParseState { pub fn new(s: &str) -> Self { Self { input: s.into(), offset: 0, + captures: vec![], } } @@ -77,6 +100,51 @@ impl ParseState { pub fn eoi(&self) -> bool { self.offset >= self.input.len() } + + pub fn captures<'a>(&'a self) -> Captures<'a, T> { + Captures { + input: &self.input, + raw: &self.captures, + index: 0, + } + } +} + +impl<'a, T: Clone> Iterator for Captures<'a, T> { + type Item = Capture<'a, T>; + + fn next(&mut self) -> Option { + let head = self.raw.get(self.index)?; + let subtree_len = head.subtree_len.unwrap().get(); + let subtree_slice = &self.raw[self.index..self.index + subtree_len]; + self.index += subtree_len; + Some(Capture { + input: &self.input, + raw: subtree_slice, + }) + } +} + +impl<'a, T: Clone> Capture<'a, T> { + pub fn children(&self) -> Captures<'a, T> { + Captures { + input: &self.input, + raw: &self.raw[1..], + index: 0, + } + } + + pub fn kind(&self) -> T { + self.raw[0].kind.clone() + } + + pub fn span(&self) -> Range { + self.raw[0].span.clone() + } + + pub fn text(&self) -> &'a str { + &self.input[self.span()] + } } #[cfg(test)] @@ -112,7 +180,7 @@ mod tests { var_name = "foo" / "bar"; // case must match } - fn parse bool>(rule: T, s: &str) -> bool { + fn parse) -> bool>(rule: T, s: &str) -> bool { println!("About to parse: {}", s); let mut p = ParseState::new(s); rule(&mut p) && p.eoi()