Add ability to walk the capture group tree

This commit is contained in:
2025-06-14 13:09:52 -07:00
parent bdb5650904
commit 0f7616a5a9
+48 -1
View File
@@ -10,6 +10,7 @@ pub struct Captures<'a, T: Clone> {
input: &'a str, input: &'a str,
raw: &'a [RawCapture<T>], raw: &'a [RawCapture<T>],
index: usize, index: usize,
walk: bool,
} }
pub struct Capture<'a, T: Clone> { pub struct Capture<'a, T: Clone> {
@@ -38,8 +39,15 @@ impl<T: Clone> ParseState<T> {
input: &self.input, input: &self.input,
raw: &self.captures, raw: &self.captures,
index: 0, index: 0,
walk: false,
} }
} }
pub fn walk_captures<'a>(&'a self) -> Captures<'a, T> {
let mut result = self.captures();
result.walk = true;
result
}
} }
pub mod backend { pub mod backend {
@@ -159,7 +167,7 @@ impl<'a, T: Clone> Iterator for Captures<'a, T> {
let head = self.raw.get(self.index)?; let head = self.raw.get(self.index)?;
let subtree_len = head.subtree_len.unwrap().get(); let subtree_len = head.subtree_len.unwrap().get();
let subtree_slice = &self.raw[self.index..self.index + subtree_len]; let subtree_slice = &self.raw[self.index..self.index + subtree_len];
self.index += subtree_len; self.index += if self.walk { 1 } else { subtree_len };
Some(Capture { Some(Capture {
input: &self.input, input: &self.input,
raw: subtree_slice, raw: subtree_slice,
@@ -173,9 +181,16 @@ impl<'a, T: Clone> Capture<'a, T> {
input: &self.input, input: &self.input,
raw: &self.raw[1..], raw: &self.raw[1..],
index: 0, index: 0,
walk: false,
} }
} }
pub fn walk_children(&self) -> Captures<'a, T> {
let mut result = self.children();
result.walk = true;
result
}
pub fn kind(&self) -> T { pub fn kind(&self) -> T {
self.raw[0].kind.clone() self.raw[0].kind.clone()
} }
@@ -223,6 +238,7 @@ mod tests {
hex_config = "let " var_name " = 0x" ('a'..'f' / '0'..'9')+; hex_config = "let " var_name " = 0x" ('a'..'f' / '0'..'9')+;
var_name = "foo" / "bar"; // case must match var_name = "foo" / "bar"; // case must match
email_text = (!email ANY / email)*;
email = #Email:(#User:user "@" #Domain:domain); email = #Email:(#User:user "@" #Domain:domain);
user = ('a'..'z'i)+; user = ('a'..'z'i)+;
domain = user+ ("." user)+; domain = user+ ("." user)+;
@@ -358,4 +374,35 @@ mod tests {
] ]
"#); "#);
} }
#[test]
fn test_walk_captures() {
let mut p = ParseState::new("Contact alice@foo.com or bob@bar.net.");
assert!(email_text(&mut p));
let extract_by_tag = |tag| {
p.walk_captures()
.filter_map(|c| {
if c.kind() == tag {
Some(c.text())
} else {
None
}
})
.collect::<Vec<_>>()
};
let users = extract_by_tag(Tag::User);
let domains = extract_by_tag(Tag::Domain);
assert_debug_snapshot!(users, @r#"
[
"alice",
"bob",
]
"#);
assert_debug_snapshot!(domains, @r#"
[
"foo.com",
"bar.net",
]
"#);
}
} }