Skip to main content

lrpar/
parser.rs

1#![allow(clippy::derive_partial_eq_without_eq)]
2use std::{
3    error::Error,
4    fmt::{self, Debug, Display, Write as _},
5    hash::Hash,
6    marker::PhantomData,
7    vec,
8};
9
10// Can be used on non-wasm32 but to avoid the dependency.
11#[cfg(not(target_arch = "wasm32"))]
12use std::time::{Duration, Instant};
13#[cfg(target_arch = "wasm32")]
14use web_time::{Duration, Instant};
15
16use cactus::Cactus;
17use cfgrammar::{
18    RIdx, Span, TIdx,
19    header::{HeaderError, HeaderErrorKind, Value},
20    span::Location,
21    yacc::YaccGrammar,
22};
23use lrtable::{Action, StIdx, StateTable};
24use num_traits::{AsPrimitive, PrimInt, Unsigned};
25use proc_macro2::TokenStream;
26use quote::quote;
27#[cfg(feature = "serde")]
28use serde::{Deserialize, Serialize};
29
30use crate::{LexError, Lexeme, LexerTypes, NonStreamingLexer, cpctplus};
31
32#[cfg(test)]
33const RECOVERY_TIME_BUDGET: u64 = 60_000; // milliseconds
34#[cfg(not(test))]
35const RECOVERY_TIME_BUDGET: u64 = 500; // milliseconds
36
37#[deprecated(
38    since = "0.14.0",
39    note = "Use the version of `Node` exported from your `lrpar_mod!`"
40)]
41pub type Node<T, S> = _deprecated_moved_::Node<T, S>;
42
43#[doc(hidden)]
44pub mod _deprecated_moved_ {
45    use super::*;
46    /// A generic parse tree.
47    #[derive(Debug, Clone, PartialEq)]
48    pub enum Node<LexemeT: Lexeme<StorageT>, StorageT> {
49        /// Terminals store a single lexeme.
50        Term { lexeme: LexemeT },
51        /// Nonterminals reference a rule and have zero or more `Node`s as children.
52        Nonterm {
53            ridx: RIdx<StorageT>,
54            nodes: Vec<Node<LexemeT, StorageT>>,
55        },
56    }
57}
58
59#[allow(deprecated)]
60impl<LexemeT: Lexeme<StorageT>, StorageT: 'static + PrimInt + Unsigned> Node<LexemeT, StorageT>
61where
62    usize: AsPrimitive<StorageT>,
63{
64    /// Return a pretty-printed version of this node.
65    pub fn pp(&self, grm: &YaccGrammar<StorageT>, input: &str) -> String {
66        let mut st = vec![(0, self)]; // Stack of (indent level, node) pairs
67        let mut s = String::new();
68        while let Some((indent, e)) = st.pop() {
69            for _ in 0..indent {
70                s.push(' ');
71            }
72            match *e {
73                Node::Term { lexeme } => {
74                    let tidx = TIdx(lexeme.tok_id());
75                    let tn = grm.token_name(tidx).unwrap();
76                    let lt = &input[lexeme.span().start()..lexeme.span().end()];
77                    writeln!(s, "{} {}", tn, lt).ok();
78                }
79                Node::Nonterm { ridx, ref nodes } => {
80                    writeln!(s, "{}", grm.rule_name_str(ridx)).ok();
81                    for x in nodes.iter().rev() {
82                        st.push((indent + 1, x));
83                    }
84                }
85            }
86        }
87        s
88    }
89}
90
91type PStack<StorageT> = Vec<StIdx<StorageT>>; // Parse stack
92type TokenCostFn<'a, StorageT> = &'a (dyn Fn(TIdx<StorageT>) -> u8 + 'a);
93type ActionFn<'a, 'b, 'input, StorageT, LexerTypesT, ActionT, ParamT> = &'a dyn Fn(
94    RIdx<StorageT>,
95    &'b dyn NonStreamingLexer<'input, LexerTypesT>,
96    Span,
97    vec::Drain<AStackType<<LexerTypesT as LexerTypes>::LexemeT, ActionT>>,
98    ParamT,
99) -> ActionT;
100
101#[derive(Debug)]
102pub enum AStackType<LexemeT, ActionT> {
103    ActionType(ActionT),
104    Lexeme(LexemeT),
105}
106
107pub(super) struct Parser<
108    'a,
109    'b: 'a,
110    'input: 'b,
111    StorageT: 'static + Eq + Hash + PrimInt + Unsigned,
112    LexerTypesT: LexerTypes<StorageT = StorageT>,
113    ActionT: 'a,
114    ParamT: Clone,
115> where
116    usize: AsPrimitive<StorageT>,
117{
118    rcvry_kind: RecoveryKind,
119    pub(super) grm: &'a YaccGrammar<StorageT>,
120    pub(super) token_cost: TokenCostFn<'a, StorageT>,
121    pub(super) stable: &'a StateTable<StorageT>,
122    lexer: &'b dyn NonStreamingLexer<'input, LexerTypesT>,
123    // In the long term, we should remove the `lexemes` field entirely, as the `NonStreamingLexer` API is
124    // powerful enough to allow us to incrementally obtain lexemes and buffer them when necessary.
125    pub(super) lexemes: Vec<LexerTypesT::LexemeT>,
126    actions: &'a [ActionFn<'a, 'b, 'input, LexerTypesT::StorageT, LexerTypesT, ActionT, ParamT>],
127    param: ParamT,
128}
129
130impl<
131    'a,
132    'b: 'a,
133    'input: 'b,
134    StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
135    LexerTypesT: LexerTypes<StorageT = StorageT>,
136    Node,
137>
138    Parser<
139        'a,
140        'b,
141        'input,
142        StorageT,
143        LexerTypesT,
144        Node,
145        (
146            &dyn Fn(LexerTypesT::LexemeT) -> Node,
147            &dyn Fn(RIdx<StorageT>, Vec<Node>) -> Node,
148        ),
149    >
150where
151    usize: AsPrimitive<StorageT>,
152{
153    fn parse_map(
154        rcvry_kind: RecoveryKind,
155        grm: &YaccGrammar<StorageT>,
156        token_cost: TokenCostFn<'a, StorageT>,
157        stable: &StateTable<StorageT>,
158        lexer: &'b dyn NonStreamingLexer<'input, LexerTypesT>,
159        lexemes: Vec<LexerTypesT::LexemeT>,
160        fterm: &'a dyn Fn(LexerTypesT::LexemeT) -> Node,
161        fnonterm: &'a dyn Fn(RIdx<StorageT>, Vec<Node>) -> Node,
162    ) -> (Option<Node>, Vec<LexParseError<StorageT, LexerTypesT>>) {
163        for tidx in grm.iter_tidxs() {
164            assert!(token_cost(tidx) > 0);
165        }
166        let mut actions: Vec<
167            ActionFn<
168                'a,
169                'b,
170                'input,
171                StorageT,
172                LexerTypesT,
173                Node,
174                (
175                    &'a dyn Fn(LexerTypesT::LexemeT) -> Node,
176                    &'a dyn Fn(RIdx<StorageT>, Vec<Node>) -> Node,
177                ),
178            >,
179        > = Vec::new();
180        actions.resize(usize::from(grm.prods_len()), &action_map);
181        let psr = Parser {
182            rcvry_kind,
183            grm,
184            token_cost,
185            stable,
186            lexer,
187            lexemes,
188            actions: actions.as_slice(),
189            param: (fterm, fnonterm),
190        };
191        let mut pstack = vec![stable.start_state()];
192        let mut astack = Vec::new();
193        let mut errors = Vec::new();
194        let mut spans = Vec::new();
195        let accpt = psr.lr(0, &mut pstack, &mut astack, &mut errors, &mut spans);
196        (accpt, errors)
197    }
198}
199
200fn action_map<StorageT, LexerTypesT: LexerTypes, Node>(
201    ridx: RIdx<StorageT>,
202    _lexer: &dyn NonStreamingLexer<LexerTypesT>,
203    _span: Span,
204    astack: vec::Drain<AStackType<LexerTypesT::LexemeT, Node>>,
205    param: (
206        &dyn Fn(LexerTypesT::LexemeT) -> Node,
207        &dyn Fn(RIdx<StorageT>, Vec<Node>) -> Node,
208    ),
209) -> Node
210where
211    usize: AsPrimitive<LexerTypesT::StorageT>,
212    LexerTypesT::LexemeT: Lexeme<StorageT>,
213{
214    let (fterm, fnonterm) = param;
215    let mut nodes = Vec::with_capacity(astack.len());
216    for a in astack {
217        nodes.push(match a {
218            AStackType::ActionType(n) => n,
219            AStackType::Lexeme(lexeme) => fterm(lexeme),
220        });
221    }
222    fnonterm(ridx, nodes)
223}
224
225#[deprecated(
226    since = "0.14.0",
227    note = "Deprecated with `parse_generictree` there is no direct replacement, besides a custom action"
228)]
229#[allow(deprecated)]
230/// The action which implements [`cfgrammar::yacc::YaccOriginalActionKind::GenericParseTree`].
231/// Usually you should just use the action kind directly. But you can also call this from
232/// within a custom action to return a generic parse tree with custom behavior.
233pub fn action_generictree<StorageT, LexerTypesT: LexerTypes>(
234    ridx: RIdx<StorageT>,
235    _lexer: &dyn NonStreamingLexer<LexerTypesT>,
236    _span: Span,
237    astack: vec::Drain<AStackType<LexerTypesT::LexemeT, Node<LexerTypesT::LexemeT, StorageT>>>,
238    _param: (),
239) -> Node<LexerTypesT::LexemeT, StorageT>
240where
241    usize: AsPrimitive<LexerTypesT::StorageT>,
242    LexerTypesT::LexemeT: Lexeme<StorageT>,
243{
244    let mut nodes = Vec::with_capacity(astack.len());
245    for a in astack {
246        nodes.push(match a {
247            AStackType::ActionType(n) => n,
248            AStackType::Lexeme(lexeme) => Node::Term { lexeme },
249        });
250    }
251    Node::Nonterm { ridx, nodes }
252}
253
254impl<
255    'a,
256    'b: 'a,
257    'input: 'b,
258    StorageT: 'static + Debug + Eq + Hash + PrimInt + Unsigned,
259    LexerTypesT: LexerTypes<StorageT = StorageT>,
260    ActionT: 'a,
261    ParamT: Clone,
262> Parser<'a, 'b, 'input, StorageT, LexerTypesT, ActionT, ParamT>
263where
264    usize: AsPrimitive<StorageT>,
265{
266    fn parse_actions(
267        rcvry_kind: RecoveryKind,
268        grm: &'a YaccGrammar<StorageT>,
269        token_cost: TokenCostFn<'a, StorageT>,
270        stable: &'a StateTable<StorageT>,
271        lexer: &'b dyn NonStreamingLexer<'input, LexerTypesT>,
272        lexemes: Vec<LexerTypesT::LexemeT>,
273        actions: &'a [ActionFn<'a, 'b, 'input, StorageT, LexerTypesT, ActionT, ParamT>],
274        param: ParamT,
275    ) -> (Option<ActionT>, Vec<LexParseError<StorageT, LexerTypesT>>) {
276        for tidx in grm.iter_tidxs() {
277            assert!(token_cost(tidx) > 0);
278        }
279        let psr = Parser {
280            rcvry_kind,
281            grm,
282            token_cost,
283            stable,
284            lexer,
285            lexemes,
286            actions,
287            param,
288        };
289        let mut pstack = vec![stable.start_state()];
290        let mut astack = Vec::new();
291        let mut errors = Vec::new();
292        let mut spans = Vec::new();
293        let accpt = psr.lr(0, &mut pstack, &mut astack, &mut errors, &mut spans);
294        (accpt, errors)
295    }
296
297    /// Start parsing text at `laidx` (using the lexeme in `lexeme_prefix`, if it is not `None`,
298    /// as the first lexeme) up to (but excluding) `end_laidx` (if it's specified). Parsing
299    /// continues as long as possible (assuming that any errors encountered can be recovered from)
300    /// unless `end_laidx` is `None`, at which point this function returns as soon as it
301    /// encounters an error.
302    ///
303    /// Note that if `lexeme_prefix` is specified, `laidx` will still be incremented, and thus
304    /// `end_laidx` *must* be set to `laidx + 1` in order that the parser doesn't skip the real
305    /// lexeme at position `laidx`.
306    ///
307    /// Return `Some(value)` if the parse reached an accept state (i.e. all the input was consumed,
308    /// possibly after making repairs) or `None` (i.e. some of the input was not consumed, even
309    /// after possibly making repairs) otherwise.
310    fn lr(
311        &self,
312        mut laidx: usize,
313        pstack: &mut PStack<StorageT>,
314        astack: &mut Vec<AStackType<LexerTypesT::LexemeT, ActionT>>,
315        errors: &mut Vec<LexParseError<StorageT, LexerTypesT>>,
316        spans: &mut Vec<Span>,
317    ) -> Option<ActionT> {
318        let mut recoverer = None;
319        let mut recovery_budget = Duration::from_millis(RECOVERY_TIME_BUDGET);
320        loop {
321            debug_assert_eq!(astack.len(), spans.len());
322            let stidx = *pstack.last().unwrap();
323            let la_tidx = self.next_tidx(laidx);
324
325            match self.stable.action(stidx, la_tidx) {
326                Action::Reduce(pidx) => {
327                    let ridx = self.grm.prod_to_rule(pidx);
328                    let pop_idx = pstack.len() - self.grm.prod(pidx).len();
329
330                    pstack.drain(pop_idx..);
331                    let prior = *pstack.last().unwrap();
332                    pstack.push(self.stable.goto(prior, ridx).unwrap());
333
334                    let span = if spans.is_empty() {
335                        Span::new(0, 0)
336                    } else if pop_idx - 1 < spans.len() {
337                        Span::new(spans[pop_idx - 1].start(), spans[spans.len() - 1].end())
338                    } else {
339                        Span::new(spans[spans.len() - 1].start(), spans[spans.len() - 1].end())
340                    };
341                    spans.truncate(pop_idx - 1);
342                    spans.push(span);
343
344                    let v = AStackType::ActionType(self.actions[usize::from(pidx)](
345                        ridx,
346                        self.lexer,
347                        span,
348                        astack.drain(pop_idx - 1..),
349                        self.param.clone(),
350                    ));
351                    astack.push(v);
352                }
353                Action::Shift(state_id) => {
354                    let la_lexeme = self.next_lexeme(laidx);
355                    pstack.push(state_id);
356                    astack.push(AStackType::Lexeme(la_lexeme));
357
358                    spans.push(la_lexeme.span());
359                    laidx += 1;
360                }
361                Action::Accept => {
362                    debug_assert_eq!(la_tidx, self.grm.eof_token_idx());
363                    debug_assert_eq!(astack.len(), 1);
364                    match astack.drain(..).next().unwrap() {
365                        AStackType::ActionType(v) => return Some(v),
366                        _ => unreachable!(),
367                    }
368                }
369                Action::Error => {
370                    if recoverer.is_none() {
371                        recoverer = Some(match self.rcvry_kind {
372                            RecoveryKind::CPCTPlus => cpctplus::recoverer(self),
373                            RecoveryKind::None => {
374                                let la_lexeme = self.next_lexeme(laidx);
375                                errors.push(
376                                    ParseError {
377                                        stidx,
378                                        lexeme: la_lexeme,
379                                        repairs: vec![],
380                                    }
381                                    .into(),
382                                );
383                                return None;
384                            }
385                        });
386                    }
387
388                    let before = Instant::now();
389                    let finish_by = before + recovery_budget;
390                    let (new_laidx, repairs) = recoverer
391                        .as_ref()
392                        .unwrap()
393                        .as_ref()
394                        .recover(finish_by, self, laidx, pstack, astack, spans);
395                    let after = Instant::now();
396                    recovery_budget = recovery_budget
397                        .checked_sub(after - before)
398                        .unwrap_or_else(|| Duration::new(0, 0));
399                    let keep_going = !repairs.is_empty();
400                    let la_lexeme = self.next_lexeme(laidx);
401                    errors.push(
402                        ParseError {
403                            stidx,
404                            lexeme: la_lexeme,
405                            repairs,
406                        }
407                        .into(),
408                    );
409                    if !keep_going {
410                        return None;
411                    }
412                    laidx = new_laidx;
413                }
414            }
415        }
416    }
417
418    /// Parse from `laidx` up to (but excluding) `end_laidx` mutating `pstack` as parsing occurs.
419    /// Returns the index of the token it parsed up to (by definition <= end_laidx: can be less if
420    /// the input is < end_laidx, or if an error is encountered). Does not do any form of error
421    /// recovery.
422    pub(super) fn lr_upto(
423        &self,
424        lexeme_prefix: Option<LexerTypesT::LexemeT>,
425        mut laidx: usize,
426        end_laidx: usize,
427        pstack: &mut PStack<StorageT>,
428        astack: &mut Option<&mut Vec<AStackType<LexerTypesT::LexemeT, ActionT>>>,
429        spans: &mut Option<&mut Vec<Span>>,
430    ) -> usize {
431        assert!(lexeme_prefix.is_none() || end_laidx == laidx + 1);
432        while laidx != end_laidx && laidx <= self.lexemes.len() {
433            let stidx = *pstack.last().unwrap();
434            let la_tidx = if let Some(l) = lexeme_prefix {
435                TIdx(l.tok_id())
436            } else {
437                self.next_tidx(laidx)
438            };
439
440            match self.stable.action(stidx, la_tidx) {
441                Action::Reduce(pidx) => {
442                    let ridx = self.grm.prod_to_rule(pidx);
443                    let pop_idx = pstack.len() - self.grm.prod(pidx).len();
444                    if let Some(ref mut astack_uw) = *astack {
445                        if let Some(ref mut spans_uw) = *spans {
446                            let span = if spans_uw.is_empty() {
447                                Span::new(0, 0)
448                            } else if pop_idx - 1 < spans_uw.len() {
449                                Span::new(
450                                    spans_uw[pop_idx - 1].start(),
451                                    spans_uw[spans_uw.len() - 1].end(),
452                                )
453                            } else {
454                                Span::new(
455                                    spans_uw[spans_uw.len() - 1].start(),
456                                    spans_uw[spans_uw.len() - 1].end(),
457                                )
458                            };
459                            spans_uw.truncate(pop_idx - 1);
460                            spans_uw.push(span);
461
462                            let v = AStackType::ActionType(self.actions[usize::from(pidx)](
463                                ridx,
464                                self.lexer,
465                                span,
466                                astack_uw.drain(pop_idx - 1..),
467                                self.param.clone(),
468                            ));
469                            astack_uw.push(v);
470                        } else {
471                            unreachable!();
472                        }
473                    }
474
475                    pstack.drain(pop_idx..);
476                    let prior = *pstack.last().unwrap();
477                    pstack.push(self.stable.goto(prior, ridx).unwrap());
478                }
479                Action::Shift(state_id) => {
480                    if let Some(ref mut astack_uw) = *astack
481                        && let Some(spans_uw) = spans
482                    {
483                        let la_lexeme = if let Some(l) = lexeme_prefix {
484                            l
485                        } else {
486                            self.next_lexeme(laidx)
487                        };
488                        astack_uw.push(AStackType::Lexeme(la_lexeme));
489                        spans_uw.push(la_lexeme.span());
490                    }
491                    pstack.push(state_id);
492                    laidx += 1;
493                }
494                Action::Accept => {
495                    break;
496                }
497                Action::Error => {
498                    break;
499                }
500            }
501        }
502        laidx
503    }
504
505    /// Return a `Lexeme` for the next lemexe (if `laidx` == `self.lexemes.len()` this will be
506    /// a lexeme constructed to look as if contains the EOF token).
507    pub(super) fn next_lexeme(&self, laidx: usize) -> LexerTypesT::LexemeT {
508        let llen = self.lexemes.len();
509        debug_assert!(laidx <= llen);
510        if laidx < llen {
511            self.lexemes[laidx]
512        } else {
513            // We have to artificially construct a Lexeme for the EOF lexeme.
514            let last_la_end = if llen == 0 {
515                0
516            } else {
517                debug_assert!(laidx > 0);
518                let last_la = self.lexemes[laidx - 1];
519                last_la.span().end()
520            };
521
522            Lexeme::new_faulty(
523                StorageT::from(u32::from(self.grm.eof_token_idx())).unwrap(),
524                last_la_end,
525                0,
526            )
527        }
528    }
529
530    /// Return the `TIdx` of the next lexeme (if `laidx` == `self.lexemes.len()` this will be the
531    /// EOF `TIdx`).
532    pub(super) fn next_tidx(&self, laidx: usize) -> TIdx<StorageT> {
533        let ll = self.lexemes.len();
534        debug_assert!(laidx <= ll);
535        if laidx < ll {
536            TIdx(self.lexemes[laidx].tok_id())
537        } else {
538            self.grm.eof_token_idx()
539        }
540    }
541
542    /// Start parsing text at `laidx` (using the lexeme in `lexeme_prefix`, if it is not `None`,
543    /// as the first lexeme) up to (but excluding) `end_laidx`. If an error is encountered, parsing
544    /// immediately terminates (without recovery).
545    ///
546    /// Note that if `lexeme_prefix` is specified, `laidx` will still be incremented, and thus
547    /// `end_laidx` *must* be set to `laidx + 1` in order that the parser doesn't skip the real
548    /// lexeme at position `laidx`.
549    #[allow(deprecated)]
550    pub(super) fn lr_cactus(
551        &self,
552        lexeme_prefix: Option<LexerTypesT::LexemeT>,
553        mut laidx: usize,
554        end_laidx: usize,
555        mut pstack: Cactus<StIdx<StorageT>>,
556        tstack: &mut Option<&mut Vec<Node<LexerTypesT::LexemeT, StorageT>>>,
557    ) -> (usize, Cactus<StIdx<StorageT>>) {
558        assert!(lexeme_prefix.is_none() || end_laidx == laidx + 1);
559        while laidx != end_laidx {
560            let stidx = *pstack.val().unwrap();
561            let la_tidx = if let Some(l) = lexeme_prefix {
562                TIdx(l.tok_id())
563            } else {
564                self.next_tidx(laidx)
565            };
566
567            match self.stable.action(stidx, la_tidx) {
568                Action::Reduce(pidx) => {
569                    let ridx = self.grm.prod_to_rule(pidx);
570                    let pop_num = self.grm.prod(pidx).len();
571                    if let Some(ref mut tstack_uw) = *tstack {
572                        let nodes = tstack_uw
573                            .drain(pstack.len() - pop_num - 1..)
574                            .collect::<Vec<Node<LexerTypesT::LexemeT, StorageT>>>();
575                        tstack_uw.push(Node::Nonterm { ridx, nodes });
576                    }
577
578                    for _ in 0..pop_num {
579                        pstack = pstack.parent().unwrap();
580                    }
581                    let prior = *pstack.val().unwrap();
582                    pstack = pstack.child(self.stable.goto(prior, ridx).unwrap());
583                }
584                Action::Shift(state_id) => {
585                    if let Some(ref mut tstack_uw) = *tstack {
586                        let la_lexeme = if let Some(l) = lexeme_prefix {
587                            l
588                        } else {
589                            self.next_lexeme(laidx)
590                        };
591                        tstack_uw.push(Node::Term { lexeme: la_lexeme });
592                    }
593                    pstack = pstack.child(state_id);
594                    laidx += 1;
595                }
596                Action::Accept => {
597                    debug_assert_eq!(la_tidx, self.grm.eof_token_idx());
598                    if let Some(ref tstack_uw) = *tstack {
599                        debug_assert_eq!(tstack_uw.len(), 1);
600                    }
601                    break;
602                }
603                Action::Error => {
604                    break;
605                }
606            }
607        }
608        (laidx, pstack)
609    }
610}
611
612pub(super) trait Recoverer<
613    StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
614    LexerTypesT: LexerTypes<StorageT = StorageT>,
615    ActionT,
616    ParamT: Clone,
617> where
618    usize: AsPrimitive<StorageT>,
619{
620    fn recover(
621        &self,
622        finish_by: Instant,
623        parser: &Parser<StorageT, LexerTypesT, ActionT, ParamT>,
624        in_laidx: usize,
625        in_pstack: &mut PStack<StorageT>,
626        astack: &mut Vec<AStackType<LexerTypesT::LexemeT, ActionT>>,
627        spans: &mut Vec<Span>,
628    ) -> (usize, Vec<Vec<ParseRepair<LexerTypesT::LexemeT, StorageT>>>);
629}
630
631/// What recovery algorithm should be used when a syntax error is encountered?
632#[derive(Clone, Copy, Debug)]
633#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
634#[non_exhaustive]
635pub enum RecoveryKind {
636    /// The CPCT+ algorithm from Diekmann/Tratt "Don't Panic! Better, Fewer, Syntax Errors for LR
637    /// Parsers".
638    CPCTPlus,
639    /// Don't use error recovery: return as soon as the first syntax error is encountered.
640    None,
641}
642
643impl TryFrom<RecoveryKind> for Value<Location> {
644    type Error = cfgrammar::header::HeaderError<Location>;
645    fn try_from(rk: RecoveryKind) -> Result<Value<Location>, Self::Error> {
646        let from_loc = Location::Other("From<RecoveryKind>".to_string());
647        Ok(Value::Namespaced(format!("RecoveryKind::{rk:?}"), from_loc))
648    }
649}
650
651impl TryFrom<&Value<Location>> for RecoveryKind {
652    type Error = cfgrammar::header::HeaderError<Location>;
653    fn try_from(rk: &Value<Location>) -> Result<RecoveryKind, Self::Error> {
654        match rk {
655            Value::Namespaced(rs, loc) => match rs.as_str() {
656                "RecoveryKind::CPCTPlus" | "CPCTPlus" => Ok(RecoveryKind::CPCTPlus),
657                "RecoveryKind::None" | "None" => Ok(RecoveryKind::None),
658                _ => Err(HeaderError {
659                    kind: HeaderErrorKind::ConversionError(
660                        "RecoveryKind",
661                        "Cannot convert to RecoveryKind",
662                    ),
663                    locations: vec![loc.clone()],
664                }),
665            },
666            value => Err(HeaderError {
667                kind: HeaderErrorKind::ConversionError(
668                    "RecoveryKind",
669                    "Cannot convert to RecoveryKind",
670                ),
671                locations: vec![value.primary_location().clone()],
672            }),
673        }
674    }
675}
676
677impl quote::ToTokens for RecoveryKind {
678    fn to_tokens(&self, tokens: &mut TokenStream) {
679        tokens.extend(match *self {
680            RecoveryKind::CPCTPlus => quote!(::lrpar::RecoveryKind::CPCTPlus),
681            RecoveryKind::None => quote!(::lrpar::RecoveryKind::None),
682        })
683    }
684}
685
686/// A lexing or parsing error. Although the two are quite distinct in terms of what can be reported
687/// to users, both can (at least conceptually) occur at any point of the intertwined lexing/parsing
688/// process.
689#[derive(Debug)]
690pub enum LexParseError<
691    StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
692    LexerTypesT: LexerTypes<StorageT = StorageT>,
693> where
694    usize: AsPrimitive<StorageT>,
695{
696    LexError(LexerTypesT::LexErrorT),
697    ParseError(ParseError<LexerTypesT::LexemeT, StorageT>),
698}
699
700impl<StorageT: Debug + Hash + PrimInt + Unsigned, LexerTypesT: LexerTypes<StorageT = StorageT>>
701    LexParseError<StorageT, LexerTypesT>
702where
703    usize: AsPrimitive<StorageT>,
704{
705    /// A pretty-printer of a lexer/parser error. This isn't suitable for all purposes, but it's
706    /// often good enough. The output format is not guaranteed to be stable but is likely to be of
707    /// the form:
708    ///
709    /// ```text
710    /// Parsing error at line 3 column 8. Repair sequences found:
711    ///   1: Insert ID
712    ///   2: Delete +, Shift 3
713    /// ```
714    ///
715    /// If you are using the compile-time parse system, your `grm_y` module exposes a suitable
716    /// [`epp`](../ctbuilder/struct.CTParserBuilder.html#method.process_file) function; if you are
717    /// using the run-time system,
718    /// [`YaccGrammar`](../../cfgrammar/yacc/grammar/struct.YaccGrammar.html) exposes a suitable
719    /// [`epp`](../../cfgrammar/yacc/grammar/struct.YaccGrammar.html#method.token_epp) function,
720    /// though you will have to wrap it in a closure e.g. `&|t| grm.token_epp(t)`.
721    pub fn pp<'a>(
722        &self,
723        lexer: &dyn NonStreamingLexer<LexerTypesT>,
724        epp: &dyn Fn(TIdx<StorageT>) -> Option<&'a str>,
725    ) -> String {
726        match self {
727            LexParseError::LexError(e) => {
728                let ((line, col), _) = lexer.line_col(e.span());
729                format!("Lexing error at line {} column {}.", line, col)
730            }
731            LexParseError::ParseError(e) => {
732                let ((line, col), _) = lexer.line_col(e.lexeme().span());
733                let mut out = format!("Parsing error at line {} column {}.", line, col);
734                let repairs_len = e.repairs().len();
735                if repairs_len == 0 {
736                    out.push_str(" No repair sequences found.");
737                } else {
738                    out.push_str(" Repair sequences found:");
739                    for (i, rs) in e.repairs().iter().enumerate() {
740                        let padding = ((repairs_len as f64).log10() as usize)
741                            - (((i + 1) as f64).log10() as usize)
742                            + 1;
743                        write!(out, "\n  {}{}: ", " ".repeat(padding), i + 1).ok();
744                        let mut rs_out = Vec::new();
745
746                        // Merge together Deletes iff they are consecutive (if they are separated
747                        // by even a single character, they will not be merged).
748                        let mut i = 0;
749                        while i < rs.len() {
750                            match rs[i] {
751                                ParseRepair::Delete(l) => {
752                                    let mut j = i + 1;
753                                    let mut last_end = l.span().end();
754                                    while j < rs.len() {
755                                        if let ParseRepair::Delete(next_l) = rs[j]
756                                            && next_l.span().start() == last_end
757                                        {
758                                            last_end = next_l.span().end();
759                                            j += 1;
760                                            continue;
761                                        }
762                                        break;
763                                    }
764                                    let t = &lexer
765                                        .span_str(Span::new(l.span().start(), last_end))
766                                        .replace('\n', "\\n");
767                                    rs_out.push(format!("Delete {}", t));
768                                    i = j;
769                                }
770                                ParseRepair::Insert(tidx) => {
771                                    rs_out.push(format!("Insert {}", epp(tidx).unwrap()));
772                                    i += 1;
773                                }
774                                ParseRepair::Shift(l) => {
775                                    let t = &lexer.span_str(l.span()).replace('\n', "\\n");
776                                    rs_out.push(format!("Shift {}", t));
777                                    i += 1;
778                                }
779                            }
780                        }
781
782                        out.push_str(&rs_out.join(", "));
783                    }
784                }
785                out
786            }
787        }
788    }
789}
790
791impl<
792    StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
793    LexerTypesT: LexerTypes<StorageT = StorageT>,
794> fmt::Display for LexParseError<StorageT, LexerTypesT>
795where
796    usize: AsPrimitive<StorageT>,
797{
798    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
799        match *self {
800            LexParseError::LexError(ref e) => Display::fmt(e, f),
801            LexParseError::ParseError(ref e) => Display::fmt(e, f),
802        }
803    }
804}
805
806impl<
807    StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
808    LexerTypesT: LexerTypes<StorageT = StorageT>,
809> Error for LexParseError<StorageT, LexerTypesT>
810where
811    usize: AsPrimitive<StorageT>,
812{
813}
814
815impl<
816    StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
817    LexerTypesT: LexerTypes<StorageT = StorageT, LexErrorT = T>,
818    T: LexError,
819> From<T> for LexParseError<StorageT, LexerTypesT>
820where
821    usize: AsPrimitive<StorageT>,
822{
823    fn from(err: T) -> LexParseError<StorageT, LexerTypesT> {
824        LexParseError::LexError(err)
825    }
826}
827
828impl<
829    StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
830    LexerTypesT: LexerTypes<StorageT = StorageT>,
831> From<ParseError<LexerTypesT::LexemeT, StorageT>> for LexParseError<StorageT, LexerTypesT>
832where
833    usize: AsPrimitive<StorageT>,
834{
835    fn from(
836        err: ParseError<LexerTypesT::LexemeT, StorageT>,
837    ) -> LexParseError<StorageT, LexerTypesT> {
838        LexParseError::ParseError(err)
839    }
840}
841
842/// A run-time parser builder.
843pub struct RTParserBuilder<
844    'a,
845    StorageT: 'static + Eq + Debug + Hash + PrimInt + Unsigned,
846    LexerTypesT: LexerTypes<StorageT = StorageT>,
847> where
848    usize: AsPrimitive<StorageT>,
849{
850    grm: &'a YaccGrammar<StorageT>,
851    stable: &'a StateTable<StorageT>,
852    recoverer: RecoveryKind,
853    term_costs: &'a dyn Fn(TIdx<StorageT>) -> u8,
854    phantom: PhantomData<(StorageT, LexerTypesT)>,
855}
856
857impl<
858    'a,
859    StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
860    LexerTypesT: LexerTypes<StorageT = StorageT>,
861> RTParserBuilder<'a, StorageT, LexerTypesT>
862where
863    usize: AsPrimitive<StorageT>,
864{
865    /// Create a new run-time parser from a `YaccGrammar`, and a `StateTable`.
866    pub fn new(grm: &'a YaccGrammar<StorageT>, stable: &'a StateTable<StorageT>) -> Self {
867        RTParserBuilder {
868            grm,
869            stable,
870            recoverer: RecoveryKind::CPCTPlus,
871            term_costs: &|_| 1,
872            phantom: PhantomData,
873        }
874    }
875
876    /// Set the recoverer for this parser to `rk`.
877    pub fn recoverer(mut self, rk: RecoveryKind) -> Self {
878        self.recoverer = rk;
879        self
880    }
881
882    pub fn term_costs(mut self, f: &'a dyn Fn(TIdx<StorageT>) -> u8) -> Self {
883        self.term_costs = f;
884        self
885    }
886
887    #[deprecated(
888        since = "0.14.0",
889        note = "Use `parse_map` to return a `Node` from your `lrpar_mod!` instead"
890    )]
891    #[allow(deprecated)]
892    /// Parse input, and (if possible) return a generic parse tree. See the arguments for
893    /// [`parse_actions`](#method.parse_actions) for more details about the return value.
894    pub fn parse_generictree(
895        &self,
896        lexer: &dyn NonStreamingLexer<LexerTypesT>,
897    ) -> (
898        Option<Node<LexerTypesT::LexemeT, StorageT>>,
899        Vec<LexParseError<StorageT, LexerTypesT>>,
900    ) {
901        self.parse_map(lexer, &|lexeme| Node::Term { lexeme }, &|ridx, nodes| {
902            Node::Nonterm { ridx, nodes }
903        })
904    }
905
906    /// Parse input, and (if possible) return a generic parse tree. See the arguments for
907    /// [`parse_actions`](#method.parse_actions) for more details about the return value.
908    pub fn parse_map<Node>(
909        &self,
910        lexer: &dyn NonStreamingLexer<LexerTypesT>,
911        fterm: &dyn Fn(LexerTypesT::LexemeT) -> Node,
912        fnonterm: &dyn Fn(RIdx<StorageT>, Vec<Node>) -> Node,
913    ) -> (Option<Node>, Vec<LexParseError<StorageT, LexerTypesT>>) {
914        let lexemes = match lexer.iter().collect() {
915            Ok(lexemes) => lexemes,
916            Err(e) => return (None, vec![e.into()]),
917        };
918        Parser::<
919            StorageT,
920            LexerTypesT,
921            Node,
922            (
923                &dyn Fn(LexerTypesT::LexemeT) -> Node,
924                &dyn Fn(RIdx<StorageT>, Vec<Node>) -> Node,
925            ),
926        >::parse_map(
927            self.recoverer,
928            self.grm,
929            self.term_costs,
930            self.stable,
931            lexer,
932            lexemes,
933            fterm,
934            fnonterm,
935        )
936    }
937
938    #[deprecated(since = "0.14.0", note = "Use `parse_map` instead")]
939    /// Parse input, returning any errors found. See the arguments for
940    /// [`parse_actions`](#method.parse_actions) for more details about the return value.
941    pub fn parse_noaction(
942        &self,
943        lexer: &dyn NonStreamingLexer<LexerTypesT>,
944    ) -> Vec<LexParseError<StorageT, LexerTypesT>> {
945        self.parse_map(lexer, &|_| (), &|_, _| ()).1
946    }
947
948    /// Parse input, execute actions, and return the associated value (if possible) and/or any
949    /// lexing/parsing errors encountered. Note that the two parts of the (value, errors) return
950    /// pair are entirely independent: one can encounter errors without a value being produced
951    /// (`None, [...]`), errors and a value (`Some(...), [...]`), as well as a value and no errors
952    /// (`Some(...), []`). Errors are sorted by the position they were found in the input and can
953    /// be a mix of lexing and parsing errors.
954    pub fn parse_actions<'b: 'a, 'input: 'b, ActionT: 'a, ParamT: Clone>(
955        &self,
956        lexer: &'b dyn NonStreamingLexer<'input, LexerTypesT>,
957        actions: &'a [ActionFn<'a, 'b, 'input, StorageT, LexerTypesT, ActionT, ParamT>],
958        param: ParamT,
959    ) -> (Option<ActionT>, Vec<LexParseError<StorageT, LexerTypesT>>) {
960        let lexemes = match lexer.iter().collect() {
961            Ok(lexemes) => lexemes,
962            Err(e) => return (None, vec![e.into()]),
963        };
964        Parser::parse_actions(
965            self.recoverer,
966            self.grm,
967            self.term_costs,
968            self.stable,
969            lexer,
970            lexemes,
971            actions,
972            param,
973        )
974    }
975
976    pub fn grammar(&self) -> &YaccGrammar<StorageT> {
977        self.grm
978    }
979}
980
981/// After a parse error is encountered, the parser attempts to find a way of recovering. Each entry
982/// in the sequence of repairs is represented by a `ParseRepair`.
983#[derive(Clone, Debug, Eq, Hash, PartialEq)]
984pub enum ParseRepair<LexemeT: Lexeme<StorageT>, StorageT: Hash> {
985    /// Insert a `Symbol::Token`.
986    Insert(TIdx<StorageT>),
987    /// Delete a symbol.
988    Delete(LexemeT),
989    /// Shift a symbol.
990    Shift(LexemeT),
991}
992
993/// Records a single parse error.
994#[derive(Clone, Debug, PartialEq)]
995pub struct ParseError<LexemeT: Lexeme<StorageT>, StorageT: Hash> {
996    stidx: StIdx<StorageT>,
997    lexeme: LexemeT,
998    repairs: Vec<Vec<ParseRepair<LexemeT, StorageT>>>,
999}
1000
1001impl<LexemeT: Lexeme<StorageT>, StorageT: Debug + Hash> Display for ParseError<LexemeT, StorageT> {
1002    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1003        write!(f, "Parse error at lexeme {:?}", self.lexeme)
1004    }
1005}
1006
1007impl<LexemeT: Lexeme<StorageT>, StorageT: Debug + Hash> Error for ParseError<LexemeT, StorageT> {}
1008
1009impl<LexemeT: Lexeme<StorageT>, StorageT: Hash + PrimInt + Unsigned> ParseError<LexemeT, StorageT> {
1010    /// Return the state table index where this error was detected.
1011    pub fn stidx(&self) -> StIdx<StorageT> {
1012        self.stidx
1013    }
1014
1015    /// Return the lexeme where this error was detected.
1016    pub fn lexeme(&self) -> &LexemeT {
1017        &self.lexeme
1018    }
1019
1020    /// Return the repairs found that would fix this error. Note that there are infinite number of
1021    /// possible repairs for any error, so this is by definition a (finite) subset.
1022    pub fn repairs(&self) -> &Vec<Vec<ParseRepair<LexemeT, StorageT>>> {
1023        &self.repairs
1024    }
1025}
1026
1027#[cfg(test)]
1028pub(crate) mod test {
1029    use std::collections::HashMap;
1030
1031    use cfgrammar::{
1032        Span,
1033        yacc::{YaccGrammar, YaccKind, YaccOriginalActionKind},
1034    };
1035    use lrtable::{Minimiser, from_yacc};
1036    use num_traits::ToPrimitive;
1037    use regex::Regex;
1038
1039    use super::*;
1040    use crate::{
1041        Lexeme, Lexer,
1042        test_utils::{TestLexError, TestLexeme, TestLexerTypes},
1043    };
1044
1045    #[allow(deprecated)]
1046    pub(crate) fn do_parse<'input>(
1047        rcvry_kind: RecoveryKind,
1048        lexs: &str,
1049        grms: &str,
1050        input: &'input str,
1051    ) -> (
1052        YaccGrammar<u16>,
1053        SmallLexer<'input>,
1054        Result<
1055            Node<TestLexeme, u16>,
1056            (
1057                Option<Node<TestLexeme, u16>>,
1058                Vec<LexParseError<u16, TestLexerTypes>>,
1059            ),
1060        >,
1061    ) {
1062        do_parse_with_costs(rcvry_kind, lexs, grms, input, &HashMap::new())
1063    }
1064
1065    #[allow(deprecated)]
1066    fn do_parse_with_costs<'input>(
1067        rcvry_kind: RecoveryKind,
1068        lexs: &str,
1069        grms: &str,
1070        input: &'input str,
1071        costs: &HashMap<&str, u8>,
1072    ) -> (
1073        YaccGrammar<u16>,
1074        SmallLexer<'input>,
1075        Result<
1076            Node<TestLexeme, u16>,
1077            (
1078                Option<Node<TestLexeme, u16>>,
1079                Vec<LexParseError<u16, TestLexerTypes>>,
1080            ),
1081        >,
1082    ) {
1083        let grm = YaccGrammar::<u16>::new_with_storaget(
1084            YaccKind::Original(YaccOriginalActionKind::GenericParseTree),
1085            grms,
1086        )
1087        .unwrap();
1088        let (_, stable) = from_yacc(&grm, Minimiser::Pager).unwrap();
1089        let rule_ids = grm
1090            .tokens_map()
1091            .iter()
1092            .map(|(&n, &i)| (n.to_owned(), u32::from(i).to_u16().unwrap()))
1093            .collect();
1094        let lexer_rules = small_lexer(lexs, rule_ids);
1095        let lexemes = small_lex(lexer_rules, input);
1096        let lexer = SmallLexer { lexemes, s: input };
1097        let costs_tidx = costs
1098            .iter()
1099            .map(|(k, v)| (grm.token_idx(k).unwrap(), v))
1100            .collect::<HashMap<_, _>>();
1101        let (r, errs) = RTParserBuilder::new(&grm, &stable)
1102            .recoverer(rcvry_kind)
1103            .term_costs(&|tidx| **costs_tidx.get(&tidx).unwrap_or(&&1))
1104            .parse_generictree(&lexer);
1105        if let Some(node) = r {
1106            if errs.is_empty() {
1107                (grm, lexer, Ok(node))
1108            } else {
1109                (grm, lexer, Err((Some(node), errs)))
1110            }
1111        } else {
1112            (grm, lexer, Err((None, errs)))
1113        }
1114    }
1115
1116    fn check_parse_output(lexs: &str, grms: &str, input: &str, expected: &str) {
1117        let (grm, _, pt) = do_parse(RecoveryKind::CPCTPlus, lexs, grms, input);
1118        assert_eq!(expected, pt.unwrap().pp(&grm, input));
1119    }
1120
1121    // SmallLexer is our highly simplified version of lrlex (allowing us to avoid having to have
1122    // lrlex as a dependency of lrpar). The format is the same as lrlex *except*:
1123    //   * The initial "%%" isn't needed, and only "'" is valid as a rule name delimiter.
1124    //   * "Unnamed" rules aren't allowed (e.g. you can't have a rule which discards whitespaces).
1125    pub(crate) struct SmallLexer<'input> {
1126        lexemes: Vec<TestLexeme>,
1127        s: &'input str,
1128    }
1129
1130    impl Lexer<TestLexerTypes> for SmallLexer<'_> {
1131        fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = Result<TestLexeme, TestLexError>> + 'a> {
1132            Box::new(self.lexemes.iter().map(|x| Ok(*x)))
1133        }
1134    }
1135
1136    impl<'input> NonStreamingLexer<'input, TestLexerTypes> for SmallLexer<'input> {
1137        fn span_str(&self, span: Span) -> &'input str {
1138            &self.s[span.start()..span.end()]
1139        }
1140
1141        fn span_lines_str(&self, _: Span) -> &'input str {
1142            unreachable!();
1143        }
1144
1145        fn line_col(&self, _: Span) -> ((usize, usize), (usize, usize)) {
1146            unreachable!();
1147        }
1148    }
1149
1150    fn small_lexer(lexs: &str, ids_map: HashMap<String, u16>) -> Vec<(u16, Regex)> {
1151        let mut rules = Vec::new();
1152        for l in lexs.split('\n').map(|x| x.trim()).filter(|x| !x.is_empty()) {
1153            assert!(l.rfind('\'') == Some(l.len() - 1));
1154            let i = l[..l.len() - 1].rfind('\'').unwrap();
1155            let name = &l[i + 1..l.len() - 1];
1156            let re = &l[..i - 1].trim();
1157            rules.push((
1158                ids_map[name],
1159                Regex::new(&format!("\\A(?:{})", re)).unwrap(),
1160            ));
1161        }
1162        rules
1163    }
1164
1165    fn small_lex(rules: Vec<(u16, Regex)>, input: &str) -> Vec<TestLexeme> {
1166        let mut lexemes = vec![];
1167        let mut i = 0;
1168        while i < input.len() {
1169            let mut longest = 0; // Length of the longest match
1170            let mut longest_tok_id = 0; // This is only valid iff longest != 0
1171            for (tok_id, r) in rules.iter() {
1172                if let Some(m) = r.find(&input[i..]) {
1173                    let len = m.end();
1174                    if len > longest {
1175                        longest = len;
1176                        longest_tok_id = *tok_id;
1177                    }
1178                }
1179            }
1180            assert!(longest > 0);
1181            lexemes.push(Lexeme::new(longest_tok_id, i, longest));
1182            i += longest;
1183        }
1184        lexemes
1185    }
1186
1187    #[test]
1188    fn simple_parse() {
1189        // From p4 of https://www.cs.umd.edu/class/spring2014/cmsc430/lectures/lec07.pdf
1190        check_parse_output(
1191            "[a-zA-Z_] 'ID'
1192             \\+ '+'",
1193            "
1194%start E
1195%%
1196E: T '+' E
1197 | T;
1198T: 'ID';
1199",
1200            "a+b",
1201            "E
1202 T
1203  ID a
1204 + +
1205 E
1206  T
1207   ID b
1208",
1209        );
1210    }
1211
1212    #[test]
1213    fn parse_empty_rules() {
1214        let lexs = "[a-zA-Z_] 'ID'";
1215        let grms = "%start S
1216%%
1217S: L;
1218L: 'ID'
1219 | ;
1220";
1221        check_parse_output(
1222            lexs, grms, "", "S
1223 L
1224",
1225        );
1226
1227        check_parse_output(
1228            lexs,
1229            grms,
1230            "x",
1231            "S
1232 L
1233  ID x
1234",
1235        );
1236    }
1237
1238    #[test]
1239    fn recursive_parse() {
1240        let lexs = "\\+ '+'
1241                    \\* '*'
1242                    [0-9]+ 'INT'";
1243        let grms = "%start Expr
1244%%
1245Expr : Expr '+' Term | Term;
1246Term : Term '*' Factor | Factor;
1247Factor : 'INT';";
1248
1249        check_parse_output(
1250            lexs,
1251            grms,
1252            "2+3*4",
1253            "Expr
1254 Expr
1255  Term
1256   Factor
1257    INT 2
1258 + +
1259 Term
1260  Term
1261   Factor
1262    INT 3
1263  * *
1264  Factor
1265   INT 4
1266",
1267        );
1268        check_parse_output(
1269            lexs,
1270            grms,
1271            "2*3+4",
1272            "Expr
1273 Expr
1274  Term
1275   Term
1276    Factor
1277     INT 2
1278   * *
1279   Factor
1280    INT 3
1281 + +
1282 Term
1283  Factor
1284   INT 4
1285",
1286        );
1287    }
1288
1289    #[test]
1290    fn parse_error() {
1291        let lexs = "\\( '('
1292                    \\) ')'
1293                    [a-zA-Z_][a-zA-Z_0-9]* 'ID'";
1294        let grms = "%start Call
1295%%
1296Call: 'ID' '(' ')';";
1297
1298        check_parse_output(
1299            lexs,
1300            grms,
1301            "f()",
1302            "Call
1303 ID f
1304 ( (
1305 ) )
1306",
1307        );
1308
1309        let (grm, _, pr) = do_parse(RecoveryKind::CPCTPlus, lexs, grms, "f(");
1310        let (_, errs) = pr.unwrap_err();
1311        assert_eq!(errs.len(), 1);
1312        let err_tok_id = usize::from(grm.eof_token_idx()).to_u16().unwrap();
1313        match &errs[0] {
1314            LexParseError::ParseError(e) => {
1315                assert_eq!(e.lexeme(), &Lexeme::new_faulty(err_tok_id, 2, 0));
1316                assert!(e.lexeme().faulty());
1317            }
1318            _ => unreachable!(),
1319        }
1320
1321        let (grm, _, pr) = do_parse(RecoveryKind::CPCTPlus, lexs, grms, "f(f(");
1322        let (_, errs) = pr.unwrap_err();
1323        assert_eq!(errs.len(), 1);
1324        let err_tok_id = usize::from(grm.token_idx("ID").unwrap()).to_u16().unwrap();
1325        match &errs[0] {
1326            LexParseError::ParseError(e) => {
1327                assert_eq!(e.lexeme(), &Lexeme::new(err_tok_id, 2, 1));
1328                assert!(!e.lexeme().faulty());
1329            }
1330            _ => unreachable!(),
1331        }
1332    }
1333
1334    #[test]
1335    fn test_parse_map() {
1336        #[derive(PartialEq, Debug)]
1337        enum TestParseMap<'a> {
1338            Term(&'a str, &'a str),
1339            NonTerm(&'a str, Vec<TestParseMap<'a>>),
1340        }
1341        let lex_src = r#"[0-9]+ 'INT'
1342\+ '+'
1343\* '*'
1344"#;
1345        let grammar_src = "
1346%grmtools{yacckind: Original(NoAction)}
1347%start Expr
1348%%
1349Expr : Expr '+' Term | Term;
1350Term : Term '*' Factor | Factor;
1351Factor : 'INT';";
1352        let input_src = "2*3+4";
1353        let grm = str::parse::<YaccGrammar<u16>>(grammar_src).unwrap();
1354        let (_, stable) = lrtable::from_yacc(&grm, lrtable::Minimiser::Pager).unwrap();
1355        let rt_parser = RTParserBuilder::new(&grm, &stable);
1356        let rule_ids = grm
1357            .tokens_map()
1358            .iter()
1359            .map(|(&n, &i)| (n.to_owned(), u32::from(i).to_u16().unwrap()))
1360            .collect();
1361        let lexer_rules = small_lexer(lex_src, rule_ids);
1362        let lexemes = small_lex(lexer_rules, input_src);
1363        let lexer = SmallLexer {
1364            lexemes,
1365            s: input_src,
1366        };
1367
1368        let (parse_map, errs) = rt_parser.parse_map(
1369            &lexer,
1370            &|lexeme: TestLexeme| {
1371                let tidx = TIdx(lexeme.tok_id());
1372                let tn = &grm.token_name(tidx).unwrap();
1373                let lt = &input_src[lexeme.span().start()..lexeme.span().end()];
1374                TestParseMap::Term(tn, lt)
1375            },
1376            &|ridx, nodes| {
1377                let rule_name = &grm.rule_name_str(ridx);
1378                TestParseMap::NonTerm(rule_name, nodes)
1379            },
1380        );
1381        assert!(parse_map.is_some() && errs.is_empty());
1382        // Sanity check the `parse_generictree` output.
1383        check_parse_output(
1384            lex_src,
1385            grammar_src,
1386            input_src,
1387            "Expr
1388 Expr
1389  Term
1390   Term
1391    Factor
1392     INT 2
1393   * *
1394   Factor
1395    INT 3
1396 + +
1397 Term
1398  Factor
1399   INT 4
1400",
1401        );
1402
1403        let expected_parse_map = {
1404            use TestParseMap::*;
1405            NonTerm(
1406                "Expr",
1407                vec![
1408                    NonTerm(
1409                        "Expr",
1410                        vec![NonTerm(
1411                            "Term",
1412                            vec![
1413                                NonTerm("Term", vec![NonTerm("Factor", vec![Term("INT", "2")])]),
1414                                Term("*", "*"),
1415                                NonTerm("Factor", vec![Term("INT", "3")]),
1416                            ],
1417                        )],
1418                    ),
1419                    Term("+", "+"),
1420                    NonTerm("Term", vec![NonTerm("Factor", vec![Term("INT", "4")])]),
1421                ],
1422            )
1423        };
1424        assert_eq!(parse_map, Some(expected_parse_map));
1425    }
1426}