toktok_core/
error.rs

1use crate::{Input, Span, State};
2use std::cmp::Ordering;
3use std::error::Error as StdError;
4use std::fmt;
5
6#[derive(Debug)]
7pub struct StateError<T>(Option<Error<T>>);
8
9impl<T> StateError<T> {
10    pub fn none() -> Self {
11        Self(None)
12    }
13
14    pub fn is_some(&self) -> bool {
15        self.0.is_some()
16    }
17
18    pub fn unwrap(self) -> ParserError<T> {
19        ParserError(self.0.unwrap())
20    }
21
22    pub fn and(self, err: Error<T>) -> ParserError<T> {
23        ParserError(Error::merge(self.0, err))
24    }
25}
26
27#[derive(Debug)]
28pub struct ParserError<T>(Error<T>);
29
30impl<T> ParserError<T> {
31    pub fn recover<'s, 't>(self, input: Input<'s, 't, T>) -> Result<State<'s, 't, T>, Self> {
32        if self.0.is_fail {
33            Err(self)
34        } else {
35            Ok(State::from_parts(input, StateError(Some(self.0))))
36        }
37    }
38
39    pub fn inore_fail(mut self) -> Self {
40        self.0.is_fail = false;
41        self
42    }
43
44    pub fn with_is_fail(mut self) -> Self {
45        self.0.is_fail = true;
46        self
47    }
48
49    pub fn is_fail(&self) -> bool {
50        self.0.is_fail
51    }
52}
53
54impl<T> fmt::Display for ParserError<T>
55where
56    T: fmt::Display,
57{
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        self.0.fmt(f)
60    }
61}
62
63impl<T> StdError for ParserError<T>
64where
65    T: fmt::Debug + fmt::Display,
66{
67    fn source(&self) -> Option<&(dyn StdError + 'static)> {
68        self.0.source()
69    }
70}
71
72#[derive(Debug)]
73pub struct Error<T> {
74    span: Span,
75    is_fail: bool,
76    kind: ErrorKind<T>,
77}
78
79#[derive(Debug)]
80pub enum ErrorKind<T> {
81    Expected(Vec<TokenExpected<T>>, TokenFound<T>),
82    ExpectedNegative(Box<dyn StdError + Send + Sync>),
83    Custom(Box<dyn StdError + Send + Sync>),
84}
85
86#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
87pub enum TokenExpected<T> {
88    Token(T),
89    Custom(&'static str),
90    Eoi,
91}
92
93#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
94pub enum TokenFound<T> {
95    Token(T),
96    Eoi,
97}
98
99impl<T> Error<T> {
100    pub fn new_custom(span: impl Into<Span>, err: Box<dyn StdError + Send + Sync>) -> Self {
101        Self { span: span.into(), is_fail: false, kind: ErrorKind::Custom(err) }
102    }
103
104    pub fn new_expected(
105        span: impl Into<Span>,
106        expected: impl IntoIterator<Item = TokenExpected<T>>,
107        found: TokenFound<T>,
108    ) -> Self {
109        Self {
110            span: span.into(),
111            is_fail: false,
112            kind: ErrorKind::Expected(expected.into_iter().collect(), found),
113        }
114    }
115
116    pub fn new_expected_negative(
117        span: impl Into<Span>,
118        err: Box<dyn StdError + Send + Sync>,
119    ) -> Self {
120        Self { span: span.into(), is_fail: false, kind: ErrorKind::ExpectedNegative(err) }
121    }
122
123    pub fn with_is_fail(mut self) -> Self {
124        self.is_fail = true;
125        self
126    }
127
128    pub fn is_fail(&self) -> bool {
129        self.is_fail
130    }
131
132    pub fn span(&self) -> Span {
133        self.span.clone()
134    }
135
136    pub fn kind(&self) -> &ErrorKind<T> {
137        &self.kind
138    }
139
140    pub fn pretty_print(&self, options: &PrettyPrintOptions<T>) -> String
141    where
142        T: fmt::Display,
143    {
144        match &self.kind {
145            ErrorKind::Expected(expected, found) => {
146                let tmp_expected_filtered;
147                let expected = match &options.filter_expected {
148                    Some(filter_expected) => {
149                        tmp_expected_filtered = filter_expected(expected);
150                        &tmp_expected_filtered
151                    }
152                    None => expected,
153                };
154
155                let mut message = String::new();
156                message += "found: ";
157                message += &token_found_str(found, options.rename_token_found.as_deref());
158                message += ", expected one of: ";
159                for (idx, token) in expected.iter().enumerate() {
160                    if idx != 0 {
161                        message += ", ";
162                    }
163                    message += &token_expected_str(token, options.rename_token_expected.as_deref());
164                }
165                message
166            }
167            ErrorKind::ExpectedNegative(err) => err.to_string(),
168            ErrorKind::Custom(err) => err.to_string(),
169        }
170    }
171
172    fn merge(e1: Option<Self>, e2: Self) -> Self {
173        let e1 = match e1 {
174            Some(e1) => e1,
175            None => return e2,
176        };
177
178        match e1.cmp(&e2) {
179            Ordering::Greater => e1,
180            Ordering::Less => e2,
181            Ordering::Equal => {
182                debug_assert_eq!(e1.span, e2.span);
183                Self {
184                    span: e1.span,
185                    kind: match (e1.kind, e2.kind) {
186                        (ErrorKind::Custom(err1), ErrorKind::Custom(_err2)) => {
187                            ErrorKind::Custom(err1)
188                        }
189                        (
190                            ErrorKind::Expected(expected1, found1),
191                            ErrorKind::Expected(expected2, _found2),
192                        ) => ErrorKind::Expected(
193                            vec![expected1.into_iter(), expected2.into_iter()]
194                                .into_iter()
195                                .flatten()
196                                .collect(),
197                            found1,
198                        ),
199                        (ErrorKind::ExpectedNegative(err1), ErrorKind::ExpectedNegative(_err2)) => {
200                            ErrorKind::ExpectedNegative(err1)
201                        }
202                        (_, ErrorKind::Custom(err)) | (ErrorKind::Custom(err), _) => {
203                            ErrorKind::Custom(err)
204                        }
205                        (_, ErrorKind::ExpectedNegative(err))
206                        | (ErrorKind::ExpectedNegative(err), _) => ErrorKind::ExpectedNegative(err),
207                    },
208                    is_fail: e1.is_fail || e2.is_fail,
209                }
210            }
211        }
212    }
213
214    fn cmp(&self, other: &Self) -> Ordering {
215        match (&self.span, &other.span) {
216            (Span::Eoi, Span::Eoi) => Ordering::Equal,
217            (Span::Eoi, Span::Range(_)) => Ordering::Greater,
218            (Span::Range(_), Span::Eoi) => Ordering::Less,
219            (Span::Range(r1), Span::Range(r2)) => match r1.end.cmp(&r2.end) {
220                Ordering::Equal => r1.start.cmp(&r2.start),
221                ordering => ordering,
222            },
223        }
224    }
225}
226
227impl<T> From<ParserError<T>> for Error<T> {
228    fn from(e: ParserError<T>) -> Self {
229        e.0
230    }
231}
232
233impl<T> fmt::Display for Error<T>
234where
235    T: fmt::Display,
236{
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        write!(f, "{}", self.pretty_print(&Default::default()))
239    }
240}
241
242impl<T> StdError for Error<T>
243where
244    T: fmt::Debug + fmt::Display,
245{
246    fn source(&self) -> Option<&(dyn StdError + 'static)> {
247        match &self.kind {
248            ErrorKind::Expected(_, _) => None,
249            ErrorKind::ExpectedNegative(err) => Some(&**err),
250            ErrorKind::Custom(err) => Some(&**err),
251        }
252    }
253}
254
255#[allow(clippy::type_complexity)]
256pub struct PrettyPrintOptions<T> {
257    pub rename_token_expected: Option<Box<dyn Fn(&TokenExpected<T>) -> String>>,
258    pub rename_token_found: Option<Box<dyn Fn(&TokenFound<T>) -> String>>,
259    pub filter_expected: Option<Box<dyn Fn(&[TokenExpected<T>]) -> Vec<TokenExpected<T>>>>,
260}
261
262impl<T> Default for PrettyPrintOptions<T> {
263    fn default() -> Self {
264        Self { rename_token_expected: None, rename_token_found: None, filter_expected: None }
265    }
266}
267
268#[allow(clippy::type_complexity)]
269fn token_expected_str<T>(
270    token: &TokenExpected<T>,
271    rename_token: Option<&dyn Fn(&TokenExpected<T>) -> String>,
272) -> String
273where
274    T: fmt::Display,
275{
276    match rename_token {
277        Some(rename_token) => rename_token(token),
278        None => match token {
279            TokenExpected::Eoi => "<EOI>".to_string(),
280            TokenExpected::Custom(name) => format!("<{}>", name),
281            TokenExpected::Token(token) => format!("{}", token),
282        },
283    }
284}
285
286#[allow(clippy::type_complexity)]
287fn token_found_str<T>(
288    token: &TokenFound<T>,
289    rename_token: Option<&dyn Fn(&TokenFound<T>) -> String>,
290) -> String
291where
292    T: fmt::Display,
293{
294    match rename_token {
295        Some(rename_token) => rename_token(token),
296        None => match token {
297            TokenFound::Eoi => "<EOI>".to_string(),
298            TokenFound::Token(token) => format!("{}", token),
299        },
300    }
301}