1use crate::{
4 Arity, BottomUpTa, CondensedTa, CondensedTopDownTa, Explicit, ExplicitBuilder, FxHashSet,
5 Interner, KeySet, Memo, ParseControl, SetTrie, StateId, Symbol, SymbolSet,
6 control::ParseCancelled, run::cartesian_product,
7};
8use fixedbitset::FixedBitSet;
9use smallvec::SmallVec;
10use std::collections::VecDeque;
11use std::hash::Hash;
12
13type FxHashMap<K, V> = hashbrown::HashMap<K, V, rustc_hash::FxBuildHasher>;
14
15#[cfg(feature = "stats")]
16macro_rules! stat_inc {
17 ($stats:expr, $field:ident) => {
18 $stats.$field += 1;
19 };
20}
21
22#[cfg(not(feature = "stats"))]
23macro_rules! stat_inc {
24 ($stats:expr, $field:ident) => {};
25}
26
27pub fn materialize<A: BottomUpTa>(
42 a: &A,
43 alphabet: &[(Symbol, Arity)],
44) -> (Explicit, Interner<A::State>) {
45 let memo = Memo::new(a);
46 let mut known = Vec::<StateId>::new();
47 let mut known_bits = FixedBitSet::new();
48 let mut worklist = Vec::<StateId>::new();
49
50 for &(symbol, arity) in alphabet {
51 if arity == 0 {
52 collect_step(
53 &memo,
54 symbol,
55 &[],
56 &mut known,
57 &mut known_bits,
58 &mut worklist,
59 );
60 }
61 }
62
63 while let Some(popped) = worklist.pop() {
64 let snapshot = known.clone();
65 for &(symbol, arity) in alphabet {
66 match arity {
67 0 => {}
68 1 => collect_step(
69 &memo,
70 symbol,
71 &[popped],
72 &mut known,
73 &mut known_bits,
74 &mut worklist,
75 ),
76 2 => {
77 for &other in &snapshot {
78 collect_step(
79 &memo,
80 symbol,
81 &[popped, other],
82 &mut known,
83 &mut known_bits,
84 &mut worklist,
85 );
86 if other != popped {
87 collect_step(
88 &memo,
89 symbol,
90 &[other, popped],
91 &mut known,
92 &mut known_bits,
93 &mut worklist,
94 );
95 }
96 }
97 }
98 n => {
99 let pools = vec![snapshot.as_slice(); n as usize];
100 cartesian_product(&pools, |tuple| {
101 if tuple.contains(&popped) {
102 collect_step(
103 &memo,
104 symbol,
105 tuple,
106 &mut known,
107 &mut known_bits,
108 &mut worklist,
109 );
110 }
111 });
112 }
113 }
114 }
115 }
116
117 memo.into_explicit()
118}
119
120fn collect_step<A: BottomUpTa>(
121 memo: &Memo<&A>,
122 symbol: Symbol,
123 children: &[StateId],
124 known: &mut Vec<StateId>,
125 known_bits: &mut FixedBitSet,
126 worklist: &mut Vec<StateId>,
127) {
128 memo.step(symbol, children, &mut |q| {
129 if !known_bits.contains(q.index()) {
130 known_bits.grow(q.index() + 1);
131 known_bits.set(q.index(), true);
132 known.push(q);
133 worklist.push(q);
134 }
135 });
136}
137
138#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
140pub struct IndexedCondensedIntersectionStats {
141 pub output_states: usize,
143 pub output_rules: usize,
145 pub right_nullary_rules: usize,
147 pub right_indexed_queries: usize,
149 #[cfg(feature = "stats")]
151 pub queue_pops: usize,
152 #[cfg(feature = "stats")]
154 pub left_occurrences_considered: usize,
155 #[cfg(feature = "stats")]
157 pub right_rules_scanned: usize,
158 #[cfg(feature = "stats")]
160 pub symbol_arity_matches: usize,
161 #[cfg(feature = "stats")]
163 pub child_tuple_matches: usize,
164}
165
166impl IndexedCondensedIntersectionStats {
167 pub fn right_queries(&self) -> usize {
169 self.right_nullary_rules + self.right_indexed_queries
170 }
171}
172
173#[derive(Clone)]
174pub(crate) struct OwnedRule {
175 pub(crate) symbol: Symbol,
176 pub(crate) children: SmallVec<[StateId; 2]>,
177 pub(crate) result: StateId,
178 pub(crate) weight: f64,
179}
180
181#[derive(Clone)]
182pub(crate) struct OwnedCondensedRule<S> {
183 pub(crate) children: SmallVec<[S; 2]>,
184 pub(crate) symbols: SymbolSet,
185 pub(crate) result: S,
186}
187
188#[derive(Clone, Debug, Default)]
189pub(crate) struct ProductStateMap {
190 by_right: Vec<FxHashMap<StateId, StateId>>,
191}
192
193impl ProductStateMap {
194 fn new() -> Self {
195 Self::default()
196 }
197
198 pub(crate) fn get(&self, left: StateId, right: StateId) -> Option<StateId> {
199 self.by_right
200 .get(right.index())
201 .and_then(|partners| partners.get(&left).copied())
202 }
203
204 pub(crate) fn insert(&mut self, left: StateId, right: StateId, product: StateId) {
205 if self.by_right.len() <= right.index() {
206 self.by_right
207 .resize_with(right.index() + 1, FxHashMap::default);
208 }
209 self.by_right[right.index()].insert(left, product);
210 }
211}
212
213#[derive(Debug, Default)]
214pub(crate) struct TrustedRuleTracker {
215 #[cfg(debug_assertions)]
216 seen: FxHashSet<(Symbol, SmallVec<[StateId; 2]>, StateId)>,
217}
218
219impl TrustedRuleTracker {
220 pub(crate) fn add_rule(
221 &mut self,
222 builder: &mut ExplicitBuilder,
223 symbol: Symbol,
224 children: SmallVec<[StateId; 2]>,
225 parent: StateId,
226 weight: f64,
227 ) {
228 #[cfg(debug_assertions)]
229 {
230 let key = (symbol, children.clone(), parent);
231 debug_assert!(
232 self.seen.insert(key),
233 "trusted materializer generated a duplicate transition"
234 );
235 }
236
237 builder.add_weighted_rule_inline(symbol, children, parent, weight);
238 }
239}
240
241#[derive(Clone, Copy)]
242enum StateSetView<'a> {
243 One(StateId),
244 Many(&'a FxHashSet<StateId>),
245}
246
247impl KeySet<StateId> for StateSetView<'_> {
248 fn len(&self) -> usize {
249 match self {
250 StateSetView::One(_) => 1,
251 StateSetView::Many(states) => states.len(),
252 }
253 }
254
255 fn contains(&self, key: &StateId) -> bool {
256 match self {
257 StateSetView::One(state) => state == key,
258 StateSetView::Many(states) => states.contains(key),
259 }
260 }
261
262 fn for_each(&self, out: &mut dyn FnMut(&StateId)) {
263 match self {
264 StateSetView::One(state) => out(state),
265 StateSetView::Many(states) => {
266 for state in *states {
267 out(state);
268 }
269 }
270 }
271 }
272}
273
274#[derive(Default)]
275pub(crate) struct LeftIndex {
276 nullary_by_symbol: FxHashMap<Symbol, Vec<usize>>,
277 pub(crate) by_state: FxHashMap<StateId, Vec<(Symbol, usize, usize)>>,
278 by_children: SetTrie<StateId, FxHashMap<Symbol, Vec<usize>>>,
279 by_rotated_children: Vec<SetTrie<StateId, FxHashMap<Symbol, Vec<usize>>>>,
280}
281
282impl LeftIndex {
283 pub(crate) fn build(rules: &[OwnedRule]) -> Self {
284 Self::build_filtered(rules, |_, _| true)
285 }
286
287 pub(crate) fn build_filtered(
288 rules: &[OwnedRule],
289 mut include: impl FnMut(usize, &OwnedRule) -> bool,
290 ) -> Self {
291 let mut index = Self::default();
292 for (rule_idx, rule) in rules.iter().enumerate() {
293 if !include(rule_idx, rule) {
294 continue;
295 }
296 index
297 .by_children
298 .get_or_insert_with(&rule.children, FxHashMap::default)
299 .entry(rule.symbol)
300 .or_default()
301 .push(rule_idx);
302 if rule.children.is_empty() {
303 index
304 .nullary_by_symbol
305 .entry(rule.symbol)
306 .or_default()
307 .push(rule_idx);
308 }
309 for (position, &child) in rule.children.iter().enumerate() {
310 index
311 .by_state
312 .entry(child)
313 .or_default()
314 .push((rule.symbol, position, rule_idx));
315 if index.by_rotated_children.len() <= position {
316 index
317 .by_rotated_children
318 .resize_with(position + 1, SetTrie::default);
319 }
320 let mut rotated_children = SmallVec::<[StateId; 4]>::new();
321 rotated_children.push(child);
322 rotated_children.extend(rule.children[..position].iter().copied());
323 rotated_children.extend(rule.children[position + 1..].iter().copied());
324 index.by_rotated_children[position]
325 .get_or_insert_with(&rotated_children, FxHashMap::default)
326 .entry(rule.symbol)
327 .or_default()
328 .push(rule_idx);
329 }
330 }
331 index
332 }
333
334 pub(crate) fn rule_indexes_for_sets_into<S>(
335 &self,
336 symbols: &SymbolSet,
337 child_sets: &[S],
338 out: &mut Vec<usize>,
339 ) where
340 S: KeySet<StateId>,
341 {
342 out.clear();
343 self.by_children
344 .for_each_value_for_key_sets(child_sets, |rules_by_symbol| {
345 if symbols.len() < rules_by_symbol.len() {
346 for symbol in symbols.iter() {
347 if let Some(rule_indexes) = rules_by_symbol.get(&symbol) {
348 out.extend(rule_indexes.iter().copied());
349 }
350 }
351 } else {
352 for (&symbol, rule_indexes) in rules_by_symbol {
353 if symbols.contains(symbol) {
354 out.extend(rule_indexes.iter().copied());
355 }
356 }
357 }
358 });
359 }
360
361 fn extend_symbol_matches(
362 symbols: &SymbolSet,
363 rules_by_symbol: &FxHashMap<Symbol, Vec<usize>>,
364 out: &mut Vec<usize>,
365 ) {
366 if symbols.len() < rules_by_symbol.len() {
367 for symbol in symbols.iter() {
368 if let Some(rule_indexes) = rules_by_symbol.get(&symbol) {
369 out.extend(rule_indexes.iter().copied());
370 }
371 }
372 } else {
373 for (&symbol, rule_indexes) in rules_by_symbol {
374 if symbols.contains(symbol) {
375 out.extend(rule_indexes.iter().copied());
376 }
377 }
378 }
379 }
380
381 #[allow(dead_code)]
382 pub(crate) fn rule_indexes_for_rotated_sets_into<S>(
383 &self,
384 trigger_position: usize,
385 symbols: &SymbolSet,
386 rotated_child_sets: &[S],
387 out: &mut Vec<usize>,
388 ) where
389 S: KeySet<StateId>,
390 {
391 out.clear();
392 let Some(trie) = self.by_rotated_children.get(trigger_position) else {
393 return;
394 };
395 trie.for_each_value_for_key_sets(rotated_child_sets, |rules_by_symbol| {
396 Self::extend_symbol_matches(symbols, rules_by_symbol, out);
397 });
398 }
399
400 pub(crate) fn rule_indexes_for_rotated_trigger_sets_into<S>(
401 &self,
402 trigger_position: usize,
403 trigger_left: StateId,
404 symbols: &SymbolSet,
405 sibling_sets: &[S],
406 out: &mut Vec<usize>,
407 ) where
408 S: KeySet<StateId>,
409 {
410 out.clear();
411 let Some(trie) = self.by_rotated_children.get(trigger_position) else {
412 return;
413 };
414 trie.for_each_value_for_prefix_and_key_sets(
415 &[trigger_left],
416 sibling_sets,
417 |rules_by_symbol| {
418 Self::extend_symbol_matches(symbols, rules_by_symbol, out);
419 },
420 );
421 }
422}
423
424pub(crate) struct NullaryEdge {
426 pub(crate) rule_index: usize,
427 pub(crate) parent_left: StateId,
428 pub(crate) parent_right: StateId,
429 pub(crate) symbol: Symbol,
430 pub(crate) weight: f64,
431}
432
433pub(crate) struct CandidateEdge {
435 pub(crate) parent_left: StateId,
436 pub(crate) parent_right: StateId,
437 pub(crate) children: SmallVec<[StateId; 2]>,
438 pub(crate) weight: f64,
439 pub(crate) symbol: Symbol,
440 pub(crate) trigger_position: usize,
441}
442
443pub(crate) trait StateInterner<T> {
448 fn intern(&mut self, state: T) -> StateId;
449}
450
451impl<T> StateInterner<T> for Interner<T>
452where
453 T: Clone + Eq + Hash,
454{
455 fn intern(&mut self, state: T) -> StateId {
456 Interner::intern(self, state)
457 }
458}
459
460pub(crate) fn for_each_nullary_edge<R, I>(
461 left_rules: &[OwnedRule],
462 left_index: &LeftIndex,
463 right: &R,
464 right_interner: &mut I,
465 stats: &mut IndexedCondensedIntersectionStats,
466 control: Option<&ParseControl>,
467 on_edge: &mut dyn FnMut(NullaryEdge),
468) where
469 R: CondensedTa,
470 R::State: Clone + Eq + Hash,
471 I: StateInterner<R::State>,
472{
473 right.condensed_nullary_rules(&mut |symbols, right_result| {
474 if control.is_some_and(ParseControl::is_cancelled) {
475 return;
476 }
477 stats.right_nullary_rules += 1;
478 let right_result = right_interner.intern(right_result);
479 for symbol in symbols.iter() {
480 let Some(left_rule_indexes) = left_index.nullary_by_symbol.get(&symbol) else {
481 continue;
482 };
483 for &left_rule_idx in left_rule_indexes {
484 if control.is_some_and(ParseControl::is_cancelled) {
485 return;
486 }
487 let left_rule = &left_rules[left_rule_idx];
488 on_edge(NullaryEdge {
489 rule_index: left_rule_idx,
490 parent_left: left_rule.result,
491 parent_right: right_result,
492 symbol,
493 weight: left_rule.weight,
494 });
495 }
496 }
497 });
498}
499
500#[allow(clippy::too_many_arguments)]
508pub(crate) fn for_each_candidate_edge<R: CondensedTa>(
509 trigger_left: StateId,
510 trigger_right: StateId,
511 trigger_product: StateId,
512 left_rules: &[OwnedRule],
513 left_index: &LeftIndex,
514 product_ids: &ProductStateMap,
515 right: &R,
516 right_interner: &mut Interner<R::State>,
517 right_by_child_cache: &mut FxHashMap<(usize, StateId), Vec<OwnedCondensedRule<StateId>>>,
518 stats: &mut IndexedCondensedIntersectionStats,
519 control: Option<&ParseControl>,
520 on_edge: &mut dyn FnMut(CandidateEdge),
521) where
522 R::State: Clone + Eq + Hash,
523{
524 let Some(left_occurrences) = left_index.by_state.get(&trigger_left) else {
525 return;
526 };
527
528 for &(symbol, position, left_rule_idx) in left_occurrences {
529 if control.is_some_and(ParseControl::is_cancelled) {
530 return;
531 }
532 stat_inc!(stats, left_occurrences_considered);
533 let left_rule = &left_rules[left_rule_idx];
534 let cache_key = (position, trigger_right);
535 let right_rules = right_by_child_cache.entry(cache_key).or_insert_with(|| {
536 stats.right_indexed_queries += 1;
537 let raw_state = right_interner.resolve(trigger_right).clone();
538 let mut collected = Vec::new();
539 right.condensed_rules_by_child(
540 position,
541 &raw_state,
542 &mut |children, symbols, result| {
543 if control.is_some_and(ParseControl::is_cancelled) {
544 return;
545 }
546 collected.push(OwnedCondensedRule {
547 children: children
548 .iter()
549 .cloned()
550 .map(|child| right_interner.intern(child))
551 .collect(),
552 symbols: symbols.clone(),
553 result: right_interner.intern(result),
554 });
555 },
556 );
557 collected
558 });
559
560 for right_rule in right_rules {
561 if control.is_some_and(ParseControl::is_cancelled) {
562 return;
563 }
564 stat_inc!(stats, right_rules_scanned);
565 if !right_rule.symbols.contains(symbol)
566 || right_rule.children.len() != left_rule.children.len()
567 {
568 continue;
569 }
570 stat_inc!(stats, symbol_arity_matches);
571
572 let mut children = SmallVec::<[StateId; 2]>::new();
573 let mut ok = true;
574 for (child_position, (&left_child, &right_child)) in left_rule
575 .children
576 .iter()
577 .zip(&right_rule.children)
578 .enumerate()
579 {
580 if child_position == position
581 && left_child == trigger_left
582 && right_child == trigger_right
583 {
584 children.push(trigger_product);
585 } else if let Some(child) = product_ids.get(left_child, right_child) {
586 children.push(child);
587 } else {
588 ok = false;
589 break;
590 }
591 }
592 if !ok {
593 continue;
594 }
595 stat_inc!(stats, child_tuple_matches);
596
597 on_edge(CandidateEdge {
598 parent_left: left_rule.result,
599 parent_right: right_rule.result,
600 children,
601 weight: left_rule.weight,
602 symbol,
603 trigger_position: position,
604 });
605 }
606 }
607}
608
609pub fn materialize_indexed_condensed_intersection<R>(
618 left: &Explicit,
619 right: &R,
620) -> (
621 Explicit,
622 Interner<R::State>,
623 IndexedCondensedIntersectionStats,
624)
625where
626 R: CondensedTa,
627 R::State: Clone + Eq + Hash,
628{
629 let (explicit, right_interner, _pairs, stats) =
630 materialize_indexed_condensed_intersection_with_pairs(left, right);
631 (explicit, right_interner, stats)
632}
633
634#[allow(clippy::type_complexity)]
641pub fn materialize_indexed_condensed_intersection_with_pairs<R>(
642 left: &Explicit,
643 right: &R,
644) -> (
645 Explicit,
646 Interner<R::State>,
647 Vec<(StateId, StateId)>,
648 IndexedCondensedIntersectionStats,
649)
650where
651 R: CondensedTa,
652 R::State: Clone + Eq + Hash,
653{
654 materialize_indexed_condensed_intersection_with_pairs_controlled(
655 left,
656 right,
657 &ParseControl::new(),
658 )
659 .expect("a fresh parse control cannot be cancelled")
660}
661
662#[allow(clippy::type_complexity)]
663pub(crate) fn materialize_indexed_condensed_intersection_with_pairs_controlled<R>(
664 left: &Explicit,
665 right: &R,
666 control: &ParseControl,
667) -> Result<
668 (
669 Explicit,
670 Interner<R::State>,
671 Vec<(StateId, StateId)>,
672 IndexedCondensedIntersectionStats,
673 ),
674 ParseCancelled,
675>
676where
677 R: CondensedTa,
678 R::State: Clone + Eq + Hash,
679{
680 control.check()?;
681 let left_rules: Vec<_> = left
682 .rules()
683 .map(|rule| OwnedRule {
684 symbol: rule.symbol,
685 children: rule.children.iter().copied().collect(),
686 result: rule.result,
687 weight: rule.weight,
688 })
689 .collect();
690 let left_index = LeftIndex::build(&left_rules);
691
692 let mut right_interner = Interner::new();
693 let mut product_ids = ProductStateMap::new();
694 let mut product_pairs = Vec::<(StateId, StateId)>::new();
695 let mut queue = VecDeque::<(StateId, StateId, StateId)>::new();
696 let mut builder = ExplicitBuilder::new();
697 let mut rule_tracker = TrustedRuleTracker::default();
698 let mut right_by_child_cache =
699 FxHashMap::<(usize, StateId), Vec<OwnedCondensedRule<StateId>>>::default();
700 let mut stats = IndexedCondensedIntersectionStats::default();
701
702 let mut nullary_edges = Vec::<NullaryEdge>::new();
705 for_each_nullary_edge(
706 &left_rules,
707 &left_index,
708 right,
709 &mut right_interner,
710 &mut stats,
711 Some(control),
712 &mut |edge| nullary_edges.push(edge),
713 );
714 control.check()?;
715 for edge in nullary_edges {
716 control.check()?;
717 let (parent, is_new) = get_or_create_product_id(
718 edge.parent_left,
719 edge.parent_right,
720 left,
721 right,
722 &mut product_ids,
723 &mut product_pairs,
724 &right_interner,
725 &mut builder,
726 );
727 if is_new {
728 queue.push_back((edge.parent_left, edge.parent_right, parent));
729 }
730 rule_tracker.add_rule(
731 &mut builder,
732 edge.symbol,
733 SmallVec::new(),
734 parent,
735 edge.weight,
736 );
737 }
738
739 while let Some((left_state, right_state, current_product)) = queue.pop_front() {
740 control.check()?;
741 stat_inc!(stats, queue_pops);
742
743 let mut candidate_edges = Vec::<CandidateEdge>::new();
747 for_each_candidate_edge(
748 left_state,
749 right_state,
750 current_product,
751 &left_rules,
752 &left_index,
753 &product_ids,
754 right,
755 &mut right_interner,
756 &mut right_by_child_cache,
757 &mut stats,
758 Some(control),
759 &mut |edge| candidate_edges.push(edge),
760 );
761 control.check()?;
762 for edge in candidate_edges {
763 control.check()?;
764 if !current_product_is_latest(&edge.children, edge.trigger_position, current_product) {
765 continue;
766 }
767
768 let (parent, is_new) = get_or_create_product_id(
769 edge.parent_left,
770 edge.parent_right,
771 left,
772 right,
773 &mut product_ids,
774 &mut product_pairs,
775 &right_interner,
776 &mut builder,
777 );
778 if is_new {
779 queue.push_back((edge.parent_left, edge.parent_right, parent));
780 }
781 rule_tracker.add_rule(
782 &mut builder,
783 edge.symbol,
784 edge.children,
785 parent,
786 edge.weight,
787 );
788 }
789 }
790
791 stats.output_states = product_pairs.len();
792 let explicit = builder.build_trusted();
793 stats.output_rules = explicit.rules().count();
794 Ok((explicit, right_interner, product_pairs, stats))
795}
796
797pub fn materialize_topdown_condensed_intersection<R>(
804 left: &Explicit,
805 right: &R,
806) -> (
807 Explicit,
808 Interner<R::State>,
809 IndexedCondensedIntersectionStats,
810)
811where
812 R: CondensedTopDownTa,
813 R::State: Clone + Eq + Hash,
814{
815 let (explicit, right_interner, _pairs, stats) =
816 materialize_topdown_condensed_intersection_with_pairs(left, right);
817 (explicit, right_interner, stats)
818}
819
820#[allow(clippy::type_complexity)]
823pub fn materialize_topdown_condensed_intersection_with_pairs<R>(
824 left: &Explicit,
825 right: &R,
826) -> (
827 Explicit,
828 Interner<R::State>,
829 Vec<(StateId, StateId)>,
830 IndexedCondensedIntersectionStats,
831)
832where
833 R: CondensedTopDownTa,
834 R::State: Clone + Eq + Hash,
835{
836 materialize_topdown_condensed_intersection_with_pairs_controlled(
837 left,
838 right,
839 &ParseControl::new(),
840 )
841 .expect("a fresh parse control cannot be cancelled")
842}
843
844#[allow(clippy::type_complexity)]
845pub(crate) fn materialize_topdown_condensed_intersection_with_pairs_controlled<R>(
846 left: &Explicit,
847 right: &R,
848 control: &ParseControl,
849) -> Result<
850 (
851 Explicit,
852 Interner<R::State>,
853 Vec<(StateId, StateId)>,
854 IndexedCondensedIntersectionStats,
855 ),
856 ParseCancelled,
857>
858where
859 R: CondensedTopDownTa,
860 R::State: Clone + Eq + Hash,
861{
862 control.check()?;
863 let left_rules: Vec<_> = left
864 .rules()
865 .map(|rule| OwnedRule {
866 symbol: rule.symbol,
867 children: rule.children.iter().copied().collect(),
868 result: rule.result,
869 weight: rule.weight,
870 })
871 .collect();
872 let left_index = LeftIndex::build(&left_rules);
873
874 let mut ctx = TopDownIntersection {
875 left,
876 right,
877 left_rules: &left_rules,
878 left_index: &left_index,
879 right_interner: Interner::new(),
880 product_ids: ProductStateMap::new(),
881 product_pairs: Vec::new(),
882 builder: ExplicitBuilder::new(),
883 rule_tracker: TrustedRuleTracker::default(),
884 partners: Vec::new(),
885 visited: FixedBitSet::new(),
886 matches_scratch: Vec::new(),
887 stats: IndexedCondensedIntersectionStats::default(),
888 control,
889 };
890
891 right.condensed_initial_states(&mut |q| {
892 if control.is_cancelled() {
893 return;
894 }
895 let q = ctx.intern_right(q);
896 ctx.visit(q);
897 });
898 control.check()?;
899
900 ctx.stats.output_states = ctx.product_pairs.len();
901 let explicit = ctx.builder.build_trusted();
902 ctx.stats.output_rules = explicit.rules().count();
903 Ok((explicit, ctx.right_interner, ctx.product_pairs, ctx.stats))
904}
905
906struct TopDownIntersection<'a, R>
907where
908 R: CondensedTopDownTa,
909 R::State: Clone + Eq + Hash,
910{
911 left: &'a Explicit,
912 right: &'a R,
913 left_rules: &'a [OwnedRule],
914 left_index: &'a LeftIndex,
915 right_interner: Interner<R::State>,
916 product_ids: ProductStateMap,
917 product_pairs: Vec<(StateId, StateId)>,
918 builder: ExplicitBuilder,
919 rule_tracker: TrustedRuleTracker,
920 partners: Vec<FxHashSet<StateId>>,
921 visited: FixedBitSet,
922 matches_scratch: Vec<usize>,
923 stats: IndexedCondensedIntersectionStats,
924 control: &'a ParseControl,
925}
926
927impl<R> TopDownIntersection<'_, R>
928where
929 R: CondensedTopDownTa,
930 R::State: Clone + Eq + Hash,
931{
932 fn intern_right(&mut self, state: R::State) -> StateId {
933 let id = self.right_interner.intern(state);
934 if self.partners.len() <= id.index() {
935 self.partners
936 .resize_with(id.index() + 1, FxHashSet::default);
937 }
938 if self.visited.len() <= id.index() {
939 self.visited.grow(id.index() + 1);
940 }
941 id
942 }
943
944 fn visit(&mut self, q: StateId) {
945 if self.control.is_cancelled() {
946 return;
947 }
948 if self.visited.contains(q.index()) {
949 return;
950 }
951 self.visited.set(q.index(), true);
952 self.stats.right_indexed_queries += 1;
953
954 let raw_parent = self.right_interner.resolve(q).clone();
955 let mut normal_rules = SmallVec::<[OwnedCondensedRule<StateId>; 4]>::new();
956 let mut loop_rules = SmallVec::<[OwnedCondensedRule<StateId>; 4]>::new();
957
958 self.right
959 .condensed_rules_by_parent(&raw_parent, &mut |symbols, children| {
960 if self.control.is_cancelled() {
961 return;
962 }
963 let children: SmallVec<[StateId; 2]> = children
964 .iter()
965 .cloned()
966 .map(|child| self.intern_right(child))
967 .collect();
968 let rule = OwnedCondensedRule {
969 children,
970 symbols: symbols.clone(),
971 result: q,
972 };
973 if rule.children.contains(&q) {
974 loop_rules.push(rule);
975 } else {
976 normal_rules.push(rule);
977 }
978 });
979
980 for right_rule in normal_rules {
981 if self.control.is_cancelled() {
982 return;
983 }
984 for &child in &right_rule.children {
985 self.visit(child);
986 }
987 self.process_normal_rule(&right_rule);
988 }
989
990 for right_rule in &loop_rules {
991 if self.control.is_cancelled() {
992 return;
993 }
994 for &child in &right_rule.children {
995 if child != q {
996 self.visit(child);
997 }
998 }
999 }
1000 self.process_loop_rules(q, &loop_rules);
1001 }
1002
1003 fn process_normal_rule(&mut self, right_rule: &OwnedCondensedRule<StateId>) {
1004 if !right_rule
1005 .children
1006 .iter()
1007 .all(|&child| self.has_partners(child))
1008 {
1009 return;
1010 }
1011
1012 let child_sets = right_rule
1013 .children
1014 .iter()
1015 .map(|&child| &self.partners[child.index()])
1016 .collect::<SmallVec<[&FxHashSet<StateId>; 4]>>();
1017 self.left_index.rule_indexes_for_sets_into(
1018 &right_rule.symbols,
1019 &child_sets,
1020 &mut self.matches_scratch,
1021 );
1022 drop(child_sets);
1023 let matches = std::mem::take(&mut self.matches_scratch);
1024 for &rule_idx in &matches {
1025 if self.control.is_cancelled() {
1026 return;
1027 }
1028 let left_rule = &self.left_rules[rule_idx];
1029 self.collect_pair(left_rule, right_rule);
1030 }
1031 self.matches_scratch = matches;
1032 }
1033
1034 fn process_loop_rules(&mut self, q: StateId, loop_rules: &[OwnedCondensedRule<StateId>]) {
1035 if !self.has_partners(q) {
1036 return;
1037 }
1038
1039 for right_rule in loop_rules {
1040 if self.control.is_cancelled() {
1041 return;
1042 }
1043 for loop_position in right_rule
1044 .children
1045 .iter()
1046 .enumerate()
1047 .filter_map(|(position, &child)| (child == q).then_some(position))
1048 {
1049 let mut agenda = self.partners[q.index()]
1050 .iter()
1051 .copied()
1052 .collect::<VecDeque<_>>();
1053 let mut seen = self.partners[q.index()].clone();
1054
1055 while let Some(left_at_loop) = agenda.pop_front() {
1056 if self.control.is_cancelled() {
1057 return;
1058 }
1059 let mut child_sets = SmallVec::<[StateSetView<'_>; 4]>::new();
1060 let mut missing = false;
1061 for (position, &right_child) in right_rule.children.iter().enumerate() {
1062 if position == loop_position {
1063 child_sets.push(StateSetView::One(left_at_loop));
1064 } else if self.has_partners(right_child) {
1065 child_sets
1066 .push(StateSetView::Many(&self.partners[right_child.index()]));
1067 } else {
1068 missing = true;
1069 break;
1070 }
1071 }
1072 if missing {
1073 continue;
1074 }
1075
1076 self.left_index.rule_indexes_for_sets_into(
1077 &right_rule.symbols,
1078 &child_sets,
1079 &mut self.matches_scratch,
1080 );
1081 drop(child_sets);
1082 let matches = std::mem::take(&mut self.matches_scratch);
1083
1084 let mut newly_added = SmallVec::<[StateId; 4]>::new();
1085 for &rule_idx in &matches {
1086 if self.control.is_cancelled() {
1087 return;
1088 }
1089 let left_rule = &self.left_rules[rule_idx];
1090 if self.collect_pair(left_rule, right_rule) && seen.insert(left_rule.result)
1091 {
1092 newly_added.push(left_rule.result);
1093 }
1094 }
1095 for state in newly_added {
1096 agenda.push_back(state);
1097 }
1098 self.matches_scratch = matches;
1099 }
1100 }
1101 }
1102 }
1103
1104 fn has_partners(&self, right_state: StateId) -> bool {
1105 self.partners
1106 .get(right_state.index())
1107 .is_some_and(|partners| !partners.is_empty())
1108 }
1109
1110 fn collect_pair(
1111 &mut self,
1112 left_rule: &OwnedRule,
1113 right_rule: &OwnedCondensedRule<StateId>,
1114 ) -> bool {
1115 let mut children = SmallVec::<[StateId; 2]>::new();
1116 for (&left_child, &right_child) in left_rule.children.iter().zip(&right_rule.children) {
1117 let Some(child_pair) = self.product_ids.get(left_child, right_child) else {
1118 return false;
1119 };
1120 children.push(child_pair);
1121 }
1122
1123 let (parent, is_new_product) = get_or_create_product_id(
1124 left_rule.result,
1125 right_rule.result,
1126 self.left,
1127 self.right,
1128 &mut self.product_ids,
1129 &mut self.product_pairs,
1130 &self.right_interner,
1131 &mut self.builder,
1132 );
1133 if is_new_product {
1134 self.partners[right_rule.result.index()].insert(left_rule.result);
1135 }
1136
1137 self.rule_tracker.add_rule(
1138 &mut self.builder,
1139 left_rule.symbol,
1140 children,
1141 parent,
1142 left_rule.weight,
1143 );
1144 is_new_product
1145 }
1146}
1147
1148fn current_product_is_latest(
1149 children: &SmallVec<[StateId; 2]>,
1150 current_position: usize,
1151 current_product: StateId,
1152) -> bool {
1153 let mut latest = StateId(0);
1154 let mut first_latest_position = 0;
1155 for (position, &child) in children.iter().enumerate() {
1156 if position == 0 || child.0 > latest.0 {
1157 latest = child;
1158 first_latest_position = position;
1159 }
1160 }
1161 latest == current_product && first_latest_position == current_position
1162}
1163
1164#[allow(clippy::too_many_arguments)]
1165pub(crate) fn get_or_create_product_id<R>(
1166 left_state: StateId,
1167 right_state: StateId,
1168 left: &Explicit,
1169 right: &R,
1170 ids: &mut ProductStateMap,
1171 pairs: &mut Vec<(StateId, StateId)>,
1172 right_interner: &Interner<R::State>,
1173 builder: &mut ExplicitBuilder,
1174) -> (StateId, bool)
1175where
1176 R: BottomUpTa,
1177{
1178 if let Some(id) = ids.get(left_state, right_state) {
1179 return (id, false);
1180 }
1181 let id = builder.new_state();
1182 ids.insert(left_state, right_state, id);
1183 pairs.push((left_state, right_state));
1184 if left.is_accepting(&left_state) && right.is_accepting(right_interner.resolve(right_state)) {
1185 builder.add_accepting(id);
1186 }
1187 (id, true)
1188}
1189
1190pub(crate) fn get_or_create_product_id_direct(
1191 left_state: StateId,
1192 right_state: StateId,
1193 ids: &mut ProductStateMap,
1194 pairs: &mut Vec<(StateId, StateId)>,
1195) -> (StateId, bool) {
1196 if let Some(id) = ids.get(left_state, right_state) {
1197 return (id, false);
1198 }
1199
1200 let id = StateId(pairs.len() as u32);
1201 ids.insert(left_state, right_state, id);
1202 pairs.push((left_state, right_state));
1203 (id, true)
1204}
1205
1206#[cfg(test)]
1207mod tests {
1208 use super::*;
1209 use crate::{BottomUpTa, ExplicitBuilder};
1210
1211 #[test]
1212 fn product_state_map_is_right_major_and_sparse() {
1213 let mut map = ProductStateMap::new();
1214 let left = StateId(7);
1215 let right = StateId(3);
1216 let product = StateId(11);
1217
1218 assert_eq!(map.get(left, right), None);
1219 map.insert(left, right, product);
1220 assert_eq!(map.get(left, right), Some(product));
1221 assert_eq!(map.get(StateId(8), right), None);
1222 assert_eq!(map.get(left, StateId(4)), None);
1223 }
1224
1225 #[test]
1226 fn state_set_view_matches_set_trie_traversal() {
1227 let mut trie = SetTrie::new();
1228 trie.get_or_insert_with(&[StateId(1), StateId(2)], Vec::new)
1229 .push("hit");
1230 trie.get_or_insert_with(&[StateId(3), StateId(2)], Vec::new)
1231 .push("miss");
1232
1233 let second = FxHashSet::from_iter([StateId(2)]);
1234 let key_sets = [StateSetView::One(StateId(1)), StateSetView::Many(&second)];
1235 let mut values = Vec::new();
1236 trie.for_each_value_for_key_sets(&key_sets, |found| {
1237 values.extend(found.iter().copied());
1238 });
1239
1240 assert_eq!(values, vec!["hit"]);
1241 }
1242
1243 #[test]
1244 fn left_index_reuses_output_buffer_for_set_queries() {
1245 let symbol = Symbol(1);
1246 let other_symbol = Symbol(2);
1247 let q0 = StateId(0);
1248 let q1 = StateId(1);
1249 let rules = vec![
1250 OwnedRule {
1251 symbol,
1252 children: SmallVec::from_slice(&[q0, q1]),
1253 result: StateId(2),
1254 weight: 1.0,
1255 },
1256 OwnedRule {
1257 symbol: other_symbol,
1258 children: SmallVec::from_slice(&[q0, q1]),
1259 result: StateId(3),
1260 weight: 1.0,
1261 },
1262 ];
1263 let index = LeftIndex::build(&rules);
1264 let mut symbols = SymbolSet::default();
1265 symbols.insert(symbol);
1266 let first = FxHashSet::from_iter([q0]);
1267 let second = FxHashSet::from_iter([q1]);
1268 let key_sets = [&first, &second];
1269 let mut out = vec![usize::MAX];
1270
1271 index.rule_indexes_for_sets_into(&symbols, &key_sets, &mut out);
1272 assert_eq!(out, vec![0]);
1273
1274 symbols.insert(other_symbol);
1275 index.rule_indexes_for_sets_into(&symbols, &key_sets, &mut out);
1276 out.sort_unstable();
1277 assert_eq!(out, vec![0, 1]);
1278 }
1279
1280 #[test]
1281 fn materializes_explicit_identity_fragment() {
1282 let a = Symbol(0);
1283 let f = Symbol(1);
1284 let mut b = ExplicitBuilder::new();
1285 let leaf = b.new_state();
1286 let root = b.new_state();
1287 b.add_rule(a, vec![], leaf);
1288 b.add_rule(f, vec![leaf, leaf], root);
1289 b.add_accepting(root);
1290 let explicit = b.build();
1291
1292 let (mat, _interner) = materialize(&explicit, &[(a, 0), (f, 2)]);
1293 let mut leaves = Vec::new();
1294 mat.step(a, &[], &mut |q| leaves.push(q));
1295 let mut roots = Vec::new();
1296 mat.step(f, &[leaves[0], leaves[0]], &mut |q| roots.push(q));
1297 assert_eq!(roots.len(), 1);
1298 assert!(mat.is_accepting(&roots[0]));
1299 }
1300
1301 #[test]
1302 fn topdown_condensed_intersection_matches_indexed_on_explicit_pair() {
1303 let a = Symbol(0);
1304 let f = Symbol(1);
1305
1306 let mut left_builder = ExplicitBuilder::new();
1307 let left_leaf = left_builder.new_state();
1308 let left_root = left_builder.new_state();
1309 left_builder.add_rule(a, vec![], left_leaf);
1310 left_builder.add_rule(f, vec![left_leaf, left_leaf], left_root);
1311 left_builder.add_accepting(left_root);
1312 let left = left_builder.build();
1313
1314 let mut right_builder = ExplicitBuilder::new();
1315 let right_leaf = right_builder.new_state();
1316 let right_root = right_builder.new_state();
1317 right_builder.add_rule(a, vec![], right_leaf);
1318 right_builder.add_rule(f, vec![right_leaf, right_leaf], right_root);
1319 right_builder.add_accepting(right_root);
1320 let right = right_builder.build();
1321
1322 let (topdown, _, topdown_stats) = materialize_topdown_condensed_intersection(&left, &right);
1323 let (indexed, _, indexed_stats) = materialize_indexed_condensed_intersection(&left, &right);
1324
1325 assert_eq!(topdown_stats.output_states, indexed_stats.output_states);
1326 assert_eq!(topdown_stats.output_rules, indexed_stats.output_rules);
1327 assert_eq!(topdown.rules().count(), indexed.rules().count());
1328 assert!(!topdown.is_empty());
1329 }
1330}