2024-02-29 02:04:38 +00:00
|
|
|
use crate::{
|
|
|
|
error::{RuntimeError, ValidationError},
|
|
|
|
Context,
|
|
|
|
};
|
2024-02-25 18:49:26 +00:00
|
|
|
|
2024-03-17 04:49:01 +00:00
|
|
|
use super::{AbstractTree, Action, Identifier, Positioned, Statement, Type};
|
2024-02-25 18:49:26 +00:00
|
|
|
|
2024-02-25 19:26:22 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
2024-03-08 21:14:47 +00:00
|
|
|
pub struct Assignment {
|
2024-02-28 22:49:46 +00:00
|
|
|
identifier: Identifier,
|
2024-03-17 04:49:01 +00:00
|
|
|
r#type: Option<Positioned<Type>>,
|
2024-03-08 17:39:35 +00:00
|
|
|
operator: AssignmentOperator,
|
2024-03-17 04:49:01 +00:00
|
|
|
statement: Box<Positioned<Statement>>,
|
2024-02-25 18:49:26 +00:00
|
|
|
}
|
|
|
|
|
2024-03-08 17:39:35 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
|
|
|
pub enum AssignmentOperator {
|
|
|
|
Assign,
|
|
|
|
AddAssign,
|
|
|
|
SubAssign,
|
|
|
|
}
|
|
|
|
|
2024-03-08 21:14:47 +00:00
|
|
|
impl Assignment {
|
2024-03-08 17:39:35 +00:00
|
|
|
pub fn new(
|
|
|
|
identifier: Identifier,
|
2024-03-17 04:49:01 +00:00
|
|
|
r#type: Option<Positioned<Type>>,
|
2024-03-08 17:39:35 +00:00
|
|
|
operator: AssignmentOperator,
|
2024-03-17 04:49:01 +00:00
|
|
|
statement: Positioned<Statement>,
|
2024-03-08 17:39:35 +00:00
|
|
|
) -> Self {
|
2024-02-25 18:49:26 +00:00
|
|
|
Self {
|
|
|
|
identifier,
|
2024-02-29 02:04:38 +00:00
|
|
|
r#type,
|
2024-03-08 17:39:35 +00:00
|
|
|
operator,
|
2024-02-25 18:49:26 +00:00
|
|
|
statement: Box::new(statement),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-08 21:14:47 +00:00
|
|
|
impl AbstractTree for Assignment {
|
2024-02-29 02:04:38 +00:00
|
|
|
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
2024-03-11 19:17:01 +00:00
|
|
|
Ok(Type::None)
|
2024-02-29 02:04:38 +00:00
|
|
|
}
|
|
|
|
|
2024-03-06 17:15:03 +00:00
|
|
|
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
2024-03-17 04:49:01 +00:00
|
|
|
let statement_type = self.statement.node.expected_type(context)?;
|
|
|
|
|
|
|
|
if let Some(Positioned {
|
|
|
|
node: expected_type,
|
|
|
|
position: expected_position,
|
|
|
|
}) = &self.r#type
|
|
|
|
{
|
|
|
|
expected_type.check(&statement_type).map_err(|conflict| {
|
|
|
|
ValidationError::TypeCheck {
|
|
|
|
conflict,
|
|
|
|
actual_position: self.statement.position,
|
|
|
|
expected_position: expected_position.clone(),
|
|
|
|
}
|
|
|
|
})?;
|
2024-03-06 23:15:25 +00:00
|
|
|
|
2024-03-17 04:49:01 +00:00
|
|
|
context.set_type(self.identifier.clone(), expected_type.clone())?;
|
2024-03-06 23:15:25 +00:00
|
|
|
} else {
|
|
|
|
context.set_type(self.identifier.clone(), statement_type)?;
|
2024-03-06 17:15:03 +00:00
|
|
|
}
|
|
|
|
|
2024-03-12 01:57:27 +00:00
|
|
|
self.identifier.validate(context)?;
|
2024-03-17 04:49:01 +00:00
|
|
|
self.statement.node.validate(context)?;
|
2024-03-11 18:49:44 +00:00
|
|
|
|
2024-03-06 17:15:03 +00:00
|
|
|
Ok(())
|
2024-02-29 02:04:38 +00:00
|
|
|
}
|
|
|
|
|
2024-03-08 17:24:11 +00:00
|
|
|
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
2024-03-17 04:49:01 +00:00
|
|
|
let action = self.statement.node.run(context)?;
|
2024-03-08 17:24:11 +00:00
|
|
|
let value = match action {
|
|
|
|
Action::Return(value) => value,
|
|
|
|
r#break => return Ok(r#break),
|
|
|
|
};
|
2024-02-25 18:49:26 +00:00
|
|
|
|
2024-03-08 18:26:55 +00:00
|
|
|
match self.operator {
|
|
|
|
AssignmentOperator::Assign => {
|
|
|
|
context.set_value(self.identifier, value)?;
|
|
|
|
}
|
|
|
|
AssignmentOperator::AddAssign => {
|
2024-03-14 15:49:10 +00:00
|
|
|
if let Some(previous_value) = context.use_value(&self.identifier)? {
|
2024-03-08 18:26:55 +00:00
|
|
|
let new_value = previous_value.add(&value)?;
|
|
|
|
|
|
|
|
context.set_value(self.identifier, new_value)?;
|
|
|
|
} else {
|
|
|
|
return Err(RuntimeError::ValidationFailure(
|
|
|
|
ValidationError::VariableNotFound(self.identifier),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AssignmentOperator::SubAssign => {
|
2024-03-14 15:49:10 +00:00
|
|
|
if let Some(previous_value) = context.use_value(&self.identifier)? {
|
2024-03-08 18:26:55 +00:00
|
|
|
let new_value = previous_value.subtract(&value)?;
|
|
|
|
|
|
|
|
context.set_value(self.identifier, new_value)?;
|
|
|
|
} else {
|
|
|
|
return Err(RuntimeError::ValidationFailure(
|
|
|
|
ValidationError::VariableNotFound(self.identifier),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-02-25 18:49:26 +00:00
|
|
|
|
2024-03-08 17:24:11 +00:00
|
|
|
Ok(Action::None)
|
2024-02-25 18:49:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2024-03-06 17:15:03 +00:00
|
|
|
use crate::{
|
|
|
|
abstract_tree::{Expression, ValueNode},
|
2024-03-17 04:49:01 +00:00
|
|
|
error::TypeConflict,
|
2024-03-08 17:24:11 +00:00
|
|
|
Value,
|
2024-03-06 17:15:03 +00:00
|
|
|
};
|
2024-03-02 00:15:03 +00:00
|
|
|
|
|
|
|
use super::*;
|
2024-02-25 18:49:26 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn assign_value() {
|
2024-03-02 00:15:03 +00:00
|
|
|
let context = Context::new();
|
2024-02-26 21:27:01 +00:00
|
|
|
|
2024-03-02 00:15:03 +00:00
|
|
|
Assignment::new(
|
|
|
|
Identifier::new("foobar"),
|
|
|
|
None,
|
2024-03-08 17:39:35 +00:00
|
|
|
AssignmentOperator::Assign,
|
2024-03-17 04:49:01 +00:00
|
|
|
Positioned {
|
2024-03-17 06:51:33 +00:00
|
|
|
node: Statement::Expression(
|
|
|
|
Expression::Value(ValueNode::Integer(42)).positioned((0..1).into()),
|
|
|
|
),
|
2024-03-17 04:49:01 +00:00
|
|
|
position: (0, 0),
|
|
|
|
},
|
2024-03-02 00:15:03 +00:00
|
|
|
)
|
|
|
|
.run(&context)
|
|
|
|
.unwrap();
|
2024-02-26 21:27:01 +00:00
|
|
|
|
2024-03-02 00:15:03 +00:00
|
|
|
assert_eq!(
|
2024-03-14 15:49:10 +00:00
|
|
|
context.use_value(&Identifier::new("foobar")),
|
2024-03-02 00:15:03 +00:00
|
|
|
Ok(Some(Value::integer(42)))
|
|
|
|
)
|
2024-02-25 18:49:26 +00:00
|
|
|
}
|
2024-03-06 17:15:03 +00:00
|
|
|
|
2024-03-08 18:26:55 +00:00
|
|
|
#[test]
|
|
|
|
fn add_assign_value() {
|
|
|
|
let context = Context::new();
|
|
|
|
|
|
|
|
context
|
|
|
|
.set_value(Identifier::new("foobar"), Value::integer(1))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
Assignment::new(
|
|
|
|
Identifier::new("foobar"),
|
|
|
|
None,
|
|
|
|
AssignmentOperator::AddAssign,
|
2024-03-17 04:49:01 +00:00
|
|
|
Positioned {
|
2024-03-17 06:51:33 +00:00
|
|
|
node: Statement::Expression(
|
|
|
|
Expression::Value(ValueNode::Integer(41)).positioned((0..1).into()),
|
|
|
|
),
|
2024-03-17 04:49:01 +00:00
|
|
|
position: (0, 0),
|
|
|
|
},
|
2024-03-08 18:26:55 +00:00
|
|
|
)
|
|
|
|
.run(&context)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(
|
2024-03-14 15:49:10 +00:00
|
|
|
context.use_value(&Identifier::new("foobar")),
|
2024-03-08 18:26:55 +00:00
|
|
|
Ok(Some(Value::integer(42)))
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn subtract_assign_value() {
|
|
|
|
let context = Context::new();
|
|
|
|
|
|
|
|
context
|
|
|
|
.set_value(Identifier::new("foobar"), Value::integer(43))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
Assignment::new(
|
|
|
|
Identifier::new("foobar"),
|
|
|
|
None,
|
|
|
|
AssignmentOperator::SubAssign,
|
2024-03-17 04:49:01 +00:00
|
|
|
Positioned {
|
2024-03-17 06:51:33 +00:00
|
|
|
node: Statement::Expression(
|
|
|
|
Expression::Value(ValueNode::Integer(1)).positioned((0..1).into()),
|
|
|
|
),
|
2024-03-17 04:49:01 +00:00
|
|
|
position: (0, 0),
|
|
|
|
},
|
2024-03-08 18:26:55 +00:00
|
|
|
)
|
|
|
|
.run(&context)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(
|
2024-03-14 15:49:10 +00:00
|
|
|
context.use_value(&Identifier::new("foobar")),
|
2024-03-08 18:26:55 +00:00
|
|
|
Ok(Some(Value::integer(42)))
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-03-06 17:15:03 +00:00
|
|
|
#[test]
|
|
|
|
fn type_check() {
|
|
|
|
let validation = Assignment::new(
|
|
|
|
Identifier::new("foobar"),
|
2024-03-17 04:49:01 +00:00
|
|
|
Some(Positioned {
|
|
|
|
node: Type::Boolean,
|
|
|
|
position: (0, 0),
|
|
|
|
}),
|
2024-03-08 17:39:35 +00:00
|
|
|
AssignmentOperator::Assign,
|
2024-03-17 04:49:01 +00:00
|
|
|
Positioned {
|
2024-03-17 06:51:33 +00:00
|
|
|
node: Statement::Expression(
|
|
|
|
Expression::Value(ValueNode::Integer(42)).positioned((0..1).into()),
|
|
|
|
),
|
2024-03-17 04:49:01 +00:00
|
|
|
position: (0, 0),
|
|
|
|
},
|
2024-03-06 17:15:03 +00:00
|
|
|
)
|
|
|
|
.validate(&Context::new());
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
validation,
|
2024-03-17 04:49:01 +00:00
|
|
|
Err(ValidationError::TypeCheck {
|
|
|
|
conflict: TypeConflict {
|
|
|
|
actual: Type::Integer,
|
|
|
|
expected: Type::Boolean
|
|
|
|
},
|
|
|
|
actual_position: (0, 0),
|
|
|
|
expected_position: (0, 0),
|
|
|
|
})
|
2024-03-06 17:15:03 +00:00
|
|
|
)
|
|
|
|
}
|
2024-02-25 18:49:26 +00:00
|
|
|
}
|