diff --git a/integration_tests/operator_precedence_test.ucg b/integration_tests/operator_precedence_test.ucg index 6ef09bb..f7ff793 100644 --- a/integration_tests/operator_precedence_test.ucg +++ b/integration_tests/operator_precedence_test.ucg @@ -61,4 +61,14 @@ assert { assert { ok = (1 == 1) || (1 != 1) == true, desc = "(1 == 1) && (1 != 1) == true", +}; + +assert { + ok = 4 %% 2 == 0, + desc = "4 %% 2 is 0", +}; + +assert { + ok = 4 %% 3 == 1, + desc = "4 %% 3 is 1", }; \ No newline at end of file diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 2d62a34..ca4ea2f 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -392,6 +392,7 @@ pub enum BinaryExprType { Sub, Mul, Div, + Mod, // Boolean AND, OR, @@ -433,6 +434,7 @@ impl BinaryExprType { // Product operators are next tightly bound BinaryExprType::Mul => 4, BinaryExprType::Div => 4, + BinaryExprType::Mod => 4, // Boolean operators bind tighter than math BinaryExprType::AND => 5, BinaryExprType::OR => 5, diff --git a/src/build/mod.rs b/src/build/mod.rs index 63502b0..0864e7c 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -613,6 +613,33 @@ impl<'a> FileBuilder<'a> { } } + fn mod_vals( + &self, + lpos: &Position, + rpos: &Position, + left: Rc, + right: Rc, + ) -> Result, Box> { + match *left { + Val::Int(i) => { + eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i % ii), "Integer") + } + Val::Float(f) => { + eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f % ff), "Float") + } + ref expr => { + return Err(Box::new(error::BuildError::new( + format!( + "{} does not support the 'modulus' operation", + expr.type_name() + ), + error::ErrorType::Unsupported, + lpos.clone(), + ))); + } + } + } + fn divide_vals( &self, lpos: &Position, @@ -964,6 +991,7 @@ impl<'a> FileBuilder<'a> { &BinaryExprType::Sub => self.subtract_vals(&def.pos, def.right.pos(), left, right), &BinaryExprType::Mul => self.multiply_vals(&def.pos, def.right.pos(), left, right), &BinaryExprType::Div => self.divide_vals(&def.pos, def.right.pos(), left, right), + &BinaryExprType::Mod => self.mod_vals(&def.pos, def.right.pos(), left, right), // Handle Comparison operators here &BinaryExprType::Equal => self.do_deep_equal(def.right.pos(), left, right), &BinaryExprType::GT => self.do_gt(&def.right.pos(), left, right), diff --git a/src/parse/precedence.rs b/src/parse/precedence.rs index bc64b31..83806a5 100644 --- a/src/parse/precedence.rs +++ b/src/parse/precedence.rs @@ -59,7 +59,10 @@ make_fn!( (Element::Op(BinaryExprType::Mul))), do_each!( _ => punct!("/"), - (Element::Op(BinaryExprType::Div))) + (Element::Op(BinaryExprType::Div))), + do_each!( + _ => punct!("%%"), + (Element::Op(BinaryExprType::Mod))) ) ); @@ -175,6 +178,9 @@ fn parse_product_operator(i: SliceIter) -> Result, B &BinaryExprType::Div => { return Result::Complete(i_.clone(), op.clone()); } + &BinaryExprType::Mod => { + return Result::Complete(i_.clone(), op.clone()); + } _other => { // noop } @@ -423,9 +429,9 @@ pub fn op_expression<'a>(i: SliceIter<'a, Token>) -> Result, Ex ); Result::Fail(err) } - Result::Abort(_e) => { + Result::Abort(e) => { let err = Error::new( - "Abort while parsing operator expression", + format!("Abort while parsing operator expression\n{}", e), Box::new(rest.clone()), ); Result::Abort(err) diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs index ea0b401..4f3879d 100644 --- a/src/tokenizer/mod.rs +++ b/src/tokenizer/mod.rs @@ -198,6 +198,10 @@ make_fn!(slashtok, do_text_token_tok!(TokenType::PUNCT, "/") ); +make_fn!(modulustok, + do_text_token_tok!(TokenType::PUNCT, "%%") +); + make_fn!(pcttok, do_text_token_tok!(TokenType::PUNCT, "%") ); @@ -403,6 +407,7 @@ fn token<'a>(input: OffsetStrIter<'a>) -> Result, Token> { startok, comment, // Note comment must come before slashtok slashtok, + modulustok, pcttok, eqeqtok, notequaltok,