lrpar 0.1.0

Yacc-compatible parser generator
Documentation
// Copyright (c) 2018 King's College London
// created by the Software Development Team <http://soft-dev.org/>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0>, or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, or the UPL-1.0 license <http://opensource.org/licenses/UPL>
// at your option. This file may not be copied, modified, or distributed except according to those
// terms.

use std::{fmt::Debug, hash::Hash, time::Instant};

use lrtable::{Action, StIdx};
use num_traits::{AsPrimitive, PrimInt, Unsigned};

use parser::{AStackType, ParseRepair, Parser, Recoverer};

struct Panic;

pub(crate) fn recoverer<
    'a,
    StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
    ActionT: 'static
>(
    _: &'a Parser<StorageT, ActionT>
) -> Box<Recoverer<StorageT, ActionT> + 'a>
where
    usize: AsPrimitive<StorageT>,
    u32: AsPrimitive<StorageT>
{
    Box::new(Panic)
}

impl<StorageT: 'static + Debug + Hash + PrimInt + Unsigned, ActionT: 'static>
    Recoverer<StorageT, ActionT> for Panic
where
    usize: AsPrimitive<StorageT>,
    u32: AsPrimitive<StorageT>
{
    fn recover(
        &self,
        finish_by: Instant,
        parser: &Parser<StorageT, ActionT>,
        in_laidx: usize,
        in_pstack: &mut Vec<StIdx>,
        _: &mut Vec<AStackType<ActionT, StorageT>>
    ) -> (usize, Vec<Vec<ParseRepair<StorageT>>>) {
        // This recoverer is based on that in Compiler Design in C by Allen I. Holub p.348.
        //
        // It doesn't really fit into our recoverer mould very well: it can't always flesh out a
        // valid parse tree, and it often recovers in a way that the user can't emulate (i.e. there
        // is no way the user can adjust their input to get the parser into the same state as the
        // recovery algorithm manages).
        let iter_pstack = in_pstack.clone();
        for laidx in in_laidx..parser.lexemes.len() + 1 {
            if Instant::now() >= finish_by {
                break;
            }
            for (st_i, st) in iter_pstack.iter().enumerate().rev() {
                match parser.stable.action(*st, parser.next_tidx(laidx)) {
                    Action::Error => (),
                    _ => {
                        let mut rprs = Vec::with_capacity(laidx - in_laidx);
                        // It's often possible that we found a state that will accept the lexeme at
                        // in_laidx, at which point there are no repairs the user can make which will
                        // emulate this panic mode (i.e. the list of actions they are advised to take
                        // will be empty). There isn't much we can do about this.
                        for j in in_laidx..laidx {
                            rprs.push(ParseRepair::Delete(parser.next_lexeme(j)));
                        }
                        in_pstack.drain(st_i + 1..);
                        return (laidx, vec![rprs]);
                    }
                }
            }
        }
        (in_laidx, vec![])
    }
}