diff --git a/Cargo.lock b/Cargo.lock index 4a12ece5..7d9781f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1268,7 +1268,6 @@ dependencies = [ "factorial", "itertools 0.14.0", "md5", - "miette", "ndc_macros", "num", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index bbc02b32..095b6c01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ derive_builder = { version = "0.20.2" } derive_more = { version = "2.1.1", features = ["deref", "deref_mut", "from", "display", "constructor"] } factorial = "0.4.0" itertools = "0.14.0" -miette = { version = "7.6.0", features = ["fancy"] } ndc_lib = { path = "ndc_lib" } ndc_lsp = { path = "ndc_lsp" } ndc_macros = { path = "ndc_macros" } diff --git a/ndc_bin/Cargo.toml b/ndc_bin/Cargo.toml index d97b12bc..ce86853e 100644 --- a/ndc_bin/Cargo.toml +++ b/ndc_bin/Cargo.toml @@ -13,7 +13,7 @@ anyhow.workspace = true clap.workspace = true itertools.workspace = true strsim.workspace = true -miette.workspace = true +miette = { version = "7.6.0", features = ["fancy"] } ndc_lib.workspace = true ndc_lsp.workspace = true owo-colors.workspace = true diff --git a/ndc_bin/src/diagnostic.rs b/ndc_bin/src/diagnostic.rs new file mode 100644 index 00000000..eeed1162 --- /dev/null +++ b/ndc_bin/src/diagnostic.rs @@ -0,0 +1,74 @@ +use miette::{Diagnostic, LabeledSpan, SourceSpan}; +use ndc_lib::interpreter::InterpreterError; +use ndc_lib::lexer::Span; +use std::fmt; + +fn span_to_source_span(span: Span) -> SourceSpan { + (span.offset(), span.end() - span.offset()).into() +} + +pub struct NdcReport { + message: String, + span: Option, + label: &'static str, + help: Option, +} + +impl fmt::Display for NdcReport { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl fmt::Debug for NdcReport { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl std::error::Error for NdcReport {} + +impl Diagnostic for NdcReport { + fn help<'a>(&'a self) -> Option> { + self.help + .as_ref() + .map(|h| -> Box { Box::new(h) }) + } + + fn labels(&self) -> Option + '_>> { + self.span.map(|s| -> Box> { + Box::new(std::iter::once(LabeledSpan::at(s, self.label))) + }) + } +} + +impl From for NdcReport { + fn from(err: InterpreterError) -> Self { + match err { + InterpreterError::Lexer { cause } => Self { + message: cause.to_string(), + span: Some(span_to_source_span(cause.span())), + label: "here", + help: cause.help_text().map(str::to_owned), + }, + InterpreterError::Parser { cause } => Self { + message: cause.to_string(), + span: Some(span_to_source_span(cause.span())), + label: "here", + help: cause.help_text().map(str::to_owned), + }, + InterpreterError::Resolver { cause } => Self { + message: cause.to_string(), + span: Some(span_to_source_span(cause.span())), + label: "related to this", + help: None, + }, + InterpreterError::Evaluation(cause) => Self { + message: cause.to_string(), + span: Some(span_to_source_span(cause.span())), + label: "related to this", + help: cause.help_text().map(str::to_owned), + }, + } + } +} diff --git a/ndc_bin/src/main.rs b/ndc_bin/src/main.rs index 9669a061..6fe89f93 100644 --- a/ndc_bin/src/main.rs +++ b/ndc_bin/src/main.rs @@ -10,6 +10,7 @@ use std::path::PathBuf; use std::process; use std::{fs, io::Write}; +mod diagnostic; mod repl; mod docs; @@ -170,10 +171,7 @@ fn start_lsp() { } pub fn into_miette_result(result: Result) -> miette::Result { - match result { - Err(err) => Err(err)?, - Ok(val) => Ok(val), - } + result.map_err(|e| miette::Report::new(diagnostic::NdcReport::from(e))) } #[cfg(test)] diff --git a/ndc_lib/Cargo.toml b/ndc_lib/Cargo.toml index 28b01be9..3c684b8b 100644 --- a/ndc_lib/Cargo.toml +++ b/ndc_lib/Cargo.toml @@ -11,7 +11,6 @@ derive_more.workspace = true derive_builder.workspace = true factorial.workspace = true itertools.workspace = true -miette.workspace = true ndc_macros.workspace = true num.workspace = true once_cell.workspace = true diff --git a/ndc_lib/src/ast/parser.rs b/ndc_lib/src/ast/parser.rs index 97a0494d..534455a3 100644 --- a/ndc_lib/src/ast/parser.rs +++ b/ndc_lib/src/ast/parser.rs @@ -1,7 +1,5 @@ use std::fmt::Write; -use miette::Diagnostic; - use crate::ast::Expression; use crate::ast::expression::{Binding, ExpressionLocation, ForBody, ForIteration, Lvalue}; use crate::ast::operator::{BinaryOperator, LogicalOperator, UnaryOperator}; @@ -1269,13 +1267,11 @@ impl Parser { } } -#[derive(thiserror::Error, Diagnostic, Debug)] +#[derive(thiserror::Error, Debug)] #[error("{text}")] pub struct Error { text: String, - #[help] help_text: Option, - #[label("here")] span: Span, } @@ -1310,6 +1306,14 @@ impl Error { pub fn location(&self) -> Span { self.span } + + pub fn span(&self) -> Span { + self.span + } + + pub fn help_text(&self) -> Option<&str> { + self.help_text.as_deref() + } } fn tokens_to_string(tokens: &[Token]) -> String { diff --git a/ndc_lib/src/interpreter/evaluate/mod.rs b/ndc_lib/src/interpreter/evaluate/mod.rs index 0cd17e65..5a24b6f9 100644 --- a/ndc_lib/src/interpreter/evaluate/mod.rs +++ b/ndc_lib/src/interpreter/evaluate/mod.rs @@ -720,13 +720,11 @@ fn declare_or_assign_variable( Ok(Value::unit()) } -#[derive(thiserror::Error, miette::Diagnostic, Debug)] +#[derive(thiserror::Error, Debug)] #[error("{text}")] pub struct EvaluationError { text: String, - #[label("related to this")] span: Span, - #[help] help_text: Option, } @@ -825,6 +823,14 @@ impl EvaluationError { help_text: None, } } + + pub fn span(&self) -> Span { + self.span + } + + pub fn help_text(&self) -> Option<&str> { + self.help_text.as_deref() + } } pub trait ErrorConverter: fmt::Debug + fmt::Display { @@ -999,21 +1005,19 @@ fn resolve_and_call( let opt = match resolved { Binding::None => None, Binding::Resolved(var) => Some(environment.borrow().get(*var)), - Binding::Dynamic(dynamic_binding) => dynamic_binding - .iter() - .find_map(|binding| { - let value = environment.borrow().get(*binding); + Binding::Dynamic(dynamic_binding) => dynamic_binding.iter().find_map(|binding| { + let value = environment.borrow().get(*binding); - let Value::Function(fun) = &value else { - panic!("dynamic binding resolved to non-function type at runtime"); - }; + let Value::Function(fun) = &value else { + panic!("dynamic binding resolved to non-function type at runtime"); + }; - if fun.static_type().is_fn_and_matches(&arg_types) { - return Some(value); - } + if fun.static_type().is_fn_and_matches(&arg_types) { + return Some(value); + } - None - }), + None + }), }; if opt.is_none() { @@ -1033,7 +1037,9 @@ fn resolve_and_call( } }); if let Some(inner_fn) = inner_fn { - return inner_fn.call_vectorized(&mut args, environment).add_span(span); + return inner_fn + .call_vectorized(&mut args, environment) + .add_span(span); } } } @@ -1068,7 +1074,9 @@ fn resolve_and_call( fn vectorized_element_types(left: &StaticType, right: &StaticType) -> [StaticType; 2] { let left_elem = left.sequence_element_type().unwrap_or_else(|| left.clone()); - let right_elem = right.sequence_element_type().unwrap_or_else(|| right.clone()); + let right_elem = right + .sequence_element_type() + .unwrap_or_else(|| right.clone()); [left_elem, right_elem] } diff --git a/ndc_lib/src/interpreter/function.rs b/ndc_lib/src/interpreter/function.rs index 0e7dd076..9228e7f3 100644 --- a/ndc_lib/src/interpreter/function.rs +++ b/ndc_lib/src/interpreter/function.rs @@ -132,25 +132,22 @@ impl Function { }; let result = match (left, right) { - (Value::Sequence(Sequence::Tuple(left_rc)), Value::Sequence(Sequence::Tuple(right_rc))) => { - left_rc - .iter() - .zip(right_rc.iter()) - .map(|(l, r)| self.call(&mut [l.clone(), r.clone()], env)) - .collect::, _>>()? - } - (left @ Value::Number(_), Value::Sequence(Sequence::Tuple(right_rc))) => { - right_rc - .iter() - .map(|r| self.call(&mut [left.clone(), r.clone()], env)) - .collect::, _>>()? - } - (Value::Sequence(Sequence::Tuple(left_rc)), right @ Value::Number(_)) => { - left_rc - .iter() - .map(|l| self.call(&mut [l.clone(), right.clone()], env)) - .collect::, _>>()? - } + ( + Value::Sequence(Sequence::Tuple(left_rc)), + Value::Sequence(Sequence::Tuple(right_rc)), + ) => left_rc + .iter() + .zip(right_rc.iter()) + .map(|(l, r)| self.call(&mut [l.clone(), r.clone()], env)) + .collect::, _>>()?, + (left @ Value::Number(_), Value::Sequence(Sequence::Tuple(right_rc))) => right_rc + .iter() + .map(|r| self.call(&mut [left.clone(), r.clone()], env)) + .collect::, _>>()?, + (Value::Sequence(Sequence::Tuple(left_rc)), right @ Value::Number(_)) => left_rc + .iter() + .map(|l| self.call(&mut [l.clone(), right.clone()], env)) + .collect::, _>>()?, _ => panic!("caller should handle all checks before vectorizing"), }; diff --git a/ndc_lib/src/interpreter/mod.rs b/ndc_lib/src/interpreter/mod.rs index 9aed1847..3a8f9514 100644 --- a/ndc_lib/src/interpreter/mod.rs +++ b/ndc_lib/src/interpreter/mod.rs @@ -8,8 +8,6 @@ use crate::interpreter::function::FunctionCarrier; use crate::interpreter::semantic::analyser::{Analyser, ScopeTree}; use crate::interpreter::value::Value; use crate::lexer::{Lexer, TokenLocation}; -use miette::Diagnostic; - pub mod environment; pub mod evaluate; pub mod function; @@ -121,27 +119,23 @@ impl Interpreter { } } -#[derive(thiserror::Error, Diagnostic, Debug)] +#[derive(thiserror::Error, Debug)] pub enum InterpreterError { #[error("Error while lexing source")] - #[diagnostic(transparent)] Lexer { #[from] cause: crate::lexer::Error, }, #[error("Error while parsing source")] - #[diagnostic(transparent)] Parser { #[from] cause: crate::ast::Error, }, #[error("Error during static analysis")] - #[diagnostic(transparent)] Resolver { #[from] cause: semantic::analyser::AnalysisError, }, #[error("Error while executing code")] - #[diagnostic(transparent)] Evaluation(#[from] EvaluationError), } diff --git a/ndc_lib/src/interpreter/semantic/analyser.rs b/ndc_lib/src/interpreter/semantic/analyser.rs index 8aa839a0..511a90b1 100644 --- a/ndc_lib/src/interpreter/semantic/analyser.rs +++ b/ndc_lib/src/interpreter/semantic/analyser.rs @@ -107,7 +107,10 @@ impl Analyser { parameters: Some(param_types.clone()), return_type: Box::new(StaticType::Any), }; - Some(self.scope_tree.create_local_binding(name.clone(), placeholder)) + Some( + self.scope_tree + .create_local_binding(name.clone(), placeholder), + ) } else { None }; @@ -129,7 +132,8 @@ impl Analyser { if let Some(slot) = pre_slot { // TODO: is this correct, for now we just always create a new binding, we could // also produce an error if we are generating a conflicting binding - self.scope_tree.update_binding_type(slot, function_type.clone()); + self.scope_tree + .update_binding_type(slot, function_type.clone()); *resolved_name = Some(slot); } @@ -803,15 +807,18 @@ impl Scope { self.identifiers.len() - 1 } } -#[derive(thiserror::Error, miette::Diagnostic, Debug)] +#[derive(thiserror::Error, Debug)] #[error("{text}")] pub struct AnalysisError { text: String, - #[label("related to this")] span: Span, } -impl AnalysisError {} +impl AnalysisError { + pub fn span(&self) -> Span { + self.span + } +} impl AnalysisError { fn unable_to_index_into(typ: &StaticType, span: Span) -> Self { diff --git a/ndc_lib/src/lexer/mod.rs b/ndc_lib/src/lexer/mod.rs index c1122bff..97e186c8 100644 --- a/ndc_lib/src/lexer/mod.rs +++ b/ndc_lib/src/lexer/mod.rs @@ -3,7 +3,6 @@ mod span; mod string; mod token; -use miette::{Diagnostic, SourceSpan}; use number::NumberLexer; use std::collections::VecDeque; use std::str::Chars; @@ -238,18 +237,11 @@ impl SourceIterator<'_> { } } -#[derive(Diagnostic, thiserror::Error, Debug)] +#[derive(thiserror::Error, Debug)] #[error("{text}")] -#[diagnostic()] pub struct Error { text: String, - location: Span, - - #[label("here")] - location_ss: SourceSpan, - - #[help] help_text: Option, } @@ -259,7 +251,6 @@ impl Error { Self { text, location: source, - location_ss: source.into(), help_text: None, } } @@ -269,7 +260,6 @@ impl Error { Self { text: "Unterminated string".to_string(), location: span, - location_ss: span.into(), help_text: None, } } @@ -279,7 +269,6 @@ impl Error { Self { text, location: source, - location_ss: source.into(), help_text: Some(help_text), } } @@ -287,4 +276,12 @@ impl Error { pub fn location(&self) -> Span { self.location } + + pub fn span(&self) -> Span { + self.location + } + + pub fn help_text(&self) -> Option<&str> { + self.help_text.as_deref() + } } diff --git a/ndc_lib/src/lexer/span.rs b/ndc_lib/src/lexer/span.rs index 6b341b94..32178cf0 100644 --- a/ndc_lib/src/lexer/span.rs +++ b/ndc_lib/src/lexer/span.rs @@ -37,9 +37,3 @@ impl Span { self.offset + self.length } } - -impl From for miette::SourceSpan { - fn from(val: Span) -> Self { - (val.offset, val.length).into() - } -} diff --git a/tests/build.rs b/tests/build.rs index cee2fbf6..56612338 100644 --- a/tests/build.rs +++ b/tests/build.rs @@ -17,10 +17,7 @@ fn main() { } fn generate_tests(output: &mut impl Write, base: &Path, dir: &Path) { - let mut entries: Vec<_> = fs::read_dir(dir) - .unwrap() - .filter_map(|e| e.ok()) - .collect(); + let mut entries: Vec<_> = fs::read_dir(dir).unwrap().filter_map(|e| e.ok()).collect(); entries.sort_by_key(|e| e.path()); for entry in entries {