impl loop and add tests for both unary operators and loops

This commit is contained in:
Lukáš Hozda 2017-10-28 16:40:38 +02:00
parent 679f0de6b6
commit caf8a411aa
3 changed files with 72 additions and 0 deletions

View File

@ -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::Break => Err(EvalAltResult::LoopBreak),
Stmt::Return => Err(EvalAltResult::Return(Box::new(()))), Stmt::Return => Err(EvalAltResult::Return(Box::new(()))),
Stmt::ReturnWithVal(ref a) => { Stmt::ReturnWithVal(ref a) => {

View File

@ -90,6 +90,7 @@ pub enum Stmt {
If(Box<Expr>, Box<Stmt>), If(Box<Expr>, Box<Stmt>),
IfElse(Box<Expr>, Box<Stmt>, Box<Stmt>), IfElse(Box<Expr>, Box<Stmt>, Box<Stmt>),
While(Box<Expr>, Box<Stmt>), While(Box<Expr>, Box<Stmt>),
Loop(Box<Stmt>),
Var(String, Option<Box<Expr>>), Var(String, Option<Box<Expr>>),
Block(Vec<Stmt>), Block(Vec<Stmt>),
Expr(Box<Expr>), Expr(Box<Expr>),
@ -142,6 +143,7 @@ pub enum Token {
If, If,
Else, Else,
While, While,
Loop,
LessThan, LessThan,
GreaterThan, GreaterThan,
Bang, Bang,
@ -398,6 +400,7 @@ impl<'a> TokenIterator<'a> {
"if" => return Some(Token::If), "if" => return Some(Token::If),
"else" => return Some(Token::Else), "else" => return Some(Token::Else),
"while" => return Some(Token::While), "while" => return Some(Token::While),
"loop" => return Some(Token::Loop),
"break" => return Some(Token::Break), "break" => return Some(Token::Break),
"return" => return Some(Token::Return), "return" => return Some(Token::Return),
"fn" => return Some(Token::Fn), "fn" => return Some(Token::Fn),
@ -805,6 +808,14 @@ fn parse_while<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Pars
Ok(Stmt::While(Box::new(guard), Box::new(body))) Ok(Stmt::While(Box::new(guard), Box::new(body)))
} }
fn parse_loop<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
input.next();
let body = try!(parse_block(input));
Ok(Stmt::Loop(Box::new(body)))
}
fn parse_var<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> { fn parse_var<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
input.next(); input.next();
@ -868,6 +879,7 @@ fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Parse
match input.peek() { match input.peek() {
Some(&Token::If) => parse_if(input), Some(&Token::If) => parse_if(input),
Some(&Token::While) => parse_while(input), Some(&Token::While) => parse_while(input),
Some(&Token::Loop) => parse_loop(input),
Some(&Token::Break) => { Some(&Token::Break) => {
input.next(); input.next();
Ok(Stmt::Break) Ok(Stmt::Break)

View File

@ -410,3 +410,50 @@ fn test_comments() {
assert!(engine.eval::<i64>("let /* I am a multiline comment, yay! */ x = 5; x").is_ok()); assert!(engine.eval::<i64>("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::<i64>("let x = -5; x").unwrap(), -5);
assert_eq!(engine.eval::<i64>("fn n(x) { -x } n(5)").unwrap(), -5);
assert_eq!(engine.eval::<i64>("5 - -(-5)").unwrap(), 0);
}
#[test]
fn test_not() {
let mut engine = Engine::new();
assert_eq!(engine.eval::<bool>("let not_true = !true; not_true").unwrap(), false);
assert_eq!(engine.eval::<bool>("fn not(x) { !x } not(false)").unwrap(), true);
// TODO - do we allow stacking unary operators directly? e.g '!!!!!!!true'
assert_eq!(engine.eval::<bool>("!(!(!(!(true))))").unwrap(), true)
}
#[test]
fn test_loop() {
let mut engine = Engine::new();
assert!(
engine.eval::<bool>("
let x = 0;
let i = 0;
loop {
if i < 10 {
x = x + i;
i = i + 1;
}
else {
break;
}
}
x == 45
").unwrap()
)
}