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}