From: Mark Thom Date: Mon, 3 Feb 2020 05:13:14 +0000 (-0700) Subject: add multifile and module scoped predicates X-Git-Tag: v0.8.118~19 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=740bd528c48392e6881838e7766e4ff516dde885;p=scryer-prolog.git add multifile and module scoped predicates --- diff --git a/Cargo.lock b/Cargo.lock index ea650ad5..60b7c3d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index f26c3b3a..ef165b35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/prolog/forms.rs b/src/prolog/forms.rs index d0c02028..9897d9ce 100644 --- a/src/prolog/forms.rs +++ b/src/prolog/forms.rs @@ -31,26 +31,6 @@ pub enum TopLevel { } impl TopLevel { - pub fn name(&self) -> Option { - 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) -> 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); #[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), ModuleInitialization(Vec, VecDeque), // 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 { 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; #[derive(Clone, PartialEq)] pub enum ModuleExport { OpDecl(OpDecl), - PredicateKey(PredicateKey), + PredicateKey(PredicateKey), } #[derive(Clone)] diff --git a/src/prolog/lib/dcgs.pl b/src/prolog/lib/dcgs.pl index e144cd0d..20997551 100644 --- a/src/prolog/lib/dcgs.pl +++ b/src/prolog/lib/dcgs.pl @@ -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 )) :- diff --git a/src/prolog/machine/code_repo.rs b/src/prolog/machine/code_repo.rs index fee126e9..ac780f38 100644 --- a/src/prolog/machine/code_repo.rs +++ b/src/prolog/machine/code_repo.rs @@ -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, ) -> 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::::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::::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 index 00000000..1237498e --- /dev/null +++ b/src/prolog/machine/code_walker.rs @@ -0,0 +1,81 @@ +use crate::prolog::instructions::*; + +use std::collections::VecDeque; + +fn scan_for_trust_me(code: &Code, jmp_offsets: &mut VecDeque, 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, 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); + } + } +} diff --git a/src/prolog/machine/compile.rs b/src/prolog/machine/compile.rs index e026b25d..efb62cda 100644 --- a/src/prolog/machine/compile.rs +++ b/src/prolog/machine/compile.rs @@ -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( 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, module: Option, 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, wam: &Machine, + key: PredicateKey, + in_situ_code: &mut Code, code_dir: &mut CodeDir, - code_offset: usize, - ) -> Result { - 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::::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::::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, + 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, ) -> 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( &self, - name: ClauseName, - arity: usize, - worker: &mut TopLevelBatchWorker + indicator: MultiFileIndicator, + worker: &mut TopLevelBatchWorker, ) -> 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( @@ -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( &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( 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) } diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index 0b3507a1..8670f9bf 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -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), diff --git a/src/prolog/machine/machine_indices.rs b/src/prolog/machine/machine_indices.rs index 0a7a35af..0d1aac8b 100644 --- a/src/prolog/machine/machine_indices.rs +++ b/src/prolog/machine/machine_indices.rs @@ -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>); 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; + // key type: module name, predicate indicator. pub type DynamicCodeDir = IndexMap<(ClauseName, ClauseName, usize), DynamicPredicateInfo>; pub type GlobalVarDir = IndexMap)>; +pub(crate) struct ModuleStub { + pub(crate) atom_tbl: TabledData, + pub(crate) in_situ_code_dir: InSituCodeDir, +} + +impl ModuleStub { + pub(crate) fn new(atom_tbl: TabledData) -> Self { + ModuleStub { + atom_tbl, + in_situ_code_dir: InSituCodeDir::new(), + } + } +} + +pub(crate) type ModuleStubDir = IndexMap; + pub struct IndexStore { pub(super) atom_tbl: TabledData, 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 { 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; pub type TermDir = IndexMap)>; +pub struct TermDirQuantumEntry { + pub old_terms: (Predicate, VecDeque), + pub new_terms: (Predicate, VecDeque), + 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) -> 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); + +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, diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 05749105..3aaeeb74 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -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 { +fn try_in_situ_lookup(name: ClauseName, arity: usize, indices: &IndexStore) -> Option +{ 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(()) diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index 14465337..ede50593 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -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 { 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) { diff --git a/src/prolog/machine/modules.rs b/src/prolog/machine/modules.rs index 8f5e39c1..00bb0bc1 100644 --- a/src/prolog/machine/modules.rs +++ b/src/prolog/machine/modules.rs @@ -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( 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) => { diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 4aae067c..d88dad91 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -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, 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 { - 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(); diff --git a/src/prolog/machine/term_expansion.rs b/src/prolog/machine/term_expansion.rs index 2d417b66..9af68c67 100644 --- a/src/prolog/machine/term_expansion.rs +++ b/src/prolog/machine/term_expansion.rs @@ -73,11 +73,10 @@ pub struct TermStream<'a, R: Read> { stack: Vec, 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 { + pub fn expand_term(&mut self, term: Term, op_dir: &OpDir) -> Result { 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 { 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 ))?; diff --git a/src/prolog/machine/toplevel.rs b/src/prolog/machine/toplevel.rs index cbe0ff21..05302225 100644 --- a/src/prolog/machine/toplevel.rs +++ b/src/prolog/machine/toplevel.rs @@ -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 { +fn setup_predicate_indicator(term: &mut Term) -> Result +{ 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 Result +{ + 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, @@ -327,19 +376,8 @@ fn setup_qualified_import( } } -fn is_consistent( - name: Option, - arity: usize, - clauses: &Vec, -) -> bool +fn merge_clauses(tls: &mut VecDeque) -> Result { - match clauses.first() { - Some(ref cl) => name == cl.name() && arity == cl.arity(), - None => true, - } -} - -fn merge_clauses(tls: &mut VecDeque) -> Result { let mut clauses: Vec = vec![]; while let Some(tl) = tls.pop_front() { @@ -347,20 +385,21 @@ fn merge_clauses(tls: &mut VecDeque) -> Result 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) { } 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, + queue: &mut VecDeque +) { + 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( + indices: &CompositeIndices, + intra_module_term_dirs: &mut IndexMap, + top_level_term_dirs: &mut TermDirQuantum, + key: &PredicateKey, + preds: &mut Vec, + queue: &mut VecDeque, +) { + 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 { 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)> - { - self.old_term_dir.get(&(name, arity)) - } - - #[inline] - fn add_new( - &mut self, - key: PredicateKey, - preds: &mut Vec, - queue: VecDeque - ) { - 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 - ) { - 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)>, 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, + pub(crate) non_counted_bt_preds: IndexSet, } 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)?; diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 1d11a902..66798683 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -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, } }; } diff --git a/src/prolog/toplevel.pl b/src/prolog/toplevel.pl index 4db95bbd..e58465ab 100644 --- a/src/prolog/toplevel.pl +++ b/src/prolog/toplevel.pl @@ -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), diff --git a/src/prolog/write.rs b/src/prolog/write.rs index 23c8076c..f76694fc 100644 --- a/src/prolog/write.rs +++ b/src/prolog/write.rs @@ -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.")