Skip to main content

pegtastic_runtime/
error.rs

1//! Parse error reporting
2
3use std::fmt::{ self, Display, Debug };
4use crate::{ RuleResult, Parse };
5use std::collections::HashSet;
6
7/// A set of literals or names that failed to match
8#[derive(PartialEq, Eq, Debug, Clone)]
9pub struct ExpectedSet {
10    expected: HashSet<&'static str>,
11}
12
13impl ExpectedSet {
14    /// Iterator of expected literals
15    pub fn tokens<'a>(&'a self) -> impl Iterator<Item = &'static str> + 'a {
16        self.expected.iter().map(|x| *x)
17    }
18}
19
20impl Display for ExpectedSet {
21    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
22         if self.expected.is_empty() {
23            write!(fmt, "<unreported>")?;
24        } else if self.expected.len() == 1 {
25            write!(fmt, "{}", self.expected.iter().next().unwrap())?;
26        } else {
27            let mut errors = self.tokens().collect::<Vec<_>>();
28            errors.sort();
29            let mut iter = errors.into_iter();
30
31            write!(fmt, "one of {}", iter.next().unwrap())?;
32            for elem in iter {
33                write!(fmt, ", {}", elem)?;
34            }
35        }
36
37        Ok(())
38    }
39}
40
41/// An error from a parse failure
42#[derive(PartialEq, Eq, Debug, Clone)]
43pub struct ParseError<L> {
44    /// The furthest position the parser reached in the input
45    pub location: L,
46
47    /// The set of literals that failed to match at that position
48    pub expected: ExpectedSet,
49}
50
51impl<L: Display> Display for ParseError<L> {
52    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
53        write!(fmt, "error at {}: expected {}", self.location, self.expected)
54    }
55}
56
57impl<L: Display + Debug> ::std::error::Error for ParseError<L> {
58    fn description(&self) -> &str {
59        "parse error"
60    }
61}
62
63#[doc(hidden)]
64pub struct ErrorState {
65    pub max_err_pos: usize,
66    pub suppress_fail: usize,
67    pub reparsing_on_error: bool,
68    pub expected: ExpectedSet,
69}
70
71impl ErrorState {
72    pub fn new(initial_pos: usize) -> ErrorState {
73        ErrorState {
74            max_err_pos: initial_pos,
75            suppress_fail: 0,
76            reparsing_on_error: false,
77            expected: ExpectedSet { expected: HashSet::new() },
78        }
79    }
80
81    pub fn reparse_for_error(&mut self) {
82        self.suppress_fail = 0;
83        self.reparsing_on_error = true;
84    }
85
86    #[inline(never)]
87    pub fn mark_failure_slow_path(&mut self, pos: usize, expected: &'static str) {
88        if pos == self.max_err_pos {
89            self.expected.expected.insert(expected);
90        }
91    }
92
93    #[inline(always)]
94    pub fn mark_failure(&mut self, pos: usize, expected: &'static str) -> RuleResult<()> {
95        if self.suppress_fail == 0 {
96            if self.reparsing_on_error {
97                self.mark_failure_slow_path(pos, expected);
98            } else if pos > self.max_err_pos {
99                self.max_err_pos = pos;
100            }
101        }
102        RuleResult::Failed
103    }
104
105    pub fn into_parse_error<I: Parse + ?Sized>(self, input: &I) -> ParseError<I::PositionRepr> {
106        ParseError {
107            location: Parse::position_repr(input, self.max_err_pos.into()),
108            expected: self.expected,
109        }
110    }
111}