Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
2 changes: 1 addition & 1 deletion ndc_bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
74 changes: 74 additions & 0 deletions ndc_bin/src/diagnostic.rs
Original file line number Diff line number Diff line change
@@ -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<SourceSpan>,
label: &'static str,
help: Option<String>,
}

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<Box<dyn fmt::Display + 'a>> {
self.help
.as_ref()
.map(|h| -> Box<dyn fmt::Display> { Box::new(h) })
}

fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
self.span.map(|s| -> Box<dyn Iterator<Item = LabeledSpan>> {
Box::new(std::iter::once(LabeledSpan::at(s, self.label)))
})
}
}

impl From<InterpreterError> 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),
},
}
}
}
6 changes: 2 additions & 4 deletions ndc_bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::path::PathBuf;
use std::process;
use std::{fs, io::Write};

mod diagnostic;
mod repl;

mod docs;
Expand Down Expand Up @@ -170,10 +171,7 @@ fn start_lsp() {
}

pub fn into_miette_result<T>(result: Result<T, InterpreterError>) -> miette::Result<T> {
match result {
Err(err) => Err(err)?,
Ok(val) => Ok(val),
}
result.map_err(|e| miette::Report::new(diagnostic::NdcReport::from(e)))
}

#[cfg(test)]
Expand Down
1 change: 0 additions & 1 deletion ndc_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 9 additions & 5 deletions ndc_lib/src/ast/parser.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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<String>,
#[label("here")]
span: Span,
}

Expand Down Expand Up @@ -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 {
Expand Down
42 changes: 25 additions & 17 deletions ndc_lib/src/interpreter/evaluate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand All @@ -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);
}
}
}
Expand Down Expand Up @@ -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]
}

Expand Down
35 changes: 16 additions & 19 deletions ndc_lib/src/interpreter/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Result<Vec<_>, _>>()?
}
(left @ Value::Number(_), Value::Sequence(Sequence::Tuple(right_rc))) => {
right_rc
.iter()
.map(|r| self.call(&mut [left.clone(), r.clone()], env))
.collect::<Result<Vec<_>, _>>()?
}
(Value::Sequence(Sequence::Tuple(left_rc)), right @ Value::Number(_)) => {
left_rc
.iter()
.map(|l| self.call(&mut [l.clone(), right.clone()], env))
.collect::<Result<Vec<_>, _>>()?
}
(
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::<Result<Vec<_>, _>>()?,
(left @ Value::Number(_), Value::Sequence(Sequence::Tuple(right_rc))) => right_rc
.iter()
.map(|r| self.call(&mut [left.clone(), r.clone()], env))
.collect::<Result<Vec<_>, _>>()?,
(Value::Sequence(Sequence::Tuple(left_rc)), right @ Value::Number(_)) => left_rc
.iter()
.map(|l| self.call(&mut [l.clone(), right.clone()], env))
.collect::<Result<Vec<_>, _>>()?,
_ => panic!("caller should handle all checks before vectorizing"),
};

Expand Down
8 changes: 1 addition & 7 deletions ndc_lib/src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
}
17 changes: 12 additions & 5 deletions ndc_lib/src/interpreter/semantic/analyser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand All @@ -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);
}

Expand Down Expand Up @@ -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 {
Expand Down
Loading