100 lines
2.6 KiB
Rust
100 lines
2.6 KiB
Rust
|
use crate::schala::{SourceReference, Stage};
|
||
|
use crate::source_map::Location;
|
||
|
use crate::tokenizing::{Token, TokenKind};
|
||
|
use crate::parsing::ParseError;
|
||
|
use crate::typechecking::TypeError;
|
||
|
|
||
|
pub struct SchalaError {
|
||
|
errors: Vec<Error>,
|
||
|
//TODO unify these sometime
|
||
|
formatted_parse_error: Option<String>,
|
||
|
}
|
||
|
|
||
|
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<SchalaError> {
|
||
|
let token_errors: Vec<Error> = 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<Location>,
|
||
|
text: Option<String>,
|
||
|
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
|
||
|
)
|
||
|
}
|