]> Repositorios git - scryer-prolog.git/commitdiff
add support for comparison operators.
authorMark Thom <[email protected]>
Mon, 4 Dec 2017 00:44:51 +0000 (17:44 -0700)
committerMark Thom <[email protected]>
Mon, 4 Dec 2017 00:44:51 +0000 (17:44 -0700)
README.md
src/prolog/arithmetic.rs
src/prolog/ast.rs
src/prolog/builtins.rs
src/prolog/codegen.rs
src/prolog/io.rs
src/prolog/iterators.rs
src/prolog/machine.rs
src/prolog/macros.rs

index c30fdd4c36a75d8574c9f737586505f25814b7f9..4cdaeba803f2b21b825ab292804aa30a5f94cdda 100644 (file)
--- a/README.md
+++ b/README.md
@@ -49,6 +49,7 @@ 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`,
       `(xor)/2`, `(rem)/2`, `(mod)/2`, `(/\)/2`, `(\/)/2`, `(>>)/2`, `(<<)/2`.
+    * Comparison operators: `>`, `<`, `=<`, `>=`, `=:=`, `=\\=`.
 * `atomic/1`
 * `call/N` (1 <= N <= 63)
 * `catch/3`
index ffbcc84fee53e27e88051143630c307d583b6a16..8f00940ed58141186fd3d44543d8c2606e642b79 100644 (file)
@@ -1,7 +1,5 @@
 use prolog::ast::*;
 use prolog::fixtures::*;
-use prolog::num::{BigInt, Zero};
-use prolog::ordered_float::{OrderedFloat};
 
 use std::cmp::{min, max};
 use std::vec::Vec;
@@ -75,13 +73,14 @@ impl<'a> Iterator for ArithExprIterator<'a> {
 
 pub struct ArithmeticEvaluator<'a> {
     bindings: &'a AllocVarDict<'a>,
+    target_int: usize,
     interm: Vec<ArithmeticTerm>,
     interm_c: usize
 }
 
 impl<'a> ArithmeticEvaluator<'a> {
-    pub fn new(bindings: &'a AllocVarDict<'a>) -> Self {
-        ArithmeticEvaluator { bindings, interm: Vec::new(), interm_c: 1 }
+    pub fn new(bindings: &'a AllocVarDict<'a>, target_int: usize) -> Self {
+        ArithmeticEvaluator { bindings, target_int, interm: Vec::new(), interm_c: target_int }
     }
 
     fn get_unary_instr(name: &Atom, a1: ArithmeticTerm, t: usize)
@@ -213,20 +212,14 @@ impl<'a> ArithmeticEvaluator<'a> {
             }
         }
 
+
         if let Some(arith_term) = self.interm.pop() {
+            let t = self.target_int;
+            
             match arith_term {
-                n @ ArithmeticTerm::Integer(_) => {
-                    let zero = ArithmeticTerm::Integer(BigInt::zero());
-                    code.push(arith![add!(zero, n, 1)]);
-                },
-                n @ ArithmeticTerm::Float(_) => {
-                    let zero = ArithmeticTerm::Float(OrderedFloat(0f64));
-                    code.push(arith![add!(zero, n, 1)]);
-                },
-                r @ ArithmeticTerm::Reg(_) => {
-                    let zero = ArithmeticTerm::Integer(BigInt::zero());
-                    code.push(arith![add!(zero, r, 1)]);
-                },
+                n @ ArithmeticTerm::Integer(_) => code.push(move_at!(n, t)),                
+                n @ ArithmeticTerm::Float(_)   => code.push(move_at!(n, t)),
+                r @ ArithmeticTerm::Reg(_)     => code.push(move_at!(r, t)),
                 _ => {}
             };
         }
index d684d176b44fc96891b313279fa5f920dd89a6ea..9578467b8e3bf04a1de7780f95a6f9e2188cf00a 100644 (file)
@@ -328,9 +328,20 @@ impl InlinedQueryTerm {
     }
 }
 
+#[derive(Clone, Copy)]
+pub enum CompareNumberQT {
+    GreaterThan,
+    LessThan,
+    GreaterThanOrEqual,
+    LessThanOrEqual,
+    NotEqual,
+    Equal
+}
+
 pub enum QueryTerm {
     CallN(Vec<Box<Term>>),
     Catch(Vec<Box<Term>>),
+    CompareNumber(CompareNumberQT, Vec<Box<Term>>),
     Cut,
     Is(Vec<Box<Term>>),
     Inlined(InlinedQueryTerm),
@@ -342,6 +353,7 @@ impl QueryTerm {
     pub fn arity(&self) -> usize {
         match self {
             &QueryTerm::Catch(_) => 3,
+            &QueryTerm::CompareNumber(_, _) => 2,
             &QueryTerm::Throw(_) => 1,
             &QueryTerm::Inlined(ref term) => term.arity(),
             &QueryTerm::Is(_) => 2,
@@ -451,6 +463,54 @@ impl Number {
             &Number::Rational(ref r) => r.is_zero()
         }
     }
+
+    pub fn gt(self, n2: Number) -> bool {
+        match NumberPair::from(self, n2) {
+            NumberPair::Integer(n1, n2) => n1 > n2,
+            NumberPair::Float(n1, n2) => n1 > n2,
+            NumberPair::Rational(n1, n2) => n1 > n2
+        }
+    }
+    
+    pub fn gte(self, n2: Number) -> bool {
+        match NumberPair::from(self, n2) {
+            NumberPair::Integer(n1, n2) => n1 >= n2,
+            NumberPair::Float(n1, n2) => n1 >= n2,
+            NumberPair::Rational(n1, n2) => n1 >= n2
+        }
+    }
+
+    pub fn lt(self, n2: Number) -> bool {
+        match NumberPair::from(self, n2) {
+            NumberPair::Integer(n1, n2) => n1 < n2,
+            NumberPair::Float(n1, n2) => n1 < n2,
+            NumberPair::Rational(n1, n2) => n1 < n2
+        }
+    }
+
+    pub fn lte(self, n2: Number) -> bool {
+        match NumberPair::from(self, n2) {
+            NumberPair::Integer(n1, n2) => n1 <= n2,
+            NumberPair::Float(n1, n2) => n1 <= n2,
+            NumberPair::Rational(n1, n2) => n1 <= n2
+        }
+    }
+
+    pub fn ne(self, n2: Number) -> bool {
+        match NumberPair::from(self, n2) {
+            NumberPair::Integer(n1, n2) => n1 != n2,
+            NumberPair::Float(n1, n2) => n1 != n2,
+            NumberPair::Rational(n1, n2) => n1 != n2
+        }
+    }
+
+    pub fn eq(self, n2: Number) -> bool {
+        match NumberPair::from(self, n2) {
+            NumberPair::Integer(n1, n2) => n1 == n2,
+            NumberPair::Float(n1, n2) => n1 == n2,
+            NumberPair::Rational(n1, n2) => n1 == n2
+        }
+    }    
 }
 
 enum NumberPair {
@@ -675,6 +735,8 @@ pub enum ControlInstruction {
     Execute(Atom, usize),
     ExecuteN(usize),
     Goto(usize, usize), // p, arity.
+    CompareNumberCall(CompareNumberQT),
+    CompareNumberExecute(CompareNumberQT),    
     IsCall(RegType),
     IsExecute(RegType),
     Proceed,
@@ -728,6 +790,7 @@ pub enum FactInstruction {
 }
 
 pub enum QueryInstruction {
+    MoveArithmeticTerm(ArithmeticTerm, usize),
     GetVariable(RegType, usize),
     PutConstant(Level, Constant, RegType),
     PutList(Level, RegType),
index da435d9feb49b494908b7a14c5dd29276173d071..7c074e81fdea36c2776e600a3ed822d302ef4bb9 100644 (file)
@@ -140,6 +140,14 @@ pub fn build_code_dir() -> (Code, CodeDir, OpDir)
     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));
+
+    // arithmetic comparison operators.
+    op_dir.insert((String::from(">"), Fixity::In), (XFX, 700));
+    op_dir.insert((String::from("<"), Fixity::In), (XFX, 700));
+    op_dir.insert((String::from("=\\="), Fixity::In), (XFX, 700));
+    op_dir.insert((String::from("=:="), Fixity::In), (XFX, 700));
+    op_dir.insert((String::from(">="), Fixity::In), (XFX, 700));
+    op_dir.insert((String::from("=<"), Fixity::In), (XFX, 700));
     
     // 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 aa573b92961229313e09faf59b29e3d43820da42..c1784829171efa132d2941f179ec18763296499c 100644 (file)
@@ -276,6 +276,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                     *ctrl = ControlInstruction::Execute(name, arity),
                 ControlInstruction::CallN(arity) =>
                     *ctrl = ControlInstruction::ExecuteN(arity),
+                ControlInstruction::CompareNumberCall(cmp) =>
+                    *ctrl = ControlInstruction::CompareNumberExecute(cmp),
                 ControlInstruction::IsCall(r) =>
                     *ctrl = ControlInstruction::IsExecute(r),
                 ControlInstruction::CatchCall =>
@@ -322,6 +324,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
         }
     }
     
+    fn call_arith_eval(&self, term: &'a Term, target_int: usize) -> Result<Code, ArithmeticError> {
+        let mut evaluator = ArithmeticEvaluator::new(self.marker.bindings(), target_int);
+        evaluator.eval(term)
+    }
+    
     fn compile_seq(&mut self,
                    iter: ChunkedIterator<'a>,
                    conjunct_info: &ConjunctInfo<'a>,
@@ -355,11 +362,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                     &QueryTerm::Inlined(ref term) =>
                         self.compile_inlined(term, term_loc, code),
                     &QueryTerm::Is(ref terms) => {
-                        let mut arith_code = {
-                            let mut evaluator = ArithmeticEvaluator::new(self.marker.bindings());
-                            evaluator.eval(terms[1].as_ref())?
-                        };
-
+                        let mut arith_code = self.call_arith_eval(terms[1].as_ref(), 1)?;
                         code.append(&mut arith_code);
 
                         match terms[0].as_ref() {
@@ -390,6 +393,15 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                             }
                         }
                     },
+                    &QueryTerm::CompareNumber(cmp, ref terms) => {
+                        let mut larith_code = self.call_arith_eval(terms[0].as_ref(), 1)?;
+                        let mut rarith_code = self.call_arith_eval(terms[1].as_ref(), 2)?;
+
+                        code.append(&mut larith_code);
+                        code.append(&mut rarith_code);
+
+                        code.push(compare_number_call!(cmp));
+                    },
                     _ if chunk_num == 0 => {
                         self.marker.reset_arg(term.arity());
 
index eae23dcd85a1271359eb5c516b8dc27e2ed86cb1..ccede0414b1073487acf2b9cb682ea650d00085a 100644 (file)
@@ -46,6 +46,8 @@ impl fmt::Display for FactInstruction {
 impl fmt::Display for QueryInstruction {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
+            &QueryInstruction::MoveArithmeticTerm(ref at, ref r) =>
+                write!(f, "move_arithmetic_term {}, {}", at, r),
             &QueryInstruction::GetVariable(ref x, ref a) =>
                 write!(f, "query:get_variable {}, A{}", x, a),
             &QueryInstruction::PutConstant(Level::Shallow, ref constant, ref r) =>
@@ -80,6 +82,19 @@ impl fmt::Display for QueryInstruction {
     }
 }
 
+impl fmt::Display for CompareNumberQT {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &CompareNumberQT::GreaterThan => write!(f, ">"),
+            &CompareNumberQT::GreaterThanOrEqual => write!(f, ">="),
+            &CompareNumberQT::LessThan => write!(f, "<"),
+            &CompareNumberQT::LessThanOrEqual => write!(f, "<="),
+            &CompareNumberQT::NotEqual => write!(f, "=\\="),
+            &CompareNumberQT::Equal => write!(f, "=:="),            
+        }
+    }
+}
+
 impl fmt::Display for ControlInstruction {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
@@ -93,6 +108,10 @@ impl fmt::Display for ControlInstruction {
                 write!(f, "call_catch"),
             &ControlInstruction::CatchExecute =>
                 write!(f, "execute_catch"),
+            &ControlInstruction::CompareNumberCall(cmp) =>
+                write!(f, "n_compare_call {}", cmp),
+            &ControlInstruction::CompareNumberExecute(cmp) =>
+                write!(f, "n_compare_execute {}", cmp),
             &ControlInstruction::ExecuteN(arity) =>
                 write!(f, "execute_N {}", arity),
             &ControlInstruction::Deallocate =>
index 190dd1f83f3218b5309fbfc6da8fb97a7894f43b..9471619acae723f4a6019105e2cc4847d981474d 100644 (file)
@@ -40,7 +40,7 @@ impl<'a> QueryIterator<'a> {
                 let state = IteratorState::Clause(0, ClauseType::Catch, terms);
                 QueryIterator { state_stack: vec![state] }
             },
-            &QueryTerm::Is(ref terms) => {
+            &QueryTerm::CompareNumber(_, ref terms) | &QueryTerm::Is(ref terms) => {
                 let state = IteratorState::Clause(0, ClauseType::Is, terms);
                 QueryIterator { state_stack: vec![state] }
             },
@@ -270,7 +270,7 @@ impl<'a> ChunkedIterator<'a>
                     arity = child_terms.len();
                     break;
                 },
-                &QueryTerm::Is(_) => {
+                &QueryTerm::Is(_) | &QueryTerm::CompareNumber(_, _) => {
                     result.push(term);
                     arity = 2;
                     break;
index 30ef2721c41979bd9c36bc50f6afa0e0b80ed182..939b81fbed0eb1b63bec7ce6b0d047c200d56074 100644 (file)
@@ -1379,6 +1379,10 @@ impl MachineState {
 
     fn execute_query_instr(&mut self, instr: &QueryInstruction) {
         match instr {
+            &QueryInstruction::MoveArithmeticTerm(ref at, t) => {
+                let n = try_or_fail!(self, self.get_number(at));
+                self.interms[t - 1] = n;
+            },
             &QueryInstruction::GetVariable(norm, arg) =>
                 self[norm] = self.registers[arg].clone(),
             &QueryInstruction::PutConstant(_, ref constant, reg) =>
@@ -1740,6 +1744,21 @@ impl MachineState {
         };
     }
 
+    fn handle_n_compare(&mut self, cmp: CompareNumberQT) {
+        let n1 = self.interms[0].clone();
+        let n2 = self.interms[1].clone();
+        
+        self.fail = match cmp {
+            CompareNumberQT::GreaterThan if !(n1.gt(n2)) => true,
+            CompareNumberQT::GreaterThanOrEqual if !(n1.gte(n2)) => true,
+            CompareNumberQT::LessThan if !(n1.lt(n2)) => true,
+            CompareNumberQT::LessThanOrEqual if !(n1.lte(n2)) => true,
+            CompareNumberQT::NotEqual if !(n1.ne(n2)) => true,
+            CompareNumberQT::Equal if !(n1.eq(n2)) => true,
+            _ => false
+        };
+    }
+    
     fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
     {
         match instr {
@@ -1819,6 +1838,14 @@ impl MachineState {
             &ControlInstruction::ThrowExecute => {
                 self.goto_throw();
             },
+            &ControlInstruction::CompareNumberCall(cmp) => {
+                self.handle_n_compare(cmp);
+                self.p += 1;
+            },
+            &ControlInstruction::CompareNumberExecute(cmp) => {
+                self.handle_n_compare(cmp);
+                self.p = self.cp;
+            },
             &ControlInstruction::IsCall(r) => {
                 let a1 = self[r].clone();
                 let a2 = Addr::Con(Constant::from(self.interms[0].clone()));
index 8aff243791cddf16e1a755309606c64a7e373f4b..d2ec77f88d42854d831c050933a33ec4e83fd9e5 100644 (file)
@@ -16,15 +16,15 @@ macro_rules! deallocate {
     )
 }
 
-macro_rules! query {
-    [$($x:expr),+] => (
-        Line::Query(vec![$($x),+])
+macro_rules! move_at {
+    ($at:expr, $r:expr) => (
+        Line::Query(vec![QueryInstruction::MoveArithmeticTerm($at, $r)])
     )
 }
 
-macro_rules! arith {
-    ($x:expr) => (
-        Line::Arithmetic($x)
+macro_rules! query {
+    [$($x:expr),+] => (
+        Line::Query(vec![$($x),+])
     )
 }
 
@@ -50,6 +50,12 @@ macro_rules! fact {
     )
 }
 
+macro_rules! compare_number_call {
+    ($cmp: expr) => (
+        Line::Control(ControlInstruction::CompareNumberCall($cmp))
+    )
+}
+
 macro_rules! temp_v {
     ($x:expr) => (
         RegType::Temp($x)
@@ -105,12 +111,6 @@ macro_rules! put_unsafe_value {
     )
 }
 
-macro_rules! add {
-    ($r1:expr, $r2:expr, $t:expr) => (
-        ArithmeticInstruction::Add($r1, $r2, $t)
-    )
-}
-
 macro_rules! try_me_else {
     ($o:expr) => (
         Line::Choice(ChoiceInstruction::TryMeElse($o))