1use crate::{
4 BottomUpTa, DetBottomUpTa, FxHashMap, FxHashSet, IndexedBottomUpTa, StateId, Symbol, TopDownTa,
5 traits::{CondensedTa, CondensedTopDownTa, StateUniverse, SymbolSet},
6};
7use fixedbitset::FixedBitSet;
8use smallvec::SmallVec;
9use std::hash::{BuildHasher, Hash, Hasher};
10use std::sync::OnceLock;
11use thiserror::Error;
12
13type Results = SmallVec<[StateId; 2]>;
14
15#[derive(Clone, Debug)]
25pub struct Explicit {
26 num_states: u32,
27 accepting: FixedBitSet,
28 rules: Vec<StoredRule>,
29 bottom_up_indexes: OnceLock<BottomUpIndexes>,
30 reachable_cache: OnceLock<FixedBitSet>,
31 result_index: OnceLock<Vec<Vec<usize>>>,
32 indexes: OnceLock<Indexes>,
33 condensed_cache: OnceLock<Vec<CondensedRule>>,
34}
35
36#[derive(Clone, Debug, Eq)]
37struct HigherKey(Symbol, Box<[StateId]>);
38
39impl PartialEq for HigherKey {
40 fn eq(&self, other: &Self) -> bool {
41 self.0 == other.0 && self.1 == other.1
42 }
43}
44
45impl Hash for HigherKey {
46 fn hash<H: Hasher>(&self, state: &mut H) {
47 self.0.hash(state);
48 self.1.hash(state);
49 }
50}
51
52#[derive(Clone, Debug)]
53struct StoredRule {
54 symbol: Symbol,
55 children: SmallVec<[StateId; 2]>,
56 result: StateId,
57 weight: f64,
58}
59
60#[derive(Clone, Debug, PartialEq, Eq, Hash)]
61struct RuleKey {
62 symbol: Symbol,
63 children: SmallVec<[StateId; 2]>,
64 result: StateId,
65}
66
67#[derive(Clone, Debug, PartialEq, Eq)]
68struct CondensedRule {
69 children: Box<[StateId]>,
70 symbols: SymbolSet,
71 result: StateId,
72}
73
74#[derive(Clone, Debug, Default)]
75struct BottomUpIndexes {
76 nullary: FxHashMap<Symbol, Results>,
77 unary: FxHashMap<(Symbol, StateId), Results>,
78 binary: FxHashMap<(Symbol, StateId, StateId), Results>,
79 higher: FxHashMap<HigherKey, Results>,
80}
81
82#[derive(Clone, Debug, Default)]
83struct Indexes {
84 by_child: FxHashMap<(Symbol, usize, StateId), Vec<usize>>,
85}
86
87#[derive(Clone, Copy, Debug, PartialEq)]
92pub struct Rule<'a> {
93 pub symbol: Symbol,
95 pub children: &'a [StateId],
97 pub result: StateId,
99 pub weight: f64,
101}
102
103#[derive(Clone, Debug, Error, PartialEq)]
105pub enum ExplicitBuildError {
106 #[error("duplicate transition for symbol {symbol:?}, children {children:?}, result {result:?}")]
108 DuplicateTransition {
109 symbol: Symbol,
111 children: Vec<StateId>,
113 result: StateId,
115 },
116}
117
118#[derive(Clone, Debug, Default)]
127pub struct ExplicitBuilder {
128 next_state: u32,
129 accepting: Vec<StateId>,
130 rules: Vec<(Symbol, SmallVec<[StateId; 2]>, StateId, f64)>,
131}
132
133impl ExplicitBuilder {
134 pub fn new() -> Self {
136 Self::default()
137 }
138
139 pub fn new_state(&mut self) -> StateId {
143 assert_ne!(self.next_state, StateId::STUCK.0, "cannot allocate STUCK");
144 let id = StateId(self.next_state);
145 self.next_state += 1;
146 id
147 }
148
149 pub fn add_accepting(&mut self, q: StateId) {
154 self.check_state(q);
155 self.accepting.push(q);
156 }
157
158 pub fn add_rule(&mut self, f: Symbol, children: Vec<StateId>, q: StateId) {
164 self.add_weighted_rule(f, children, q, 1.0);
165 }
166
167 pub fn add_weighted_rule(
173 &mut self,
174 f: Symbol,
175 children: Vec<StateId>,
176 q: StateId,
177 weight: f64,
178 ) {
179 self.add_weighted_rule_inline(f, SmallVec::from_vec(children), q, weight);
180 }
181
182 pub(crate) fn add_weighted_rule_inline(
188 &mut self,
189 f: Symbol,
190 children: SmallVec<[StateId; 2]>,
191 q: StateId,
192 weight: f64,
193 ) {
194 self.check_state(q);
195 for &child in &children {
196 self.check_state(child);
197 }
198 self.rules.push((f, children, q, weight));
199 }
200
201 pub fn build(self) -> Explicit {
206 self.try_build()
207 .expect("explicit automaton contains duplicate transitions")
208 }
209
210 pub fn try_build(self) -> Result<Explicit, ExplicitBuildError> {
217 self.finish(true)
218 }
219
220 pub(crate) fn build_trusted(self) -> Explicit {
226 self.finish(false)
227 .expect("trusted explicit automaton build cannot fail")
228 }
229
230 fn finish(self, check_duplicates: bool) -> Result<Explicit, ExplicitBuildError> {
231 let mut accepting = FixedBitSet::with_capacity(self.next_state as usize);
232 for q in self.accepting {
233 accepting.set(q.index(), true);
234 }
235
236 let mut seen = FxHashSet::default();
237 let mut stored = Vec::with_capacity(self.rules.len());
238
239 for (symbol, children, result, weight) in self.rules {
240 if check_duplicates {
241 let key = RuleKey {
242 symbol,
243 children: children.clone(),
244 result,
245 };
246 if !seen.insert(key) {
247 return Err(ExplicitBuildError::DuplicateTransition {
248 symbol,
249 children: children.into_vec(),
250 result,
251 });
252 }
253 }
254 let rule = StoredRule {
255 symbol,
256 children,
257 result,
258 weight,
259 };
260 stored.push(rule);
261 }
262
263 Ok(Explicit {
264 num_states: self.next_state,
265 accepting,
266 rules: stored,
267 bottom_up_indexes: OnceLock::new(),
268 reachable_cache: OnceLock::new(),
269 result_index: OnceLock::new(),
270 indexes: OnceLock::new(),
271 condensed_cache: OnceLock::new(),
272 })
273 }
274
275 fn check_state(&self, q: StateId) {
276 assert!(
277 !q.is_stuck(),
278 "StateId::STUCK is not a valid explicit state"
279 );
280 assert!(
281 q.0 < self.next_state,
282 "state {:?} was not allocated by this builder",
283 q
284 );
285 }
286}
287
288impl Explicit {
289 pub fn num_states(&self) -> u32 {
291 self.num_states
292 }
293
294 pub fn num_rules(&self) -> usize {
296 self.rules.len()
297 }
298
299 pub fn is_empty(&self) -> bool {
304 !self
305 .reachable_states()
306 .ones()
307 .any(|idx| self.accepting.contains(idx))
308 }
309
310 pub fn reachable_states(&self) -> FixedBitSet {
317 self.reachable_cache
318 .get_or_init(|| self.compute_reachable_states())
319 .clone()
320 }
321
322 fn compute_reachable_states(&self) -> FixedBitSet {
323 let mut reachable = FixedBitSet::with_capacity(self.num_states as usize);
324 let mut worklist = Vec::new();
325
326 let mut remaining: Vec<usize> = self.rules.iter().map(|r| r.children.len()).collect();
327 let mut mentions: FxHashMap<StateId, Vec<usize>> = FxHashMap::default();
328
329 for (idx, rule) in self.rules.iter().enumerate() {
330 if rule.children.is_empty()
331 && mark_reachable(&mut reachable, &mut worklist, rule.result)
332 {
333 continue;
334 }
335 let mut unique_children: SmallVec<[StateId; 4]> = SmallVec::new();
336 for &child in rule.children.iter() {
337 if !unique_children.contains(&child) {
338 unique_children.push(child);
339 mentions.entry(child).or_default().push(idx);
340 }
341 }
342 }
343
344 while let Some(q) = worklist.pop() {
345 let Some(dependents) = mentions.get(&q) else {
346 continue;
347 };
348 for &idx in dependents {
349 if remaining[idx] == 0 {
350 continue;
351 }
352 let rule = &self.rules[idx];
353 let newly_satisfied = rule.children.iter().filter(|&&c| c == q).count();
354 remaining[idx] = remaining[idx].saturating_sub(newly_satisfied);
355 if remaining[idx] == 0 {
356 mark_reachable(&mut reachable, &mut worklist, rule.result);
357 }
358 }
359 }
360
361 reachable
362 }
363
364 pub fn rules(&self) -> impl Iterator<Item = Rule<'_>> {
369 self.rules.iter().map(|rule| Rule {
370 symbol: rule.symbol,
371 children: rule.children.as_slice(),
372 result: rule.result,
373 weight: rule.weight,
374 })
375 }
376
377 pub fn rules_topdown(&self, parent: StateId) -> impl Iterator<Item = Rule<'_>> {
379 self.result_index()[parent.index()]
380 .iter()
381 .map(|&rule_idx| self.rule(rule_idx))
382 }
383
384 pub fn rule(&self, rule_idx: usize) -> Rule<'_> {
391 let rule = &self.rules[rule_idx];
392 Rule {
393 symbol: rule.symbol,
394 children: rule.children.as_slice(),
395 result: rule.result,
396 weight: rule.weight,
397 }
398 }
399
400 pub(crate) fn rule_indexes_topdown(&self, parent: StateId) -> &[usize] {
401 &self.result_index()[parent.index()]
402 }
403
404 fn result_index(&self) -> &[Vec<usize>] {
405 self.result_index.get_or_init(|| {
406 let mut counts = vec![0usize; self.num_states as usize];
407 for rule in &self.rules {
408 counts[rule.result.index()] += 1;
409 }
410
411 let mut by_result = counts
412 .into_iter()
413 .map(Vec::with_capacity)
414 .collect::<Vec<_>>();
415 for (rule_idx, rule) in self.rules.iter().enumerate() {
416 by_result[rule.result.index()].push(rule_idx);
417 }
418 by_result
419 })
420 }
421
422 fn bottom_up_indexes(&self) -> &BottomUpIndexes {
423 self.bottom_up_indexes.get_or_init(|| {
424 let mut indexes = BottomUpIndexes::default();
425 for rule in &self.rules {
426 match rule.children.len() {
427 0 => push_result(indexes.nullary.entry(rule.symbol).or_default(), rule.result),
428 1 => push_result(
429 indexes
430 .unary
431 .entry((rule.symbol, rule.children[0]))
432 .or_default(),
433 rule.result,
434 ),
435 2 => push_result(
436 indexes
437 .binary
438 .entry((rule.symbol, rule.children[0], rule.children[1]))
439 .or_default(),
440 rule.result,
441 ),
442 _ => push_result(
443 indexes
444 .higher
445 .entry(HigherKey(
446 rule.symbol,
447 rule.children.clone().into_vec().into_boxed_slice(),
448 ))
449 .or_default(),
450 rule.result,
451 ),
452 }
453 }
454 indexes
455 })
456 }
457
458 fn lookup_higher<'a>(
459 indexes: &'a BottomUpIndexes,
460 f: Symbol,
461 children: &[StateId],
462 ) -> Option<&'a Results> {
463 let mut hasher = indexes.higher.hasher().build_hasher();
464 f.hash(&mut hasher);
465 children.hash(&mut hasher);
466 let hash = hasher.finish();
467 indexes
468 .higher
469 .raw_entry()
470 .from_hash(hash, |k| k.0 == f && &*k.1 == children)
471 .map(|(_, v)| v)
472 }
473
474 fn indexes(&self) -> &Indexes {
475 self.indexes.get_or_init(|| {
476 let mut indexes = Indexes::default();
477 for (rule_idx, rule) in self.rules.iter().enumerate() {
478 for (position, &child) in rule.children.iter().enumerate() {
479 indexes
480 .by_child
481 .entry((rule.symbol, position, child))
482 .or_default()
483 .push(rule_idx);
484 }
485 }
486 indexes
487 })
488 }
489
490 fn condensed_cache(&self) -> &[CondensedRule] {
491 self.condensed_cache.get_or_init(|| {
492 let mut groups: FxHashMap<(Vec<StateId>, StateId), SymbolSet> = FxHashMap::default();
493 for rule in &self.rules {
494 groups
495 .entry((rule.children.to_vec(), rule.result))
496 .or_default()
497 .insert(rule.symbol);
498 }
499
500 let mut condensed: Vec<_> = groups
501 .into_iter()
502 .map(|((children, result), symbols)| CondensedRule {
503 children: children.into_boxed_slice(),
504 symbols,
505 result,
506 })
507 .collect();
508 condensed.sort_by(|a, b| {
509 (&a.children, a.result, a.symbols.iter().collect::<Vec<_>>()).cmp(&(
510 &b.children,
511 b.result,
512 b.symbols.iter().collect::<Vec<_>>(),
513 ))
514 });
515 condensed
516 })
517 }
518}
519
520impl BottomUpTa for Explicit {
521 type State = StateId;
522
523 fn step(&self, f: Symbol, children: &[StateId], out: &mut dyn FnMut(StateId)) {
524 let indexes = self.bottom_up_indexes();
525 let results = match children.len() {
526 0 => indexes.nullary.get(&f),
527 1 => indexes.unary.get(&(f, children[0])),
528 2 => indexes.binary.get(&(f, children[0], children[1])),
529 _ => Self::lookup_higher(indexes, f, children),
530 };
531 if let Some(results) = results {
532 for &q in results {
533 out(q);
534 }
535 }
536 }
537
538 fn is_accepting(&self, q: &StateId) -> bool {
539 !q.is_stuck() && self.accepting.contains(q.index())
540 }
541}
542
543impl DetBottomUpTa for Explicit {
544 fn step_det(&self, f: Symbol, children: &[StateId]) -> Option<StateId> {
545 let indexes = self.bottom_up_indexes();
546 let results = match children.len() {
547 0 => indexes.nullary.get(&f),
548 1 => indexes.unary.get(&(f, children[0])),
549 2 => indexes.binary.get(&(f, children[0], children[1])),
550 _ => Self::lookup_higher(indexes, f, children),
551 }?;
552 (results.len() == 1).then_some(results[0])
553 }
554}
555
556impl IndexedBottomUpTa for Explicit {
557 fn step_partial(
558 &self,
559 f: Symbol,
560 position: usize,
561 state_at_position: &StateId,
562 out: &mut dyn FnMut(&[StateId], StateId),
563 ) {
564 let Some(rule_indexes) = self
565 .indexes()
566 .by_child
567 .get(&(f, position, *state_at_position))
568 else {
569 return;
570 };
571
572 for &rule_idx in rule_indexes {
573 let rule = &self.rules[rule_idx];
574 out(&rule.children, rule.result);
575 }
576 }
577}
578
579impl TopDownTa for Explicit {
580 fn step_topdown(&self, parent: &StateId, out: &mut dyn FnMut(Symbol, &[StateId])) {
581 if parent.is_stuck() {
582 return;
583 }
584 let Some(rule_indexes) = self.result_index().get(parent.index()) else {
585 return;
586 };
587 for &rule_idx in rule_indexes {
588 let rule = &self.rules[rule_idx];
589 out(rule.symbol, &rule.children);
590 }
591 }
592
593 fn initial_states(&self, out: &mut dyn FnMut(StateId)) {
594 for idx in self.accepting.ones() {
595 out(StateId(idx as u32));
596 }
597 }
598}
599
600impl StateUniverse for Explicit {
601 fn all_states(&self, out: &mut dyn FnMut(StateId)) {
602 for idx in 0..self.num_states {
603 out(StateId(idx));
604 }
605 }
606}
607
608impl CondensedTa for Explicit {
609 fn condensed_rules(&self, out: &mut dyn FnMut(&[StateId], &SymbolSet, StateId)) {
610 for rule in self.condensed_cache() {
611 out(&rule.children, &rule.symbols, rule.result);
612 }
613 }
614
615 fn condensed_nullary_rules(&self, out: &mut dyn FnMut(&SymbolSet, StateId)) {
616 for rule in self.condensed_cache() {
617 if rule.children.is_empty() {
618 out(&rule.symbols, rule.result);
619 }
620 }
621 }
622
623 fn condensed_rules_by_child(
624 &self,
625 position: usize,
626 state: &StateId,
627 out: &mut dyn FnMut(&[StateId], &SymbolSet, StateId),
628 ) {
629 for rule in self.condensed_cache() {
630 if rule.children.get(position) == Some(state) {
631 out(&rule.children, &rule.symbols, rule.result);
632 }
633 }
634 }
635}
636
637impl CondensedTopDownTa for Explicit {
638 fn condensed_rules_by_parent(
639 &self,
640 parent: &StateId,
641 out: &mut dyn FnMut(&SymbolSet, &[StateId]),
642 ) {
643 for rule in self.condensed_cache() {
644 if &rule.result == parent {
645 out(&rule.symbols, &rule.children);
646 }
647 }
648 }
649
650 fn condensed_initial_states(&self, out: &mut dyn FnMut(StateId)) {
651 self.initial_states(out);
652 }
653}
654
655fn push_result(results: &mut Results, q: StateId) {
656 if !results.contains(&q) {
657 results.push(q);
658 }
659}
660
661fn mark_reachable(bits: &mut FixedBitSet, worklist: &mut Vec<StateId>, q: StateId) -> bool {
662 if bits.contains(q.index()) {
663 false
664 } else {
665 bits.set(q.index(), true);
666 worklist.push(q);
667 true
668 }
669}
670
671#[cfg(test)]
672mod tests {
673 use super::*;
674 use crate::BottomUpTa;
675 use std::collections::hash_map::DefaultHasher;
676
677 #[test]
678 fn add_rule_defaults_to_unit_weight() {
679 let mut b = ExplicitBuilder::new();
680 let q = b.new_state();
681 b.add_rule(Symbol(1), vec![], q);
682 let e = b.build();
683 let rule = e.rules().next().unwrap();
684 assert_eq!(rule.weight, 1.0);
685 let mut out = Vec::new();
686 e.step(Symbol(1), &[], &mut |q| out.push(q));
687 assert_eq!(out, vec![q]);
688 }
689
690 #[test]
691 fn add_weighted_rule_stores_weight() {
692 let mut b = ExplicitBuilder::new();
693 let q = b.new_state();
694 b.add_weighted_rule(Symbol(1), vec![], q, 0.25);
695 let e = b.build();
696 let rule = e.rules().next().unwrap();
697 assert_eq!(rule.weight, 0.25);
698 }
699
700 #[test]
701 fn add_weighted_rule_inline_preserves_child_tuple() {
702 let mut b = ExplicitBuilder::new();
703 let left = b.new_state();
704 let right = b.new_state();
705 let parent = b.new_state();
706 b.add_weighted_rule_inline(
707 Symbol(1),
708 SmallVec::from_slice(&[left, right]),
709 parent,
710 0.75,
711 );
712
713 let e = b.build();
714 let rule = e.rules().next().unwrap();
715 assert_eq!(rule.children, &[left, right]);
716 assert_eq!(rule.result, parent);
717 assert_eq!(rule.weight, 0.75);
718 }
719
720 #[test]
721 fn builder_rejects_duplicate_transition_with_same_weight() {
722 let mut b = ExplicitBuilder::new();
723 let q = b.new_state();
724 b.add_weighted_rule(Symbol(1), vec![], q, 0.5);
725 b.add_weighted_rule(Symbol(1), vec![], q, 0.5);
726 assert!(matches!(
727 b.try_build(),
728 Err(ExplicitBuildError::DuplicateTransition { .. })
729 ));
730 }
731
732 #[test]
733 fn builder_rejects_duplicate_transition_with_different_weight() {
734 let mut b = ExplicitBuilder::new();
735 let q = b.new_state();
736 b.add_weighted_rule(Symbol(1), vec![], q, 0.5);
737 b.add_weighted_rule(Symbol(1), vec![], q, 0.75);
738 assert!(matches!(
739 b.try_build(),
740 Err(ExplicitBuildError::DuplicateTransition { .. })
741 ));
742 }
743
744 #[test]
745 fn deterministic_matches_step_for_single_result() {
746 let mut b = ExplicitBuilder::new();
749 let q = b.new_state();
750 b.add_rule(Symbol(1), vec![], q);
751 let e = b.build();
752 assert_eq!(e.step_det(Symbol(1), &[]), Some(q));
753 }
754
755 #[test]
756 fn nondeterministic_step_det_returns_none() {
757 let mut b = ExplicitBuilder::new();
761 let q0 = b.new_state();
762 let q1 = b.new_state();
763 b.add_rule(Symbol(1), vec![], q0);
764 b.add_rule(Symbol(1), vec![], q1);
765 let e = b.build();
766 assert_eq!(e.step_det(Symbol(1), &[]), None);
767 }
768
769 #[test]
770 fn reachable_saturates_rules() {
771 let mut b = ExplicitBuilder::new();
775 let leaf = b.new_state();
776 let root = b.new_state();
777 b.add_rule(Symbol(0), vec![], leaf);
778 b.add_rule(Symbol(1), vec![leaf, leaf], root);
779 b.add_accepting(root);
780 let e = b.build();
781 let r = e.reachable_states();
782 assert!(r.contains(leaf.index()));
783 assert!(r.contains(root.index()));
784 assert!(!e.is_empty());
785 }
786
787 #[test]
788 fn higher_key_hash_matches_borrowed_tuple() {
789 let children = [StateId(1), StateId(2), StateId(3)];
793 let key = HigherKey(Symbol(7), Box::from(children));
794 let mut a = DefaultHasher::new();
795 key.hash(&mut a);
796 let mut b = DefaultHasher::new();
797 Symbol(7).hash(&mut b);
798 children.hash(&mut b);
799 assert_eq!(a.finish(), b.finish());
800 }
801
802 #[test]
803 fn higher_arity_lookup_works() {
804 let mut b = ExplicitBuilder::new();
807 let q0 = b.new_state();
808 let q1 = b.new_state();
809 let q2 = b.new_state();
810 let q3 = b.new_state();
811 b.add_rule(Symbol(9), vec![q0, q1, q2], q3);
812 let e = b.build();
813 assert_eq!(e.step_det(Symbol(9), &[q0, q1, q2]), Some(q3));
814 }
815
816 #[test]
817 fn indexed_step_partial_finds_matching_binary_rules() {
818 let mut b = ExplicitBuilder::new();
822 let left = b.new_state();
823 let right = b.new_state();
824 let root = b.new_state();
825 let other = b.new_state();
826 b.add_rule(Symbol(3), vec![left, right], root);
827 b.add_rule(Symbol(3), vec![other, right], other);
828 let e = b.build();
829
830 let mut found = Vec::new();
831 e.step_partial(Symbol(3), 0, &left, &mut |children, result| {
832 found.push((children.to_vec(), result));
833 });
834
835 assert_eq!(found, vec![(vec![left, right], root)]);
836 }
837
838 #[test]
839 fn indexed_step_partial_supports_higher_arity_rules() {
840 let mut b = ExplicitBuilder::new();
844 let q0 = b.new_state();
845 let q1 = b.new_state();
846 let q2 = b.new_state();
847 let q3 = b.new_state();
848 b.add_rule(Symbol(9), vec![q0, q1, q2], q3);
849 let e = b.build();
850
851 let mut found = Vec::new();
852 e.step_partial(Symbol(9), 1, &q1, &mut |children, result| {
853 found.push((children.to_vec(), result));
854 });
855
856 assert_eq!(found, vec![(vec![q0, q1, q2], q3)]);
857 }
858
859 #[test]
860 fn topdown_enumerates_rules_by_parent() {
861 let mut b = ExplicitBuilder::new();
864 let leaf = b.new_state();
865 let root = b.new_state();
866 b.add_rule(Symbol(0), vec![], leaf);
867 b.add_rule(Symbol(1), vec![leaf, leaf], root);
868 b.add_accepting(root);
869 let e = b.build();
870
871 let mut rules = Vec::new();
872 e.step_topdown(&root, &mut |symbol, children| {
873 rules.push((symbol, children.to_vec()));
874 });
875 let mut initials = Vec::new();
876 e.initial_states(&mut |q| initials.push(q));
877
878 assert_eq!(rules, vec![(Symbol(1), vec![leaf, leaf])]);
879 assert_eq!(initials, vec![root]);
880 }
881
882 #[test]
883 fn bottom_up_indexes_are_built_lazily() {
884 let mut b = ExplicitBuilder::new();
885 let leaf = b.new_state();
886 let root = b.new_state();
887 b.add_rule(Symbol(0), vec![], leaf);
888 b.add_rule(Symbol(1), vec![leaf], root);
889 b.add_accepting(root);
890 let e = b.build();
891
892 assert!(e.bottom_up_indexes.get().is_none());
893
894 let mut topdown_rules = Vec::new();
895 e.step_topdown(&root, &mut |symbol, children| {
896 topdown_rules.push((symbol, children.to_vec()));
897 });
898 assert_eq!(topdown_rules, vec![(Symbol(1), vec![leaf])]);
899 assert!(e.bottom_up_indexes.get().is_none());
900
901 let best = e.viterbi().unwrap();
902 assert_eq!(*best.arena().get_label(best.root()), Symbol(1));
903 assert!(e.bottom_up_indexes.get().is_none());
904
905 let mut leaves = Vec::new();
906 e.step(Symbol(0), &[], &mut |q| leaves.push(q));
907 assert_eq!(leaves, vec![leaf]);
908 assert!(e.bottom_up_indexes.get().is_some());
909 }
910
911 #[test]
913 fn indexed_rule_access_matches_iteration() {
914 let mut b = ExplicitBuilder::new();
915 let q = b.new_state();
916 let r = b.new_state();
917 b.add_rule(Symbol(0), vec![], q);
918 b.add_rule(Symbol(1), vec![q], r);
919 b.add_accepting(r);
920 let a = b.build();
921
922 assert_eq!(a.num_rules(), 2);
923 for (i, rule) in a.rules().enumerate() {
924 assert_eq!(a.rule(i), rule);
925 }
926 }
927
928 #[test]
929 fn condensed_rules_groups_symbols_by_shape() {
930 let mut b = ExplicitBuilder::new();
934 let q0 = b.new_state();
935 let q1 = b.new_state();
936 let qr = b.new_state();
937 b.add_rule(Symbol(0), vec![q0, q1], qr);
939 b.add_rule(Symbol(1), vec![q0, q1], qr);
940 b.add_rule(Symbol(2), vec![q1, q0], qr);
942 let e = b.build();
943
944 let mut groups: Vec<(Vec<StateId>, SymbolSet, StateId)> = Vec::new();
945 e.condensed_rules(&mut |children, sym_set, result| {
946 groups.push((children.to_vec(), sym_set.clone(), result));
947 });
948
949 let shared = groups
951 .iter()
952 .find(|(c, _, _)| c.as_slice() == [q0, q1])
953 .expect("group (q0,q1)->qr must exist");
954 assert!(shared.1.contains(Symbol(0)));
955 assert!(shared.1.contains(Symbol(1)));
956 assert_eq!(shared.2, qr);
957
958 let solo = groups
960 .iter()
961 .find(|(c, _, _)| c.as_slice() == [q1, q0])
962 .expect("group (q1,q0)->qr must exist");
963 assert!(solo.1.contains(Symbol(2)));
964 assert_eq!(solo.1.len(), 1);
965 }
966}