diff --git a/src/engine.rs b/src/engine.rs index a67ea15e..5428bb29 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -7,7 +7,7 @@ use std::fmt; use parser::{lex, parse, Expr, Stmt, FnDef}; use fn_register::FnRegister; -use std::ops::{Add, Sub, Mul, Div, Neg, BitAnd, BitOr, BitXor, Shl, Shr}; +use std::ops::{Add, Sub, Mul, Div, Neg, BitAnd, BitOr, BitXor, Shl, Shr, Rem}; use std::cmp::{PartialOrd, PartialEq}; #[derive(Debug)] @@ -1417,6 +1417,7 @@ impl Engine { fn binary_xor(x: T, y: T) -> ::Output { x ^ y } fn left_shift>(x: T, y: T) -> >::Output { x.shl(y) } fn right_shift>(x: T, y: T) -> >::Output { x.shr(y) } + fn modulo>(x: T, y: T) -> >::Output { x % y} reg_op!(engine, "+", add, i32, i64, u32, u64, f32, f64); reg_op!(engine, "-", sub, i32, i64, u32, u64, f32, f64); @@ -1439,6 +1440,7 @@ impl Engine { reg_op!(engine, "^", binary_xor, i32, i64, u32, u64); reg_op!(engine, "<<", left_shift, i32, i64, u32, u64); reg_op!(engine, ">>", right_shift, i32, i64, u32, u64); + reg_op!(engine, "%", modulo, i32, i64, u32, u64); reg_un!(engine, "-", neg, i32, i64, f32, f64); reg_un!(engine, "!", not, bool); diff --git a/src/parser.rs b/src/parser.rs index 4276bf37..ec8db86b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -168,6 +168,8 @@ pub enum Token { LeftShift, RightShift, XOr, + Modulo, + ModuloEquals, LexErr(LexError), } @@ -676,6 +678,15 @@ impl<'a> TokenIterator<'a> { } _ => return Some(Token::XOr) } + }, + '%' => { + match self.char_stream.peek() { + Some(&'=') => { + self.char_stream.next(); + return Some(Token::ModuloEquals); + } + _ => return Some(Token::Modulo) + } } _x if _x.is_whitespace() => (), _ => return Some(Token::LexErr(LexError::UnexpectedChar)), @@ -714,11 +725,13 @@ fn get_precedence(token: &Token) -> i32 { | Token::RightShiftEquals | Token::AndEquals | Token::OrEquals - | Token::XOrEquals => 10, + | Token::XOrEquals + | Token::ModuloEquals => 10, Token::Or | Token::XOr | Token::Pipe => 11, - Token::And => 12, + Token::And + | Token::Ampersand => 12, Token::LessThan | Token::LessThanEqual | Token::GreaterThan @@ -731,6 +744,7 @@ fn get_precedence(token: &Token) -> i32 { | Token::Multiply => 40, Token::LeftShift | Token::RightShift => 50, + Token::Modulo => 60, Token::Period => 100, _ => -1, } @@ -997,6 +1011,15 @@ fn parse_binop<'a>(input: &mut Peekable>, Box::new(Expr::FnCall(">>".to_string(), vec![lhs_copy, rhs])) ) }, + Token::Ampersand => Expr::FnCall("&".to_string(), vec![lhs_curr, rhs]), + Token::Modulo => Expr::FnCall("%".to_string(), vec![lhs_curr, rhs]), + Token::ModuloEquals => { + let lhs_copy = lhs_curr.clone(); + Expr::Assignment( + Box::new(lhs_curr), + Box::new(Expr::FnCall("%".to_string(), vec![lhs_copy, rhs])) + ) + } _ => return Err(ParseError::UnknownOperator), }; } diff --git a/src/tests.rs b/src/tests.rs index bc76c046..9ff3fc65 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -707,6 +707,17 @@ fn test_hex_literal() { } } +#[test] +fn test_modulo_equals() { + let mut engine = Engine::new(); + + if let Ok(result) = engine.eval::("let x = 10; x %= 4; x") { + assert_eq!(result, 2); + } else { + assert!(false); + } +} + #[test] fn test_octal_literal() { let mut engine = Engine::new(); @@ -714,7 +725,7 @@ fn test_octal_literal() { if let Ok(result) = engine.eval::("let x = 0o77; x") { assert_eq!(result, 63); } else { - assert!(false); + assert!(false) } if let Ok(result) = engine.eval::("let x = 0o1234; x") { @@ -739,4 +750,45 @@ fn test_binary_literal() { } else { assert!(false); } +} + +#[test] +fn test_binary_ops() { + let mut engine = Engine::new(); + + if let Ok(result) = engine.eval::("10 % 4") { + assert_eq!(result, 2); + } else { + assert!(false); + } + + if let Ok(result) = engine.eval::("10 << 4") { + assert_eq!(result, 160); + } else { + assert!(false); + } + + if let Ok(result) = engine.eval::("10 >> 4") { + assert_eq!(result, 0); + } else { + assert!(false); + } + + if let Ok(result) = engine.eval::("10 & 4") { + assert_eq!(result, 0); + } else { + assert!(false); + } + + if let Ok(result) = engine.eval::("10 | 4") { + assert_eq!(result, 14); + } else { + assert!(false); + } + + if let Ok(result) = engine.eval::("10 ^ 4") { + assert_eq!(result, 14); + } else { + assert!(false); + } } \ No newline at end of file