]> Repositorios git - scryer-prolog.git/commitdiff
add bitwise and modular operations.
authorMark Thom <[email protected]>
Sun, 3 Dec 2017 21:15:01 +0000 (14:15 -0700)
committerMark Thom <[email protected]>
Sun, 3 Dec 2017 21:15:01 +0000 (14:15 -0700)
README.md
src/main.rs
src/prolog/arithmetic.rs
src/prolog/ast.rs
src/prolog/builtins.rs
src/prolog/io.rs
src/prolog/machine.rs

index fe91e241c5762ac2a39274155e734baa331928bb..8ea011bbc397d5fa3a3564df37555b39f0f09a11 100644 (file)
--- 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
index c174a5921a15bd352480818f19386b0e44562d14..db52bf6f13eb6cd1d4372b27bbc29f80b52c7f2d 100644 (file)
@@ -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);
     }
 }
 
index 5978b6bb798244fc56f7e4789b065eb9bfed0214..ffbcc84fee53e27e88051143630c307d583b6a16 100644 (file)
@@ -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)
         }
     }
index b58f6b2f13af4716b6dbc7bf62a9b96391101b54..720919f9adb35a28c3be3f82f2ae88160e71c4f8 100644 (file)
@@ -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)
 }
 
index d50bab0354ea3c4597e23e4d83844f20d1a68fec..da435d9feb49b494908b7a14c5dd29276173d071 100644 (file)
@@ -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)
index 5b274f5ffce10cd8c53ef9ea843eb9603f00513f..eae23dcd85a1271359eb5c516b8dc27e2ed86cb1 100644 (file)
@@ -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)
         }
index c876076e0384be3f54c14a016e0dc4297df944fb..30ef2721c41979bd9c36bc50f6afa0e0b80ed182 100644 (file)
@@ -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<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;
             }
         };