start enabling the dynamic database

This commit is contained in:
Mark Thom
2019-02-28 21:49:07 -07:00
parent 9db7172bfe
commit 818a971833
16 changed files with 510 additions and 167 deletions

View File

@@ -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<TopLevel>);
pub type PredicateCompileQueue = (Predicate, VecDeque<TopLevel>);
// 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<PredicateCompileQueue>,
pub struct GatherResult {
dynamic_clause_map: DynamicClauseMap,
pub(crate) worker_results: Vec<PredicateCompileQueue>,
toplevel_results: Vec<PredicateCompileQueue>,
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<PredicateCompileQueue>, wam: &Machine,
code_dir: &mut CodeDir)
-> Result<Code, SessionError>
@@ -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<R: Read>(&mut self, wam: &mut Machine, src: R, indices: &mut IndexStore)
-> Result<GatherResult, SessionError>
{
@@ -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();
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);
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));
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<R: Read>(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<R: Read>(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<R: Read>(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
}
}

View File

@@ -175,10 +175,12 @@ impl<T: CopierTarget> CopyTermState<T> {
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),

View File

@@ -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

View File

@@ -162,13 +162,21 @@ pub struct Rule {
pub struct Predicate(pub Vec<PredicateClause>);
impl Predicate {
#[inline]
pub fn new() -> Self {
Predicate(vec![])
}
#[inline]
pub fn clauses(self) -> Vec<PredicateClause> {
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<usize> 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<TopLevel>),
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());

View File

@@ -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))
).

View File

@@ -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

View File

@@ -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;

View File

@@ -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) =>

View File

@@ -23,11 +23,27 @@ use std::mem;
use std::ops::Index;
use std::rc::Rc;
pub type InSituCodeDir = HashMap<PredicateKey, usize>;
//pub type DynamicPredicateClauses = VecDeque<(PredicateClause, VecDeque<TopLevel>)>;
#[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<PredicateKey, usize>;
pub type DynamicCodeDir = HashMap<PredicateKey, DynamicPredicateInfo>;
pub struct IndexStore {
pub(super) atom_tbl: TabledData<Atom>,
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<DynamicPredicateInfo> {
self.dynamic_code_dir.get(&(name, arity)).cloned()
}
#[inline]
pub fn take_module(&mut self, name: ClauseName) -> Option<Module> {
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;
}
};
}
}
}

View File

@@ -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));

View File

@@ -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())
}

View File

@@ -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 }

View File

@@ -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 }
}

View File

@@ -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<Box<Term>>) -> Option<Com
type CompileTimeHookCompileInfo = (CompileTimeHook, PredicateClause, VecDeque<TopLevel>);
fn setup_fact(term: Term) -> Result<Term, ParserError>
{
match term {
Term::Clause(..) | Term::Constant(_, Constant::Atom(..)) =>
Ok(term),
_ =>
Err(ParserError::InadmissibleFact)
}
}
fn setup_op_decl(mut terms: Vec<Box<Term>>) -> Result<OpDecl, ParserError>
{
let name = match *terms.pop().unwrap() {
@@ -155,7 +145,7 @@ fn setup_op_decl(mut terms: Vec<Box<Term>>) -> Result<OpDecl, ParserError>
}
}
fn setup_predicate_export(mut term: Term) -> Result<PredicateKey, ParserError>
fn setup_predicate_indicator(mut term: Term) -> Result<PredicateKey, ParserError>
{
match term {
Term::Clause(_, ref name, ref mut terms, Some(_))
@@ -185,7 +175,7 @@ fn setup_module_decl(mut terms: Vec<Box<Term>>) -> Result<ModuleDecl, ParserErro
let mut exports = Vec::new();
while let Term::Cons(_, t1, t2) = export_list {
exports.push(setup_predicate_export(*t1)?);
exports.push(setup_predicate_indicator(*t1)?);
export_list = *t2;
}
@@ -227,7 +217,7 @@ fn setup_qualified_import(mut terms: Vec<Box<Term>>) -> Result<UseModuleExport,
let mut exports = Vec::new();
while let Term::Cons(_, t1, t2) = export_list {
exports.push(setup_predicate_export(*t1)?);
exports.push(setup_predicate_indicator(*t1)?);
export_list = *t2;
}
@@ -252,8 +242,11 @@ fn setup_declaration(term: Term) -> Result<Declaration, ParserError>
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<VecDeque<Term>>,
}
impl RelationWorker {
fn new() -> Self {
RelationWorker { queue: VecDeque::new() }
RelationWorker { dynamic_clauses: vec![],
queue: VecDeque::new() }
}
fn setup_fact(&mut self, term: Term) -> Result<Term, ParserError>
{
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<Term>
@@ -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<Rule, ParserError>
{
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<R: Read>(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<TopLevel>)>,
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<PredicateClause>)
-> Result<(Predicate, VecDeque<TopLevel>), 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<Option<Declaration>, 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)

View File

@@ -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)

View File

@@ -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]