From 6439d0973324ece906441cb50e785a3ca8c17169 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Wed, 6 Mar 2019 00:09:55 -0700 Subject: [PATCH] give dynamic predicates a better respect for module bounds --- src/prolog/machine/compile.rs | 76 +++++++++++++++++++++----- src/prolog/machine/dynamic_database.rs | 47 ++++++++++++---- src/prolog/machine/machine_errors.rs | 1 + src/prolog/machine/machine_indices.rs | 25 ++++++--- src/prolog/machine/mod.rs | 2 +- src/prolog/machine/system_calls.rs | 18 +++--- src/prolog/write.rs | 20 +++++-- 7 files changed, 142 insertions(+), 47 deletions(-) diff --git a/src/prolog/machine/compile.rs b/src/prolog/machine/compile.rs index 42cecfe6..aed7d02f 100644 --- a/src/prolog/machine/compile.rs +++ b/src/prolog/machine/compile.rs @@ -158,6 +158,36 @@ pub fn compile_term(wam: &mut Machine, packet: TopLevelPacket) -> EvalSession } } +pub(super) +fn compile_into_module(wam: &mut Machine, src: R, name: ClauseName) -> EvalSession +{ + let mut indices = default_index_store!(wam.atom_tbl_of(&name)); + try_eval_session!(setup_indices(wam, name.owning_module(), &mut indices)); + + let mut compiler = ListingCompiler::new(&wam.code_repo); + + let results = try_eval_session!(compiler.gather_items(wam, src, &mut indices)); + let module_code = try_eval_session!(compiler.generate_code(results.worker_results, wam, + &mut indices.code_dir, 0)); + + let mut clause_code_generator = ClauseCodeGenerator::new(module_code.len()); + try_eval_session!(clause_code_generator.generate_clause_code(results.dynamic_clause_map, + wam)); + + match wam.indices.modules.get_mut(&name.owning_module()) { + Some(module) => { + let code_dir = mem::replace(&mut indices.code_dir, CodeDir::new()); + module.code_dir.extend(as_module_code_dir(code_dir)); + }, + _ => unreachable!() + }; + + wam.code_repo.code.extend(module_code.into_iter()); + clause_code_generator.add_clause_code(wam); + + EvalSession::EntrySuccess +} + pub struct GatherResult { dynamic_clause_map: DynamicClauseMap, pub(crate) worker_results: Vec, @@ -186,6 +216,15 @@ impl ClauseCodeGenerator { continue; } + if let Some(idx) = wam.indices.code_dir.get(&(name.clone(), arity)) { + if !idx.is_undefined() && name.owning_module() != idx.module_name() { + let err_str = format!("{}/{}", name.as_str(), arity); + let err_str = clause_name!(err_str, wam.indices.atom_tbl()); + + return Err(SessionError::CannotOverwriteDynamicClause(err_str)); + } + } + let predicate = Predicate(heads_and_tails.into_iter().map(|(head, tail)| { let clause = Term::Clause(Cell::default(), clause_name!("clause"), vec![Box::new(head), Box::new(tail)], @@ -360,7 +399,7 @@ impl ListingCompiler { pub(crate) fn generate_code(&mut self, decls: Vec, wam: &Machine, - code_dir: &mut CodeDir) + code_dir: &mut CodeDir, code_offset: usize) -> Result { let mut code = vec![]; @@ -369,7 +408,7 @@ impl ListingCompiler { let (name, arity) = decl.predicate_indicator().ok_or(SessionError::NamelessEntry)?; let non_counted_bt = self.non_counted_bt_preds.contains(&(name.clone(), arity)); - let p = code.len() + wam.code_size(); + let p = code.len() + wam.code_size() + code_offset; let mut decl_code = compile_relation(&TopLevel::Predicate(decl), non_counted_bt, wam.machine_flags())?; @@ -541,9 +580,10 @@ fn compile_work(compiler: &mut ListingCompiler, wam: &mut Machine, src: let mut results = try_eval_session!(compiler.gather_items(wam, src, &mut indices)); let module_code = try_eval_session!(compiler.generate_code(results.worker_results, wam, - &mut indices.code_dir)); + &mut indices.code_dir, 0)); let toplvl_code = try_eval_session!(compiler.generate_code(results.toplevel_results, wam, - &mut results.toplevel_indices.code_dir)); + &mut results.toplevel_indices.code_dir, + module_code.len())); if let Some(ref mut module) = &mut compiler.module { module.term_expansions = results.addition_results.take_term_expansions(); @@ -556,13 +596,18 @@ fn compile_work(compiler: &mut ListingCompiler, wam: &mut Machine, src: try_eval_session!(wam.code_repo.compile_hook(CompileTimeHook::UserGoalExpansion, flags)); if let Some(module) = compiler.module.take() { - try_eval_session!(add_non_module_code(wam, results.dynamic_clause_map, toplvl_code, - results.toplevel_indices)); + let mut clause_code_generator = ClauseCodeGenerator::new(module_code.len() + toplvl_code.len()); + + try_eval_session!(clause_code_generator.generate_clause_code(results.dynamic_clause_map, wam)); + try_eval_session!(wam.check_toplevel_code(&results.toplevel_indices)); add_module_code(wam, module, module_code, indices); + add_toplevel_code(wam, toplvl_code, results.toplevel_indices); + + clause_code_generator.add_clause_code(wam); } else { try_eval_session!(add_non_module_code(wam, results.dynamic_clause_map, module_code, - indices)); + indices)); } EvalSession::EntrySuccess @@ -574,12 +619,12 @@ fn compile_work(compiler: &mut ListingCompiler, wam: &mut Machine, src: pub fn compile_special_form(wam: &mut Machine, src: R) -> Result { let mut indices = default_index_store!(wam.indices.atom_tbl.clone()); - setup_indices(wam, &mut indices)?; + setup_indices(wam, clause_name!("builtins"), &mut indices)?; let mut compiler = ListingCompiler::new(&wam.code_repo); let results = compiler.gather_items(wam, src, &mut indices)?; - compiler.generate_code(results.worker_results, wam, &mut indices.code_dir) + compiler.generate_code(results.worker_results, wam, &mut indices.code_dir, 0) } #[inline] @@ -596,12 +641,15 @@ pub fn compile_listing(wam: &mut Machine, src: R, indices: IndexStore) } } -fn setup_indices(wam: &mut Machine, indices: &mut IndexStore) -> Result<(), SessionError> { - if let Some(builtins) = wam.indices.take_module(clause_name!("builtins")) { +pub(super) +fn setup_indices(wam: &mut Machine, module: ClauseName, indices: &mut IndexStore) + -> Result<(), SessionError> +{ + if let Some(module) = wam.indices.take_module(module) { let flags = wam.machine_flags(); - let result = indices.use_module(&mut wam.code_repo, flags, &builtins); + let result = indices.use_module(&mut wam.code_repo, flags, &module); - wam.indices.insert_module(builtins); + wam.indices.insert_module(module); result } else { Err(SessionError::ModuleNotFound) @@ -610,6 +658,6 @@ fn setup_indices(wam: &mut Machine, indices: &mut IndexStore) -> Result<(), Sess pub fn compile_user_module(wam: &mut Machine, src: R) -> EvalSession { let mut indices = default_index_store!(wam.indices.atom_tbl.clone()); - try_eval_session!(setup_indices(wam, &mut indices)); + try_eval_session!(setup_indices(wam, clause_name!("builtins"), &mut indices)); compile_listing(wam, src, indices) } diff --git a/src/prolog/machine/dynamic_database.rs b/src/prolog/machine/dynamic_database.rs index 4fe01ad6..4351f263 100644 --- a/src/prolog/machine/dynamic_database.rs +++ b/src/prolog/machine/dynamic_database.rs @@ -6,8 +6,27 @@ use prolog::machine::compile::*; use prolog::machine::machine_errors::*; use prolog::num::ToPrimitive; +use std::io::Read; + impl Machine { - fn get_predicate_key(&self, name: RegType, arity: RegType) -> PredicateKey { + pub(super) + fn atom_tbl_of(&self, name: &ClauseName) -> TabledData { + match name { + &ClauseName::User(ref rc) => rc.table.clone(), + _ => self.indices.atom_tbl() + } + } + + fn compile_into_machine(&mut self, src: R, name: ClauseName) -> EvalSession + { + match name.owning_module().as_str() { + "user" => compile_user_module(self, src), + _ => compile_into_module(self, src, name) + } + } + + fn get_predicate_key(&self, name: RegType, arity: RegType) -> PredicateKey + { let name = self.machine_st[name].clone(); let arity = self.machine_st[arity].clone(); @@ -25,12 +44,10 @@ impl Machine { (name, arity) } - fn print_new_dynamic_clause(&self, addrs: VecDeque, name: RegType, arity: RegType) + fn print_new_dynamic_clause(&self, addrs: VecDeque, name: ClauseName, arity: usize) -> String { let mut output = PrinterOutputter::new(); - let (name, arity) = self.get_predicate_key(name, arity); - output.append(format!(":- dynamic({}/{}). ", name.as_str(), arity).as_str()); for addr in addrs { @@ -56,18 +73,19 @@ impl Machine { self.indices.dynamic_code_dir.remove(&(name, arity)); } - fn handle_eval_result_from_dynamic_compile(&mut self, pred_str: String, src: ClauseName) + fn handle_eval_result_from_dynamic_compile(&mut self, pred_str: String, name: ClauseName, + src: ClauseName) { let machine_st = mem::replace(&mut self.machine_st, MachineState::new()); - let result = compile_user_module(self, pred_str.as_bytes()); + let result = self.compile_into_machine(pred_str.as_bytes(), name); self.machine_st = machine_st; if let EvalSession::Error(err) = result { let h = self.machine_st.heap.h; let stub = MachineError::functor_stub(src, 1); let err = MachineError::session_error(h, err); - let err = self.machine_st.error_form(err, stub); + let err = self.machine_st.error_form(err, stub); self.machine_st.throw_exception(err); } @@ -75,6 +93,8 @@ impl Machine { fn recompile_dynamic_predicate(&mut self, place: DynamicAssertPlace) { + let (name, arity) = self.get_predicate_key(temp_v!(3), temp_v!(4)); + let stub = MachineError::functor_stub(place.predicate_name(), 1); let pred_str = match self.machine_st.try_from_list(temp_v!(2), stub) { Ok(addrs) => { @@ -82,16 +102,17 @@ impl Machine { let added_clause = self.machine_st[temp_v!(1)].clone(); place.push_to_queue(&mut addrs, added_clause); - self.print_new_dynamic_clause(addrs, temp_v!(3), temp_v!(4)) + self.print_new_dynamic_clause(addrs, name.clone(), arity) }, Err(err) => return self.machine_st.throw_exception(err) }; - self.handle_eval_result_from_dynamic_compile(pred_str, place.predicate_name()); + self.handle_eval_result_from_dynamic_compile(pred_str, name, place.predicate_name()); } - fn retract_from_dynamic_predicate(&mut self) { + fn retract_from_dynamic_predicate(&mut self) + { let index = self.machine_st[temp_v!(3)].clone(); let index = match self.machine_st.store(self.machine_st.deref(index)) { Addr::Con(Constant::Number(Number::Integer(n))) => n.to_usize().unwrap(), @@ -99,6 +120,8 @@ impl Machine { }; let stub = MachineError::functor_stub(clause_name!("retract"), 1); + let (name, arity) = self.get_predicate_key(temp_v!(1), temp_v!(2)); + let pred_str = match self.machine_st.try_from_list(temp_v!(4), stub) { Ok(addrs) => { let mut addrs = VecDeque::from(addrs); @@ -109,13 +132,13 @@ impl Machine { return; } - self.print_new_dynamic_clause(addrs, temp_v!(1), temp_v!(2)) + self.print_new_dynamic_clause(addrs, name.clone(), arity) }, Err(err) => return self.machine_st.throw_exception(err) }; - self.handle_eval_result_from_dynamic_compile(pred_str, clause_name!("retract")); + self.handle_eval_result_from_dynamic_compile(pred_str, name, clause_name!("retract")); } pub(super) diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index 5e7d2af9..566f30f1 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -369,6 +369,7 @@ impl MachineState { pub enum SessionError { CannotOverwriteBuiltIn(ClauseName), + CannotOverwriteDynamicClause(ClauseName), CannotOverwriteImport(ClauseName), ModuleDoesNotContainExport, ModuleNotFound, diff --git a/src/prolog/machine/machine_indices.rs b/src/prolog/machine/machine_indices.rs index 26db373b..2f515086 100644 --- a/src/prolog/machine/machine_indices.rs +++ b/src/prolog/machine/machine_indices.rs @@ -389,16 +389,27 @@ pub struct IndexStore { } impl IndexStore { - pub fn predicate_exists(&self, name: ClauseName, arity: usize, + pub fn predicate_exists(&self, name: ClauseName, module: ClauseName, arity: usize, op_spec: Option<(usize, Specifier)>) -> bool { - match ClauseType::from(name, arity, op_spec) { - ClauseType::Named(name, arity, _) => - self.code_dir.contains_key(&(name, arity)), - ClauseType::Op(op_decl, ..) => - self.code_dir.contains_key(&(op_decl.name(), op_decl.arity())), - _ => true + match self.modules.get(&module) { + Some(module) => + match ClauseType::from(name, arity, op_spec) { + ClauseType::Named(name, arity, _) => + module.code_dir.contains_key(&(name, arity)), + ClauseType::Op(op_decl, ..) => + module.code_dir.contains_key(&(op_decl.name(), op_decl.arity())), + _ => true + }, + None => + match ClauseType::from(name, arity, op_spec) { + ClauseType::Named(name, arity, _) => + self.code_dir.contains_key(&(name, arity)), + ClauseType::Op(op_decl, ..) => + self.code_dir.contains_key(&(op_decl.name(), op_decl.arity())), + _ => true + } } } diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index bee13631..2eacb5d7 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -226,7 +226,7 @@ impl Machine { // ensure we don't try to overwrite an existing predicate from a different module. if !existing_idx.is_undefined() && !idx.is_undefined() { // allow the overwriting of user-level predicates by all other predicates. - if existing_idx.module_name() == key.0.owning_module() { + if existing_idx.module_name().as_str() == "user" { continue; } diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 3b7bcb32..e4ff895e 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -370,18 +370,18 @@ impl MachineState { }, &SystemClauseType::GetCurrentPredicateList => { let mut addrs = vec![]; - + for ((name, arity), idx) in indices.code_dir.iter() { if idx.is_undefined() { continue; } - + let h = self.heap.h; self.heap.push(HeapCellValue::NamedStr(2, clause_name!("/"), Some((400, YFX)))); self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::Atom(name.clone(), None)))); self.heap.push(heap_integer!(*arity)); - + addrs.push(Addr::Str(h)); } @@ -614,12 +614,16 @@ impl MachineState { self.fail = match self.store(self.deref(head)) { Addr::Str(s) => match self.heap[s].clone() { - HeapCellValue::NamedStr(arity, name, op_spec) => - indices.predicate_exists(name, arity, op_spec), + HeapCellValue::NamedStr(arity, name, op_spec) => { + let module = name.owning_module(); + indices.predicate_exists(name, module, arity, op_spec) + }, _ => unreachable!() }, - Addr::Con(Constant::Atom(name, op_spec)) => - indices.predicate_exists(name, 0, op_spec), + Addr::Con(Constant::Atom(name, op_spec)) => { + let module = name.owning_module(); + indices.predicate_exists(name, module, 0, op_spec) + }, head => { let err = MachineError::type_error(ValidType::Callable, head); let stub = MachineError::functor_stub(clause_name!("clause"), 2); diff --git a/src/prolog/write.rs b/src/prolog/write.rs index 641135b6..9d3dd8e5 100644 --- a/src/prolog/write.rs +++ b/src/prolog/write.rs @@ -249,19 +249,27 @@ impl fmt::Display for IndexingInstruction { impl fmt::Display for SessionError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + &SessionError::CannotOverwriteDynamicClause(ref msg) => + write!(f, "cannot overwrite dynamic predicate {}", msg), &SessionError::CannotOverwriteBuiltIn(ref msg) => write!(f, "cannot overwrite {}", msg), &SessionError::CannotOverwriteImport(ref msg) => write!(f, "cannot overwrite import {}", msg), &SessionError::ModuleNotFound => write!(f, "module not found."), - &SessionError::ModuleDoesNotContainExport => write!(f, "module does not contain claimed export."), - &SessionError::QueryFailure => write!(f, "false."), - &SessionError::QueryFailureWithException(ref e) => write!(f, "{}", error_string(e)), + &SessionError::ModuleDoesNotContainExport => + write!(f, "module does not contain claimed export."), + &SessionError::QueryFailure => + write!(f, "false."), + &SessionError::QueryFailureWithException(ref e) => + write!(f, "{}", error_string(e)), &SessionError::OpIsInfixAndPostFix => write!(f, "cannot define an op to be both postfix and infix."), - &SessionError::NamelessEntry => write!(f, "the predicate head is not an atom or clause."), - &SessionError::ParserError(ref e) => write!(f, "syntax_error({})", e.as_str()), - &SessionError::UserPrompt => write!(f, "enter predicate at [user] prompt") + &SessionError::NamelessEntry => + write!(f, "the predicate head is not an atom or clause."), + &SessionError::ParserError(ref e) => + write!(f, "syntax_error({})", e.as_str()), + &SessionError::UserPrompt => + write!(f, "enter predicate at [user] prompt") } } } -- 2.54.0