1
0

Add fancy validation errors

This commit is contained in:
Jeff 2024-02-16 20:18:07 -05:00
parent 1094a5662c
commit a46d5bb4ea
7 changed files with 146 additions and 24 deletions

View File

@ -134,7 +134,7 @@ impl AbstractTree for Assignment {
let new_value = match self.operator { let new_value = match self.operator {
AssignmentOperator::PlusEqual => { AssignmentOperator::PlusEqual => {
if let Some(left) = context.get_value(&self.identifier)? { if let Some(left) = context.get_value(&self.identifier)? {
left.add(right)? left.add(right, self.syntax_position)?
} else { } else {
return Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::VariableIdentifierNotFound(self.identifier.clone()), ValidationError::VariableIdentifierNotFound(self.identifier.clone()),
@ -143,7 +143,7 @@ impl AbstractTree for Assignment {
} }
AssignmentOperator::MinusEqual => { AssignmentOperator::MinusEqual => {
if let Some(left) = context.get_value(&self.identifier)? { if let Some(left) = context.get_value(&self.identifier)? {
left.subtract(right)? left.subtract(right, self.syntax_position)?
} else { } else {
return Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::VariableIdentifierNotFound(self.identifier.clone()), ValidationError::VariableIdentifierNotFound(self.identifier.clone()),

View File

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, AssignmentOperator, Context, Format, Identifier, Index, IndexExpression, AbstractTree, AssignmentOperator, Context, Format, Identifier, Index, IndexExpression,
Statement, SyntaxNode, Type, Value, SourcePosition, Statement, SyntaxNode, Type, Value,
}; };
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
@ -11,6 +11,7 @@ pub struct IndexAssignment {
index: Index, index: Index,
operator: AssignmentOperator, operator: AssignmentOperator,
statement: Statement, statement: Statement,
position: SourcePosition,
} }
impl AbstractTree for IndexAssignment { impl AbstractTree for IndexAssignment {
@ -30,6 +31,7 @@ impl AbstractTree for IndexAssignment {
index, index,
operator, operator,
statement, statement,
position: node.range().into(),
}) })
} }
@ -60,7 +62,7 @@ impl AbstractTree for IndexAssignment {
let new_value = match self.operator { let new_value = match self.operator {
AssignmentOperator::PlusEqual => { AssignmentOperator::PlusEqual => {
if let Some(previous_value) = context.get_value(index_identifier)? { if let Some(previous_value) = context.get_value(index_identifier)? {
previous_value.add(value)? previous_value.add(value, self.position)?
} else { } else {
return Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::VariableIdentifierNotFound(index_identifier.clone()), ValidationError::VariableIdentifierNotFound(index_identifier.clone()),
@ -69,7 +71,7 @@ impl AbstractTree for IndexAssignment {
} }
AssignmentOperator::MinusEqual => { AssignmentOperator::MinusEqual => {
if let Some(previous_value) = context.get_value(index_identifier)? { if let Some(previous_value) = context.get_value(index_identifier)? {
previous_value.subtract(value)? previous_value.subtract(value, self.position)?
} else { } else {
Value::none() Value::none()
} }

View File

@ -2,7 +2,8 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, Context, Expression, Format, MathOperator, SyntaxNode, Type, Value, AbstractTree, Context, Expression, Format, MathOperator, SourcePosition, SyntaxNode, Type,
Value,
}; };
/// Abstract representation of a math operation. /// Abstract representation of a math operation.
@ -14,6 +15,7 @@ pub struct Math {
left: Expression, left: Expression,
operator: MathOperator, operator: MathOperator,
right: Expression, right: Expression,
position: SourcePosition,
} }
impl AbstractTree for Math { impl AbstractTree for Math {
@ -33,6 +35,7 @@ impl AbstractTree for Math {
left, left,
operator, operator,
right, right,
position: node.range().into(),
}) })
} }
@ -49,11 +52,11 @@ impl AbstractTree for Math {
let left = self.left.run(source, context)?; let left = self.left.run(source, context)?;
let right = self.right.run(source, context)?; let right = self.right.run(source, context)?;
let value = match self.operator { let value = match self.operator {
MathOperator::Add => left.add(right)?, MathOperator::Add => left.add(right, self.position)?,
MathOperator::Subtract => left.subtract(right)?, MathOperator::Subtract => left.subtract(right, self.position)?,
MathOperator::Multiply => left.multiply(right)?, MathOperator::Multiply => left.multiply(right, self.position)?,
MathOperator::Divide => left.divide(right)?, MathOperator::Divide => left.divide(right, self.position)?,
MathOperator::Modulo => left.modulo(right)?, MathOperator::Modulo => left.modulo(right, self.position)?,
}; };
Ok(value) Ok(value)

View File

@ -45,7 +45,15 @@ impl Error {
"Dust does not recognize this syntax.".dimmed() "Dust does not recognize this syntax.".dimmed()
) )
} }
Error::Validation(_) => todo!(), Error::Validation(validation_error) => {
let report = validation_error.create_report(source);
format!(
"{}\n{}\n{report}",
"Validation Error".bold().yellow().underline(),
"Dust prevented the program from running.".dimmed()
)
}
Error::Runtime(runtime_error) => { Error::Runtime(runtime_error) => {
let report = runtime_error.create_report(source); let report = runtime_error.create_report(source);

View File

@ -7,7 +7,6 @@ use std::{
time, time,
}; };
use colored::Colorize;
use lyneate::Report; use lyneate::Report;
use crate::{SourcePosition, Type, Value}; use crate::{SourcePosition, Type, Value};

View File

@ -1,5 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use lyneate::Report;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{Identifier, SourcePosition, Type, TypeDefinition, Value}; use crate::{Identifier, SourcePosition, Type, TypeDefinition, Value};
@ -12,24 +13,28 @@ pub enum ValidationError {
CannotAdd { CannotAdd {
left: Value, left: Value,
right: Value, right: Value,
position: SourcePosition,
}, },
/// Two value are incompatible for subtraction. /// Two value are incompatible for subtraction.
CannotSubtract { CannotSubtract {
left: Value, left: Value,
right: Value, right: Value,
position: SourcePosition,
}, },
/// Two value are incompatible for multiplication. /// Two value are incompatible for multiplication.
CannotMultiply { CannotMultiply {
left: Value, left: Value,
right: Value, right: Value,
position: SourcePosition,
}, },
/// Two value are incompatible for dividing. /// Two value are incompatible for dividing.
CannotDivide { CannotDivide {
left: Value, left: Value,
right: Value, right: Value,
position: SourcePosition,
}, },
/// The attempted conversion is impossible. /// The attempted conversion is impossible.
@ -146,6 +151,91 @@ pub enum ValidationError {
} }
impl ValidationError { impl ValidationError {
pub fn create_report(&self, source: &str) -> String {
let messages = match self {
ValidationError::CannotAdd {
left,
right,
position,
} => vec![
((
position.start_byte..position.end_byte,
format!(""),
(255, 159, 64),
)),
],
ValidationError::CannotSubtract {
left,
right,
position,
} => todo!(),
ValidationError::CannotMultiply {
left,
right,
position,
} => todo!(),
ValidationError::CannotDivide {
left,
right,
position,
} => todo!(),
ValidationError::ConversionImpossible {
initial_type,
target_type,
} => todo!(),
ValidationError::ExpectedString { actual } => todo!(),
ValidationError::ExpectedInteger { actual } => todo!(),
ValidationError::ExpectedFloat { actual } => todo!(),
ValidationError::ExpectedNumber { actual } => todo!(),
ValidationError::ExpectedNumberOrString { actual } => todo!(),
ValidationError::ExpectedBoolean { actual } => todo!(),
ValidationError::ExpectedList { actual } => todo!(),
ValidationError::ExpectedMinLengthList {
minimum_len,
actual_len,
} => todo!(),
ValidationError::ExpectedFixedLenList {
expected_len,
actual,
} => todo!(),
ValidationError::ExpectedMap { actual } => todo!(),
ValidationError::ExpectedFunction { actual } => todo!(),
ValidationError::ExpectedCollection { actual } => todo!(),
ValidationError::ExpectedBuiltInFunctionArgumentAmount {
function_name,
expected,
actual,
} => todo!(),
ValidationError::ExpectedFunctionArgumentAmount {
expected,
actual,
position,
} => todo!(),
ValidationError::ExpectedFunctionArgumentMinimum {
minumum_expected,
actual,
position,
} => todo!(),
ValidationError::RwLock(_) => todo!(),
ValidationError::TypeCheck {
expected,
actual,
position,
} => vec![(
position.start_byte..position.end_byte,
format!("Type {actual} is incompatible with {expected}."),
(200, 200, 200),
)],
ValidationError::TypeCheckExpectedFunction { actual, position } => todo!(),
ValidationError::VariableIdentifierNotFound(_) => todo!(),
ValidationError::TypeDefinitionNotFound(_) => todo!(),
ValidationError::ExpectedEnumDefintion { actual } => todo!(),
ValidationError::ExpectedStructDefintion { actual } => todo!(),
};
Report::new_byte_spanned(source, messages).display_str()
}
pub fn expect_argument_amount( pub fn expect_argument_amount(
function_name: &str, function_name: &str,
expected: usize, expected: usize,

View File

@ -2,7 +2,7 @@
use crate::{ use crate::{
built_in_values::BuiltInValue, built_in_values::BuiltInValue,
error::{rw_lock_error::RwLockError, RuntimeError, ValidationError}, error::{rw_lock_error::RwLockError, RuntimeError, ValidationError},
Identifier, Type, TypeSpecification, Identifier, SourcePosition, Type, TypeSpecification,
}; };
use serde::{ use serde::{
@ -258,7 +258,7 @@ impl Value {
} }
/// Return the sum of `self` and `other`. /// Return the sum of `self` and `other`.
pub fn add(self, other: Self) -> Result<Value, ValidationError> { pub fn add(self, other: Self, position: SourcePosition) -> Result<Value, ValidationError> {
match (self, other) { match (self, other) {
(Value::Float(left), Value::Float(right)) => Ok(Value::Float(left + right)), (Value::Float(left), Value::Float(right)) => Ok(Value::Float(left + right)),
(Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left + right as f64)), (Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left + right as f64)),
@ -270,51 +270,71 @@ impl Value {
Ok(Value::List(list)) Ok(Value::List(list))
} }
(Value::String(left), Value::String(right)) => Ok(Value::String(left + &right)), (Value::String(left), Value::String(right)) => Ok(Value::String(left + &right)),
(left, right) => Err(ValidationError::CannotAdd { left, right }), (left, right) => Err(ValidationError::CannotAdd {
left,
right,
position,
}),
} }
} }
/// Return the difference of `self` and `other`. /// Return the difference of `self` and `other`.
pub fn subtract(self, other: Self) -> Result<Value, ValidationError> { pub fn subtract(self, other: Self, position: SourcePosition) -> Result<Value, ValidationError> {
match (self, other) { match (self, other) {
(Value::Float(left), Value::Float(right)) => Ok(Value::Float(left - right)), (Value::Float(left), Value::Float(right)) => Ok(Value::Float(left - right)),
(Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left - right as f64)), (Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left - right as f64)),
(Value::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 - right)), (Value::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 - right)),
(Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left - right)), (Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left - right)),
(left, right) => Err(ValidationError::CannotSubtract { left, right }), (left, right) => Err(ValidationError::CannotSubtract {
left,
right,
position,
}),
} }
} }
/// Return the product of `self` and `other`. /// Return the product of `self` and `other`.
pub fn multiply(self, other: Self) -> Result<Value, ValidationError> { pub fn multiply(self, other: Self, position: SourcePosition) -> Result<Value, ValidationError> {
match (self, other) { match (self, other) {
(Value::Float(left), Value::Float(right)) => Ok(Value::Float(left * right)), (Value::Float(left), Value::Float(right)) => Ok(Value::Float(left * right)),
(Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left * right as f64)), (Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left * right as f64)),
(Value::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 * right)), (Value::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 * right)),
(Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left * right)), (Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left * right)),
(left, right) => Err(ValidationError::CannotMultiply { left, right }), (left, right) => Err(ValidationError::CannotMultiply {
left,
right,
position,
}),
} }
} }
/// Return the quotient of `self` and `other`. /// Return the quotient of `self` and `other`.
pub fn divide(self, other: Self) -> Result<Value, ValidationError> { pub fn divide(self, other: Self, position: SourcePosition) -> Result<Value, ValidationError> {
match (self, other) { match (self, other) {
(Value::Float(left), Value::Float(right)) => Ok(Value::Float(left / right)), (Value::Float(left), Value::Float(right)) => Ok(Value::Float(left / right)),
(Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left / right as f64)), (Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left / right as f64)),
(Value::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 / right)), (Value::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 / right)),
(Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left / right)), (Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left / right)),
(left, right) => Err(ValidationError::CannotDivide { left, right }), (left, right) => Err(ValidationError::CannotDivide {
left,
right,
position,
}),
} }
} }
/// Return the remainder after diving `self` and `other`. /// Return the remainder after diving `self` and `other`.
pub fn modulo(self, other: Self) -> Result<Value, ValidationError> { pub fn modulo(self, other: Self, position: SourcePosition) -> Result<Value, ValidationError> {
match (self, other) { match (self, other) {
(Value::Float(left), Value::Float(right)) => Ok(Value::Float(left % right)), (Value::Float(left), Value::Float(right)) => Ok(Value::Float(left % right)),
(Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left % right as f64)), (Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left % right as f64)),
(Value::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 % right)), (Value::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 % right)),
(Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left % right)), (Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left % right)),
(left, right) => Err(ValidationError::CannotDivide { left, right }), (left, right) => Err(ValidationError::CannotDivide {
left,
right,
position,
}),
} }
} }
} }