use crate::parsing::ParseError; use crate::schala::{SourceReference, Stage}; use crate::source_map::Location; use crate::tokenizing::{Token, TokenKind}; use crate::typechecking::TypeError; pub struct SchalaError { errors: Vec, //TODO unify these sometime formatted_parse_error: Option, } impl SchalaError { pub(crate) fn display(&self) -> String { if let Some(ref err) = self.formatted_parse_error { err.clone() } else { self.errors[0].text.as_ref().cloned().unwrap_or_default() } } pub(crate) fn from_type_error(err: TypeError) -> Self { Self { formatted_parse_error: None, errors: vec![Error { location: None, text: Some(err.msg), stage: Stage::Typechecking, }], } } pub(crate) fn from_string(text: String, stage: Stage) -> Self { Self { formatted_parse_error: None, errors: vec![Error { location: None, text: Some(text), stage, }], } } pub(crate) fn from_parse_error( parse_error: ParseError, source_reference: &SourceReference, ) -> Self { Self { formatted_parse_error: Some(format_parse_error(parse_error, source_reference)), errors: vec![], } } pub(crate) fn from_tokens(tokens: &[Token]) -> Option { let token_errors: Vec = tokens .iter() .filter_map(|tok| match tok.kind { TokenKind::Error(ref err) => Some(Error { location: Some(tok.location), text: Some(err.clone()), stage: Stage::Tokenizing, }), _ => None, }) .collect(); if token_errors.is_empty() { None } else { Some(SchalaError { errors: token_errors, formatted_parse_error: None, }) } } } #[allow(dead_code)] struct Error { location: Option, text: Option, stage: Stage, } fn format_parse_error(error: ParseError, source_reference: &SourceReference) -> String { let line_num = error.token.location.line_num; let ch = error.token.location.char_num; let line_from_program = source_reference.get_line(line_num); let location_pointer = format!("{}^", " ".repeat(ch)); let line_num_digits = format!("{}", line_num).chars().count(); let space_padding = " ".repeat(line_num_digits); let production = match error.production_name { Some(n) => format!("\n(from production \"{}\")", n), None => "".to_string(), }; format!( r#" {error_msg}{production} {space_padding} | {line_num} | {} {space_padding} | {} "#, line_from_program, location_pointer, error_msg = error.msg, space_padding = space_padding, line_num = line_num, production = production ) }