use std::fmt::Debug; use pest::iterators::Pair; use crate::Rule; pub type AstResult<'a, T, ET = T> = Result>; #[derive(Debug, Clone)] pub enum ParseError<'a, T> { PreAst(pest::error::Error), Ast(AstError<'a, T>), } #[derive(Debug, Clone)] pub struct AstError<'a, T> { pub span: pest::Span<'a>, pub error_code: ErrorCode, pub error_message: String, pub recovered: Option, } #[derive(Debug, Clone)] pub enum ErrorCode { InvalidStatement, AstGenBug, } impl From> for ParseError<'_, T> { fn from(value: pest::error::Error) -> Self { Self::PreAst(value) } } impl<'a, T> From> for ParseError<'a, T> { fn from(value: AstError<'a, T>) -> Self { Self::Ast(value) } } impl<'a, F> AstError<'a, F> { pub fn get(self) -> AstError<'a, T> { AstError { span: self.span, error_code: self.error_code, error_message: self.error_message, recovered: None, } } #[track_caller] pub fn bug_unimplemented(pair: Pair<'a, Rule>) -> AstResult<'a, T, F> { let loc = std::panic::Location::caller(); Err(Self { span: pair.as_span(), error_code: ErrorCode::AstGenBug, error_message: format!( "Possible bug, unimplemented: {:#?}, at {}:{}", pair.as_rule(), loc.file(), loc.line(), ), recovered: None, }) } } pub trait IntoErr { fn get(self) -> T; fn get_map(self, m: impl Fn(FA) -> FR) -> T; } impl<'a, T, TE, TE2> IntoErr, TE, TE2> for AstResult<'a, T, TE> { fn get(self) -> AstResult<'a, T, TE2> { self.map_err(AstError::get) } fn get_map(self, m: impl Fn(TE) -> TE2) -> AstResult<'a, T, TE2> { self.map_err(|e| AstError { span: e.span, error_code: e.error_code, error_message: e.error_message, recovered: e.recovered.map(m), }) } } pub trait GetLength { fn len(&self) -> usize; } impl GetLength for Result, E> { fn len(&self) -> usize { if let Ok(v) = self { v.len() } else { 0 } } } pub fn collect_recovered<'a, T: Debug, ET>( pairs: impl Iterator>, ) -> AstResult<'a, Vec, Vec> where T: TryFrom, Error = AstError<'a, ET>>, { collect_recovered_map(pairs, T::try_from) } pub fn collect_recovered_map<'a, T: Debug, F, ET>( pairs: impl Iterator>, f: F, ) -> AstResult<'a, Vec, Vec> where F: Fn(pest::iterators::Pair<'a, Rule>) -> AstResult<'a, T, ET>, { let mut items = Vec::new(); let mut last_error: Option> = None; for pair in pairs { match f(pair) { Ok(item) => items.push(item), Err(e) => { last_error = Some(e); } } } match last_error { Some(ast_err) => Err(AstError { span: ast_err.span, error_code: ast_err.error_code, error_message: ast_err.error_message, recovered: Some(items), }), None => Ok(items), } } pub struct AstErrorAnalyzer<'a, T>(pub Option>); impl<'a, T> AstErrorAnalyzer<'a, T> { pub fn get>( &mut self, r: AstResult<'a, V, V2>, ) -> AstResult<'a, V, V2> { if let Err(e) = r { self.0 = Some(e.clone().get()); if let Some(recovered) = e.recovered { Ok(recovered.into()) } else { Err(e) } } else { r } } pub fn build(self, v: T) -> AstResult<'a, T> { if let Some(mut e) = self.0 { e.recovered = Some(v); Err(e) } else { Ok(v) } } }