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;
}
}
-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)
}
}
-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
}
}
+ /*
+ 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)
.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>
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();
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());
}
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>
{
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)?;
}
}
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));
}
}
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));
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
}
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
}
}
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),
#[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
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)]
GetAttrVarQueueDelimiter,
GetAttrVarQueueBeyond,
GetBValue,
+ GetClause,
GetLiftedHeapFromOffset,
GetLiftedHeapFromOffsetDiff,
GetSCCCleaner,
+ HeadIsDynamic,
InstallSCCCleaner,
InstallInferenceCounter,
LiftedHeapLength,
ModuleOf,
+ NoSuchPredicate,
RedoAttrVarBindings,
RemoveCallPolicyCheck,
RemoveInferenceCounter,
&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"),
("$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),
CallN,
Hook(CompileTimeHook),
Inlined(InlinedClauseType),
- Named(ClauseName, CodeIndex),
+ Named(ClauseName, usize, CodeIndex), // name, arity, index.
Op(OpDecl, CodeIndex),
System(SystemClauseType)
}
} else if name.as_str() == "call" {
ClauseType::CallN
} else {
- ClauseType::Named(name, CodeIndex::default())
+ ClauseType::Named(name, arity, CodeIndex::default())
}
})
})
}
}
+#[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.
}
| &CodePtr::CallN(_, ref local)
| &CodePtr::Local(ref local) => local.clone(),
&CodePtr::VerifyAttrInterrupt(p) => LocalCodePtr::DirEntry(p),
+ &CodePtr::DynamicTransaction(_, p) => p
}
}
}
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)
}
}
}
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)
#[derive(Clone)]
pub enum Declaration {
+ Dynamic(ClauseName, usize), // name, arity
Hook(CompileTimeHook, PredicateClause, VecDeque<TopLevel>),
Module(ModuleDecl),
NonCountedBacktracking(ClauseName, usize), // name, arity
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());
(>)/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
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
).
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))
+ ).
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 }
}
}
+#[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
self.p = dir_entry!(p);
}
+ pub(super)
fn execute_at_index(&mut self, arity: usize, p: usize)
{
self.num_of_args = arity;
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) =>
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,
}
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)
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(),
},
&CodePtr::VerifyAttrInterrupt(p) =>
Some(RefOrOwned::Borrowed(&self.code[p])),
-
+ &CodePtr::DynamicTransaction(..) =>
+ None
}
}
}
// 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));
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)
}
}
+ 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() {
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)
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;
- }
- };
- }
- }
}
_ => 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())) {
_ => 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![]);
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));
}
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;
// 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,
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())
}
($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 }
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();
},
&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 {
}
}
- max_var_length = std::cmp::max(max_var_length, var.len());
continue;
},
_ => {}
modify_head_of_queue(machine_st, &mut queue, term, h);
}
- TermWriteResult { heap_loc, var_dict, max_var_length }
+ TermWriteResult { heap_loc, var_dict }
}
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;
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);
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() {
}
}
-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(_))
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;
}
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;
}
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)
},
}
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>
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)?;
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();
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))))
}
}
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)
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
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
}
TopLevelBatchWorker { term_stream,
rel_worker: RelationWorker::new(),
results: vec![],
+ dynamic_clause_map: HashMap::new(),
in_module: false }
}
}
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)?;
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>
// 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);
}
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)
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)
[["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]