Skip to main content

rusty_alto/
run.rs

1//! Deterministic and nondeterministic bottom-up automaton runners.
2
3use crate::{BottomUpTa, DetBottomUpTa, StateId, Symbol};
4use fixedbitset::FixedBitSet;
5use packed_term_arena::tree::{Tree, TreeArena};
6use smallvec::SmallVec;
7
8/// Side table produced by [`run_det`].
9///
10/// The table stores one state per arena node. Rejected nodes receive
11/// [`StateId::STUCK`].
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct DetRun {
14    /// State assigned to each node, indexed by [`Tree::index`].
15    pub states: Vec<StateId>,
16    /// State assigned to the root, or [`StateId::STUCK`] if the tree rejected.
17    pub root_state: StateId,
18}
19
20/// Small sorted set of states used by nondeterministic runs.
21///
22/// The set keeps states deduplicated. It stores a few states inline, which is
23/// efficient for the common case where each node has only a small number of
24/// possible states.
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct StateSet<S>(SmallVec<[S; 4]>);
27
28impl<S> Default for StateSet<S> {
29    fn default() -> Self {
30        Self(SmallVec::new())
31    }
32}
33
34impl<S: Clone + Ord> StateSet<S> {
35    /// Create an empty state set.
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /// Insert a state, preserving sorted and deduplicated order.
41    pub fn insert(&mut self, s: S) {
42        match self.0.binary_search(&s) {
43            Ok(_) => {}
44            Err(idx) => self.0.insert(idx, s),
45        }
46    }
47
48    /// Iterate over states in sorted order.
49    pub fn iter(&self) -> impl Iterator<Item = &S> {
50        self.0.iter()
51    }
52
53    /// Return the states as a slice.
54    pub fn as_slice(&self) -> &[S] {
55        &self.0
56    }
57
58    /// Return whether this set contains no states.
59    pub fn is_empty(&self) -> bool {
60        self.0.is_empty()
61    }
62
63    /// Return the number of states in the set.
64    pub fn len(&self) -> usize {
65        self.0.len()
66    }
67}
68
69/// Side table produced by [`run_nondet`].
70///
71/// Each node can have zero or more possible states. An empty set means the
72/// subtree rooted at that node is rejected.
73#[derive(Clone, Debug, PartialEq, Eq)]
74pub struct NonDetRun<S> {
75    /// State set assigned to each node, indexed by [`Tree::index`].
76    pub states: Vec<StateSet<S>>,
77    /// State set assigned to the root.
78    pub root_states: StateSet<S>,
79}
80
81/// Run a deterministic automaton over an arena tree.
82///
83/// This is the fastest runner. It requires automata whose states are
84/// [`StateId`] and that implement [`DetBottomUpTa`]. For implicit automata with
85/// richer state values, wrap the automaton in [`crate::Memo`] first.
86///
87/// If a child is stuck, the parent is stuck without querying the automaton.
88pub fn run_det<A>(a: &A, arena: &TreeArena<Symbol>, root: Tree) -> DetRun
89where
90    A: DetBottomUpTa<State = StateId>,
91{
92    let mut states = vec![StateId::STUCK; arena.len()];
93    let mut visited = FixedBitSet::with_capacity(arena.len());
94    let mut buf: SmallVec<[StateId; 4]> = SmallVec::new();
95
96    for node in arena.post_order(root) {
97        if visited.contains(node.index()) {
98            continue;
99        }
100        visited.set(node.index(), true);
101
102        buf.clear();
103        let mut any_stuck = false;
104        for &child in arena.get_children(node) {
105            let cs = states[child.index()];
106            if cs.is_stuck() {
107                any_stuck = true;
108                break;
109            }
110            buf.push(cs);
111        }
112        if any_stuck {
113            continue;
114        }
115        states[node.index()] = a
116            .step_det(*arena.get_label(node), &buf)
117            .unwrap_or(StateId::STUCK);
118    }
119
120    DetRun {
121        root_state: states[root.index()],
122        states,
123    }
124}
125
126/// Run a nondeterministic automaton over an arena tree.
127///
128/// This runner stores a set of possible states at every node. It is more
129/// general than [`run_det`] but does more allocation and tuple enumeration, so
130/// deterministic automata should prefer [`run_det`] when possible.
131pub fn run_nondet<A>(a: &A, arena: &TreeArena<Symbol>, root: Tree) -> NonDetRun<A::State>
132where
133    A: BottomUpTa,
134    A::State: Ord,
135{
136    let mut states = vec![StateSet::new(); arena.len()];
137    let mut visited = FixedBitSet::with_capacity(arena.len());
138
139    for node in arena.post_order(root) {
140        if visited.contains(node.index()) {
141            continue;
142        }
143        visited.set(node.index(), true);
144
145        let local = {
146            let child_ids: SmallVec<[Tree; 4]> = arena.get_children(node).iter().copied().collect();
147            let mut pools: SmallVec<[&[A::State]; 4]> = SmallVec::new();
148            let mut any_empty = false;
149            for child in child_ids {
150                let set = &states[child.index()];
151                if set.is_empty() {
152                    any_empty = true;
153                    break;
154                }
155                pools.push(set.as_slice());
156            }
157            if any_empty {
158                None
159            } else {
160                let mut local = StateSet::new();
161                cartesian_product(&pools, |tuple| {
162                    a.step(*arena.get_label(node), tuple, &mut |q| local.insert(q));
163                });
164                Some(local)
165            }
166        };
167
168        if let Some(local) = local {
169            states[node.index()] = local;
170        }
171    }
172
173    NonDetRun {
174        root_states: states[root.index()].clone(),
175        states,
176    }
177}
178
179pub(crate) fn cartesian_product<T: Clone>(pools: &[&[T]], mut f: impl FnMut(&[T])) {
180    if pools.iter().any(|pool| pool.is_empty()) {
181        return;
182    }
183    if pools.is_empty() {
184        f(&[]);
185        return;
186    }
187
188    let mut indices = vec![0; pools.len()];
189    let mut tuple: Vec<T> = pools.iter().map(|pool| pool[0].clone()).collect();
190
191    loop {
192        f(&tuple);
193
194        let mut pos = pools.len();
195        loop {
196            if pos == 0 {
197                return;
198            }
199            pos -= 1;
200            indices[pos] += 1;
201            if indices[pos] < pools[pos].len() {
202                tuple[pos] = pools[pos][indices[pos]].clone();
203                for reset in pos + 1..pools.len() {
204                    indices[reset] = 0;
205                    tuple[reset] = pools[reset][0].clone();
206                }
207                break;
208            }
209        }
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216    use crate::{ExplicitBuilder, Symbol};
217
218    #[test]
219    fn cartesian_empty_pools_is_unit() {
220        let mut tuples = Vec::<Vec<u8>>::new();
221        cartesian_product::<u8>(&[], |tuple| tuples.push(tuple.to_vec()));
222        assert_eq!(tuples, vec![vec![]]);
223    }
224
225    #[test]
226    fn cartesian_empty_member_is_empty() {
227        let a = [1, 2];
228        let b: [i32; 0] = [];
229        let mut count = 0;
230        cartesian_product(&[&a[..], &b[..]], |_| count += 1);
231        assert_eq!(count, 0);
232    }
233
234    #[test]
235    fn deterministic_run_accepts_tree() {
236        let a = Symbol(0);
237        let f = Symbol(1);
238        let mut builder = ExplicitBuilder::new();
239        let leaf = builder.new_state();
240        let root_state = builder.new_state();
241        builder.add_rule(a, vec![], leaf);
242        builder.add_rule(f, vec![leaf, leaf], root_state);
243        builder.add_accepting(root_state);
244        let automaton = builder.build();
245
246        let mut arena = TreeArena::new();
247        let left = arena.add_node(a, vec![]);
248        let right = arena.add_node(a, vec![]);
249        let root = arena.add_node(f, vec![left, right]);
250
251        let run = run_det(&automaton, &arena, root);
252        assert_eq!(run.root_state, root_state);
253        assert!(automaton.is_accepting(&run.root_state));
254    }
255
256    #[test]
257    fn nondeterministic_run_collects_states() {
258        let a = Symbol(0);
259        let mut builder = ExplicitBuilder::new();
260        let q0 = builder.new_state();
261        let q1 = builder.new_state();
262        builder.add_rule(a, vec![], q0);
263        builder.add_rule(a, vec![], q1);
264        let automaton = builder.build();
265        let mut arena = TreeArena::new();
266        let root = arena.add_node(a, vec![]);
267        let run = run_nondet(&automaton, &arena, root);
268        assert_eq!(run.root_states.len(), 2);
269    }
270
271    #[test]
272    fn deterministic_run_handles_shared_stuck_nodes() {
273        let leaf_symbol = Symbol(0);
274        let parent_symbol = Symbol(1);
275        let builder = ExplicitBuilder::new();
276        let automaton = builder.build();
277
278        let mut arena = TreeArena::new();
279        let shared = arena.add_node(leaf_symbol, vec![]);
280        let root = arena.add_node(parent_symbol, vec![shared, shared]);
281
282        let run = run_det(&automaton, &arena, root);
283        assert!(run.states[shared.index()].is_stuck());
284        assert!(run.root_state.is_stuck());
285    }
286}