use crate::lexer::Span; use crate::parse::ScriptToken; use peg::error::ExpectedSet; use std::error::Error; use std::fmt::{Display, Formatter}; #[derive(Debug)] pub enum TokenErrorKind<'a> { Incomplete, NomError(nom::error::Error<Span<'a>>), } #[derive(Debug)] pub struct TokenError<'a> { pub kind: TokenErrorKind<'a>, } impl<'a> From<TokenErrorKind<'a>> for TokenError<'a> { fn from(value: TokenErrorKind<'a>) -> Self { TokenError { kind: value } } } impl<'a> Display for TokenError<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match &self.kind { TokenErrorKind::Incomplete => write!(f, "Incomplete Program"), TokenErrorKind::NomError(err) => write!(f, "{}", err), } } } impl<'a> Error for TokenError<'a> {} impl<'a> From<nom::Err<nom::error::Error<Span<'a>>>> for TokenError<'a> { fn from(value: nom::Err<nom::error::Error<Span<'a>>>) -> Self { match value { nom::Err::Error(err) => TokenErrorKind::NomError(err).into(), nom::Err::Failure(err) => TokenErrorKind::NomError(err).into(), nom::Err::Incomplete(_) => TokenErrorKind::Incomplete.into(), } } } #[derive(Clone, Debug)] pub enum ParseErrorKind<'a> { Unexpected { found: ScriptToken<'a>, expected: peg::error::ExpectedSet, }, } #[derive(Clone, Debug)] pub struct ParseError<'a> { pub kind: ParseErrorKind<'a>, } #[derive(Debug)] pub enum ForgeErrorKind<'a> { IncompleteInput, LexerError(nom::error::Error<Span<'a>>), UnexpectedToken { found: ScriptToken<'a>, expected: peg::error::ExpectedSet, }, } #[derive(Debug)] pub struct ForgeError<'a> { pub kind: ForgeErrorKind<'a>, } impl<'a> From<ParseError<'a>> for ForgeError<'a> { fn from(value: ParseError<'a>) -> Self { match value.kind { ParseErrorKind::Unexpected { found, expected } => ForgeError { kind: ForgeErrorKind::UnexpectedToken { found, expected }, }, } } } impl<'a> From<TokenError<'a>> for ForgeError<'a> { fn from(value: TokenError<'a>) -> Self { match value.kind { TokenErrorKind::Incomplete => ForgeError { kind: ForgeErrorKind::IncompleteInput, }, TokenErrorKind::NomError(span) => ForgeError { kind: ForgeErrorKind::LexerError(span), }, } } } pub type ForgeResult<'a, T> = Result<T, ForgeError<'a>>; pub fn print_unexpected_token<'a>( source: &'a str, token: &'a ScriptToken<'a>, expected: &'a peg::error::ExpectedSet, ) { let line = token.position.location_line() as usize; let column = token.position.get_column(); let previous_line = if line > 1 { source.lines().nth(line - 2) } else { None }; let source_line = source.lines().nth(line - 1).expect("Missing line"); let next_line = source.lines().nth(line); let largest_line_num = line.max(line.saturating_sub(1)).max(line.saturating_add(1)); let number_length = format!("{}", largest_line_num).len(); eprintln!("| Script error on line {} at \"{}\"\n|", line, token); if let Some(prev) = previous_line { eprintln!("| [{:>width$}] {}", line - 1, prev, width = number_length); } eprintln!( "| [{:>width$}] {}", line, source_line, width = number_length ); eprintln!( "| {} {}{}", vec![" "; number_length + 2].join(""), vec![" "; column - 1].join(""), vec!["^"; token.token_type.len()].join(""), ); if let Some(next) = next_line { eprintln!("| [{:>width$}] {}", line + 1, next, width = number_length); } eprintln!("|\n| Failed To Parse: expected {}", expected); } pub struct ErrorFormat<'a>(pub &'a str, pub &'a ScriptToken<'a>, pub &'a ExpectedSet); impl<'a> Display for ErrorFormat<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let ErrorFormat(source, token, expected) = self; let line = token.position.location_line() as usize; let column = token.position.get_column(); let previous_line = if line > 1 { source.lines().nth(line - 2) } else { None }; let source_line = source.lines().nth(line - 1).expect("Missing line"); let next_line = source.lines().nth(line); let largest_line_num = line.max(line.saturating_sub(1)).max(line.saturating_add(1)); let number_length = format!("{}", largest_line_num).len(); writeln!(f, "| Script error on line {} at \"{}\"\n|", line, token)?; if let Some(prev) = previous_line { writeln!( f, "| [{:>width$}] {}", line - 1, prev, width = number_length )?; } writeln!( f, "| [{:>width$}] {}", line, source_line, width = number_length )?; writeln!( f, "| {} {}{}", vec![" "; number_length + 2].join(""), vec![" "; column - 1].join(""), vec!["^"; token.token_type.len()].join(""), )?; if let Some(next) = next_line { writeln!( f, "| [{:>width$}] {}", line + 1, next, width = number_length )?; } writeln!(f, "|\n| Failed To Parse: expected {}", expected) } } pub fn print_forge_error<'a>(source: &'a str, fe: &'a ForgeError) { match &fe.kind { ForgeErrorKind::IncompleteInput => eprintln!("| Unexpected end of file"), ForgeErrorKind::LexerError(err) => eprintln!("| {}", err), ForgeErrorKind::UnexpectedToken { found, expected } => { print_unexpected_token(source, found, expected) } } } pub fn format_forge_error<'a>(source: &'a str, fe: &'a ForgeError) -> String { match &fe.kind { ForgeErrorKind::IncompleteInput => String::from("| Unexpected end of file"), ForgeErrorKind::LexerError(err) => format!("| {}", err), ForgeErrorKind::UnexpectedToken { found, expected } => { format!("{}", ErrorFormat(source, found, expected)) } } }