Skip to content

Commit c9c0bfd

Browse files
committed
yeast: Lower query choices
Lowering is fairly straightforward, since we have essentially the same structure in the AST and in the QueryNode type.
1 parent ac9a750 commit c9c0bfd

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

‎shared/yeast-macros/src/lower.rs‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ impl Pattern {
159159
}
160160
}
161161
}
162+
Pattern::Choice { alternatives } => {
163+
let alternatives = alternatives.iter().map(Pattern::lower);
164+
quote! {
165+
yeast::query::QueryNode::Choice {
166+
alternatives: vec![#(#alternatives),*],
167+
}
168+
}
169+
}
162170
Pattern::Sequence(_) | Pattern::Repeated { .. } => {
163171
unreachable!("sequence and repeated patterns cannot match a single node")
164172
}
@@ -168,6 +176,9 @@ impl Pattern {
168176
fn lower_list(&self) -> Vec<TokenStream> {
169177
match self {
170178
Pattern::Sequence(patterns) => patterns.iter().flat_map(Pattern::lower_list).collect(),
179+
Pattern::Choice { .. } if !self.matches_single_node() => {
180+
vec![self.lower_list_choice()]
181+
}
171182
Pattern::Capture {
172183
name,
173184
pattern,
@@ -226,6 +237,21 @@ impl Pattern {
226237
}
227238
}
228239

240+
fn lower_list_choice(&self) -> TokenStream {
241+
let Pattern::Choice { alternatives } = self else {
242+
unreachable!("expected query choice pattern");
243+
};
244+
let alternatives = alternatives.iter().map(|alternative| {
245+
let children = alternative.lower_list();
246+
quote! { vec![#(#children),*] }
247+
});
248+
quote! {
249+
yeast::query::QueryListElem::Choice {
250+
alternatives: vec![#(#alternatives),*],
251+
}
252+
}
253+
}
254+
229255
fn captures(&self) -> Vec<CaptureBinding> {
230256
let mut captures = Vec::new();
231257
self.collect_captures(Cardinality::SINGLE, &mut captures);
@@ -254,6 +280,9 @@ impl Pattern {
254280
cardinality,
255281
});
256282
}
283+
Pattern::Choice { alternatives } => {
284+
captures.extend(unify_choice_captures(alternatives, cardinality));
285+
}
257286
Pattern::Sequence(patterns) => {
258287
for pattern in patterns {
259288
pattern.collect_captures(cardinality, captures);
@@ -269,6 +298,41 @@ impl Pattern {
269298
}
270299
}
271300

301+
fn unify_choice_captures(
302+
alternatives: &[Pattern],
303+
parent: Cardinality,
304+
) -> Vec<CaptureBinding> {
305+
let alternative_count = alternatives.len();
306+
let mut order = Vec::new();
307+
let mut by_name = std::collections::BTreeMap::<String, Vec<CaptureBinding>>::new();
308+
309+
for alternative in alternatives {
310+
for capture in alternative.captures() {
311+
let name = capture.name.to_string();
312+
if !by_name.contains_key(&name) {
313+
order.push(name.clone());
314+
}
315+
by_name.entry(name).or_default().push(capture);
316+
}
317+
}
318+
319+
order
320+
.into_iter()
321+
.map(|name| {
322+
let captures = by_name.remove(&name).unwrap();
323+
let present_in_every_alternative = captures.len() == alternative_count;
324+
let mut capture = captures[0].clone();
325+
for other in captures.iter().skip(1) {
326+
capture.cardinality.multiple |= other.cardinality.multiple;
327+
capture.cardinality.required &= other.cardinality.required;
328+
}
329+
capture.cardinality.required &= present_in_every_alternative;
330+
capture.cardinality = combine_cardinality(parent, capture.cardinality);
331+
capture
332+
})
333+
.collect()
334+
}
335+
272336
fn combine_cardinality(parent: Cardinality, child: Cardinality) -> Cardinality {
273337
Cardinality {
274338
multiple: parent.multiple || child.multiple,

0 commit comments

Comments
 (0)