Improve errors
This commit is contained in:
parent
1750132ed8
commit
7dcfccf7cb
@ -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::*;
|
||||||
|
@ -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!(),
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)))
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)))
|
||||||
|
21
src/error.rs
21
src/error.rs
@ -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,
|
||||||
|
61
src/main.rs
61
src/main.rs
@ -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()
|
||||||
|
@ -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)))
|
||||||
|
54
src/value.rs
54
src/value.rs
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user