]> Repositorios git - scryer-prolog.git/commitdiff
start enabling the dynamic database
authorMark Thom <[email protected]>
Fri, 1 Mar 2019 04:49:07 +0000 (21:49 -0700)
committerMark Thom <[email protected]>
Fri, 1 Mar 2019 04:49:07 +0000 (21:49 -0700)
16 files changed:
src/prolog/compile.rs
src/prolog/copier.rs
src/prolog/heap_print.rs
src/prolog/instructions.rs
src/prolog/lib/builtins.pl
src/prolog/machine/machine_errors.rs
src/prolog/machine/machine_state.rs
src/prolog/machine/machine_state_impl.rs
src/prolog/machine/mod.rs
src/prolog/machine/system_calls.rs
src/prolog/machine/term_expansion.rs
src/prolog/macros.rs
src/prolog/read.rs
src/prolog/toplevel.rs
src/prolog/write.rs
src/tests.rs

index f318bb391a8fe27ba74def7bdb56f082bfdfefef..727f70d5dba875b053670d779298bdbb7b301949 100644 (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();
-
-        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<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
     }
 }
index 96e4ce5e46e189dad75beb25831838219be91006..d3db0fef1d510b8b2b62a168ad197fd9eec21aa7 100644 (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),
index e379e9d1fd91691d168c00badbc5ff25362d41bf..0b2ac0f9aa75b954ff766e5c453adf1c0078b170 100644 (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
index 1f992dcc8905e7b8c5d067df33ca84947cac9d4d..cdd1900160bd0f0b3acf11231775a2c80ccee3be 100644 (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());
index 7239783d3c35004dd3d938cf009d39fbc944458c..aeb08af5d020648f422573a2bb45a54b653b16a3 100644 (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))
+    ).
index 4a3af940d4f215f13391499cf8d4d041213931c8..653ada62b6c6ea4bfe97c3ad71391cf593f71717 100644 (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
index 8cc4b62eaed49f66c278401792c9dc083cd5c31d..287b19c9288adf984364750964c41d15159c06be 100644 (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;
index a579f35b96a70d03270bc816d6285b8399cb21df..87340b8630d226294d4c80683fdc0f22e766baa8 100644 (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) =>
index b74464157b9dfa4caee9cb67f2f007c4958e30e9..a35d331808b79fc84a172134a7b9251c0177503b 100644 (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;
-                }
-            };
-        }
-    }
 }
index 3cee711e565d63e7cd6533bf9cd912219b29216a..2e1bc2c0aae77b260bd12966beb86b6148bb8f2e 100644 (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));
index 0674e8a3a8b6141c8dc76f8de06243e1b9cdc7d5..3209d220e00b33bfeb85da1ce0e938bd3ca10021 100644 (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())
         }
index 4a94c43bb0a7a94d8eb40e0de53a9f8723330226..f1cc166e15971fb9b2c0ccf0dfbcac7a08e0e02b 100644 (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 }
index d72a813fdd2d4ed8f9ae64063fbe3d037423950a..357b8b57bb2eb975ad3ab4e6402db370b79acafa 100644 (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 }
 }
index 5853268d4a42ae65ae16f11d36af2d6c8281f7c1..2b28e56c545177da337d41bdbc977bf0756eaaa8 100644 (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)
index 206fc87c2b043cd497c71c7a74a8ca8cfe4c7c81..25b075e3c7952b06591417fab3905fbe8fba6ab2 100644 (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)
index a9fe895828f626ae7a6dec1949821c82ad227125..3824cad9390c7178331e7648cf8a4faa2c01061a 100644 (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]