diff --git a/src/engine.rs b/src/engine.rs index 76057cb4..ba67c959 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1153,6 +1153,19 @@ impl Engine { } } } + Stmt::Loop(ref body) => { + loop { + match self.eval_stmt(scope, body) { + Err(EvalAltResult::LoopBreak) => { + return Ok(Box::new(())); + } + Err(x) => { + return Err(x); + } + _ => (), + } + } + } Stmt::Break => Err(EvalAltResult::LoopBreak), Stmt::Return => Err(EvalAltResult::Return(Box::new(()))), Stmt::ReturnWithVal(ref a) => { diff --git a/src/parser.rs b/src/parser.rs index 701ab509..2561b7a2 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -90,6 +90,7 @@ pub enum Stmt { If(Box, Box), IfElse(Box, Box, Box), While(Box, Box), + Loop(Box), Var(String, Option>), Block(Vec), Expr(Box), @@ -142,6 +143,7 @@ pub enum Token { If, Else, While, + Loop, LessThan, GreaterThan, Bang, @@ -398,6 +400,7 @@ impl<'a> TokenIterator<'a> { "if" => return Some(Token::If), "else" => return Some(Token::Else), "while" => return Some(Token::While), + "loop" => return Some(Token::Loop), "break" => return Some(Token::Break), "return" => return Some(Token::Return), "fn" => return Some(Token::Fn), @@ -805,6 +808,14 @@ fn parse_while<'a>(input: &mut Peekable>) -> Result(input: &mut Peekable>) -> Result { + input.next(); + + let body = try!(parse_block(input)); + + Ok(Stmt::Loop(Box::new(body))) +} + fn parse_var<'a>(input: &mut Peekable>) -> Result { input.next(); @@ -868,6 +879,7 @@ fn parse_stmt<'a>(input: &mut Peekable>) -> Result parse_if(input), Some(&Token::While) => parse_while(input), + Some(&Token::Loop) => parse_loop(input), Some(&Token::Break) => { input.next(); Ok(Stmt::Break) diff --git a/src/tests.rs b/src/tests.rs index acec7717..98f75862 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -410,3 +410,50 @@ fn test_comments() { assert!(engine.eval::("let /* I am a multiline comment, yay! */ x = 5; x").is_ok()); } + +#[test] +fn test_unary_minus() { + let mut engine = Engine::new(); + + assert_eq!(engine.eval::("let x = -5; x").unwrap(), -5); + + assert_eq!(engine.eval::("fn n(x) { -x } n(5)").unwrap(), -5); + + assert_eq!(engine.eval::("5 - -(-5)").unwrap(), 0); +} + +#[test] +fn test_not() { + let mut engine = Engine::new(); + + assert_eq!(engine.eval::("let not_true = !true; not_true").unwrap(), false); + + assert_eq!(engine.eval::("fn not(x) { !x } not(false)").unwrap(), true); + + // TODO - do we allow stacking unary operators directly? e.g '!!!!!!!true' + assert_eq!(engine.eval::("!(!(!(!(true))))").unwrap(), true) +} + +#[test] +fn test_loop() { + let mut engine = Engine::new(); + + assert!( + engine.eval::(" + let x = 0; + let i = 0; + + loop { + if i < 10 { + x = x + i; + i = i + 1; + } + else { + break; + } + } + + x == 45 + ").unwrap() + ) +}