]> Repositorios git - scryer-prolog.git/commitdiff
add ground/1.
authorMark Thom <[email protected]>
Fri, 9 Feb 2018 06:51:05 +0000 (23:51 -0700)
committerMark Thom <[email protected]>
Fri, 9 Feb 2018 06:51:05 +0000 (23:51 -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 1fbeb7ca7a10392bff844fb61f06a5bc0f282525..17499cce36a60186783b789db205d2425b6e0845 100644 (file)
--- a/README.md
+++ b/README.md
@@ -94,6 +94,7 @@ The following predicates are built-in to rusty-wam.
 * `false/0`
 * `float/1`
 * `functor/3`
+* `ground/1`
 * `integer/1`
 * `length/2`
 * `maplist/1..8`
index 24223854f55c3506045201cd569c57f0f60566d6..8c419f93787c70fa0346c38ea45c79a0c82e1e86 100644 (file)
@@ -399,8 +399,9 @@ pub enum QueryTerm {
     Catch(Vec<Box<Term>>),
     Cut,
     Display(Vec<Box<Term>>),
-    DuplicateTerm(Vec<Box<Term>>),
+    DuplicateTerm(Vec<Box<Term>>),    
     Functor(Vec<Box<Term>>),
+    Ground(Vec<Box<Term>>),
     Inlined(InlinedQueryTerm),
     Is(Vec<Box<Term>>),
     Jump(JumpStub),
@@ -416,8 +417,9 @@ impl QueryTerm {
             &QueryTerm::Catch(_) => 3,
             &QueryTerm::Display(_) => 1,
             &QueryTerm::Throw(_) => 1,
-            &QueryTerm::DuplicateTerm(_) => 2,
+            &QueryTerm::DuplicateTerm(_) => 2,            
             &QueryTerm::Functor(_) => 3,
+            &QueryTerm::Ground(_) => 1,
             &QueryTerm::Inlined(ref term) => term.arity(),
             &QueryTerm::Is(_) => 2,
             &QueryTerm::Jump(ref vars) => vars.len(),
@@ -444,6 +446,7 @@ pub enum ClauseType<'a> {
     Display,
     DuplicateTerm,
     Functor,
+    Ground,
     Is,
     Root(&'a TabledRc<Atom>),
     SetupCallCleanup,
@@ -461,6 +464,7 @@ impl<'a> ClauseType<'a> {
             &ClauseType::Deep(_, _, name, _) => name.as_str(),
             &ClauseType::DuplicateTerm => "duplicate_term",
             &ClauseType::Functor => "functor",
+            &ClauseType::Ground  => "ground",            
             &ClauseType::Is => "is",
             &ClauseType::Root(name) => name.as_str(),
             &ClauseType::SetupCallCleanup => "setup_call_cleanup",
@@ -853,7 +857,7 @@ pub enum ControlInstruction {
     DisplayExecute,
     Deallocate,
     DuplicateTermCall,
-    DuplicateTermExecute,
+    DuplicateTermExecute,    
     Execute(TabledRc<Atom>, usize),
     ExecuteN(usize),
     FunctorCall,
@@ -861,6 +865,8 @@ pub enum ControlInstruction {
     GetCleanerCall,
     GotoCall(usize, usize),    // p, arity.
     GotoExecute(usize, usize), // p, arity.
+    GroundCall,
+    GroundExecute,
     JmpByCall(usize, usize),    // arity, global_offset.
     JmpByExecute(usize, usize),
     IsCall(RegType, ArithmeticTerm),
@@ -883,7 +889,7 @@ impl ControlInstruction {
             &ControlInstruction::DuplicateTermCall => true,
             &ControlInstruction::DuplicateTermExecute => true,
             &ControlInstruction::Execute(_, _)  => true,
-            &ControlInstruction::CallN(_) => true,
+            &ControlInstruction::CallN(_) => true,            
             &ControlInstruction::ExecuteN(_) => true,
             &ControlInstruction::FunctorCall => true,
             &ControlInstruction::FunctorExecute => true,
@@ -892,6 +898,8 @@ impl ControlInstruction {
             &ControlInstruction::GetCleanerCall => true,
             &ControlInstruction::GotoCall(..) => true,
             &ControlInstruction::GotoExecute(..) => true,
+            &ControlInstruction::GroundCall => true,
+            &ControlInstruction::GroundExecute => true,
             &ControlInstruction::Proceed => true,
             &ControlInstruction::IsCall(..) => true,
             &ControlInstruction::IsExecute(..) => true,
@@ -964,7 +972,7 @@ pub type Code = Vec<Line>;
 
 pub type CodeDeque = VecDeque<Line>;
 
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq, Hash)]
 pub enum Addr {
     Con(Constant),
     Lis(usize),
index bd902e95143528bdae401655bea101de135cab96..a0889d58201a80ea1686d5f1b08036db2110e7a5 100644 (file)
@@ -538,7 +538,8 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
          is_nonvar!(temp_v!(1)), // nonvar/1, 380.
          proceed!(),
          restore_cut_policy!(), // restore_cut_policy/0, 382.
-         proceed!()
+         proceed!(),
+         ground_execute!(), // ground/1, 384.
     ]
 }
 
@@ -588,7 +589,8 @@ pub fn build_code_dir(atom_tbl: TabledData<Atom>) -> (Code, CodeDir, OpDir)
     op_dir.insert((tabled_rc!("->", atom_tbl), Fixity::In), (XFY, 1050));
 
     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)
     for arity in 0 .. 63 {
@@ -630,6 +632,8 @@ pub fn build_code_dir(atom_tbl: TabledData<Atom>) -> (Code, CodeDir, OpDir)
     code_dir.insert((tabled_rc!("string", atom_tbl), 1), (PredicateKeyType::BuiltIn, 376));
     code_dir.insert((tabled_rc!("float", atom_tbl), 1), (PredicateKeyType::BuiltIn, 378));
     code_dir.insert((tabled_rc!("nonvar", atom_tbl), 1), (PredicateKeyType::BuiltIn, 380));
+
+    code_dir.insert((tabled_rc!("ground", atom_tbl), 1), (PredicateKeyType::BuiltIn, 384));
     
     (builtin_code, code_dir, op_dir)
 }
index a52cbe6430d7279b3ad859f7f2c95240fb39a8e2..a386e611e94b758e787818ffbafd685576caef9e 100644 (file)
@@ -259,8 +259,10 @@ 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::Ground(_) =>
+                code.push(Line::Control(ControlInstruction::GroundCall)),            
             &QueryTerm::Functor(_) =>
                 code.push(Line::Control(ControlInstruction::FunctorCall)),
             &QueryTerm::Inlined(_) =>
@@ -301,6 +303,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                         *ctrl = ControlInstruction::DisplayExecute,
                     ControlInstruction::DuplicateTermCall =>
                         *ctrl = ControlInstruction::DuplicateTermExecute,
+                    ControlInstruction::GroundCall =>
+                        *ctrl = ControlInstruction::GroundExecute,
                     ControlInstruction::FunctorCall =>
                         *ctrl = ControlInstruction::FunctorExecute,
                     ControlInstruction::JmpByCall(arity, offset) =>
index 57a9a00966368f3b1d1318011e51affefb305758..53f4ddc1540acc5d11b4c0291f6714621df996d3 100644 (file)
@@ -1,6 +1,7 @@
 use prolog::ast::*;
 use prolog::machine::machine_state::MachineState;
 
+use std::collections::HashSet;
 use std::vec::Vec;
 
 pub struct HeapCellPreOrderIterator<'a> {
@@ -30,7 +31,7 @@ impl<'a> HeapCellPreOrderIterator<'a> {
                 self.follow(a.clone())
         }
     }
-    
+
     // called under the assumption that the location at r is about to
     // be visited, and so any follow up states need to be added to
     // state_stack. returns the dereferenced Addr from Ref.
@@ -121,4 +122,61 @@ impl MachineState {
     pub fn post_order_iter<'a>(&'a self, a: Addr) -> HeapCellPostOrderIterator<'a> {
         HeapCellPostOrderIterator::new(HeapCellPreOrderIterator::new(self, a))
     }
+
+    pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr) -> HeapCellAcyclicIterator<HeapCellPreOrderIterator<'a>>
+    {
+        HeapCellAcyclicIterator::new(HeapCellPreOrderIterator::new(self, a))
+    }
+}
+
+pub trait MutStackHeapCellIterator {
+    fn stack(&mut self) -> &mut Vec<Addr>;
+}
+
+impl<'a> MutStackHeapCellIterator for HeapCellPreOrderIterator<'a> {
+    fn stack(&mut self) -> &mut Vec<Addr> {
+        &mut self.state_stack
+    }
+}
+
+pub struct HeapCellAcyclicIterator<HeapCellIter> {
+    iter: HeapCellIter,
+    seen: HashSet<Addr>
+}
+
+impl<HeapCellIter: MutStackHeapCellIterator> HeapCellAcyclicIterator<HeapCellIter>
+{
+    pub fn new(mut iter: HeapCellIter) -> Self {
+        let mut seen = HashSet::new();
+
+        if let Some(addr) = iter.stack().last() {
+            seen.insert(addr.clone());
+        }
+
+        HeapCellAcyclicIterator { iter, seen }
+    }
+}
+
+impl<HeapCellIter> Iterator for HeapCellAcyclicIterator<HeapCellIter>
+    where HeapCellIter: Iterator<Item=HeapCellValue>
+                      + MutStackHeapCellIterator
+{
+    type Item = HeapCellValue;
+    
+    fn next(&mut self) -> Option<Self::Item> {
+        while let Some(hcv) = self.iter.next() {
+            if let Some(addr) = self.iter.stack().pop() {
+                if self.seen.contains(&addr) {
+                    continue;
+                } else {
+                    self.iter.stack().push(addr.clone());
+                    self.seen.insert(addr);
+                }
+            }
+
+            return Some(hcv);
+        }
+
+        None
+    }
 }
index c5f26212108580165c3fd6943985ab573d42c72a..dc23a742f768b44590835d4878684bc8953f74fe 100644 (file)
@@ -131,6 +131,10 @@ impl fmt::Display for ControlInstruction {
                 write!(f, "functor_execute"),
             &ControlInstruction::Deallocate =>
                 write!(f, "deallocate"),
+            &ControlInstruction::GroundCall =>
+                write!(f, "ground_call"),
+            &ControlInstruction::GroundExecute =>
+                write!(f, "ground_execute"),
             &ControlInstruction::Execute(ref name, arity) =>
                 write!(f, "execute {}/{}", name, arity),
             &ControlInstruction::GetCleanerCall =>
@@ -212,7 +216,7 @@ impl fmt::Display for BuiltInInstruction {
             &BuiltInInstruction::IsString(r) =>
                 write!(f, "is_string {}", r),
             &BuiltInInstruction::IsInteger(r) =>
-                write!(f, "is_integer {}", r),            
+                write!(f, "is_integer {}", r),
             &BuiltInInstruction::DynamicIs =>
                 write!(f, "call_is"),
             &BuiltInInstruction::IsVar(r) =>
index b309f7ef249443cb6cdae896cb3a5baa7ee734d5..0a5b08c16c2bccdbdc162b7d82ee1d7740c8daf8 100644 (file)
@@ -60,6 +60,10 @@ impl<'a> QueryIterator<'a> {
                 let state = TermIterState::Clause(0, ClauseType::Display, terms);
                 QueryIterator { state_stack: vec![state] }
             },
+            &QueryTerm::Ground(ref terms) => {
+                let state = TermIterState::Clause(0, ClauseType::Ground, terms);
+                QueryIterator { state_stack: vec![state] }
+            },
             &QueryTerm::Inlined(InlinedQueryTerm::CompareNumber(qt, ref terms)) => {                
                 let state = TermIterState::Clause(0, ClauseType::CompareNumber(qt), terms);
                 QueryIterator { state_stack: vec![state] }            
@@ -317,7 +321,7 @@ impl<'a> ChunkedIterator<'a>
                     arity = child_terms.len();
                     break;
                 },
-                &QueryTerm::Display(_) => {
+                &QueryTerm::Ground(_) | &QueryTerm::Display(_) => {
                     result.push(term);
                     arity = 1;
                     break;
@@ -341,7 +345,7 @@ impl<'a> ChunkedIterator<'a>
                     if self.term_loc.chunk_num() > 0 {
                         self.deep_cut_encountered = true;
                     }
-                },
+                },                
             };
 
             item = self.iter.next();
index 2c584b6385df70ec28368144c503ab213cb73434..08872cf25bbc10256ff156933069b7957fedf27a 100644 (file)
@@ -1461,6 +1461,56 @@ 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);
+
+        for (v1, v2) in iter1.zip(iter2) {
+            match (v1, v2) {
+                (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) =>
+                    if ar1 != ar2 || *n1 != *n2 {
+                        return true;
+                    },
+                (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Lis(_))) =>
+                    continue,
+                (HeapCellValue::Addr(a1), HeapCellValue::Addr(a2)) =>
+                    if a1 != a2 {
+                        return true;
+                    },
+                _ => {
+                    return true;
+                }
+            }
+        }
+
+        false
+    }
+    */
+
+    // returns true on failure.
+    fn ground_test(&self) -> bool
+    {
+        let a = self.store(self.deref(self[temp_v!(1)].clone()));
+
+        for v in self.acyclic_pre_order_iter(a) {
+            match v {
+                HeapCellValue::Addr(Addr::HeapCell(..)) =>
+                    return true,
+                HeapCellValue::Addr(Addr::StackCell(..)) =>
+                    return true,
+                _ => {}
+            }
+        };
+
+        false
+    }
+
     pub(super) fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, cut_policy: &mut Box<CutPolicy>,
                                      instr: &ControlInstruction)
     {
@@ -1570,6 +1620,14 @@ SetupCallCleanupCutPolicy.")
                 self.duplicate_term();
                 self.p = self.cp;
             },
+            &ControlInstruction::GroundCall => {
+                self.fail = self.ground_test();
+                self.p += 1;
+            },
+            &ControlInstruction::GroundExecute => {
+                self.fail = self.ground_test();
+                self.p = self.cp;
+            },
             &ControlInstruction::Execute(ref name, arity) =>
                 self.try_execute_predicate(code_dir, name.clone(), arity),
             &ControlInstruction::ExecuteN(arity) =>
index a0859e994f6415d8da29cb658827b99dd5847c9f..a201d353d9ee295f8780be3e03387b78e73dc3f9 100644 (file)
@@ -562,3 +562,9 @@ macro_rules! restore_cut_policy {
         Line::BuiltIn(BuiltInInstruction::RestoreCutPolicy)
     )
 }
+
+macro_rules! ground_execute {
+    () => (
+        Line::Control(ControlInstruction::GroundExecute)
+    )
+}
index 7687a367636a79972788b2633de8fbba7778eb10..5de18ac4728701492cc19fd08d32b40f4d5918a3 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 7687a367636a79972788b2633de8fbba7778eb10
+Subproject commit 5de18ac4728701492cc19fd08d32b40f4d5918a3
index 64c7573f0e263a7eee05ad5c0e322f5fb516f259..07ddd886c436f2b1e9a3bf955053c39179d05f0f 100644 (file)
@@ -1328,6 +1328,12 @@ fn test_queries_on_builtins()
     assert_prolog_success!(&mut wam, "?- nonvar(\"sdfsa\").");
     assert_prolog_success!(&mut wam, "?- nonvar(atom).");
     assert_prolog_success!(&mut wam, "?- nonvar([1,2,3]).");
+
+    assert_prolog_success!(&mut wam, "?- A = f(A), ground(f(f(A))), ground(f(A)), ground(A).");
+    assert_prolog_failure!(&mut wam, "?- B = f(A), ground(B).");
+    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])).");
 }
 
 #[test]