use crate::{ parsing::{Location, ParseError}, schala::{SourceReference, Stage}, symbol_table::SymbolError, type_inference::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() } } #[allow(dead_code)] 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_symbol_table(symbol_errs: Vec) -> Self { //TODO this could be better let errors = symbol_errs .into_iter() .map(|_symbol_err| Error { location: None, text: Some("symbol table error".to_string()), stage: Stage::Symbols, }) .collect(); Self { errors, formatted_parse_error: None } } 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![], } } } #[allow(dead_code)] struct Error { location: Option, text: Option, stage: Stage, } fn format_parse_error(error: ParseError, source_reference: &SourceReference) -> String { let offset = error.location.offset; let (line_start, line_num, line_from_program) = source_reference.get_line(offset); let ch = offset - line_start; let location_pointer = format!("{}^", " ".repeat(ch)); let line_num_digits = format!("{}", line_num).chars().count(); let space_padding = " ".repeat(line_num_digits); format!( r#" {error_msg} {space_padding} | {line_num} | {} {space_padding} | {} "#, line_from_program, location_pointer, error_msg = error.msg, space_padding = space_padding, line_num = line_num, ) }