prism_parser/core/
recovery.rs

1use crate::core::context::ParserContext;
2use crate::core::parser::Parser;
3use crate::core::pos::Pos;
4use crate::core::presult::PResult;
5use crate::core::presult::PResult::{PErr, POk};
6use crate::core::state::ParserState;
7use crate::error::error_printer::ErrorLabel;
8use crate::error::ParseError;
9use crate::grammar::action_result::ActionResult;
10
11const MAX_RECOVERIES: usize = 2;
12
13pub fn parse_with_recovery<'a, 'arn: 'a, 'grm: 'arn, O, E: ParseError<L = ErrorLabel<'grm>>>(
14    sub: &'a impl Parser<'arn, 'grm, O, E>,
15    pos: Pos,
16    state: &mut ParserState<'arn, 'grm, E>,
17    context: ParserContext,
18) -> Result<O, Vec<E>> {
19    let mut result_errors: Vec<E> = Vec::new();
20    let mut err_state: Option<(Pos, Pos)> = None;
21
22    loop {
23        match sub.parse(pos, state, context) {
24            POk(o, _, _, _) => {
25                return if result_errors.is_empty() {
26                    Ok(o)
27                } else {
28                    // Update last error
29                    if let Some(last) = result_errors.last_mut() {
30                        last.set_end(err_state.unwrap().1);
31                    }
32                    Err(result_errors)
33                };
34            }
35            PErr(e, p) => {
36                //If this is the first time we encounter *this* error, log it and retry
37                if err_state.is_none() || err_state.unwrap().1 < p {
38                    // Update last error
39                    if let Some(last) = result_errors.last_mut() {
40                        last.set_end(err_state.unwrap().1);
41                    }
42
43                    // Check if we can accept more errors
44                    if result_errors.len() >= MAX_RECOVERIES {
45                        return Err(result_errors);
46                    }
47
48                    // Add new error
49                    result_errors.push(e);
50                    err_state = Some((p, p));
51                } else if let Some((_err_state_start, err_state_end)) = &mut err_state {
52                    //If the error now spans rest of file, we could not recover
53                    if *err_state_end == Pos::end(state.input) {
54                        result_errors
55                            .last_mut()
56                            .unwrap()
57                            .set_end(Pos::end(state.input));
58                        return Err(result_errors);
59                    }
60
61                    //Increase offset by one char and repeat
62                    *err_state_end = err_state_end.next(state.input).0;
63                    assert!(*err_state_end <= Pos::end(state.input));
64                } else {
65                    unreachable!()
66                }
67                state
68                    .recovery_points
69                    .insert(err_state.unwrap().0, err_state.unwrap().1);
70                state
71                    .recovery_points
72                    .insert(err_state.unwrap().1, err_state.unwrap().1);
73                state.clear();
74            }
75        }
76    }
77}
78
79pub fn recovery_point<'a, 'arn: 'a, 'grm: 'arn, E: ParseError<L = ErrorLabel<'grm>> + 'arn>(
80    item: impl Parser<'arn, 'grm, &'arn ActionResult<'arn, 'grm>, E> + 'a,
81) -> impl Parser<'arn, 'grm, &'arn ActionResult<'arn, 'grm>, E> + 'a {
82    move |pos: Pos,
83          state: &mut ParserState<'arn, 'grm, E>,
84          context: ParserContext|
85          -> PResult<_, E> {
86        // First try original parse
87        match item.parse(
88            pos,
89            state,
90            ParserContext {
91                recovery_disabled: true,
92                ..context
93            },
94        ) {
95            r @ POk(_, _, _, _) => r,
96            PErr(e, s) => {
97                if let Some(to) = state.recovery_points.get(&s) {
98                    POk(ActionResult::VOID, pos, *to, Some((e, s)))
99                } else {
100                    PErr(e, s)
101                }
102            }
103        }
104    }
105}