Skip to main content

mist_parser/
error.rs

1use std::fmt::Debug;
2
3use pest::iterators::Pair;
4
5use crate::Rule;
6
7pub type AstResult<'a, T, ET = T> = Result<T, AstError<'a, ET>>;
8
9#[derive(Debug, Clone)]
10pub enum ParseError<'a, T> {
11    PreAst(pest::error::Error<Rule>),
12    Ast(AstError<'a, T>),
13}
14
15#[derive(Debug, Clone)]
16pub struct AstError<'a, T> {
17    pub span: pest::Span<'a>,
18    pub error_code: ErrorCode,
19    pub error_message: String,
20    pub recovered: Option<T>,
21}
22
23#[derive(Debug, Clone)]
24pub enum ErrorCode {
25    InvalidStatement,
26    AstGenBug,
27}
28
29impl<T> From<pest::error::Error<Rule>> for ParseError<'_, T> {
30    fn from(value: pest::error::Error<Rule>) -> Self {
31        Self::PreAst(value)
32    }
33}
34
35impl<'a, T> From<AstError<'a, T>> for ParseError<'a, T> {
36    fn from(value: AstError<'a, T>) -> Self {
37        Self::Ast(value)
38    }
39}
40
41impl<'a, F> AstError<'a, F> {
42    pub fn get<T>(self) -> AstError<'a, T> {
43        AstError {
44            span: self.span,
45            error_code: self.error_code,
46            error_message: self.error_message,
47            recovered: None,
48        }
49    }
50
51    #[track_caller]
52    pub fn bug_unimplemented<T>(pair: Pair<'a, Rule>) -> AstResult<'a, T, F> {
53        let loc = std::panic::Location::caller();
54
55        Err(Self {
56            span: pair.as_span(),
57            error_code: ErrorCode::AstGenBug,
58            error_message: format!(
59                "Possible bug, unimplemented: {:#?}, at {}:{}",
60                pair.as_rule(),
61                loc.file(),
62                loc.line(),
63            ),
64            recovered: None,
65        })
66    }
67}
68
69pub trait IntoErr<T, FA, FR> {
70    fn get(self) -> T;
71    fn get_map(self, m: impl Fn(FA) -> FR) -> T;
72}
73
74impl<'a, T, TE, TE2> IntoErr<AstResult<'a, T, TE2>, TE, TE2> for AstResult<'a, T, TE> {
75    fn get(self) -> AstResult<'a, T, TE2> {
76        self.map_err(AstError::get)
77    }
78
79    fn get_map(self, m: impl Fn(TE) -> TE2) -> AstResult<'a, T, TE2> {
80        self.map_err(|e| AstError {
81            span: e.span,
82            error_code: e.error_code,
83            error_message: e.error_message,
84            recovered: e.recovered.map(m),
85        })
86    }
87}
88
89pub trait GetLength {
90    fn len(&self) -> usize;
91}
92
93impl<T, E> GetLength for Result<Vec<T>, E> {
94    fn len(&self) -> usize {
95        if let Ok(v) = self { v.len() } else { 0 }
96    }
97}
98
99pub fn collect_recovered<'a, T: Debug, ET>(
100    pairs: impl Iterator<Item = pest::iterators::Pair<'a, Rule>>,
101) -> AstResult<'a, Vec<T>, Vec<T>>
102where
103    T: TryFrom<pest::iterators::Pair<'a, Rule>, Error = AstError<'a, ET>>,
104{
105    collect_recovered_map(pairs, T::try_from)
106}
107
108pub fn collect_recovered_map<'a, T: Debug, F, ET>(
109    pairs: impl Iterator<Item = pest::iterators::Pair<'a, Rule>>,
110    f: F,
111) -> AstResult<'a, Vec<T>, Vec<T>>
112where
113    F: Fn(pest::iterators::Pair<'a, Rule>) -> AstResult<'a, T, ET>,
114{
115    let mut items = Vec::new();
116    let mut last_error: Option<AstError<'a, ET>> = None;
117
118    for pair in pairs {
119        match f(pair) {
120            Ok(item) => items.push(item),
121            Err(e) => {
122                last_error = Some(e);
123            }
124        }
125    }
126
127    match last_error {
128        Some(ast_err) => Err(AstError {
129            span: ast_err.span,
130            error_code: ast_err.error_code,
131            error_message: ast_err.error_message,
132            recovered: Some(items),
133        }),
134        None => Ok(items),
135    }
136}
137
138pub struct AstErrorAnalyzer<'a, T>(pub Option<AstError<'a, T>>);
139
140impl<'a, T> AstErrorAnalyzer<'a, T> {
141    pub fn get<V: Clone, V2: Clone + Into<V>>(
142        &mut self,
143        r: AstResult<'a, V, V2>,
144    ) -> AstResult<'a, V, V2> {
145        if let Err(e) = r {
146            self.0 = Some(e.clone().get());
147
148            if let Some(recovered) = e.recovered {
149                Ok(recovered.into())
150            } else {
151                Err(e)
152            }
153        } else {
154            r
155        }
156    }
157
158    pub fn build(self, v: T) -> AstResult<'a, T> {
159        if let Some(mut e) = self.0 {
160            e.recovered = Some(v);
161
162            Err(e)
163        } else {
164            Ok(v)
165        }
166    }
167}