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}