diff --git a/examples/errors.rs b/examples/errors.rs index 3cfd837..8f6c534 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,9 +1,10 @@ -use eww_config::{ast::*, config::*}; +use eww_config::{ast::*, config::*, format_diagnostic::ToDiagnostic}; fn main() { let mut files = codespan_reporting::files::SimpleFiles::new(); - let input = r#"(hi :bar 22 :baz {"hi" asdfasdf * 2} (foo) (baz))"#; + let input = r#" + (hi :bar 22 :baz {(foo == bar ? 12.K : 12)} (foo) (baz))"#; let file_id = files.add("foo.eww", input); let ast = eww_config::parse_string(file_id, input); @@ -12,10 +13,12 @@ fn main() { println!("{:?}", ast); } Err(err) => { - let diag = err.pretty_diagnostic(&files); + dbg!(&err); + let diag = err.to_diagnostic(&files); use codespan_reporting::term; + let config = term::Config::default(); let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); - term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); + term::emit(&mut writer, &config, &files, &diag).unwrap(); } } } diff --git a/src/error.rs b/src/error.rs index 0e6ba14..095fdef 100644 --- a/src/error.rs +++ b/src/error.rs @@ -30,15 +30,6 @@ impl AstError { } } - pub fn pretty_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> diagnostic::Diagnostic { - let diag = diagnostic::Diagnostic::error().with_message(format!("{}", self)); - if let Some(span) = self.get_span() { - diag.with_labels(vec![diagnostic::Label::primary(span.2, span.0..span.1)]) - } else { - diag - } - } - pub fn from_parse_error( file_id: usize, err: lalrpop_util::ParseError, diff --git a/src/format_diagnostic.rs b/src/format_diagnostic.rs new file mode 100644 index 0000000..5fd8a71 --- /dev/null +++ b/src/format_diagnostic.rs @@ -0,0 +1,81 @@ +use codespan_reporting::{diagnostic, files}; +use simplexpr::dynval; + +use crate::{ast::Span, error::AstError, parse_error}; +use diagnostic::*; + +fn span_to_label(span: Span) -> Label { + Label::primary(span.2, span.0..span.1) +} + +pub trait ToDiagnostic { + fn to_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> Diagnostic; +} + +impl ToDiagnostic for AstError { + fn to_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> Diagnostic { + let diag = Diagnostic::error(); + if let Some(span) = self.get_span() { + use lalrpop_util::ParseError::*; + match self { + AstError::InvalidDefinition(_) => todo!(), + + AstError::MissingNode(_, expected) => diag + .with_message(format!("Missing {}", expected)) + .with_labels(vec![span_to_label(span).with_message(format!("Expected `{}` here", expected))]), + + AstError::WrongExprType(_, expected, actual) => diag + .with_message("Wrong type of expression") + .with_notes(vec![format!("Expected: {}\nGot: {}", expected, actual)]) + .with_labels(vec![span_to_label(span).with_message(format!("Expected a `{}` here", expected))]), + + AstError::ParseError { file_id, source } => { + lalrpop_error_to_diagnostic(source, diag, span, move |diag, error| match error { + parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, diag, span), + parse_error::ParseError::LexicalError(_) => diag + .with_message("Invalid token") + .with_labels(vec![span_to_label(span).with_message("Invalid token")]), + }) + } + } + } else { + diag.with_message(format!("{}", self)) + } + } +} + +fn lalrpop_error_to_diagnostic( + error: &lalrpop_util::ParseError, + diag: Diagnostic, + span: Span, + handle_user_error: impl FnOnce(Diagnostic, &E) -> Diagnostic, +) -> Diagnostic { + use lalrpop_util::ParseError::*; + match error { + InvalidToken { location } => diag.with_message("Invalid token").with_labels(vec![span_to_label(span)]), + UnrecognizedEOF { location, expected } => diag + .with_message("Input ended unexpectedly. Check if you have any unclosed delimiters") + .with_labels(vec![span_to_label(span)]), + UnrecognizedToken { token, expected } => diag + .with_message(format!("Unexpected token `{}` encoutered", token.1)) + .with_labels(vec![span_to_label(span).with_message("Token unexpected")]), + + ExtraToken { token } => diag.with_message(format!("Extra token encountered: `{}`", token.1)), + User { error } => handle_user_error(diag, error), + } +} + +fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, diag: Diagnostic, span: Span) -> Diagnostic { + match error { + simplexpr::error::Error::ParseError { source } => lalrpop_error_to_diagnostic(source, diag, span, move |diag, error| { + diag.with_message("Invalid token").with_labels(vec![span_to_label(span).with_message("Invalid token")]) + }), + simplexpr::error::Error::ConversionError(dynval::ConversionError { value, target_type, source }) => diag + .with_message(format!("{}", error)) + .with_labels(vec![span_to_label(span).with_message(format!("{} is not of type `{}`", value, target_type))]) + .with_notes(source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()), + simplexpr::error::Error::Spanned(..) => todo!(), + simplexpr::error::Error::Eval(error) => diag.with_message(format!("{}", error)).with_labels(vec![span_to_label(span)]), + simplexpr::error::Error::Other(error) => diag.with_message(format!("{}", error)).with_labels(vec![span_to_label(span)]), + } +} diff --git a/src/lib.rs b/src/lib.rs index cc06510..ffcee55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,9 +5,11 @@ pub mod ast; pub mod config; pub mod error; +pub mod format_diagnostic; mod lexer; mod parse_error; pub mod value; + use ast::Ast; use error::{AstError, AstResult};