2024-03-20 08:42:13 +00:00
|
|
|
use std::{io, sync::PoisonError};
|
2024-02-25 18:49:26 +00:00
|
|
|
|
2024-03-20 08:42:13 +00:00
|
|
|
use ariadne::{sources, Color, Fmt, Label, Report, 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-20 12:36:18 +00:00
|
|
|
reason: String,
|
2024-03-06 22:32:31 +00:00
|
|
|
},
|
|
|
|
Lex {
|
|
|
|
expected: String,
|
2024-03-16 19:01:45 +00:00
|
|
|
span: (usize, usize),
|
2024-03-20 12:36:18 +00:00
|
|
|
reason: String,
|
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-20 12:36:18 +00:00
|
|
|
pub fn build_report(self, source: &str) -> Result<Vec<u8>, io::Error> {
|
2024-03-18 09:47:21 +00:00
|
|
|
let (mut builder, validation_error, error_position) = match self {
|
2024-03-20 12:36:18 +00:00
|
|
|
Error::Parse {
|
|
|
|
expected,
|
|
|
|
span,
|
|
|
|
reason,
|
|
|
|
} => {
|
|
|
|
let description = if expected.is_empty() {
|
|
|
|
"Invalid token.".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(
|
2024-03-20 20:15:45 +00:00
|
|
|
ReportKind::Custom("Parsing Error", Color::Yellow),
|
2024-03-18 07:24:41 +00:00
|
|
|
"input",
|
|
|
|
span.1,
|
|
|
|
)
|
2024-03-20 12:36:18 +00:00
|
|
|
.with_message(description)
|
2024-03-18 07:24:41 +00:00
|
|
|
.with_label(
|
|
|
|
Label::new(("input", span.0..span.1))
|
2024-03-20 12:36:18 +00:00
|
|
|
.with_message(reason)
|
2024-03-18 07:24:41 +00:00
|
|
|
.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-20 12:36:18 +00:00
|
|
|
Error::Lex {
|
|
|
|
expected,
|
|
|
|
span,
|
|
|
|
reason,
|
|
|
|
} => {
|
|
|
|
let description = if expected.is_empty() {
|
|
|
|
"Invalid character.".to_string()
|
2024-03-18 07:24:41 +00:00
|
|
|
} else {
|
|
|
|
format!("Expected {expected}.")
|
2024-03-07 03:15:35 +00:00
|
|
|
};
|
|
|
|
|
2024-03-18 07:24:41 +00:00
|
|
|
(
|
|
|
|
Report::build(
|
2024-03-20 20:15:45 +00:00
|
|
|
ReportKind::Custom("Lexing Error", Color::Yellow),
|
2024-03-18 07:24:41 +00:00
|
|
|
"input",
|
|
|
|
span.1,
|
|
|
|
)
|
2024-03-20 12:36:18 +00:00
|
|
|
.with_message(description)
|
2024-03-18 07:24:41 +00:00
|
|
|
.with_label(
|
|
|
|
Label::new(("input", span.0..span.1))
|
2024-03-20 12:36:18 +00:00
|
|
|
.with_message(reason)
|
2024-03-18 07:24:41 +00:00
|
|
|
.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 20:15:45 +00:00
|
|
|
ReportKind::Custom("Runtime Error", Color::Red),
|
2024-03-18 07:24:41 +00:00
|
|
|
"input",
|
|
|
|
position.1,
|
2024-03-20 20:15:45 +00:00
|
|
|
)
|
|
|
|
.with_message("An error occured that forced the program to exit.")
|
|
|
|
.with_note(
|
|
|
|
"There may be unexpected side-effects because the program could not finish.",
|
|
|
|
)
|
|
|
|
.with_help(
|
|
|
|
"This is the interpreter's fault. Please submit a bug with this error message.",
|
2024-03-18 07:24:41 +00:00
|
|
|
),
|
|
|
|
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 20:15:45 +00:00
|
|
|
ReportKind::Custom("Validation Error", Color::Magenta),
|
2024-03-18 07:24:41 +00:00
|
|
|
"input",
|
|
|
|
position.1,
|
2024-03-20 05:29:07 +00:00
|
|
|
)
|
2024-03-20 20:15:45 +00:00
|
|
|
.with_message("The syntax is valid but this code is not sound.")
|
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-18 07:24:41 +00:00
|
|
|
}
|
2024-03-07 03:15:35 +00:00
|
|
|
}
|
2024-03-17 17:36:31 +00:00
|
|
|
|
2024-03-20 08:42:13 +00:00
|
|
|
let mut output = Vec::new();
|
|
|
|
|
2024-03-17 17:36:31 +00:00
|
|
|
builder
|
2024-03-20 08:42:13 +00:00
|
|
|
.finish()
|
2024-03-20 12:36:18 +00:00
|
|
|
.write_for_stdout(sources([("input", source)]), &mut output)?;
|
2024-03-20 08:42:13 +00:00
|
|
|
|
2024-03-20 12:36:18 +00:00
|
|
|
Ok(output)
|
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-20 12:36:18 +00:00
|
|
|
reason: error.reason().to_string(),
|
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-20 12:36:18 +00:00
|
|
|
reason: error.reason().to_string(),
|
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),
|
2024-02-29 02:04:38 +00:00
|
|
|
ValidationFailure(ValidationError),
|
2024-02-25 18:49:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<RwLockPoisonError> for RuntimeError {
|
|
|
|
fn from(error: RwLockPoisonError) -> Self {
|
|
|
|
RuntimeError::RwLockPoison(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-20 21:05:37 +00:00
|
|
|
impl<T> From<PoisonError<T>> for RuntimeError {
|
|
|
|
fn from(_: PoisonError<T>) -> Self {
|
|
|
|
RuntimeError::RwLockPoison(RwLockPoisonError)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-29 02:04:38 +00:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-29 02:04:38 +00:00
|
|
|
#[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-18 07:24:41 +00:00
|
|
|
PropertyNotFound {
|
|
|
|
identifier: Identifier,
|
|
|
|
position: SourcePosition,
|
|
|
|
},
|
2024-02-29 02:04:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|