diff --git a/Cargo.lock b/Cargo.lock index e322baa..9599d91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -356,7 +356,6 @@ dependencies = [ "log", "rand", "rayon", - "stanza", ] [[package]] @@ -1101,12 +1100,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "stanza" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d230b987a5b524a015ded47fed54c5177598a71c90508acee1c5d0b4c955f74" - [[package]] name = "strip-ansi-escapes" version = "0.2.0" diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index 322489b..4584cf7 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -17,4 +17,3 @@ env_logger = "0.11.3" log = "0.4.21" rand = "0.8.5" rayon = "1.9.0" -stanza = "0.5.1" diff --git a/dust-lang/src/abstract_tree/assignment.rs b/dust-lang/src/abstract_tree/assignment.rs index 57dd729..0d6e2db 100644 --- a/dust-lang/src/abstract_tree/assignment.rs +++ b/dust-lang/src/abstract_tree/assignment.rs @@ -69,8 +69,8 @@ impl AbstractNode for Assignment { Ok(()) } - fn run(self, context: &Context) -> Result { - let action = self.statement.run(context)?; + fn run(self, context: &mut Context, clear_variables: bool) -> Result { + let action = self.statement.run(context, clear_variables)?; let right = match action { Action::Return(value) => value, r#break => return Ok(r#break), @@ -81,7 +81,7 @@ impl AbstractNode for Assignment { context.set_value(self.identifier.node, right)?; } AssignmentOperator::AddAssign => { - if let Some(left) = context.get_value(&self.identifier.node)? { + if let Some(left) = context.use_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); @@ -120,7 +120,7 @@ impl AbstractNode for Assignment { } } AssignmentOperator::SubAssign => { - if let Some(left) = context.get_value(&self.identifier.node)? { + if let Some(left) = context.use_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); @@ -175,7 +175,7 @@ mod tests { #[test] fn assign_value() { - let context = Context::new(); + let mut context = Context::new(); Assignment::new( Identifier::new("foobar").with_position((0, 0)), @@ -185,18 +185,18 @@ mod tests { ValueNode::Integer(42).with_position((0, 0)), )), ) - .run(&context) + .run(&mut context, true) .unwrap(); assert_eq!( - context.get_value(&Identifier::new("foobar")), + context.use_value(&Identifier::new("foobar")), Ok(Some(Value::integer(42))) ) } #[test] fn add_assign_value() { - let context = Context::new(); + let mut context = Context::new(); context .set_value(Identifier::new("foobar"), Value::integer(1)) @@ -210,18 +210,18 @@ mod tests { ValueNode::Integer(41).with_position((0, 0)), )), ) - .run(&context) + .run(&mut context, true) .unwrap(); assert_eq!( - context.get_value(&Identifier::new("foobar")), + context.use_value(&Identifier::new("foobar")), Ok(Some(Value::integer(42))) ) } #[test] fn subtract_assign_value() { - let context = Context::new(); + let mut context = Context::new(); context .set_value(Identifier::new("foobar"), Value::integer(43)) @@ -235,11 +235,11 @@ mod tests { ValueNode::Integer(1).with_position((0, 0)), )), ) - .run(&context) + .run(&mut context, true) .unwrap(); assert_eq!( - context.get_value(&Identifier::new("foobar")), + context.use_value(&Identifier::new("foobar")), Ok(Some(Value::integer(42))) ) } diff --git a/dust-lang/src/abstract_tree/async_block.rs b/dust-lang/src/abstract_tree/async_block.rs index 99516b2..8451fab 100644 --- a/dust-lang/src/abstract_tree/async_block.rs +++ b/dust-lang/src/abstract_tree/async_block.rs @@ -33,7 +33,7 @@ impl AbstractNode for AsyncBlock { Ok(()) } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &mut Context, _clear_variables: bool) -> Result { let statement_count = self.statements.len(); let final_result = RwLock::new(Ok(Action::None)); @@ -41,7 +41,7 @@ impl AbstractNode for AsyncBlock { .into_par_iter() .enumerate() .find_map_first(|(index, statement)| { - let result = statement.run(_context); + let result = statement.run(&mut _context.clone(), _clear_variables); if index == statement_count - 1 { let get_write_lock = final_result.write(); diff --git a/dust-lang/src/abstract_tree/block.rs b/dust-lang/src/abstract_tree/block.rs index c9e0cc3..f060335 100644 --- a/dust-lang/src/abstract_tree/block.rs +++ b/dust-lang/src/abstract_tree/block.rs @@ -37,11 +37,11 @@ impl AbstractNode for Block { Ok(()) } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &mut Context, _clear_variables: bool) -> Result { let mut previous = Action::None; for statement in self.statements { - previous = statement.run(_context)?; + previous = statement.run(_context, _clear_variables)?; } Ok(previous) @@ -72,7 +72,7 @@ mod tests { ]); assert_eq!( - block.run(&Context::new()).unwrap(), + block.run(&mut Context::new(), true).unwrap(), Action::Return(Value::integer(42)) ) } diff --git a/dust-lang/src/abstract_tree/built_in_function_call.rs b/dust-lang/src/abstract_tree/built_in_function_call.rs index 882240b..82e25df 100644 --- a/dust-lang/src/abstract_tree/built_in_function_call.rs +++ b/dust-lang/src/abstract_tree/built_in_function_call.rs @@ -27,10 +27,14 @@ impl AbstractNode for BuiltInFunctionCall { } fn validate(&self, _context: &Context) -> Result<(), ValidationError> { - Ok(()) + match self { + BuiltInFunctionCall::ReadLine => Ok(()), + BuiltInFunctionCall::Sleep(expression) => expression.validate(_context), + BuiltInFunctionCall::WriteLine(expression) => expression.validate(_context), + } } - fn run(self, context: &Context) -> Result { + fn run(self, context: &mut Context, _clear_variables: bool) -> Result { match self { BuiltInFunctionCall::ReadLine => { let mut buffer = String::new(); @@ -40,7 +44,7 @@ impl AbstractNode for BuiltInFunctionCall { Ok(Action::Return(Value::string(buffer))) } BuiltInFunctionCall::Sleep(expression) => { - let action = expression.clone().run(context)?; + let action = expression.clone().run(context, _clear_variables)?; let value = if let Action::Return(value) = action { value } else { @@ -56,7 +60,7 @@ impl AbstractNode for BuiltInFunctionCall { Ok(Action::None) } BuiltInFunctionCall::WriteLine(expression) => { - let action = expression.clone().run(context)?; + let action = expression.clone().run(context, _clear_variables)?; let value = if let Action::Return(value) = action { value } else { diff --git a/dust-lang/src/abstract_tree/expression.rs b/dust-lang/src/abstract_tree/expression.rs index 3048eee..1e76262 100644 --- a/dust-lang/src/abstract_tree/expression.rs +++ b/dust-lang/src/abstract_tree/expression.rs @@ -65,7 +65,9 @@ impl AbstractNode for Expression { match self { Expression::FunctionCall(function_call) => function_call.node.validate(context), Expression::Identifier(identifier) => { - if context.contains(&identifier.node)? { + let found = context.add_expected_use(&identifier.node)?; + + if found { Ok(()) } else { Err(ValidationError::VariableNotFound { @@ -85,11 +87,13 @@ impl AbstractNode for Expression { } } - fn run(self, _context: &Context) -> Result { + fn run(self, context: &mut Context, _clear_variables: bool) -> Result { match self { - Expression::FunctionCall(function_call) => function_call.node.run(_context), + Expression::FunctionCall(function_call) => { + function_call.node.run(context, _clear_variables) + } Expression::Identifier(identifier) => { - if let Some(value) = _context.get_value(&identifier.node)? { + if let Some(value) = context.use_value(&identifier.node)? { Ok(Action::Return(value)) } else { Err(RuntimeError::ValidationFailure( @@ -100,13 +104,13 @@ impl AbstractNode for Expression { )) } } - Expression::MapIndex(map_index) => map_index.node.run(_context), - Expression::ListIndex(list_index) => list_index.node.run(_context), - Expression::Logic(logic) => logic.node.run(_context), - Expression::Math(math) => math.node.run(_context), - Expression::Value(value_node) => value_node.node.run(_context), + Expression::MapIndex(map_index) => map_index.node.run(context, _clear_variables), + Expression::ListIndex(list_index) => list_index.node.run(context, _clear_variables), + Expression::Logic(logic) => logic.node.run(context, _clear_variables), + Expression::Math(math) => math.node.run(context, _clear_variables), + Expression::Value(value_node) => value_node.node.run(context, _clear_variables), Expression::BuiltInFunctionCall(built_in_function_call) => { - built_in_function_call.node.run(_context) + built_in_function_call.node.run(context, _clear_variables) } } } diff --git a/dust-lang/src/abstract_tree/function_call.rs b/dust-lang/src/abstract_tree/function_call.rs index e9206f5..eb0826b 100644 --- a/dust-lang/src/abstract_tree/function_call.rs +++ b/dust-lang/src/abstract_tree/function_call.rs @@ -42,6 +42,8 @@ impl AbstractNode for FunctionCall { } fn validate(&self, context: &Context) -> Result<(), ValidationError> { + self.function.validate(context)?; + for expression in &self.arguments { expression.validate(context)?; } @@ -95,9 +97,9 @@ impl AbstractNode for FunctionCall { } } - fn run(self, context: &Context) -> Result { + fn run(self, context: &mut Context, _clear_variables: bool) -> Result { let function_position = self.function.position(); - let action = self.function.run(context)?; + let action = self.function.run(context, _clear_variables)?; let value = if let Action::Return(value) = action { value } else { @@ -119,7 +121,7 @@ impl AbstractNode for FunctionCall { for expression in self.arguments { let expression_position = expression.position(); - let action = expression.run(context)?; + let action = expression.run(context, _clear_variables)?; let value = if let Action::Return(value) = action { value } else { @@ -131,7 +133,7 @@ impl AbstractNode for FunctionCall { arguments.push(value); } - let function_context = Context::new(); + let mut function_context = Context::new(); for (type_parameter, type_argument) in function .type_parameters() @@ -145,6 +147,6 @@ impl AbstractNode for FunctionCall { } function_context.inherit_data_from(&context)?; - function.clone().call(arguments, function_context) + function.clone().call(arguments, &mut function_context) } } diff --git a/dust-lang/src/abstract_tree/if_else.rs b/dust-lang/src/abstract_tree/if_else.rs index 449afeb..559d8e7 100644 --- a/dust-lang/src/abstract_tree/if_else.rs +++ b/dust-lang/src/abstract_tree/if_else.rs @@ -89,9 +89,9 @@ impl AbstractNode for IfElse { Ok(()) } - fn run(self, context: &Context) -> Result { + fn run(self, context: &mut Context, _clear_variables: bool) -> Result { let if_position = self.if_expression.position(); - let action = self.if_expression.run(context)?; + let action = self.if_expression.run(context, _clear_variables)?; let value = if let Action::Return(value) = action { value } else { @@ -102,11 +102,11 @@ impl AbstractNode for IfElse { if let ValueInner::Boolean(if_boolean) = value.inner().as_ref() { if *if_boolean { - self.if_block.node.run(context) + self.if_block.node.run(context, _clear_variables) } else { for (expression, block) in self.else_ifs { let expression_position = expression.position(); - let action = expression.run(context)?; + let action = expression.run(context, _clear_variables)?; let value = if let Action::Return(value) = action { value } else { @@ -117,7 +117,7 @@ impl AbstractNode for IfElse { if let ValueInner::Boolean(else_if_boolean) = value.inner().as_ref() { if *else_if_boolean { - return block.node.run(context); + return block.node.run(context, _clear_variables); } } else { return Err(RuntimeError::ValidationFailure( @@ -130,7 +130,7 @@ impl AbstractNode for IfElse { } if let Some(else_statement) = self.else_block { - else_statement.node.run(context) + else_statement.node.run(context, _clear_variables) } else { Ok(Action::None) } @@ -167,7 +167,7 @@ mod tests { Vec::with_capacity(0), None ) - .run(&Context::new()) + .run(&mut Context::new(), true) .unwrap(), Action::Return(Value::string("foo".to_string())) ) diff --git a/dust-lang/src/abstract_tree/list_index.rs b/dust-lang/src/abstract_tree/list_index.rs index 236d079..5fb7ed1 100644 --- a/dust-lang/src/abstract_tree/list_index.rs +++ b/dust-lang/src/abstract_tree/list_index.rs @@ -74,9 +74,9 @@ impl AbstractNode for ListIndex { } } - fn run(self, context: &Context) -> Result { + fn run(self, context: &mut Context, _clear_variables: bool) -> Result { let left_position = self.left.position(); - let left_action = self.left.run(context)?; + let left_action = self.left.run(context, _clear_variables)?; let left_value = if let Action::Return(value) = left_action { value } else { @@ -85,7 +85,7 @@ impl AbstractNode for ListIndex { )); }; let right_position = self.right.position(); - let right_action = self.right.run(context)?; + let right_action = self.right.run(context, _clear_variables)?; let right_value = if let Action::Return(value) = right_action { value } else { diff --git a/dust-lang/src/abstract_tree/logic.rs b/dust-lang/src/abstract_tree/logic.rs index 137e284..07869f7 100644 --- a/dust-lang/src/abstract_tree/logic.rs +++ b/dust-lang/src/abstract_tree/logic.rs @@ -83,10 +83,10 @@ impl AbstractNode for Logic { } } - fn run(self, context: &Context) -> Result { + fn run(self, context: &mut Context, _clear_variables: bool) -> Result { let run_and_expect_value = |expression: Expression| -> Result { let expression_position = expression.position(); - let action = expression.run(context)?; + let action = expression.run(&mut context.clone(), _clear_variables)?; let value = if let Action::Return(value) = action { value } else { @@ -100,7 +100,7 @@ impl AbstractNode for Logic { let run_and_expect_boolean = |expression: Expression| -> Result { let expression_position = expression.position(); - let action = expression.run(context)?; + let action = expression.run(&mut context.clone(), _clear_variables)?; let value = if let Action::Return(value) = action { value } else { @@ -198,7 +198,7 @@ mod tests { Expression::Value(ValueNode::Integer(42).with_position((0, 0))), Expression::Value(ValueNode::Integer(42).with_position((0, 0))) ) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ) } @@ -210,7 +210,7 @@ mod tests { Expression::Value(ValueNode::Integer(42).with_position((0, 0))), Expression::Value(ValueNode::Integer(43).with_position((0, 0))) ) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ) } @@ -222,7 +222,7 @@ mod tests { Expression::Value(ValueNode::Integer(43).with_position((0, 0))), Expression::Value(ValueNode::Integer(42).with_position((0, 0))) ) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ) } @@ -234,7 +234,7 @@ mod tests { Expression::Value(ValueNode::Integer(42).with_position((0, 0))), Expression::Value(ValueNode::Integer(43).with_position((0, 0))) ) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ) } @@ -246,7 +246,7 @@ mod tests { Expression::Value(ValueNode::Integer(42).with_position((0, 0))), Expression::Value(ValueNode::Integer(41).with_position((0, 0))) ) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ); @@ -255,7 +255,7 @@ mod tests { Expression::Value(ValueNode::Integer(42).with_position((0, 0))), Expression::Value(ValueNode::Integer(42).with_position((0, 0))), ) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ); } @@ -267,7 +267,7 @@ mod tests { Expression::Value(ValueNode::Integer(41).with_position((0, 0))), Expression::Value(ValueNode::Integer(42).with_position((0, 0))), ) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ); @@ -276,7 +276,7 @@ mod tests { Expression::Value(ValueNode::Integer(42).with_position((0, 0))), Expression::Value(ValueNode::Integer(42).with_position((0, 0))), ) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ); } @@ -288,7 +288,7 @@ mod tests { Expression::Value(ValueNode::Boolean(true).with_position((0, 0))), Expression::Value(ValueNode::Boolean(true).with_position((0, 0))), ) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ) } @@ -300,7 +300,7 @@ mod tests { Expression::Value(ValueNode::Boolean(true).with_position((0, 0))), Expression::Value(ValueNode::Boolean(false).with_position((0, 0))), ) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ) } @@ -311,7 +311,7 @@ mod tests { Logic::Not(Expression::Value( ValueNode::Boolean(false).with_position((0, 0)) )) - .run(&Context::new()), + .run(&mut Context::new(), true), Ok(Action::Return(Value::boolean(true))) ) } diff --git a/dust-lang/src/abstract_tree/loop.rs b/dust-lang/src/abstract_tree/loop.rs index b15139c..176a106 100644 --- a/dust-lang/src/abstract_tree/loop.rs +++ b/dust-lang/src/abstract_tree/loop.rs @@ -31,10 +31,10 @@ impl AbstractNode for Loop { Ok(()) } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &mut Context, _clear_variables: bool) -> Result { loop { for statement in &self.statements { - let action = statement.clone().run(_context)?; + let action = statement.clone().run(_context, _clear_variables)?; match action { Action::Return(_) => {} @@ -65,81 +65,3 @@ impl Ord for Loop { self.statements.cmp(&other.statements) } } - -#[cfg(test)] -mod tests { - use crate::{ - abstract_tree::{ - Assignment, AssignmentOperator, Block, Expression, IfElse, Logic, ValueNode, WithPos, - }, - identifier::Identifier, - Value, - }; - - use super::*; - - #[test] - fn basic_loop() { - let action = Loop::new(vec![Statement::Break(().with_position((0, 0)))]) - .run(&Context::new()) - .unwrap(); - - assert_eq!(action, Action::Break) - } - - #[test] - fn complex_loop() { - let action = Block::new(vec![ - Statement::Assignment( - Assignment::new( - Identifier::new("i").with_position((0, 0)), - None, - AssignmentOperator::Assign, - Statement::Expression(Expression::Value( - ValueNode::Integer(1).with_position((0, 0)), - )), - ) - .with_position((0, 0)), - ), - Statement::Loop( - Loop::new(vec![Statement::IfElse( - IfElse::new( - Expression::Logic( - Box::new(Logic::Greater( - Expression::Identifier(Identifier::new("i").with_position((0, 0))), - Expression::Value(ValueNode::Integer(2).with_position((0, 0))), - )) - .with_position((0, 0)), - ), - Block::new(vec![Statement::Break(().with_position((0, 0)))]) - .with_position((0, 0)), - Vec::with_capacity(0), - Some( - Block::new(vec![Statement::Assignment( - Assignment::new( - Identifier::new("i").with_position((0, 0)), - None, - AssignmentOperator::AddAssign, - Statement::Expression(Expression::Value( - ValueNode::Integer(1).with_position((0, 0)), - )), - ) - .with_position((0, 0)), - )]) - .with_position((0, 0)), - ), - ) - .with_position((0, 0)), - )]) - .with_position((0, 0)), - ), - Statement::Expression(Expression::Identifier( - Identifier::new("i").with_position((0, 0)), - )), - ]) - .run(&Context::new()) - .unwrap(); - - assert_eq!(action, Action::Return(Value::integer(3))) - } -} diff --git a/dust-lang/src/abstract_tree/map_index.rs b/dust-lang/src/abstract_tree/map_index.rs index fcc6c90..ae5989b 100644 --- a/dust-lang/src/abstract_tree/map_index.rs +++ b/dust-lang/src/abstract_tree/map_index.rs @@ -1,6 +1,7 @@ use crate::{ context::Context, error::{RuntimeError, ValidationError}, + identifier::Identifier, value::ValueInner, }; @@ -8,25 +9,26 @@ use super::{AbstractNode, Action, Expression, Type, ValueNode, WithPosition}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct MapIndex { - left: Expression, - right: Expression, + collection: Expression, + index: WithPosition, } impl MapIndex { - pub fn new(left: Expression, right: Expression) -> Self { - Self { left, right } + pub fn new(left: Expression, right: WithPosition) -> Self { + Self { + collection: left, + index: right, + } } } impl AbstractNode for MapIndex { fn expected_type(&self, context: &Context) -> Result { - if let ( - Expression::Identifier(collection_identifier), - Expression::Identifier(index_identifier), - ) = (&self.left, &self.right) + if let (Expression::Identifier(collection_identifier), index) = + (&self.collection, &self.index) { let collection = - if let Some(collection) = context.get_value(&collection_identifier.node)? { + if let Some(collection) = context.use_value(&collection_identifier.node)? { collection } else { return Err(ValidationError::VariableNotFound { @@ -36,12 +38,12 @@ impl AbstractNode for MapIndex { }; if let ValueInner::Map(map) = collection.inner().as_ref() { - return if let Some(value) = map.get(&index_identifier.node) { + return if let Some(value) = map.get(&index.node) { Ok(value.r#type(context)?) } else { Err(ValidationError::PropertyNotFound { - identifier: index_identifier.node.clone(), - position: self.right.position(), + identifier: index.node.clone(), + position: index.position, }) }; }; @@ -52,14 +54,14 @@ impl AbstractNode for MapIndex { node: ValueNode::Map(properties), .. }), - Expression::Identifier(identifier), - ) = (&self.left, &self.right) + index, + ) = (&self.collection, &self.index) { return if let Some(type_result) = properties .iter() .find_map(|(property, type_option, expression)| { - if property == &identifier.node { + if property == &index.node { if let Some(r#type) = type_option { Some(r#type.node.expected_type(context)) } else { @@ -81,11 +83,11 @@ impl AbstractNode for MapIndex { node: ValueNode::Structure { fields, .. }, .. }), - Expression::Identifier(identifier), - ) = (&self.left, &self.right) + index, + ) = (&self.collection, &self.index) { return if let Some(type_result) = fields.iter().find_map(|(property, expression)| { - if property == &identifier.node { + if property == &index.node { Some(expression.expected_type(context)) } else { None @@ -97,43 +99,19 @@ impl AbstractNode for MapIndex { }; } - Err(ValidationError::CannotIndexWith { - collection_type: self.left.expected_type(context)?, - collection_position: self.left.position(), - index_type: self.right.expected_type(context)?, - index_position: self.right.position(), + Err(ValidationError::CannotIndex { + r#type: self.collection.expected_type(context)?, + position: self.collection.position(), }) } fn validate(&self, context: &Context) -> Result<(), ValidationError> { - let left_type = self.left.expected_type(context)?; - - if let ( - Expression::Value(WithPosition { - node: ValueNode::Map(_), - .. - }), - Expression::Identifier(_), - ) = (&self.left, &self.right) - { - Ok(()) - } else if let (Expression::Identifier(_), Expression::Identifier(_)) = - (&self.left, &self.right) - { - Ok(()) - } else { - Err(ValidationError::CannotIndexWith { - collection_type: left_type, - collection_position: self.left.position(), - index_type: self.right.expected_type(context)?, - index_position: self.right.position(), - }) - } + self.collection.validate(context) } - fn run(self, context: &Context) -> Result { - let collection_position = self.left.position(); - let action = self.left.run(context)?; + fn run(self, context: &mut Context, _clear_variables: bool) -> Result { + let collection_position = self.collection.position(); + let action = self.collection.run(context, _clear_variables)?; let collection = if let Action::Return(value) = action { value } else { @@ -142,22 +120,18 @@ impl AbstractNode for MapIndex { )); }; - if let (ValueInner::Map(map), Expression::Identifier(identifier)) = - (collection.inner().as_ref(), &self.right) - { + if let ValueInner::Map(map) = collection.inner().as_ref() { let action = map - .get(&identifier.node) + .get(&self.index.node) .map(|value| Action::Return(value.clone())) .unwrap_or(Action::None); Ok(action) } else { Err(RuntimeError::ValidationFailure( - ValidationError::CannotIndexWith { - collection_type: collection.r#type(context)?, - collection_position, - index_type: self.right.expected_type(context)?, - index_position: self.right.position(), + ValidationError::CannotIndex { + r#type: collection.r#type(context)?, + position: collection_position, }, )) } diff --git a/dust-lang/src/abstract_tree/math.rs b/dust-lang/src/abstract_tree/math.rs index b40f6a6..96223a1 100644 --- a/dust-lang/src/abstract_tree/math.rs +++ b/dust-lang/src/abstract_tree/math.rs @@ -88,10 +88,10 @@ impl AbstractNode for Math { } } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &mut Context, _clear_variables: bool) -> Result { let run_and_expect_value = |position: SourcePosition, expression: Expression| -> Result { - let action = expression.run(_context)?; + let action = expression.run(&mut _context.clone(), _clear_variables)?; let value = if let Action::Return(value) = action { value } else { diff --git a/dust-lang/src/abstract_tree/mod.rs b/dust-lang/src/abstract_tree/mod.rs index 288ab87..f98d601 100644 --- a/dust-lang/src/abstract_tree/mod.rs +++ b/dust-lang/src/abstract_tree/mod.rs @@ -98,13 +98,17 @@ impl AbstractTree { AbstractTree(statements) } - pub fn run(self, context: &Context) -> Result, Vec> { + pub fn run( + self, + context: &mut Context, + clear_variables: bool, + ) -> Result, Vec> { let valid_statements = self.validate(context)?; let mut previous_value = None; for statement in valid_statements { let position = statement.position(); - let run = statement.run(context); + let run = statement.run(context, clear_variables); match run { Ok(action) => match action { @@ -124,7 +128,7 @@ impl AbstractTree { Ok(previous_value) } - fn validate(self, context: &Context) -> Result, Vec> { + fn validate(self, context: &mut Context) -> Result, Vec> { let mut errors = Vec::new(); let mut valid_statements = Vec::new(); @@ -139,7 +143,7 @@ impl AbstractTree { } else if errors.is_empty() { if let Statement::StructureDefinition(_) = statement { let position = statement.position(); - let run = statement.run(context); + let run = statement.run(context, true); if let Err(runtime_error) = run { errors.push(Error::Runtime { @@ -176,5 +180,5 @@ impl Index for AbstractTree { pub trait AbstractNode: Sized { fn expected_type(&self, context: &Context) -> Result; fn validate(&self, context: &Context) -> Result<(), ValidationError>; - fn run(self, context: &Context) -> Result; + fn run(self, context: &mut Context, clear_variables: bool) -> Result; } diff --git a/dust-lang/src/abstract_tree/statement.rs b/dust-lang/src/abstract_tree/statement.rs index fc5c5b4..b38acf1 100644 --- a/dust-lang/src/abstract_tree/statement.rs +++ b/dust-lang/src/abstract_tree/statement.rs @@ -70,19 +70,25 @@ impl AbstractNode for Statement { } } - fn run(self, _context: &Context) -> Result { - match self { - Statement::Assignment(assignment) => assignment.node.run(_context), - Statement::AsyncBlock(async_block) => async_block.node.run(_context), - Statement::Block(block) => block.node.run(_context), + fn run(self, context: &mut Context, clear_variables: bool) -> Result { + let result = match self { + Statement::Assignment(assignment) => assignment.node.run(context, clear_variables), + Statement::AsyncBlock(async_block) => async_block.node.run(context, clear_variables), + Statement::Block(block) => block.node.run(context, clear_variables), Statement::Break(_) => Ok(Action::Break), - Statement::Expression(expression) => expression.run(_context), - Statement::IfElse(if_else) => if_else.node.run(_context), - Statement::Loop(r#loop) => r#loop.node.run(_context), - Statement::While(r#while) => r#while.node.run(_context), + Statement::Expression(expression) => expression.run(context, clear_variables), + Statement::IfElse(if_else) => if_else.node.run(context, clear_variables), + Statement::Loop(r#loop) => r#loop.node.run(context, clear_variables), + Statement::While(r#while) => r#while.node.run(context, clear_variables), Statement::StructureDefinition(structure_definition) => { - structure_definition.node.run(_context) + structure_definition.node.run(context, clear_variables) } + }; + + if clear_variables { + context.clean()?; } + + result } } diff --git a/dust-lang/src/abstract_tree/structure_definition.rs b/dust-lang/src/abstract_tree/structure_definition.rs index 91f0a25..18fb543 100644 --- a/dust-lang/src/abstract_tree/structure_definition.rs +++ b/dust-lang/src/abstract_tree/structure_definition.rs @@ -27,7 +27,7 @@ impl AbstractNode for StructureDefinition { Ok(()) } - fn run(self, context: &Context) -> Result { + fn run(self, context: &mut Context, _clear_variables: bool) -> Result { let struct_type = Type::Structure { name: self.name.clone(), fields: self.fields, diff --git a/dust-lang/src/abstract_tree/type.rs b/dust-lang/src/abstract_tree/type.rs index 5cb0b77..fccc719 100644 --- a/dust-lang/src/abstract_tree/type.rs +++ b/dust-lang/src/abstract_tree/type.rs @@ -152,7 +152,7 @@ impl AbstractNode for Type { Ok(()) } - fn run(self, _: &Context) -> Result { + fn run(self, _context: &mut Context, _clear_variables: bool) -> Result { Ok(Action::None) } } diff --git a/dust-lang/src/abstract_tree/value_node.rs b/dust-lang/src/abstract_tree/value_node.rs index 3308ad8..f855ebf 100644 --- a/dust-lang/src/abstract_tree/value_node.rs +++ b/dust-lang/src/abstract_tree/value_node.rs @@ -181,7 +181,7 @@ impl AbstractNode for ValueNode { Ok(()) } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &mut Context, _clear_variables: bool) -> Result { let value = match self { ValueNode::Boolean(boolean) => Value::boolean(boolean), ValueNode::Float(float) => Value::float(float), @@ -191,7 +191,7 @@ impl AbstractNode for ValueNode { for expression in expression_list { let expression_position = expression.position(); - let action = expression.run(_context)?; + let action = expression.run(_context, _clear_variables)?; let value = if let Action::Return(value) = action { WithPosition { node: value, @@ -213,7 +213,7 @@ impl AbstractNode for ValueNode { for (identifier, _type, expression) in property_list { let expression_position = expression.position(); - let action = expression.run(_context)?; + let action = expression.run(_context, _clear_variables)?; let value = if let Action::Return(value) = action { value } else { @@ -243,7 +243,7 @@ impl AbstractNode for ValueNode { for (identifier, expression) in expressions { let expression_position = expression.position(); - let action = expression.run(_context)?; + let action = expression.run(_context, _clear_variables)?; let value = if let Action::Return(value) = action { value } else { diff --git a/dust-lang/src/abstract_tree/while.rs b/dust-lang/src/abstract_tree/while.rs index 602bc8e..472eb7c 100644 --- a/dust-lang/src/abstract_tree/while.rs +++ b/dust-lang/src/abstract_tree/while.rs @@ -37,10 +37,13 @@ impl AbstractNode for While { Ok(()) } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &mut Context, _clear_variables: bool) -> Result { let get_boolean = || -> Result { let expression_position = self.expression.position(); - let action = self.expression.clone().run(_context)?; + let action = self + .expression + .clone() + .run(&mut _context.clone(), _clear_variables)?; if let Action::Return(value) = action { Ok(value) @@ -53,7 +56,9 @@ impl AbstractNode for While { while let ValueInner::Boolean(true) = get_boolean()?.inner().as_ref() { for statement in &self.statements { - let action = statement.clone().run(_context)?; + let action = statement + .clone() + .run(&mut _context.clone(), _clear_variables)?; match action { Action::Return(_) => {} @@ -66,63 +71,3 @@ impl AbstractNode for While { Ok(Action::None) } } - -#[cfg(test)] -mod tests { - use crate::{ - abstract_tree::{Assignment, AssignmentOperator, Block, Logic, ValueNode, WithPos}, - identifier::Identifier, - }; - - use super::*; - - #[test] - fn simple_while_loop() { - let action = Statement::Block( - Block::new(vec![ - Statement::Assignment( - Assignment::new( - Identifier::new("i").with_position((0, 0)), - None, - AssignmentOperator::Assign, - Statement::Expression(Expression::Value( - ValueNode::Integer(3).with_position((0, 0)), - )), - ) - .with_position((0, 0)), - ), - Statement::While( - While::new( - Expression::Logic( - Box::new(Logic::Less( - Expression::Identifier(Identifier::new("i").with_position((0, 0))), - Expression::Value(ValueNode::Integer(3).with_position((0, 0))), - )) - .with_position((0, 0)), - ), - vec![Statement::Assignment( - Assignment::new( - Identifier::new("i").with_position((0, 0)), - None, - AssignmentOperator::AddAssign, - Statement::Expression(Expression::Value( - ValueNode::Integer(1).with_position((0, 0)), - )), - ) - .with_position((0, 0)), - )], - ) - .with_position((0, 0)), - ), - Statement::Expression(Expression::Identifier( - Identifier::new("i").with_position((0, 0)), - )), - ]) - .with_position((0, 0)), - ) - .run(&Context::new()) - .unwrap(); - - assert_eq!(action, Action::Return(Value::integer(3))) - } -} diff --git a/dust-lang/src/context.rs b/dust-lang/src/context.rs index c3d01bd..af095bf 100644 --- a/dust-lang/src/context.rs +++ b/dust-lang/src/context.rs @@ -3,6 +3,8 @@ use std::{ sync::{Arc, RwLock, RwLockReadGuard}, }; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; + use crate::{ abstract_tree::Type, error::{RwLockPoisonError, ValidationError}, @@ -12,7 +14,7 @@ use crate::{ #[derive(Clone, Debug)] pub struct Context { - inner: Arc>>, + inner: Arc>>, } impl Context { @@ -22,7 +24,7 @@ impl Context { } } - pub fn with_data(data: BTreeMap) -> Self { + pub fn with_data(data: BTreeMap) -> Self { Self { inner: Arc::new(RwLock::new(data)), } @@ -30,16 +32,17 @@ impl Context { pub fn inner( &self, - ) -> Result>, RwLockPoisonError> { + ) -> Result>, RwLockPoisonError> + { Ok(self.inner.read()?) } pub fn inherit_types_from(&self, other: &Context) -> Result<(), RwLockPoisonError> { let mut self_data = self.inner.write()?; - for (identifier, value_data) in other.inner.read()?.iter() { + for (identifier, (value_data, usage_data)) in other.inner.read()?.iter() { if let ValueData::Type(Type::Function { .. }) = value_data { - self_data.insert(identifier.clone(), value_data.clone()); + self_data.insert(identifier.clone(), (value_data.clone(), usage_data.clone())); } } @@ -49,8 +52,8 @@ impl Context { pub fn inherit_data_from(&self, other: &Context) -> Result<(), RwLockPoisonError> { let mut self_data = self.inner.write()?; - for (identifier, value_data) in other.inner.read()?.iter() { - self_data.insert(identifier.clone(), value_data.clone()); + for (identifier, (value_data, usage_data)) in other.inner.read()?.iter() { + self_data.insert(identifier.clone(), (value_data.clone(), usage_data.clone())); } Ok(()) @@ -61,7 +64,9 @@ impl Context { } pub fn get_type(&self, identifier: &Identifier) -> Result, ValidationError> { - if let Some(value_data) = self.inner.read()?.get(identifier) { + if let Some((value_data, _)) = self.inner.read()?.get(identifier) { + log::trace!("Using {identifier}'s type."); + let r#type = match value_data { ValueData::Type(r#type) => r#type.clone(), ValueData::Value(value) => value.r#type(self)?, @@ -73,8 +78,13 @@ impl Context { } } - pub fn get_value(&self, identifier: &Identifier) -> Result, RwLockPoisonError> { - if let Some(ValueData::Value(value)) = self.inner.read()?.get(identifier) { + pub fn use_value(&self, identifier: &Identifier) -> Result, RwLockPoisonError> { + if let Some((ValueData::Value(value), usage_data)) = self.inner.write()?.get_mut(identifier) + { + log::trace!("Using {identifier}'s value."); + + usage_data.actual += 1; + Ok(Some(value.clone())) } else { Ok(None) @@ -82,26 +92,75 @@ impl Context { } pub fn set_type(&self, identifier: Identifier, r#type: Type) -> Result<(), RwLockPoisonError> { + log::info!("Setting {identifier} to type {}.", r#type); + self.inner .write()? - .insert(identifier, ValueData::Type(r#type)); + .insert(identifier, (ValueData::Type(r#type), UsageData::new())); Ok(()) } pub fn set_value(&self, identifier: Identifier, value: Value) -> Result<(), RwLockPoisonError> { - self.inner - .write()? - .insert(identifier, ValueData::Value(value)); + log::info!("Setting {identifier} to value {value}."); + + let mut inner = self.inner.write()?; + let old_usage_data = inner.remove(&identifier).map(|(_, usage_data)| usage_data); + + if let Some(usage_data) = old_usage_data { + inner.insert(identifier, (ValueData::Value(value), usage_data)); + } else { + inner.insert(identifier, (ValueData::Value(value), UsageData::new())); + } Ok(()) } pub fn remove(&self, identifier: &Identifier) -> Result, RwLockPoisonError> { - let removed = self.inner.write()?.remove(identifier); + let removed = self + .inner + .write()? + .remove(identifier) + .map(|(value_data, _)| value_data); Ok(removed) } + + pub fn clean(&mut self) -> Result<(), RwLockPoisonError> { + let clean_variables = self + .inner + .write()? + .clone() + .into_par_iter() + .filter(|(identifier, (_, usage_data))| { + if usage_data.actual < usage_data.expected { + true + } else { + log::debug!("Removing variable {identifier}."); + + false + } + }) + .collect(); + + self.inner = Arc::new(RwLock::new(clean_variables)); + + Ok(()) + } + + pub fn add_expected_use(&self, identifier: &Identifier) -> Result { + let mut inner = self.inner.write()?; + + if let Some((_, usage_data)) = inner.get_mut(identifier) { + log::trace!("Adding expected use for variable {identifier}."); + + usage_data.expected += 1; + + Ok(true) + } else { + Ok(false) + } + } } #[derive(Clone, Debug, PartialEq)] @@ -109,3 +168,18 @@ pub enum ValueData { Type(Type), Value(Value), } + +#[derive(Clone, Debug)] +pub struct UsageData { + pub actual: u32, + pub expected: u32, +} + +impl UsageData { + pub fn new() -> Self { + Self { + actual: 0, + expected: 0, + } + } +} diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index c6d10f3..ded5f90 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -33,7 +33,7 @@ pub fn interpret_without_std( source_id: &str, source: &str, ) -> Result, InterpreterError> { - let interpreter = Interpreter::new(Context::new()); + let mut interpreter = Interpreter::new(Context::new()); interpreter.run(Arc::from(source_id.to_string()), Arc::from(source)) } @@ -52,7 +52,7 @@ impl Interpreter { } pub fn run( - &self, + &mut self, source_id: Arc, source: Arc, ) -> Result, InterpreterError> { @@ -70,7 +70,7 @@ impl Interpreter { errors, })?; let value_option = abstract_tree - .run(&self.context) + .run(&mut self.context, true) .map_err(|errors| InterpreterError { source_id, errors })?; Ok(value_option) @@ -134,7 +134,7 @@ impl Interpreter { }; abstract_tree - .run(&self.context) + .run(&mut self.context.clone(), false) .map_err(|errors| InterpreterError { source_id, errors }) .err() }); diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index fbfbbaf..ad1d593 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -322,13 +322,29 @@ pub fn parser<'src>( just(Token::Control(Control::DoubleColon)), ); - let atom = choice(( - range.clone(), + let map_atom = choice(( + map.clone(), structure_instance.clone(), + identifier_expression.clone(), + )); + + let map_index = map_atom + .then_ignore(just(Token::Control(Control::Dot))) + .then(positioned_identifier.clone()) + .map_with(|(expression, identifier), state| { + Expression::MapIndex( + Box::new(MapIndex::new(expression, identifier)).with_position(state.span()), + ) + }); + + let atom = choice(( + map_index.clone(), + range.clone(), parsed_function.clone(), list.clone(), - map.clone(), basic_value.clone(), + map.clone(), + structure_instance.clone(), identifier_expression.clone(), expression.clone().delimited_by( just(Token::Control(Control::ParenOpen)), @@ -384,15 +400,6 @@ pub fn parser<'src>( ) }, ), - infix( - left(4), - just(Token::Control(Control::Dot)), - |left, _, right, span| { - Expression::MapIndex( - Box::new(MapIndex::new(left, right)).with_position(span), - ) - }, - ), infix( left(1), just(Token::Operator(Operator::Equal)), @@ -493,8 +500,9 @@ pub fn parser<'src>( )); choice(( - built_in_function_call, logic_math_indexes_and_function_calls, + map_index, + built_in_function_call, range, structure_instance, parsed_function, @@ -765,7 +773,7 @@ mod tests { )]) .with_position((0, 10)) ), - Expression::Identifier(Identifier::new("x").with_position((11, 12))) + Identifier::new("x").with_position((11, 12)) )) .with_position((0, 12)) )) @@ -775,7 +783,7 @@ mod tests { Statement::Expression(Expression::MapIndex( Box::new(MapIndex::new( Expression::Identifier(Identifier::new("foo").with_position((0, 3))), - Expression::Identifier(Identifier::new("x").with_position((4, 5))) + Identifier::new("x").with_position((4, 5)) )) .with_position((0, 5)) )) @@ -924,22 +932,14 @@ mod tests { #[test] fn function_call() { assert_eq!( - parse(&lex("io.read_line()").unwrap()).unwrap()[0], + parse(&lex("foobar()").unwrap()).unwrap()[0], Statement::Expression(Expression::FunctionCall( FunctionCall::new( - Expression::MapIndex( - Box::new(MapIndex::new( - Expression::Identifier(Identifier::new("io").with_position((0, 2))), - Expression::Identifier( - Identifier::new("read_line").with_position((3, 12)) - ) - )) - .with_position((0, 12)) - ), + Expression::Identifier(Identifier::new("foobar").with_position((0, 6))), Vec::with_capacity(0), Vec::with_capacity(0), ) - .with_position((0, 14)) + .with_position((0, 8)) )) ) } diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index dd3ce88..d46bece 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -6,12 +6,6 @@ use std::{ sync::Arc, }; -use stanza::{ - renderer::{console::Console, Renderer}, - style::{HAlign, MinWidth, Styles}, - table::Table, -}; - use crate::{ abstract_tree::{AbstractNode, Action, Block, Type, WithPos, WithPosition}, context::Context, @@ -104,31 +98,31 @@ impl Value { impl Display for Value { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - fn create_table() -> Table { - Table::with_styles(Styles::default().with(HAlign::Centred).with(MinWidth(3))) - } - match self.inner().as_ref() { ValueInner::Boolean(boolean) => write!(f, "{boolean}"), ValueInner::Float(float) => write!(f, "{float}"), ValueInner::Integer(integer) => write!(f, "{integer}"), ValueInner::List(list) => { - let mut table = create_table(); + write!(f, "[")?; - for value in list { - table = table.with_row([value.node.to_string()]); + for (index, value) in list.into_iter().enumerate() { + if index == list.len() - 1 { + write!(f, "{}", value.node)?; + } else { + write!(f, "{}, ", value.node)?; + } } - write!(f, "{}", Console::default().render(&table)) + write!(f, "]") } ValueInner::Map(map) => { - let mut table = create_table(); + write!(f, "[")?; - for (identifier, value) in map { - table = table.with_row([identifier.as_str(), &value.to_string()]); + for (key, value) in map { + writeln!(f, "{key} = {value},")?; } - write!(f, "{}", Console::default().render(&table)) + write!(f, "]") } ValueInner::Range(_) => todo!(), ValueInner::String(string) => write!(f, "{string}"), @@ -161,13 +155,13 @@ impl Display for Value { write!(f, "): {} {:?}", return_type.node, body.node) } ValueInner::Structure { name, fields } => { - let mut table = create_table(); + write!(f, "{}\n{{", name.node)?; - for (identifier, value) in fields { - table = table.with_row([identifier.as_str(), &value.to_string()]); + for (key, value) in fields { + writeln!(f, "{key} = {value},")?; } - write!(f, "{}\n{}", name.node, Console::default().render(&table)) + write!(f, "}}") } } } @@ -318,11 +312,15 @@ impl Function { &self.type_parameters } - pub fn call(self, arguments: Vec, context: Context) -> Result { + pub fn call( + self, + arguments: Vec, + context: &mut Context, + ) -> Result { for ((identifier, _), value) in self.parameters.into_iter().zip(arguments.into_iter()) { context.set_value(identifier.clone(), value)?; } - self.body.node.run(&context) + self.body.node.run(context, true) } } diff --git a/dust-shell/src/cli.rs b/dust-shell/src/cli.rs index c7e8aef..5b9a647 100644 --- a/dust-shell/src/cli.rs +++ b/dust-shell/src/cli.rs @@ -19,7 +19,7 @@ use reedline::{ }; pub fn run_shell(context: Context) -> Result<(), io::Error> { - let interpreter = Interpreter::new(context.clone()); + let mut interpreter = Interpreter::new(context.clone()); let mut keybindings = default_emacs_keybindings(); keybindings.add_binding( @@ -222,7 +222,7 @@ impl Completer for DustCompleter { } } - for (key, value_data) in self.context.inner().unwrap().iter() { + for (key, (value_data, _)) in self.context.inner().unwrap().iter() { let description = match value_data { ValueData::Value(value) => value.to_string(), ValueData::Type(r#type) => r#type.to_string(),