From c3402394a23899f4ebe94cf7b8b382a72155d86d Mon Sep 17 00:00:00 2001 From: Jeff Date: Sun, 17 Mar 2024 16:59:52 -0400 Subject: [PATCH] Rewrite while loops --- src/abstract_tree/function_call.rs | 30 ++++++++-- src/abstract_tree/if_else.rs | 36 +++++++---- src/abstract_tree/logic.rs | 82 ++++++++++++++++++++----- src/abstract_tree/while.rs | 96 +++++++++++++++++++++++++----- src/error.rs | 24 ++++++-- src/parser.rs | 25 +++++--- src/value.rs | 24 ++------ 7 files changed, 235 insertions(+), 82 deletions(-) diff --git a/src/abstract_tree/function_call.rs b/src/abstract_tree/function_call.rs index 55cb38f..c1be10b 100644 --- a/src/abstract_tree/function_call.rs +++ b/src/abstract_tree/function_call.rs @@ -1,6 +1,7 @@ use crate::{ context::Context, error::{RuntimeError, ValidationError}, + value::ValueInner, }; use super::{AbstractTree, Action, Expression, Type, WithPosition}; @@ -25,24 +26,43 @@ impl FunctionCall { impl AbstractTree for FunctionCall { fn expected_type(&self, _context: &Context) -> Result { - if let Type::Function { return_type, .. } = self.function.node.expected_type(_context)? { + let function_node_type = self.function.node.expected_type(_context)?; + + if let Type::Function { return_type, .. } = function_node_type { Ok(*return_type) } else { - Err(ValidationError::ExpectedFunction) + Err(ValidationError::ExpectedFunction { + actual: function_node_type, + position: self.function.position, + }) } } fn validate(&self, _context: &Context) -> Result<(), ValidationError> { - if let Type::Function { .. } = self.function.node.expected_type(_context)? { + let function_node_type = self.function.node.expected_type(_context)?; + + if let Type::Function { .. } = function_node_type { Ok(()) } else { - Err(ValidationError::ExpectedFunction) + Err(ValidationError::ExpectedFunction { + actual: function_node_type, + position: self.function.position, + }) } } fn run(self, context: &Context) -> Result { let value = self.function.node.run(context)?.as_return_value()?; - let function = value.as_function()?; + let function = if let ValueInner::Function(function) = value.inner().as_ref() { + function + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedFunction { + actual: value.r#type(), + position: self.function.position, + }, + )); + }; let mut arguments = Vec::with_capacity(self.arguments.len()); for expression in self.arguments { diff --git a/src/abstract_tree/if_else.rs b/src/abstract_tree/if_else.rs index 869148b..078bddc 100644 --- a/src/abstract_tree/if_else.rs +++ b/src/abstract_tree/if_else.rs @@ -1,6 +1,7 @@ use crate::{ context::Context, error::{RuntimeError, ValidationError}, + value::ValueInner, }; use super::{AbstractTree, Action, Block, Expression, Type, WithPosition}; @@ -35,7 +36,9 @@ impl AbstractTree for IfElse { self.if_expression.node.validate(context)?; self.if_block.validate(context)?; - if let Type::Boolean = self.if_expression.node.expected_type(context)? { + let if_expression_type = self.if_expression.node.expected_type(context)?; + + if let Type::Boolean = if_expression_type { if let Some(else_block) = &self.else_block { else_block.validate(context)?; @@ -53,24 +56,31 @@ impl AbstractTree for IfElse { Ok(()) } else { - Err(ValidationError::ExpectedBoolean) + Err(ValidationError::ExpectedBoolean { + actual: if_expression_type, + position: self.if_expression.position, + }) } } fn run(self, _context: &Context) -> Result { - let if_boolean = self - .if_expression - .node - .run(_context)? - .as_return_value()? - .as_boolean()?; + let value = self.if_expression.node.run(_context)?.as_return_value()?; - if if_boolean { - self.if_block.run(_context) - } else if let Some(else_statement) = self.else_block { - else_statement.run(_context) + if let ValueInner::Boolean(if_boolean) = value.inner().as_ref() { + if *if_boolean { + self.if_block.run(_context) + } else if let Some(else_statement) = self.else_block { + else_statement.run(_context) + } else { + Ok(Action::None) + } } else { - Ok(Action::None) + Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedBoolean { + actual: value.r#type(), + position: self.if_expression.position, + }, + )) } } } diff --git a/src/abstract_tree/logic.rs b/src/abstract_tree/logic.rs index de04c5a..32adbef 100644 --- a/src/abstract_tree/logic.rs +++ b/src/abstract_tree/logic.rs @@ -1,6 +1,7 @@ use crate::{ context::Context, error::{RuntimeError, ValidationError}, + value::ValueInner, Value, }; @@ -52,14 +53,20 @@ impl AbstractTree for Logic { if let (Type::Boolean, Type::Boolean) = (left, right) { Ok(()) } else { - Err(ValidationError::ExpectedBoolean) + Err(ValidationError::ExpectedBoolean { + actual: todo!(), + position: todo!(), + }) } } Logic::Not(expression) => { if let Type::Boolean = expression.node.expected_type(context)? { Ok(()) } else { - Err(ValidationError::ExpectedBoolean) + Err(ValidationError::ExpectedBoolean { + actual: todo!(), + position: todo!(), + }) } } } @@ -104,25 +111,72 @@ impl AbstractTree for Logic { left <= right } Logic::And(left, right) => { - let left = left.node.run(_context)?.as_return_value()?.as_boolean()?; - let right = right.node.run(_context)?.as_return_value()?.as_boolean()?; + let left_value = left.node.run(_context)?.as_return_value()?; + let right_value = right.node.run(_context)?.as_return_value()?; - left && right + let left = if let ValueInner::Boolean(boolean) = left_value.inner().as_ref() { + boolean + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedBoolean { + actual: left_value.r#type(), + position: left.position, + }, + )); + }; + let right = if let ValueInner::Boolean(boolean) = right_value.inner().as_ref() { + boolean + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedBoolean { + actual: right_value.r#type(), + position: right.position, + }, + )); + }; + + *left && *right } Logic::Or(left, right) => { - let left = left.node.run(_context)?.as_return_value()?.as_boolean()?; - let right = right.node.run(_context)?.as_return_value()?.as_boolean()?; + let left_value = left.node.run(_context)?.as_return_value()?; + let right_value = right.node.run(_context)?.as_return_value()?; - left || right + let left = if let ValueInner::Boolean(boolean) = left_value.inner().as_ref() { + boolean + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedBoolean { + actual: left_value.r#type(), + position: left.position, + }, + )); + }; + let right = if let ValueInner::Boolean(boolean) = right_value.inner().as_ref() { + boolean + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedBoolean { + actual: right_value.r#type(), + position: right.position, + }, + )); + }; + + *left || *right } Logic::Not(statement) => { - let boolean = statement - .node - .run(_context)? - .as_return_value()? - .as_boolean()?; + let value = statement.node.run(_context)?.as_return_value()?; - !boolean + if let ValueInner::Boolean(boolean) = value.inner().as_ref() { + !boolean + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedBoolean { + actual: value.r#type(), + position: statement.position, + }, + )); + } } }; diff --git a/src/abstract_tree/while.rs b/src/abstract_tree/while.rs index 215c399..cbb5cae 100644 --- a/src/abstract_tree/while.rs +++ b/src/abstract_tree/while.rs @@ -1,19 +1,27 @@ use crate::{ context::Context, error::{RuntimeError, ValidationError}, + value::ValueInner, + Value, }; -use super::{AbstractTree, Action, Block, Expression, Type, WithPosition}; +use super::{AbstractTree, Action, Expression, Statement, Type, WithPosition}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct While { expression: WithPosition, - block: Block, + statements: Vec>, } impl While { - pub fn new(expression: WithPosition, block: Block) -> Self { - Self { expression, block } + pub fn new( + expression: WithPosition, + statements: Vec>, + ) -> Self { + Self { + expression, + statements, + } } } @@ -24,23 +32,79 @@ impl AbstractTree for While { fn validate(&self, _context: &Context) -> Result<(), ValidationError> { self.expression.node.validate(_context)?; - self.block.validate(_context) + + for statement in &self.statements { + statement.node.validate(_context)?; + } + + Ok(()) } - fn run(self, context: &Context) -> Result { - while self - .expression - .node - .clone() - .run(context)? - .as_return_value()? - .as_boolean()? - { - if let Action::Break = self.block.clone().run(context)? { - break; + fn run(self, _context: &Context) -> Result { + let get_boolean = || -> Result { + let value = self.expression.node.run(_context)?.as_return_value()?; + + Ok(value) + }; + + if let ValueInner::Boolean(boolean) = get_boolean()?.inner().as_ref() { + while *boolean { + for statement in &self.statements { + let action = statement.node.clone().run(_context)?; + + match action { + Action::Return(_) => {} + Action::None => {} + Action::Break => return Ok(Action::Break), + } + } } } Ok(Action::None) } } + +#[cfg(test)] +mod tests { + use crate::abstract_tree::{ + Assignment, AssignmentOperator, Block, Identifier, Logic, ValueNode, + }; + + use super::*; + + #[test] + fn simple_while_loop() { + let result = Statement::Block(Block::new(vec![ + Statement::Assignment(Assignment::new( + Identifier::new("i"), + None, + AssignmentOperator::Assign, + Statement::Expression(Expression::Value(ValueNode::Integer(3))) + .with_position((0, 0)), + )) + .with_position((0, 0)), + Statement::While(While { + expression: 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)), + statements: vec![Statement::Assignment(Assignment::new( + Identifier::new("i"), + 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)), + ])) + .run(&Context::new()); + + assert_eq!(result, Ok(Action::Return(Value::integer(3)))) + } +} diff --git a/src/error.rs b/src/error.rs index adcac14..17acd1e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -64,11 +64,17 @@ impl Error { } }, Error::Validation { error, position } => match error { - ValidationError::ExpectedBoolean => { - builder.add_label(Label::new(0..0).with_message("Expected boolean.")); + ValidationError::ExpectedBoolean { actual, position } => { + builder.add_label( + Label::new(position.0..position.1) + .with_message(format!("Expected boolean but got {actual}.")), + ); } ValidationError::ExpectedIntegerOrFloat => { - builder.add_label(Label::new(0..0).with_message("Expected integer or float.")); + builder.add_label( + Label::new(position.0..position.1) + .with_message("Expected integer or float."), + ); } ValidationError::RwLockPoison(_) => todo!(), ValidationError::TypeCheck { @@ -95,7 +101,7 @@ impl Error { ValidationError::CannotIndex(_) => todo!(), ValidationError::CannotIndexWith(_, _) => todo!(), ValidationError::InterpreterExpectedReturn => todo!(), - ValidationError::ExpectedFunction => todo!(), + ValidationError::ExpectedFunction { actual, position } => todo!(), ValidationError::ExpectedValue => todo!(), }, } @@ -144,8 +150,14 @@ impl From for RuntimeError { pub enum ValidationError { CannotIndex(Type), CannotIndexWith(Type, Type), - ExpectedBoolean, - ExpectedFunction, + ExpectedBoolean { + actual: Type, + position: SourcePosition, + }, + ExpectedFunction { + actual: Type, + position: SourcePosition, + }, ExpectedIntegerOrFloat, ExpectedValue, InterpreterExpectedReturn, diff --git a/src/parser.rs b/src/parser.rs index f70392e..1c0f489 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -386,9 +386,18 @@ pub fn parser<'src>() -> DustParser<'src> { let r#while = just(Token::Keyword("while")) .ignore_then(positioned_expression.clone()) - .then(block.clone()) - .map_with(|(expression, block), state| { - Statement::While(While::new(expression, block)).with_position(state.span()) + .then( + positioned_statement + .clone() + .repeated() + .collect() + .delimited_by( + just(Token::Control(Control::CurlyOpen)), + just(Token::Control(Control::CurlyClose)), + ), + ) + .map_with(|(expression, statements), state| { + Statement::While(While::new(expression, statements)).with_position(state.span()) }); let if_else = just(Token::Keyword("if")) @@ -431,14 +440,14 @@ mod tests { parse(&lex("while true { output('hi') }").unwrap()).unwrap()[0].node, Statement::While(While::new( Expression::Value(ValueNode::Boolean(true)).with_position((6, 11)), - Block::new(vec![Statement::Expression(Expression::FunctionCall( - FunctionCall::new( + vec![ + Statement::Expression(Expression::FunctionCall(FunctionCall::new( Expression::Identifier(Identifier::new("output")).with_position((13, 19)), vec![Expression::Value(ValueNode::String("hi".to_string())) .with_position((20, 24))] - ) - )) - .with_position((13, 26))]) + ))) + .with_position((13, 26)) + ] )) ) } diff --git a/src/value.rs b/src/value.rs index acc5676..1de6bac 100644 --- a/src/value.rs +++ b/src/value.rs @@ -93,28 +93,12 @@ impl Value { } } - pub fn as_boolean(&self) -> Result { + pub fn as_boolean(&self) -> Option { if let ValueInner::Boolean(boolean) = self.0.as_ref() { - return Ok(*boolean); + Some(*boolean) + } else { + None } - - Err(ValidationError::ExpectedBoolean) - } - - pub fn as_number(&self) -> Result { - if let ValueInner::Boolean(boolean) = self.0.as_ref() { - return Ok(*boolean); - } - - Err(ValidationError::ExpectedBoolean) - } - - pub fn as_function(&self) -> Result<&Function, ValidationError> { - if let ValueInner::Function(function) = self.0.as_ref() { - return Ok(function); - } - - Err(ValidationError::ExpectedFunction) } pub fn as_list(&self) -> Option<&Vec> {