Improve errors
This commit is contained in:
parent
1750132ed8
commit
7dcfccf7cb
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
error::{RuntimeError, ValidationError},
|
||||
Context,
|
||||
value::ValueInner,
|
||||
Context, Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Action, Identifier, Statement, Type, WithPosition};
|
||||
@ -70,40 +71,94 @@ impl AbstractTree for Assignment {
|
||||
|
||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||
let action = self.statement.node.run(context)?;
|
||||
let value = match action {
|
||||
let right = match action {
|
||||
Action::Return(value) => value,
|
||||
r#break => return Ok(r#break),
|
||||
};
|
||||
|
||||
match self.operator {
|
||||
AssignmentOperator::Assign => {
|
||||
context.set_value(self.identifier.node, value)?;
|
||||
context.set_value(self.identifier.node, right)?;
|
||||
}
|
||||
AssignmentOperator::AddAssign => {
|
||||
if let Some(previous_value) = context.get_value(&self.identifier.node)? {
|
||||
let new_value = previous_value.add(&value)?;
|
||||
if let Some(left) = context.get_value(&self.identifier.node)? {
|
||||
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)?;
|
||||
} else {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::VariableNotFound {
|
||||
identifier: self.identifier.node,
|
||||
position: self.identifier.position,
|
||||
},
|
||||
ValidationError::VariableNotFound(self.identifier.node),
|
||||
));
|
||||
}
|
||||
}
|
||||
AssignmentOperator::SubAssign => {
|
||||
if let Some(previous_value) = context.get_value(&self.identifier.node)? {
|
||||
let new_value = previous_value.subtract(&value)?;
|
||||
if let Some(left) = context.get_value(&self.identifier.node)? {
|
||||
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)?;
|
||||
} else {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::VariableNotFound {
|
||||
identifier: self.identifier.node,
|
||||
position: self.identifier.position,
|
||||
},
|
||||
ValidationError::VariableNotFound(self.identifier.node),
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -118,7 +173,6 @@ mod tests {
|
||||
use crate::{
|
||||
abstract_tree::{Expression, ValueNode},
|
||||
error::TypeConflict,
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
@ -28,10 +28,7 @@ impl AbstractTree for Identifier {
|
||||
if let Some(r#type) = context.get_type(self)? {
|
||||
Ok(r#type)
|
||||
} else {
|
||||
Err(ValidationError::VariableNotFound {
|
||||
identifier: todo!(),
|
||||
position: todo!(),
|
||||
})
|
||||
Err(ValidationError::VariableNotFound(self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,10 +36,7 @@ impl AbstractTree for Identifier {
|
||||
if context.contains(self)? {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ValidationError::VariableNotFound {
|
||||
identifier: todo!(),
|
||||
position: todo!(),
|
||||
})
|
||||
Err(ValidationError::VariableNotFound(self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,10 +47,7 @@ impl AbstractTree for Identifier {
|
||||
Ok(action)
|
||||
} else {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::VariableNotFound {
|
||||
identifier: todo!(),
|
||||
position: todo!(),
|
||||
},
|
||||
ValidationError::VariableNotFound(self.clone()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,14 @@ impl AbstractTree for IfElse {
|
||||
}
|
||||
|
||||
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 *if_boolean {
|
||||
|
@ -69,8 +69,22 @@ impl AbstractTree for ListIndex {
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let left_value = self.left.node.run(_context)?.as_return_value()?;
|
||||
let right_value = self.right.node.run(_context)?.as_return_value()?;
|
||||
let left_action = self.left.node.run(_context)?;
|
||||
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()) {
|
||||
let found_item = list.get(index as usize);
|
||||
|
@ -84,110 +84,100 @@ impl AbstractTree for Logic {
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let boolean = match self {
|
||||
Logic::Equal(left, right) => {
|
||||
let left = left.node.run(_context)?.as_return_value()?;
|
||||
let right = right.node.run(_context)?.as_return_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
|
||||
let run_and_expect_value =
|
||||
|expression: WithPosition<Expression>| -> Result<Value, RuntimeError> {
|
||||
let action = expression.node.run(_context)?;
|
||||
let value = if let Action::Return(value) = action {
|
||||
value
|
||||
} else {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedBoolean {
|
||||
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,
|
||||
},
|
||||
ValidationError::InterpreterExpectedReturn(expression.position),
|
||||
));
|
||||
};
|
||||
|
||||
*left && *right
|
||||
}
|
||||
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() {
|
||||
boolean
|
||||
} else {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedBoolean {
|
||||
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,
|
||||
},
|
||||
));
|
||||
Ok(value)
|
||||
};
|
||||
|
||||
*left || *right
|
||||
}
|
||||
Logic::Not(statement) => {
|
||||
let value = statement.node.run(_context)?.as_return_value()?;
|
||||
let run_and_expect_boolean =
|
||||
|expression: WithPosition<Expression>| -> Result<bool, RuntimeError> {
|
||||
let action = expression.node.run(_context)?;
|
||||
let value = if let Action::Return(value) = action {
|
||||
value
|
||||
} else {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::InterpreterExpectedReturn(expression.position),
|
||||
));
|
||||
};
|
||||
|
||||
if let ValueInner::Boolean(boolean) = value.inner().as_ref() {
|
||||
!boolean
|
||||
Ok(*boolean)
|
||||
} else {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedBoolean {
|
||||
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]
|
||||
fn equal() {
|
||||
assert!(Logic::Equal(
|
||||
assert_eq!(
|
||||
Logic::Equal(
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal() {
|
||||
assert!(Logic::NotEqual(
|
||||
assert_eq!(
|
||||
Logic::NotEqual(
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
|
||||
Expression::Value(ValueNode::Integer(43)).with_position((0, 0)),
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater() {
|
||||
assert!(Logic::Greater(
|
||||
assert_eq!(
|
||||
Logic::Greater(
|
||||
Expression::Value(ValueNode::Integer(43)).with_position((0, 0)),
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less() {
|
||||
assert!(Logic::Less(
|
||||
assert_eq!(
|
||||
Logic::Less(
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
|
||||
Expression::Value(ValueNode::Integer(43)).with_position((0, 0)),
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_or_equal() {
|
||||
assert!(Logic::GreaterOrEqual(
|
||||
assert_eq!(
|
||||
Logic::GreaterOrEqual(
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
|
||||
Expression::Value(ValueNode::Integer(41)).with_position((0, 0)),
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap());
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
);
|
||||
|
||||
assert!(Logic::GreaterOrEqual(
|
||||
assert_eq!(
|
||||
Logic::GreaterOrEqual(
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_or_equal() {
|
||||
assert!(Logic::LessOrEqual(
|
||||
assert_eq!(
|
||||
Logic::LessOrEqual(
|
||||
Expression::Value(ValueNode::Integer(41)).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())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap());
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
);
|
||||
|
||||
assert!(Logic::LessOrEqual(
|
||||
assert_eq!(
|
||||
Logic::LessOrEqual(
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
|
||||
Expression::Value(ValueNode::Integer(42)).with_position((0, 0)),
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn and() {
|
||||
assert!(Logic::And(
|
||||
assert_eq!(
|
||||
Logic::And(
|
||||
Expression::Value(ValueNode::Boolean(true)).with_position((0, 0)),
|
||||
Expression::Value(ValueNode::Boolean(true)).with_position((0, 0)),
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn or() {
|
||||
assert!(Logic::Or(
|
||||
assert_eq!(
|
||||
Logic::Or(
|
||||
Expression::Value(ValueNode::Boolean(true)).with_position((0, 0)),
|
||||
Expression::Value(ValueNode::Boolean(false)).with_position((0, 0)),
|
||||
)
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap())
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not() {
|
||||
assert!(
|
||||
Logic::Not(Expression::Value(ValueNode::Boolean(false)).with_position((0, 0)))
|
||||
.run(&Context::new())
|
||||
.unwrap()
|
||||
.as_return_value()
|
||||
.unwrap()
|
||||
.as_boolean()
|
||||
.unwrap()
|
||||
assert_eq!(
|
||||
Logic::Not(Expression::Value(ValueNode::Boolean(false)).with_position((0, 0)),)
|
||||
.run(&Context::new()),
|
||||
Ok(Action::Return(Value::boolean(true)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ mod tests {
|
||||
fn complex_loop() {
|
||||
let action = Block::new(vec![
|
||||
Statement::Assignment(Assignment::new(
|
||||
Identifier::new("i"),
|
||||
Identifier::new("i").with_position((0, 0)),
|
||||
None,
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Value(ValueNode::Integer(1)))
|
||||
@ -105,7 +105,7 @@ mod tests {
|
||||
.with_position((10, 15)),
|
||||
Block::new(vec![Statement::Break.with_position((18, 24))]),
|
||||
Some(Block::new(vec![Statement::Assignment(Assignment::new(
|
||||
Identifier::new("i"),
|
||||
Identifier::new("i").with_position((0, 0)),
|
||||
None,
|
||||
AssignmentOperator::AddAssign,
|
||||
Statement::Expression(Expression::Value(ValueNode::Integer(1)))
|
||||
|
@ -39,9 +39,10 @@ impl AbstractTree for MapIndex {
|
||||
return if let Some(value) = map.get(index_identifier) {
|
||||
Ok(value.r#type())
|
||||
} else {
|
||||
Err(ValidationError::PropertyNotFound(
|
||||
collection_identifier.clone(),
|
||||
))
|
||||
Err(ValidationError::PropertyNotFound {
|
||||
identifier: index_identifier.clone(),
|
||||
position: self.right.position,
|
||||
})
|
||||
};
|
||||
};
|
||||
}
|
||||
@ -72,7 +73,7 @@ impl AbstractTree for MapIndex {
|
||||
|
||||
Err(ValidationError::CannotIndexWith {
|
||||
collection_type: left_type,
|
||||
collection_position: todo!(),
|
||||
collection_position: self.left.position,
|
||||
index_type: self.right.node.expected_type(_context)?,
|
||||
index_position: self.right.position,
|
||||
})
|
||||
@ -100,7 +101,14 @@ impl AbstractTree for MapIndex {
|
||||
}
|
||||
|
||||
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)) =
|
||||
(collection.inner().as_ref(), &self.right.node)
|
||||
@ -115,7 +123,7 @@ impl AbstractTree for MapIndex {
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::CannotIndexWith {
|
||||
collection_type: collection.r#type(),
|
||||
collection_position: todo!(),
|
||||
collection_position: self.left.position,
|
||||
index_type: self.right.node.expected_type(_context)?,
|
||||
index_position: self.right.position,
|
||||
},
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
Value,
|
||||
};
|
||||
|
||||
use super::{AbstractTree, Action, Expression, Type, WithPosition};
|
||||
use super::{AbstractTree, Action, Expression, SourcePosition, Type, WithPosition};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Math {
|
||||
@ -37,71 +37,218 @@ impl AbstractTree for Math {
|
||||
let left_type = left.node.expected_type(context)?;
|
||||
let right_type = right.node.expected_type(context)?;
|
||||
|
||||
match (left_type, right_type) {
|
||||
(Type::Integer, Type::Integer)
|
||||
| (Type::Float, Type::Float)
|
||||
| (Type::Integer, Type::Float)
|
||||
| (Type::Float, Type::Integer) => Ok(()),
|
||||
_ => Err(ValidationError::ExpectedIntegerOrFloat),
|
||||
if let Type::Integer | Type::Float = left_type {
|
||||
if let Type::Integer | Type::Float = right_type {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ValidationError::ExpectedIntegerOrFloat(right.position))
|
||||
}
|
||||
} else {
|
||||
Err(ValidationError::ExpectedIntegerOrFloat(left.position))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let value = match self {
|
||||
Math::Add(left, right) => {
|
||||
let left_value = left.node.run(_context)?.as_return_value()?;
|
||||
let right_value = right.node.run(_context)?.as_return_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)
|
||||
let run_and_expect_value =
|
||||
|expression: Expression, position: SourcePosition| -> Result<Value, RuntimeError> {
|
||||
let action = expression.run(_context)?;
|
||||
let value = if let Action::Return(value) = action {
|
||||
value
|
||||
} else {
|
||||
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) => {
|
||||
let left_value = left.node.run(_context)?.as_return_value()?;
|
||||
let right_value = right.node.run(_context)?.as_return_value()?;
|
||||
let left_value = run_and_expect_value(left.node, left.position)?;
|
||||
let right_value = run_and_expect_value(right.node, right.position)?;
|
||||
|
||||
if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
|
||||
(left_value.inner().as_ref(), right_value.inner().as_ref())
|
||||
{
|
||||
Value::integer(left / right)
|
||||
} else {
|
||||
match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
|
||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
||||
let quotient = left.saturating_div(*right);
|
||||
|
||||
Value::integer(quotient)
|
||||
}
|
||||
(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,
|
||||
));
|
||||
ValidationError::ExpectedIntegerOrFloat(right.position).into(),
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedIntegerOrFloat(left.position),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Math::Modulo(left, right) => {
|
||||
let left_value = left.node.run(_context)?.as_return_value()?;
|
||||
let right_value = right.node.run(_context)?.as_return_value()?;
|
||||
let left_value = run_and_expect_value(left.node, left.position)?;
|
||||
let right_value = run_and_expect_value(right.node, right.position)?;
|
||||
|
||||
if let (ValueInner::Integer(left), ValueInner::Integer(right)) =
|
||||
(left_value.inner().as_ref(), right_value.inner().as_ref())
|
||||
{
|
||||
Value::integer(left % right)
|
||||
} else {
|
||||
match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
|
||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
||||
let remainder = left % right;
|
||||
|
||||
Value::integer(remainder)
|
||||
}
|
||||
(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,
|
||||
));
|
||||
ValidationError::ExpectedIntegerOrFloat(right.position).into(),
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedIntegerOrFloat(left.position),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -121,7 +121,14 @@ impl AbstractTree for ValueNode {
|
||||
let mut value_list = Vec::with_capacity(expression_list.len());
|
||||
|
||||
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);
|
||||
}
|
||||
@ -132,7 +139,14 @@ impl AbstractTree for ValueNode {
|
||||
let mut property_map = BTreeMap::new();
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -42,9 +42,15 @@ impl AbstractTree for While {
|
||||
|
||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||
let get_boolean = || -> Result<Value, RuntimeError> {
|
||||
let value = self.expression.node.run(_context)?.as_return_value()?;
|
||||
let action = self.expression.node.run(_context)?;
|
||||
|
||||
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() {
|
||||
@ -77,7 +83,7 @@ mod tests {
|
||||
fn simple_while_loop() {
|
||||
let action = Statement::Block(Block::new(vec![
|
||||
Statement::Assignment(Assignment::new(
|
||||
Identifier::new("i"),
|
||||
Identifier::new("i").with_position((0, 0)),
|
||||
None,
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Value(ValueNode::Integer(3)))
|
||||
@ -91,7 +97,7 @@ mod tests {
|
||||
)))
|
||||
.with_position((0, 0)),
|
||||
statements: vec![Statement::Assignment(Assignment::new(
|
||||
Identifier::new("i"),
|
||||
Identifier::new("i").with_position((0, 0)),
|
||||
None,
|
||||
AssignmentOperator::AddAssign,
|
||||
Statement::Expression(Expression::Value(ValueNode::Integer(1)))
|
||||
|
21
src/error.rs
21
src/error.rs
@ -96,6 +96,7 @@ impl Error {
|
||||
};
|
||||
|
||||
let type_color = Color::Green;
|
||||
let identifier_color = Color::Blue;
|
||||
|
||||
if let Some(validation_error) = validation_error {
|
||||
match validation_error {
|
||||
@ -133,16 +134,10 @@ impl Error {
|
||||
.with_message(format!("Got type {} here.", actual.fg(type_color))),
|
||||
]);
|
||||
}
|
||||
ValidationError::VariableNotFound {
|
||||
identifier,
|
||||
position,
|
||||
} => {
|
||||
builder.add_label(
|
||||
Label::new(("input", position.0..position.1))
|
||||
.with_message(format!("The variable {identifier} does not exist."))
|
||||
.with_priority(1),
|
||||
);
|
||||
}
|
||||
ValidationError::VariableNotFound(identifier) => builder.set_message(format!(
|
||||
"{} does not exist in this context.",
|
||||
identifier.fg(identifier_color)
|
||||
)),
|
||||
ValidationError::CannotIndex { r#type, position } => builder.add_label(
|
||||
Label::new(("input", position.0..position.1))
|
||||
.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.
|
||||
expected_position: SourcePosition,
|
||||
},
|
||||
VariableNotFound {
|
||||
identifier: Identifier,
|
||||
position: SourcePosition,
|
||||
},
|
||||
|
||||
VariableNotFound(Identifier),
|
||||
PropertyNotFound {
|
||||
identifier: Identifier,
|
||||
position: SourcePosition,
|
||||
|
61
src/main.rs
61
src/main.rs
@ -1,13 +1,12 @@
|
||||
//! Command line interface for the dust programming language.
|
||||
|
||||
use ariadne::{sources, Color, Label, Report, ReportKind, Source};
|
||||
use chumsky::span::SimpleSpan;
|
||||
use ariadne::sources;
|
||||
use clap::Parser;
|
||||
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.
|
||||
#[derive(Parser, Debug)]
|
||||
@ -55,58 +54,8 @@ fn main() {
|
||||
}
|
||||
Err(errors) => {
|
||||
for error in errors {
|
||||
let mut report_builder = match &error {
|
||||
Error::Parse { expected, span } => {
|
||||
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
|
||||
error
|
||||
.build_report()
|
||||
.finish()
|
||||
.eprint(sources([("input", &source)]))
|
||||
.unwrap()
|
||||
|
@ -491,7 +491,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(&lex("foobar : bool = true").unwrap()).unwrap()[0].node,
|
||||
Statement::Assignment(Assignment::new(
|
||||
Identifier::new("foobar"),
|
||||
Identifier::new("foobar").with_position((0, 6)),
|
||||
Some(Type::Boolean.with_position((9, 14))),
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Value(ValueNode::Boolean(true)))
|
||||
@ -505,7 +505,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(&lex("foobar: list = []").unwrap()).unwrap()[0].node,
|
||||
Statement::Assignment(Assignment::new(
|
||||
Identifier::new("foobar"),
|
||||
Identifier::new("foobar").with_position((0, 6)),
|
||||
Some(Type::List.with_position((8, 13))),
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Value(ValueNode::List(vec![])))
|
||||
@ -519,7 +519,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(&lex("foobar : list(bool) = [true]").unwrap()).unwrap()[0].node,
|
||||
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))),
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Value(ValueNode::List(vec![Expression::Value(
|
||||
@ -536,7 +536,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(&lex("foobar : [bool, str] = [true, '42']").unwrap()).unwrap()[0],
|
||||
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))),
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Value(ValueNode::List(vec![
|
||||
@ -554,7 +554,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(&lex("foobar : () -> any = some_function").unwrap()).unwrap()[0].node,
|
||||
Statement::Assignment(Assignment::new(
|
||||
Identifier::new("foobar"),
|
||||
Identifier::new("foobar").with_position((0, 6)),
|
||||
Some(
|
||||
Type::Function {
|
||||
parameter_types: vec![],
|
||||
@ -709,7 +709,7 @@ mod tests {
|
||||
.with_position((10, 15)),
|
||||
Block::new(vec![Statement::Break.with_position((18, 24))]),
|
||||
Some(Block::new(vec![Statement::Assignment(Assignment::new(
|
||||
Identifier::new("i"),
|
||||
Identifier::new("i").with_position((33, 34)),
|
||||
None,
|
||||
AssignmentOperator::AddAssign,
|
||||
Statement::Expression(Expression::Value(ValueNode::Integer(1)))
|
||||
@ -799,7 +799,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(&lex("foobar = 1").unwrap()).unwrap()[0].node,
|
||||
Statement::Assignment(Assignment::new(
|
||||
Identifier::new("foobar"),
|
||||
Identifier::new("foobar").with_position((0, 6)),
|
||||
None,
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Value(ValueNode::Integer(1)))
|
||||
@ -813,7 +813,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(&lex("foobar: int = 1").unwrap()).unwrap()[0].node,
|
||||
Statement::Assignment(Assignment::new(
|
||||
Identifier::new("foobar"),
|
||||
Identifier::new("foobar").with_position((0, 6)),
|
||||
Some(Type::Integer.with_position((8, 12))),
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Value(ValueNode::Integer(1)))
|
||||
|
54
src/value.rs
54
src/value.rs
@ -16,7 +16,7 @@ use stanza::{
|
||||
use crate::{
|
||||
abstract_tree::{AbstractTree, Action, Block, Identifier, Type, WithPosition},
|
||||
context::Context,
|
||||
error::{RuntimeError, ValidationError},
|
||||
error::RuntimeError,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@ -127,58 +127,6 @@ impl Value {
|
||||
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user