]> Repositorios git - scryer-prolog.git/commitdiff
add eq, neq predicates.
authorMark Thom <[email protected]>
Sun, 11 Feb 2018 06:34:30 +0000 (23:34 -0700)
committerMark Thom <[email protected]>
Sun, 11 Feb 2018 06:34:30 +0000 (23:34 -0700)
README.md
src/prolog/ast.rs
src/prolog/builtins.rs
src/prolog/codegen.rs
src/prolog/heap_iter.rs
src/prolog/io.rs
src/prolog/iterators.rs
src/prolog/machine/machine_state_impl.rs
src/prolog/macros.rs
src/prolog/parser
src/tests.rs

index 17499cce36a60186783b789db205d2425b6e0845..e2f976999e5507d5e0ba5b21e228a7380b94b744 100644 (file)
--- a/README.md
+++ b/README.md
@@ -18,23 +18,23 @@ Extend rusty-wam to include the following, among other features:
 * call/N as a built-in meta-predicate (_done_).
 * ISO Prolog compliant throw/catch (_done_).
 * Built-in and user-defined operators of all fixities, with custom
-  associativity and precedence (_done_). 
+  associativity and precedence (_done_).
 * Bignum, rational number and floating point arithmetic (_done_).
 * Built-in control operators (`,`, `;`, `->`, etc.) (_done_).
 * Built-in predicates for list processing and top-level declarative
   control (`setup_call_control/3`, `call_with_inference_limit/3`,
   etc.) (_in progress_).
-* Add a rudimentary module system.  
+* Add a rudimentary module system.
 * Attributed variables using the SICStus Prolog interface and
   semantics. Adding coroutines like `dif/2`, `freeze/2`, etc.
-  is straightforward with attributed variables. 
+  is straightforward with attributed variables.
 * An occurs check.
 * Mode declarations.
 * Extensions for clp(FD).
 * `if_` and related predicates, following the developments of the
   paper "Indexing `dif/2`".
 * Strings, blobs, and other data types.
-  
+
 ## Phase 3
 
 Use the WAM code produced by the completed code generator to target LLVM
@@ -78,6 +78,8 @@ The following predicates are built-in to rusty-wam.
       `(xor)/2`, `(rem)/2`, `(mod)/2`, `(/\)/2`, `(\/)/2`, `(>>)/2`, `(<<)/2`.
     * Comparison operators: `>`, `<`, `=<`, `>=`, `=:=`, `=\=`.
 * `(\+)/1`
+* `(==)/2`
+* `(\==)/2`
 * `(=)/2`
 * `(=..)/2`
 * `(->)/2`
@@ -159,7 +161,7 @@ prolog> :{
 member(X, [X|_]).
 member(X, [_|Xs]) :- member(X, Xs).
 }:
-prolog> ?- member(X, [a, b, c]).      
+prolog> ?- member(X, [a, b, c]).
 true .
 X = a ;
 X = b ;
index 4d82a1947bb8e8455ce858e0ccd119dd2c2f78a7..79f726429b0eea9c8e023a212f99bb558321337c 100644 (file)
@@ -399,12 +399,14 @@ pub enum QueryTerm {
     Catch(Vec<Box<Term>>),
     Cut,
     Display(Vec<Box<Term>>),
-    DuplicateTerm(Vec<Box<Term>>),    
+    DuplicateTerm(Vec<Box<Term>>),
+    Eq(Vec<Box<Term>>),
     Functor(Vec<Box<Term>>),
     Ground(Vec<Box<Term>>),
     Inlined(InlinedQueryTerm),
     Is(Vec<Box<Term>>),
     Jump(JumpStub),
+    NotEq(Vec<Box<Term>>),
     SetupCallCleanup(Vec<Box<Term>>),
     Term(Term),
     Throw(Vec<Box<Term>>)
@@ -417,12 +419,14 @@ impl QueryTerm {
             &QueryTerm::Catch(_) => 3,
             &QueryTerm::Display(_) => 1,
             &QueryTerm::Throw(_) => 1,
-            &QueryTerm::DuplicateTerm(_) => 2,            
+            &QueryTerm::DuplicateTerm(_) => 2,
+            &QueryTerm::Eq(_) => 2,
             &QueryTerm::Functor(_) => 3,
             &QueryTerm::Ground(_) => 1,
             &QueryTerm::Inlined(ref term) => term.arity(),
             &QueryTerm::Is(_) => 2,
             &QueryTerm::Jump(ref vars) => vars.len(),
+            &QueryTerm::NotEq(_) => 2,
             &QueryTerm::CallN(ref terms) => terms.len(),
             &QueryTerm::Cut => 0,
             &QueryTerm::SetupCallCleanup(_) => 3,
@@ -445,9 +449,11 @@ pub enum ClauseType<'a> {
     Deep(Level, &'a Cell<RegType>, &'a TabledRc<Atom>, Option<Fixity>),
     Display,
     DuplicateTerm,
+    Eq,
     Functor,
     Ground,
     Is,
+    NotEq,
     Root(&'a TabledRc<Atom>),
     SetupCallCleanup,
     Throw,
@@ -463,9 +469,11 @@ impl<'a> ClauseType<'a> {
             &ClauseType::Display => "display",
             &ClauseType::Deep(_, _, name, _) => name.as_str(),
             &ClauseType::DuplicateTerm => "duplicate_term",
+            &ClauseType::Eq => "==",
             &ClauseType::Functor => "functor",
             &ClauseType::Ground  => "ground",            
             &ClauseType::Is => "is",
+            &ClauseType::NotEq => "\\==",
             &ClauseType::Root(name) => name.as_str(),
             &ClauseType::SetupCallCleanup => "setup_call_cleanup",
             &ClauseType::Throw => "throw"
@@ -857,7 +865,9 @@ pub enum ControlInstruction {
     DisplayExecute,
     Deallocate,
     DuplicateTermCall,
-    DuplicateTermExecute,    
+    DuplicateTermExecute,
+    EqCall,
+    EqExecute,
     Execute(TabledRc<Atom>, usize),
     ExecuteN(usize),
     FunctorCall,
@@ -871,6 +881,8 @@ pub enum ControlInstruction {
     JmpByExecute(usize, usize),
     IsCall(RegType, ArithmeticTerm),
     IsExecute(RegType, ArithmeticTerm),
+    NotEqCall,
+    NotEqExecute,
     Proceed,
     ThrowCall,
     ThrowExecute,
@@ -888,11 +900,15 @@ impl ControlInstruction {
             &ControlInstruction::DisplayExecute => true,
             &ControlInstruction::DuplicateTermCall => true,
             &ControlInstruction::DuplicateTermExecute => true,
+            &ControlInstruction::EqCall => true,
+            &ControlInstruction::EqExecute => true,
             &ControlInstruction::Execute(_, _)  => true,
             &ControlInstruction::CallN(_) => true,            
             &ControlInstruction::ExecuteN(_) => true,
             &ControlInstruction::FunctorCall => true,
             &ControlInstruction::FunctorExecute => true,
+            &ControlInstruction::NotEqCall => true,
+            &ControlInstruction::NotEqExecute => true,
             &ControlInstruction::ThrowCall => true,
             &ControlInstruction::ThrowExecute => true,
             &ControlInstruction::GetCleanerCall => true,
@@ -903,7 +919,7 @@ impl ControlInstruction {
             &ControlInstruction::IsCall(..) => true,
             &ControlInstruction::IsExecute(..) => true,
             &ControlInstruction::JmpByCall(..) => true,
-            &ControlInstruction::JmpByExecute(..) => true,
+            &ControlInstruction::JmpByExecute(..) => true,            
             _ => false
         }
     }
index a0889d58201a80ea1686d5f1b08036db2110e7a5..dc05b5687c783524330de9b8bee6ca9b5c69dc9f 100644 (file)
@@ -540,6 +540,8 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
          restore_cut_policy!(), // restore_cut_policy/0, 382.
          proceed!(),
          ground_execute!(), // ground/1, 384.
+         eq_execute!(), // (==)/2, 385.
+         not_eq_execute!(), // (\==)/2, 386.
     ]
 }
 
@@ -590,6 +592,7 @@ pub fn build_code_dir(atom_tbl: TabledData<Atom>) -> (Code, CodeDir, OpDir)
 
     op_dir.insert((tabled_rc!("=..", atom_tbl), Fixity::In), (XFX, 700));
     op_dir.insert((tabled_rc!("==", atom_tbl), Fixity::In), (XFX, 700));
+    op_dir.insert((tabled_rc!("\\==", atom_tbl), 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)
@@ -634,6 +637,8 @@ pub fn build_code_dir(atom_tbl: TabledData<Atom>) -> (Code, CodeDir, OpDir)
     code_dir.insert((tabled_rc!("nonvar", atom_tbl), 1), (PredicateKeyType::BuiltIn, 380));
 
     code_dir.insert((tabled_rc!("ground", atom_tbl), 1), (PredicateKeyType::BuiltIn, 384));
+    code_dir.insert((tabled_rc!("==", atom_tbl), 2), (PredicateKeyType::BuiltIn, 385));
+    code_dir.insert((tabled_rc!("\\==", atom_tbl), 2), (PredicateKeyType::BuiltIn, 386));    
     
     (builtin_code, code_dir, op_dir)
 }
index a386e611e94b758e787818ffbafd685576caef9e..6e16dcc940499615ed4ab36279fb23afcd721f5f 100644 (file)
@@ -259,10 +259,12 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                 code.push(Line::Control(ControlInstruction::CatchCall)),
             &QueryTerm::Display(_) =>
                 code.push(Line::Control(ControlInstruction::DisplayCall)),
-            &QueryTerm::DuplicateTerm(_) =>                
+            &QueryTerm::DuplicateTerm(_) =>
                 code.push(Line::Control(ControlInstruction::DuplicateTermCall)),
+            &QueryTerm::Eq(_) =>
+                code.push(Line::Control(ControlInstruction::EqCall)),
             &QueryTerm::Ground(_) =>
-                code.push(Line::Control(ControlInstruction::GroundCall)),            
+                code.push(Line::Control(ControlInstruction::GroundCall)),
             &QueryTerm::Functor(_) =>
                 code.push(Line::Control(ControlInstruction::FunctorCall)),
             &QueryTerm::Inlined(_) =>
@@ -270,6 +272,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
             &QueryTerm::Jump(ref vars) => {
                 code.push(jmp_call!(vars.len(), 0));
             },
+            &QueryTerm::NotEq(_) =>
+                code.push(Line::Control(ControlInstruction::NotEqCall)),
             &QueryTerm::Term(Term::Constant(_, Constant::Atom(ref atom))) => {
                 let call = ControlInstruction::Call(atom.clone(), 0, pvs);
                 code.push(Line::Control(call));
@@ -303,12 +307,16 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                         *ctrl = ControlInstruction::DisplayExecute,
                     ControlInstruction::DuplicateTermCall =>
                         *ctrl = ControlInstruction::DuplicateTermExecute,
+                    ControlInstruction::EqCall =>
+                        *ctrl = ControlInstruction::EqExecute,
                     ControlInstruction::GroundCall =>
                         *ctrl = ControlInstruction::GroundExecute,
                     ControlInstruction::FunctorCall =>
                         *ctrl = ControlInstruction::FunctorExecute,
                     ControlInstruction::JmpByCall(arity, offset) =>
                         *ctrl = ControlInstruction::JmpByExecute(arity, offset),
+                    ControlInstruction::NotEqCall =>
+                        *ctrl = ControlInstruction::NotEqExecute,
                     ControlInstruction::CatchCall =>
                         *ctrl = ControlInstruction::CatchExecute,
                     ControlInstruction::ThrowCall =>
@@ -509,21 +517,14 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                     },
                     &QueryTerm::Inlined(ref term) =>
                         self.compile_inlined(term, term_loc, code)?,
-                    _ if chunk_num == 0 => {
-                        self.marker.reset_arg(term.arity());
-
-                        let iter  = term.post_order_iter();
-                        let query = self.compile_target(iter, term_loc, is_exposed);
-
-                        if !query.is_empty() {
-                            code.push(Line::Query(query));
-                        }
-
-                        Self::add_conditional_call(code, term, conjunct_info.perm_vars());
-                    },
                     _ => {
-                        let num_vars = conjunct_info.perm_vs.vars_above_threshold(i + 1);
-                        self.compile_query_line(term, term_loc, code, num_vars, is_exposed);
+                        let num_perm_vars = if chunk_num == 0 {
+                            conjunct_info.perm_vars()
+                        } else {
+                            conjunct_info.perm_vs.vars_above_threshold(i + 1)
+                        };
+
+                        self.compile_query_line(term, term_loc, code, num_perm_vars, is_exposed);
                     },
                 };
 
@@ -666,18 +667,18 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
     }
 
     fn compile_query_line(&mut self, term: &'a QueryTerm, term_loc: GenContext,
-                          code: &mut Code, index: usize, is_exposed: bool)
+                          code: &mut Code, num_perm_vars_left: usize, is_exposed: bool)
     {
         self.marker.reset_arg(term.arity());
 
-        let iter = term.post_order_iter();
-        let compiled_query = self.compile_target(iter, term_loc, is_exposed);
+        let iter  = term.post_order_iter();
+        let query = self.compile_target(iter, term_loc, is_exposed);
 
-        if !compiled_query.is_empty() {
-            code.push(Line::Query(compiled_query));
+        if !query.is_empty() {
+            code.push(Line::Query(query));
         }
 
-        Self::add_conditional_call(code, term, index);
+        Self::add_conditional_call(code, term, num_perm_vars_left);
     }
 
     pub fn compile_query(&mut self, query: &'a Vec<QueryTerm>) -> Result<Code, ParserError>
index 608d5d13e0e28836ca1c3e6246f6c43540668e74..a61f4a0e36dcdc459d1d49a4fc377c346b87196f 100644 (file)
@@ -123,10 +123,18 @@ impl MachineState {
         HeapCellPostOrderIterator::new(HeapCellPreOrderIterator::new(self, a))
     }
 
-    pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr) -> HeapCellAcyclicIterator<HeapCellPreOrderIterator<'a>>
+    pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr)
+                                      -> HeapCellAcyclicIterator<HeapCellPreOrderIterator<'a>>
     {
         HeapCellAcyclicIterator::new(HeapCellPreOrderIterator::new(self, a))
     }
+
+    pub fn zipped_acyclic_pre_order_iter<'a>(&'a self, a1: Addr, a2: Addr)
+                                             -> HeapCellZippedAcyclicIterator<HeapCellPreOrderIterator<'a>>
+    {
+        HeapCellZippedAcyclicIterator::new(HeapCellPreOrderIterator::new(self, a1),
+                                           HeapCellPreOrderIterator::new(self, a2))
+    }
 }
 
 pub trait MutStackHeapCellIterator {
@@ -171,3 +179,43 @@ impl<HeapCellIter> Iterator for HeapCellAcyclicIterator<HeapCellIter>
         self.iter.next()
     }
 }
+
+pub struct HeapCellZippedAcyclicIterator<HeapCellIter> {
+    i1: HeapCellIter,
+    i2: HeapCellIter,
+    seen: HashSet<(Addr, Addr)>
+}
+
+impl<HeapCellIter: MutStackHeapCellIterator> HeapCellZippedAcyclicIterator<HeapCellIter>
+{
+    pub fn new(i1: HeapCellIter, i2: HeapCellIter) -> Self {
+        HeapCellZippedAcyclicIterator { i1, i2, seen: HashSet::new() }
+    }
+}
+
+impl<HeapCellIter> Iterator for HeapCellZippedAcyclicIterator<HeapCellIter>
+    where HeapCellIter: Iterator<Item=HeapCellValue>
+                      + MutStackHeapCellIterator
+{
+    type Item = (HeapCellValue, HeapCellValue);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        while let (Some(a1), Some(a2)) = (self.i1.stack().pop(), self.i2.stack().pop()) {
+            if self.seen.contains(&(a1.clone(), a2.clone())) {
+                continue;
+            } else {
+                self.i1.stack().push(a1.clone());
+                self.i2.stack().push(a2.clone());                
+                self.seen.insert((a1, a2));
+
+                break;
+            }
+        }
+
+        if let (Some(v1), Some(v2)) = (self.i1.next(), self.i2.next()) {
+            Some((v1, v2))
+        } else {
+            None
+        }
+    }
+}
index dc23a742f768b44590835d4878684bc8953f74fe..55d0d9eed8f7f98535fb2147a51c8bf662ccf554 100644 (file)
@@ -123,6 +123,10 @@ impl fmt::Display for ControlInstruction {
                 write!(f, "call_duplicate_term"),
             &ControlInstruction::DuplicateTermExecute =>
                 write!(f, "execute_duplicate_term"),
+            &ControlInstruction::EqCall =>
+                write!(f, "eq_call"),
+            &ControlInstruction::EqExecute =>
+                write!(f, "eq_execute"),
             &ControlInstruction::ExecuteN(arity) =>
                 write!(f, "execute_N {}", arity),
             &ControlInstruction::FunctorCall =>
@@ -151,6 +155,10 @@ impl fmt::Display for ControlInstruction {
                 write!(f, "jmp_by_call {}/{}", offset, arity),
             &ControlInstruction::JmpByExecute(arity, offset) =>
                 write!(f, "jmp_by_execute {}/{}", offset, arity),
+            &ControlInstruction::NotEqCall =>
+                write!(f, "neq_call"),
+            &ControlInstruction::NotEqExecute =>
+                write!(f, "neq_execute"),
             &ControlInstruction::Proceed =>
                 write!(f, "proceed"),
             &ControlInstruction::ThrowCall =>
index 0a5b08c16c2bccdbdc162b7d82ee1d7740c8daf8..4b0f7f6334f2968829812aaeb083b02f0b4077ce 100644 (file)
@@ -52,6 +52,10 @@ impl<'a> QueryIterator<'a> {
                 let state = TermIterState::Clause(0, ClauseType::SetupCallCleanup, terms);
                 QueryIterator { state_stack: vec![state] }
             },
+            &QueryTerm::Eq(ref terms) => {
+                let state = TermIterState::Clause(0, ClauseType::Eq, terms);
+                QueryIterator { state_stack: vec![state] }
+            },
             &QueryTerm::Functor(ref terms) => {
                 let state = TermIterState::Clause(0, ClauseType::Functor, terms);
                 QueryIterator { state_stack: vec![state] }
@@ -64,9 +68,9 @@ impl<'a> QueryIterator<'a> {
                 let state = TermIterState::Clause(0, ClauseType::Ground, terms);
                 QueryIterator { state_stack: vec![state] }
             },
-            &QueryTerm::Inlined(InlinedQueryTerm::CompareNumber(qt, ref terms)) => {                
+            &QueryTerm::Inlined(InlinedQueryTerm::CompareNumber(qt, ref terms)) => {
                 let state = TermIterState::Clause(0, ClauseType::CompareNumber(qt), terms);
-                QueryIterator { state_stack: vec![state] }            
+                QueryIterator { state_stack: vec![state] }
             },
             &QueryTerm::Is(ref terms) => {
                 let state = TermIterState::Clause(0, ClauseType::Is, terms);
@@ -79,8 +83,12 @@ impl<'a> QueryIterator<'a> {
           | &QueryTerm::Inlined(InlinedQueryTerm::IsFloat(ref terms))
           | &QueryTerm::Inlined(InlinedQueryTerm::IsRational(ref terms))
           | &QueryTerm::Inlined(InlinedQueryTerm::IsCompound(ref terms))
-          | &QueryTerm::Inlined(InlinedQueryTerm::IsString(ref terms)) =>      
+          | &QueryTerm::Inlined(InlinedQueryTerm::IsString(ref terms)) =>
                 Self::from_term(terms[0].as_ref()),
+            &QueryTerm::NotEq(ref terms) => {
+                let state = TermIterState::Clause(0, ClauseType::NotEq, terms);
+                QueryIterator { state_stack: vec![state] }
+            },
             &QueryTerm::Term(ref term) =>
                 Self::from_term(term),
             &QueryTerm::Throw(ref term) => {
@@ -332,7 +340,8 @@ impl<'a> ChunkedIterator<'a>
                     arity = 3;
                     break;
                 },
-                &QueryTerm::Is(_) | &QueryTerm::DuplicateTerm(_) => {
+                &QueryTerm::Eq(_) | &QueryTerm::NotEq(_)
+              | &QueryTerm::Is(_) | &QueryTerm::DuplicateTerm(_) => {
                     result.push(term);
                     arity = 2;
                     break;
@@ -345,7 +354,7 @@ impl<'a> ChunkedIterator<'a>
                     if self.term_loc.chunk_num() > 0 {
                         self.deep_cut_encountered = true;
                     }
-                },                
+                },
             };
 
             item = self.iter.next();
index 08872cf25bbc10256ff156933069b7957fedf27a..a9c594b8a479943a6f33554c0ad9efe3fd44b80e 100644 (file)
@@ -1461,17 +1461,15 @@ SetupCallCleanupCutPolicy.")
         self.unify(Addr::HeapCell(old_h), a2);
     }
 
-    /* TODO: needs careful consideration of cyclic terms.
     // returns true on failure.
     fn eq_test(&self) -> bool
     {
         let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
         let a2 = self.store(self.deref(self[temp_v!(2)].clone()));
 
-        let iter1 = self.acyclic_pre_order_iter(a1);
-        let iter2 = self.acyclic_pre_order_iter(a2);
+        let iter = self.zipped_acyclic_pre_order_iter(a1, a2);
 
-        for (v1, v2) in iter1.zip(iter2) {
+        for (v1, v2) in iter {
             match (v1, v2) {
                 (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) =>
                     if ar1 != ar2 || *n1 != *n2 {
@@ -1482,16 +1480,13 @@ SetupCallCleanupCutPolicy.")
                 (HeapCellValue::Addr(a1), HeapCellValue::Addr(a2)) =>
                     if a1 != a2 {
                         return true;
-                    },
-                _ => {
-                    return true;
-                }
+                    },                
+                _ => return true
             }
         }
 
         false
     }
-    */
 
     // returns true on failure.
     fn ground_test(&self) -> bool
@@ -1620,6 +1615,14 @@ SetupCallCleanupCutPolicy.")
                 self.duplicate_term();
                 self.p = self.cp;
             },
+            &ControlInstruction::EqCall => {
+                self.fail = self.eq_test();
+                self.p += 1;
+            },
+            &ControlInstruction::EqExecute => {
+                self.fail = self.eq_test();
+                self.p = self.cp;
+            },
             &ControlInstruction::GroundCall => {
                 self.fail = self.ground_test();
                 self.p += 1;
@@ -1707,6 +1710,14 @@ SetupCallCleanupCutPolicy.")
                 self.b0 = self.b;
                 self.p += offset;
             },
+            &ControlInstruction::NotEqCall => {
+                self.fail = !self.eq_test();
+                self.p += 1;
+            },
+            &ControlInstruction::NotEqExecute => {
+                self.fail = !self.eq_test();
+                self.p = self.cp;
+            },            
             &ControlInstruction::Proceed =>
                 self.p = self.cp,
             &ControlInstruction::ThrowCall => {
index a201d353d9ee295f8780be3e03387b78e73dc3f9..281129a8f899b45ae2b77e0beb0bdeb4a13ed632 100644 (file)
@@ -568,3 +568,15 @@ macro_rules! ground_execute {
         Line::Control(ControlInstruction::GroundExecute)
     )
 }
+
+macro_rules! eq_execute {
+    () => (
+        Line::Control(ControlInstruction::EqExecute)
+    )
+}
+
+macro_rules! not_eq_execute {
+    () => (
+        Line::Control(ControlInstruction::NotEqExecute)
+    )
+}
index 5de18ac4728701492cc19fd08d32b40f4d5918a3..b50ed579fdb245f2d42fcd05b546c23f077b0991 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 5de18ac4728701492cc19fd08d32b40f4d5918a3
+Subproject commit b50ed579fdb245f2d42fcd05b546c23f077b0991
index c4382421cf1becd1ccd3c0d33aeb928cca93abfe..dd2019b5ed481a22fdf6c703f9e226027595c610 100644 (file)
@@ -1339,6 +1339,18 @@ fn test_queries_on_builtins()
     assert_prolog_failure!(&mut wam, "?- B = f(A), ground(A).");
 
     assert_prolog_success!(&mut wam, "?- ground(x), ground(f(x)), X = f(x), ground(g(f(X), [a,b])).");
+
+    assert_prolog_success!(&mut wam, "?- A = f(A), g(A, B) == g(f(A), B).");
+    assert_prolog_failure!(&mut wam, "?- A = f(A), g(A, B) == g(f(A), b).");
+    assert_prolog_failure!(&mut wam, "?- A == B.");
+    assert_prolog_failure!(&mut wam, "?- A == 12.1.");
+    assert_prolog_success!(&mut wam, "?- X = x, f(X, x) == f(x, X).");
+
+    assert_prolog_failure!(&mut wam, "?- A = f(A), g(A, B) \\== g(f(A), B).");
+    assert_prolog_success!(&mut wam, "?- A = f(A), g(A, B) \\== g(f(A), b).");
+    assert_prolog_success!(&mut wam, "?- A \\== B.");
+    assert_prolog_success!(&mut wam, "?- A \\== 12.1.");
+    assert_prolog_failure!(&mut wam, "?- X = x, f(X, x) \\== f(x, X).");
 }
 
 #[test]
@@ -1393,5 +1405,5 @@ fn test_queries_on_setup_call_cleanup()
     assert_prolog_success!(&mut wam, "?- catch(setup_call_cleanup(true,throw(goal),throw(cl)), Pat, true).",
                            [["Pat = goal"]]);
     assert_prolog_success!(&mut wam, "?- catch(( setup_call_cleanup(true,(G=1;G=2),throw(cl)), throw(cont)), Pat, true).",
-                           [["Pat = cont", "G = _1"]]);
+                           [["Pat = cont", "G = _1"]]);    
 }