From a46d5bb4eaad88f590c3e8c04eec398e86e297da Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 16 Feb 2024 20:18:07 -0500 Subject: [PATCH] Add fancy validation errors --- src/abstract_tree/assignment.rs | 4 +- src/abstract_tree/index_assignment.rs | 8 ++- src/abstract_tree/math.rs | 15 +++-- src/error/mod.rs | 10 ++- src/error/runtime_error.rs | 1 - src/error/validation_error.rs | 90 +++++++++++++++++++++++++++ src/value/mod.rs | 42 +++++++++---- 7 files changed, 146 insertions(+), 24 deletions(-) diff --git a/src/abstract_tree/assignment.rs b/src/abstract_tree/assignment.rs index c9a7c9e..3dc7830 100644 --- a/src/abstract_tree/assignment.rs +++ b/src/abstract_tree/assignment.rs @@ -134,7 +134,7 @@ impl AbstractTree for Assignment { let new_value = match self.operator { AssignmentOperator::PlusEqual => { if let Some(left) = context.get_value(&self.identifier)? { - left.add(right)? + left.add(right, self.syntax_position)? } else { return Err(RuntimeError::ValidationFailure( ValidationError::VariableIdentifierNotFound(self.identifier.clone()), @@ -143,7 +143,7 @@ impl AbstractTree for Assignment { } AssignmentOperator::MinusEqual => { if let Some(left) = context.get_value(&self.identifier)? { - left.subtract(right)? + left.subtract(right, self.syntax_position)? } else { return Err(RuntimeError::ValidationFailure( ValidationError::VariableIdentifierNotFound(self.identifier.clone()), diff --git a/src/abstract_tree/index_assignment.rs b/src/abstract_tree/index_assignment.rs index b2ba926..1335929 100644 --- a/src/abstract_tree/index_assignment.rs +++ b/src/abstract_tree/index_assignment.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::{ error::{RuntimeError, SyntaxError, ValidationError}, 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)] @@ -11,6 +11,7 @@ pub struct IndexAssignment { index: Index, operator: AssignmentOperator, statement: Statement, + position: SourcePosition, } impl AbstractTree for IndexAssignment { @@ -30,6 +31,7 @@ impl AbstractTree for IndexAssignment { index, operator, statement, + position: node.range().into(), }) } @@ -60,7 +62,7 @@ impl AbstractTree for IndexAssignment { let new_value = match self.operator { AssignmentOperator::PlusEqual => { if let Some(previous_value) = context.get_value(index_identifier)? { - previous_value.add(value)? + previous_value.add(value, self.position)? } else { return Err(RuntimeError::ValidationFailure( ValidationError::VariableIdentifierNotFound(index_identifier.clone()), @@ -69,7 +71,7 @@ impl AbstractTree for IndexAssignment { } AssignmentOperator::MinusEqual => { if let Some(previous_value) = context.get_value(index_identifier)? { - previous_value.subtract(value)? + previous_value.subtract(value, self.position)? } else { Value::none() } diff --git a/src/abstract_tree/math.rs b/src/abstract_tree/math.rs index 0f24230..d7096a3 100644 --- a/src/abstract_tree/math.rs +++ b/src/abstract_tree/math.rs @@ -2,7 +2,8 @@ use serde::{Deserialize, Serialize}; use crate::{ 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. @@ -14,6 +15,7 @@ pub struct Math { left: Expression, operator: MathOperator, right: Expression, + position: SourcePosition, } impl AbstractTree for Math { @@ -33,6 +35,7 @@ impl AbstractTree for Math { left, operator, right, + position: node.range().into(), }) } @@ -49,11 +52,11 @@ impl AbstractTree for Math { let left = self.left.run(source, context)?; let right = self.right.run(source, context)?; let value = match self.operator { - MathOperator::Add => left.add(right)?, - MathOperator::Subtract => left.subtract(right)?, - MathOperator::Multiply => left.multiply(right)?, - MathOperator::Divide => left.divide(right)?, - MathOperator::Modulo => left.modulo(right)?, + MathOperator::Add => left.add(right, self.position)?, + MathOperator::Subtract => left.subtract(right, self.position)?, + MathOperator::Multiply => left.multiply(right, self.position)?, + MathOperator::Divide => left.divide(right, self.position)?, + MathOperator::Modulo => left.modulo(right, self.position)?, }; Ok(value) diff --git a/src/error/mod.rs b/src/error/mod.rs index 5c88de5..16a6bc1 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -45,7 +45,15 @@ impl Error { "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) => { let report = runtime_error.create_report(source); diff --git a/src/error/runtime_error.rs b/src/error/runtime_error.rs index 2878700..2cd082d 100644 --- a/src/error/runtime_error.rs +++ b/src/error/runtime_error.rs @@ -7,7 +7,6 @@ use std::{ time, }; -use colored::Colorize; use lyneate::Report; use crate::{SourcePosition, Type, Value}; diff --git a/src/error/validation_error.rs b/src/error/validation_error.rs index aafa44e..d1cda36 100644 --- a/src/error/validation_error.rs +++ b/src/error/validation_error.rs @@ -1,5 +1,6 @@ use std::fmt::{self, Display, Formatter}; +use lyneate::Report; use serde::{Deserialize, Serialize}; use crate::{Identifier, SourcePosition, Type, TypeDefinition, Value}; @@ -12,24 +13,28 @@ pub enum ValidationError { CannotAdd { left: Value, right: Value, + position: SourcePosition, }, /// Two value are incompatible for subtraction. CannotSubtract { left: Value, right: Value, + position: SourcePosition, }, /// Two value are incompatible for multiplication. CannotMultiply { left: Value, right: Value, + position: SourcePosition, }, /// Two value are incompatible for dividing. CannotDivide { left: Value, right: Value, + position: SourcePosition, }, /// The attempted conversion is impossible. @@ -146,6 +151,91 @@ pub enum 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( function_name: &str, expected: usize, diff --git a/src/value/mod.rs b/src/value/mod.rs index fcbac3f..7ced6a6 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -2,7 +2,7 @@ use crate::{ built_in_values::BuiltInValue, error::{rw_lock_error::RwLockError, RuntimeError, ValidationError}, - Identifier, Type, TypeSpecification, + Identifier, SourcePosition, Type, TypeSpecification, }; use serde::{ @@ -258,7 +258,7 @@ impl Value { } /// Return the sum of `self` and `other`. - pub fn add(self, other: Self) -> Result { + pub fn add(self, other: Self, position: SourcePosition) -> Result { match (self, other) { (Value::Float(left), Value::Float(right)) => Ok(Value::Float(left + right)), (Value::Float(left), Value::Integer(right)) => Ok(Value::Float(left + right as f64)), @@ -270,51 +270,71 @@ impl Value { Ok(Value::List(list)) } (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`. - pub fn subtract(self, other: Self) -> Result { + pub fn subtract(self, other: Self, position: SourcePosition) -> Result { match (self, other) { (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::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 - 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`. - pub fn multiply(self, other: Self) -> Result { + pub fn multiply(self, other: Self, position: SourcePosition) -> Result { match (self, other) { (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::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 * 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`. - pub fn divide(self, other: Self) -> Result { + pub fn divide(self, other: Self, position: SourcePosition) -> Result { match (self, other) { (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::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 / 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`. - pub fn modulo(self, other: Self) -> Result { + pub fn modulo(self, other: Self, position: SourcePosition) -> Result { match (self, other) { (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::Integer(left), Value::Float(right)) => Ok(Value::Float(left as f64 % 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, + }), } } }