From: Mark Thom Date: Fri, 9 Feb 2018 06:51:05 +0000 (-0700) Subject: add ground/1. X-Git-Tag: v0.8.110~575 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=9c1b48865bc87931ea04939fa406ebcc85f0529a;p=scryer-prolog.git add ground/1. --- diff --git a/README.md b/README.md index 1fbeb7ca..17499cce 100644 --- 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` diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index 24223854..8c419f93 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -399,8 +399,9 @@ pub enum QueryTerm { Catch(Vec>), Cut, Display(Vec>), - DuplicateTerm(Vec>), + DuplicateTerm(Vec>), Functor(Vec>), + Ground(Vec>), Inlined(InlinedQueryTerm), Is(Vec>), 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), 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, 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; pub type CodeDeque = VecDeque; -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Eq, Hash)] pub enum Addr { Con(Constant), Lis(usize), diff --git a/src/prolog/builtins.rs b/src/prolog/builtins.rs index bd902e95..a0889d58 100644 --- a/src/prolog/builtins.rs +++ b/src/prolog/builtins.rs @@ -538,7 +538,8 @@ fn get_builtins(atom_tbl: TabledData) -> 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) -> (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) -> (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) } diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index a52cbe64..a386e611 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -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) => diff --git a/src/prolog/heap_iter.rs b/src/prolog/heap_iter.rs index 57a9a009..53f4ddc1 100644 --- a/src/prolog/heap_iter.rs +++ b/src/prolog/heap_iter.rs @@ -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> + { + HeapCellAcyclicIterator::new(HeapCellPreOrderIterator::new(self, a)) + } +} + +pub trait MutStackHeapCellIterator { + fn stack(&mut self) -> &mut Vec; +} + +impl<'a> MutStackHeapCellIterator for HeapCellPreOrderIterator<'a> { + fn stack(&mut self) -> &mut Vec { + &mut self.state_stack + } +} + +pub struct HeapCellAcyclicIterator { + iter: HeapCellIter, + seen: HashSet +} + +impl HeapCellAcyclicIterator +{ + 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 Iterator for HeapCellAcyclicIterator + where HeapCellIter: Iterator + + MutStackHeapCellIterator +{ + type Item = HeapCellValue; + + fn next(&mut self) -> Option { + 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 + } } diff --git a/src/prolog/io.rs b/src/prolog/io.rs index c5f26212..dc23a742 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -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) => diff --git a/src/prolog/iterators.rs b/src/prolog/iterators.rs index b309f7ef..0a5b08c1 100644 --- a/src/prolog/iterators.rs +++ b/src/prolog/iterators.rs @@ -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(); diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs index 2c584b63..08872cf2 100644 --- a/src/prolog/machine/machine_state_impl.rs +++ b/src/prolog/machine/machine_state_impl.rs @@ -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, 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) => diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index a0859e99..a201d353 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -562,3 +562,9 @@ macro_rules! restore_cut_policy { Line::BuiltIn(BuiltInInstruction::RestoreCutPolicy) ) } + +macro_rules! ground_execute { + () => ( + Line::Control(ControlInstruction::GroundExecute) + ) +} diff --git a/src/prolog/parser b/src/prolog/parser index 7687a367..5de18ac4 160000 --- a/src/prolog/parser +++ b/src/prolog/parser @@ -1 +1 @@ -Subproject commit 7687a367636a79972788b2633de8fbba7778eb10 +Subproject commit 5de18ac4728701492cc19fd08d32b40f4d5918a3 diff --git a/src/tests.rs b/src/tests.rs index 64c7573f..07ddd886 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -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]