Improve errors
This commit is contained in:
parent
7263507e84
commit
13c95dd12f
@ -39,6 +39,10 @@ impl AbstractNode for FunctionCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
|
fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
|
||||||
|
for expression in &self.arguments {
|
||||||
|
expression.node.validate(_context)?;
|
||||||
|
}
|
||||||
|
|
||||||
let function_node_type = self.function.node.expected_type(_context)?;
|
let function_node_type = self.function.node.expected_type(_context)?;
|
||||||
|
|
||||||
if let Type::Function { .. } = function_node_type {
|
if let Type::Function { .. } = function_node_type {
|
||||||
|
@ -19,18 +19,50 @@ pub enum Math {
|
|||||||
impl AbstractNode for Math {
|
impl AbstractNode for Math {
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||||
match self {
|
match self {
|
||||||
Math::Add(left, _)
|
Math::Add(left, right)
|
||||||
| Math::Subtract(left, _)
|
| Math::Subtract(left, right)
|
||||||
| Math::Multiply(left, _)
|
| Math::Multiply(left, right)
|
||||||
| Math::Divide(left, _)
|
| Math::Divide(left, right)
|
||||||
| Math::Modulo(left, _) => left.node.expected_type(_context),
|
| Math::Modulo(left, right) => {
|
||||||
|
let left_type = left.node.expected_type(_context)?;
|
||||||
|
let right_type = right.node.expected_type(_context)?;
|
||||||
|
|
||||||
|
if let Type::Float = left_type {
|
||||||
|
return Ok(Type::Float);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Type::Float = right_type {
|
||||||
|
return Ok(Type::Float);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(left_type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
||||||
match self {
|
match self {
|
||||||
Math::Add(left, right)
|
Math::Add(left, right) => {
|
||||||
| Math::Subtract(left, right)
|
let left_type = left.node.expected_type(context)?;
|
||||||
|
let right_type = right.node.expected_type(context)?;
|
||||||
|
|
||||||
|
if let Type::Integer | Type::Float | Type::String = left_type {
|
||||||
|
if let Type::Integer | Type::Float | Type::String = right_type {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ValidationError::ExpectedIntegerFloatOrString {
|
||||||
|
actual: right_type,
|
||||||
|
position: right.position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ValidationError::ExpectedIntegerFloatOrString {
|
||||||
|
actual: left_type,
|
||||||
|
position: left.position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Math::Subtract(left, right)
|
||||||
| Math::Multiply(left, right)
|
| Math::Multiply(left, right)
|
||||||
| Math::Divide(left, right)
|
| Math::Divide(left, right)
|
||||||
| Math::Modulo(left, right) => {
|
| Math::Modulo(left, right) => {
|
||||||
@ -91,6 +123,13 @@ impl AbstractNode for Math {
|
|||||||
|
|
||||||
Value::float(sum)
|
Value::float(sum)
|
||||||
}
|
}
|
||||||
|
(ValueInner::String(left), ValueInner::String(right)) => {
|
||||||
|
let mut concatenated = String::with_capacity(left.len() + right.len());
|
||||||
|
|
||||||
|
concatenated.extend(left.chars().chain(right.chars()));
|
||||||
|
|
||||||
|
Value::string(concatenated)
|
||||||
|
}
|
||||||
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
|
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
|
||||||
return Err(RuntimeError::ValidationFailure(
|
return Err(RuntimeError::ValidationFailure(
|
||||||
ValidationError::ExpectedIntegerOrFloat(right.position),
|
ValidationError::ExpectedIntegerOrFloat(right.position),
|
||||||
|
@ -30,190 +30,6 @@ pub enum Error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
|
||||||
pub fn build_report<'id, Id: Debug + Hash + Eq + Clone>(
|
|
||||||
self,
|
|
||||||
source_id: Id,
|
|
||||||
) -> Result<Report<'id, (Id, Range<usize>)>, io::Error> {
|
|
||||||
let (mut builder, validation_error, error_position) = match self {
|
|
||||||
Error::Parse {
|
|
||||||
expected,
|
|
||||||
span,
|
|
||||||
reason,
|
|
||||||
} => {
|
|
||||||
let description = if expected.is_empty() {
|
|
||||||
"Invalid token.".to_string()
|
|
||||||
} else {
|
|
||||||
format!("Expected {expected}.")
|
|
||||||
};
|
|
||||||
|
|
||||||
(
|
|
||||||
Report::build(
|
|
||||||
ReportKind::Custom("Parsing Error", Color::Yellow),
|
|
||||||
source_id.clone(),
|
|
||||||
span.1,
|
|
||||||
)
|
|
||||||
.with_message(description)
|
|
||||||
.with_label(
|
|
||||||
Label::new((source_id.clone(), span.0..span.1))
|
|
||||||
.with_message(reason)
|
|
||||||
.with_color(Color::Red),
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
span.into(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Error::Lex {
|
|
||||||
expected,
|
|
||||||
span,
|
|
||||||
reason,
|
|
||||||
} => {
|
|
||||||
let description = if expected.is_empty() {
|
|
||||||
"Invalid character.".to_string()
|
|
||||||
} else {
|
|
||||||
format!("Expected {expected}.")
|
|
||||||
};
|
|
||||||
|
|
||||||
(
|
|
||||||
Report::build(
|
|
||||||
ReportKind::Custom("Lexing Error", Color::Yellow),
|
|
||||||
source_id.clone(),
|
|
||||||
span.1,
|
|
||||||
)
|
|
||||||
.with_message(description)
|
|
||||||
.with_label(
|
|
||||||
Label::new((source_id.clone(), span.0..span.1))
|
|
||||||
.with_message(reason)
|
|
||||||
.with_color(Color::Red),
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
span.into(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Error::Runtime { error, position } => (
|
|
||||||
Report::build(
|
|
||||||
ReportKind::Custom("Runtime Error", Color::Red),
|
|
||||||
source_id.clone(),
|
|
||||||
position.1,
|
|
||||||
)
|
|
||||||
.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.",
|
|
||||||
),
|
|
||||||
if let RuntimeError::ValidationFailure(validation_error) = error {
|
|
||||||
Some(validation_error)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
position,
|
|
||||||
),
|
|
||||||
Error::Validation { error, position } => (
|
|
||||||
Report::build(
|
|
||||||
ReportKind::Custom("Validation Error", Color::Magenta),
|
|
||||||
source_id.clone(),
|
|
||||||
position.1,
|
|
||||||
)
|
|
||||||
.with_message("The syntax is valid but this code is not sound.")
|
|
||||||
.with_note("This error was detected by the interpreter before running the code."),
|
|
||||||
Some(error),
|
|
||||||
position,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_color = Color::Green;
|
|
||||||
let identifier_color = Color::Blue;
|
|
||||||
|
|
||||||
if let Some(validation_error) = validation_error {
|
|
||||||
match validation_error {
|
|
||||||
ValidationError::ExpectedBoolean { actual, position } => {
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((source_id, position.0..position.1)).with_message(format!(
|
|
||||||
"Expected {} but got {}.",
|
|
||||||
"boolean".fg(type_color),
|
|
||||||
actual.fg(type_color)
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(position) => {
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((source_id, position.0..position.1)).with_message(format!(
|
|
||||||
"Expected {} or {}.",
|
|
||||||
"integer".fg(type_color),
|
|
||||||
"float".fg(type_color)
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ValidationError::RwLockPoison(_) => todo!(),
|
|
||||||
ValidationError::TypeCheck {
|
|
||||||
conflict,
|
|
||||||
actual_position,
|
|
||||||
expected_position: expected_postion,
|
|
||||||
} => {
|
|
||||||
let TypeConflict { actual, expected } = conflict;
|
|
||||||
|
|
||||||
builder = builder.with_message("A type conflict was found.");
|
|
||||||
|
|
||||||
builder.add_labels([
|
|
||||||
Label::new((source_id.clone(), expected_postion.0..expected_postion.1))
|
|
||||||
.with_message(format!(
|
|
||||||
"Type {} established here.",
|
|
||||||
expected.fg(type_color)
|
|
||||||
)),
|
|
||||||
Label::new((source_id, actual_position.0..actual_position.1))
|
|
||||||
.with_message(format!("Got type {} here.", actual.fg(type_color))),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
ValidationError::VariableNotFound(identifier) => builder.add_label(
|
|
||||||
Label::new((source_id, error_position.0..error_position.1)).with_message(
|
|
||||||
format!(
|
|
||||||
"Variable {} does not exist in this context.",
|
|
||||||
identifier.fg(identifier_color)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ValidationError::CannotIndex { r#type, position } => builder.add_label(
|
|
||||||
Label::new((source_id, position.0..position.1))
|
|
||||||
.with_message(format!("Cannot index into a {}.", r#type.fg(type_color))),
|
|
||||||
),
|
|
||||||
ValidationError::CannotIndexWith {
|
|
||||||
collection_type,
|
|
||||||
collection_position,
|
|
||||||
index_type,
|
|
||||||
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((
|
|
||||||
source_id.clone(),
|
|
||||||
collection_position.0..collection_position.1,
|
|
||||||
))
|
|
||||||
.with_message(
|
|
||||||
format!("This has type {}.", collection_type.fg(type_color),),
|
|
||||||
),
|
|
||||||
Label::new((source_id, index_position.0..index_position.1))
|
|
||||||
.with_message(format!("This has type {}.", index_type.fg(type_color),)),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
ValidationError::InterpreterExpectedReturn(_) => todo!(),
|
|
||||||
ValidationError::ExpectedFunction { .. } => todo!(),
|
|
||||||
ValidationError::ExpectedValue(_) => todo!(),
|
|
||||||
ValidationError::PropertyNotFound { .. } => todo!(),
|
|
||||||
ValidationError::WrongArguments { .. } => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(builder.finish())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Rich<'_, char>> for Error {
|
impl From<Rich<'_, char>> for Error {
|
||||||
fn from(error: Rich<'_, char>) -> Self {
|
fn from(error: Rich<'_, char>) -> Self {
|
||||||
Error::Lex {
|
Error::Lex {
|
||||||
@ -299,6 +115,10 @@ pub enum ValidationError {
|
|||||||
position: SourcePosition,
|
position: SourcePosition,
|
||||||
},
|
},
|
||||||
ExpectedIntegerOrFloat(SourcePosition),
|
ExpectedIntegerOrFloat(SourcePosition),
|
||||||
|
ExpectedIntegerFloatOrString {
|
||||||
|
actual: Type,
|
||||||
|
position: SourcePosition,
|
||||||
|
},
|
||||||
ExpectedValue(SourcePosition),
|
ExpectedValue(SourcePosition),
|
||||||
InterpreterExpectedReturn(SourcePosition),
|
InterpreterExpectedReturn(SourcePosition),
|
||||||
RwLockPoison(RwLockPoisonError),
|
RwLockPoison(RwLockPoisonError),
|
||||||
|
@ -3,6 +3,7 @@ use std::{
|
|||||||
io::{stderr, Write},
|
io::{stderr, Write},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::Command,
|
process::Command,
|
||||||
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ariadne::sources;
|
use ariadne::sources;
|
||||||
@ -17,6 +18,8 @@ use reedline::{
|
|||||||
SqliteBackedHistory, Suggestion,
|
SqliteBackedHistory, Suggestion,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
pub fn run_shell(context: Context) {
|
pub fn run_shell(context: Context) {
|
||||||
let mut interpreter = Interpreter::new(context.clone());
|
let mut interpreter = Interpreter::new(context.clone());
|
||||||
let mut keybindings = default_emacs_keybindings();
|
let mut keybindings = default_emacs_keybindings();
|
||||||
@ -87,11 +90,14 @@ pub fn run_shell(context: Context) {
|
|||||||
}
|
}
|
||||||
Ok(None) => {}
|
Ok(None) => {}
|
||||||
Err(errors) => {
|
Err(errors) => {
|
||||||
for error in errors {
|
let source_id = Rc::new("input".to_string());
|
||||||
let report = error.build_report(&"input").unwrap();
|
let reports = Error::Dust { errors }
|
||||||
|
.build_reports(source_id.clone(), &buffer)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for report in reports {
|
||||||
report
|
report
|
||||||
.write_for_stdout(sources([(&"input", buffer.clone())]), stderr())
|
.write_for_stdout(sources([(source_id.clone(), &buffer)]), stderr())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,23 @@
|
|||||||
use ariadne::{Color, Fmt, Label, Report, ReportKind};
|
use ariadne::{Color, Fmt, Label, Report, ReportKind};
|
||||||
use dust_lang::error::{Error as DustError, RuntimeError, TypeConflict, ValidationError};
|
use clap::error;
|
||||||
use std::{fmt::Debug, io, ops::Range, path::Path, rc::Rc};
|
use dust_lang::{
|
||||||
|
abstract_tree::Type,
|
||||||
|
error::{Error as DustError, RuntimeError, TypeConflict, ValidationError},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
fmt::{self, Debug, Display, Formatter},
|
||||||
|
io,
|
||||||
|
ops::Range,
|
||||||
|
path::Path,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Dust {
|
Dust {
|
||||||
errors: Vec<dust_lang::error::Error>,
|
errors: Vec<dust_lang::error::Error>,
|
||||||
},
|
},
|
||||||
|
Io(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
@ -203,6 +214,20 @@ impl Error {
|
|||||||
ValidationError::ExpectedValue(_) => todo!(),
|
ValidationError::ExpectedValue(_) => todo!(),
|
||||||
ValidationError::PropertyNotFound { .. } => todo!(),
|
ValidationError::PropertyNotFound { .. } => todo!(),
|
||||||
ValidationError::WrongArguments { .. } => todo!(),
|
ValidationError::WrongArguments { .. } => todo!(),
|
||||||
|
ValidationError::ExpectedIntegerFloatOrString { actual, position } => {
|
||||||
|
builder = builder.with_message(format!(
|
||||||
|
"Expected an {}, {} or {}.",
|
||||||
|
Type::Integer.fg(type_color),
|
||||||
|
Type::Float.fg(type_color),
|
||||||
|
Type::String.fg(type_color)
|
||||||
|
));
|
||||||
|
|
||||||
|
builder.add_labels([Label::new((
|
||||||
|
source_id.clone(),
|
||||||
|
position.0..position.1,
|
||||||
|
))
|
||||||
|
.with_message(format!("This has type {}.", actual.fg(type_color),))])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let report = builder.finish();
|
let report = builder.finish();
|
||||||
|
@ -1 +1,6 @@
|
|||||||
io.write_line("Hello, world!")
|
io.write_line("Hello, world!")
|
||||||
|
io.write_line("Enter your name...")
|
||||||
|
|
||||||
|
message = io.read_line()
|
||||||
|
|
||||||
|
io.write_line("Hello " + message + "!")
|
||||||
|
Loading…
Reference in New Issue
Block a user