diff --git a/src/abstract_tree/assignment.rs b/src/abstract_tree/assignment.rs index b46bf56..4ecc505 100644 --- a/src/abstract_tree/assignment.rs +++ b/src/abstract_tree/assignment.rs @@ -1,10 +1,9 @@ use crate::{ error::{RuntimeError, ValidationError}, - value::Value, Context, }; -use super::{AbstractTree, Identifier, Statement, Type}; +use super::{AbstractTree, Action, Identifier, Statement, Type}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct Assignment<'src> { @@ -42,12 +41,16 @@ impl<'src> AbstractTree for Assignment<'src> { Ok(()) } - fn run(self, context: &Context) -> Result { - let value = self.statement.run(context)?; + fn run(self, context: &Context) -> Result { + let action = self.statement.run(context)?; + let value = match action { + Action::Return(value) => value, + r#break => return Ok(r#break), + }; context.set_value(self.identifier, value)?; - Ok(Value::none()) + Ok(Action::None) } } @@ -56,6 +59,7 @@ mod tests { use crate::{ abstract_tree::{Expression, ValueNode}, error::TypeCheckError, + Value, }; use super::*; diff --git a/src/abstract_tree/block.rs b/src/abstract_tree/block.rs index b588258..2ce6340 100644 --- a/src/abstract_tree/block.rs +++ b/src/abstract_tree/block.rs @@ -4,7 +4,7 @@ use crate::{ Value, }; -use super::{AbstractTree, Statement, Type}; +use super::{AbstractTree, Action, Statement, Type}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct Block<'src> { @@ -32,14 +32,18 @@ impl<'src> AbstractTree for Block<'src> { Ok(()) } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &Context) -> Result { let mut previous = Value::none(); for statement in self.statements { - previous = statement.run(_context)?; + let action = statement.run(_context)?; + previous = match action { + Action::Return(value) => value, + r#break => return Ok(r#break), + }; } - Ok(previous) + Ok(Action::Return(previous)) } } @@ -57,7 +61,10 @@ mod tests { Statement::Expression(Expression::Value(ValueNode::Integer(42))), ]); - assert_eq!(block.run(&Context::new()), Ok(Value::integer(42))) + assert_eq!( + block.run(&Context::new()), + Ok(Action::Return(Value::integer(42))) + ) } #[test] diff --git a/src/abstract_tree/expression.rs b/src/abstract_tree/expression.rs index 4d181fe..b1cd6d4 100644 --- a/src/abstract_tree/expression.rs +++ b/src/abstract_tree/expression.rs @@ -1,10 +1,9 @@ use crate::{ context::Context, error::{RuntimeError, ValidationError}, - Value, }; -use super::{AbstractTree, Identifier, Index, Logic, Math, Type, ValueNode}; +use super::{AbstractTree, Action, Identifier, Index, Logic, Math, Type, ValueNode}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Expression<'src> { @@ -36,7 +35,7 @@ impl<'src> AbstractTree for Expression<'src> { } } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &Context) -> Result { match self { Expression::Identifier(identifier) => identifier.run(_context), Expression::Index(index) => index.run(_context), diff --git a/src/abstract_tree/identifier.rs b/src/abstract_tree/identifier.rs index 466ab4f..b644f00 100644 --- a/src/abstract_tree/identifier.rs +++ b/src/abstract_tree/identifier.rs @@ -9,7 +9,7 @@ use crate::{ Value, }; -use super::{AbstractTree, Type}; +use super::{AbstractTree, Action, Type}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct Identifier(Arc); @@ -41,13 +41,13 @@ impl AbstractTree for Identifier { } } - fn run(self, context: &Context) -> Result { + fn run(self, context: &Context) -> Result { let value = context .get_value(&self)? .unwrap_or_else(Value::none) .clone(); - Ok(value) + Ok(Action::Return(value)) } } diff --git a/src/abstract_tree/index.rs b/src/abstract_tree/index.rs index bd145bb..26a4236 100644 --- a/src/abstract_tree/index.rs +++ b/src/abstract_tree/index.rs @@ -4,7 +4,7 @@ use crate::{ Value, }; -use super::{AbstractTree, Expression, Type, ValueNode}; +use super::{AbstractTree, Action, Expression, Type, ValueNode}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct Index<'src> { @@ -58,15 +58,16 @@ impl<'src> AbstractTree for Index<'src> { } } - fn run(self, _context: &Context) -> Result { - let left_value = self.left.run(_context)?; - let right_value = self.right.run(_context)?; + fn run(self, _context: &Context) -> Result { + let left_value = self.left.run(_context)?.as_return_value()?; + let right_value = self.right.run(_context)?.as_return_value()?; if let (Some(list), Some(index)) = (left_value.as_list(), right_value.as_integer()) { - Ok(list - .get(index as usize) - .cloned() - .unwrap_or_else(Value::none)) + Ok(Action::Return( + list.get(index as usize) + .cloned() + .unwrap_or_else(Value::none), + )) } else { Err(RuntimeError::ValidationFailure( ValidationError::CannotIndexWith(left_value.r#type(), right_value.r#type()), diff --git a/src/abstract_tree/logic.rs b/src/abstract_tree/logic.rs index df89a7e..886935f 100644 --- a/src/abstract_tree/logic.rs +++ b/src/abstract_tree/logic.rs @@ -4,7 +4,7 @@ use crate::{ Value, }; -use super::{AbstractTree, Expression, Type}; +use super::{AbstractTree, Action, Expression, Type}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Logic<'src> { @@ -59,24 +59,64 @@ impl<'src> AbstractTree for Logic<'src> { } } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &Context) -> Result { let boolean = match self { - Logic::Equal(left, right) => left.run(_context)? == right.run(_context)?, - Logic::NotEqual(left, right) => left.run(_context)? != right.run(_context)?, - Logic::Greater(left, right) => left.run(_context)? > right.run(_context)?, - Logic::Less(left, right) => left.run(_context)? < right.run(_context)?, - Logic::GreaterOrEqual(left, right) => left.run(_context)? >= right.run(_context)?, - Logic::LessOrEqual(left, right) => left.run(_context)? <= right.run(_context)?, + Logic::Equal(left, right) => { + let left = left.run(_context)?.as_return_value()?; + let right = right.run(_context)?.as_return_value()?; + + left == right + } + Logic::NotEqual(left, right) => { + let left = left.run(_context)?.as_return_value()?; + let right = right.run(_context)?.as_return_value()?; + + left != right + } + Logic::Greater(left, right) => { + let left = left.run(_context)?.as_return_value()?; + let right = right.run(_context)?.as_return_value()?; + + left > right + } + Logic::Less(left, right) => { + let left = left.run(_context)?.as_return_value()?; + let right = right.run(_context)?.as_return_value()?; + + left < right + } + Logic::GreaterOrEqual(left, right) => { + let left = left.run(_context)?.as_return_value()?; + let right = right.run(_context)?.as_return_value()?; + + left >= right + } + Logic::LessOrEqual(left, right) => { + let left = left.run(_context)?.as_return_value()?; + let right = right.run(_context)?.as_return_value()?; + + left <= right + } Logic::And(left, right) => { - left.run(_context)?.as_boolean()? && right.run(_context)?.as_boolean()? + let left = left.run(_context)?.as_return_value()?.as_boolean()?; + let right = right.run(_context)?.as_return_value()?.as_boolean()?; + + left && right } Logic::Or(left, right) => { - left.run(_context)?.as_boolean()? || right.run(_context)?.as_boolean()? + let left = left.run(_context)?.as_return_value()?.as_boolean()?; + let right = right.run(_context)?.as_return_value()?.as_boolean()?; + + left || right + } + Logic::Not(statement) => { + let boolean = statement.run(_context)?.as_return_value()?.as_boolean()?; + + !boolean } - Logic::Not(statement) => !statement.run(_context)?.as_boolean()?, }; - Ok(Value::boolean(boolean)) + Ok(Action::Return(Value::boolean(boolean))) } } @@ -94,6 +134,8 @@ mod tests { ) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()) } @@ -106,6 +148,8 @@ mod tests { ) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()) } @@ -118,6 +162,8 @@ mod tests { ) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()) } @@ -130,6 +176,8 @@ mod tests { ) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()) } @@ -142,6 +190,8 @@ mod tests { ) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()); @@ -151,6 +201,8 @@ mod tests { ) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()) } @@ -163,6 +215,8 @@ mod tests { ) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()); @@ -172,6 +226,8 @@ mod tests { ) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()) } @@ -184,6 +240,8 @@ mod tests { ) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()) } @@ -196,6 +254,8 @@ mod tests { ) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()) } @@ -205,6 +265,8 @@ mod tests { assert!(Logic::Not(Expression::Value(ValueNode::Boolean(false))) .run(&Context::new()) .unwrap() + .as_return_value() + .unwrap() .as_boolean() .unwrap()) } diff --git a/src/abstract_tree/loop.rs b/src/abstract_tree/loop.rs index 8172615..2767cd6 100644 --- a/src/abstract_tree/loop.rs +++ b/src/abstract_tree/loop.rs @@ -1,10 +1,9 @@ use crate::{ context::Context, error::{RuntimeError, ValidationError}, - Value, }; -use super::{AbstractTree, Statement, Type}; +use super::{AbstractTree, Action, Statement, Type}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct Loop<'src> { @@ -19,14 +18,54 @@ impl<'src> Loop<'src> { impl<'src> AbstractTree for Loop<'src> { fn expected_type(&self, _context: &Context) -> Result { - todo!() + Ok(Type::None) } fn validate(&self, _context: &Context) -> Result<(), ValidationError> { - todo!() + for statement in &self.statements { + statement.validate(_context)?; + } + + Ok(()) } - fn run(self, _: &Context) -> Result { - todo!() + fn run(self, _context: &Context) -> Result { + let mut index = 0; + + loop { + if index == self.statements.len() - 1 { + index = 0; + } else { + index += 1; + } + + let statement = self.statements[index].clone(); + + if let Statement::Break(expression) = statement { + break expression.run(_context); + } else { + statement.run(_context)?; + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + abstract_tree::{Expression, ValueNode}, + Value, + }; + + use super::*; + + #[test] + fn basic_loop() { + let result = Loop { + statements: vec![Statement::Break(Expression::Value(ValueNode::Integer(42)))], + } + .run(&Context::new()); + + assert_eq!(result, Ok(Action::Return(Value::integer(42)))) } } diff --git a/src/abstract_tree/math.rs b/src/abstract_tree/math.rs index 67ebd41..a8e7b0f 100644 --- a/src/abstract_tree/math.rs +++ b/src/abstract_tree/math.rs @@ -5,7 +5,7 @@ use crate::{ Value, }; -use super::{AbstractTree, Expression, Type}; +use super::{AbstractTree, Action, Expression, Type}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Math<'src> { @@ -48,78 +48,80 @@ impl<'src> AbstractTree for Math<'src> { } } - fn run(self, _context: &Context) -> Result { - match self { + fn run(self, _context: &Context) -> Result { + let value = match self { Math::Add(left, right) => { - let left_value = left.run(_context)?; - let right_value = right.run(_context)?; + let left_value = left.run(_context)?.as_return_value()?; + let right_value = right.run(_context)?.as_return_value()?; if let (ValueInner::Integer(left), ValueInner::Integer(right)) = (left_value.inner().as_ref(), right_value.inner().as_ref()) { - Ok(Value::integer(left + right)) + Value::integer(left + right) } else { - Err(RuntimeError::ValidationFailure( + return Err(RuntimeError::ValidationFailure( ValidationError::ExpectedIntegerOrFloat, - )) + )); } } Math::Subtract(left, right) => { - let left_value = left.run(_context)?; - let right_value = right.run(_context)?; + let left_value = left.run(_context)?.as_return_value()?; + let right_value = right.run(_context)?.as_return_value()?; if let (ValueInner::Integer(left), ValueInner::Integer(right)) = (left_value.inner().as_ref(), right_value.inner().as_ref()) { - Ok(Value::integer(left - right)) + Value::integer(left - right) } else { - Err(RuntimeError::ValidationFailure( + return Err(RuntimeError::ValidationFailure( ValidationError::ExpectedIntegerOrFloat, - )) + )); } } Math::Multiply(left, right) => { - let left_value = left.run(_context)?; - let right_value = right.run(_context)?; + let left_value = left.run(_context)?.as_return_value()?; + let right_value = right.run(_context)?.as_return_value()?; if let (ValueInner::Integer(left), ValueInner::Integer(right)) = (left_value.inner().as_ref(), right_value.inner().as_ref()) { - Ok(Value::integer(left * right)) + Value::integer(left * right) } else { - Err(RuntimeError::ValidationFailure( + return Err(RuntimeError::ValidationFailure( ValidationError::ExpectedIntegerOrFloat, - )) + )); } } Math::Divide(left, right) => { - let left_value = left.run(_context)?; - let right_value = right.run(_context)?; + let left_value = left.run(_context)?.as_return_value()?; + let right_value = right.run(_context)?.as_return_value()?; if let (ValueInner::Integer(left), ValueInner::Integer(right)) = (left_value.inner().as_ref(), right_value.inner().as_ref()) { - Ok(Value::integer(left / right)) + Value::integer(left / right) } else { - Err(RuntimeError::ValidationFailure( + return Err(RuntimeError::ValidationFailure( ValidationError::ExpectedIntegerOrFloat, - )) + )); } } Math::Modulo(left, right) => { - let left_value = left.run(_context)?; - let right_value = right.run(_context)?; + let left_value = left.run(_context)?.as_return_value()?; + let right_value = right.run(_context)?.as_return_value()?; if let (ValueInner::Integer(left), ValueInner::Integer(right)) = (left_value.inner().as_ref(), right_value.inner().as_ref()) { - Ok(Value::integer(left % right)) + Value::integer(left % right) } else { - Err(RuntimeError::ValidationFailure( + return Err(RuntimeError::ValidationFailure( ValidationError::ExpectedIntegerOrFloat, - )) + )); } } - } + }; + + Ok(Action::Return(value)) } } diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index f598672..aebd480 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -25,5 +25,22 @@ use crate::{ pub trait AbstractTree { fn expected_type(&self, context: &Context) -> Result; fn validate(&self, context: &Context) -> Result<(), ValidationError>; - fn run(self, context: &Context) -> Result; + fn run(self, context: &Context) -> Result; +} + +#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)] +pub enum Action { + Break(Value), + Return(Value), + None, +} + +impl Action { + pub fn as_return_value(self) -> Result { + if let Action::Return(value) = self { + Ok(value) + } else { + Err(ValidationError::InterpreterExpectedReturn) + } + } } diff --git a/src/abstract_tree/statement.rs b/src/abstract_tree/statement.rs index 4d3c449..e226713 100644 --- a/src/abstract_tree/statement.rs +++ b/src/abstract_tree/statement.rs @@ -3,12 +3,13 @@ use crate::{ error::{RuntimeError, ValidationError}, }; -use super::{AbstractTree, Assignment, Block, Expression, Loop, Type, Value}; +use super::{AbstractTree, Action, Assignment, Block, Expression, Loop, Type}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Statement<'src> { Assignment(Assignment<'src>), Block(Block<'src>), + Break(Expression<'src>), Expression(Expression<'src>), Loop(Loop<'src>), } @@ -18,6 +19,7 @@ impl<'src> AbstractTree for Statement<'src> { match self { Statement::Assignment(assignment) => assignment.expected_type(_context), Statement::Block(block) => block.expected_type(_context), + Statement::Break(expression) => expression.expected_type(_context), Statement::Expression(expression) => expression.expected_type(_context), Statement::Loop(r#loop) => r#loop.expected_type(_context), } @@ -27,17 +29,19 @@ impl<'src> AbstractTree for Statement<'src> { match self { Statement::Assignment(assignment) => assignment.validate(_context), Statement::Block(_) => todo!(), + Statement::Break(expression) => expression.validate(_context), Statement::Expression(expression) => expression.validate(_context), - Statement::Loop(_) => todo!(), + Statement::Loop(r#loop) => r#loop.validate(_context), } } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &Context) -> Result { match self { Statement::Assignment(assignment) => assignment.run(_context), Statement::Block(_) => todo!(), + Statement::Break(expression) => expression.run(_context), Statement::Expression(expression) => expression.run(_context), - Statement::Loop(_) => todo!(), + Statement::Loop(r#loop) => r#loop.run(_context), } } } diff --git a/src/abstract_tree/type.rs b/src/abstract_tree/type.rs index 3b96ad9..8dabb79 100644 --- a/src/abstract_tree/type.rs +++ b/src/abstract_tree/type.rs @@ -4,10 +4,9 @@ use crate::{ abstract_tree::Identifier, context::Context, error::{RuntimeError, TypeCheckError, ValidationError}, - Value, }; -use super::AbstractTree; +use super::{AbstractTree, Action}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Type { @@ -100,8 +99,8 @@ impl AbstractTree for Type { Ok(()) } - fn run(self, _: &Context) -> Result { - Ok(Value::none()) + fn run(self, _: &Context) -> Result { + Ok(Action::None) } } diff --git a/src/abstract_tree/value_node.rs b/src/abstract_tree/value_node.rs index f06fce0..c5030a0 100644 --- a/src/abstract_tree/value_node.rs +++ b/src/abstract_tree/value_node.rs @@ -6,7 +6,7 @@ use crate::{ Value, }; -use super::{AbstractTree, Expression, Identifier, Type}; +use super::{AbstractTree, Action, Expression, Identifier, Type}; #[derive(Clone, Debug, PartialEq)] pub enum ValueNode<'src> { @@ -48,7 +48,7 @@ impl<'src> AbstractTree for ValueNode<'src> { Ok(()) } - fn run(self, _context: &Context) -> Result { + fn run(self, _context: &Context) -> Result { let value = match self { ValueNode::Boolean(boolean) => Value::boolean(boolean), ValueNode::Float(float) => Value::float(float), @@ -57,7 +57,7 @@ impl<'src> AbstractTree for ValueNode<'src> { let mut value_list = Vec::with_capacity(expression_list.len()); for expression in expression_list { - let value = expression.run(_context)?; + let value = expression.run(_context)?.as_return_value()?; value_list.push(value); } @@ -68,7 +68,7 @@ impl<'src> AbstractTree for ValueNode<'src> { let mut property_map = BTreeMap::new(); for (identifier, _type, expression) in property_list { - let value = expression.run(_context)?; + let value = expression.run(_context)?.as_return_value()?; property_map.insert(identifier, value); } @@ -86,7 +86,7 @@ impl<'src> AbstractTree for ValueNode<'src> { } }; - Ok(value) + Ok(Action::Return(value)) } } diff --git a/src/error.rs b/src/error.rs index bad8a92..c4b553e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -87,6 +87,7 @@ impl Error { } ValidationError::CannotIndex(_) => todo!(), ValidationError::CannotIndexWith(_, _) => todo!(), + ValidationError::InterpreterExpectedReturn => todo!(), } report.finish() @@ -143,6 +144,7 @@ pub enum ValidationError { CannotIndexWith(Type, Type), ExpectedBoolean, ExpectedIntegerOrFloat, + InterpreterExpectedReturn, RwLockPoison(RwLockPoisonError), TypeCheck(TypeCheckError), VariableNotFound(Identifier), diff --git a/src/lexer.rs b/src/lexer.rs index 5fc1d81..930e470 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -207,6 +207,7 @@ pub fn lexer<'src>() -> impl Parser< let keyword = choice(( just("bool").padded(), + just("break").padded(), just("float").padded(), just("int").padded(), just("list").padded(), diff --git a/src/lib.rs b/src/lib.rs index 178d41e..15ec7c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,14 +5,14 @@ pub mod lexer; pub mod parser; pub mod value; -use abstract_tree::AbstractTree; +use abstract_tree::{AbstractTree, Action}; use context::Context; use error::Error; use lexer::lex; pub use parser::{parse, parser, DustParser}; pub use value::Value; -pub fn interpret(source: &str) -> Result> { +pub fn interpret(source: &str) -> Result, Vec> { let context = Context::new(); let mut interpreter = Interpreter::new(context); @@ -28,7 +28,7 @@ impl Interpreter { Interpreter { context } } - pub fn run(&mut self, source: &str) -> Result> { + pub fn run(&mut self, source: &str) -> Result, Vec> { let tokens = lex(source)?; let statements = parse(&tokens)?; let errors = statements @@ -48,11 +48,15 @@ impl Interpreter { return Err(errors); } - let mut value = Value::none(); + let mut value = None; for (statement, _span) in statements { value = match statement.run(&self.context) { - Ok(value) => value, + Ok(action) => match action { + Action::Break(value) => Some(value), + Action::Return(value) => Some(value), + Action::None => continue, + }, Err(runtime_error) => return Err(vec![Error::Runtime(runtime_error)]), } } diff --git a/src/main.rs b/src/main.rs index 21e36ba..d17d66b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,7 +48,7 @@ fn main() { match eval_result { Ok(value) => { - if !value.is_none() { + if let Some(value) = value { println!("{value}") } } diff --git a/src/parser.rs b/src/parser.rs index 31803e2..9e548df 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -212,9 +212,14 @@ pub fn parser<'src>() -> DustParser<'src> { let statement = recursive(|statement| { let expression_statement = expression + .clone() .map(|expression| Statement::Expression(expression)) .boxed(); + let r#break = just(Token::Keyword("break")) + .ignore_then(expression) + .map(|expression| Statement::Break(expression)); + let assignment = identifier .then(type_specification.clone().or_not()) .then_ignore(just(Token::Operator(Operator::Assign))) @@ -238,6 +243,7 @@ pub fn parser<'src>() -> DustParser<'src> { let r#loop = statement .clone() .repeated() + .at_least(1) .collect() .delimited_by( just(Token::Keyword("loop")).then(just(Token::Control(Control::CurlyOpen))), @@ -246,7 +252,7 @@ pub fn parser<'src>() -> DustParser<'src> { .map(|statements| Statement::Loop(Loop::new(statements))) .boxed(); - choice((assignment, expression_statement, block, r#loop)) + choice((assignment, expression_statement, r#break, block, r#loop)) .then_ignore(just(Token::Control(Control::Semicolon)).or_not()) }); @@ -319,8 +325,10 @@ mod tests { #[test] fn r#loop() { assert_eq!( - parse(&lex("loop {}").unwrap()).unwrap()[0].0, - Statement::Loop(Loop::new(vec![])) + parse(&lex("loop { 42 }").unwrap()).unwrap()[0].0, + Statement::Loop(Loop::new(vec![Statement::Expression(Expression::Value( + ValueNode::Integer(42) + ))])) ); } diff --git a/tests/expressions.rs b/tests/expressions.rs index 63d9935..70bd8ad 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -2,23 +2,26 @@ use dust_lang::*; #[test] fn logic() { - assert_eq!(interpret("1 == 1"), Ok(Value::boolean(true))); + assert_eq!(interpret("1 == 1"), Ok(Some(Value::boolean(true)))); assert_eq!( interpret("('42' == '42') && (42 != 0)"), - Ok(Value::boolean(true)) + Ok(Some(Value::boolean(true))) ); } #[test] fn math() { - assert_eq!(interpret("1 + 1"), Ok(Value::integer(2))); + assert_eq!(interpret("1 + 1"), Ok(Some(Value::integer(2)))); assert_eq!( interpret("2 * (21 + 19 + 1 * 2) / 2"), - Ok(Value::integer(42)) + Ok(Some(Value::integer(42))) ); } #[test] fn list_index() { - assert_eq!(interpret("foo = [1, 2, 3]; foo.2"), Ok(Value::integer(3))); + assert_eq!( + interpret("foo = [1, 2, 3]; foo.2"), + Ok(Some(Value::integer(3))) + ); } diff --git a/tests/statements.rs b/tests/statements.rs new file mode 100644 index 0000000..a9ce096 --- /dev/null +++ b/tests/statements.rs @@ -0,0 +1,20 @@ +use dust_lang::*; + +#[test] +fn loops_and_breaks() { + assert_eq!( + interpret( + " + i = 0; + loop { + if i == 3 { + break 'foobar' + } else { + i += 1 + } + } + " + ), + Ok(Some(Value::string("foobar"))) + ) +} diff --git a/tests/variables.rs b/tests/variables.rs index b41fe46..12a1d20 100644 --- a/tests/variables.rs +++ b/tests/variables.rs @@ -6,14 +6,17 @@ use dust_lang::{ #[test] fn set_and_get_variable() { - assert_eq!(interpret("foobar = true; foobar"), Ok(Value::boolean(true))); + assert_eq!( + interpret("foobar = true; foobar"), + Ok(Some(Value::boolean(true))) + ); } #[test] fn set_variable_with_type() { assert_eq!( interpret("foobar: bool = true; foobar"), - Ok(Value::boolean(true)) + Ok(Some(Value::boolean(true))) ); }