Skip to content

Commit ac9a750

Browse files
committed
yeast: Match query choices
Adds the logic that handles matching query choices, extending the QueryNode type with a matching Choice branch.
1 parent 0e7bfb3 commit ac9a750

2 files changed

Lines changed: 79 additions & 7 deletions

File tree

‎shared/yeast/src/lib.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,8 +1287,12 @@ impl<'a, C> RuleIndex<'a, C> {
12871287
let mut by_kind: BTreeMap<&'static str, Vec<&'a Rule<C>>> = BTreeMap::new();
12881288
let mut wildcard = Vec::new();
12891289
for rule in rules {
1290-
match rule.query.root_kind() {
1291-
Some(kind) => by_kind.entry(kind).or_default().push(rule),
1290+
match rule.query.root_kinds() {
1291+
Some(kinds) => {
1292+
for kind in kinds {
1293+
by_kind.entry(kind).or_default().push(rule);
1294+
}
1295+
}
12921296
None => wildcard.push(rule),
12931297
}
12941298
}

‎shared/yeast/src/query.rs‎

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,42 @@ pub enum QueryNode {
1616
UnnamedNode {
1717
kind: &'static str,
1818
},
19+
Choice {
20+
alternatives: Vec<QueryNode>,
21+
},
1922
Capture {
2023
capture: &'static str,
2124
node: Box<QueryNode>,
2225
},
2326
}
2427

2528
impl QueryNode {
26-
/// Returns the root node kind this query matches, if it's specific.
27-
/// Returns None for wildcards (Any) and captures wrapping wildcards.
29+
/// Returns the root node kind this query matches when it has exactly one.
30+
/// Choices whose alternatives share a root kind return that kind; wildcards
31+
/// and choices spanning multiple root kinds return `None`.
2832
pub fn root_kind(&self) -> Option<&'static str> {
33+
let kinds = self.root_kinds()?;
34+
(kinds.len() == 1).then_some(kinds[0])
35+
}
36+
37+
/// Returns all root node kinds this query can match when they are known.
38+
/// Returns `None` for wildcards and choices containing a wildcard.
39+
pub(crate) fn root_kinds(&self) -> Option<Vec<&'static str>> {
2940
match self {
30-
QueryNode::Node { kind, .. } => Some(kind),
31-
QueryNode::UnnamedNode { kind } => Some(kind),
32-
QueryNode::Capture { node, .. } => node.root_kind(),
41+
QueryNode::Node { kind, .. } => Some(vec![kind]),
42+
QueryNode::UnnamedNode { kind } => Some(vec![kind]),
43+
QueryNode::Choice { alternatives } => {
44+
let mut kinds = Vec::new();
45+
for alternative in alternatives {
46+
for kind in alternative.root_kinds()? {
47+
if !kinds.contains(&kind) {
48+
kinds.push(kind);
49+
}
50+
}
51+
}
52+
Some(kinds)
53+
}
54+
QueryNode::Capture { node, .. } => node.root_kinds(),
3355
QueryNode::Any { .. } => None,
3456
}
3557
}
@@ -41,6 +63,9 @@ pub enum QueryListElem {
4163
children: Vec<QueryListElem>,
4264
rep: Rep,
4365
},
66+
Choice {
67+
alternatives: Vec<Vec<QueryListElem>>,
68+
},
4469
SingleNode(QueryNode),
4570
}
4671

@@ -60,6 +85,9 @@ impl QueryNode {
6085
QueryNode::Any { match_unnamed } => !match_unnamed,
6186
QueryNode::Node { .. } => true,
6287
QueryNode::UnnamedNode { .. } => false,
88+
QueryNode::Choice { alternatives } => {
89+
alternatives.iter().all(QueryNode::matches_named_only)
90+
}
6391
QueryNode::Capture { node, .. } => node.matches_named_only(),
6492
}
6593
}
@@ -105,6 +133,14 @@ impl QueryNode {
105133
.ok_or_else(|| format!("unnamed Node kind {kind} not found in language"))?;
106134
Ok(node.kind == target_kind)
107135
}
136+
QueryNode::Choice { alternatives } => {
137+
if let Some(captures) = match_choice(alternatives, ast, node)? {
138+
matches.merge(&captures);
139+
Ok(true)
140+
} else {
141+
Ok(false)
142+
}
143+
}
108144
QueryNode::Capture {
109145
capture,
110146
node: sub_query,
@@ -119,6 +155,20 @@ impl QueryNode {
119155
}
120156
}
121157

158+
fn match_choice(
159+
alternatives: &[QueryNode],
160+
ast: &Ast,
161+
node: Id,
162+
) -> Result<Option<Captures>, String> {
163+
for alternative in alternatives {
164+
let mut captures = Captures::new();
165+
if alternative.do_match(ast, node, &mut captures)? {
166+
return Ok(Some(captures));
167+
}
168+
}
169+
Ok(None)
170+
}
171+
122172
fn match_children<'a>(
123173
child_matchers: impl Iterator<Item = &'a QueryListElem>,
124174
ast: &Ast,
@@ -177,6 +227,24 @@ impl QueryListElem {
177227
Ok(true)
178228
}
179229
}
230+
QueryListElem::Choice { alternatives } => {
231+
let start = remaining_children.clone();
232+
for alternative in alternatives {
233+
let mut candidate_children = start.clone();
234+
let mut candidate_captures = Captures::new();
235+
if match_children(
236+
alternative.iter(),
237+
ast,
238+
&mut candidate_children,
239+
&mut candidate_captures,
240+
)? {
241+
*remaining_children = candidate_children;
242+
matches.merge(&candidate_captures);
243+
return Ok(true);
244+
}
245+
}
246+
Ok(false)
247+
}
180248
QueryListElem::SingleNode(sub_query) => {
181249
// Forward-scan semantics: advance through the iterator until
182250
// we find a child that matches `sub_query`. Skip ahead past

0 commit comments

Comments
 (0)