[[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)",
"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)",
"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"
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"
}
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, _)), ..) => {
}
}
+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)]
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()
+ }
+ }
}
}
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
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),
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;
}
}
#[derive(Clone, PartialEq)]
pub enum ModuleExport {
OpDecl(OpDecl),
- PredicateKey(PredicateKey),
+ PredicateKey(PredicateKey),
}
#[derive(Clone)]
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 )) :-
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,
.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
})
.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]
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,
--- /dev/null
+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);
+ }
+ }
+}
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;
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))
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 {
}
}
+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),
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,
) {
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(
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(())
listing_src: ListingSource,
) -> Self {
ListingCompiler {
- non_counted_bt_preds: IndexSet::new(),
module: None,
user_term_dir: TermDir::new(),
orig_term_expansion_lens: code_repo
}
}
- /*
- 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(
.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(
wam: &mut Machine,
indices: &mut IndexStore,
flags: MachineFlags,
+ non_counted_bt_preds: &mut IndexSet<PredicateKey>,
) -> Result<(), SessionError> {
match decl {
Declaration::Dynamic(..) => {
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)
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>(
&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
_ => {}
};
- 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();
result
}
-
+
pub(crate) fn gather_items<R: Read>(
&mut self,
wam: &mut Machine,
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(),
}
}
- 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
top_level_term_dirs = worker.term_dirs;
TermDirQuantum::new()
};
-
+
Ok(GatherResult {
worker_results: worker.results,
dynamic_clause_map: worker.dynamic_clause_map,
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(),
})
}
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 {
}
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)?;
}
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,
)?;
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)
}
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;
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"),
CannotOverwriteBuiltIn(ClauseName),
CannotOverwriteImport(ClauseName),
InvalidFileName(ClauseName),
- ModuleDoesNotContainExport,
+ ModuleDoesNotContainExport(ClauseName, PredicateKey),
ModuleNotFound,
NamelessEntry,
OpIsInfixAndPostFix(ClauseName),
pub enum IndexPtr {
DynamicUndefined, // a predicate, declared as dynamic, whose location in code is as yet undefined.
Undefined,
+ InSituDirEntry(usize),
Index(usize),
UserGoalExpansion,
UserTermExpansion
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;
}
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,
}
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)
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())
}
}
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,
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(());
+ }
_ => {}
}
}
}
}
-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,
+ },
}
}
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))
}
IndexPtr::UserGoalExpansion => {
machine_st.call_at_index(arity, LocalCodePtr::UserGoalExpansion(0));
}
+ IndexPtr::InSituDirEntry(p) => {
+ machine_st.call_at_index(arity, LocalCodePtr::InSituDirEntry(p));
+ }
}
Ok(())
IndexPtr::UserGoalExpansion => {
machine_st.execute_at_index(arity, LocalCodePtr::UserGoalExpansion(0));
}
+ IndexPtr::InSituDirEntry(p) => {
+ machine_st.execute_at_index(arity, LocalCodePtr::InSituDirEntry(p));
+ }
}
Ok(())
mod attributed_variables;
pub(super) mod code_repo;
+pub mod code_walker;
pub mod compile;
mod copier;
mod dynamic_database;
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()
+ })
+ }
}
}
default_index_store!(atom_tbl.clone()),
true,
ListingSource::from_file_and_path(
- clause_name!("builtins.pl"),
+ clause_name!("builtins.pl"),
lib_path.clone(),
),
);
);
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(),
),
);
);
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(),
)
);
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) {
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) {
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) => {
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) => {
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::*;
use indexmap::{IndexMap, IndexSet};
-use std::collections::VecDeque;
use std::io::{stdin, stdout, Write};
use std::iter::once;
use std::mem;
}
}
-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("$");
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));
}
};
- 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();
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 {
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());
}
.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![],
}
}
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)
+ }
}
}
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();
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
))?;
use std::collections::VecDeque;
use std::io::Read;
use std::mem;
+use std::ops::DerefMut;
use std::rc::Rc;
enum IndexSource<'a, T> {
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(
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 =>
}
}
+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>,
}
}
-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() {
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())
}
}
_ => {
}
let tail_term = conq_terms.pop_back().unwrap();
+
terms.push(fold_by_str(
conq_terms.into_iter(),
tail_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,
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)?))
)?))
}
+ 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>,
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)?;
} 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,
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)),
}
}
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> {
dynamic_clause_map: IndexMap::new(),
in_module: false,
term_dirs: TermDirQuantum::new(),
+ intra_module_term_dirs: IndexMap::new(),
+ non_counted_bt_preds: IndexSet::new(),
}
}
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))
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)?;
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,
}
};
}
expand_goals(Goals, ExpandedGoals),
Goals \== ExpandedGoals.
-
'$module_expand_goal'(UnexpandedGoals, ExpandedGoals) :-
( '$module_of'(Module, UnexpandedGoals),
'$module_exists'(Module),
&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"),
}
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.")