Implement nested capture groups, iterators

This commit is contained in:
2025-05-10 18:28:05 -07:00
parent c027089936
commit dec8213638
+72 -18
View File
@@ -1,4 +1,7 @@
use std::ops::{Range, RangeInclusive};
use std::{
num::NonZero,
ops::{Range, RangeInclusive},
};
pub struct Parser {
input: String,
@@ -27,7 +30,7 @@ impl Parser {
self.captures.truncate(sp.num_captures);
}
pub fn iter(&self) -> Captures {
pub fn iter<'a>(&'a self) -> Captures<'a> {
Captures {
input: &self.input,
raw: &self.captures,
@@ -52,34 +55,41 @@ impl<'a> Iterator for Captures<'a> {
type Item = Capture<'a>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(x) = self.raw.get(self.index) {
self.index = x.sibling_index;
return Some(Capture {
input: &self.input,
raw: x,
});
}
None
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,
})
}
}
pub struct Capture<'a> {
input: &'a str,
raw: &'a RawCapture,
raw: &'a [RawCapture],
}
impl<'a> Capture<'a> {
pub fn children(&self) -> Captures<'a> {
Captures {
input: &self.input,
raw: &self.raw[1..],
index: 0,
}
}
pub fn kind(&self) -> &'static str {
&self.raw.kind
&self.raw[0].kind
}
pub fn span(&self) -> Range<usize> {
self.raw.span.clone()
self.raw[0].span.clone()
}
pub fn text(&self) -> &'a str {
let span = self.raw.span.clone();
&self.input[span]
&self.input[self.span()]
}
}
@@ -87,7 +97,7 @@ impl<'a> Capture<'a> {
struct RawCapture {
kind: &'static str,
span: Range<usize>,
sibling_index: usize,
subtree_len: Option<NonZero<usize>>,
}
pub trait Rule {
@@ -271,11 +281,12 @@ where
p.captures.push(RawCapture {
kind: self.0,
span: 0..0,
sibling_index: 0,
subtree_len: None,
});
if self.1.parse(p) {
let subtree_len = NonZero::new(p.captures.len() - index).unwrap();
p.captures[index].span = start_offset..p.offset;
p.captures[index].sibling_index = p.captures.len();
p.captures[index].subtree_len = Some(subtree_len);
true
} else {
p.restore(save);
@@ -417,4 +428,47 @@ mod test {
]
"#);
}
#[test]
fn test_nested_captures() {
let letter = || 'a'..='z';
let user = Tag("user", (letter, star!(letter)));
let domain = Tag("domain", (letter, star!(Opt("."), letter)));
let email = Tag("email", (user, "@", domain));
let rule = star!(Alt((email, Dot)));
let mut p = Parser::new("Send to alice@foo.net or bob@bar.com.");
assert!(rule.parse(&mut p));
// Make sure the capture groups were correct
let mut results = Vec::new();
for email in p.iter() {
assert_eq!(email.kind(), "email");
for group in email.children() {
results.push(format!("{}: {}", group.kind(), group.text()));
}
}
assert_debug_snapshot!(results, @r#"
[
"user: alice",
"domain: foo.net",
"user: bob",
"domain: bar.com",
]
"#);
// Make sure &str lifetimes outlive the Capture structs
let slices: Vec<&str> = p
.iter()
.flat_map(|email| email.children())
.map(|group| group.text())
.collect();
assert_debug_snapshot!(slices, @r#"
[
"alice",
"foo.net",
"bob",
"bar.com",
]
"#);
}
}