]> Repositorios git - scryer-prolog.git/commitdiff
add support for rational numbers, division.
authorMark Thom <[email protected]>
Sat, 2 Dec 2017 18:10:25 +0000 (11:10 -0700)
committerMark Thom <[email protected]>
Sat, 2 Dec 2017 18:10:25 +0000 (11:10 -0700)
Cargo.lock
Cargo.toml
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
src/prolog/macros.rs

index 6717f6f28252f8893fcb56299ea9d384713ec7c0..bcb30e0c75cedda153291d521de381559ff638b4 100644 (file)
@@ -1,6 +1,6 @@
 [root]
 name = "rusty-wam"
-version = "0.7.2"
+version = "0.7.3"
 dependencies = [
  "lazy_static 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
index 092db674128df5c7ea29843b35972198245514fe..6a8c39d1f70b2aa03b8829c99c3a41fe2e2356dd 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "rusty-wam"
-version = "0.7.2"
+version = "0.7.3"
 authors = ["Mark Thom"]
 
 [dependencies]
index 26bdbd609dcaf098c1048235a9fb3a9e62a7f047..fe91e241c5762ac2a39274155e734baa331928bb 100644 (file)
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ Extend rusty-wam to include the following, among other features:
 * ISO Prolog compliant throw/catch (_done_).
 * Built-in and user-defined operators of all fixities, with custom
   associativity and precedence (_done_). 
-* Bignum and floating point arithmetic (_in progress_).
+* Bignum, rational number and floating point arithmetic (_in progress_).
 * Built-in control operators (`,`, `;`, `->`, etc.).
 * Built-in predicates for list processing and top-level declarative
   control (`setup_call_control/3`, `call_with_inference_limit/3`,
@@ -47,7 +47,7 @@ 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.
+    * is/2 works for (+)/2, (-)/{1,2}, (*)/2, (//)/2, (div)/2, (/)/2, (rdiv)/2.
 * atomic/1
 * call/N (1 <= N <= 63)
 * catch/3
index 64bd07b41fac248850d020d9820e08386fdac1c1..c174a5921a15bd352480818f19386b0e44562d14 100644 (file)
@@ -757,17 +757,32 @@ mod tests {
         assert_eq!(submit(&mut wam, "?- 7 is 3 * 2."), false);
         assert_eq!(submit(&mut wam, "?- 7 is 3.5 * 2."), false);
         assert_eq!(submit(&mut wam, "?- 7.0 is 3.5 * 2."), true);
+        assert_eq!(submit(&mut wam, "?- 7.0 is 14 / 2."), true);
+        assert_eq!(submit(&mut wam, "?- 4.666 is 14.0 / 3."), false);
+        assert_eq!(submit(&mut wam, "?- 4.0 is 8.0 / 2."), true);
 
         submit(&mut wam, "f(X) :- X is 5 // 0.");
 
         assert_eq!(submit(&mut wam, "?- catch(f(X), evaluation_error(E), true), E = zero_divisor."),
                    true);
+        
+        submit(&mut wam, "f(X) :- X is (5 rdiv 1) / 0.");
 
+        assert_eq!(submit(&mut wam, "?- catch(f(X), evaluation_error(E), true), E = zero_divisor."),
+                   true);
+        
+        submit(&mut wam, "f(X) :- X is 5.0 / 0.");
+
+        assert_eq!(submit(&mut wam, "?- catch(f(X), evaluation_error(E), true), E = zero_divisor."),
+                   true);
+        
         assert_eq!(submit(&mut wam, "?- X is ((3 + 4) // 2) + 2 - 1 // 1, Y is 2+2, Z is X+Y."),
                    true);
 
         assert_eq!(submit(&mut wam, "?- X is ((3 + 4) // 2) + 2 - 1 // 1, Y is 2+2, Z = 8, Y is 4."),
                    true);
+
+        assert_eq!(submit(&mut wam, "?- X is (3 rdiv 4) / 2, Y is 3 rdiv 8, X = Y."), true);
     }
 }
 
index 513412308650d29460758a8f28d11914947ab2a5..5978b6bb798244fc56f7e4789b065eb9bfed0214 100644 (file)
@@ -97,12 +97,14 @@ impl<'a> ArithmeticEvaluator<'a> {
                         -> Result<ArithmeticInstruction, ArithmeticError>
     {
         match name.as_str() {
-            "+"   => Ok(ArithmeticInstruction::Add(a1, a2, t)),
-            "-"   => Ok(ArithmeticInstruction::Sub(a1, a2, t)),
-            "//"  => Ok(ArithmeticInstruction::IDiv(a1, a2, t)),
-            "div" => Ok(ArithmeticInstruction::IDiv(a1, a2, t)),
-            "*"   => Ok(ArithmeticInstruction::Mul(a1, a2, t)),
-             _    => Err(ArithmeticError::InvalidOp)
+            "+"    => Ok(ArithmeticInstruction::Add(a1, a2, t)),
+            "-"    => 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)),
+            "rdiv" => Ok(ArithmeticInstruction::RDiv(a1, a2, t)),
+            "*"    => Ok(ArithmeticInstruction::Mul(a1, a2, t)),
+             _     => Err(ArithmeticError::InvalidOp)
         }
     }
 
index 0f7c993e5929df101cc20a49a993f462d7aac631..b58f6b2f13af4716b6dbc7bf62a9b96391101b54 100644 (file)
@@ -1,5 +1,6 @@
 use prolog::num::bigint::BigInt;
-use prolog::num::ToPrimitive;
+use prolog::num::{Float, ToPrimitive, Zero};
+use prolog::num::rational::Ratio;
 
 use prolog::ordered_float::*;
 
@@ -9,7 +10,7 @@ use std::collections::{HashMap, VecDeque};
 use std::fmt;
 use std::io::Error as IOError;
 use std::num::{ParseFloatError};
-use std::ops::{Add, AddAssign, Sub, Mul, Neg};
+use std::ops::{Add, AddAssign, Div, Sub, Mul, Neg};
 use std::str::Utf8Error;
 use std::vec::Vec;
 
@@ -268,6 +269,7 @@ pub enum Constant {
     Atom(Atom),
     Float(OrderedFloat<f64>),
     Integer(BigInt),
+    Rational(Ratio<BigInt>),
     String(String),
     Usize(usize),
     EmptyList
@@ -284,6 +286,8 @@ impl fmt::Display for Constant {
                 write!(f, "{}", fl),
             &Constant::Integer(ref i) =>
                 write!(f, "{}", i),
+            &Constant::Rational(ref r) =>
+                write!(f, "{}", r),
             &Constant::String(ref s) =>
                 write!(f, "{}", s),
             &Constant::Usize(integer) =>
@@ -295,6 +299,7 @@ impl fmt::Display for Constant {
 impl From<Number> for Constant {
     fn from(n: Number) -> Self {
         match n {
+            Number::Rational(r) => Constant::Rational(r),
             Number::Integer(n) => Constant::Integer(n),
             Number::Float(f) => Constant::Float(f)
         }
@@ -309,10 +314,10 @@ pub enum Term {
     Var(Cell<VarReg>, Var)
 }
 
-pub enum InlinedQueryTerm {    
+pub enum InlinedQueryTerm {
     IsAtomic(Vec<Box<Term>>),
     IsVar(Vec<Box<Term>>)
-}    
+}
 
 impl InlinedQueryTerm {
     pub fn arity(&self) -> usize {
@@ -434,24 +439,98 @@ impl IndexedChoiceInstruction {
 #[derive(Clone)]
 pub enum Number {
     Float(OrderedFloat<f64>),
-    Integer(BigInt)
+    Integer(BigInt),
+    Rational(Ratio<BigInt>)
+}
+
+impl Number {
+    pub fn is_zero(&self) -> bool {
+        match self {
+            &Number::Float(fl)       => fl.into_inner().is_zero(),
+            &Number::Integer(ref bi) => bi.is_zero(),
+            &Number::Rational(ref r) => r.is_zero()
+        }
+    }
+}
+
+enum NumberPair {
+    Float(OrderedFloat<f64>, OrderedFloat<f64>),
+    Integer(BigInt, BigInt),
+    Rational(Ratio<BigInt>, Ratio<BigInt>)
+}
+
+impl NumberPair {
+    fn flip(self) -> NumberPair {
+        match self {
+            NumberPair::Float(f1, f2)    => NumberPair::Float(f2, f1),
+            NumberPair::Integer(n1, n2)  => NumberPair::Integer(n2, n1),
+            NumberPair::Rational(r1, r2) => NumberPair::Rational(r2, r1)
+        }
+    }
+
+    fn integer_float_pair(n1: BigInt, n2: OrderedFloat<f64>) -> NumberPair {
+        match n1.to_f64() {
+            Some(f1) => NumberPair::Float(OrderedFloat(f1), n2),
+            None => if let Some(r) = Ratio::from_float(n2.into_inner()) {
+                NumberPair::Rational(Ratio::from_integer(n1), r)
+            } else if n2.into_inner().is_sign_positive() {
+                NumberPair::Float(OrderedFloat(f64::infinity()),
+                                  OrderedFloat(f64::infinity()))
+            } else {
+                NumberPair::Float(OrderedFloat(f64::neg_infinity()),
+                                  OrderedFloat(f64::neg_infinity()))
+            }
+        }
+    }
+
+    fn float_rational_pair(n1: OrderedFloat<f64>, n2: Ratio<BigInt>) -> NumberPair {
+        if let Some(r) = Ratio::from_float(n1.into_inner()) {
+            NumberPair::Rational(r, n2)
+        } else if n1.into_inner().is_sign_positive() {
+            NumberPair::Float(OrderedFloat(f64::infinity()),
+                              OrderedFloat(f64::infinity()))
+        } else {
+            NumberPair::Float(OrderedFloat(f64::neg_infinity()),
+                              OrderedFloat(f64::neg_infinity()))
+        }
+    }
+
+    fn from(n1: Number, n2: Number) -> NumberPair
+    {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                NumberPair::Integer(n1, n2),
+            (Number::Float(n1), Number::Float(n2)) =>
+                NumberPair::Float(n1, n2),
+            (Number::Rational(n1), Number::Rational(n2)) =>
+                NumberPair::Rational(n1, n2),            
+            (Number::Integer(n1), Number::Float(n2)) =>
+                Self::integer_float_pair(n1, n2),
+            (Number::Float(n1), Number::Integer(n2)) =>
+                Self::integer_float_pair(n2, n1).flip(),
+            (Number::Float(n1), Number::Rational(n2)) =>
+                Self::float_rational_pair(n1, n2),
+            (Number::Rational(n1), Number::Float(n2)) =>
+                Self::float_rational_pair(n2, n1).flip(),
+            (Number::Rational(n1), Number::Integer(n2)) =>
+                NumberPair::Rational(n1, Ratio::from_integer(n2)),
+            (Number::Integer(n1), Number::Rational(n2)) =>
+                NumberPair::Rational(Ratio::from_integer(n1), n2)
+        }
+    }
 }
 
 impl Add<Number> for Number {
     type Output = Number;
 
     fn add(self, rhs: Number) -> Self::Output {
-        match (self, rhs) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
+        match NumberPair::from(self, rhs) {
+            NumberPair::Float(f1, f2) =>
+                Number::Float(OrderedFloat(f1.into_inner() + f2.into_inner())),
+            NumberPair::Integer(n1, n2) =>
                 Number::Integer(n1 + n2),
-            (Number::Float(n1), Number::Float(n2)) =>
-                Number::Float(OrderedFloat(n1.into_inner() + n2.into_inner())),
-            (Number::Integer(n1), Number::Float(n2))
-          | (Number::Float(n2), Number::Integer(n1)) =>
-                match n1.to_f64() {
-                    Some(n1) => Number::Float(OrderedFloat(n1 + n2.into_inner())),
-                    None     => Number::Integer(n1)
-                }
+            NumberPair::Rational(r1, r2) =>
+                Number::Rational(r1 + r2)
         }
     }
 }
@@ -460,17 +539,13 @@ impl Sub<Number> for Number {
     type Output = Number;
 
     fn sub(self, rhs: Number) -> Self::Output {
-        match (self, rhs) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
+        match NumberPair::from(self, rhs) {
+            NumberPair::Float(f1, f2) =>
+                Number::Float(OrderedFloat(f1.into_inner() - f2.into_inner())),
+            NumberPair::Integer(n1, n2) =>
                 Number::Integer(n1 - n2),
-            (Number::Float(n1), Number::Float(n2)) =>
-                Number::Float(OrderedFloat(n1.into_inner() - n2.into_inner())),
-            (Number::Integer(n1), Number::Float(n2))
-          | (Number::Float(n2), Number::Integer(n1)) =>
-                match n1.to_f64() {
-                    Some(n1) => Number::Float(OrderedFloat(n1 - n2.into_inner())),
-                    None     => Number::Integer(n1)
-                }
+            NumberPair::Rational(r1, r2) =>
+                Number::Rational(r1 - r2)
         }
     }
 }
@@ -479,41 +554,46 @@ impl Mul<Number> for Number {
     type Output = Number;
 
     fn mul(self, rhs: Number) -> Self::Output {
-        match (self, rhs) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
+        match NumberPair::from(self, rhs) {
+            NumberPair::Float(f1, f2) =>
+                Number::Float(OrderedFloat(f1.into_inner() * f2.into_inner())),
+            NumberPair::Integer(n1, n2) =>
                 Number::Integer(n1 * n2),
-            (Number::Float(n1), Number::Float(n2)) =>
-                Number::Float(OrderedFloat(n1.into_inner() * n2.into_inner())),
-            (Number::Integer(n1), Number::Float(n2))
-          | (Number::Float(n2), Number::Integer(n1)) =>
-                match n1.to_f64() {
-                    Some(n1) => Number::Float(OrderedFloat(n1 * n2.into_inner())),
-                    None     => Number::Integer(n1)
-                }
-        }
+            NumberPair::Rational(r1, r2) =>
+                Number::Rational(r1 * r2)
+        }        
     }
 }
-
-/*TODO: reserved for proper division.
+    
 impl Div<Number> for Number {
     type Output = Number;
 
     fn div(self, rhs: Number) -> Self::Output {
-        match (self, rhs) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
-                Number::Integer(n1 / n2),
-            (Number::Float(n1), Number::Float(n2)) =>
-                Number::Float(OrderedFloat(n1.into_inner() / n2.into_inner())),
-            (Number::Integer(n1), Number::Float(n2))
-          | (Number::Float(n2), Number::Integer(n1)) =>
+        match NumberPair::from(self, rhs) {
+            NumberPair::Float(f1, f2) =>
+                Number::Float(OrderedFloat(f1.into_inner() / f2.into_inner())),
+            NumberPair::Integer(n1, n2) =>
                 match n1.to_f64() {
-                    Some(n1) => Number::Float(OrderedFloat(n1 / n2.into_inner())),
-                    None     => Number::Integer(n1)
-                }
+                    Some(f1) => if let Some(f2) = n2.to_f64() {
+                        Number::Float(OrderedFloat(f1 / f2))
+                    } else {
+                        let r1 = Ratio::from_integer(n1);
+                        let r2 = Ratio::from_integer(n2);
+
+                        Number::Rational(r1 / r2)
+                    },
+                    None => {
+                        let r1 = Ratio::from_integer(n1);
+                        let r2 = Ratio::from_integer(n2);
+
+                        Number::Rational(r1 / r2)
+                    },
+                },
+            NumberPair::Rational(r1, r2) =>
+                Number::Rational(r1 / r2)
         }
     }
 }
-*/
 
 impl Neg for Number {
     type Output = Number;
@@ -521,7 +601,8 @@ impl Neg for Number {
     fn neg(self) -> Self::Output {
         match self {
             Number::Integer(n) => Number::Integer(-n),
-            Number::Float(f) => Number::Float(OrderedFloat(-1.0 * f.into_inner()))
+            Number::Float(f) => Number::Float(OrderedFloat(-1.0 * f.into_inner())),
+            Number::Rational(r) => Number::Rational(- r)
         }
     }
 }
@@ -549,6 +630,8 @@ pub enum ArithmeticInstruction {
     Sub(ArithmeticTerm, ArithmeticTerm, usize),
     Mul(ArithmeticTerm, ArithmeticTerm, usize),
     IDiv(ArithmeticTerm, ArithmeticTerm, usize),
+    RDiv(ArithmeticTerm, ArithmeticTerm, usize),
+    Div(ArithmeticTerm, ArithmeticTerm, usize),
     Neg(ArithmeticTerm, usize)
 }
 
@@ -584,7 +667,7 @@ pub enum ControlInstruction {
     IsExecute(RegType),
     Proceed,
     ThrowCall,
-    ThrowExecute,    
+    ThrowExecute,
 }
 
 impl ControlInstruction {
@@ -601,7 +684,7 @@ impl ControlInstruction {
             &ControlInstruction::Goto(_, _) => true,
             &ControlInstruction::Proceed => true,
             &ControlInstruction::IsCall(_) => true,
-            &ControlInstruction::IsExecute(_) => true,            
+            &ControlInstruction::IsExecute(_) => true,
             _ => false
         }
     }
index d547b364e510acd95c5264c6fc939554ddbc2709..d50bab0354ea3c4597e23e4d83844f20d1a68fec 100644 (file)
@@ -128,9 +128,11 @@ pub fn build_code_dir() -> (Code, CodeDir, OpDir)
     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, 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));
     
     // 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 38f46bfe79a3c0518e42414fe498d5edad614d1c..5b274f5ffce10cd8c53ef9ea843eb9603f00513f 100644 (file)
@@ -212,8 +212,12 @@ impl fmt::Display for ArithmeticInstruction {
                 write!(f, "sub {}, {}, @{}", a1, a2, t),
             &ArithmeticInstruction::Mul(ref a1, ref a2, ref t) =>
                 write!(f, "mul {}, {}, @{}", a1, a2, t),
+            &ArithmeticInstruction::Div(ref a1, ref a2, ref t) =>
+                write!(f, "div {}, {}, @{}", a1, a2, t),
             &ArithmeticInstruction::IDiv(ref a1, ref a2, ref t) =>
                 write!(f, "idiv {}, {}, @{}", a1, a2, t),
+            &ArithmeticInstruction::RDiv(ref a1, ref a2, ref t) =>
+                write!(f, "rdiv {}, {}, @{}", a1, a2, t),
             &ArithmeticInstruction::Neg(ref a, ref t) =>
                 write!(f, "neg {}, @{}", a, t)
         }
index 41d58e9d409000c52b0ceff15b87f2c0f03883b2..c876076e0384be3f54c14a016e0dc4297df944fb 100644 (file)
@@ -6,6 +6,7 @@ use prolog::heapview::*;
 use prolog::and_stack::*;
 use prolog::num::Zero;
 use prolog::num::bigint::BigInt;
+use prolog::num::rational::Ratio;
 use prolog::or_stack::*;
 use prolog::ordered_float::OrderedFloat;
 use prolog::fixtures::*;
@@ -610,7 +611,7 @@ macro_rules! try_or_fail {
         match $e {
             Ok(val)  => val,
             Err(msg) => {
-                $s.throw_exception(string!(msg));
+                $s.throw_exception(msg);
                 return;
             }
         }
@@ -841,7 +842,23 @@ impl MachineState {
         };
     }
 
-    fn get_number(&self, at: &ArithmeticTerm) -> Result<Number, &'static str> {
+    fn get_rational(&self, at: &ArithmeticTerm) -> Result<Ratio<BigInt>, Vec<HeapCellValue>> {
+        let n = self.get_number(at)?;
+
+        match n {
+            Number::Rational(r) => Ok(r),
+            Number::Float(fl) =>
+                if let Some(r) = Ratio::from_float(fl.into_inner()) {
+                    Ok(r)
+                } else {
+                    Err(functor!("instantiation_error", 1, [atom!("(is)/2")]))
+                },
+            Number::Integer(bi) =>
+                Ok(Ratio::from_integer(bi))
+        }
+    }
+    
+    fn get_number(&self, at: &ArithmeticTerm) -> Result<Number, Vec<HeapCellValue>> {
         match at {
             &ArithmeticTerm::Reg(r) => {
                 let addr = self[r].clone();
@@ -852,8 +869,10 @@ impl MachineState {
                         Ok(Number::Integer(bi)),
                     Addr::Con(Constant::Float(fl)) =>
                         Ok(Number::Float(fl)),
+                    Addr::Con(Constant::Rational(r)) =>
+                        Ok(Number::Rational(r)),
                     _ =>
-                        Err("is/2: variable not instantiated to number.")
+                        Err(functor!("instantiation_error", 1, [atom!("(is)/2")]))
                 }
             },
             &ArithmeticTerm::Interm(i)   => Ok(self.interms[i-1].clone()),
@@ -885,6 +904,20 @@ impl MachineState {
                 self.interms[t - 1] = n1 * n2;
                 self.p += 1;
             },
+            &ArithmeticInstruction::RDiv(ref a1, ref a2, t) => {
+                let r1 = try_or_fail!(self, self.get_rational(a1));
+                let r2 = try_or_fail!(self, self.get_rational(a2));
+
+                if r2 == Ratio::zero() {
+                    self.throw_exception(functor!("evaluation_error",
+                                                  1,
+                                                  [atom!("zero_divisor")]));
+                    return;
+                }
+
+                self.interms[t - 1] = Number::Rational(r1 / r2);
+                self.p += 1;
+            },
             &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));
@@ -914,6 +947,20 @@ impl MachineState {
 
                 self.interms[t - 1] = - n1;
                 self.p += 1;
+            },
+            &ArithmeticInstruction::Div(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));
+
+                if n2.is_zero() {
+                    self.throw_exception(functor!("evaluation_error",
+                                                  1,
+                                                  [atom!("zero_divisor")]));
+                    return;
+                }
+                                         
+                self.interms[t - 1] = n1 / n2;
+                self.p += 1;
             }
         };
     }
index 75ee4eb690ce6ca7b1022ea71d438b67edbf2abf..8aff243791cddf16e1a755309606c64a7e373f4b 100644 (file)
@@ -28,12 +28,6 @@ macro_rules! arith {
     )
 }
 
-macro_rules! string {
-    ($str:expr) => {
-        vec![HeapCellValue::Con(Constant::String(String::from($str)))]
-    }
-}
-
 macro_rules! functor {
     ($name:expr, $len:expr, [$($args:expr),*]) => {{
         if $len > 0 {