add cyclic_term

This commit is contained in:
Mark Thom
2018-03-28 23:41:19 -06:00
parent ef65c996ec
commit 7b160199fd
9 changed files with 72 additions and 41 deletions

View File

@@ -134,6 +134,7 @@ The following predicates are built-in to rusty-wam.
* `catch/3` * `catch/3`
* `compare/3` * `compare/3`
* `compound/1` * `compound/1`
* `cyclic_term/1`
* `display/1` * `display/1`
* `duplicate_term/2` * `duplicate_term/2`
* `false/0` * `false/0`

View File

@@ -132,11 +132,14 @@ impl PredicateClause {
} }
pub type OpDirKey = (ClauseName, Fixity); pub type OpDirKey = (ClauseName, Fixity);
// name and fixity -> operator type and precedence. // name and fixity -> operator type and precedence.
pub type OpDir = HashMap<OpDirKey, (Specifier, usize, ClauseName)>; pub type OpDir = HashMap<OpDirKey, (Specifier, usize, ClauseName)>;
pub type CodeDir = HashMap<PredicateKey, (usize, ClauseName)>; pub type CodeDir = HashMap<PredicateKey, (usize, ClauseName)>;
pub type TermDir = HashMap<PredicateKey, Predicate>;
pub type PredicateKey = (ClauseName, usize); // name, arity. pub type PredicateKey = (ClauseName, usize); // name, arity.
pub struct ModuleDecl { pub struct ModuleDecl {
@@ -249,11 +252,7 @@ impl TopLevel {
&TopLevel::Declaration(_) => None, &TopLevel::Declaration(_) => None,
&TopLevel::Fact(ref term) => term.name(), &TopLevel::Fact(ref term) => term.name(),
&TopLevel::Predicate(ref clauses) => &TopLevel::Predicate(ref clauses) =>
if let Some(ref term) = clauses.first() { clauses.first().and_then(|ref term| term.name()),
term.name()
} else {
None
},
&TopLevel::Query(_) => None, &TopLevel::Query(_) => None,
&TopLevel::Rule(Rule { ref head, .. }) => &TopLevel::Rule(Rule { ref head, .. }) =>
Some(head.0.clone()) Some(head.0.clone())
@@ -270,6 +269,15 @@ impl TopLevel {
&TopLevel::Rule(Rule { ref head, .. }) => head.1.len() &TopLevel::Rule(Rule { ref head, .. }) => head.1.len()
} }
} }
pub fn as_predicate(self) -> Option<Predicate> {
match self {
TopLevel::Fact(term) => Some(vec![PredicateClause::Fact(term)]),
TopLevel::Rule(rule) => Some(vec![PredicateClause::Rule(rule)]),
TopLevel::Predicate(pred) => Some(pred),
_ => None
}
}
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@@ -680,6 +688,7 @@ pub enum ClauseType {
Catch, Catch,
Compare, Compare,
CompareTerm(CompareTermQT), CompareTerm(CompareTermQT),
CyclicTerm,
Display, Display,
DuplicateTerm, DuplicateTerm,
Eq, Eq,
@@ -781,6 +790,7 @@ impl ClauseType {
&ClauseType::Catch => clause_name!("catch"), &ClauseType::Catch => clause_name!("catch"),
&ClauseType::Compare => clause_name!("compare"), &ClauseType::Compare => clause_name!("compare"),
&ClauseType::CompareTerm(qt) => clause_name!(qt.name()), &ClauseType::CompareTerm(qt) => clause_name!(qt.name()),
&ClauseType::CyclicTerm => clause_name!("cyclic_term"),
&ClauseType::Display => clause_name!("display"), &ClauseType::Display => clause_name!("display"),
&ClauseType::DuplicateTerm => clause_name!("duplicate_term"), &ClauseType::DuplicateTerm => clause_name!("duplicate_term"),
&ClauseType::Eq => clause_name!("=="), &ClauseType::Eq => clause_name!("=="),
@@ -806,6 +816,7 @@ impl ClauseType {
("call_with_inference_limit", 3) => ClauseType::CallWithInferenceLimit, ("call_with_inference_limit", 3) => ClauseType::CallWithInferenceLimit,
("catch", 3) => ClauseType::Catch, ("catch", 3) => ClauseType::Catch,
("compare", 3) => ClauseType::Compare, ("compare", 3) => ClauseType::Compare,
("cyclic_term", 1) => ClauseType::CyclicTerm,
("@>", 2) => ClauseType::CompareTerm(CompareTermQT::GreaterThan), ("@>", 2) => ClauseType::CompareTerm(CompareTermQT::GreaterThan),
("@<", 2) => ClauseType::CompareTerm(CompareTermQT::LessThan), ("@<", 2) => ClauseType::CompareTerm(CompareTermQT::LessThan),
("@>=", 2) => ClauseType::CompareTerm(CompareTermQT::GreaterThanOrEqual), ("@>=", 2) => ClauseType::CompareTerm(CompareTermQT::GreaterThanOrEqual),

View File

@@ -626,6 +626,7 @@ fn get_builtins() -> Code {
sort_execute!(), // sort/2, 467. sort_execute!(), // sort/2, 467.
keysort_execute!(), // keysort/2, 468. keysort_execute!(), // keysort/2, 468.
acyclic_term_execute!(), // acyclic_term/1, 469. acyclic_term_execute!(), // acyclic_term/1, 469.
cyclic_term_execute!(), // cyclic_term/1, 470.
] ]
} }
@@ -742,6 +743,7 @@ pub fn build_code_and_op_dirs() -> (CodeDir, OpDir)
code_dir.insert((clause_name!("sort"), 2), (467, builtin.clone())); code_dir.insert((clause_name!("sort"), 2), (467, builtin.clone()));
code_dir.insert((clause_name!("keysort"), 2), (468, builtin.clone())); code_dir.insert((clause_name!("keysort"), 2), (468, builtin.clone()));
code_dir.insert((clause_name!("acyclic_term"), 1), (469, builtin.clone())); code_dir.insert((clause_name!("acyclic_term"), 1), (469, builtin.clone()));
code_dir.insert((clause_name!("cyclic_term"), 1), (470, builtin.clone()));
(code_dir, op_dir) (code_dir, op_dir)
} }
@@ -804,7 +806,8 @@ pub fn builtin_module() -> Module
(clause_name!("atom"), 1), (clause_name!("atom"), 1),
(clause_name!("sort"), 2), (clause_name!("sort"), 2),
(clause_name!("keysort"), 2), (clause_name!("keysort"), 2),
(clause_name!("acyclic_term"), 1)]); (clause_name!("acyclic_term"), 1),
(clause_name!("cyclic_term"), 1)]);
for arity in 0 .. 63 { for arity in 0 .. 63 {
module_decl.exports.push((clause_name!("call"), arity)); module_decl.exports.push((clause_name!("call"), arity));

View File

@@ -7,7 +7,7 @@ use prolog::iterators::*;
use prolog::targets::*; use prolog::targets::*;
use std::cell::Cell; use std::cell::Cell;
use std::collections::HashMap; use std::collections::{HashMap};
use std::rc::Rc; use std::rc::Rc;
use std::vec::Vec; use std::vec::Vec;

View File

@@ -496,7 +496,7 @@ fn compile_decl(wam: &mut Machine, tl: TopLevel, queue: Vec<TopLevel>) -> EvalSe
if !code.is_empty() { if !code.is_empty() {
if let Some(name) = tl.name() { if let Some(name) = tl.name() {
wam.add_user_code(name, tl.arity(), code) wam.add_user_code(name, tl.arity(), code, tl.as_predicate().unwrap())
} else { } else {
EvalSession::from(EvalError::NamelessEntry) EvalSession::from(EvalError::NamelessEntry)
} }

View File

@@ -1,7 +1,6 @@
use prolog::and_stack::*; use prolog::and_stack::*;
use prolog::ast::*; use prolog::ast::*;
use prolog::copier::*; use prolog::copier::*;
use prolog::heap_iter::*;
use prolog::num::{BigInt, BigUint, Zero, One}; use prolog::num::{BigInt, BigUint, Zero, One};
use prolog::or_stack::*; use prolog::or_stack::*;
use prolog::heap_print::*; use prolog::heap_print::*;
@@ -10,7 +9,7 @@ use prolog::tabled_rc::*;
use downcast::Any; use downcast::Any;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{HashMap, HashSet}; use std::collections::HashMap;
use std::mem::swap; use std::mem::swap;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use std::rc::Rc; use std::rc::Rc;
@@ -398,29 +397,7 @@ pub(crate) trait CallPolicy: Any {
match ct { match ct {
&ClauseType::AcyclicTerm => { &ClauseType::AcyclicTerm => {
let addr = machine_st[temp_v!(1)].clone(); let addr = machine_st[temp_v!(1)].clone();
let mut seen = HashSet::new(); machine_st.fail = machine_st.is_cyclic_term(addr);
let mut fail = false;
{
let mut iter = machine_st.pre_order_iter(addr);
loop {
if let Some(addr) = iter.stack().last() {
if !seen.contains(addr) {
seen.insert(addr.clone());
} else {
fail = true;
break;
}
}
if iter.next().is_none() {
break;
}
}
}
machine_st.fail = fail;
return_from_clause!(lco, machine_st) return_from_clause!(lco, machine_st)
}, },
&ClauseType::Arg => { &ClauseType::Arg => {
@@ -476,6 +453,11 @@ pub(crate) trait CallPolicy: Any {
return_from_clause!(lco, machine_st) return_from_clause!(lco, machine_st)
}, },
&ClauseType::CyclicTerm => {
let addr = machine_st[temp_v!(1)].clone();
machine_st.fail = !machine_st.is_cyclic_term(addr);
return_from_clause!(lco, machine_st)
},
&ClauseType::Display => { &ClauseType::Display => {
let output = machine_st.print_term(machine_st[temp_v!(1)].clone(), let output = machine_st.print_term(machine_st[temp_v!(1)].clone(),
DisplayFormatter {}, DisplayFormatter {},

View File

@@ -11,7 +11,7 @@ use prolog::or_stack::*;
use prolog::tabled_rc::*; use prolog::tabled_rc::*;
use std::cmp::{max, Ordering}; use std::cmp::{max, Ordering};
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::rc::Rc; use std::rc::Rc;
macro_rules! try_or_fail { macro_rules! try_or_fail {
@@ -1001,6 +1001,30 @@ impl MachineState {
} }
} }
pub(super) fn is_cyclic_term(&self, addr: Addr) -> bool {
let mut seen = HashSet::new();
let mut fail = false;
let mut iter = self.pre_order_iter(addr);
loop {
if let Some(addr) = iter.stack().last() {
if !seen.contains(addr) {
seen.insert(addr.clone());
} else {
fail = true;
break;
}
}
if iter.next().is_none() {
break;
}
}
fail
}
fn try_get_arg(&mut self) -> Result<(), Vec<HeapCellValue>> fn try_get_arg(&mut self) -> Result<(), Vec<HeapCellValue>>
{ {
let a1 = self.store(self.deref(self[temp_v!(1)].clone())); let a1 = self.store(self.deref(self[temp_v!(1)].clone()));

View File

@@ -27,6 +27,7 @@ pub struct Machine {
code: Code, code: Code,
code_dir: CodeDir, code_dir: CodeDir,
pub op_dir: OpDir, pub op_dir: OpDir,
term_dir: TermDir,
modules: HashMap<ClauseName, Module>, modules: HashMap<ClauseName, Module>,
cached_query: Option<Code> cached_query: Option<Code>
} }
@@ -68,6 +69,7 @@ impl Machine {
cut_policy: Box::new(DefaultCutPolicy {}), cut_policy: Box::new(DefaultCutPolicy {}),
code, code,
code_dir, code_dir,
term_dir: TermDir::new(),
op_dir, op_dir,
modules: HashMap::new(), modules: HashMap::new(),
cached_query: None cached_query: None
@@ -173,7 +175,8 @@ impl Machine {
self.code.extend(code.into_iter()); self.code.extend(code.into_iter());
} }
pub fn add_user_code(&mut self, name: ClauseName, arity: usize, code: Code) -> EvalSession pub fn add_user_code(&mut self, name: ClauseName, arity: usize, code: Code, pred: Predicate)
-> EvalSession
{ {
match self.code_dir.get(&(name.clone(), arity)) { match self.code_dir.get(&(name.clone(), arity)) {
Some(&(_, ref mod_name)) if mod_name == &clause_name!("builtin") => Some(&(_, ref mod_name)) if mod_name == &clause_name!("builtin") =>
@@ -184,6 +187,7 @@ impl Machine {
let offset = self.code.len(); let offset = self.code.len();
self.code.extend(code.into_iter()); self.code.extend(code.into_iter());
self.term_dir.insert((name.clone(), arity), pred);
self.code_dir.insert((name, arity), (offset, clause_name!("user"))); self.code_dir.insert((name, arity), (offset, clause_name!("user")));
EvalSession::EntrySuccess EvalSession::EntrySuccess

View File

@@ -734,6 +734,12 @@ macro_rules! acyclic_term_execute {
) )
} }
macro_rules! cyclic_term_execute {
() => (
Line::Control(ControlInstruction::CallClause(ClauseType::CyclicTerm, 1, 0, true))
)
}
macro_rules! return_from_clause { macro_rules! return_from_clause {
($lco:expr, $machine_st:expr) => {{ ($lco:expr, $machine_st:expr) => {{
if $lco { if $lco {