use itertools::Itertools;
use teleparse_macros::ToSpan;
use crate::{Lexicon, Span};
use super::FirstSet;
#[derive(Debug, Clone, ToSpan, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Error<L: Lexicon> {
pub span: Span,
pub data: ErrorKind<L>,
}
impl<L: Lexicon> Error<L> {
pub fn new<S: Into<Span>>(span: S, data: ErrorKind<L>) -> Self {
Self {
span: span.into(),
data,
}
}
pub fn message(&self, input: &str) -> String {
match &self.data {
ErrorKind::Custom(msg) => msg.clone(),
ErrorKind::UnexpectedCharacters => {
format!("Unexpected: {}", self.span.get(input))
},
ErrorKind::UnexpectedTokens => {
format!("Unexpected token(s): {}", self.span.get(input))
},
ErrorKind::Expecting(set) => {
let set = set.as_terminal_set().to_repr().into_iter().join(", ");
format!("Expecting one of {set}")
},
ErrorKind::UnexpectedEof => "Unexpected end of file".to_string(),
ErrorKind::UnexpectedNoAdvanceInLoop => "Unexpected: Parser did not advance in a loop. The grammar is probably not LL(1), and this is a bug since the parser should catch that before parsing.".to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum ErrorKind<L: Lexicon> {
Custom(String),
UnexpectedCharacters,
UnexpectedTokens,
Expecting(FirstSet<L>),
UnexpectedEof,
UnexpectedNoAdvanceInLoop,
}
pub enum Result<T, L: Lexicon> {
Success(T),
Recovered(T, Vec<Error<L>>),
Panic(Vec<Error<L>>),
}
impl<T, L: Lexicon> From<(T, Vec<Error<L>>)> for Result<T, L> {
#[inline]
fn from((value, errors): (T, Vec<Error<L>>)) -> Self {
if errors.is_empty() {
Result::Success(value)
} else {
Result::Recovered(value, errors)
}
}
}
impl<T, L: Lexicon> Result<T, L> {
#[inline]
pub fn map<T2, F: FnOnce(T) -> T2>(self, f: F) -> Result<T2, L> {
match self {
Self::Success(obj) => Result::Success(f(obj)),
Self::Recovered(obj, errors) => Result::Recovered(f(obj), errors),
Self::Panic(errors) => Result::Panic(errors),
}
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! handle_result {
($errors:ident, $parse:expr) => {{
let result = $parse;
match result {
$crate::syntax::Result::Success(x) => x,
$crate::syntax::Result::Recovered(x, e) => {
$errors.extend(e);
x
}
$crate::syntax::Result::Panic(e) => {
$errors.extend(e);
return $crate::syntax::Result::Panic($errors);
}
}
}};
}