dust/src/error.rs

319 lines
10 KiB
Rust
Raw Normal View History

2024-03-17 21:39:39 +00:00
use std::{io, ops::Range, sync::PoisonError};
2024-02-25 18:49:26 +00:00
2024-03-18 07:24:41 +00:00
use ariadne::{Color, Fmt, Label, Report, ReportBuilder, ReportKind};
2024-03-16 19:01:45 +00:00
use chumsky::{prelude::Rich, span::Span};
2024-02-25 18:49:26 +00:00
2024-03-06 22:32:31 +00:00
use crate::{
2024-03-17 11:48:06 +00:00
abstract_tree::{Identifier, SourcePosition, Type},
2024-03-06 22:32:31 +00:00
lexer::Token,
};
2024-02-25 18:49:26 +00:00
#[derive(Debug, PartialEq)]
2024-03-06 20:36:58 +00:00
pub enum Error {
2024-03-06 22:32:31 +00:00
Parse {
expected: String,
2024-03-16 19:01:45 +00:00
span: (usize, usize),
2024-03-06 22:32:31 +00:00
},
Lex {
expected: String,
2024-03-16 19:01:45 +00:00
span: (usize, usize),
2024-03-06 22:32:31 +00:00
},
2024-03-17 17:36:31 +00:00
Runtime {
error: RuntimeError,
position: SourcePosition,
},
2024-03-17 11:31:45 +00:00
Validation {
error: ValidationError,
2024-03-17 11:48:06 +00:00
position: SourcePosition,
2024-03-17 11:31:45 +00:00
},
2024-02-25 18:49:26 +00:00
}
2024-03-07 03:15:35 +00:00
impl Error {
2024-03-18 07:24:41 +00:00
pub fn build_report<'a>(self) -> ReportBuilder<'a, (&'a str, Range<usize>)> {
2024-03-18 09:47:21 +00:00
let (mut builder, validation_error, error_position) = match self {
2024-03-16 19:01:45 +00:00
Error::Parse { expected, span } => {
2024-03-18 07:24:41 +00:00
let message = if expected.is_empty() {
2024-03-20 05:29:07 +00:00
"Invalid character.".to_string()
2024-03-18 07:24:41 +00:00
} else {
format!("Expected {expected}.")
2024-03-16 19:01:45 +00:00
};
2024-03-18 07:24:41 +00:00
(
Report::build(
ReportKind::Custom("Parsing Error", Color::White),
"input",
span.1,
)
.with_label(
Label::new(("input", span.0..span.1))
.with_message(message)
.with_color(Color::Red),
),
None,
2024-03-18 09:47:21 +00:00
span.into(),
2024-03-18 07:24:41 +00:00
)
2024-03-16 19:01:45 +00:00
}
2024-03-07 03:15:35 +00:00
Error::Lex { expected, span } => {
2024-03-18 07:24:41 +00:00
let message = if expected.is_empty() {
"Invalid token.".to_string()
} else {
format!("Expected {expected}.")
2024-03-07 03:15:35 +00:00
};
2024-03-18 07:24:41 +00:00
(
Report::build(
ReportKind::Custom("Dust Error", Color::White),
"input",
span.1,
)
.with_label(
Label::new(("input", span.0..span.1))
.with_message(message)
.with_color(Color::Red),
),
None,
2024-03-18 09:47:21 +00:00
span.into(),
2024-03-18 07:24:41 +00:00
)
2024-03-07 03:15:35 +00:00
}
2024-03-18 07:24:41 +00:00
Error::Runtime { error, position } => (
Report::build(
2024-03-20 05:29:07 +00:00
ReportKind::Custom("Runtime Error", Color::White),
2024-03-18 07:24:41 +00:00
"input",
position.1,
),
if let RuntimeError::ValidationFailure(validation_error) = error {
Some(validation_error)
} else {
None
},
2024-03-18 09:47:21 +00:00
position,
2024-03-18 07:24:41 +00:00
),
Error::Validation { error, position } => (
Report::build(
2024-03-20 05:29:07 +00:00
ReportKind::Custom("Validation Error", Color::White),
2024-03-18 07:24:41 +00:00
"input",
position.1,
2024-03-20 05:29:07 +00:00
)
.with_note("This error was detected by the interpreter before running the code."),
2024-03-18 07:24:41 +00:00
Some(error),
2024-03-18 09:47:21 +00:00
position,
2024-03-18 07:24:41 +00:00
),
};
let type_color = Color::Green;
2024-03-18 09:39:09 +00:00
let identifier_color = Color::Blue;
2024-03-18 07:24:41 +00:00
if let Some(validation_error) = validation_error {
match validation_error {
2024-03-17 20:59:52 +00:00
ValidationError::ExpectedBoolean { actual, position } => {
2024-03-18 07:24:41 +00:00
builder.add_label(Label::new(("input", position.0..position.1)).with_message(
format!(
"Expected {} but got {}.",
"boolean".fg(type_color),
actual.fg(type_color)
),
));
2024-03-17 17:36:31 +00:00
}
2024-03-18 07:24:41 +00:00
ValidationError::ExpectedIntegerOrFloat(position) => {
builder.add_label(Label::new(("input", position.0..position.1)).with_message(
format!(
"Expected {} or {}.",
"integer".fg(type_color),
"float".fg(type_color)
),
));
2024-03-17 17:36:31 +00:00
}
ValidationError::RwLockPoison(_) => todo!(),
ValidationError::TypeCheck {
conflict,
actual_position,
expected_position: expected_postion,
} => {
let TypeConflict { actual, expected } = conflict;
2024-03-20 05:29:07 +00:00
builder = builder.with_message("A type conflict was found.");
2024-03-17 17:36:31 +00:00
builder.add_labels([
2024-03-18 07:24:41 +00:00
Label::new(("input", expected_postion.0..expected_postion.1)).with_message(
format!("Type {} established here.", expected.fg(type_color)),
),
Label::new(("input", actual_position.0..actual_position.1))
2024-03-17 22:03:43 +00:00
.with_message(format!("Got type {} here.", actual.fg(type_color))),
2024-03-17 17:36:31 +00:00
]);
}
2024-03-18 09:47:21 +00:00
ValidationError::VariableNotFound(identifier) => builder.add_label(
Label::new(("input", error_position.0..error_position.1)).with_message(
format!(
"Variable {} does not exist in this context.",
identifier.fg(identifier_color)
),
),
),
2024-03-17 22:03:43 +00:00
ValidationError::CannotIndex { r#type, position } => builder.add_label(
2024-03-18 07:24:41 +00:00
Label::new(("input", position.0..position.1))
2024-03-17 22:03:43 +00:00
.with_message(format!("Cannot index into a {}.", r#type.fg(type_color))),
),
2024-03-18 01:07:03 +00:00
ValidationError::CannotIndexWith {
collection_type,
2024-03-18 07:24:41 +00:00
collection_position,
2024-03-18 01:07:03 +00:00
index_type,
2024-03-18 07:24:41 +00:00
index_position,
} => {
builder = builder.with_message(format!(
"Cannot index into {} with {}.",
collection_type.clone().fg(type_color),
index_type.clone().fg(type_color)
));
builder.add_labels([
Label::new(("input", collection_position.0..collection_position.1))
.with_message(format!(
"This has type {}.",
collection_type.fg(type_color),
)),
Label::new(("input", index_position.0..index_position.1))
.with_message(format!("This has type {}.", index_type.fg(type_color),)),
])
}
ValidationError::InterpreterExpectedReturn(_) => todo!(),
2024-03-17 21:39:39 +00:00
ValidationError::ExpectedFunction { .. } => todo!(),
2024-03-18 07:24:41 +00:00
ValidationError::ExpectedValue(_) => todo!(),
ValidationError::PropertyNotFound { .. } => todo!(),
2024-03-18 12:15:30 +00:00
ValidationError::WrongArguments { .. } => todo!(),
2024-03-19 23:33:02 +00:00
ValidationError::TypeNotFound(_) => todo!(),
2024-03-18 07:24:41 +00:00
}
2024-03-07 03:15:35 +00:00
}
2024-03-17 17:36:31 +00:00
builder
2024-03-07 03:15:35 +00:00
}
}
2024-03-06 20:36:58 +00:00
impl From<Rich<'_, char>> for Error {
fn from(error: Rich<'_, char>) -> Self {
Error::Lex {
expected: error.expected().map(|error| error.to_string()).collect(),
2024-03-16 19:01:45 +00:00
span: (error.span().start(), error.span().end()),
2024-03-06 20:36:58 +00:00
}
2024-02-25 18:49:26 +00:00
}
}
2024-03-06 20:36:58 +00:00
impl<'src> From<Rich<'_, Token<'src>>> for Error {
fn from(error: Rich<'_, Token<'src>>) -> Self {
Error::Parse {
expected: error.expected().map(|error| error.to_string()).collect(),
2024-03-16 19:01:45 +00:00
span: (error.span().start(), error.span().end()),
2024-03-06 20:36:58 +00:00
}
}
}
2024-03-17 21:39:39 +00:00
#[derive(Debug)]
2024-02-25 18:49:26 +00:00
pub enum RuntimeError {
2024-03-17 21:39:39 +00:00
Io(io::Error),
2024-02-25 18:49:26 +00:00
RwLockPoison(RwLockPoisonError),
ValidationFailure(ValidationError),
2024-02-25 18:49:26 +00:00
}
impl From<RwLockPoisonError> for RuntimeError {
fn from(error: RwLockPoisonError) -> Self {
RuntimeError::RwLockPoison(error)
}
}
impl From<ValidationError> for RuntimeError {
fn from(error: ValidationError) -> Self {
RuntimeError::ValidationFailure(error)
}
}
2024-03-17 21:39:39 +00:00
impl From<io::Error> for RuntimeError {
fn from(error: io::Error) -> Self {
RuntimeError::Io(error)
}
}
impl PartialEq for RuntimeError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(RuntimeError::Io(_), RuntimeError::Io(_)) => false,
(RuntimeError::RwLockPoison(_), RuntimeError::RwLockPoison(_)) => true,
(RuntimeError::ValidationFailure(left), RuntimeError::ValidationFailure(right)) => {
left == right
}
_ => false,
}
}
}
#[derive(Debug, PartialEq)]
pub enum ValidationError {
2024-03-17 22:03:43 +00:00
CannotIndex {
r#type: Type,
position: SourcePosition,
},
2024-03-18 01:07:03 +00:00
CannotIndexWith {
collection_type: Type,
2024-03-18 07:24:41 +00:00
collection_position: SourcePosition,
2024-03-18 01:07:03 +00:00
index_type: Type,
2024-03-18 07:24:41 +00:00
index_position: SourcePosition,
2024-03-18 01:07:03 +00:00
},
2024-03-17 20:59:52 +00:00
ExpectedBoolean {
actual: Type,
position: SourcePosition,
},
ExpectedFunction {
actual: Type,
position: SourcePosition,
},
2024-03-18 07:24:41 +00:00
ExpectedIntegerOrFloat(SourcePosition),
ExpectedValue(SourcePosition),
InterpreterExpectedReturn(SourcePosition),
2024-03-06 17:15:03 +00:00
RwLockPoison(RwLockPoisonError),
2024-03-17 04:49:01 +00:00
TypeCheck {
/// The mismatch that caused the error.
conflict: TypeConflict,
/// The position of the item that gave the "actual" type.
2024-03-17 11:48:06 +00:00
actual_position: SourcePosition,
2024-03-17 04:49:01 +00:00
/// The position of the item that gave the "expected" type.
2024-03-17 11:48:06 +00:00
expected_position: SourcePosition,
2024-03-17 04:49:01 +00:00
},
2024-03-18 12:15:30 +00:00
WrongArguments {
expected: Vec<Type>,
actual: Vec<Type>,
},
2024-03-18 09:39:09 +00:00
VariableNotFound(Identifier),
2024-03-19 23:33:02 +00:00
TypeNotFound(Identifier),
2024-03-18 07:24:41 +00:00
PropertyNotFound {
identifier: Identifier,
position: SourcePosition,
},
}
impl From<RwLockPoisonError> for ValidationError {
fn from(error: RwLockPoisonError) -> Self {
ValidationError::RwLockPoison(error)
}
}
2024-03-19 22:31:52 +00:00
impl<T> From<PoisonError<T>> for ValidationError {
fn from(_: PoisonError<T>) -> Self {
ValidationError::RwLockPoison(RwLockPoisonError)
}
}
2024-02-25 18:49:26 +00:00
#[derive(Debug, PartialEq)]
pub struct RwLockPoisonError;
impl<T> From<PoisonError<T>> for RwLockPoisonError {
fn from(_: PoisonError<T>) -> Self {
RwLockPoisonError
}
}
2024-03-06 17:15:03 +00:00
#[derive(Debug, PartialEq)]
2024-03-17 04:49:01 +00:00
pub struct TypeConflict {
2024-03-06 17:15:03 +00:00
pub actual: Type,
pub expected: Type,
}