Skip to main content

earley_omnitool/
recognizer.rs

1use std::cmp::Ordering;
2use std::ops::Range;
3
4use bit_matrix::BitMatrix;
5use bit_matrix::row::BitVecSlice;
6use cfg::*;
7
8use events::{MedialItems, PredictedSymbols};
9use forest::{Forest, NullForest};
10use grammar::InternalGrammar;
11use item::{CompletedItem, CompletedItemLinked, Item, Origin};
12// use policy::{PerformancePolicy, NullPerformancePolicy};
13
14/// The recognizer implements the Earley algorithm. It parses the given input according
15/// to the `grammar`. The parse result is constructed inside the `forest`.
16///
17/// To save memory, it only retains those parts of the Earley table that may be useful
18/// in the future.
19pub struct Recognizer<'g, F = NullForest>
20where
21    F: Forest,
22{
23    // The forest.
24    pub forest: F,
25    // The grammar.
26    pub grammar: &'g InternalGrammar,
27    // The policy.
28    // policy: P,
29
30    // Chart's items.
31
32    // Predicted items are stored in a bit matrix. The bit matrix has a row for every Earley set.
33    //
34    // Length of `predicted` is earleme + 1, so that earleme points to the last
35    pub(super) predicted: BitMatrix,
36
37    // Medial items.
38    //
39    // N.B. This structure could be moved into its own module.
40    pub(super) medial: Vec<Item<F::NodeRef>>,
41    // Gearley's secret sauce: we have a binary heap for online sorting.
42    //
43    // Completed items are stored for the latest Earley set.
44    // They are ordered by (origin, dot), starting with highest
45    // origin and dot. The creation of a completed item can only be caused
46    // by a scan or a completion of an item that has a higher (origin, dot)
47    // pair value.
48    pub(super) complete: Vec<CompletedItemLinked<F::NodeRef>>,
49
50    // Chart's indices. They point to the beginning of each Earley set.
51    //
52    // Length of `indices` is `earleme` + 2, so that earleme points to
53    // the beginning of the range of indices for the last range.
54    pub(super) indices: Vec<usize>,
55    // Index that points to the beginning of the latest set. Equivalent to
56    // the last element of `indices`.
57    pub(super) current_medial_start: usize,
58
59    // The input location.
60    pub(super) earleme: usize,
61
62    pub(super) lookahead_hint: Option<Option<Symbol>>,
63}
64
65impl<'g, F> Recognizer<'g, F>
66where
67    F: Forest,
68{
69    /// Creates a new recognizer for a given grammar and forest. The recognizer has an initial
70    /// Earley set that predicts the grammar's start symbol.
71    pub fn new(grammar: &'g InternalGrammar, forest: F) -> Recognizer<'g, F> {
72        let mut recognizer = Recognizer {
73            forest,
74            grammar,
75            // The initial location is 0.
76            earleme: 0,
77            // The first Earley set begins at 0 and ends at 0. The second Earley set begins at 0.
78            indices: vec![0, 0],
79            current_medial_start: 0,
80            // Reserve some capacity for vectors.
81            predicted: BitMatrix::new(8, grammar.num_syms()),
82            medial: Vec::with_capacity(256),
83            complete: Vec::with_capacity(32),
84            lookahead_hint: None,
85        };
86        recognizer.predict(grammar.start_sym());
87        recognizer
88    }
89
90    /// Makes the current Earley set predict a given symbol.
91    pub fn predict(&mut self, symbol: Symbol) {
92        self.predicted[self.earleme].predict(symbol, self.grammar.predict(symbol));
93    }
94
95    pub fn begin_earleme(&mut self) {
96        // nothing to do
97    }
98
99    /// Reads a token. Creates a leaf bocage node with the given value. After reading one or more
100    /// tokens, the parse can be advanced.
101    pub fn scan(&mut self, symbol: Symbol, value: F::LeafValue) {
102        // This method is a part of the scan pass.
103        if let Some(internal) = self.grammar.to_internal(symbol) {
104            let earleme = self.earleme as Origin;
105            // Add a leaf node to the forest with the given value.
106            let node = self.forest.leaf(symbol, earleme + 1, value);
107            self.complete(earleme, internal, node);
108        }
109    }
110
111    #[inline]
112    pub fn lookahead_hint(&mut self, lookahead: Option<Symbol>) {
113        let to_internal = |sym| self.grammar.to_internal(sym).unwrap();
114        self.lookahead_hint = Some(lookahead.map(to_internal));
115    }
116
117    /// Advances the parse. Calling this method may set the finished node, which can be accessed
118    /// through the `finished_node` method.
119    pub fn end_earleme(&mut self) -> bool {
120        if self.is_exhausted() {
121            false
122        } else {
123            // Completion pass, which saves successful parses.
124            self.complete_all_sums_entirely();
125            // Do the rest.
126            self.advance_without_completion();
127            true
128        }
129    }
130
131    /// Advances the parse. Omits the completion pass, which should be done through
132    /// the `completions` method. Keep in mind that calling this method may not set
133    /// the finished node, which should be tracked externally.
134    pub fn advance_without_completion(&mut self) {
135        self.sort_medial_items();
136        self.remove_unary_medial_items();
137        self.remove_unreachable_sets();
138        self.earleme += 1;
139        // `earleme` is now at least 1.
140        // Prediction pass.
141        self.prediction_pass();
142        // Store the index.
143        self.current_medial_start = self.medial.len();
144        self.indices.push(self.current_medial_start);
145    }
146
147    /// Checks whether the recognizer is exhausted. The recognizer is exhausted when it can't accept
148    /// more input.
149    #[inline]
150    pub fn is_exhausted(&self) -> bool {
151        self.medial.len() == self.current_medial_start && self.complete.is_empty()
152    }
153
154    /// Sorts medial items with deduplication.
155    fn sort_medial_items(&mut self) {
156        let grammar = &self.grammar;
157        // Build index by postdot
158        // These medial positions themselves are sorted by postdot symbol.
159        self.medial[self.current_medial_start..].sort_unstable_by(|a, b| {
160            (grammar.get_rhs1_cmp(a.dot), a.dot, a.origin).cmp(&(
161                grammar.get_rhs1_cmp(b.dot),
162                b.dot,
163                b.origin,
164            ))
165        });
166    }
167
168    fn remove_unary_medial_items(&mut self) {
169        while let Some(&item) = self.medial.last() {
170            if self.grammar.get_rhs1(item.dot).is_some() {
171                break;
172            }
173            self.medial.pop();
174        }
175    }
176
177    fn remove_unreachable_sets(&mut self) {
178        let origin = |item: &Item<F::NodeRef>| item.origin as usize;
179        let max_origin = self.medial[self.current_medial_start..]
180            .iter()
181            .map(origin)
182            .max()
183            .unwrap_or(self.earleme);
184        let diff = self.earleme - max_origin;
185        if diff <= 1 {
186            return;
187        }
188        // | 0 | 1 | 2 | 3 |
189        //               ^ current_medial_start
190        //   _________diff = 2
191        //       ____drop = 1
192        //           ^ self.earleme = 2
193        //   ^ m = 0
194        // | 0 | 1 | 2 |
195        let drop = diff - 1;
196        let new_medial_start = self.indices[self.indices.len() - 1 - drop];
197        self.indices.truncate(self.indices.len() - drop);
198        let current_medial_length = self.medial.len() - self.current_medial_start;
199        for i in 0..current_medial_length {
200            self.medial[new_medial_start as usize + i] = self.medial[self.current_medial_start + i];
201        }
202        self.medial
203            .truncate(new_medial_start as usize + current_medial_length);
204        self.current_medial_start = new_medial_start as usize;
205        self.earleme -= drop;
206        self.predicted.truncate(self.earleme + 1);
207        for dst in self.predicted[self.earleme].iter_mut() {
208            *dst = 0;
209        }
210    }
211
212    /// Performs the prediction pass.
213    fn prediction_pass(&mut self) {
214        // Add a row to the matrix.
215        self.predicted.grow(1, false);
216        // Iterate through medial items in the current set.
217        let iter = self.medial[self.current_medial_start..].iter();
218        // For each medial item in the current set, predict its postdot symbol.
219        let row = &mut self.predicted[self.earleme];
220        for ei in iter {
221            let postdot = self.grammar.get_rhs1(ei.dot).unwrap();
222            row.predict(postdot, self.grammar.predict(postdot));
223        }
224    }
225
226    /// Complete items.
227    pub fn complete(&mut self, set_id: Origin, sym: Symbol, rhs_link: F::NodeRef) {
228        debug_assert!(sym != self.grammar.eof());
229        if self.predicted[set_id as usize].get(sym.usize()) {
230            self.complete_medial_items(set_id, sym, rhs_link);
231            self.complete_predictions(set_id, sym, rhs_link);
232        }
233    }
234
235    /// Complete medial items in a given Earley set.
236    fn complete_medial_items(&mut self, set_id: Origin, sym: Symbol, rhs_link: F::NodeRef) {
237        // Iterate through medial items to complete them.
238        let set_range = self.medial_item_set_range(set_id, sym);
239        if let Some(hint) = self.lookahead_hint {
240            for idx in set_range {
241                // New completed item.
242                // from A ::= B • C
243                // to   A ::= B   C •
244                //
245                // We might link to medial items by index, here.
246                let dot = self.medial[idx].dot;
247                if !self.grammar.can_follow(self.grammar.get_lhs(dot), hint) {
248                    continue;
249                }
250                self.heap_push_linked(CompletedItemLinked {
251                    idx: idx as u32,
252                    node: Some(rhs_link),
253                });
254            }
255        } else {
256            for idx in set_range {
257                // New completed item.
258                // from A ::= B • C
259                // to   A ::= B   C •
260                //
261                // We might link to medial items by index, here.
262                self.heap_push_linked(CompletedItemLinked {
263                    idx: idx as u32,
264                    node: Some(rhs_link),
265                });
266            }
267        }
268    }
269
270    fn medial_item_set_range(&mut self, set_id: Origin, sym: Symbol) -> Range<usize> {
271        // Huh, can we reduce complexity here?
272        let outer_start = self.indices[set_id as usize];
273        let outer_end = self.indices[set_id as usize + 1];
274        let specific_set = &self.medial[outer_start..outer_end];
275
276        let inner_start = if specific_set.len() >= 16 {
277            // When the set has 16 or more items, we use binary search to narrow down the range of
278            // items.
279            let set_idx = specific_set.binary_search_by(|ei| {
280                (self.grammar.get_rhs1(ei.dot), Ordering::Greater).cmp(&(Some(sym), Ordering::Less))
281            });
282            match set_idx {
283                Ok(idx) | Err(idx) => idx,
284            }
285        } else {
286            specific_set
287                .iter()
288                .take_while(|ei| self.grammar.get_rhs1(ei.dot).unwrap() < sym)
289                .count()
290        };
291
292        // The range contains items that have the same RHS1 symbol.
293        let inner_end = specific_set[inner_start..]
294            .iter()
295            .take_while(|ei| self.grammar.get_rhs1(ei.dot) == Some(sym))
296            .count();
297        outer_start + inner_start..outer_start + inner_start + inner_end
298    }
299
300    /// Complete predicted items that have a common postdot symbol.
301    fn complete_predictions(&mut self, set_id: Origin, sym: Symbol, rhs_link: F::NodeRef) {
302        // New item, either completed or pre-terminal. Ensure uniqueness.
303        // from A ::= • B   c
304        // to   A ::=   B • c
305        self.complete_unary_predictions(set_id, sym, rhs_link);
306        self.complete_binary_predictions(set_id, sym, rhs_link);
307    }
308
309    /// Complete an item if predicted at rhs0.
310    fn complete_unary_predictions(&mut self, set_id: Origin, sym: Symbol, rhs_link: F::NodeRef) {
311        for trans in self.grammar.unary_completions(sym) {
312            if self.predicted[set_id as usize].get(trans.symbol.usize()) {
313                // No checks for uniqueness, because `medial` will be deduplicated.
314                // from A ::= • B
315                // to   A ::=   B •
316                // ---
317                // We could push to `medial` as well and link from `complete` to `medial`.
318
319                if let Some(hint) = self.lookahead_hint {
320                    if !self
321                        .grammar
322                        .can_follow(self.grammar.get_lhs(trans.dot), hint)
323                    {
324                        continue;
325                    }
326                }
327                self.heap_push(CompletedItem {
328                    origin: set_id,
329                    dot: trans.dot,
330                    left_node: rhs_link,
331                    right_node: None,
332                });
333            }
334        }
335    }
336
337    /// Complete an item if predicted at rhs1.
338    fn complete_binary_predictions(&mut self, set_id: Origin, sym: Symbol, rhs_link: F::NodeRef) {
339        for trans in self.grammar.binary_completions(sym) {
340            if self.predicted[set_id as usize].get(trans.symbol.usize()) {
341                if let Some(hint) = self.lookahead_hint {
342                    if !self
343                        .grammar
344                        .first(self.grammar.get_rhs1(trans.dot).unwrap(), hint)
345                    {
346                        continue;
347                    }
348                }
349                // No checks for uniqueness, because `medial` will be deduplicated.
350                // from A ::= • B   C
351                // to   A ::=   B • C
352                // Where C is terminal or nonterminal.
353
354                self.medial.push(Item {
355                    origin: set_id,
356                    dot: trans.dot,
357                    node: rhs_link,
358                });
359            }
360        }
361    }
362
363    /// Resets the recognizer to its initial state by removing all contents.
364    pub fn reset(&mut self) {
365        self.earleme = 0;
366        self.predict(self.grammar.start_sym());
367        // Indices reset to [0, 0].
368        self.indices.clear();
369        self.indices.push(0);
370        self.indices.push(0);
371        // Current medial start reset to 0.
372        self.current_medial_start = 0;
373        // Remove items.
374        self.medial.clear();
375        self.complete.clear();
376    }
377
378    // Finished node access.
379
380    /// Checks whether there is a valid parse that ends at the current
381    /// position.
382    pub fn is_finished(&self) -> bool {
383        self.finished_node().is_some()
384    }
385
386    /// Retrieves the bocage node that represents the parse that has finished at the current
387    /// location.
388    ///
389    /// # Panics
390    ///
391    /// Panics when the parse has not finished at the current location.
392    pub fn finished_node(&self) -> Option<F::NodeRef> {
393        if self.grammar.has_trivial_derivation() && self.earleme == 0 {
394            Some(self.forest.nulling(self.grammar.externalized_start_sym()))
395        } else {
396            let has_dot_before_eof = |item: &&Item<_>| item.dot == self.grammar.dot_before_eof();
397            let item_node = |item: &Item<_>| item.node;
398            self.medial.last().filter(has_dot_before_eof).map(item_node)
399        }
400    }
401
402    // Event access.
403
404    /// Accesses predicted symbols.
405    pub fn predicted_symbols(&self) -> PredictedSymbols {
406        let earleme = self.earleme();
407        PredictedSymbols {
408            iter: self.predicted.iter_row(earleme),
409            idx: 0,
410        }
411    }
412
413    /// Accesses medial items.
414    pub fn medial_items(&self) -> MedialItems<F::NodeRef> {
415        let indices_len = self.indices.len();
416        // Next-to-last index, which points to the beginning of the set before the current set.
417        // The current set is empty.
418        let items_start = self.indices[indices_len - 2];
419        MedialItems {
420            iter: self.medial[items_start..].iter(),
421        }
422    }
423
424    // Accessors.
425
426    /// Returns the current location number.
427    pub fn earleme(&self) -> usize {
428        self.earleme
429    }
430
431    // Completion
432
433    /// Performs the completion pass.
434    pub fn complete_all_sums_entirely(&mut self) {
435        while let Some(mut completion) = self.next_sum() {
436            // Include all items in the completion.
437            completion.complete_entire_sum();
438        }
439        self.lookahead_hint = None;
440    }
441
442    /// Allows iteration through groups of completions that have unique symbol and origin.
443    pub fn next_sum<'r>(&'r mut self) -> Option<CompleteSum<'g, 'r, F>> {
444        if let Some(ei) = self.heap_peek() {
445            let lhs_sym = self.grammar.get_lhs(ei.dot);
446            Some(CompleteSum {
447                origin: ei.origin,
448                lhs_sym,
449                recognizer: self,
450            })
451        } else {
452            None
453        }
454    }
455}
456
457/// A group of completed items.
458pub struct CompleteSum<'g, 'r, F>
459where
460    F: Forest,
461{
462    /// The origin location of this completion.
463    origin: Origin,
464    /// The symbol of this completion.
465    lhs_sym: Symbol,
466    /// The recognizer.
467    recognizer: &'r mut Recognizer<'g, F>,
468}
469
470impl<'g, 'r, F> CompleteSum<'g, 'r, F>
471where
472    F: Forest,
473    'g: 'r,
474{
475    /// Completes all items.
476    pub fn complete_entire_sum(&mut self) {
477        self.recognizer.forest.begin_sum();
478        // For each item, include it in the completion.
479        while let Some(item) = self.next_summand() {
480            self.push_summand(item);
481        }
482        // Use all items for completion.
483        self.complete_sum();
484    }
485
486    /// Skips all items.
487    pub fn skip_entire_sum(&mut self) {
488        // For each item, include it in the completion.
489        while let Some(_) = self.next_summand() {}
490    }
491
492    /// Allows iteration through completed items.
493    #[inline]
494    pub fn next_summand(&mut self) -> Option<CompletedItem<F::NodeRef>> {
495        if let Some(completion) = self.recognizer.heap_peek() {
496            let completion_lhs_sym = self.recognizer.grammar.get_lhs(completion.dot);
497            if self.origin == completion.origin && self.lhs_sym == completion_lhs_sym {
498                self.recognizer.heap_pop();
499                Some(completion)
500            } else {
501                None
502            }
503        } else {
504            None
505        }
506    }
507
508    /// Includes an item in the completion.
509    #[inline]
510    pub fn push_summand(&mut self, completed_item: CompletedItem<F::NodeRef>) {
511        self.recognizer.forest.push_summand(completed_item);
512    }
513
514    /// Uses the completion to complete items in the recognizer.
515    #[inline]
516    pub fn complete_sum(&mut self) -> F::NodeRef {
517        let node = self.recognizer.forest.sum(self.lhs_sym, self.origin);
518        self.recognizer.complete(self.origin, self.lhs_sym, node);
519        node
520    }
521
522    /// Returns the origin location of this completion.
523    #[inline]
524    pub fn origin(&self) -> Origin {
525        self.origin
526    }
527
528    /// Returns the symbol of this completion.
529    #[inline]
530    pub fn symbol(&self) -> Symbol {
531        self.lhs_sym
532    }
533}
534
535trait Predict {
536    fn predict(&mut self, sym: Symbol, source: &BitVecSlice);
537}
538
539impl Predict for BitVecSlice {
540    fn predict(&mut self, sym: Symbol, source: &BitVecSlice) {
541        if !self[sym.usize()] {
542            // The source in the prediction matrix is the row that corresponds to the predicted
543            // symbol.
544            //
545            // The destination in `predicted` is now the `self` that corresponds to the current
546            // location.
547            for (dst, &src) in self.iter_mut().zip(source.iter()) {
548                *dst |= src;
549            }
550        }
551    }
552}