diff --git a/src/compiler/lex.rs b/src/compiler/lex.rs index c71affe..99a3bdd 100644 --- a/src/compiler/lex.rs +++ b/src/compiler/lex.rs @@ -101,6 +101,7 @@ impl Lexer { Box::new(Lexer::mul), Box::new(Lexer::div), Box::new(Lexer::equal), + Box::new(Lexer::remainder), Box::new(Lexer::magic), Box::new(Lexer::print), // remove print statements after FFI @@ -269,6 +270,9 @@ impl Lexer { Lexer::literal(source, "==", Token::Equal) } + pub fn remainder(source: &str) -> Result { + Lexer::literal(source, "%", Token::Rem) + } /// Matches a `print` expression. pub fn print(source: &str) -> Result { Lexer::literal(source, "print", Token::Print) diff --git a/src/compiler/parse.rs b/src/compiler/parse.rs index 35693b7..ba2e0ac 100644 --- a/src/compiler/parse.rs +++ b/src/compiler/parse.rs @@ -180,6 +180,7 @@ impl Parser { Token::Sub => self.sub(left), Token::Mul => self.mul(left), Token::Div => self.div(left), + Token::Rem => self.remainder(left), Token::Equal => self.equal(left), @@ -208,7 +209,8 @@ impl Parser { | Token::Sub => Prec::AddSub, Token::Mul - | Token::Div => Prec::MulDiv, + | Token::Div + | Token::Rem => Prec::MulDiv, // postfix Token::End @@ -549,6 +551,11 @@ impl Parser { return self.binop(Token::Equal, Prec::Logic, "equal", left); } + /// Parses an equality, calls out to FFI. + pub fn remainder(&mut self, left: Spanned) -> Result, Syntax> { + return self.binop(Token::Rem, Prec::MulDiv, "remainder", left); + } + /// Parses a function call. /// Function calls are a bit magical, /// because they're just a series of expressions. diff --git a/src/compiler/token.rs b/src/compiler/token.rs index 470b792..df4b189 100644 --- a/src/compiler/token.rs +++ b/src/compiler/token.rs @@ -38,7 +38,7 @@ pub enum Token { // Operators Add, Sub, - Mul, Div, + Mul, Div, Rem, Equal, @@ -72,6 +72,7 @@ impl Display for Token { Token::Sub => "a subtraction", Token::Mul => "a multiplication", Token::Div => "a division", + Token::Rem => "a remainder operator", Token::Equal => "an equality test", Token::End => "end of source", Token::Keyword(k) => { return write!(f, "the pseudokeyword '{}", k); }, diff --git a/src/core/math.rs b/src/core/math.rs index 1135231..ebaeb61 100644 --- a/src/core/math.rs +++ b/src/core/math.rs @@ -48,3 +48,17 @@ pub fn div(data: Data) -> Result { return Ok(result); } + +/// remainder of left operand by right operand division. +/// Raises a runtime error if there is a division by zero. +pub fn remainder(data: Data) -> Result { + let result = match binop(data) { + (Data::Real(_), Data::Real(r)) if r == 0.0 => Err("Division by zero")?, + (Data::Real(l), Data::Real(r)) => Data::Real(l.rem_euclid(r)), + (Data::Integer(_), Data::Integer(n)) if n == 0 => Err("Division by zero")?, + (Data::Integer(l), Data::Integer(r)) => Data::Integer(l.rem_euclid(r)), + _ => Err("Division between unsupported datatypes")?, + }; + + return Ok(result); +} diff --git a/src/core/mod.rs b/src/core/mod.rs index 37bd34b..31629f6 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -21,6 +21,7 @@ pub fn ffi_core() -> FFI { ffi.add("sub", FFIFunction::new(Box::new(math::sub))).unwrap(); ffi.add("mul", FFIFunction::new(Box::new(math::mul))).unwrap(); ffi.add("div", FFIFunction::new(Box::new(math::div))).unwrap(); + ffi.add("remainder", FFIFunction::new(Box::new(math::remainder))).unwrap(); // io ffi.add("println", FFIFunction::new(Box::new(io::println))).unwrap(); diff --git a/tests/snippets/remainder.pn b/tests/snippets/remainder.pn new file mode 100644 index 0000000..6968636 --- /dev/null +++ b/tests/snippets/remainder.pn @@ -0,0 +1,5 @@ +-- action: run +-- outcome: success +-- expect: 1.0 + +print(3.0 % 2.0)