]> Repositorios git - scryer-prolog.git/commitdiff
add multifile and module scoped predicates
authorMark Thom <[email protected]>
Mon, 3 Feb 2020 05:13:14 +0000 (22:13 -0700)
committerMark Thom <[email protected]>
Mon, 3 Feb 2020 05:13:14 +0000 (22:13 -0700)
18 files changed:
Cargo.lock
Cargo.toml
src/prolog/forms.rs
src/prolog/lib/dcgs.pl
src/prolog/machine/code_repo.rs
src/prolog/machine/code_walker.rs [new file with mode: 0644]
src/prolog/machine/compile.rs
src/prolog/machine/machine_errors.rs
src/prolog/machine/machine_indices.rs
src/prolog/machine/machine_state.rs
src/prolog/machine/mod.rs
src/prolog/machine/modules.rs
src/prolog/machine/system_calls.rs
src/prolog/machine/term_expansion.rs
src/prolog/machine/toplevel.rs
src/prolog/macros.rs
src/prolog/toplevel.pl
src/prolog/write.rs

index ea650ad5f53f5b9a13082878f0efae0a2f493786..60b7c3d6110ae232c9883f225d51960ac00f7e39 100644 (file)
@@ -311,8 +311,7 @@ dependencies = [
 
 [[package]]
 name = "prolog_parser"
-version = "0.8.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
+version = "0.8.38"
 dependencies = [
  "lexical 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-rug-adapter 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -447,7 +446,7 @@ dependencies = [
  "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-rug-adapter 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "prolog_parser 0.8.37 (registry+https://github.com/rust-lang/crates.io-index)",
+ "prolog_parser 0.8.38",
  "ref_thread_local 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rug 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -590,7 +589,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "443c53b3c3531dfcbfa499d8893944db78474ad7a1d87fa2d94d1a2231693ac6"
 "checksum ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7eb5259643245d3f292c7a146b2df53bba24d7eab159410e648eb73dc164669d"
 "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
-"checksum prolog_parser 0.8.37 (registry+https://github.com/rust-lang/crates.io-index)" = "cb3da90085db170f1045f7d6851da12400ab205af9ec2acaa5ee42ab45a25dd8"
 "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
 "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
 "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
index f26c3b3a13661fc7fba09b07668ce0d889d069f6..ef165b352e42540edaeb18e91ca0644339ad95f8 100644 (file)
@@ -24,7 +24,7 @@ libc = "0.2.62"
 nix = "0.15.0"
 num-rug-adapter = { optional = true, version = "0.1.1" }
 ordered-float = "0.5.0"
-prolog_parser = { version = "0.8.37", default-features = false }
+prolog_parser = { version = "0.8.38", path = "../prolog_parser", default-features = false }
 ref_thread_local = "0.0.0"
 rug = { version = "1.4.0", optional = true }
 rustyline = "5.0.3"
index d0c02028886db95c05868aaeb779b267a2a2f270..9897d9ce5545f9d8a20a0343bfeb14a85248987d 100644 (file)
@@ -31,26 +31,6 @@ pub enum TopLevel {
 }
 
 impl TopLevel {
-    pub fn name(&self) -> Option<ClauseName> {
-        match self {
-            &TopLevel::Declaration(_) => None,
-            &TopLevel::Fact(ref term, ..) => term.name(),
-            &TopLevel::Predicate(ref clauses) => clauses.0.first().and_then(|ref term| term.name()),
-            &TopLevel::Query(_) => None,
-            &TopLevel::Rule(Rule { ref head, .. }, ..) => Some(head.0.clone()),
-        }
-    }
-
-    pub fn arity(&self) -> usize {
-        match self {
-            &TopLevel::Declaration(_) => 0,
-            &TopLevel::Fact(ref term, ..) => term.arity(),
-            &TopLevel::Predicate(ref clauses) => clauses.0.first().map(|t| t.arity()).unwrap_or(0),
-            &TopLevel::Query(_) => 0,
-            &TopLevel::Rule(Rule { ref head, .. }, ..) => head.1.len(),
-        }
-    }
-
     pub fn is_end_of_file_atom(&self) -> bool {
         match self {
             &TopLevel::Fact(Term::Constant(_, Constant::Atom(ref name, _)), ..) => {
@@ -159,6 +139,190 @@ impl ListingSource {
     }
 }
 
+fn resolved_term_and_module(term: &Term) -> Option<(ClauseName, ClauseName)>
+{
+    match term {
+        Term::Clause(_, ref name, ref terms, _) => {
+            if name.as_str() == ":" && terms.len() == 2 {
+                let module_name = match terms[0].as_ref() {
+                    &Term::Constant(_, Constant::Atom(ref module_name, _)) => {
+                        module_name.clone()
+                    }
+                    _ => {
+                        return Some((name.owning_module(), name.clone()));
+                    }
+                };
+
+                match terms[1].as_ref() {
+                    Term::Clause(_, ref name, ..)
+                  | Term::Constant(_, Constant::Atom(ref name, ..)) => {
+                        return Some((module_name, name.clone()));
+                    }
+                    _ => {
+                    }
+                }
+
+                Some((name.owning_module(), name.clone()))
+            } else {
+                Some((name.owning_module(), name.clone()))
+            }
+        }
+        Term::Constant(_, Constant::Atom(ref name, _)) => {
+            Some((name.owning_module(), name.clone()))
+        }
+        _ => {
+            None
+        }
+    }
+}
+
+fn resolved_term_arity(term: &Term) -> usize
+{
+    match term {
+        Term::Clause(_, ref name, ref terms, _) => {
+            if name.as_str() == ":" && terms.len() == 2 {
+                match terms[0].as_ref() {
+                    &Term::Constant(_, Constant::Atom(..)) => {
+                    }
+                    _ => {
+                        return 2;
+                    }
+                }
+
+                match terms[1].as_ref() {
+                    Term::Clause(_, _, ref terms, _) => {
+                        terms.len()
+                    }
+                    Term::Constant(_, Constant::Atom(..)) => {
+                        0
+                    }
+                    _ => {
+                        2
+                    }
+                }
+            } else {
+                terms.len()
+            }
+        }
+        _ => {
+            0
+        }
+    }
+}
+
+pub trait ClauseConsistency {
+    fn is_consistent(&self, clauses: &Vec<PredicateClause>) -> bool {
+        match clauses.first() {
+            Some(ref cl) => {
+                self.name_and_module() == cl.name_and_module() && self.arity() == cl.arity()
+            }
+            None => {
+                true
+            }
+        }
+    }
+
+    fn name_and_module(&self) -> Option<(ClauseName, ClauseName)>;
+    fn arity(&self) -> usize;
+}
+
+/* Of course '$current_module$' isn't the name of the current
+ * module. It'll do if no module is explicitly specified through
+ * (:)/2.
+ */
+impl ClauseConsistency for Term {
+    fn name_and_module(&self) -> Option<(ClauseName, ClauseName)>
+    {
+        match self {
+            Term::Clause(_, ref name, ref terms, _) =>
+                match name.as_str() {
+                    ":-" => {
+                        match terms.len() {
+                            1 => None, // a declaration.
+                            2 => resolved_term_and_module(&terms[0]),
+                            _ => Some((name.owning_module(), clause_name!(":-"))),
+                        }
+                    }
+                    _ => {
+                        resolved_term_and_module(self)
+                    }
+                },
+            Term::Constant(_, Constant::Atom(ref name, _)) => {
+                Some((name.owning_module(), name.clone()))
+            }
+            _ => {
+                None
+            }
+        }
+    }
+
+    fn arity(&self) -> usize {
+        match self {
+            Term::Clause(_, ref name, ref terms, _) =>
+                match name.as_str() {
+                    ":-" => {
+                        match terms.len() {
+                            1 => 0,
+                            2 => resolved_term_arity(&terms[0]),
+                            _ => terms.len(),
+                        }
+                    }
+                    _ => {
+                        resolved_term_arity(self)
+                    }
+                },
+            _ => {
+                0
+            }
+        }
+    }
+}
+
+impl ClauseConsistency for Rule {
+    fn name_and_module(&self) -> Option<(ClauseName, ClauseName)> {
+        Some((self.head.0.owning_module(), self.head.0.clone()))
+    }
+    
+    fn arity(&self) -> usize {
+        self.head.1.len()    
+    }
+}
+
+impl ClauseConsistency for PredicateClause {
+    fn name_and_module(&self) -> Option<(ClauseName, ClauseName)> {
+        match self {
+            &PredicateClause::Fact(ref term, ..) => {
+                term.name_and_module()
+                    .map(|(_, name)| (name.owning_module(), name))
+            }
+            &PredicateClause::Rule(ref rule, ..) => {
+                rule.name_and_module()
+            }
+        }
+    }
+
+    fn arity(&self) -> usize {
+        match self {
+            &PredicateClause::Fact(ref term, ..) => {
+                term.arity()
+            }
+            &PredicateClause::Rule(ref rule, ..) => {
+                rule.arity()
+            }
+        }
+    }
+}
+
+impl ClauseConsistency for Predicate {
+    fn name_and_module(&self) -> Option<(ClauseName, ClauseName)> {
+        self.0.first().and_then(|clause| clause.name_and_module())
+    }
+
+    fn arity(&self) -> usize {
+        self.0.first().map(|clause| clause.arity()).unwrap_or(0)
+    }
+}
+
 pub type CompiledResult = (Predicate, VecDeque<TopLevel>);
 
 #[derive(Clone)]
@@ -177,8 +341,24 @@ impl PredicateClause {
 
     pub fn arity(&self) -> usize {
         match self {
-            &PredicateClause::Fact(ref term, ..) => term.arity(),
-            &PredicateClause::Rule(ref rule, ..) => rule.head.1.len(),
+            &PredicateClause::Fact(ref term, ..) => {
+                term.arity()
+            }
+            &PredicateClause::Rule(ref rule, ..) => {
+                if rule.head.0.as_str() == ":" && rule.head.1.len() == 2 {
+                    match (rule.head.1)[0].as_ref() {
+                        &Term::Constant(_, Constant::Atom(..)) => {
+                        }
+                        _ => {
+                            return 2;
+                        }
+                    }
+
+                    (rule.head.1)[1].arity()
+                } else {
+                    rule.head.1.len()
+                }
+            }
         }
     }
 
@@ -196,6 +376,14 @@ pub enum ModuleSource {
     File(ClauseName),
 }
 
+pub type ScopedPredicateKey = (ClauseName, PredicateKey); // module name, predicate indicator.
+
+#[derive(Clone)]
+pub enum MultiFileIndicator {
+    LocalScoped(ClauseName, usize), // name, arity
+    ModuleScoped(ScopedPredicateKey),
+}
+
 #[derive(Clone)]
 pub enum Declaration {
     Dynamic(ClauseName, usize), // name, arity
@@ -203,7 +391,7 @@ pub enum Declaration {
     Hook(CompileTimeHook, PredicateClause, VecDeque<TopLevel>),
     ModuleInitialization(Vec<QueryTerm>, VecDeque<TopLevel>), // goal
     Module(ModuleDecl),
-    MultiFile(ClauseName, usize),
+    MultiFile(MultiFileIndicator),
     NonCountedBacktracking(ClauseName, usize), // name, arity
     Op(OpDecl),
     UseModule(ModuleSource),
@@ -314,11 +502,11 @@ pub fn fetch_op_spec(
     op_dir: &OpDir,
 ) -> Option<SharedOpDesc> {
     if let Some(ref op_desc) = &spec {
-        if op_desc.arity() != arity {            
+        if op_desc.arity() != arity {
             /* it's possible to extend operator functors with
              * additional terms. When that happens,
              * void the op_spec by returning None. */
-            return None; 
+            return None;
         }
     }
 
@@ -358,7 +546,7 @@ pub type ModuleDir = IndexMap<ClauseName, Module>;
 #[derive(Clone, PartialEq)]
 pub enum ModuleExport {
     OpDecl(OpDecl),
-    PredicateKey(PredicateKey),    
+    PredicateKey(PredicateKey),
 }
 
 #[derive(Clone)]
index e144cd0d4f7a901fbd02f25faafdb64af32b0501..20997551f5014b478655d3a5f35df6328c3e9186 100644 (file)
@@ -44,6 +44,17 @@ phrase_(phrase(NonTerminal), S0, S) :-
 phrase_([T|Ts], S0, S) :-
     append([T|Ts], S, S0).
 
+% The same version of the below two dcg_rule clauses, but with module scoping.
+dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :-
+    dcg_non_terminal(NonTerminal, S0, S, Head),
+    dcg_body(GRBody, S0, S1, Goal1),
+    dcg_terminals(Terminals, S, S1, Goal2),
+    Body = ( Goal1, Goal2 ).
+dcg_rule(( M:NonTerminal --> GRBody ), ( M:Head :- Body )) :-
+    NonTerminal \= ( _, _ ),
+    dcg_non_terminal(NonTerminal, S0, S, Head),
+    dcg_body(GRBody, S0, S, Body).
+
 % This program uses append/3 as defined in the Prolog prologue.
 % Expands a DCG rule into a Prolog rule, when no error condition applies.
 dcg_rule(( NonTerminal, Terminals --> GRBody ), ( Head :- Body )) :-
index fee126e9f3c66cb0a8422755dcb749a658b64fe9..ac780f38866cbb540e6e3bf397527f68292ed384 100644 (file)
@@ -9,7 +9,10 @@ use crate::prolog::machine::compile::*;
 use crate::prolog::machine::machine_errors::*;
 use crate::prolog::machine::machine_indices::*;
 
+use indexmap::IndexSet;
+
 use std::collections::VecDeque;
+use std::mem;
 
 pub struct CodeRepo {
     pub(super) cached_query: Code,
@@ -59,11 +62,13 @@ impl CodeRepo {
             .unwrap_or((Predicate::new(), VecDeque::from(vec![])))
     }
 
-    pub fn add_in_situ_result(
+    pub(crate) fn add_in_situ_result(
         &mut self,
         result: &CompiledResult,
         in_situ_code_dir: &mut InSituCodeDir,
+        in_situ_module_dir: &mut ModuleStubDir,
         flags: MachineFlags,
+        non_counted_bt_preds: &IndexSet<PredicateKey>,
     ) -> Result<(), SessionError> {
         let (ref decl, ref queue) = result;
         let (name, arity) = decl
@@ -75,17 +80,26 @@ impl CodeRepo {
             })
             .ok_or(SessionError::NamelessEntry)?;
 
+        let non_counted_bt = non_counted_bt_preds.contains(&(name.clone(), arity));
+        let module_name = name.owning_module();
+
         let p = self.in_situ_code.len();
-        in_situ_code_dir.insert((name, arity), p);
 
-        let mut cg = CodeGenerator::<DebrayAllocator>::new(true, flags);
-        // clone the decl to avoid the need to wipe its register cells later.
-        let mut decl_code = cg.compile_predicate(&decl.0.clone())?;
+        match in_situ_module_dir.get_mut(&module_name) {
+            Some(ref mut module_stub) if name.has_table(&module_stub.atom_tbl) => {
+                module_stub.in_situ_code_dir.insert((name, arity), p);
+            }
+            _ => {
+                in_situ_code_dir.insert((name, arity), p);
+            }
+        }        
+
+        let mut cg = CodeGenerator::<DebrayAllocator>::new(non_counted_bt, flags);
+        let mut decl_code = cg.compile_predicate(&decl.0)?;
 
-        compile_appendix(&mut decl_code, queue, true, flags)?;
+        compile_appendix(&mut decl_code, queue, non_counted_bt, flags)?;
 
-        self.in_situ_code.extend(decl_code.into_iter());
-        Ok(())
+        Ok(self.in_situ_code.extend(decl_code.into_iter()))
     }
 
     #[inline]
@@ -93,6 +107,11 @@ impl CodeRepo {
         self.cached_query.len()
     }
 
+    #[inline]
+    pub(super) fn take_in_situ_code(&mut self) -> Code {
+        mem::replace(&mut self.in_situ_code, Code::new())
+    }
+
     pub(super) fn lookup_instr<'a>(
         &'a self,
         last_call: bool,
diff --git a/src/prolog/machine/code_walker.rs b/src/prolog/machine/code_walker.rs
new file mode 100644 (file)
index 0000000..1237498
--- /dev/null
@@ -0,0 +1,81 @@
+use crate::prolog::instructions::*;
+
+use std::collections::VecDeque;
+
+fn scan_for_trust_me(code: &Code, jmp_offsets: &mut VecDeque<usize>, after_idx: &mut usize) {
+    for (idx, instr) in code[*after_idx..].iter().enumerate() {
+        match instr {
+            &Line::Choice(ChoiceInstruction::TrustMe)
+          | &Line::IndexedChoice(IndexedChoiceInstruction::Trust(..)) => {
+                *after_idx += idx;
+                return;
+            }
+            &Line::Control(ControlInstruction::JmpBy(_, offset, ..)) => {
+                jmp_offsets.push_back(*after_idx + idx + offset)
+            }
+            _ => {}
+        }
+    }
+}
+
+fn capture_next_range(code: &Code, queue: &mut VecDeque<usize>, last_idx: &mut usize) {
+    loop {
+        match &code[*last_idx] {
+            &Line::Choice(ChoiceInstruction::TryMeElse(..))
+          | &Line::IndexedChoice(IndexedChoiceInstruction::Try(..)) => {
+                    *last_idx += 1;
+                    scan_for_trust_me(code, queue, last_idx);
+                }
+            &Line::Control(ControlInstruction::JmpBy(_, offset, _, false)) => {
+                queue.push_back(*last_idx + offset);
+                *last_idx += 1;
+            }
+            &Line::Control(ControlInstruction::JmpBy(_, offset, _, true)) => {
+                queue.push_back(*last_idx + offset);
+                break;
+            }
+            &Line::Control(ControlInstruction::Proceed)
+          | &Line::Control(ControlInstruction::CallClause(_, _, _, true, _)) =>
+                break,
+            _ =>
+                *last_idx += 1,
+        };
+    }
+}
+
+/* This function walks the code of a single predicate, supposed to
+ * begin in code at the offset p. Each instruction is passed to the
+ * walker function.
+ */
+pub fn walk_code(code: &Code, p: usize, mut walker: impl FnMut(&Line))
+{
+    let mut queue = VecDeque::from(vec![p]);
+
+    while let Some(first_idx) = queue.pop_front() {
+        let mut last_idx = first_idx;
+
+        capture_next_range(code, &mut queue, &mut last_idx);
+
+        for instr in &code[first_idx .. last_idx + 1] {
+            walker(instr);
+        }
+    }
+}
+
+/* A function for code walking that might result in modification to
+ * the code. Otherwise identical to walk_code.
+ */
+pub fn walk_code_mut(code: &mut Code, p: usize, mut walker: impl FnMut(&mut Line))
+{
+    let mut queue = VecDeque::from(vec![p]);
+
+    while let Some(first_idx) = queue.pop_front() {
+        let mut last_idx = first_idx;
+
+        capture_next_range(code, &mut queue, &mut last_idx);
+
+        for instr in &mut code[first_idx .. last_idx + 1] {
+            walker(instr);
+        }
+    }
+}
index e026b25dc92b8f231254ad179074f6f9d3c654c0..efb62cda764deee06c5f34984149bd908041e944 100644 (file)
@@ -7,6 +7,7 @@ use crate::prolog::debray_allocator::*;
 use crate::prolog::forms::*;
 use crate::prolog::instructions::*;
 use crate::prolog::iterators::*;
+use crate::prolog::machine::code_walker::*;
 use crate::prolog::machine::machine_errors::*;
 use crate::prolog::machine::machine_indices::*;
 use crate::prolog::machine::term_expansion::ExpansionAdditionResult;
@@ -367,24 +368,36 @@ fn compile_into_module_impl<R: Read>(
     wam.code_repo.compile_hook(CompileTimeHook::TermExpansion, flags)?;
     wam.code_repo.compile_hook(CompileTimeHook::GoalExpansion, flags)?;
 
-    let results = compiler.gather_items(wam, src, &mut indices)?;
-    let module_code =
-        compiler.generate_code(results.worker_results, wam, &mut indices.code_dir, 0)?;
+    let mut results = compiler.gather_items(wam, src, &mut indices)?;
 
-    let mut clause_code_generator = ClauseCodeGenerator::new(module_code.len(), module_name.clone());
+    compiler.adapt_in_situ_code(
+        results.worker_results,
+        wam,
+        &mut indices.code_dir,
+        &mut indices.module_dir,
+        &mut results.in_situ_code,
+        &results.in_situ_code_dir,
+        &results.in_situ_module_dir,
+    )?;
+
+    let mut clause_code_generator = ClauseCodeGenerator::new(
+        results.in_situ_code.len(),
+        module_name.clone()
+    );
 
     clause_code_generator.generate_clause_code(&results.dynamic_clause_map, wam)?;
 
     let top_level_term_dir = results.top_level_term_dirs.consolidate();
-    
-    add_module_code(
+
+    add_module(
         wam,
         compiler.module.take().unwrap(),
-        module_code,
         indices,
         top_level_term_dir,
     );
-    
+
+    wam.code_repo.code.extend(results.in_situ_code.into_iter());
+
     clause_code_generator.add_clause_code(wam, results.dynamic_clause_map);
 
     Ok(compiler.drop_expansions(wam.machine_flags(), &mut wam.code_repo))
@@ -399,6 +412,9 @@ pub struct GatherResult {
     top_level_terms: Vec<(Term, usize, usize)>,
     top_level_term_dirs: TermDirQuantum,
     module_term_dirs: TermDirQuantum,
+    in_situ_code_dir: InSituCodeDir,
+    in_situ_code: Code,
+    in_situ_module_dir: ModuleStubDir,
 }
 
 pub struct ClauseCodeGenerator {
@@ -490,8 +506,24 @@ impl ClauseCodeGenerator {
     }
 }
 
+fn insert_or_refresh_term_dir_quantum(
+    term_dir: &TermDir,
+    key: PredicateKey,
+    term_dirs: &mut TermDirQuantum
+) {
+    match term_dir.get(&key) {
+        Some((ref preds, ref queue)) => {
+            let entry = TermDirQuantumEntry::from(preds, queue);
+            term_dirs.insert_or_refresh(key, entry);
+        }
+        None => {
+            let entry = TermDirQuantumEntry::from(&Predicate::new(), &VecDeque::new());
+            term_dirs.insert_or_refresh(key, entry);
+        }
+    }
+}
+
 pub struct ListingCompiler {
-    non_counted_bt_preds: IndexSet<PredicateKey>,
     module: Option<Module>,
     user_term_dir: TermDir,
     orig_term_expansion_lens: (usize, usize),
@@ -501,23 +533,22 @@ pub struct ListingCompiler {
     listing_src: ListingSource, // a file? a module?
 }
 
-fn add_toplevel_code(
+fn add_toplevel(
     wam: &mut Machine,
-    code: Code,
     indices: IndexStore,
-    term_dir: TermDir,    
+    term_dir: TermDir,
 ) {
-    wam.add_batched_code(code, indices.code_dir);
+    wam.add_batched_code_dir(indices.code_dir);
     wam.add_batched_ops(indices.op_dir);
+    wam.add_in_situ_module_dir(indices.module_dir);
 
     wam.code_repo.term_dir.extend(term_dir.into_iter());
 }
 
 #[inline]
-fn add_module_code(
+fn add_module(
     wam: &mut Machine,
     mut module: Module,
-    code: Code,
     indices: IndexStore,
     term_dir: TermDir,
 ) {
@@ -525,7 +556,8 @@ fn add_module_code(
     module.op_dir.extend(indices.op_dir.into_iter());
     module.term_dir.extend(term_dir.into_iter());
 
-    wam.add_module(module, code);
+    wam.add_in_situ_module_dir(indices.module_dir);
+    wam.add_module(module);
 }
 
 fn add_non_module_code(
@@ -540,7 +572,8 @@ fn add_non_module_code(
     let mut clause_code_generator = ClauseCodeGenerator::new(code.len(), clause_name!("user"));
     clause_code_generator.generate_clause_code(&dynamic_clause_map, wam)?;
 
-    add_toplevel_code(wam, code, indices, term_dir);
+    add_toplevel(wam, indices, term_dir);
+    wam.code_repo.code.extend(code.into_iter());
     clause_code_generator.add_clause_code(wam, dynamic_clause_map);
 
     Ok(())
@@ -580,7 +613,6 @@ impl ListingCompiler {
         listing_src: ListingSource,
     ) -> Self {
         ListingCompiler {
-            non_counted_bt_preds: IndexSet::new(),
             module: None,
             user_term_dir: TermDir::new(),
             orig_term_expansion_lens: code_repo
@@ -593,31 +625,37 @@ 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.
+    /* 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) {
+    fn localize_self_calls(&mut self, key: PredicateKey, code: &mut Code, p: usize, target_p: usize)
+    {
+        let (name, arity) = key;
+
         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_name, ref shared_op_desc, ref mut idx)
-                        if op_name == &name && shared_op_desc.arity() == arity =>
-                    {
-                        *idx = self_idx.clone();
+        set_code_index!(self_idx, IndexPtr::Index(target_p), self.get_module_name());
+
+        walk_code_mut(code, p, |instr|
+            match instr {
+                Line::Control(ControlInstruction::CallClause(ref mut ct, ..)) => {
+                    match ct {
+                        ClauseType::Named(ref ct_name, ct_arity, ref mut idx)
+                            if ct_name == &name && arity == *ct_arity =>
+                        {
+                            *idx = self_idx.clone();
+                        }
+                        ClauseType::Op(ref op_name, ref shared_op_desc, ref mut idx)
+                            if op_name == &name && shared_op_desc.arity() == arity =>
+                        {
+                            *idx = self_idx.clone();
+                        }
+                        _ => {}
                     }
-                    _ => {}
                 }
-            }
-        }
+                _ => {}
+            },
+        );
     }
 
     fn use_module(
@@ -707,45 +745,103 @@ impl ListingCompiler {
            .map_err(SessionError::from)
     }
 
-    pub(crate) fn generate_code(
+    fn set_code_index(
         &mut self,
-        decls: Vec<PredicateCompileQueue>,
         wam: &Machine,
+        key: PredicateKey,
+        in_situ_code: &mut Code,
         code_dir: &mut CodeDir,
-        code_offset: usize,
-    ) -> Result<Code, SessionError> {
-        let mut code = vec![];
-
-        for (decl, queue) in decls {
-            let (name, arity) = decl
-                .predicate_indicator()
-                .ok_or(SessionError::NamelessEntry)?;
-            
-            let non_counted_bt = self.non_counted_bt_preds.contains(&(name.clone(), arity));
+        in_situ_code_dir: &InSituCodeDir,
+        decl: PredicateCompileQueue,
+    ) -> Result<(), SessionError> {
+        let p = wam.code_repo.code.len();
 
-            let p = code.len() + wam.code_repo.code.len() + code_offset;
-            let mut cg = CodeGenerator::<DebrayAllocator>::new(non_counted_bt, wam.machine_flags());
+        let idx = code_dir
+            .entry(key.clone())
+            .or_insert(CodeIndex::default());
 
-            let decl = TopLevel::Predicate(decl);
-            let mut decl_code = compile_relation(&mut cg, &decl)?;
+        Ok(match in_situ_code_dir.get(&key) {
+            Some(in_situ_p) => {
+                set_code_index!(idx, IndexPtr::Index(p + *in_situ_p), self.get_module_name());
+                self.localize_self_calls(key, in_situ_code, *in_situ_p, p + *in_situ_p);
+            }
+            None => {
+                let flags = wam.machine_flags();
+                let (decl, queue) = decl;
 
-            compile_appendix(&mut decl_code, &queue, non_counted_bt, wam.machine_flags())?;
+                let mut cg = CodeGenerator::<DebrayAllocator>::new(false, flags);
+                let mut decl_code = cg.compile_predicate(&decl.0)?;
 
-            let idx = code_dir
-                .entry((name.clone(), arity))
-                .or_insert(CodeIndex::default());
+                compile_appendix(&mut decl_code, &queue, false, flags)?;
 
-            set_code_index!(idx, IndexPtr::Index(p), self.get_module_name());
+                let in_situ_p = in_situ_code.len();
 
-            self.localize_self_calls(name, arity, &mut decl_code, p);
-            code.extend(decl_code.into_iter());
-        }
+                in_situ_code.extend(decl_code.into_iter());
 
-        Ok(code)
+                set_code_index!(idx, IndexPtr::Index(p + in_situ_p), self.get_module_name());
+                self.localize_self_calls(key, in_situ_code, in_situ_p, p + in_situ_p);
+            }
+        })
     }
 
-    fn add_non_counted_bt_flag(&mut self, name: ClauseName, arity: usize) {
-        self.non_counted_bt_preds.insert((name, arity));
+    fn adapt_in_situ_code(
+        &mut self,
+        decls: Vec<PredicateCompileQueue>,
+        wam: &Machine,
+        code_dir: &mut CodeDir,
+        module_dir: &mut ModuleDir,
+        in_situ_code: &mut Code,
+        in_situ_code_dir: &InSituCodeDir,
+        in_situ_module_dir: &ModuleStubDir,
+    ) -> Result<(), SessionError> {
+        for decl in decls {
+            let key = decl.0
+                .predicate_indicator()
+                .ok_or(SessionError::NamelessEntry)?;
+
+            let (name, _arity) = key.clone();
+            let module_name = name.owning_module();
+
+            match in_situ_module_dir.get(&module_name) {
+                Some(ref module_stub) if name.has_table(&module_stub.atom_tbl) => {
+                    let module =
+                        module_dir.entry(module_name.clone())
+                                  .or_insert_with(|| {
+                                      let module_decl = ModuleDecl {
+                                          name: module_name.clone(),
+                                          exports: vec![]
+                                      };
+
+                                      Module::new(
+                                          module_decl,
+                                          module_stub.atom_tbl.clone(),
+                                          self.listing_src.clone(),
+                                      )
+                                  });
+
+                    self.set_code_index(
+                        wam,
+                        key,
+                        in_situ_code,
+                        &mut module.code_dir,
+                        &module_stub.in_situ_code_dir,
+                        decl,
+                    )?;
+                }
+                _ => {
+                    self.set_code_index(
+                        wam,
+                        key,
+                        in_situ_code,
+                        code_dir,
+                        in_situ_code_dir,
+                        decl,
+                    )?;
+                }
+            }
+        }
+
+        Ok(())
     }
 
     fn add_term_dir_terms(
@@ -822,6 +918,7 @@ impl ListingCompiler {
         wam: &mut Machine,
         indices: &mut IndexStore,
         flags: MachineFlags,
+        non_counted_bt_preds: &mut IndexSet<PredicateKey>,
     ) -> Result<(), SessionError> {
         match decl {
             Declaration::Dynamic(..) => {
@@ -872,7 +969,8 @@ impl ListingCompiler {
                 Ok(())
             }
             Declaration::NonCountedBacktracking(name, arity) => {
-                Ok(self.add_non_counted_bt_flag(name, arity))
+                non_counted_bt_preds.insert((name, arity));
+                Ok(())
             }
             Declaration::Op(op_decl) => {
                 self.submit_op(wam, indices, &op_decl)
@@ -929,50 +1027,35 @@ impl ListingCompiler {
 
     fn setup_multifile_decl<R: Read>(
         &self,
-        name: ClauseName,
-        arity: usize,
-        worker: &mut TopLevelBatchWorker<R>
+        indicator: MultiFileIndicator,
+        worker: &mut TopLevelBatchWorker<R>,
     ) -> Result<(), SessionError> {
-        let module_name = name.owning_module();
+        match indicator {
+            MultiFileIndicator::LocalScoped(name, arity) => {
+                let term_dir  = &worker.term_stream.wam.code_repo.term_dir;
+                let key       = (name, arity);
+                let term_dirs = &mut worker.term_dirs;
 
-        let term_dir = match module_name.as_str() {
-            "user" => {
-                &worker.term_stream.wam.code_repo.term_dir
+                insert_or_refresh_term_dir_quantum(term_dir, key, term_dirs);
             }
-            _ => {
-                if let Some(ref module) = self.module {
-                    &module.term_dir
-                } else {
-                    match worker.term_stream.wam.indices.modules.get(&module_name) {
-                        Some(ref module) => {
-                            &module.term_dir
-                        }
-                        None => {
-                            return Err(SessionError::ModuleNotFound);
-                        }
+            MultiFileIndicator::ModuleScoped((module_name, key)) => {
+                match worker.term_stream.wam.indices.modules.get(&module_name) {
+                    Some(ref module) => {
+                        let term_dir  = &module.term_dir;
+                        let term_dirs = worker.intra_module_term_dirs
+                            .entry(module_name)
+                            .or_insert(TermDirQuantum::new());
+
+                        insert_or_refresh_term_dir_quantum(term_dir, key, term_dirs);
+                    }
+                    None => {
+                        return Err(SessionError::ModuleNotFound);
                     }
                 }
             }
         };
 
-        Ok(match term_dir.get(&(name.clone(), arity)) {
-            Some((ref preds, ref queue)) => {
-                let (preds, queue) = (preds.clone(), queue.clone());
-
-                worker.term_dirs.set_old(
-                    (name.clone(), arity),
-                    preds,
-                    queue,
-                );
-            }
-            None => {
-                worker.term_dirs.set_old(
-                    (name.clone(), arity),
-                    Predicate::new(),
-                    VecDeque::new(),                        
-                );
-            }
-        })
+        Ok(())
     }
 
     fn process_and_commit_decl<R: Read>(
@@ -1001,8 +1084,8 @@ impl ListingCompiler {
             &Declaration::Hook(hook, _, ref queue) if !hook.has_module_scope() => {
                 worker.term_stream.incr_expansion_lens(hook, 1, queue.len())
             }
-            &Declaration::MultiFile(ref name, arity) => {
-                self.setup_multifile_decl(name.clone(), arity, worker)?;
+            &Declaration::MultiFile(ref indicator) => {
+                self.setup_multifile_decl(indicator.clone(), worker)?;
             }
             &Declaration::UseModule(_) | &Declaration::UseQualifiedModule(..) => {
                 update_expansion_lengths = true
@@ -1010,7 +1093,13 @@ impl ListingCompiler {
             _ => {}
         };
 
-        let result = self.process_decl(decl, &mut worker.term_stream.wam, indices, flags);
+        let result = self.process_decl(
+            decl,
+            &mut worker.term_stream.wam,
+            indices,
+            flags,
+            &mut worker.non_counted_bt_preds,
+        );
 
         if update_expansion_lengths {
             worker.term_stream.update_expansion_lens();
@@ -1018,7 +1107,7 @@ impl ListingCompiler {
 
         result
     }
-    
+
     pub(crate) fn gather_items<R: Read>(
         &mut self,
         wam: &mut Machine,
@@ -1040,10 +1129,11 @@ impl ListingCompiler {
                 mem::swap(&mut worker.results, &mut toplevel_results);
                 worker.in_module = true;
 
-                self.process_and_commit_decl(decl, &mut worker, 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());
+
                     top_level_term_dirs = mem::replace(
                         &mut worker.term_dirs,
                         TermDirQuantum::new(),
@@ -1056,7 +1146,7 @@ impl ListingCompiler {
             }
         }
 
-        let addition_results = worker.term_stream.rollback_expansion_code()?;        
+        let addition_results = worker.term_stream.rollback_expansion_code()?;
 
         let module_term_dirs = if self.module.is_some() {
             worker.term_dirs
@@ -1064,7 +1154,7 @@ impl ListingCompiler {
             top_level_term_dirs = worker.term_dirs;
             TermDirQuantum::new()
         };
-        
+
         Ok(GatherResult {
             worker_results: worker.results,
             dynamic_clause_map: worker.dynamic_clause_map,
@@ -1074,6 +1164,9 @@ impl ListingCompiler {
             top_level_terms: worker.term_stream.top_level_terms(),
             top_level_term_dirs,
             module_term_dirs,
+            in_situ_code_dir: worker.term_stream.wam.indices.take_in_situ_code_dir(),
+            in_situ_code: worker.term_stream.wam.code_repo.take_in_situ_code(),
+            in_situ_module_dir: worker.term_stream.wam.indices.take_in_situ_module_dir(),
         })
     }
 
@@ -1119,18 +1212,29 @@ fn compile_work_impl(
     let top_level_term_dir = results.top_level_term_dirs.consolidate();
     let module_term_dir = results.module_term_dirs.consolidate();
 
-    let module_code = compiler.generate_code(
+    let mut code = results.in_situ_code;
+
+    let in_situ_code_dir = results.in_situ_code_dir;
+    let in_situ_module_dir = results.in_situ_module_dir;
+
+    compiler.adapt_in_situ_code(
         results.worker_results,
         wam,
         &mut indices.code_dir,
-        0
+        &mut indices.module_dir,
+        &mut code,
+        &in_situ_code_dir,
+        &in_situ_module_dir,
     )?;
 
-    let toplvl_code = compiler.generate_code(
+    compiler.adapt_in_situ_code(
         results.toplevel_results,
         wam,
         &mut results.toplevel_indices.code_dir,
-        module_code.len()
+        &mut indices.module_dir,
+        &mut code,
+        &in_situ_code_dir,
+        &in_situ_module_dir,
     )?;
 
     if let Some(ref mut module) = &mut compiler.module {
@@ -1154,8 +1258,7 @@ fn compile_work_impl(
         }
 
         let mut clause_code_generator =
-            ClauseCodeGenerator::new(module_code.len() + toplvl_code.len(),
-                                     module.module_decl.name.clone());
+            ClauseCodeGenerator::new(code.len(), module.module_decl.name.clone());
 
         wam.check_toplevel_code(&results.toplevel_indices)?;
         clause_code_generator.generate_clause_code(&results.dynamic_clause_map, wam)?;
@@ -1165,23 +1268,25 @@ fn compile_work_impl(
         }
 
         if module.is_impromptu_module {
-            add_module_code(wam, module, module_code, indices, module_term_dir);
+            add_module(wam, module, indices, module_term_dir);
 
             let module = wam.indices.take_module(compiler.listing_src.name()).unwrap();
 
             wam.indices.use_module(&mut wam.code_repo, wam.machine_st.flags, &module)?;
             wam.indices.insert_module(module);
         } else {
-            add_module_code(wam, module, module_code, indices, module_term_dir);
+            add_module(wam, module, indices, module_term_dir);
         }
 
-        add_toplevel_code(wam, toplvl_code, results.toplevel_indices, top_level_term_dir);
+        add_toplevel(wam, results.toplevel_indices, top_level_term_dir);
+        wam.code_repo.code.extend(code.into_iter());
+        
         clause_code_generator.add_clause_code(wam, results.dynamic_clause_map);
     } else {
         add_non_module_code(
             wam,
             results.dynamic_clause_map,
-            module_code,
+            code,
             indices,
             top_level_term_dir,
         )?;
@@ -1231,14 +1336,24 @@ pub fn compile_special_form<R: Read>(
     setup_indices(wam, clause_name!("builtins"), &mut indices)?;
 
     let mut compiler = ListingCompiler::new(&wam.code_repo, true, listing_src);
-    let results = compiler.gather_items(wam, src, &mut indices)?;
+    let mut results = compiler.gather_items(wam, src, &mut indices)?;
 
-    let code = compiler.generate_code(results.worker_results, wam, &mut indices.code_dir, 0)?;
-    let p = wam.code_repo.code.len();
+    compiler.adapt_in_situ_code(
+        results.worker_results,
+        wam,
+        &mut indices.code_dir,
+        &mut indices.module_dir,
+        &mut results.in_situ_code,
+        &results.in_situ_code_dir,
+        &results.in_situ_module_dir,
+    )?;
 
+    let p = wam.code_repo.code.len();
     let top_level_term_dir = results.top_level_term_dirs.consolidate();
-    
-    add_toplevel_code(wam, code, indices, top_level_term_dir);
+
+    add_toplevel(wam, indices, top_level_term_dir);
+
+    wam.code_repo.code.extend(results.in_situ_code.into_iter());
 
     Ok(p)
 }
index 0b3507a1b4b266b157bc3d4c1922bf246e51a29b..8670f9bf64d9bfce329cd7f7c206f6908715d763 100644 (file)
@@ -1,6 +1,7 @@
 use prolog_parser::ast::*;
 use prolog_parser::string_list::*;
 
+use crate::prolog::forms::PredicateKey;
 use crate::prolog::machine::machine_indices::*;
 use crate::prolog::machine::machine_state::*;
 use crate::prolog::rug::Integer;
@@ -134,7 +135,7 @@ impl MachineError {
             SessionError::InvalidFileName(filename) => {
                 Self::existence_error(h, ExistenceError::Module(filename))
             }
-            SessionError::ModuleDoesNotContainExport => Self::permission_error(
+            SessionError::ModuleDoesNotContainExport(..) => Self::permission_error(
                 PermissionError::Access,
                 "private_procedure",
                 clause_name!("module_does_not_contain_claimed_export"),
@@ -533,7 +534,7 @@ pub enum SessionError {
     CannotOverwriteBuiltIn(ClauseName),
     CannotOverwriteImport(ClauseName),
     InvalidFileName(ClauseName),
-    ModuleDoesNotContainExport,
+    ModuleDoesNotContainExport(ClauseName, PredicateKey),
     ModuleNotFound,
     NamelessEntry,
     OpIsInfixAndPostFix(ClauseName),
index 0a7a35afe94e86ba121834aa8463757e023f0b53..0d1aac8b1304d030569a603569d640e34318bfda 100644 (file)
@@ -214,6 +214,7 @@ impl HeapCellValue {
 pub enum IndexPtr {
     DynamicUndefined, // a predicate, declared as dynamic, whose location in code is as yet undefined.
     Undefined,
+    InSituDirEntry(usize),
     Index(usize),
     UserGoalExpansion,
     UserTermExpansion
@@ -223,6 +224,11 @@ pub enum IndexPtr {
 pub struct CodeIndex(pub Rc<RefCell<(IndexPtr, ClauseName)>>);
 
 impl CodeIndex {
+    #[inline]
+    pub fn new(ptr: IndexPtr, module_name: ClauseName) -> Self {
+        CodeIndex(Rc::new(RefCell::new(( ptr, module_name ))))
+    }
+
     #[inline]
     pub fn is_undefined(&self) -> bool {
         let index_ptr = &self.0.borrow().0;
@@ -540,17 +546,36 @@ impl Default for DynamicPredicateInfo {
 }
 
 pub type InSituCodeDir = IndexMap<PredicateKey, usize>;
+
 // key type: module name, predicate indicator.
 pub type DynamicCodeDir = IndexMap<(ClauseName, ClauseName, usize), DynamicPredicateInfo>;
 
 pub type GlobalVarDir = IndexMap<ClauseName, (Ball, Option<usize>)>;
 
+pub(crate) struct ModuleStub {
+    pub(crate) atom_tbl: TabledData<Atom>,
+    pub(crate) in_situ_code_dir: InSituCodeDir,
+}
+
+impl ModuleStub {
+    pub(crate) fn new(atom_tbl: TabledData<Atom>) -> Self {
+        ModuleStub {
+            atom_tbl,
+            in_situ_code_dir: InSituCodeDir::new(),
+        }
+    }
+}
+
+pub(crate) type ModuleStubDir = IndexMap<ClauseName, ModuleStub>;
+
 pub struct IndexStore {
     pub(super) atom_tbl: TabledData<Atom>,
     pub(super) code_dir: CodeDir,
+    pub(super) module_dir: ModuleDir,
     pub(super) dynamic_code_dir: DynamicCodeDir,
     pub(super) global_variables: GlobalVarDir,
     pub(super) in_situ_code_dir: InSituCodeDir,
+    pub(super) in_situ_module_dir: ModuleStubDir,
     pub(super) modules: ModuleDir,
     pub(super) op_dir: OpDir,
 }
@@ -607,6 +632,16 @@ impl IndexStore {
         self.dynamic_code_dir.get(&(module, name, arity)).cloned()
     }
 
+    #[inline]
+    pub(crate) fn take_in_situ_module_dir(&mut self) -> ModuleStubDir {
+        mem::replace(&mut self.in_situ_module_dir, ModuleStubDir::new())
+    }
+
+    #[inline]
+    pub fn take_in_situ_code_dir(&mut self) -> InSituCodeDir {
+        mem::replace(&mut self.in_situ_code_dir, InSituCodeDir::new())
+    }
+
     #[inline]
     pub fn take_module(&mut self, name: ClauseName) -> Option<Module> {
         self.modules.swap_remove(&name)
@@ -622,12 +657,13 @@ impl IndexStore {
         IndexStore {
             atom_tbl: TabledData::new(Rc::new("user".to_string())),
             code_dir: CodeDir::new(),
+            module_dir: ModuleDir::new(),
             dynamic_code_dir: DynamicCodeDir::new(),
             global_variables: GlobalVarDir::new(),
             in_situ_code_dir: InSituCodeDir::new(),
+            in_situ_module_dir: ModuleStubDir::new(),
             op_dir: default_op_dir(),
             modules: ModuleDir::new(),
-            //            parsing_stream: readline::parsing_stream(String::new())
         }
     }
 
@@ -680,6 +716,77 @@ impl IndexStore {
 pub type CodeDir = BTreeMap<PredicateKey, CodeIndex>;
 pub type TermDir = IndexMap<PredicateKey, (Predicate, VecDeque<TopLevel>)>;
 
+pub struct TermDirQuantumEntry {
+    pub old_terms: (Predicate, VecDeque<TopLevel>),
+    pub new_terms: (Predicate, VecDeque<TopLevel>),
+    pub is_fresh: bool,
+}
+
+impl TermDirQuantumEntry {
+    #[inline]
+    pub fn new() -> Self {
+        TermDirQuantumEntry {
+            old_terms: (Predicate::new(), VecDeque::new()),
+            new_terms: (Predicate::new(), VecDeque::new()),
+            is_fresh: false,
+        }
+    }
+
+    pub fn from(preds: &Predicate, queue: &VecDeque<TopLevel>) -> Self
+    {
+        let mut entry = TermDirQuantumEntry::new();
+        entry.is_fresh = false;
+
+        (entry.old_terms.0).0.extend(preds.0.iter().cloned());
+        entry.old_terms.1.extend(queue.iter().cloned());
+
+        entry
+    }
+}
+
+pub struct TermDirQuantum(IndexMap<PredicateKey, TermDirQuantumEntry>);
+
+impl TermDirQuantum {
+    #[inline]
+    pub fn new() -> Self {
+        TermDirQuantum(IndexMap::new())
+    }
+
+    #[inline]
+    pub fn insert_or_refresh(&mut self, key: PredicateKey, mut entry: TermDirQuantumEntry) {
+        if let Some(prev_entry) = self.get_mut(&key) {
+            prev_entry.is_fresh = true;
+        } else {
+            entry.is_fresh = true;
+            self.0.insert(key, entry);
+        }
+    }
+
+    #[inline]
+    pub fn insert(&mut self, key: PredicateKey, entry: TermDirQuantumEntry) {
+        self.0.insert(key, entry);
+    }
+
+    #[inline]
+    pub fn get_mut(&mut self, key: &PredicateKey) -> Option<&mut TermDirQuantumEntry> {
+        self.0.get_mut(key)
+    }
+
+    pub fn consolidate(self) -> TermDir {
+        let mut term_dir = TermDir::new();
+
+        for (key, entry) in self.0 {
+            let (preds, queue) =
+                term_dir.entry(key).or_insert((Predicate::new(), VecDeque::new()));
+
+            preds.0.extend((entry.new_terms.0).0.into_iter());
+            queue.extend(entry.new_terms.1.into_iter());
+        }
+
+        term_dir
+    }
+}
+
 #[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
 pub enum CompileTimeHook {
     GoalExpansion,
index 0574910546528bab80e835d99c6f80d082e6d3d9..3aaeeb7496be1860d68e6cce20b6268296b11c62 100644 (file)
@@ -372,6 +372,15 @@ impl MachineState {
 
                     return Ok(());
                 }
+                IndexPtr::InSituDirEntry(p) => {
+                    if last_call {
+                        self.execute_at_index(arity, LocalCodePtr::InSituDirEntry(p));
+                    } else {
+                        self.call_at_index(arity, LocalCodePtr::InSituDirEntry(p));
+                    }
+
+                    return Ok(());                    
+                }
                 _ => {}
             }
         }
@@ -384,19 +393,21 @@ impl MachineState {
     }
 }
 
-fn try_in_situ_lookup(name: ClauseName, arity: usize, indices: &IndexStore) -> Option<usize> {
+fn try_in_situ_lookup(name: ClauseName, arity: usize, indices: &IndexStore) -> Option<usize>
+{
     match indices.in_situ_code_dir.get(&(name.clone(), arity)) {
         Some(p) => Some(*p),
-        None => match indices.code_dir.get(&(name, arity)) {
-            Some(ref idx) => {
-                if let &IndexPtr::Index(p) = &idx.0.borrow().0 {
-                    Some(p)
-                } else {
-                    None
+        None =>
+            match indices.code_dir.get(&(name, arity)) {
+                Some(ref idx) => {
+                    if let IndexPtr::Index(p) = idx.0.borrow().0 {
+                        Some(p)
+                    } else {
+                        None
+                    }
                 }
-            }
-            _ => None,
-        },
+                _ => None,
+            },
     }
 }
 
@@ -633,10 +644,12 @@ pub(crate) trait CallPolicy: Any {
         indices: &IndexStore,
     ) -> CallResult {
         match idx.0.borrow().0 {
-            IndexPtr::DynamicUndefined =>
-                machine_st.fail = true,
-            IndexPtr::Undefined =>
-                return try_in_situ(machine_st, name, arity, indices, false),
+            IndexPtr::DynamicUndefined => {
+                machine_st.fail = true;
+            }
+            IndexPtr::Undefined => {
+                return try_in_situ(machine_st, name, arity, indices, false);
+            }
             IndexPtr::Index(compiled_tl_index) => {
                 machine_st.call_at_index(arity, LocalCodePtr::DirEntry(compiled_tl_index))
             }
@@ -646,6 +659,9 @@ pub(crate) trait CallPolicy: Any {
             IndexPtr::UserGoalExpansion => {
                 machine_st.call_at_index(arity, LocalCodePtr::UserGoalExpansion(0));
             }
+            IndexPtr::InSituDirEntry(p) => {
+                machine_st.call_at_index(arity, LocalCodePtr::InSituDirEntry(p));
+            }
         }
 
         Ok(())
@@ -673,6 +689,9 @@ pub(crate) trait CallPolicy: Any {
             IndexPtr::UserGoalExpansion => {
                 machine_st.execute_at_index(arity, LocalCodePtr::UserGoalExpansion(0));
             }
+            IndexPtr::InSituDirEntry(p) => {
+                machine_st.execute_at_index(arity, LocalCodePtr::InSituDirEntry(p));
+            }
         }
 
         Ok(())
index 14465337c59af35dc3eb3c991ad73f4c1d8dc74f..ede50593ffb1cd82324e48e63125d4633be234e7 100644 (file)
@@ -10,6 +10,7 @@ use crate::prolog::read::*;
 
 mod attributed_variables;
 pub(super) mod code_repo;
+pub mod code_walker;
 pub mod compile;
 mod copier;
 mod dynamic_database;
@@ -109,11 +110,33 @@ impl SubModuleUser for IndexStore {
 
     fn get_code_index(&self, key: PredicateKey, module_name: ClauseName) -> Option<CodeIndex> {
         match module_name.as_str() {
-            "user" | "builtin" => self.code_dir.get(&key).cloned(),
-            _ => self
-              .modules
-              .get(&module_name)
-              .and_then(|ref module| module.code_dir.get(&key).cloned().map(CodeIndex::from))
+            "user" => {
+                self.code_dir.get(&key).cloned()
+            }
+            _ => {
+                match self.in_situ_module_dir.get(&module_name) {
+                    Some(ref module_stub) => {
+                        match module_stub.in_situ_code_dir.get(&key) {
+                            Some(p) => {
+                                return Some(CodeIndex::new(
+                                    IndexPtr::InSituDirEntry(*p),
+                                    module_name.clone()
+                                ));
+                            }
+                            None => {
+                            }
+                        }
+                    }
+                    None => {
+                    }
+                };
+
+                self.modules
+                    .get(&module_name)
+                    .and_then(|ref module| {
+                        module.code_dir.get(&key).cloned()
+                    })
+            }
         }
     }
 
@@ -333,7 +356,7 @@ impl Machine {
             default_index_store!(atom_tbl.clone()),
             true,
             ListingSource::from_file_and_path(
-                clause_name!("builtins.pl"), 
+                clause_name!("builtins.pl"),
                 lib_path.clone(),
             ),
         );
@@ -348,7 +371,7 @@ impl Machine {
         );
         compile_user_module(&mut wam, parsing_stream(LISTS.as_bytes()), true,
                             ListingSource::from_file_and_path(
-                                clause_name!("lists"), 
+                                clause_name!("lists"),
                                 lib_path.clone(),
                             ),
         );
@@ -360,7 +383,7 @@ impl Machine {
         );
         compile_user_module(&mut wam, parsing_stream(SI.as_bytes()), true,
                             ListingSource::from_file_and_path(
-                                clause_name!("si"), 
+                                clause_name!("si"),
                                 lib_path.clone(),
                             )
         );
@@ -418,10 +441,10 @@ impl Machine {
         Ok(())
     }
 
-    pub fn add_batched_code(&mut self, code: Code, code_dir: CodeDir) {
+    pub(crate) fn add_batched_code_dir(&mut self, code_dir: CodeDir) {
         // error detection has finished, so update the master index of keys.
         for (key, idx) in code_dir {
-            if let Some(ref mut master_idx) = self.indices.code_dir.get_mut(&key) {
+            if let Some(ref master_idx) = self.indices.code_dir.get(&key) {
                 // ensure we don't double borrow if master_idx == idx.
                 // we don't need to modify anything in that case.
                 if !Rc::ptr_eq(&master_idx.0, &idx.0) {
@@ -433,21 +456,37 @@ impl Machine {
 
             self.indices.code_dir.insert(key, idx);
         }
-
-        self.code_repo.code.extend(code.into_iter());
     }
 
     #[inline]
-    pub fn add_batched_ops(&mut self, op_dir: OpDir) {
+    pub(crate) fn add_batched_ops(&mut self, op_dir: OpDir) {
         self.indices.op_dir.extend(op_dir.into_iter());
     }
 
+    pub(crate) fn add_in_situ_module_dir(&mut self, module_dir: ModuleDir) {
+        for (module_name, module_skeleton) in module_dir {
+            match self.indices.modules.get_mut(&module_name) {
+                Some(ref mut module) => {
+                    for (key, idx) in module_skeleton.code_dir {
+                        if let Some(existing_idx) = module.code_dir.get(&key) {
+                            set_code_index!(existing_idx, idx.0.borrow().0, module_name.clone());
+                        } else {
+                            module.code_dir.insert(key, idx);
+                        }
+                    }
+                }
+                None => {
+                    self.add_module(module_skeleton);
+                }
+            }
+        }
+    }
+
     #[inline]
-    pub fn add_module(&mut self, module: Module, code: Code) {
+    pub fn add_module(&mut self, module: Module) {
         self.indices
             .modules
             .insert(module.module_decl.name.clone(), module);
-        self.code_repo.code.extend(code.into_iter());
     }
 
     fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) {
index 8f5e39c16ad007c21aa2b3cf4452916004b88500..00bb0bc141cb295013029bbaf35e9d24905671ad 100644 (file)
@@ -231,8 +231,13 @@ where
                     continue;
                 }
 
-                if !user.import_decl(name, arity, submodule) {
-                    return Err(SessionError::ModuleDoesNotContainExport);
+                if !user.import_decl(name.clone(), arity, submodule) {
+                    let submodule_name = submodule.module_decl.name.clone();
+                    
+                    return Err(SessionError::ModuleDoesNotContainExport(
+                        submodule_name,
+                        (name, arity)
+                    ));
                 }
             },
             ModuleExport::OpDecl(op_decl) => {
@@ -266,8 +271,13 @@ pub fn use_module<User: SubModuleUser>(
     for export in submodule.module_decl.exports.iter().cloned() {
         match export {
             ModuleExport::PredicateKey((name, arity)) => {
-                if !user.import_decl(name, arity, submodule) {
-                    return Err(SessionError::ModuleDoesNotContainExport);
+                if !user.import_decl(name.clone(), arity, submodule) {
+                    let submodule_name = submodule.module_decl.name.clone();
+
+                    return Err(SessionError::ModuleDoesNotContainExport(
+                        submodule_name,
+                        (name, arity)
+                    ));
                 }
             }
             ModuleExport::OpDecl(op_decl) => {
index 4aae067cd700f58739afa696bcaec2d377863ffc..d88dad91dfb37305f413637775d39bbcb642d9cf 100644 (file)
@@ -9,6 +9,7 @@ use crate::prolog::heap_print::*;
 use crate::prolog::instructions::*;
 use crate::prolog::machine::code_repo::CodeRepo;
 use crate::prolog::machine::copier::*;
+use crate::prolog::machine::code_walker::*;
 use crate::prolog::machine::machine_errors::*;
 use crate::prolog::machine::machine_indices::*;
 use crate::prolog::machine::machine_state::*;
@@ -21,7 +22,6 @@ use crate::ref_thread_local::RefThreadLocal;
 
 use indexmap::{IndexMap, IndexSet};
 
-use std::collections::VecDeque;
 use std::io::{stdin, stdout, Write};
 use std::iter::once;
 use std::mem;
@@ -68,22 +68,6 @@ impl BrentAlgState {
     }
 }
 
-fn scan_for_trust_me(code: &Code, jmp_offsets: &mut VecDeque<usize>, after_idx: &mut usize) {
-    for (idx, instr) in code[*after_idx..].iter().enumerate() {
-        match instr {
-            &Line::Choice(ChoiceInstruction::TrustMe)
-            | &Line::IndexedChoice(IndexedChoiceInstruction::Trust(..)) => {
-                *after_idx += idx;
-                return;
-            }
-            &Line::Control(ControlInstruction::JmpBy(_, offset, ..)) => {
-                jmp_offsets.push_back(*after_idx + idx + offset)
-            }
-            _ => {}
-        }
-    }
-}
-
 fn is_builtin_predicate(name: &ClauseName) -> bool {
     let in_builtins = name.owning_module().as_str() == "builtins";
     let hidden_name = name.as_str().starts_with("$");
@@ -537,49 +521,6 @@ impl MachineState {
         self.unify(attr_goals, target);
     }
 
-    fn create_instruction_functors(&mut self, code: &Code, first_idx: usize) -> Vec<Addr> {
-        let mut queue = VecDeque::new();
-        let mut functors = vec![];
-        let mut h = self.heap.h;
-
-        queue.push_back(first_idx);
-
-        while let Some(first_idx) = queue.pop_front() {
-            let mut last_idx = first_idx;
-
-            loop {
-                match &code[last_idx] {
-                    &Line::Choice(ChoiceInstruction::TryMeElse(..))
-                    | &Line::IndexedChoice(IndexedChoiceInstruction::Try(..)) => {
-                        last_idx += 1;
-                        scan_for_trust_me(code, &mut queue, &mut last_idx);
-                    }
-                    &Line::Control(ControlInstruction::JmpBy(_, offset, _, false)) => {
-                        queue.push_back(last_idx + offset);
-                        last_idx += 1;
-                    }
-                    &Line::Control(ControlInstruction::JmpBy(_, offset, _, true)) => {
-                        queue.push_back(last_idx + offset);
-                        break;
-                    }
-                    &Line::Control(ControlInstruction::Proceed)
-                    | &Line::Control(ControlInstruction::CallClause(_, _, _, true, _)) => break,
-                    _ => last_idx += 1,
-                };
-            }
-
-            for instr in &code[first_idx..last_idx + 1] {
-                let section = instr.to_functor(h);
-                functors.push(Addr::HeapCell(h));
-
-                h += section.len();
-                self.heap.extend(section.into_iter());
-            }
-        }
-
-        functors
-    }
-
     fn call_continuation_chunk(&mut self, chunk: Addr, return_p: LocalCodePtr) -> LocalCodePtr {
         let chunk = self.store(self.deref(chunk));
 
@@ -2382,7 +2323,21 @@ impl MachineState {
                     }
                 };
 
-                let functors = self.create_instruction_functors(&code_repo.code, first_idx);
+                let mut h = self.heap.h;
+                let mut functors = vec![];
+                
+                walk_code(
+                    &code_repo.code,
+                    first_idx,
+                    |instr| {
+                        let section = instr.to_functor(h);
+                        functors.push(Addr::HeapCell(h));
+
+                        h += section.len();
+                        self.heap.extend(section.into_iter());                        
+                    },
+                );
+                
                 let listing = Addr::HeapCell(self.heap.to_list(functors.into_iter()));
                 let listing_var = self[temp_v!(3)].clone();
 
index 2d417b66074d3d7ae11572648cb2616ddec2f454..9af68c6730f685f732124be90a0ae60037dabbfc 100644 (file)
@@ -73,11 +73,10 @@ pub struct TermStream<'a, R: Read> {
     stack: Vec<Term>,
     pub(crate) wam: &'a mut Machine,
     parser: Parser<'a, R>,
-    in_module: bool,
     pub(crate) flags: MachineFlags,
     term_expansion_lens: (usize, usize),
     goal_expansion_lens: (usize, usize),
-    top_level_terms: Vec<(Term, usize, usize)> // term, line_num, col_num.
+    top_level_terms: Vec<(Term, usize, usize)>, // term, line_num, col_num.
 }
 
 pub struct ExpansionAdditionResult {
@@ -104,6 +103,8 @@ impl ExpansionAdditionResult {
 impl<'a, R: Read> Drop for TermStream<'a, R> {
     fn drop(&mut self) {
         self.wam.indices.in_situ_code_dir.clear();
+        self.wam.indices.in_situ_module_dir.clear();
+
         self.wam.code_repo.in_situ_code.clear();
         discard_result!(self.rollback_expansion_code());
     }
@@ -126,9 +127,8 @@ impl<'a, R: Read> TermStream<'a, R> {
                 .term_dir_entry_len((clause_name!("goal_expansion"), 2)),
             wam,
             parser: Parser::new(src, atom_tbl, flags),
-            in_module: false,
             flags,
-            top_level_terms: vec![]
+            top_level_terms: vec![],
         }
     }
 
@@ -226,8 +226,12 @@ impl<'a, R: Read> TermStream<'a, R> {
                 let iter = extract_from_list(head, tail)?;
                 Ok(self.stack.extend(iter))
             }
-            Term::Clause(..) | Term::Constant(_, Constant::Atom(..)) => Ok(self.stack.push(term)),
-            _ => Err(ParserError::ExpectedTopLevelTerm),
+            Term::Clause(..) | Term::Constant(_, Constant::Atom(..)) => {
+                Ok(self.stack.push(term))
+            }
+            _ => {
+                Err(ParserError::ExpectedTopLevelTerm)
+            }
         }
     }
 
@@ -240,26 +244,36 @@ impl<'a, R: Read> TermStream<'a, R> {
         let mut parser = Parser::new(&mut stream, self.parser.get_atom_tbl(), self.flags);
 
         parser.read_term(composite_op!(
-            self.in_module,
+            false,
             &self.wam.indices.op_dir,
             op_dir
         ))
     }
 
-    pub fn read_term(&mut self, op_dir: &OpDir) -> Result<Term, ParserError> {
+    pub fn expand_term(&mut self, term: Term, op_dir: &OpDir) -> Result<Term, ParserError> {
         let mut machine_st = MachineState::new();
 
+        self.stack.push(term);
+
+        while let Some(term) = self.stack.pop() {
+            match machine_st.try_expand_term(self.wam, &term, CompileTimeHook::TermExpansion) {
+                Some(term_string) => {                        
+                    let term = self.parse_expansion_output(term_string.as_str(), op_dir)?;
+                    self.enqueue_term(term)?;
+                }
+                None => {
+                    return Ok(term);
+                }
+            };
+        }
+
+        unreachable!()
+    }
+    
+    pub fn read_term(&mut self, op_dir: &OpDir) -> Result<Term, ParserError> {
         loop {
-            while let Some(term) = self.stack.pop() {
-                match machine_st.try_expand_term(self.wam, &term, CompileTimeHook::TermExpansion) {
-                    Some(term_string) => {
-                        let term = self.parse_expansion_output(term_string.as_str(), op_dir)?;
-                        self.enqueue_term(term)?
-                    }
-                    None => {
-                        return Ok(term);
-                    }
-                };
+            if let Some(term) = self.stack.pop() {
+                return Ok(self.expand_term(term, op_dir)?);
             }
 
             self.parser.reset();
@@ -268,7 +282,7 @@ impl<'a, R: Read> TermStream<'a, R> {
             let col_num = self.col_num();
 
             let term = self.parser.read_term(composite_op!(
-                self.in_module,
+                false,
                 &self.wam.indices.op_dir,
                 op_dir
             ))?;
index cbe0ff2133a8dc64204d1c759d3927b5c428c752..0530222545b27ae74d4a9d7d4c4aa2ee0c8f5bb9 100644 (file)
@@ -15,6 +15,7 @@ use std::cell::Cell;
 use std::collections::VecDeque;
 use std::io::Read;
 use std::mem;
+use std::ops::DerefMut;
 use std::rc::Rc;
 
 enum IndexSource<'a, T> {
@@ -108,6 +109,31 @@ impl<'a, 'b, 'c, R: Read> CompositeIndices<'a, 'b, 'c, R> {
             ct => ct,
         }
     }
+
+    fn add_in_situ_module_info(&mut self, module_name: ClauseName, term: &mut Term)
+    {
+        let atom_tbl =
+            match self.term_stream.wam.indices.in_situ_module_dir.get(&module_name) {
+                Some(ref module_stub) => module_stub.atom_tbl.clone(),
+                None => {
+                    let atom_tbl = match self.term_stream.wam.indices.modules.get(&module_name) {
+                        Some(ref module) => module.atom_tbl.clone(),
+                        None => TabledData::new(module_name.to_rc()),
+                    };
+
+                    self.term_stream.wam.indices.in_situ_module_dir.insert(
+                        module_name.clone(),
+                        ModuleStub::new(atom_tbl.clone()),
+                    );
+
+                    atom_tbl
+                }
+            };
+
+        if let Some(name) = term.name() {
+            term.set_name(name.with_table(atom_tbl));
+        }
+    }
 }
 
 fn as_compile_time_hook(
@@ -196,7 +222,8 @@ fn setup_op_decl(
     to_op_decl(prec, spec.as_str(), name)
 }
 
-fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, ParserError> {
+fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, ParserError>
+{
     match term {
         Term::Clause(_, ref name, ref mut terms, Some(_))
             if name.as_str() == "/" && terms.len() == 2 =>
@@ -221,6 +248,28 @@ fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, ParserErro
     }
 }
 
+fn setup_scoped_predicate_indicator(term: &mut Term) -> Result<ScopedPredicateKey, ParserError>
+{
+    match term {
+        Term::Clause(_, ref name, ref mut terms, Some(_))
+            if name.as_str() == ":" && terms.len() == 2 =>
+        {
+            let mut predicate_indicator = *terms.pop().unwrap();
+            let module_name = *terms.pop().unwrap();
+
+            let module_name = module_name
+                .to_constant()
+                .and_then(|c| c.to_atom())
+                .ok_or(ParserError::InvalidModuleExport)?;
+
+            let key = setup_predicate_indicator(&mut predicate_indicator)?;
+
+            Ok((module_name, key))
+        }
+        _ => Err(ParserError::InvalidModuleExport),
+    }
+}
+
 fn setup_module_export(
     mut term: Term,
     atom_tbl: TabledData<Atom>,
@@ -327,19 +376,8 @@ fn setup_qualified_import(
     }
 }
 
-fn is_consistent(
-    name: Option<ClauseName>,
-    arity: usize,
-    clauses: &Vec<PredicateClause>,
-) -> bool
+fn merge_clauses(tls: &mut VecDeque<TopLevel>) -> Result<TopLevel, ParserError>
 {
-    match clauses.first() {
-        Some(ref cl) => name == cl.name() && arity == cl.arity(),
-        None => true,
-    }
-}
-
-fn merge_clauses(tls: &mut VecDeque<TopLevel>) -> Result<TopLevel, ParserError> {
     let mut clauses: Vec<PredicateClause> = vec![];
 
     while let Some(tl) = tls.pop_front() {
@@ -347,20 +385,21 @@ fn merge_clauses(tls: &mut VecDeque<TopLevel>) -> Result<TopLevel, ParserError>
             TopLevel::Query(_) if clauses.is_empty() && tls.is_empty() => return Ok(tl),
             TopLevel::Declaration(_) if clauses.is_empty() => return Ok(tl),
             TopLevel::Query(_) => return Err(ParserError::InconsistentEntry),
-            TopLevel::Fact(..) if is_consistent(tl.name(), tl.arity(), &clauses) =>
+            TopLevel::Fact(..) => {
                 if let TopLevel::Fact(fact, line_num, col_num) = tl {
                     let clause = PredicateClause::Fact(fact, line_num, col_num);
                     clauses.push(clause);
-                },
-            TopLevel::Rule(..) if is_consistent(tl.name(), tl.arity(), &clauses) => {
+                }
+            }
+            TopLevel::Rule(..) => {
                 if let TopLevel::Rule(rule, line_num, col_num) = tl {
                     let clause = PredicateClause::Rule(rule, line_num, col_num);
                     clauses.push(clause);
                 }
             }
-            TopLevel::Predicate(_) if is_consistent(tl.name(), tl.arity(), &clauses) => {
-                if let TopLevel::Predicate(pred) = tl {
-                    clauses.extend(pred.clauses().into_iter())
+            TopLevel::Predicate(..) => {
+                if let TopLevel::Predicate(predicate) = tl {
+                    clauses.extend(predicate.clauses().into_iter())
                 }
             }
             _ => {
@@ -442,6 +481,7 @@ fn check_for_internal_if_then(terms: &mut Vec<Term>) {
         }
 
         let tail_term = conq_terms.pop_back().unwrap();
+
         terms.push(fold_by_str(
             conq_terms.into_iter(),
             tail_term,
@@ -477,6 +517,78 @@ fn flatten_hook(mut term: Term) -> Term {
     term
 }
 
+fn draw_from_term_dir_impl(
+    term_dir: &TermDir,
+    term_dirs: &mut TermDirQuantum,
+    key: &PredicateKey,
+    preds: &mut Vec<PredicateClause>,
+    queue: &mut VecDeque<TopLevel>
+) {
+    if let Some(entry) = term_dirs.get_mut(key) {
+        if entry.is_fresh {
+            entry.is_fresh = false;
+
+            (entry.new_terms.0).0.extend(preds.drain(0 ..));
+            entry.new_terms.1.extend(queue.drain(0 ..));
+
+            *preds = (entry.old_terms.0).0
+                .iter()
+                .cloned()
+                .chain((entry.new_terms.0).0.iter().cloned())
+                .collect();
+
+            *queue = entry.old_terms.1
+                .iter()
+                .cloned()
+                .chain(entry.new_terms.1.iter().cloned())
+                .collect();
+        } else {
+            *entry = TermDirQuantumEntry::new();
+        }
+    } else if term_dir.contains_key(key) {
+        let entry = TermDirQuantumEntry::from(&Predicate::new(), &VecDeque::new());
+        term_dirs.insert(key.clone(), entry);
+    }
+}
+
+fn draw_from_term_dir<R: Read>(
+    indices: &CompositeIndices<R>,
+    intra_module_term_dirs: &mut IndexMap<ClauseName, TermDirQuantum>,
+    top_level_term_dirs: &mut TermDirQuantum,
+    key: &PredicateKey,
+    preds: &mut Vec<PredicateClause>,
+    queue: &mut VecDeque<TopLevel>,
+) {
+    let module = key.0.owning_module();
+
+    // aaarghhh..
+    match indices.term_stream.wam.indices.in_situ_module_dir.get(&module) {
+        // modify module_stub to do this right.
+        Some(ref module_stub) if key.0.has_table(&module_stub.atom_tbl) => {
+            if let Some(ref mut term_dirs) = intra_module_term_dirs.get_mut(&module) {
+                if let Some(ref module) = indices.term_stream.wam.indices.modules.get(&module) {
+                    return draw_from_term_dir_impl(
+                        &module.term_dir,
+                        term_dirs,
+                        key,
+                        preds,
+                        queue,
+                    );
+                }
+            }
+        }
+        _ => {}
+    }
+
+    draw_from_term_dir_impl(
+        &indices.term_stream.wam.code_repo.term_dir,
+        top_level_term_dirs,
+        key,
+        preds,
+        queue,
+    );
+}
+
 fn setup_declaration<'a, 'b, 'c, R: Read>(
     indices: &mut CompositeIndices<'a, 'b, 'c, R>,
     flags: MachineFlags,
@@ -509,8 +621,17 @@ fn setup_declaration<'a, 'b, 'c, R: Read>(
                    Ok(Declaration::NonCountedBacktracking(name, arity))
                }
                 ("multifile", 1) => {
-                    let (name, arity) = setup_predicate_indicator(&mut *terms.pop().unwrap())?;
-                    Ok(Declaration::MultiFile(name, arity))
+                    let mut term = *terms.pop().unwrap();
+
+                    match setup_predicate_indicator(&mut term) {
+                        Ok((name, arity)) =>
+                            Ok(Declaration::MultiFile(MultiFileIndicator::LocalScoped(name, arity))),
+                        _ =>
+                            setup_scoped_predicate_indicator(&mut term)
+                                .map(|key| {
+                                    Declaration::MultiFile(MultiFileIndicator::ModuleScoped(key))
+                                })
+                    }
                 }
                ("use_module", 1) => {
                    Ok(Declaration::UseModule(setup_use_module_decl(terms)?))
@@ -870,6 +991,34 @@ impl RelationWorker {
         )?))
     }
 
+    fn compact_module_scoped_head<'a, 'b, 'c, R: Read>(
+        &self,
+        term: &mut Term,
+        indices: &mut CompositeIndices<'a, 'b, 'c, R>,
+    ) {
+        let inner_term = match term {
+            Term::Clause(_, ref name, ref mut inner_terms, _)
+                if name.as_str() == ":" && inner_terms.len() == 2 => {
+                    let module_name = match inner_terms[0].as_ref() {
+                        &Term::Constant(_, Constant::Atom(ref module, _)) => {
+                            module.clone()
+                        }
+                        _ => {
+                            return;
+                        }
+                    };
+
+                    indices.add_in_situ_module_info(module_name, inner_terms[1].deref_mut());
+                    *inner_terms.pop().unwrap()
+                }
+            _ => {
+                return;
+            }
+        };
+
+        *term = inner_term;
+    }
+
     fn try_term_to_tl<'a, 'b, 'c, R: Read>(
         &mut self,
         indices: &mut CompositeIndices<'a, 'b, 'c, R>,
@@ -877,7 +1026,7 @@ impl RelationWorker {
         blocks_cuts: bool,
     ) -> Result<TopLevel, ParserError> {
         match term {
-            Term::Clause(r, name, terms, fixity) => {
+            Term::Clause(r, name, mut terms, fixity) => {
                 if let Some(hook) = is_compile_time_hook(&name, &terms) {
                     let term = Term::Clause(r, name, terms, fixity);
                     let (hook, clause, queue) = self.setup_hook(hook, indices, term)?;
@@ -888,6 +1037,8 @@ impl RelationWorker {
                 } else if name.as_str() == "?-" {
                     self.try_term_to_query(indices, terms, blocks_cuts)
                 } else if name.as_str() == ":-" && terms.len() == 2 {
+                    self.compact_module_scoped_head(&mut terms[0], indices);
+
                     Ok(TopLevel::Rule(self.setup_rule(
                         indices,
                         terms,
@@ -898,11 +1049,14 @@ impl RelationWorker {
                     Ok(TopLevel::Declaration(setup_declaration(indices, self.flags, terms,
                                                                self.line_num, self.col_num)?))
                 } else {
-                    let term = Term::Clause(r, name, terms, fixity);
+                    let mut term = Term::Clause(r, name, terms, fixity);
+                    self.compact_module_scoped_head(&mut term, indices);
+
                     Ok(TopLevel::Fact(self.setup_fact(term, true)?, self.line_num, self.col_num))
                 }
             }
-            term => Ok(TopLevel::Fact(self.setup_fact(term, true)?, self.line_num, self.col_num)),
+            term =>
+                Ok(TopLevel::Fact(self.setup_fact(term, true)?, self.line_num, self.col_num)),
         }
     }
 
@@ -949,68 +1103,15 @@ pub type DynamicClause = Vec<(Term, Term)>;
 
 pub type DynamicClauseMap = IndexMap<(ClauseName, usize), DynamicClause>;
 
-pub struct TermDirQuantum {
-    old_term_dir: TermDir,
-    new_term_dir: TermDir,
-}
-
-impl TermDirQuantum {
-    pub fn new() -> Self {
-        Self {
-            old_term_dir: TermDir::new(),
-            new_term_dir: TermDir::new()
-        }
-    }
-
-    #[inline]
-    fn get_old(&self, name: ClauseName, arity: usize) -> Option<&(Predicate, VecDeque<TopLevel>)>
-    {
-        self.old_term_dir.get(&(name, arity))
-    }
-
-    #[inline]
-    fn add_new(
-        &mut self,
-        key: PredicateKey,
-        preds: &mut Vec<PredicateClause>,
-        queue: VecDeque<TopLevel>
-    ) {
-        let preds = Predicate(mem::replace(preds, vec![]));
-        self.new_term_dir.insert(key, (preds, queue));
-    }
-
-    pub fn consolidate(self) -> TermDir {
-        let mut term_dir = self.old_term_dir;
-
-        for (key, (preds, queue)) in self.new_term_dir {
-            let (prev_preds, prev_queue) =
-                term_dir.entry(key).or_insert((Predicate::new(), VecDeque::new()));
-
-            prev_preds.0.extend(preds.0.into_iter());
-            prev_queue.extend(queue.into_iter());
-        }
-
-        term_dir
-    }
-
-    #[inline]
-    pub fn set_old(
-        &mut self,
-        key: PredicateKey,
-        pred: Predicate,
-        queue: VecDeque<TopLevel>
-    ) {
-        self.old_term_dir.insert(key, (pred, queue));
-    }
-}
-
 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,
-    pub(crate) term_dirs: TermDirQuantum
+    pub(crate) term_dirs: TermDirQuantum,
+    pub(crate) intra_module_term_dirs: IndexMap<ClauseName, TermDirQuantum>,
+    pub(crate) non_counted_bt_preds: IndexSet<PredicateKey>,
 }
 
 impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
@@ -1032,6 +1133,8 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
             dynamic_clause_map: IndexMap::new(),
             in_module: false,
             term_dirs: TermDirQuantum::new(),
+            intra_module_term_dirs: IndexMap::new(),
+            non_counted_bt_preds: IndexSet::new(),
         }
     }
 
@@ -1064,32 +1167,31 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
         let mut indices = CompositeIndices::new(
             &mut self.term_stream,
             IndexSource::Local(indices),
-            if self.in_module { None } else { Some(IndexSource::TermStream) }
+            if self.in_module { None } else { Some(IndexSource::TermStream) },
         );
 
-        let (name, arity) = (preds[0].name().unwrap(), preds[0].arity());
-
-        let (mut prev_preds, mut prev_queue) =
-            match self.term_dirs.get_old(name.clone(), arity).cloned() {
-                Some((preds, queue)) => (preds, queue),
-                None => (Predicate::new(), VecDeque::new()),
-            };
-
-        let queue = self.rel_worker.parse_queue(&mut indices)?;
-
-        prev_preds.0.extend(preds.iter().cloned());
-        prev_queue.extend(queue.iter().cloned());
+        let key = (preds[0].name().unwrap(), preds[0].arity());
 
-        let result = (prev_preds, prev_queue);
+        let mut preds = mem::replace(preds, vec![]);
+        let mut queue = self.rel_worker.parse_queue(&mut indices)?;
 
-        self.term_dirs.add_new((name, arity), preds, queue);
+        draw_from_term_dir(
+            &indices,
+            &mut self.intra_module_term_dirs,
+            &mut self.term_dirs,
+            &key,
+            &mut preds,
+            &mut queue,
+        );
 
-        let in_situ_code_dir = &mut indices.term_stream.wam.indices.in_situ_code_dir;
+        let result = (Predicate(preds), queue);
 
         indices.term_stream.wam.code_repo.add_in_situ_result(
             &result,
-            in_situ_code_dir,
+            &mut indices.term_stream.wam.indices.in_situ_code_dir,
+            &mut indices.term_stream.wam.indices.in_situ_module_dir,
             indices.term_stream.flags,
+            &self.non_counted_bt_preds,
         )?;
 
         Ok(self.results.push(result))
@@ -1120,12 +1222,18 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
 
         while !self.term_stream.eof()? {
             let term = self.term_stream.read_term(&indices.op_dir)?;
-
+            
             // if is_consistent is false, preds is non-empty.
-            if !is_consistent(term.predicate_name(), term.predicate_arity(), &preds) {
+            let term = if !term.is_consistent(&preds) {
                 self.process_result(indices, &mut preds)?;
                 self.take_dynamic_clauses();
-            }
+
+                // expand the term after the addition of the previous
+                // predicate.
+                self.term_stream.expand_term(term, &indices.op_dir)?
+            } else {
+                term
+            };
 
             let (mut tl, new_rel_worker) = self.try_term_to_tl(indices, term)?;
 
index 1d11a9028d7f932b53804c5177ecfbc81dbdbd43..667986834d7df8f639132853d2868acf51c45349 100644 (file)
@@ -241,11 +241,13 @@ macro_rules! index_store {
         IndexStore {
             atom_tbl: $atom_tbl,
             code_dir: $code_dir,
+            module_dir: ModuleDir::new(),
             dynamic_code_dir: DynamicCodeDir::new(),
             global_variables: GlobalVarDir::new(),
             in_situ_code_dir: InSituCodeDir::new(),
+            in_situ_module_dir: ModuleStubDir::new(),
             op_dir: $op_dir,
-            modules: $modules,
+            modules: $modules,            
         }
     };
 }
index 4db95bbd0d9f583769199c5d59d339c0a4255b21..e58465ab1c44b829d38ecfa28504437ef5e61d8a 100644 (file)
@@ -243,7 +243,6 @@ user:term_expansion(Term0, (:- initialization(ExpandedGoals))) :-
     expand_goals(Goals, ExpandedGoals),
     Goals \== ExpandedGoals.
 
-
 '$module_expand_goal'(UnexpandedGoals, ExpandedGoals) :-    
     (  '$module_of'(Module, UnexpandedGoals),
        '$module_exists'(Module),
index 23c8076c05cb4b91a7c4ec699ff9a53067862cf6..f76694fc2c80d22b2b5dbc2066d5bac6ec97f615 100644 (file)
@@ -45,6 +45,7 @@ impl fmt::Display for IndexPtr {
             &IndexPtr::DynamicUndefined => write!(f, "undefined"),
             &IndexPtr::Undefined => write!(f, "undefined"),
             &IndexPtr::Index(i) => write!(f, "{}", i),
+            &IndexPtr::InSituDirEntry(i) => write!(f, "in_situ({})", i),
             &IndexPtr::UserTermExpansion => write!(f, "user:term_expansion"),
             &IndexPtr::UserGoalExpansion => write!(f, "user:goal_expansion"),
         }
@@ -261,8 +262,14 @@ impl fmt::Display for SessionError {
                 write!(f, "filename {} is invalid", filename)
             }
             &SessionError::ModuleNotFound => write!(f, "module not found."),
-            &SessionError::ModuleDoesNotContainExport => {
-                write!(f, "module does not contain claimed export.")
+            &SessionError::ModuleDoesNotContainExport(ref module, ref key) => {
+                write!(
+                    f,
+                    "module {} does not contain claimed export {}/{}",
+                    module,
+                    key.0,
+                    key.1,
+                )
             }
             &SessionError::OpIsInfixAndPostFix(_) => {
                 write!(f, "cannot define an op to be both postfix and infix.")