diff --git a/src/parser.rs b/src/parser.rs index dd13de5d..8c62aa08 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -154,6 +154,8 @@ pub enum Token { Fn, Break, Return, + PlusEquals, + MinusEquals, LexErr(LexError), } @@ -436,13 +438,25 @@ impl<'a> TokenIterator<'a> { '[' => return Some(Token::LSquare), ']' => return Some(Token::RSquare), '+' => { - if self.last.is_next_unary() { return Some(Token::UnaryPlus) } - else { return Some(Token::Plus) } - } + return match self.char_stream.peek() { + Some(&'=') => { + self.char_stream.next(); + Some(Token::PlusEquals) + }, + _ if self.last.is_next_unary() => Some(Token::UnaryPlus), + _ => Some(Token::Plus), + } + }, '-' => { - if self.last.is_next_unary() { return Some(Token::UnaryMinus) } - else { return Some(Token::Minus) } - } + return match self.char_stream.peek() { + Some(&'=') => { + self.char_stream.next(); + Some(Token::MinusEquals) + }, + _ if self.last.is_next_unary() => Some(Token::UnaryMinus), + _ => Some(Token::Minus), + } + }, '*' => return Some(Token::Multiply), '/' => { match self.char_stream.peek() { @@ -544,7 +558,7 @@ impl<'a> TokenIterator<'a> { impl<'a> Iterator for TokenIterator<'a> { type Item = Token; - // TODO - perhaps this could be optimized to only keep track of last for operators? + // TODO - perhaps this could be optimized? fn next(&mut self) -> Option { self.last = match self.inner_next() { Some(c) => c, @@ -560,7 +574,9 @@ pub fn lex(input: &str) -> TokenIterator { fn get_precedence(token: &Token) -> i32 { match *token { - Token::Equals => 10, + Token::Equals + | Token::PlusEquals + | Token::MinusEquals => 10, Token::Or => 11, Token::And => 12, Token::LessThan @@ -591,7 +607,7 @@ fn parse_call_expr<'a>(id: String, input: &mut Peekable>) -> Result { let mut args = Vec::new(); - + if let Some(&Token::RParen) = input.peek() { input.next(); return Ok(Expr::FnCall(id, args)); @@ -756,6 +772,20 @@ fn parse_binop<'a>(input: &mut Peekable>, Token::Multiply => Expr::FnCall("*".to_string(), vec![lhs_curr, rhs]), Token::Divide => Expr::FnCall("/".to_string(), vec![lhs_curr, rhs]), Token::Equals => Expr::Assignment(Box::new(lhs_curr), Box::new(rhs)), + Token::PlusEquals => { + let lhs_copy = lhs_curr.clone(); + Expr::Assignment( + Box::new(lhs_curr), + Box::new(Expr::FnCall("+".to_string(), vec![lhs_copy, rhs])) + ) + }, + Token::MinusEquals => { + let lhs_copy = lhs_curr.clone(); + Expr::Assignment( + Box::new(lhs_curr), + Box::new(Expr::FnCall("-".to_string(), vec![lhs_copy, rhs])) + ) + }, Token::Period => Expr::Dot(Box::new(lhs_curr), Box::new(rhs)), Token::EqualTo => Expr::FnCall("==".to_string(), vec![lhs_curr, rhs]), Token::NotEqualTo => Expr::FnCall("!=".to_string(), vec![lhs_curr, rhs]), diff --git a/src/tests.rs b/src/tests.rs index 98f75862..cd0b1e53 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -457,3 +457,37 @@ fn test_loop() { ").unwrap() ) } + +#[test] +fn test_increment() { + let mut engine = Engine::new(); + + if let Ok(result) = engine.eval::("let x = 1; x += 2; x") { + assert_eq!(result, 3); + } else { + assert!(false); + } + + if let Ok(result) = engine.eval::("let s = \"test\"; s += \"ing\"; s") { + assert_eq!(result, "testing".to_owned()); + } else { + assert!(false); + } +} + +#[test] +fn test_decrement() { + let mut engine = Engine::new(); + + if let Ok(result) = engine.eval::("let x = 10; x -= 7; x") { + assert_eq!(result, 3); + } else { + assert!(false); + } + + if let Ok(_) = engine.eval::("let s = \"test\"; s -= \"ing\"; s") { + assert!(false); + } else { + assert!(true); + } +}