From: Mark Thom Date: Thu, 29 Mar 2018 05:41:19 +0000 (-0600) Subject: add cyclic_term X-Git-Tag: v0.8.110~506 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=7b160199fd304a6c5866d90803cb640117714d04;p=scryer-prolog.git add cyclic_term --- diff --git a/README.md b/README.md index d723b8e5..3c74cb18 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ The following predicates are built-in to rusty-wam. * `catch/3` * `compare/3` * `compound/1` +* `cyclic_term/1` * `display/1` * `duplicate_term/2` * `false/0` diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index a43fb454..89857418 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -132,11 +132,14 @@ impl PredicateClause { } pub type OpDirKey = (ClauseName, Fixity); + // name and fixity -> operator type and precedence. pub type OpDir = HashMap; pub type CodeDir = HashMap; +pub type TermDir = HashMap; + pub type PredicateKey = (ClauseName, usize); // name, arity. pub struct ModuleDecl { @@ -249,11 +252,7 @@ impl TopLevel { &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()) @@ -270,6 +269,15 @@ impl TopLevel { &TopLevel::Rule(Rule { ref head, .. }) => head.1.len() } } + + pub fn as_predicate(self) -> Option { + 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)] @@ -680,6 +688,7 @@ pub enum ClauseType { Catch, Compare, CompareTerm(CompareTermQT), + CyclicTerm, Display, DuplicateTerm, Eq, @@ -775,12 +784,13 @@ impl ClauseType { 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!("=="), @@ -806,6 +816,7 @@ impl ClauseType { ("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), @@ -1200,7 +1211,7 @@ pub enum BuiltInInstruction { CompareNumber(CompareNumberQT, ArithmeticTerm, ArithmeticTerm), DefaultRetryMeElse(usize), DefaultSetCutPoint(RegType), - DefaultTrustMe, + DefaultTrustMe, EraseBall, Fail, GetArg(bool), // last call. @@ -1211,7 +1222,7 @@ pub enum BuiltInInstruction { InstallCleaner, InstallInferenceCounter(RegType, RegType, RegType), InstallNewBlock, - InternalCallN, + InternalCallN, RemoveCallPolicyCheck, RemoveInferenceCounter(RegType, RegType), ResetBlock, diff --git a/src/prolog/builtins.rs b/src/prolog/builtins.rs index 6d3e678c..88c5a5b3 100644 --- a/src/prolog/builtins.rs +++ b/src/prolog/builtins.rs @@ -626,6 +626,7 @@ fn get_builtins() -> Code { sort_execute!(), // sort/2, 467. keysort_execute!(), // keysort/2, 468. 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!("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) } @@ -804,7 +806,8 @@ pub fn builtin_module() -> Module (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)); diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index 84a9ac6a..e10d9fd9 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -7,7 +7,7 @@ use prolog::iterators::*; use prolog::targets::*; use std::cell::Cell; -use std::collections::HashMap; +use std::collections::{HashMap}; use std::rc::Rc; use std::vec::Vec; diff --git a/src/prolog/io.rs b/src/prolog/io.rs index 66c6cf13..76e1b8f1 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -496,7 +496,7 @@ fn compile_decl(wam: &mut Machine, tl: TopLevel, queue: Vec) -> EvalSe 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) } diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 1a31351f..a0893580 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -1,7 +1,6 @@ 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::*; @@ -10,7 +9,7 @@ use prolog::tabled_rc::*; 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; @@ -397,30 +396,8 @@ pub(crate) trait CallPolicy: Any { { 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 => { @@ -476,6 +453,11 @@ pub(crate) trait CallPolicy: Any { 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 {}, diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs index 73ae9e15..5b97e455 100644 --- a/src/prolog/machine/machine_state_impl.rs +++ b/src/prolog/machine/machine_state_impl.rs @@ -11,7 +11,7 @@ use prolog::or_stack::*; 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 { @@ -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> { let a1 = self.store(self.deref(self[temp_v!(1)].clone())); diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index c94b8a06..6e6bc1ec 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -25,8 +25,9 @@ pub struct Machine { call_policy: Box, cut_policy: Box, code: Code, - code_dir: CodeDir, + code_dir: CodeDir, pub op_dir: OpDir, + term_dir: TermDir, modules: HashMap, cached_query: Option } @@ -68,6 +69,7 @@ impl Machine { cut_policy: Box::new(DefaultCutPolicy {}), code, code_dir, + term_dir: TermDir::new(), op_dir, modules: HashMap::new(), cached_query: None @@ -173,7 +175,8 @@ impl Machine { 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") => @@ -184,8 +187,9 @@ impl Machine { 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 } diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 90ec275a..85a25760 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -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 { ($lco:expr, $machine_st:expr) => {{ if $lco {