From: Mark Thom Date: Fri, 1 Mar 2019 04:49:07 +0000 (-0700) Subject: start enabling the dynamic database X-Git-Tag: v0.8.110~223 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=818a9718335a2de29a9309fe6cab227f786cd5ff;p=scryer-prolog.git start enabling the dynamic database --- diff --git a/src/prolog/compile.rs b/src/prolog/compile.rs index f318bb39..727f70d5 100644 --- a/src/prolog/compile.rs +++ b/src/prolog/compile.rs @@ -5,9 +5,10 @@ use prolog::instructions::*; use prolog::debray_allocator::*; use prolog::codegen::*; use prolog::machine::*; -use prolog::machine::term_expansion::{ExpansionAdditionResult, TermStream}; +use prolog::machine::term_expansion::{ExpansionAdditionResult}; use prolog::toplevel::*; +use std::cell::Cell; use std::collections::{HashMap, HashSet, VecDeque}; use std::io::Read; use std::mem; @@ -36,7 +37,7 @@ fn print_code(code: &Code) { } } -type PredicateCompileQueue = (Predicate, VecDeque); +pub type PredicateCompileQueue = (Predicate, VecDeque); // throw errors if declaration or query found. fn compile_relation(tl: &TopLevel, non_counted_bt: bool, flags: MachineFlags) @@ -153,8 +154,9 @@ pub fn compile_term(wam: &mut Machine, packet: TopLevelPacket) -> EvalSession } } -struct GatherResult { - worker_results: Vec, +pub struct GatherResult { + dynamic_clause_map: DynamicClauseMap, + pub(crate) worker_results: Vec, toplevel_results: Vec, toplevel_indices: IndexStore, addition_results: ExpansionAdditionResult @@ -180,6 +182,32 @@ impl ListingCompiler { } } + /* + Replace calls to self with a localized index cell, not available to the global CodeIndex. + This is done to implement logical update semantics for dynamic database updates. + */ + fn localize_self_calls(&mut self, name: ClauseName, arity: usize, code: &mut Code, p: usize) + { + let self_idx = CodeIndex::default(); + set_code_index!(self_idx, IndexPtr::Index(p), self.get_module_name()); + + for instr in code.iter_mut() { + if let &mut Line::Control(ControlInstruction::CallClause(ref mut ct, ..)) = instr { + match ct { + &mut ClauseType::Named(ref ct_name, ct_arity, ref mut idx) + if ct_name == &name && arity == ct_arity => { + *idx = self_idx.clone(); + }, + &mut ClauseType::Op(ref op_decl, ref mut idx) + if op_decl.name() == name && op_decl.arity() == arity => { + *idx = self_idx.clone(); + }, + _ => {} + } + } + } + } + fn use_module(&mut self, submodule: ClauseName, code_repo: &mut CodeRepo, flags: MachineFlags, wam_indices: &mut IndexStore, indices: &mut IndexStore) @@ -235,6 +263,49 @@ impl ListingCompiler { .unwrap_or(ClauseName::BuiltIn("user")) } + fn add_clause_code(&mut self, dynamic_clause_map: DynamicClauseMap, wam: &mut Machine) + -> Result<(), SessionError> + { + let mut code = vec![]; + let mut pi_to_loc = HashMap::new(); + + // the name of this function is misleading. first we generate the clause code.. + for ((name, arity), heads_and_tails) in dynamic_clause_map { + if heads_and_tails.is_empty() { + continue; + } + + let predicate = Predicate(heads_and_tails.into_iter().map(|(head, tail)| { + let clause = Term::Clause(Cell::default(), clause_name!("clause"), + vec![Box::new(head), Box::new(tail)], + None); + PredicateClause::Fact(clause) + }).collect()); + + let p = code.len() + wam.code_size(); + let mut decl_code = compile_relation(&TopLevel::Predicate(predicate), false, + wam.machine_flags())?; + + compile_appendix(&mut decl_code, &VecDeque::new(), false, wam.machine_flags())?; + + pi_to_loc.insert((name, arity), p); + code.extend(decl_code.into_iter()); + } + + // now that we've reached this point without error, we are free to add to the WAM. + wam.code_repo.code.extend(code.into_iter()); + + // ... then we add it to the wam. + for ((name, arity), p) in pi_to_loc { + let entry = wam.indices.dynamic_code_dir.entry((name, arity)) + .or_insert(DynamicPredicateInfo::default()); + entry.clauses_subsection_p = p; + } + + Ok(()) + } + + pub(crate) fn generate_code(&mut self, decls: Vec, wam: &Machine, code_dir: &mut CodeDir) -> Result @@ -242,11 +313,7 @@ impl ListingCompiler { let mut code = vec![]; for (decl, queue) in decls { - let (name, arity) = decl.0.first().and_then(|cl| { - let arity = cl.arity(); - cl.name().map(|name| (name, arity)) - }).ok_or(SessionError::NamelessEntry)?; - + let (name, arity) = decl.predicate_indicator().ok_or(SessionError::NamelessEntry)?; let non_counted_bt = self.non_counted_bt_preds.contains(&(name.clone(), arity)); let p = code.len() + wam.code_size(); @@ -257,7 +324,8 @@ impl ListingCompiler { let idx = code_dir.entry((name.clone(), arity)).or_insert(CodeIndex::default()); set_code_index!(idx, IndexPtr::Index(p), self.get_module_name()); - + + self.localize_self_calls(name, arity, &mut decl_code, p); code.extend(decl_code.into_iter()); } @@ -354,26 +422,32 @@ impl ListingCompiler { Ok(self.module = Some(Module::new(module_decl, atom_tbl))) } else { Err(SessionError::from(ParserError::InvalidModuleDecl)) - } + }, + Declaration::Dynamic(..) => Ok(()) } } fn process_and_commit_decl<'a, R: Read>(&mut self, decl: Declaration, - term_stream: &mut TermStream<'a, R>, + worker: &mut TopLevelBatchWorker<'a, R>, indices: &mut IndexStore, flags: MachineFlags) -> Result<(), SessionError> { match &decl { + &Declaration::Dynamic(ref name, arity) => { + worker.dynamic_clause_map.entry((name.clone(), arity)).or_insert(vec![]); + }, &Declaration::Hook(hook, _, ref queue) if self.module.is_none() => - term_stream.incr_expansion_lens(hook.user_scope(), 1, queue.len()), + worker.term_stream.incr_expansion_lens(hook.user_scope(), 1, queue.len()), &Declaration::Hook(hook, _, ref queue) if !hook.has_module_scope() => - term_stream.incr_expansion_lens(hook, 1, queue.len()), + worker.term_stream.incr_expansion_lens(hook, 1, queue.len()), _ => {} }; - self.process_decl(decl, term_stream.code_repo, term_stream.indices, indices, flags) + self.process_decl(decl, &mut worker.term_stream.code_repo, + &mut worker.term_stream.indices, indices, flags) } + pub(crate) fn gather_items(&mut self, wam: &mut Machine, src: R, indices: &mut IndexStore) -> Result { @@ -392,15 +466,13 @@ impl ListingCompiler { mem::swap(&mut worker.results, &mut toplevel_results); worker.in_module = true; - self.process_and_commit_decl(decl, &mut worker.term_stream, - indices, flags)?; + self.process_and_commit_decl(decl, &mut worker, indices, flags)?; if let &Some(ref module) = &self.module { worker.term_stream.set_atom_tbl(module.atom_tbl.clone()); } } else { - self.process_and_commit_decl(decl, &mut worker.term_stream, - indices, flags)?; + self.process_and_commit_decl(decl, &mut worker, indices, flags)?; } } @@ -408,25 +480,23 @@ impl ListingCompiler { Ok(GatherResult { worker_results: worker.results, + dynamic_clause_map: worker.dynamic_clause_map, toplevel_results, toplevel_indices, addition_results }) } - fn drop_expansions(&self, wam: &mut Machine, err: SessionError) -> SessionError { + fn drop_expansions(&self, flags: MachineFlags, code_repo: &mut CodeRepo) + { let (te_len, te_queue_len) = self.orig_term_expansion_lens; let (ge_len, ge_queue_len) = self.orig_goal_expansion_lens; - let flags = wam.machine_flags(); - - wam.code_repo.truncate_terms((clause_name!("term_expansion"), 2), te_len, te_queue_len); - wam.code_repo.truncate_terms((clause_name!("goal_expansion"), 2), ge_len, ge_queue_len); - - discard_result!(wam.code_repo.compile_hook(CompileTimeHook::UserGoalExpansion, flags)); - discard_result!(wam.code_repo.compile_hook(CompileTimeHook::UserTermExpansion, flags)); + code_repo.truncate_terms((clause_name!("term_expansion"), 2), te_len, te_queue_len); + code_repo.truncate_terms((clause_name!("goal_expansion"), 2), ge_len, ge_queue_len); - err + discard_result!(code_repo.compile_hook(CompileTimeHook::UserGoalExpansion, flags)); + discard_result!(code_repo.compile_hook(CompileTimeHook::UserTermExpansion, flags)); } } @@ -434,7 +504,7 @@ fn compile_work(compiler: &mut ListingCompiler, wam: &mut Machine, src: mut indices: IndexStore) -> EvalSession { - let mut results = try_eval_session!(compiler.gather_items(wam, src, &mut indices)); + let mut results = try_eval_session!(compiler.gather_items(wam, src, &mut indices)); let module_code = try_eval_session!(compiler.generate_code(results.worker_results, wam, &mut indices.code_dir)); @@ -446,14 +516,16 @@ fn compile_work(compiler: &mut ListingCompiler, wam: &mut Machine, src: module.goal_expansions = results.addition_results.take_goal_expansions(); } - try_eval_session!(compiler.add_code(wam, module_code, indices)); - try_eval_session!(compiler.add_code(wam, toplvl_code, results.toplevel_indices)); - let flags = wam.machine_flags(); try_eval_session!(wam.code_repo.compile_hook(CompileTimeHook::UserTermExpansion, flags)); try_eval_session!(wam.code_repo.compile_hook(CompileTimeHook::UserGoalExpansion, flags)); + try_eval_session!(compiler.add_code(wam, module_code, indices)); + try_eval_session!(compiler.add_code(wam, toplvl_code, results.toplevel_indices)); + + try_eval_session!(compiler.add_clause_code(results.dynamic_clause_map, wam)); + EvalSession::EntrySuccess } @@ -477,7 +549,10 @@ pub fn compile_listing(wam: &mut Machine, src: R, indices: IndexStore) let mut compiler = ListingCompiler::new(&wam.code_repo); match compile_work(&mut compiler, wam, src, indices) { - EvalSession::Error(e) => EvalSession::Error(compiler.drop_expansions(wam, e)), + EvalSession::Error(e) => { + compiler.drop_expansions(wam.machine_flags(), &mut wam.code_repo); + EvalSession::Error(e) + }, result => result } } diff --git a/src/prolog/copier.rs b/src/prolog/copier.rs index 96e4ce5e..d3db0fef 100644 --- a/src/prolog/copier.rs +++ b/src/prolog/copier.rs @@ -175,10 +175,12 @@ impl CopyTermState { HeapCellValue::NamedStr(..) => self.scan += 1, HeapCellValue::Addr(addr) => - match addr.clone() { + match addr { Addr::Lis(addr) => self.copy_list(addr), - Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) => + addr @ Addr::AttrVar(_) + | addr @ Addr::HeapCell(_) + | addr @ Addr::StackCell(..) => self.copy_var(addr), Addr::Str(addr) => self.copy_structure(addr), diff --git a/src/prolog/heap_print.rs b/src/prolog/heap_print.rs index e379e9d1..0b2ac0f9 100644 --- a/src/prolog/heap_print.rs +++ b/src/prolog/heap_print.rs @@ -129,7 +129,7 @@ impl HCValueOutputter for PrinterOutputter { #[inline] fn is_numbered_var(ct: &ClauseType, arity: usize) -> bool { - arity == 1 && if let &ClauseType::Named(ref name, _) = ct { + arity == 1 && if let &ClauseType::Named(ref name, ..) = ct { name.as_str() == "$VAR" } else { false diff --git a/src/prolog/instructions.rs b/src/prolog/instructions.rs index 1f992dcc..cdd19001 100644 --- a/src/prolog/instructions.rs +++ b/src/prolog/instructions.rs @@ -162,13 +162,21 @@ pub struct Rule { pub struct Predicate(pub Vec); impl Predicate { + #[inline] pub fn new() -> Self { Predicate(vec![]) } + #[inline] pub fn clauses(self) -> Vec { self.0 } + + #[inline] + pub fn predicate_indicator(&self) -> Option<(ClauseName, usize)> { + self.0.first() + .and_then(|clause| clause.name().map(|name| (name, clause.arity()))) + } } #[derive(Clone)] @@ -252,13 +260,16 @@ pub enum SystemClauseType { GetAttrVarQueueDelimiter, GetAttrVarQueueBeyond, GetBValue, + GetClause, GetLiftedHeapFromOffset, GetLiftedHeapFromOffsetDiff, GetSCCCleaner, + HeadIsDynamic, InstallSCCCleaner, InstallInferenceCounter, LiftedHeapLength, ModuleOf, + NoSuchPredicate, RedoAttrVarBindings, RemoveCallPolicyCheck, RemoveInferenceCounter, @@ -307,12 +318,15 @@ impl SystemClauseType { &SystemClauseType::GetLiftedHeapFromOffset => clause_name!("$get_lh_from_offset"), &SystemClauseType::GetLiftedHeapFromOffsetDiff => clause_name!("$get_lh_from_offset_diff"), &SystemClauseType::GetBValue => clause_name!("$get_b_value"), + &SystemClauseType::GetClause => clause_name!("$get_clause"), &SystemClauseType::GetDoubleQuotes => clause_name!("$get_double_quotes"), &SystemClauseType::GetSCCCleaner => clause_name!("$get_scc_cleaner"), + &SystemClauseType::HeadIsDynamic => clause_name!("$head_is_dynamic"), &SystemClauseType::InstallSCCCleaner => clause_name!("$install_scc_cleaner"), &SystemClauseType::InstallInferenceCounter => clause_name!("$install_inference_counter"), &SystemClauseType::LiftedHeapLength => clause_name!("$lh_length"), &SystemClauseType::ModuleOf => clause_name!("$module_of"), + &SystemClauseType::NoSuchPredicate => clause_name!("$no_such_predicate"), &SystemClauseType::RedoAttrVarBindings => clause_name!("$redo_attr_var_bindings"), &SystemClauseType::RemoveCallPolicyCheck => clause_name!("$remove_call_policy_check"), &SystemClauseType::RemoveInferenceCounter => clause_name!("$remove_inference_counter"), @@ -356,14 +370,17 @@ impl SystemClauseType { ("$truncate_if_no_lh_growth_diff", 2) => Some(SystemClauseType::TruncateIfNoLiftedHeapGrowthDiff), ("$get_attr_list", 2) => Some(SystemClauseType::GetAttributedVariableList), ("$get_b_value", 1) => Some(SystemClauseType::GetBValue), + ("$get_clause", 2) => Some(SystemClauseType::GetClause), ("$get_lh_from_offset", 2) => Some(SystemClauseType::GetLiftedHeapFromOffset), ("$get_lh_from_offset_diff", 3) => Some(SystemClauseType::GetLiftedHeapFromOffsetDiff), ("$get_double_quotes", 1) => Some(SystemClauseType::GetDoubleQuotes), ("$get_scc_cleaner", 1) => Some(SystemClauseType::GetSCCCleaner), + ("$head_is_dynamic", 1) => Some(SystemClauseType::HeadIsDynamic), ("$install_scc_cleaner", 2) => Some(SystemClauseType::InstallSCCCleaner), ("$install_inference_counter", 3) => Some(SystemClauseType::InstallInferenceCounter), ("$lh_length", 1) => Some(SystemClauseType::LiftedHeapLength), ("$module_of", 2) => Some(SystemClauseType::ModuleOf), + ("$no_such_predicate", 1) => Some(SystemClauseType::NoSuchPredicate), ("$redo_attr_var_bindings", 0) => Some(SystemClauseType::RedoAttrVarBindings), ("$remove_call_policy_check", 1) => Some(SystemClauseType::RemoveCallPolicyCheck), ("$remove_inference_counter", 2) => Some(SystemClauseType::RemoveInferenceCounter), @@ -469,7 +486,7 @@ pub enum ClauseType { CallN, Hook(CompileTimeHook), Inlined(InlinedClauseType), - Named(ClauseName, CodeIndex), + Named(ClauseName, usize, CodeIndex), // name, arity, index. Op(OpDecl, CodeIndex), System(SystemClauseType) } @@ -591,7 +608,7 @@ impl ClauseType { } else if name.as_str() == "call" { ClauseType::CallN } else { - ClauseType::Named(name, CodeIndex::default()) + ClauseType::Named(name, arity, CodeIndex::default()) } }) }) @@ -972,11 +989,33 @@ impl From<(usize, ClauseName)> for CodeIndex { } } +#[derive(Clone, Copy, PartialEq)] +pub enum DynamicAssertPlace { + Back, Front +} + +impl DynamicAssertPlace { + pub fn predicate_name(self) -> ClauseName { + match self { + DynamicAssertPlace::Back => clause_name!("assertz"), + DynamicAssertPlace::Front => clause_name!("asserta") + } + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum DynamicTransactionType { + Abolish, + Assert(DynamicAssertPlace), + Retract(usize) // dynamic index of the clause to remove. +} + #[derive(Clone, PartialEq)] pub enum CodePtr { BuiltInClause(BuiltInClauseType, LocalCodePtr), // local is the successor call. CallN(usize, LocalCodePtr), // arity, local. Local(LocalCodePtr), + DynamicTransaction(DynamicTransactionType, LocalCodePtr), // the type of transaction, the return pointer. VerifyAttrInterrupt(usize), // location of the verify attribute interrupt code in the CodeDir. } @@ -987,6 +1026,7 @@ impl CodePtr { | &CodePtr::CallN(_, ref local) | &CodePtr::Local(ref local) => local.clone(), &CodePtr::VerifyAttrInterrupt(p) => LocalCodePtr::DirEntry(p), + &CodePtr::DynamicTransaction(_, p) => p } } } @@ -1077,9 +1117,10 @@ impl Add for CodePtr { fn add(self, rhs: usize) -> Self::Output { match self { - p @ CodePtr::VerifyAttrInterrupt(_) => p, + p @ CodePtr::VerifyAttrInterrupt(_) + | p @ CodePtr::DynamicTransaction(..) => p, CodePtr::Local(local) => CodePtr::Local(local + rhs), - CodePtr::CallN(_, local) | CodePtr::BuiltInClause(_, local) => CodePtr::Local(local + rhs), + CodePtr::CallN(_, local) | CodePtr::BuiltInClause(_, local) => CodePtr::Local(local + rhs) } } } @@ -1115,7 +1156,7 @@ impl<'a> TermIterState<'a> { let op_decl = OpDecl(spec.0, spec.1, name.clone()); ClauseType::Op(op_decl, CodeIndex::default()) } else { - ClauseType::Named(name.clone(), CodeIndex::default()) + ClauseType::Named(name.clone(), subterms.len(), CodeIndex::default()) }; TermIterState::Clause(lvl, 0, cell, ct, subterms) @@ -1346,6 +1387,7 @@ impl SubModuleUser for Module { #[derive(Clone)] pub enum Declaration { + Dynamic(ClauseName, usize), // name, arity Hook(CompileTimeHook, PredicateClause, VecDeque), Module(ModuleDecl), NonCountedBacktracking(ClauseName, usize), // name, arity @@ -1522,6 +1564,16 @@ impl OpDecl { self.2.clone() } + pub fn arity(&self) -> usize { + let spec = self.1; + + if (spec | XFX != 0) || (spec | XFY != 0) || (spec | YFX != 0) { + 2 + } else { + 1 + } + } + pub fn submit(&self, module: ClauseName, op_dir: &mut OpDir) -> Result<(), SessionError> { let (prec, spec, name) = (self.0, self.1, self.2.clone()); diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 7239783d..aeb08af5 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -6,11 +6,12 @@ (>)/2, (<)/2, (=\=)/2, (=:=)/2, (-)/1, (>=)/2, (=<)/2, (,)/2, (->)/2, (;)/2, (=..)/2, (==)/2, (\==)/2, (@=<)/2, (@>=)/2, (@<)/2, (@>)/2, (=@=)/2, (\=@=)/2, (:)/2, bagof/3, - call_with_inference_limit/3, catch/3, current_prolog_flag/2, - expand_goal/2, expand_term/2, findall/3, findall/4, once/1, - repeat/0, set_prolog_flag/2, setof/3, setup_call_cleanup/3, - term_variables/2, throw/1, true/0, false/0, write/1, - write_canonical/1, writeq/1, write_term/2]). + call_with_inference_limit/3, catch/3, clause/2, + current_prolog_flag/2, expand_goal/2, expand_term/2, + findall/3, findall/4, once/1, repeat/0, set_prolog_flag/2, + setof/3, setup_call_cleanup/3, term_variables/2, throw/1, + true/0, false/0, write/1, write_canonical/1, writeq/1, + write_term/2]). /* this is an implementation specific declarative operator used to implement call_with_inference_limit/3 and setup_call_cleanup/3. switches to the default trust_me and retry_me_else. Indexing choice @@ -372,7 +373,7 @@ throw(Ball) :- '$set_ball'(Ball), '$unwind_stack'. truncate_lh_to(LhLength) :- '$truncate_lh_to'(LhLength). -check_for_compat_list(L, PI) :- +check_for_compat_list(L, PI) :- ( nonvar(L), L \= [_|_], throw(error(type_error(list, L), PI)) ; true ). @@ -453,3 +454,26 @@ setof(Template, Goal, Solution) :- keysort(PairedSolutions0, PairedSolutions), group_by_variants(PairedSolutions, GroupedSolutions), iterate_variants_and_sort(GroupedSolutions, Witnesses, Solution). + +% Clause retrieval and information. + +'$clause_body_is_valid'(B) :- + ( var(B) -> true + ; functor(B, Name, _) -> ( Name == '.' -> throw(error(type_error(callable, B), clause/2)) + ; true + ) + ; throw(error(type_error(callable, B), clause/2)) + ). + +clause(H, B) :- + ( var(H) -> throw(error(instantiation_error, clause/2)) + ; functor(H, Name, Arity) -> ( Name == '.' -> throw(error(type_error(callable, H), clause/2)) + %% '$no_such_predicate' fails if H is not callable. + ; '$no_such_predicate'(H) -> '$fail' + ; '$head_is_dynamic'(H) -> '$clause_body_is_valid'(B), + '$get_clause'(H, B) + ; throw(error(permission_error(access, private_procedure, Name/Arity), + clause/2)) + ) + ; throw(error(type_error(callable, H), clause/2)) + ). diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index 4a3af940..653ada62 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -25,6 +25,13 @@ impl MachineError { functor!("/", 2, [name, heap_integer!(arity)], (400, YFX)) } + pub(super) fn static_modification_error(perm_error: PermissionError, culprit: Addr) -> Self { + let stub = functor!("permission_error", 3, [heap_atom!(perm_error.as_str()), + heap_atom!("static_procedure"), + HeapCellValue::Addr(culprit)]); + MachineError { stub, from: ErrorProvenance::Received } + } + pub(super) fn evaluation_error(eval_error: EvalError) -> Self { let stub = functor!("evaluation_error", 1, [heap_atom!(eval_error.as_str())]); MachineError { stub, from: ErrorProvenance::Received } @@ -153,6 +160,19 @@ impl ValidType { } } +#[derive(Clone, Copy)] +pub enum PermissionError { + Modify +} + +impl PermissionError { + pub fn as_str(self) -> &'static str { + match self { + PermissionError::Modify => "modify" + } + } +} + #[derive(Clone, Copy)] pub enum DomainError { NotLessThanZero diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 8cc4b62e..287b19c9 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -231,6 +231,7 @@ impl MachineState { self.p = dir_entry!(p); } + pub(super) fn execute_at_index(&mut self, arity: usize, p: usize) { self.num_of_args = arity; diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs index a579f35b..87340b86 100644 --- a/src/prolog/machine/machine_state_impl.rs +++ b/src/prolog/machine/machine_state_impl.rs @@ -2208,7 +2208,7 @@ impl MachineState { try_or_fail!(self, call_policy.compile_hook(self, hook)), &ClauseType::Inlined(ref ct) => self.execute_inlined(ct), - &ClauseType::Named(ref name, ref idx) | &ClauseType::Op(OpDecl(.., ref name), ref idx) => + &ClauseType::Named(ref name, _, ref idx) | &ClauseType::Op(OpDecl(.., ref name), ref idx) => try_or_fail!(self, call_policy.context_call(self, name.clone(), arity, idx.clone(), indices)), &ClauseType::System(ref ct) => diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index b7446415..a35d3318 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -23,11 +23,27 @@ use std::mem; use std::ops::Index; use std::rc::Rc; -pub type InSituCodeDir = HashMap; +//pub type DynamicPredicateClauses = VecDeque<(PredicateClause, VecDeque)>; + +#[derive(Copy, Clone)] +pub struct DynamicPredicateInfo { + pub(super) clauses_subsection_p: usize, // a LocalCodePtr::DirEntry value. +// clauses: DynamicPredicateClauses +} + +impl Default for DynamicPredicateInfo { + fn default() -> Self { + DynamicPredicateInfo { clauses_subsection_p: 0 } + } +} + +pub type InSituCodeDir = HashMap; +pub type DynamicCodeDir = HashMap; pub struct IndexStore { pub(super) atom_tbl: TabledData, pub(super) code_dir: CodeDir, + pub(super) dynamic_code_dir: DynamicCodeDir, pub(super) in_situ_code_dir: InSituCodeDir, pub(super) op_dir: OpDir, pub(super) modules: ModuleDir, @@ -48,6 +64,24 @@ impl<'a, T> RefOrOwned<'a, T> { } impl IndexStore { + pub fn predicate_exists(&self, name: ClauseName, arity: usize, + op_spec: Option<(usize, Specifier)>) + -> bool + { + match ClauseType::from(name, arity, op_spec) { + ClauseType::Named(name, arity, _) => + self.code_dir.contains_key(&(name, arity)), + ClauseType::Op(op_decl, ..) => + self.code_dir.contains_key(&(op_decl.name(), op_decl.arity())), + _ => true + } + } + + #[inline] + pub fn get_clause_subsection(&self, name: ClauseName, arity: usize) -> Option { + self.dynamic_code_dir.get(&(name, arity)).cloned() + } + #[inline] pub fn take_module(&mut self, name: ClauseName) -> Option { self.modules.remove(&name) @@ -63,6 +97,7 @@ impl IndexStore { IndexStore { atom_tbl: TabledData::new(Rc::new("user".to_string())), code_dir: CodeDir::new(), + dynamic_code_dir: DynamicCodeDir::new(), in_situ_code_dir: InSituCodeDir::new(), op_dir: default_op_dir(), modules: ModuleDir::new(), @@ -204,7 +239,8 @@ impl CodeRepo { }, &CodePtr::VerifyAttrInterrupt(p) => Some(RefOrOwned::Borrowed(&self.code[p])), - + &CodePtr::DynamicTransaction(..) => + None } } } @@ -395,11 +431,11 @@ impl Machine { // ensure we don't try to overwrite an existing predicate from a different module. if !existing_idx.is_undefined() && !idx.is_undefined() { // allow the overwriting of user-level predicates by all other predicates. - if existing_idx.module_name().as_str() == "user" { + if existing_idx.module_name() == key.0.owning_module() { continue; } - if existing_idx.module_name().as_str() != idx.module_name().as_str() { + if existing_idx.module_name() != idx.module_name() { let err_str = format!("{}/{} from module {}", key.0, key.1, existing_idx.module_name().as_str()); return Err(SessionError::CannotOverwriteImport(err_str)); @@ -464,8 +500,7 @@ impl Machine { let mut heap_locs = HashMap::new(); self.code_repo.cached_query = code; - self.machine_st.run_query(&mut self.indices, &mut self.policies, &mut self.code_repo, - &alloc_locs, &mut heap_locs); + self.run_query(&alloc_locs, &mut heap_locs); if self.machine_st.fail { self.fail(&heap_locs) @@ -474,6 +509,67 @@ impl Machine { } } + fn record_var_places(&self, chunk_num: usize, alloc_locs: &AllocVarDict, + heap_locs: &mut HeapVarDict) + { + for (var, var_data) in alloc_locs { + match var_data { + &VarData::Perm(p) if p > 0 => + if !heap_locs.contains_key(var) { + let e = self.machine_st.e; + let r = var_data.as_reg_type().reg_num(); + let addr = self.machine_st.and_stack[e][r].clone(); + + heap_locs.insert(var.clone(), addr); + }, + &VarData::Temp(cn, _, _) if cn == chunk_num => { + let r = var_data.as_reg_type(); + + if r.reg_num() != 0 { + let addr = self.machine_st[r].clone(); + heap_locs.insert(var.clone(), addr); + } + }, + _ => {} + } + } + } + + pub(super) + fn run_query(&mut self, alloc_locs: &AllocVarDict, heap_locs: &mut HeapVarDict) + { + let end_ptr = top_level_code_ptr!(0, self.code_repo.size_of_cached_query()); + + while self.machine_st.p < end_ptr { + if let CodePtr::Local(LocalCodePtr::TopLevel(mut cn, p)) = self.machine_st.p { + match &self.code_repo[LocalCodePtr::TopLevel(cn, p)] { + &Line::Control(ref ctrl_instr) if ctrl_instr.is_jump_instr() => { + self.record_var_places(cn, alloc_locs, heap_locs); + cn += 1; + }, + _ => {} + } + + self.machine_st.p = top_level_code_ptr!(cn, p); + } + + self.machine_st.query_stepper(&mut self.indices, &mut self.policies, &mut self.code_repo); + + match self.machine_st.p { + CodePtr::Local(LocalCodePtr::TopLevel(_, p)) if p > 0 => {}, + CodePtr::DynamicTransaction(trans_type, p) => {}, +// self.dynamic_transaction(trans_type, p), + _ => { + if heap_locs.is_empty() { + self.record_var_places(0, alloc_locs, heap_locs); + } + + break; + } + }; + } + } + pub fn continue_query(&mut self, alloc_l: &AllocVarDict, heap_l: &mut HeapVarDict) -> EvalSession { if !self.or_stack_is_empty() { @@ -484,8 +580,7 @@ impl Machine { return EvalSession::from(SessionError::QueryFailure); } - self.machine_st.run_query(&mut self.indices, &mut self.policies, &mut self.code_repo, - alloc_l, heap_l); + self.run_query(alloc_l, heap_l); if self.machine_st.fail { self.fail(&heap_l) @@ -601,73 +696,12 @@ impl MachineState { self.fail = true, CodePtr::Local(LocalCodePtr::InSituDirEntry(p)) if p < code_repo.in_situ_code.len() => {}, - CodePtr::Local(_) => + CodePtr::Local(_) | CodePtr::DynamicTransaction(..) => break, CodePtr::VerifyAttrInterrupt(p) => self.verify_attr_interrupt(p), _ => {} - }; - } - } - - fn record_var_places(&self, chunk_num: usize, alloc_locs: &AllocVarDict, - heap_locs: &mut HeapVarDict) - { - for (var, var_data) in alloc_locs { - match var_data { - &VarData::Perm(p) if p > 0 => - if !heap_locs.contains_key(var) { - let e = self.e; - let r = var_data.as_reg_type().reg_num(); // crashes here. - let addr = self.and_stack[e][r].clone(); - - heap_locs.insert(var.clone(), addr); - }, - &VarData::Temp(cn, _, _) if cn == chunk_num => { - let r = var_data.as_reg_type(); - - if r.reg_num() != 0 { - let addr = self[r].clone(); - heap_locs.insert(var.clone(), addr); - } - }, - _ => {} } } } - - pub(super) - fn run_query(&mut self, indices: &mut IndexStore, - policies: &mut MachinePolicies, code_repo: &mut CodeRepo, - alloc_locs: &AllocVarDict, heap_locs: &mut HeapVarDict) - { - let end_ptr = top_level_code_ptr!(0, code_repo.size_of_cached_query()); - - while self.p < end_ptr { - if let CodePtr::Local(LocalCodePtr::TopLevel(mut cn, p)) = self.p { - match &code_repo[LocalCodePtr::TopLevel(cn, p)] { - &Line::Control(ref ctrl_instr) if ctrl_instr.is_jump_instr() => { - self.record_var_places(cn, alloc_locs, heap_locs); - cn += 1; - }, - _ => {} - } - - self.p = top_level_code_ptr!(cn, p); - } - - self.query_stepper(indices, policies, code_repo); - - match self.p { - CodePtr::Local(LocalCodePtr::TopLevel(_, p)) if p > 0 => {}, - _ => { - if heap_locs.is_empty() { - self.record_var_places(0, alloc_locs, heap_locs); - } - - break; - } - }; - } - } } diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 3cee711e..2e1bc2c0 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -244,6 +244,21 @@ impl MachineState { _ => self.fail = true }; }, + &SystemClauseType::HeadIsDynamic => { + let head = self[temp_v!(1)].clone(); + + self.fail = !match self.store(self.deref(head)) { + Addr::Str(s) => + match self.heap[s].clone() { + HeapCellValue::NamedStr(arity, name, ..) => + indices.get_clause_subsection(name, arity).is_some(), + _ => unreachable!() + }, + Addr::Con(Constant::Atom(name, _)) => + indices.get_clause_subsection(name, 0).is_some(), + _ => unreachable!() + }; + }, &SystemClauseType::CopyToLiftedHeap => // now, stagger everything down by the length of the heap + lh offset. match self.store(self.deref(self[temp_v!(1)].clone())) { @@ -562,6 +577,26 @@ impl MachineState { _ => self.fail = true }; }, + &SystemClauseType::NoSuchPredicate => { + let head = self[temp_v!(1)].clone(); + + self.fail = match self.store(self.deref(head)) { + Addr::Str(s) => + match self.heap[s].clone() { + HeapCellValue::NamedStr(arity, name, op_spec) => + indices.predicate_exists(name, arity, op_spec), + _ => unreachable!() + }, + Addr::Con(Constant::Atom(name, op_spec)) => + indices.predicate_exists(name, 0, op_spec), + head => { + let err = MachineError::type_error(ValidType::Callable, head); + let stub = MachineError::functor_stub(clause_name!("clause"), 2); + + return Err(self.error_form(err, stub)); + } + }; + }, &SystemClauseType::RedoAttrVarBindings => { let mut bindings = mem::replace(&mut self.attr_var_init.bindings, vec![]); @@ -727,6 +762,30 @@ impl MachineState { self.unify(a1, a2); }, + &SystemClauseType::GetClause => { + let head = self[temp_v!(1)].clone(); + let body = self[temp_v!(2)].clone(); + + let subsection = match self.store(self.deref(head)) { + Addr::Str(s) => + match self.heap[s].clone() { + HeapCellValue::NamedStr(arity, name, ..) => + indices.get_clause_subsection(name, arity), + _ => unreachable!() + }, + Addr::Con(Constant::Atom(name, _)) => + indices.get_clause_subsection(name, 0), + _ => unreachable!() + }; + + match subsection { + Some(dynamic_predicate_info) => { + self.execute_at_index(2, dynamic_predicate_info.clauses_subsection_p); + return Ok(()); + }, + _ => unreachable!() + } + }, &SystemClauseType::GetCutPoint => { let a1 = self[temp_v!(1)].clone(); let a2 = Addr::Con(Constant::Usize(self.b0)); diff --git a/src/prolog/machine/term_expansion.rs b/src/prolog/machine/term_expansion.rs index 0674e8a3..3209d220 100644 --- a/src/prolog/machine/term_expansion.rs +++ b/src/prolog/machine/term_expansion.rs @@ -288,11 +288,16 @@ impl<'a, R: Read> TermStream<'a, R> { } impl MachineState { - fn print_with_locs(&self, target: usize, max_var_length: usize, var_dict: &HeapVarDict) - -> PrinterOutputter + pub(super) + fn print_with_locs(&self, addr: Addr, var_dict: &HeapVarDict) -> PrinterOutputter { let output = PrinterOutputter::new(); - let mut printer = HCPrinter::from_heap_locs(&self, output, &var_dict); + let mut printer = HCPrinter::from_heap_locs(&self, output, var_dict); + let mut max_var_length = 0; + + for var in var_dict.keys() { + max_var_length = std::cmp::max(var.len(), max_var_length); + } printer.quoted = true; printer.numbervars = true; @@ -302,10 +307,12 @@ impl MachineState { // style variable names will be longer than the keys of the var_dict, and therefore // not equal to any of them. printer.numbervars_offset = pow(BigInt::from(10), max_var_length) * 26; - printer.see_all_locs(); - printer.print(Addr::HeapCell(target)) + let mut output = printer.print(addr); + + output.push_char('.'); + output } fn try_expand_term(&mut self, indices: &mut IndexStore, policies: &mut MachinePolicies, @@ -322,16 +329,15 @@ impl MachineState { let code = vec![call_clause!(ClauseType::Hook(hook), 2, 0, true)]; code_repo.cached_query = code; - self.run_query(indices, policies, code_repo, &AllocVarDict::new(), &mut HeapVarDict::new()); + self.query_stepper(indices, policies, code_repo); if self.fail { self.reset(); None } else { - let &TermWriteResult { heap_loc: _, max_var_length, ref var_dict } = &term_write_result; - let mut output = self.print_with_locs(h, max_var_length, var_dict); + let &TermWriteResult { heap_loc: _, ref var_dict } = &term_write_result; + let output = self.print_with_locs(Addr::HeapCell(h), var_dict); - output.push_char('.'); self.reset(); Some(output.result()) } diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 4a94c43b..f1cc166e 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -213,6 +213,7 @@ macro_rules! index_store { ($atom_tbl:expr, $code_dir:expr, $op_dir:expr, $modules:expr) => ( IndexStore { atom_tbl: $atom_tbl, code_dir: $code_dir, + dynamic_code_dir: DynamicCodeDir::new(), in_situ_code_dir: InSituCodeDir::new(), op_dir: $op_dir, modules: $modules } diff --git a/src/prolog/read.rs b/src/prolog/read.rs index d72a813f..357b8b57 100644 --- a/src/prolog/read.rs +++ b/src/prolog/read.rs @@ -75,15 +75,13 @@ fn modify_head_of_queue(machine_st: &mut MachineState, queue: &mut SubtermDeque, pub(crate) struct TermWriteResult { pub(crate) heap_loc: usize, - pub(crate) max_var_length: usize, // maximum length of the variable names encountered. - pub(crate) var_dict: HeapVarDict, + pub(crate) var_dict: HeapVarDict, } pub(crate) fn write_term_to_heap(term: &Term, machine_st: &mut MachineState) -> TermWriteResult { let heap_loc = machine_st.heap.h; - let mut max_var_length = 0; let mut queue = SubtermDeque::new(); let mut var_dict = HeapVarDict::new(); @@ -118,10 +116,8 @@ pub(crate) fn write_term_to_heap(term: &Term, machine_st: &mut MachineState) -> }, &TermRef::AnonVar(Level::Root) | &TermRef::Constant(Level::Root, ..) => machine_st.heap.push(HeapCellValue::Addr(term.as_addr(h))), - &TermRef::Var(Level::Root, _, ref name) => { - max_var_length = std::cmp::max(max_var_length, name.len()); - machine_st.heap.push(HeapCellValue::Addr(term.as_addr(h))); - }, + &TermRef::Var(Level::Root, _, ref name) => + machine_st.heap.push(HeapCellValue::Addr(term.as_addr(h))), &TermRef::AnonVar(_) => { if let Some((arity, site_h)) = queue.pop_front() { if arity > 1 { @@ -144,7 +140,6 @@ pub(crate) fn write_term_to_heap(term: &Term, machine_st: &mut MachineState) -> } } - max_var_length = std::cmp::max(max_var_length, var.len()); continue; }, _ => {} @@ -153,5 +148,5 @@ pub(crate) fn write_term_to_heap(term: &Term, machine_st: &mut MachineState) -> modify_head_of_queue(machine_st, &mut queue, term, h); } - TermWriteResult { heap_loc, var_dict, max_var_length } + TermWriteResult { heap_loc, var_dict } } diff --git a/src/prolog/toplevel.rs b/src/prolog/toplevel.rs index 5853268d..2b28e56c 100644 --- a/src/prolog/toplevel.rs +++ b/src/prolog/toplevel.rs @@ -9,7 +9,7 @@ use prolog::machine::term_expansion::*; use prolog::num::*; use std::borrow::BorrowMut; -use std::collections::{HashSet, VecDeque}; +use std::collections::{HashMap, HashSet, VecDeque}; use std::cell::Cell; use std::io::Read; use std::mem; @@ -58,9 +58,9 @@ impl<'a, 'b> CompositeIndices<'a, 'b> fn get_clause_type(&mut self, name: ClauseName, arity: usize, spec: Option<(usize, Specifier)>) -> ClauseType { match ClauseType::from(name, arity, spec) { - ClauseType::Named(name, _) => { + ClauseType::Named(name, arity, _) => { let idx = self.get_code_index(name.clone(), arity); - ClauseType::Named(name, idx.clone()) + ClauseType::Named(name, arity, idx.clone()) }, ClauseType::Op(op_decl, _) => { let idx = self.get_code_index(op_decl.2.clone(), arity); @@ -112,16 +112,6 @@ fn is_compile_time_hook(name: &ClauseName, terms: &Vec>) -> Option); -fn setup_fact(term: Term) -> Result -{ - match term { - Term::Clause(..) | Term::Constant(_, Constant::Atom(..)) => - Ok(term), - _ => - Err(ParserError::InadmissibleFact) - } -} - fn setup_op_decl(mut terms: Vec>) -> Result { let name = match *terms.pop().unwrap() { @@ -155,7 +145,7 @@ fn setup_op_decl(mut terms: Vec>) -> Result } } -fn setup_predicate_export(mut term: Term) -> Result +fn setup_predicate_indicator(mut term: Term) -> Result { match term { Term::Clause(_, ref name, ref mut terms, Some(_)) @@ -185,7 +175,7 @@ fn setup_module_decl(mut terms: Vec>) -> Result>) -> Result Result let (name, exports) = setup_qualified_import(terms)?; Ok(Declaration::UseQualifiedModule(name, exports)) } else if name.as_str() == "non_counted_backtracking" && terms.len() == 1 { - let (name, arity) = setup_predicate_export(*terms.pop().unwrap())?; + let (name, arity) = setup_predicate_indicator(*terms.pop().unwrap())?; Ok(Declaration::NonCountedBacktracking(name, arity)) + } else if name.as_str() == "dynamic" && terms.len() == 1 { + let (name, arity) = setup_predicate_indicator(*terms.pop().unwrap())?; + Ok(Declaration::Dynamic(name, arity)) } else { Err(ParserError::InconsistentEntry) }, @@ -387,12 +380,29 @@ pub enum TopLevelPacket { } struct RelationWorker { + dynamic_clauses: Vec<(Term, Term)>, // Head, Body. queue: VecDeque>, } impl RelationWorker { fn new() -> Self { - RelationWorker { queue: VecDeque::new() } + RelationWorker { dynamic_clauses: vec![], + queue: VecDeque::new() } + } + + fn setup_fact(&mut self, term: Term) -> Result + { + match term { + Term::Clause(..) | Term::Constant(_, Constant::Atom(..)) => { + let tail = Term::Constant(Cell::default(), + Constant::Atom(clause_name!("true"), None)); + + self.dynamic_clauses.push((term.clone(), tail)); + Ok(term) + }, + _ => + Err(ParserError::InadmissibleFact) + } } fn compute_head(&self, term: &Term) -> Vec @@ -617,7 +627,7 @@ impl RelationWorker { match flatten_hook(term) { Term::Clause(r, name, terms, _) => if name == hook.name() && terms.len() == hook.arity() { - let term = setup_fact(Term::Clause(r, name, terms, None))?; + let term = self.setup_fact(Term::Clause(r, name, terms, None))?; Ok((hook, PredicateClause::Fact(term), VecDeque::from(vec![]))) } else if name.as_str() == ":-" && terms.len() == 2 { let rule = self.setup_rule(indices, terms, true)?; @@ -635,8 +645,14 @@ impl RelationWorker { blocks_cuts: bool) -> Result { - let post_head_terms = terms.drain(1..).collect(); - let mut query_terms = try!(self.setup_query(indices, post_head_terms, blocks_cuts)); + let post_head_terms: Vec<_> = terms.drain(1 ..).collect(); + + let head = *terms.first().cloned().unwrap(); + let tail = *post_head_terms.first().cloned().unwrap(); + + self.dynamic_clauses.push((head, tail)); + + let mut query_terms = self.setup_query(indices, post_head_terms, blocks_cuts)?; let clauses = query_terms.drain(1 ..).collect(); let qt = query_terms.pop().unwrap(); @@ -668,9 +684,9 @@ impl RelationWorker { Ok(TopLevel::Declaration(try!(setup_declaration(term)))) } else { let term = Term::Clause(r, name, terms, fixity); - Ok(TopLevel::Fact(try!(setup_fact(term)))) + Ok(TopLevel::Fact(try!(self.setup_fact(term)))) }, - term => Ok(TopLevel::Fact(try!(setup_fact(term)))) + term => Ok(TopLevel::Fact(try!(self.setup_fact(term)))) } } @@ -701,6 +717,7 @@ impl RelationWorker { fn absorb(&mut self, other: RelationWorker) { self.queue.extend(other.queue.into_iter()); + self.dynamic_clauses.extend(other.dynamic_clauses.into_iter()); } fn expand_queue_contents<'a, R>(&mut self, term_stream: &mut TermStream<'a, R>, op_dir: &OpDir) @@ -731,7 +748,8 @@ fn term_to_toplevel<'a, R>(term_stream: &mut TermStream<'a, R>, code_dir: &mut C let mut rel_worker = RelationWorker::new(); let mut indices = composite_indices!(false, term_stream.indices, code_dir); - Ok((rel_worker.try_term_to_tl(&mut indices, term, true)?, rel_worker)) + let tl = rel_worker.try_term_to_tl(&mut indices, term, true)?; + Ok((tl, rel_worker)) } pub @@ -757,10 +775,13 @@ fn string_to_toplevel(src: R, buffer: String, wam: &mut Machine) Ok(deque_to_packet(tl, queue)) } +pub type DynamicClauseMap = HashMap<(ClauseName, usize), Vec<(Term, Term)>>; + pub struct TopLevelBatchWorker<'a, R: Read> { pub(crate) term_stream: TermStream<'a, R>, rel_worker: RelationWorker, pub(crate) results: Vec<(Predicate, VecDeque)>, + pub(crate) dynamic_clause_map: DynamicClauseMap, pub(crate) in_module: bool } @@ -776,6 +797,7 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> { TopLevelBatchWorker { term_stream, rel_worker: RelationWorker::new(), results: vec![], + dynamic_clause_map: HashMap::new(), in_module: false } } @@ -790,7 +812,7 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> { } fn process_result(&mut self, indices: &mut IndexStore, preds: &mut Vec) - -> Result<(Predicate, VecDeque), SessionError> + -> Result<(), SessionError> { self.rel_worker.expand_queue_contents(&mut self.term_stream, &indices.op_dir)?; @@ -805,7 +827,26 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> { self.term_stream.code_repo.add_in_situ_result(&result, in_situ_code_dir, self.term_stream.flags)?; - Ok(result) + Ok(self.results.push(result)) + } + + fn take_dynamic_clauses(&mut self) { + let (name, arity) = match self.rel_worker.dynamic_clauses.first() { + Some((head, tail)) => + (head.name().unwrap(), head.arity()), + None => + return + }; + + match self.dynamic_clause_map.get_mut(&(name.clone(), arity)) { + Some(ref mut entry) => { + entry.clear(); // don't treat dynamic predicates as if they're discontiguous. + entry.extend(self.rel_worker.dynamic_clauses.drain(0 ..)); + }, + _ => { + self.rel_worker.dynamic_clauses.clear(); + } + } } pub fn consume(&mut self, indices: &mut IndexStore) -> Result, SessionError> @@ -818,8 +859,8 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> { // if is_consistent is false, preds is non-empty. if !is_consistent(&tl, &preds) { - let result = self.process_result(indices, &mut preds)?; - self.results.push(result); + self.process_result(indices, &mut preds)?; + self.take_dynamic_clauses(); } self.rel_worker.absorb(new_rel_worker); @@ -834,8 +875,8 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> { } if !preds.is_empty() { - let result = self.process_result(indices, &mut preds)?; - self.results.push(result); + self.process_result(indices, &mut preds)?; + self.take_dynamic_clauses(); } Ok(None) diff --git a/src/prolog/write.rs b/src/prolog/write.rs index 206fc87c..25b075e3 100644 --- a/src/prolog/write.rs +++ b/src/prolog/write.rs @@ -136,7 +136,8 @@ impl fmt::Display for ClauseType { match self { &ClauseType::System(SystemClauseType::SetCutPoint(r)) => write!(f, "$set_cp({})", r), - &ClauseType::Named(ref name, ref idx) | &ClauseType::Op(OpDecl(.., ref name), ref idx) => + &ClauseType::Named(ref name, _, ref idx) + | &ClauseType::Op(OpDecl(.., ref name), ref idx) => { let idx = idx.0.borrow(); write!(f, "{}:{}/{}", idx.1, name, idx.0) diff --git a/src/tests.rs b/src/tests.rs index a9fe8958..3824cad9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1783,6 +1783,38 @@ fn test_queries_on_builtins() [["S = _11", "X = 1", "Y = 2"]]); assert_prolog_success!(&mut wam, "?- catch(findall(X, 4, S0, S1), error(type_error(callable, 4), _), true).", [["S0 = _3", "S1 = _4", "X = _1"]]); + + submit(&mut wam, " +:- dynamic(cat/0). +cat. + +:- dynamic(dog/0). +dog :- true. + +elk(X) :- moose(X). + +:- dynamic(legs/2). +legs(A, 6) :- insect(A). +legs(A, 7) :- A, call(A). + +:- dynamic(insect/1). +insect(ant). +insect(bee)."); + + assert_prolog_success!(&mut wam, "?- clause(cat, true)."); + assert_prolog_success!(&mut wam, "?- clause(dog, true)."); + assert_prolog_success!(&mut wam, "?- clause(legs(I, 6), Body).", + [["I = _1", "Body = insect(_1)"]]); + assert_prolog_success!(&mut wam, "?- clause(legs(C, 7), Body).", + [["C = _1", "Body = ','(_1, call(C))"]]); + assert_prolog_success!(&mut wam, "?- clause(insect(I), T).", + [["I = ant", "T = true"], + ["I = bee", "T = true"]]); + assert_prolog_failure!(&mut wam, "?- clause(x, Body)."); + assert_prolog_success!(&mut wam, "?- catch(clause(_, _), error(instantiation_error, _), true)."); + assert_prolog_success!(&mut wam, "?- catch(clause(4, _), error(type_error(callable, 4), _), true)."); + assert_prolog_success!(&mut wam, "?- catch(clause(elk(N), _), error(permission_error(access, private_procedure, elk/1), _), true)."); + assert_prolog_success!(&mut wam, "?- catch(clause(atom(N), _), error(permission_error(access, private_procedure, atom/1), _), true)."); } #[test]