From: Mark Thom Date: Sun, 3 Dec 2017 21:15:01 +0000 (-0700) Subject: add bitwise and modular operations. X-Git-Tag: v0.8.110~664 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=e8a9da0dcac65e64bdf3131bdf6742b05c37dcb5;p=scryer-prolog.git add bitwise and modular operations. --- diff --git a/README.md b/README.md index fe91e241..8ea011bb 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ ideally, very fast) [Shen](http://shenlanguage.org) implementation. The following predicates are built-in to rusty-wam. * Arithmetic support: - * is/2 works for (+)/2, (-)/{1,2}, (*)/2, (//)/2, (div)/2, (/)/2, (rdiv)/2. + * is/2 works for (+)/2, (-)/{1,2}, (*)/2, (//)/2, (div)/2, (/)/2, (rdiv)/2, + (xor)/2, (rem)/2, (mod)/2, (/\)/2, (\/)/2, (>>)/2, (<<)/2. * atomic/1 * call/N (1 <= N <= 63) * catch/3 diff --git a/src/main.rs b/src/main.rs index c174a592..db52bf6f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -783,6 +783,13 @@ mod tests { true); assert_eq!(submit(&mut wam, "?- X is (3 rdiv 4) / 2, Y is 3 rdiv 8, X = Y."), true); + + assert_eq!(submit(&mut wam, "?- X is 10 xor -4, X is -10."), true); + assert_eq!(submit(&mut wam, "?- X is 4 xor -7, X is -3."), true); + assert_eq!(submit(&mut wam, "?- X is 10 xor 5 + 55, X = 70."), true); + + assert_eq!(submit(&mut wam, "?- X is 10 rem -3, X = 1."), true); + assert_eq!(submit(&mut wam, "?- X is 10 mod -3, X is -2."), true); } } diff --git a/src/prolog/arithmetic.rs b/src/prolog/arithmetic.rs index 5978b6bb..ffbcc84f 100644 --- a/src/prolog/arithmetic.rs +++ b/src/prolog/arithmetic.rs @@ -101,9 +101,16 @@ impl<'a> ArithmeticEvaluator<'a> { "-" => Ok(ArithmeticInstruction::Sub(a1, a2, t)), "/" => Ok(ArithmeticInstruction::Div(a1, a2, t)), "//" => Ok(ArithmeticInstruction::IDiv(a1, a2, t)), - "div" => Ok(ArithmeticInstruction::IDiv(a1, a2, t)), + "div" => Ok(ArithmeticInstruction::FIDiv(a1, a2, t)), "rdiv" => Ok(ArithmeticInstruction::RDiv(a1, a2, t)), "*" => Ok(ArithmeticInstruction::Mul(a1, a2, t)), + ">>" => Ok(ArithmeticInstruction::Shr(a1, a2, t)), + "<<" => Ok(ArithmeticInstruction::Shl(a1, a2, t)), + "/\\" => Ok(ArithmeticInstruction::And(a1, a2, t)), + "\\/" => Ok(ArithmeticInstruction::Or(a1, a2, t)), + "xor" => Ok(ArithmeticInstruction::Xor(a1, a2, t)), + "mod" => Ok(ArithmeticInstruction::Mod(a1, a2, t)), + "rem" => Ok(ArithmeticInstruction::Rem(a1, a2, t)), _ => Err(ArithmeticError::InvalidOp) } } diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index b58f6b2f..720919f9 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -630,8 +630,16 @@ pub enum ArithmeticInstruction { Sub(ArithmeticTerm, ArithmeticTerm, usize), Mul(ArithmeticTerm, ArithmeticTerm, usize), IDiv(ArithmeticTerm, ArithmeticTerm, usize), + FIDiv(ArithmeticTerm, ArithmeticTerm, usize), RDiv(ArithmeticTerm, ArithmeticTerm, usize), Div(ArithmeticTerm, ArithmeticTerm, usize), + Shl(ArithmeticTerm, ArithmeticTerm, usize), + Shr(ArithmeticTerm, ArithmeticTerm, usize), + Xor(ArithmeticTerm, ArithmeticTerm, usize), + And(ArithmeticTerm, ArithmeticTerm, usize), + Or(ArithmeticTerm, ArithmeticTerm, usize), + Mod(ArithmeticTerm, ArithmeticTerm, usize), + Rem(ArithmeticTerm, ArithmeticTerm, usize), Neg(ArithmeticTerm, usize) } diff --git a/src/prolog/builtins.rs b/src/prolog/builtins.rs index d50bab03..da435d9f 100644 --- a/src/prolog/builtins.rs +++ b/src/prolog/builtins.rs @@ -127,12 +127,19 @@ pub fn build_code_dir() -> (Code, CodeDir, OpDir) op_dir.insert((String::from("is"), Fixity::In), (XFX, 700)); op_dir.insert((String::from("+"), Fixity::In), (YFX, 500)); op_dir.insert((String::from("-"), Fixity::In), (YFX, 500)); + op_dir.insert((String::from("/\\"), Fixity::In), (YFX, 500)); + op_dir.insert((String::from("\\/"), Fixity::In), (YFX, 500)); + op_dir.insert((String::from("xor"), Fixity::In), (YFX, 500)); op_dir.insert((String::from("//"), Fixity::In), (YFX, 400)); op_dir.insert((String::from("/"), Fixity::In), (YFX, 400)); op_dir.insert((String::from("div"), Fixity::In), (YFX, 400)); op_dir.insert((String::from("*"), Fixity::In), (YFX, 400)); op_dir.insert((String::from("-"), Fixity::Pre), (FY, 200)); op_dir.insert((String::from("rdiv"), Fixity::In), (YFX, 400)); + op_dir.insert((String::from("<<"), Fixity::In), (YFX, 400)); + op_dir.insert((String::from(">>"), Fixity::In), (YFX, 400)); + op_dir.insert((String::from("mod"), Fixity::In), (YFX, 400)); + op_dir.insert((String::from("rem"), Fixity::In), (YFX, 400)); // there are 63 registers in the VM, so call/N is defined for all 0 <= N <= 62 // (an extra register is needed for the predicate name) diff --git a/src/prolog/io.rs b/src/prolog/io.rs index 5b274f5f..eae23dcd 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -216,8 +216,24 @@ impl fmt::Display for ArithmeticInstruction { write!(f, "div {}, {}, @{}", a1, a2, t), &ArithmeticInstruction::IDiv(ref a1, ref a2, ref t) => write!(f, "idiv {}, {}, @{}", a1, a2, t), + &ArithmeticInstruction::FIDiv(ref a1, ref a2, ref t) => + write!(f, "floored_idiv {}, {}, @{}", a1, a2, t), &ArithmeticInstruction::RDiv(ref a1, ref a2, ref t) => write!(f, "rdiv {}, {}, @{}", a1, a2, t), + &ArithmeticInstruction::Shl(ref a1, ref a2, ref t) => + write!(f, "shl {}, {}, @{}", a1, a2, t), + &ArithmeticInstruction::Shr(ref a1, ref a2, ref t) => + write!(f, "shr {}, {}, @{}", a1, a2, t), + &ArithmeticInstruction::Xor(ref a1, ref a2, ref t) => + write!(f, "xor {}, {}, @{}", a1, a2, t), + &ArithmeticInstruction::And(ref a1, ref a2, ref t) => + write!(f, "and {}, {}, @{}", a1, a2, t), + &ArithmeticInstruction::Or(ref a1, ref a2, ref t) => + write!(f, "or {}, {}, @{}", a1, a2, t), + &ArithmeticInstruction::Mod(ref a1, ref a2, ref t) => + write!(f, "mod {}, {}, @{}", a1, a2, t), + &ArithmeticInstruction::Rem(ref a1, ref a2, ref t) => + write!(f, "rem {}, {}, @{}", a1, a2, t), &ArithmeticInstruction::Neg(ref a, ref t) => write!(f, "neg {}, @{}", a, t) } diff --git a/src/prolog/machine.rs b/src/prolog/machine.rs index c876076e..30ef2721 100644 --- a/src/prolog/machine.rs +++ b/src/prolog/machine.rs @@ -4,8 +4,8 @@ use prolog::codegen::*; use prolog::copier::*; use prolog::heapview::*; use prolog::and_stack::*; -use prolog::num::Zero; -use prolog::num::bigint::BigInt; +use prolog::num::{Integer, ToPrimitive, Zero}; +use prolog::num::bigint::{BigInt, BigUint}; use prolog::num::rational::Ratio; use prolog::or_stack::*; use prolog::ordered_float::OrderedFloat; @@ -881,6 +881,20 @@ impl MachineState { } } + fn signed_bitwise_op(&mut self, n1: BigInt, n2: BigInt, t: usize, f: Op) + where Op: FnOnce(BigUint, BigUint) -> BigUint + { + let n1_b = n1.to_signed_bytes_le(); + let n2_b = n2.to_signed_bytes_le(); + + let u_n1 = BigUint::from_bytes_le(&n1_b); + let u_n2 = BigUint::from_bytes_le(&n2_b); + + let result = BigInt::from_signed_bytes_le(&f(u_n1, u_n2).to_bytes_le()); + + self.interms[t - 1] = Number::Integer(result); + } + fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) { match instr { &ArithmeticInstruction::Add(ref a1, ref a2, t) => { @@ -918,6 +932,30 @@ impl MachineState { self.interms[t - 1] = Number::Rational(r1 / r2); self.p += 1; }, + &ArithmeticInstruction::FIDiv(ref a1, ref a2, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + let n2 = try_or_fail!(self, self.get_number(a2)); + + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => { + if n2 == BigInt::zero() { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("zero_divisor")])); + return; + } + + self.interms[t - 1] = Number::Integer(n1.div_floor(&n2)); + self.p += 1; + }, + _ => { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("expected_integer_args")])); + return; + } + } + }, &ArithmeticInstruction::IDiv(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); @@ -960,6 +998,147 @@ impl MachineState { } self.interms[t - 1] = n1 / n2; + self.p += 1; + }, + &ArithmeticInstruction::Shr(ref a1, ref a2, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + let n2 = try_or_fail!(self, self.get_number(a2)); + + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + match n2.to_usize() { + Some(n2) => self.interms[t - 1] = Number::Integer(n1 >> n2), + _ => self.interms[t - 1] = Number::Integer(n1 >> usize::max_value()) + }, + _ => { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("expected_integer_args")])); + return; + } + } + + self.p += 1; + }, + &ArithmeticInstruction::Shl(ref a1, ref a2, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + let n2 = try_or_fail!(self, self.get_number(a2)); + + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + match n2.to_usize() { + Some(n2) => self.interms[t - 1] = Number::Integer(n1 << n2), + _ => self.interms[t - 1] = Number::Integer(n1 << usize::max_value()) + }, + _ => { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("expected_integer_args")])); + return; + } + } + + self.p += 1; + }, + &ArithmeticInstruction::Xor(ref a1, ref a2, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + let n2 = try_or_fail!(self, self.get_number(a2)); + + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + self.signed_bitwise_op(n1, n2, t, |u_n1, u_n2| u_n1 ^ u_n2), + _ => { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("expected_integer_args")])); + return; + } + }; + + self.p += 1; + }, + &ArithmeticInstruction::And(ref a1, ref a2, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + let n2 = try_or_fail!(self, self.get_number(a2)); + + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + self.signed_bitwise_op(n1, n2, t, |u_n1, u_n2| u_n1 & u_n2), + _ => { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("expected_integer_args")])); + return; + } + }; + + self.p += 1; + }, + &ArithmeticInstruction::Or(ref a1, ref a2, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + let n2 = try_or_fail!(self, self.get_number(a2)); + + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + self.signed_bitwise_op(n1, n2, t, |u_n1, u_n2| u_n1 | u_n2), + _ => { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("expected_integer_args")])); + return; + } + }; + + self.p += 1; + }, + &ArithmeticInstruction::Mod(ref a1, ref a2, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + let n2 = try_or_fail!(self, self.get_number(a2)); + + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => { + if n2 == BigInt::zero() { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("zero_divisor")])); + return; + } + + self.interms[t - 1] = Number::Integer(n1.mod_floor(&n2)); + }, + _ => { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("expected_integer_args")])); + return; + } + } + + self.p += 1; + }, + &ArithmeticInstruction::Rem(ref a1, ref a2, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + let n2 = try_or_fail!(self, self.get_number(a2)); + + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => { + if n2 == BigInt::zero() { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("zero_divisor")])); + return; + } + + self.interms[t - 1] = Number::Integer(n1 % n2); + }, + _ => { + self.throw_exception(functor!("evaluation_error", + 1, + [atom!("expected_integer_args")])); + return; + } + } + self.p += 1; } };