* `catch/3`
* `compare/3`
* `compound/1`
+* `cyclic_term/1`
* `display/1`
* `duplicate_term/2`
* `false/0`
}
pub type OpDirKey = (ClauseName, Fixity);
+
// name and fixity -> operator type and precedence.
pub type OpDir = HashMap<OpDirKey, (Specifier, usize, ClauseName)>;
pub type CodeDir = HashMap<PredicateKey, (usize, ClauseName)>;
+pub type TermDir = HashMap<PredicateKey, Predicate>;
+
pub type PredicateKey = (ClauseName, usize); // name, arity.
pub struct ModuleDecl {
&TopLevel::Declaration(_) => None,
&TopLevel::Fact(ref term) => term.name(),
&TopLevel::Predicate(ref clauses) =>
- if let Some(ref term) = clauses.first() {
- term.name()
- } else {
- None
- },
+ clauses.first().and_then(|ref term| term.name()),
&TopLevel::Query(_) => None,
&TopLevel::Rule(Rule { ref head, .. }) =>
Some(head.0.clone())
&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)]
Catch,
Compare,
CompareTerm(CompareTermQT),
+ CyclicTerm,
Display,
DuplicateTerm,
Eq,
pub fn name(&self) -> ClauseName {
match self {
&ClauseType::AcyclicTerm => clause_name!("acyclic_term"),
- &ClauseType::Arg => clause_name!("arg"),
+ &ClauseType::Arg => clause_name!("arg"),
&ClauseType::CallN => clause_name!("call"),
&ClauseType::CallWithInferenceLimit => clause_name!("call_with_inference_limit"),
&ClauseType::Catch => clause_name!("catch"),
&ClauseType::Compare => clause_name!("compare"),
&ClauseType::CompareTerm(qt) => clause_name!(qt.name()),
+ &ClauseType::CyclicTerm => clause_name!("cyclic_term"),
&ClauseType::Display => clause_name!("display"),
&ClauseType::DuplicateTerm => clause_name!("duplicate_term"),
&ClauseType::Eq => clause_name!("=="),
("call_with_inference_limit", 3) => ClauseType::CallWithInferenceLimit,
("catch", 3) => ClauseType::Catch,
("compare", 3) => ClauseType::Compare,
+ ("cyclic_term", 1) => ClauseType::CyclicTerm,
("@>", 2) => ClauseType::CompareTerm(CompareTermQT::GreaterThan),
("@<", 2) => ClauseType::CompareTerm(CompareTermQT::LessThan),
("@>=", 2) => ClauseType::CompareTerm(CompareTermQT::GreaterThanOrEqual),
CompareNumber(CompareNumberQT, ArithmeticTerm, ArithmeticTerm),
DefaultRetryMeElse(usize),
DefaultSetCutPoint(RegType),
- DefaultTrustMe,
+ DefaultTrustMe,
EraseBall,
Fail,
GetArg(bool), // last call.
InstallCleaner,
InstallInferenceCounter(RegType, RegType, RegType),
InstallNewBlock,
- InternalCallN,
+ InternalCallN,
RemoveCallPolicyCheck,
RemoveInferenceCounter(RegType, RegType),
ResetBlock,
sort_execute!(), // sort/2, 467.
keysort_execute!(), // keysort/2, 468.
acyclic_term_execute!(), // acyclic_term/1, 469.
+ cyclic_term_execute!(), // cyclic_term/1, 470.
]
}
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!("acyclic_term"), 1), (469, builtin.clone()));
+ code_dir.insert((clause_name!("cyclic_term"), 1), (470, builtin.clone()));
(code_dir, op_dir)
}
(clause_name!("atom"), 1),
(clause_name!("sort"), 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 {
module_decl.exports.push((clause_name!("call"), arity));
use prolog::targets::*;
use std::cell::Cell;
-use std::collections::HashMap;
+use std::collections::{HashMap};
use std::rc::Rc;
use std::vec::Vec;
if !code.is_empty() {
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 {
EvalSession::from(EvalError::NamelessEntry)
}
use prolog::and_stack::*;
use prolog::ast::*;
use prolog::copier::*;
-use prolog::heap_iter::*;
use prolog::num::{BigInt, BigUint, Zero, One};
use prolog::or_stack::*;
use prolog::heap_print::*;
use downcast::Any;
use std::cmp::Ordering;
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
use std::mem::swap;
use std::ops::{Index, IndexMut};
use std::rc::Rc;
{
match ct {
&ClauseType::AcyclicTerm => {
- let addr = machine_st[temp_v!(1)].clone();
- let mut seen = HashSet::new();
- 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;
+ let addr = machine_st[temp_v!(1)].clone();
+ machine_st.fail = machine_st.is_cyclic_term(addr);
return_from_clause!(lco, machine_st)
},
&ClauseType::Arg => {
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 => {
let output = machine_st.print_term(machine_st[temp_v!(1)].clone(),
DisplayFormatter {},
use prolog::tabled_rc::*;
use std::cmp::{max, Ordering};
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use std::rc::Rc;
macro_rules! try_or_fail {
}
}
+ 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>>
{
let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
call_policy: Box<CallPolicy>,
cut_policy: Box<CutPolicy>,
code: Code,
- code_dir: CodeDir,
+ code_dir: CodeDir,
pub op_dir: OpDir,
+ term_dir: TermDir,
modules: HashMap<ClauseName, Module>,
cached_query: Option<Code>
}
cut_policy: Box::new(DefaultCutPolicy {}),
code,
code_dir,
+ term_dir: TermDir::new(),
op_dir,
modules: HashMap::new(),
cached_query: None
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)) {
Some(&(_, ref mod_name)) if mod_name == &clause_name!("builtin") =>
let offset = self.code.len();
self.code.extend(code.into_iter());
+ self.term_dir.insert((name.clone(), arity), pred);
self.code_dir.insert((name, arity), (offset, clause_name!("user")));
-
+
EvalSession::EntrySuccess
}
)
}
+macro_rules! cyclic_term_execute {
+ () => (
+ Line::Control(ControlInstruction::CallClause(ClauseType::CyclicTerm, 1, 0, true))
+ )
+}
+
macro_rules! return_from_clause {
($lco:expr, $machine_st:expr) => {{
if $lco {