Improve errors

This commit is contained in:
Jeff 2024-03-18 05:39:09 -04:00
parent 1750132ed8
commit 7dcfccf7cb
14 changed files with 508 additions and 413 deletions

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
Context, value::ValueInner,
Context, Value,
}; };
use super::{AbstractTree, Action, Identifier, Statement, Type, WithPosition}; use super::{AbstractTree, Action, Identifier, Statement, Type, WithPosition};
@ -70,40 +71,94 @@ impl AbstractTree for Assignment {
fn run(self, context: &Context) -> Result<Action, RuntimeError> { fn run(self, context: &Context) -> Result<Action, RuntimeError> {
let action = self.statement.node.run(context)?; let action = self.statement.node.run(context)?;
let value = match action { let right = match action {
Action::Return(value) => value, Action::Return(value) => value,
r#break => return Ok(r#break), r#break => return Ok(r#break),
}; };
match self.operator { match self.operator {
AssignmentOperator::Assign => { AssignmentOperator::Assign => {
context.set_value(self.identifier.node, value)?; context.set_value(self.identifier.node, right)?;
} }
AssignmentOperator::AddAssign => { AssignmentOperator::AddAssign => {
if let Some(previous_value) = context.get_value(&self.identifier.node)? { if let Some(left) = context.get_value(&self.identifier.node)? {
let new_value = previous_value.add(&value)?; let new_value = match (left.inner().as_ref(), right.inner().as_ref()) {
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
let sum = left.saturating_add(*right);
Value::integer(sum)
}
(ValueInner::Float(left), ValueInner::Float(right)) => {
let sum = left + right;
Value::float(sum)
}
(ValueInner::Float(left), ValueInner::Integer(right)) => {
let sum = left + *right as f64;
Value::float(sum)
}
(ValueInner::Integer(left), ValueInner::Float(right)) => {
let sum = *left as f64 + right;
Value::float(sum)
}
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(self.statement.position),
))
}
_ => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(self.identifier.position),
))
}
};
context.set_value(self.identifier.node, new_value)?; context.set_value(self.identifier.node, new_value)?;
} else { } else {
return Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::VariableNotFound { ValidationError::VariableNotFound(self.identifier.node),
identifier: self.identifier.node,
position: self.identifier.position,
},
)); ));
} }
} }
AssignmentOperator::SubAssign => { AssignmentOperator::SubAssign => {
if let Some(previous_value) = context.get_value(&self.identifier.node)? { if let Some(left) = context.get_value(&self.identifier.node)? {
let new_value = previous_value.subtract(&value)?; let new_value = match (left.inner().as_ref(), right.inner().as_ref()) {
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
let difference = left.saturating_sub(*right);
Value::integer(difference)
}
(ValueInner::Float(left), ValueInner::Float(right)) => {
let difference = left - right;
Value::float(difference)
}
(ValueInner::Float(left), ValueInner::Integer(right)) => {
let difference = left - *right as f64;
Value::float(difference)
}
(ValueInner::Integer(left), ValueInner::Float(right)) => {
let difference = *left as f64 - right;
Value::float(difference)
}
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(self.statement.position),
))
}
_ => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(self.identifier.position),
))
}
};
context.set_value(self.identifier.node, new_value)?; context.set_value(self.identifier.node, new_value)?;
} else { } else {
return Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::VariableNotFound { ValidationError::VariableNotFound(self.identifier.node),
identifier: self.identifier.node,
position: self.identifier.position,
},
)); ));
} }
} }
@ -118,7 +173,6 @@ mod tests {
use crate::{ use crate::{
abstract_tree::{Expression, ValueNode}, abstract_tree::{Expression, ValueNode},
error::TypeConflict, error::TypeConflict,
Value,
}; };
use super::*; use super::*;

View File

@ -28,10 +28,7 @@ impl AbstractTree for Identifier {
if let Some(r#type) = context.get_type(self)? { if let Some(r#type) = context.get_type(self)? {
Ok(r#type) Ok(r#type)
} else { } else {
Err(ValidationError::VariableNotFound { Err(ValidationError::VariableNotFound(self.clone()))
identifier: todo!(),
position: todo!(),
})
} }
} }
@ -39,10 +36,7 @@ impl AbstractTree for Identifier {
if context.contains(self)? { if context.contains(self)? {
Ok(()) Ok(())
} else { } else {
Err(ValidationError::VariableNotFound { Err(ValidationError::VariableNotFound(self.clone()))
identifier: todo!(),
position: todo!(),
})
} }
} }
@ -53,10 +47,7 @@ impl AbstractTree for Identifier {
Ok(action) Ok(action)
} else { } else {
Err(RuntimeError::ValidationFailure( Err(RuntimeError::ValidationFailure(
ValidationError::VariableNotFound { ValidationError::VariableNotFound(self.clone()),
identifier: todo!(),
position: todo!(),
},
)) ))
} }
} }

View File

@ -64,7 +64,14 @@ impl AbstractTree for IfElse {
} }
fn run(self, _context: &Context) -> Result<Action, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let value = self.if_expression.node.run(_context)?.as_return_value()?; let action = self.if_expression.node.run(_context)?;
let value = if let Action::Return(value) = action {
value
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::InterpreterExpectedReturn(self.if_expression.position),
));
};
if let ValueInner::Boolean(if_boolean) = value.inner().as_ref() { if let ValueInner::Boolean(if_boolean) = value.inner().as_ref() {
if *if_boolean { if *if_boolean {

View File

@ -69,8 +69,22 @@ impl AbstractTree for ListIndex {
} }
fn run(self, _context: &Context) -> Result<Action, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let left_value = self.left.node.run(_context)?.as_return_value()?; let left_action = self.left.node.run(_context)?;
let right_value = self.right.node.run(_context)?.as_return_value()?; let left_value = if let Action::Return(value) = left_action {
value
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::InterpreterExpectedReturn(self.left.position),
));
};
let right_action = self.right.node.run(_context)?;
let right_value = if let Action::Return(value) = right_action {
value
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::InterpreterExpectedReturn(self.left.position),
));
};
if let (Some(list), Some(index)) = (left_value.as_list(), right_value.as_integer()) { if let (Some(list), Some(index)) = (left_value.as_list(), right_value.as_integer()) {
let found_item = list.get(index as usize); let found_item = list.get(index as usize);

View File

@ -84,110 +84,100 @@ impl AbstractTree for Logic {
} }
fn run(self, _context: &Context) -> Result<Action, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let boolean = match self { let run_and_expect_value =
Logic::Equal(left, right) => { |expression: WithPosition<Expression>| -> Result<Value, RuntimeError> {
let left = left.node.run(_context)?.as_return_value()?; let action = expression.node.run(_context)?;
let right = right.node.run(_context)?.as_return_value()?; let value = if let Action::Return(value) = action {
value
left == right
}
Logic::NotEqual(left, right) => {
let left = left.node.run(_context)?.as_return_value()?;
let right = right.node.run(_context)?.as_return_value()?;
left != right
}
Logic::Greater(left, right) => {
let left = left.node.run(_context)?.as_return_value()?;
let right = right.node.run(_context)?.as_return_value()?;
left > right
}
Logic::Less(left, right) => {
let left = left.node.run(_context)?.as_return_value()?;
let right = right.node.run(_context)?.as_return_value()?;
left < right
}
Logic::GreaterOrEqual(left, right) => {
let left = left.node.run(_context)?.as_return_value()?;
let right = right.node.run(_context)?.as_return_value()?;
left >= right
}
Logic::LessOrEqual(left, right) => {
let left = left.node.run(_context)?.as_return_value()?;
let right = right.node.run(_context)?.as_return_value()?;
left <= right
}
Logic::And(left, right) => {
let left_value = left.node.run(_context)?.as_return_value()?;
let right_value = right.node.run(_context)?.as_return_value()?;
let left = if let ValueInner::Boolean(boolean) = left_value.inner().as_ref() {
boolean
} else { } else {
return Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean { ValidationError::InterpreterExpectedReturn(expression.position),
actual: left_value.r#type(),
position: left.position,
},
));
};
let right = if let ValueInner::Boolean(boolean) = right_value.inner().as_ref() {
boolean
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean {
actual: right_value.r#type(),
position: right.position,
},
)); ));
}; };
*left && *right Ok(value)
} };
Logic::Or(left, right) => {
let left_value = left.node.run(_context)?.as_return_value()?;
let right_value = right.node.run(_context)?.as_return_value()?;
let left = if let ValueInner::Boolean(boolean) = left_value.inner().as_ref() { let run_and_expect_boolean =
boolean |expression: WithPosition<Expression>| -> Result<bool, RuntimeError> {
let action = expression.node.run(_context)?;
let value = if let Action::Return(value) = action {
value
} else { } else {
return Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean { ValidationError::InterpreterExpectedReturn(expression.position),
actual: left_value.r#type(),
position: left.position,
},
)); ));
}; };
let right = if let ValueInner::Boolean(boolean) = right_value.inner().as_ref() {
boolean
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean {
actual: right_value.r#type(),
position: right.position,
},
));
};
*left || *right
}
Logic::Not(statement) => {
let value = statement.node.run(_context)?.as_return_value()?;
if let ValueInner::Boolean(boolean) = value.inner().as_ref() { if let ValueInner::Boolean(boolean) = value.inner().as_ref() {
!boolean Ok(*boolean)
} else { } else {
return Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedBoolean { ValidationError::ExpectedBoolean {
actual: value.r#type(), actual: value.r#type(),
position: statement.position, position: expression.position,
}, },
)); ));
} }
};
let boolean = match self {
Logic::Equal(left, right) => {
let (left_value, right_value) =
(run_and_expect_value(left)?, run_and_expect_value(right)?);
left_value == right_value
}
Logic::NotEqual(left, right) => {
let (left_value, right_value) =
(run_and_expect_value(left)?, run_and_expect_value(right)?);
left_value != right_value
}
Logic::Greater(left, right) => {
let (left_value, right_value) =
(run_and_expect_value(left)?, run_and_expect_value(right)?);
left_value > right_value
}
Logic::Less(left, right) => {
let (left_value, right_value) =
(run_and_expect_value(left)?, run_and_expect_value(right)?);
left_value < right_value
}
Logic::GreaterOrEqual(left, right) => {
let (left_value, right_value) =
(run_and_expect_value(left)?, run_and_expect_value(right)?);
left_value >= right_value
}
Logic::LessOrEqual(left, right) => {
let (left_value, right_value) =
(run_and_expect_value(left)?, run_and_expect_value(right)?);
left_value <= right_value
}
Logic::And(left, right) => {
let (left_boolean, right_boolean) = (
run_and_expect_boolean(left)?,
run_and_expect_boolean(right)?,
);
left_boolean && right_boolean
}
Logic::Or(left, right) => {
let (left_boolean, right_boolean) = (
run_and_expect_boolean(left)?,
run_and_expect_boolean(right)?,
);
left_boolean || right_boolean
}
Logic::Not(statement) => {
let boolean = run_and_expect_boolean(statement)?;
!boolean
} }
}; };
@ -203,148 +193,124 @@ mod tests {
#[test] #[test]
fn equal() { fn equal() {
assert!(Logic::Equal( assert_eq!(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Logic::Equal(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
)
.run(&Context::new()),
Ok(Action::Return(Value::boolean(true)))
) )
.run(&Context::new())
.unwrap()
.as_return_value()
.unwrap()
.as_boolean()
.unwrap())
} }
#[test] #[test]
fn not_equal() { fn not_equal() {
assert!(Logic::NotEqual( assert_eq!(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Logic::NotEqual(
Expression::Value(ValueNode::Integer(43)).with_position((0, 0)), Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
Expression::Value(ValueNode::Integer(43)).with_position((0, 0)),
)
.run(&Context::new()),
Ok(Action::Return(Value::boolean(true)))
) )
.run(&Context::new())
.unwrap()
.as_return_value()
.unwrap()
.as_boolean()
.unwrap())
} }
#[test] #[test]
fn greater() { fn greater() {
assert!(Logic::Greater( assert_eq!(
Expression::Value(ValueNode::Integer(43)).with_position((0, 0)), Logic::Greater(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Expression::Value(ValueNode::Integer(43)).with_position((0, 0)),
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
)
.run(&Context::new()),
Ok(Action::Return(Value::boolean(true)))
) )
.run(&Context::new())
.unwrap()
.as_return_value()
.unwrap()
.as_boolean()
.unwrap())
} }
#[test] #[test]
fn less() { fn less() {
assert!(Logic::Less( assert_eq!(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Logic::Less(
Expression::Value(ValueNode::Integer(43)).with_position((0, 0)), Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
Expression::Value(ValueNode::Integer(43)).with_position((0, 0)),
)
.run(&Context::new()),
Ok(Action::Return(Value::boolean(true)))
) )
.run(&Context::new())
.unwrap()
.as_return_value()
.unwrap()
.as_boolean()
.unwrap())
} }
#[test] #[test]
fn greater_or_equal() { fn greater_or_equal() {
assert!(Logic::GreaterOrEqual( assert_eq!(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Logic::GreaterOrEqual(
Expression::Value(ValueNode::Integer(41)).with_position((0, 0)), Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
) Expression::Value(ValueNode::Integer(41)).with_position((0, 0)),
.run(&Context::new()) )
.unwrap() .run(&Context::new()),
.as_return_value() Ok(Action::Return(Value::boolean(true)))
.unwrap() );
.as_boolean()
.unwrap());
assert!(Logic::GreaterOrEqual( assert_eq!(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Logic::GreaterOrEqual(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
) Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
.run(&Context::new()) )
.unwrap() .run(&Context::new()),
.as_return_value() Ok(Action::Return(Value::boolean(true)))
.unwrap() );
.as_boolean()
.unwrap())
} }
#[test] #[test]
fn less_or_equal() { fn less_or_equal() {
assert!(Logic::LessOrEqual( assert_eq!(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Logic::LessOrEqual(
Expression::Value(ValueNode::Integer(43)).with_position((0, 0)), Expression::Value(ValueNode::Integer(41)).with_position((0, 0)),
) Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
.run(&Context::new()) )
.unwrap() .run(&Context::new()),
.as_return_value() Ok(Action::Return(Value::boolean(true)))
.unwrap() );
.as_boolean()
.unwrap());
assert!(Logic::LessOrEqual( assert_eq!(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Logic::LessOrEqual(
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)), Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
) Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
.run(&Context::new()) )
.unwrap() .run(&Context::new()),
.as_return_value() Ok(Action::Return(Value::boolean(true)))
.unwrap() );
.as_boolean()
.unwrap())
} }
#[test] #[test]
fn and() { fn and() {
assert!(Logic::And( assert_eq!(
Expression::Value(ValueNode::Boolean(true)).with_position((0, 0)), Logic::And(
Expression::Value(ValueNode::Boolean(true)).with_position((0, 0)), Expression::Value(ValueNode::Boolean(true)).with_position((0, 0)),
Expression::Value(ValueNode::Boolean(true)).with_position((0, 0)),
)
.run(&Context::new()),
Ok(Action::Return(Value::boolean(true)))
) )
.run(&Context::new())
.unwrap()
.as_return_value()
.unwrap()
.as_boolean()
.unwrap())
} }
#[test] #[test]
fn or() { fn or() {
assert!(Logic::Or( assert_eq!(
Expression::Value(ValueNode::Boolean(true)).with_position((0, 0)), Logic::Or(
Expression::Value(ValueNode::Boolean(false)).with_position((0, 0)), Expression::Value(ValueNode::Boolean(true)).with_position((0, 0)),
Expression::Value(ValueNode::Boolean(false)).with_position((0, 0)),
)
.run(&Context::new()),
Ok(Action::Return(Value::boolean(true)))
) )
.run(&Context::new())
.unwrap()
.as_return_value()
.unwrap()
.as_boolean()
.unwrap())
} }
#[test] #[test]
fn not() { fn not() {
assert!( assert_eq!(
Logic::Not(Expression::Value(ValueNode::Boolean(false)).with_position((0, 0))) Logic::Not(Expression::Value(ValueNode::Boolean(false)).with_position((0, 0)),)
.run(&Context::new()) .run(&Context::new()),
.unwrap() Ok(Action::Return(Value::boolean(true)))
.as_return_value()
.unwrap()
.as_boolean()
.unwrap()
) )
} }
} }

View File

@ -90,7 +90,7 @@ mod tests {
fn complex_loop() { fn complex_loop() {
let action = Block::new(vec![ let action = Block::new(vec![
Statement::Assignment(Assignment::new( Statement::Assignment(Assignment::new(
Identifier::new("i"), Identifier::new("i").with_position((0, 0)),
None, None,
AssignmentOperator::Assign, AssignmentOperator::Assign,
Statement::Expression(Expression::Value(ValueNode::Integer(1))) Statement::Expression(Expression::Value(ValueNode::Integer(1)))
@ -105,7 +105,7 @@ mod tests {
.with_position((10, 15)), .with_position((10, 15)),
Block::new(vec![Statement::Break.with_position((18, 24))]), Block::new(vec![Statement::Break.with_position((18, 24))]),
Some(Block::new(vec![Statement::Assignment(Assignment::new( Some(Block::new(vec![Statement::Assignment(Assignment::new(
Identifier::new("i"), Identifier::new("i").with_position((0, 0)),
None, None,
AssignmentOperator::AddAssign, AssignmentOperator::AddAssign,
Statement::Expression(Expression::Value(ValueNode::Integer(1))) Statement::Expression(Expression::Value(ValueNode::Integer(1)))

View File

@ -39,9 +39,10 @@ impl AbstractTree for MapIndex {
return if let Some(value) = map.get(index_identifier) { return if let Some(value) = map.get(index_identifier) {
Ok(value.r#type()) Ok(value.r#type())
} else { } else {
Err(ValidationError::PropertyNotFound( Err(ValidationError::PropertyNotFound {
collection_identifier.clone(), identifier: index_identifier.clone(),
)) position: self.right.position,
})
}; };
}; };
} }
@ -72,7 +73,7 @@ impl AbstractTree for MapIndex {
Err(ValidationError::CannotIndexWith { Err(ValidationError::CannotIndexWith {
collection_type: left_type, collection_type: left_type,
collection_position: todo!(), collection_position: self.left.position,
index_type: self.right.node.expected_type(_context)?, index_type: self.right.node.expected_type(_context)?,
index_position: self.right.position, index_position: self.right.position,
}) })
@ -100,7 +101,14 @@ impl AbstractTree for MapIndex {
} }
fn run(self, _context: &Context) -> Result<Action, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let collection = self.left.node.run(_context)?.as_return_value()?; let action = self.left.node.run(_context)?;
let collection = if let Action::Return(value) = action {
value
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::InterpreterExpectedReturn(self.left.position),
));
};
if let (ValueInner::Map(map), Expression::Identifier(identifier)) = if let (ValueInner::Map(map), Expression::Identifier(identifier)) =
(collection.inner().as_ref(), &self.right.node) (collection.inner().as_ref(), &self.right.node)
@ -115,7 +123,7 @@ impl AbstractTree for MapIndex {
Err(RuntimeError::ValidationFailure( Err(RuntimeError::ValidationFailure(
ValidationError::CannotIndexWith { ValidationError::CannotIndexWith {
collection_type: collection.r#type(), collection_type: collection.r#type(),
collection_position: todo!(), collection_position: self.left.position,
index_type: self.right.node.expected_type(_context)?, index_type: self.right.node.expected_type(_context)?,
index_position: self.right.position, index_position: self.right.position,
}, },

View File

@ -5,7 +5,7 @@ use crate::{
Value, Value,
}; };
use super::{AbstractTree, Action, Expression, Type, WithPosition}; use super::{AbstractTree, Action, Expression, SourcePosition, Type, WithPosition};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Math { pub enum Math {
@ -37,71 +37,218 @@ impl AbstractTree for Math {
let left_type = left.node.expected_type(context)?; let left_type = left.node.expected_type(context)?;
let right_type = right.node.expected_type(context)?; let right_type = right.node.expected_type(context)?;
match (left_type, right_type) { if let Type::Integer | Type::Float = left_type {
(Type::Integer, Type::Integer) if let Type::Integer | Type::Float = right_type {
| (Type::Float, Type::Float) Ok(())
| (Type::Integer, Type::Float) } else {
| (Type::Float, Type::Integer) => Ok(()), Err(ValidationError::ExpectedIntegerOrFloat(right.position))
_ => Err(ValidationError::ExpectedIntegerOrFloat), }
} else {
Err(ValidationError::ExpectedIntegerOrFloat(left.position))
} }
} }
} }
} }
fn run(self, _context: &Context) -> Result<Action, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let value = match self { let run_and_expect_value =
Math::Add(left, right) => { |expression: Expression, position: SourcePosition| -> Result<Value, RuntimeError> {
let left_value = left.node.run(_context)?.as_return_value()?; let action = expression.run(_context)?;
let right_value = right.node.run(_context)?.as_return_value()?; let value = if let Action::Return(value) = action {
value
left_value.add(&right_value)?
}
Math::Subtract(left, right) => {
let left_value = left.node.run(_context)?.as_return_value()?;
let right_value = right.node.run(_context)?.as_return_value()?;
left_value.subtract(&right_value)?
}
Math::Multiply(left, right) => {
let left_value = left.node.run(_context)?.as_return_value()?;
let right_value = right.node.run(_context)?.as_return_value()?;
if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
(left_value.inner().as_ref(), right_value.inner().as_ref())
{
Value::integer(left * right)
} else { } else {
return Err(RuntimeError::ValidationFailure( return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat, ValidationError::InterpreterExpectedReturn(position),
)); ));
};
Ok(value)
};
let value = match self {
Math::Add(left, right) => {
let left_value = run_and_expect_value(left.node, left.position)?;
let right_value = run_and_expect_value(right.node, right.position)?;
match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
let sum = left.saturating_add(*right);
Value::integer(sum)
}
(ValueInner::Float(left), ValueInner::Float(right)) => {
let sum = left + right;
Value::float(sum)
}
(ValueInner::Float(left), ValueInner::Integer(right)) => {
let sum = left + *right as f64;
Value::float(sum)
}
(ValueInner::Integer(left), ValueInner::Float(right)) => {
let sum = *left as f64 + right;
Value::float(sum)
}
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(right.position),
))
}
_ => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(left.position),
))
}
}
}
Math::Subtract(left, right) => {
let left_value = run_and_expect_value(left.node, left.position)?;
let right_value = run_and_expect_value(right.node, right.position)?;
match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
let difference = left.saturating_sub(*right);
Value::integer(difference)
}
(ValueInner::Float(left), ValueInner::Float(right)) => {
let difference = left - right;
Value::float(difference)
}
(ValueInner::Float(left), ValueInner::Integer(right)) => {
let difference = left - *right as f64;
Value::float(difference)
}
(ValueInner::Integer(left), ValueInner::Float(right)) => {
let difference = *left as f64 - right;
Value::float(difference)
}
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(right.position),
))
}
_ => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(left.position),
))
}
}
}
Math::Multiply(left, right) => {
let left_value = run_and_expect_value(left.node, left.position)?;
let right_value = run_and_expect_value(right.node, right.position)?;
match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
let product = left.saturating_mul(*right);
Value::integer(product)
}
(ValueInner::Float(left), ValueInner::Float(right)) => {
let product = left * right;
Value::float(product)
}
(ValueInner::Float(left), ValueInner::Integer(right)) => {
let product = left * *right as f64;
Value::float(product)
}
(ValueInner::Integer(left), ValueInner::Float(right)) => {
let product = *left as f64 * right;
Value::float(product)
}
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(right.position).into(),
))
}
_ => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(left.position),
))
}
} }
} }
Math::Divide(left, right) => { Math::Divide(left, right) => {
let left_value = left.node.run(_context)?.as_return_value()?; let left_value = run_and_expect_value(left.node, left.position)?;
let right_value = right.node.run(_context)?.as_return_value()?; let right_value = run_and_expect_value(right.node, right.position)?;
if let (ValueInner::Integer(left), ValueInner::Integer(right)) = match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
(left_value.inner().as_ref(), right_value.inner().as_ref()) (ValueInner::Integer(left), ValueInner::Integer(right)) => {
{ let quotient = left.saturating_div(*right);
Value::integer(left / right)
} else { Value::integer(quotient)
return Err(RuntimeError::ValidationFailure( }
ValidationError::ExpectedIntegerOrFloat, (ValueInner::Float(left), ValueInner::Float(right)) => {
)); let quotient = left / right;
Value::float(quotient)
}
(ValueInner::Float(left), ValueInner::Integer(right)) => {
let quotient = left / *right as f64;
Value::float(quotient)
}
(ValueInner::Integer(left), ValueInner::Float(right)) => {
let quotient = *left as f64 / right;
Value::float(quotient)
}
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(right.position).into(),
))
}
_ => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(left.position),
))
}
} }
} }
Math::Modulo(left, right) => { Math::Modulo(left, right) => {
let left_value = left.node.run(_context)?.as_return_value()?; let left_value = run_and_expect_value(left.node, left.position)?;
let right_value = right.node.run(_context)?.as_return_value()?; let right_value = run_and_expect_value(right.node, right.position)?;
if let (ValueInner::Integer(left), ValueInner::Integer(right)) = match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
(left_value.inner().as_ref(), right_value.inner().as_ref()) (ValueInner::Integer(left), ValueInner::Integer(right)) => {
{ let remainder = left % right;
Value::integer(left % right)
} else { Value::integer(remainder)
return Err(RuntimeError::ValidationFailure( }
ValidationError::ExpectedIntegerOrFloat, (ValueInner::Float(left), ValueInner::Float(right)) => {
)); let remainder = left % right;
Value::float(remainder)
}
(ValueInner::Float(left), ValueInner::Integer(right)) => {
let remainder = left % *right as f64;
Value::float(remainder)
}
(ValueInner::Integer(left), ValueInner::Float(right)) => {
let remainder = *left as f64 % right;
Value::float(remainder)
}
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(right.position).into(),
))
}
_ => {
return Err(RuntimeError::ValidationFailure(
ValidationError::ExpectedIntegerOrFloat(left.position),
))
}
} }
} }
}; };

View File

@ -121,7 +121,14 @@ impl AbstractTree for ValueNode {
let mut value_list = Vec::with_capacity(expression_list.len()); let mut value_list = Vec::with_capacity(expression_list.len());
for expression in expression_list { for expression in expression_list {
let value = expression.node.run(_context)?.as_return_value()?; let action = expression.node.run(_context)?;
let value = if let Action::Return(value) = action {
value
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::InterpreterExpectedReturn(expression.position),
));
};
value_list.push(value); value_list.push(value);
} }
@ -132,7 +139,14 @@ impl AbstractTree for ValueNode {
let mut property_map = BTreeMap::new(); let mut property_map = BTreeMap::new();
for (identifier, _type, expression) in property_list { for (identifier, _type, expression) in property_list {
let value = expression.node.run(_context)?.as_return_value()?; let action = expression.node.run(_context)?;
let value = if let Action::Return(value) = action {
value
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::InterpreterExpectedReturn(expression.position),
));
};
property_map.insert(identifier, value); property_map.insert(identifier, value);
} }

View File

@ -42,9 +42,15 @@ impl AbstractTree for While {
fn run(self, _context: &Context) -> Result<Action, RuntimeError> { fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
let get_boolean = || -> Result<Value, RuntimeError> { let get_boolean = || -> Result<Value, RuntimeError> {
let value = self.expression.node.run(_context)?.as_return_value()?; let action = self.expression.node.run(_context)?;
Ok(value) if let Action::Return(value) = action {
Ok(value)
} else {
Err(RuntimeError::ValidationFailure(
ValidationError::InterpreterExpectedReturn(self.expression.position),
))
}
}; };
if let ValueInner::Boolean(boolean) = get_boolean()?.inner().as_ref() { if let ValueInner::Boolean(boolean) = get_boolean()?.inner().as_ref() {
@ -77,7 +83,7 @@ mod tests {
fn simple_while_loop() { fn simple_while_loop() {
let action = Statement::Block(Block::new(vec![ let action = Statement::Block(Block::new(vec![
Statement::Assignment(Assignment::new( Statement::Assignment(Assignment::new(
Identifier::new("i"), Identifier::new("i").with_position((0, 0)),
None, None,
AssignmentOperator::Assign, AssignmentOperator::Assign,
Statement::Expression(Expression::Value(ValueNode::Integer(3))) Statement::Expression(Expression::Value(ValueNode::Integer(3)))
@ -91,7 +97,7 @@ mod tests {
))) )))
.with_position((0, 0)), .with_position((0, 0)),
statements: vec![Statement::Assignment(Assignment::new( statements: vec![Statement::Assignment(Assignment::new(
Identifier::new("i"), Identifier::new("i").with_position((0, 0)),
None, None,
AssignmentOperator::AddAssign, AssignmentOperator::AddAssign,
Statement::Expression(Expression::Value(ValueNode::Integer(1))) Statement::Expression(Expression::Value(ValueNode::Integer(1)))

View File

@ -96,6 +96,7 @@ impl Error {
}; };
let type_color = Color::Green; let type_color = Color::Green;
let identifier_color = Color::Blue;
if let Some(validation_error) = validation_error { if let Some(validation_error) = validation_error {
match validation_error { match validation_error {
@ -133,16 +134,10 @@ impl Error {
.with_message(format!("Got type {} here.", actual.fg(type_color))), .with_message(format!("Got type {} here.", actual.fg(type_color))),
]); ]);
} }
ValidationError::VariableNotFound { ValidationError::VariableNotFound(identifier) => builder.set_message(format!(
identifier, "{} does not exist in this context.",
position, identifier.fg(identifier_color)
} => { )),
builder.add_label(
Label::new(("input", position.0..position.1))
.with_message(format!("The variable {identifier} does not exist."))
.with_priority(1),
);
}
ValidationError::CannotIndex { r#type, position } => builder.add_label( ValidationError::CannotIndex { r#type, position } => builder.add_label(
Label::new(("input", position.0..position.1)) Label::new(("input", position.0..position.1))
.with_message(format!("Cannot index into a {}.", r#type.fg(type_color))), .with_message(format!("Cannot index into a {}.", r#type.fg(type_color))),
@ -270,11 +265,7 @@ pub enum ValidationError {
/// The position of the item that gave the "expected" type. /// The position of the item that gave the "expected" type.
expected_position: SourcePosition, expected_position: SourcePosition,
}, },
VariableNotFound { VariableNotFound(Identifier),
identifier: Identifier,
position: SourcePosition,
},
PropertyNotFound { PropertyNotFound {
identifier: Identifier, identifier: Identifier,
position: SourcePosition, position: SourcePosition,

View File

@ -1,13 +1,12 @@
//! Command line interface for the dust programming language. //! Command line interface for the dust programming language.
use ariadne::{sources, Color, Label, Report, ReportKind, Source}; use ariadne::sources;
use chumsky::span::SimpleSpan;
use clap::Parser; use clap::Parser;
use colored::Colorize; use colored::Colorize;
use std::{fs::read_to_string, io::Write, ops::Range}; use std::{fs::read_to_string, io::Write};
use dust_lang::{context::Context, error::Error, Interpreter}; use dust_lang::{context::Context, Interpreter};
/// Command-line arguments to be parsed. /// Command-line arguments to be parsed.
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -55,58 +54,8 @@ fn main() {
} }
Err(errors) => { Err(errors) => {
for error in errors { for error in errors {
let mut report_builder = match &error { error
Error::Parse { expected, span } => { .build_report()
let message = if expected.is_empty() {
"Invalid token.".to_string()
} else {
format!("Expected {expected}.")
};
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),
)
}
Error::Lex { expected, span } => {
let message = if expected.is_empty() {
"Invalid token.".to_string()
} else {
format!("Expected {expected}.")
};
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),
)
}
Error::Runtime { error, position } => Report::build(
ReportKind::Custom("Dust Error", Color::White),
"input",
position.1,
),
Error::Validation { error, position } => Report::build(
ReportKind::Custom("Dust Error", Color::White),
"input",
position.1,
),
};
report_builder = error.build_report(report_builder);
report_builder
.finish() .finish()
.eprint(sources([("input", &source)])) .eprint(sources([("input", &source)]))
.unwrap() .unwrap()

View File

@ -491,7 +491,7 @@ mod tests {
assert_eq!( assert_eq!(
parse(&lex("foobar : bool = true").unwrap()).unwrap()[0].node, parse(&lex("foobar : bool = true").unwrap()).unwrap()[0].node,
Statement::Assignment(Assignment::new( Statement::Assignment(Assignment::new(
Identifier::new("foobar"), Identifier::new("foobar").with_position((0, 6)),
Some(Type::Boolean.with_position((9, 14))), Some(Type::Boolean.with_position((9, 14))),
AssignmentOperator::Assign, AssignmentOperator::Assign,
Statement::Expression(Expression::Value(ValueNode::Boolean(true))) Statement::Expression(Expression::Value(ValueNode::Boolean(true)))
@ -505,7 +505,7 @@ mod tests {
assert_eq!( assert_eq!(
parse(&lex("foobar: list = []").unwrap()).unwrap()[0].node, parse(&lex("foobar: list = []").unwrap()).unwrap()[0].node,
Statement::Assignment(Assignment::new( Statement::Assignment(Assignment::new(
Identifier::new("foobar"), Identifier::new("foobar").with_position((0, 6)),
Some(Type::List.with_position((8, 13))), Some(Type::List.with_position((8, 13))),
AssignmentOperator::Assign, AssignmentOperator::Assign,
Statement::Expression(Expression::Value(ValueNode::List(vec![]))) Statement::Expression(Expression::Value(ValueNode::List(vec![])))
@ -519,7 +519,7 @@ mod tests {
assert_eq!( assert_eq!(
parse(&lex("foobar : list(bool) = [true]").unwrap()).unwrap()[0].node, parse(&lex("foobar : list(bool) = [true]").unwrap()).unwrap()[0].node,
Statement::Assignment(Assignment::new( Statement::Assignment(Assignment::new(
Identifier::new("foobar"), Identifier::new("foobar").with_position((0, 6)),
Some(Type::ListOf(Box::new(Type::Boolean)).with_position((9, 20))), Some(Type::ListOf(Box::new(Type::Boolean)).with_position((9, 20))),
AssignmentOperator::Assign, AssignmentOperator::Assign,
Statement::Expression(Expression::Value(ValueNode::List(vec![Expression::Value( Statement::Expression(Expression::Value(ValueNode::List(vec![Expression::Value(
@ -536,7 +536,7 @@ mod tests {
assert_eq!( assert_eq!(
parse(&lex("foobar : [bool, str] = [true, '42']").unwrap()).unwrap()[0], parse(&lex("foobar : [bool, str] = [true, '42']").unwrap()).unwrap()[0],
Statement::Assignment(Assignment::new( Statement::Assignment(Assignment::new(
Identifier::new("foobar"), Identifier::new("foobar").with_position((0, 6)),
Some(Type::ListExact(vec![Type::Boolean, Type::String]).with_position((9, 21))), Some(Type::ListExact(vec![Type::Boolean, Type::String]).with_position((9, 21))),
AssignmentOperator::Assign, AssignmentOperator::Assign,
Statement::Expression(Expression::Value(ValueNode::List(vec![ Statement::Expression(Expression::Value(ValueNode::List(vec![
@ -554,7 +554,7 @@ mod tests {
assert_eq!( assert_eq!(
parse(&lex("foobar : () -> any = some_function").unwrap()).unwrap()[0].node, parse(&lex("foobar : () -> any = some_function").unwrap()).unwrap()[0].node,
Statement::Assignment(Assignment::new( Statement::Assignment(Assignment::new(
Identifier::new("foobar"), Identifier::new("foobar").with_position((0, 6)),
Some( Some(
Type::Function { Type::Function {
parameter_types: vec![], parameter_types: vec![],
@ -709,7 +709,7 @@ mod tests {
.with_position((10, 15)), .with_position((10, 15)),
Block::new(vec![Statement::Break.with_position((18, 24))]), Block::new(vec![Statement::Break.with_position((18, 24))]),
Some(Block::new(vec![Statement::Assignment(Assignment::new( Some(Block::new(vec![Statement::Assignment(Assignment::new(
Identifier::new("i"), Identifier::new("i").with_position((33, 34)),
None, None,
AssignmentOperator::AddAssign, AssignmentOperator::AddAssign,
Statement::Expression(Expression::Value(ValueNode::Integer(1))) Statement::Expression(Expression::Value(ValueNode::Integer(1)))
@ -799,7 +799,7 @@ mod tests {
assert_eq!( assert_eq!(
parse(&lex("foobar = 1").unwrap()).unwrap()[0].node, parse(&lex("foobar = 1").unwrap()).unwrap()[0].node,
Statement::Assignment(Assignment::new( Statement::Assignment(Assignment::new(
Identifier::new("foobar"), Identifier::new("foobar").with_position((0, 6)),
None, None,
AssignmentOperator::Assign, AssignmentOperator::Assign,
Statement::Expression(Expression::Value(ValueNode::Integer(1))) Statement::Expression(Expression::Value(ValueNode::Integer(1)))
@ -813,7 +813,7 @@ mod tests {
assert_eq!( assert_eq!(
parse(&lex("foobar: int = 1").unwrap()).unwrap()[0].node, parse(&lex("foobar: int = 1").unwrap()).unwrap()[0].node,
Statement::Assignment(Assignment::new( Statement::Assignment(Assignment::new(
Identifier::new("foobar"), Identifier::new("foobar").with_position((0, 6)),
Some(Type::Integer.with_position((8, 12))), Some(Type::Integer.with_position((8, 12))),
AssignmentOperator::Assign, AssignmentOperator::Assign,
Statement::Expression(Expression::Value(ValueNode::Integer(1))) Statement::Expression(Expression::Value(ValueNode::Integer(1)))

View File

@ -16,7 +16,7 @@ use stanza::{
use crate::{ use crate::{
abstract_tree::{AbstractTree, Action, Block, Identifier, Type, WithPosition}, abstract_tree::{AbstractTree, Action, Block, Identifier, Type, WithPosition},
context::Context, context::Context,
error::{RuntimeError, ValidationError}, error::RuntimeError,
}; };
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -127,58 +127,6 @@ impl Value {
None None
} }
} }
pub fn add(&self, other: &Self) -> Result<Value, ValidationError> {
match (self.inner().as_ref(), other.inner().as_ref()) {
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
let sum = left.saturating_add(*right);
Ok(Value::integer(sum))
}
(ValueInner::Float(left), ValueInner::Float(right)) => {
let sum = left + right;
Ok(Value::float(sum))
}
(ValueInner::Float(left), ValueInner::Integer(right)) => {
let sum = left + *right as f64;
Ok(Value::float(sum))
}
(ValueInner::Integer(left), ValueInner::Float(right)) => {
let sum = *left as f64 + right;
Ok(Value::float(sum))
}
_ => Err(ValidationError::ExpectedIntegerOrFloat),
}
}
pub fn subtract(&self, other: &Self) -> Result<Value, ValidationError> {
match (self.inner().as_ref(), other.inner().as_ref()) {
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
let sum = left.saturating_sub(*right);
Ok(Value::integer(sum))
}
(ValueInner::Float(left), ValueInner::Float(right)) => {
let sum = left - right;
Ok(Value::float(sum))
}
(ValueInner::Float(left), ValueInner::Integer(right)) => {
let sum = left - *right as f64;
Ok(Value::float(sum))
}
(ValueInner::Integer(left), ValueInner::Float(right)) => {
let sum = *left as f64 - right;
Ok(Value::float(sum))
}
_ => Err(ValidationError::ExpectedIntegerOrFloat),
}
}
} }
impl Display for Value { impl Display for Value {