Skip to content

Commit 27c918a

Browse files
refactor: move match_node impl out
1 parent ae6332e commit 27c918a

File tree

2 files changed

+197
-190
lines changed

2 files changed

+197
-190
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
use super::Aggregator;
2+
use crate::meta_var::MetaVariable;
3+
use crate::{Doc, Language, Node, Pattern};
4+
5+
pub(super) fn match_node_impl<'tree, D: Doc>(
6+
goal: &Pattern<D::Lang>,
7+
candidate: &Node<'tree, D>,
8+
agg: &mut impl Aggregator<'tree, D>,
9+
) -> Option<()> {
10+
use Pattern as P;
11+
match goal {
12+
// leaf = without named children
13+
P::Terminal { text, kind_id, .. } if *kind_id == candidate.kind_id() => {
14+
if *text == candidate.text() {
15+
agg.match_terminal(candidate)
16+
} else {
17+
None
18+
}
19+
}
20+
P::MetaVar { meta_var, .. } => agg.match_meta_var(meta_var, candidate),
21+
P::Internal {
22+
kind_id, children, ..
23+
} if *kind_id == candidate.kind_id() => {
24+
let cand_children = candidate.children();
25+
match_nodes_impl_recursive(children, cand_children, agg)
26+
}
27+
_ => None,
28+
}
29+
}
30+
31+
fn match_nodes_impl_recursive<'tree, D: Doc + 'tree>(
32+
goals: &[Pattern<D::Lang>],
33+
candidates: impl Iterator<Item = Node<'tree, D>>,
34+
agg: &mut impl Aggregator<'tree, D>,
35+
) -> Option<()> {
36+
let mut goal_children = goals.iter().peekable();
37+
let mut cand_children = candidates.peekable();
38+
cand_children.peek()?;
39+
loop {
40+
let curr_node = goal_children.peek().unwrap();
41+
if let Ok(optional_name) = try_get_ellipsis_mode(curr_node) {
42+
let mut matched = vec![];
43+
goal_children.next();
44+
// goal has all matched
45+
if goal_children.peek().is_none() {
46+
match_ellipsis(agg, &optional_name, matched, cand_children, 0)?;
47+
return Some(());
48+
}
49+
// skip trivial nodes in goal after ellipsis
50+
let mut skipped_anonymous = 0;
51+
while goal_children.peek().unwrap().is_trivial() {
52+
goal_children.next();
53+
skipped_anonymous += 1;
54+
if goal_children.peek().is_none() {
55+
match_ellipsis(
56+
agg,
57+
&optional_name,
58+
matched,
59+
cand_children,
60+
skipped_anonymous,
61+
)?;
62+
return Some(());
63+
}
64+
}
65+
// if next node is a Ellipsis, consume one candidate node
66+
if try_get_ellipsis_mode(goal_children.peek().unwrap()).is_ok() {
67+
matched.push(cand_children.next().unwrap());
68+
cand_children.peek()?;
69+
match_ellipsis(
70+
agg,
71+
&optional_name,
72+
matched,
73+
std::iter::empty(),
74+
skipped_anonymous,
75+
)?;
76+
continue;
77+
}
78+
loop {
79+
if match_node_impl(
80+
goal_children.peek().unwrap(),
81+
cand_children.peek().unwrap(),
82+
agg,
83+
)
84+
.is_some()
85+
{
86+
// found match non Ellipsis,
87+
match_ellipsis(
88+
agg,
89+
&optional_name,
90+
matched,
91+
std::iter::empty(),
92+
skipped_anonymous,
93+
)?;
94+
break;
95+
}
96+
matched.push(cand_children.next().unwrap());
97+
cand_children.peek()?;
98+
}
99+
}
100+
// skip if cand children is trivial
101+
loop {
102+
let Some(cand) = cand_children.peek() else {
103+
// if cand runs out, remaining goal is not matched
104+
return None;
105+
};
106+
let matched = match_node_impl(goal_children.peek().unwrap(), cand, agg).is_some();
107+
// try match goal node with candidate node
108+
if matched {
109+
break;
110+
} else if !cand.is_named() {
111+
// skip trivial node
112+
// TODO: nade with field should not be skipped
113+
cand_children.next();
114+
} else {
115+
// unmatched significant node
116+
return None;
117+
}
118+
}
119+
goal_children.next();
120+
if goal_children.peek().is_none() {
121+
// all goal found, return
122+
return Some(());
123+
}
124+
cand_children.next();
125+
cand_children.peek()?;
126+
}
127+
}
128+
129+
/// Returns Ok if ellipsis pattern is found. If the ellipsis is named, returns it name.
130+
/// If the ellipsis is unnamed, returns None. If it is not ellipsis node, returns Err.
131+
fn try_get_ellipsis_mode(node: &Pattern<impl Language>) -> Result<Option<String>, ()> {
132+
let Pattern::MetaVar { meta_var, .. } = node else {
133+
return Err(());
134+
};
135+
match meta_var {
136+
MetaVariable::Multiple => Ok(None),
137+
MetaVariable::MultiCapture(n) => Ok(Some(n.into())),
138+
_ => Err(()),
139+
}
140+
}
141+
142+
fn match_ellipsis<'t, D: Doc>(
143+
agg: &mut impl Aggregator<'t, D>,
144+
optional_name: &Option<String>,
145+
mut matched: Vec<Node<'t, D>>,
146+
cand_children: impl Iterator<Item = Node<'t, D>>,
147+
skipped_anonymous: usize,
148+
) -> Option<()> {
149+
matched.extend(cand_children);
150+
agg.match_ellipsis(optional_name.as_deref(), matched, skipped_anonymous)?;
151+
Some(())
152+
}

0 commit comments

Comments
 (0)