From b1c41f211bfd0f8fe0c60c66511bd29607623308 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 5 Mar 2018 21:13:07 -0700 Subject: [PATCH] add qualified imports --- README.md | 10 +++++- src/prolog/ast.rs | 68 ++++++++++++++++++++++++++------------- src/prolog/io.rs | 14 ++++++++ src/prolog/machine/mod.rs | 58 +++++++++++++++++++++------------ src/prolog/parser | 2 +- 5 files changed, 107 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 54a9be1a..88d5e64e 100644 --- a/README.md +++ b/README.md @@ -263,4 +263,12 @@ prolog> :{{ local_member(X, Xs) :- member(X, Xs). }}: -``` \ No newline at end of file +``` +`use_module` directives can be qualified by adding a list of imports: + +``` +prolog> :- use_module(library(lists), [member/2]). +``` + +A qualified `use_module` can be used to remove imports from the +toplevel by giving it an empty import list. diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index 849c5d69..45810283 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -170,45 +170,69 @@ impl SubModuleUser for Module { pub trait SubModuleUser { fn op_dir(&mut self) -> &mut OpDir; - fn code_dir(&mut self) -> &mut CodeDir; + fn code_dir(&mut self) -> &mut CodeDir; - fn use_module(&mut self, submodule: &Module) -> EvalSession { - for (name, arity) in submodule.module_decl.exports.iter().cloned() { - let name = name.defrock_brackets(); + // returns true on successful import. + fn import_decl(&mut self, name: ClauseName, arity: usize, submodule: &Module) -> bool { + let name = name.defrock_brackets(); - if arity == 1 { - if let Some(op_data) = submodule.op_dir.get(&(name.clone(), Fixity::Pre)) { - self.op_dir().insert((name.clone(), Fixity::Pre), op_data.clone()); - } - - if let Some(op_data) = submodule.op_dir.get(&(name.clone(), Fixity::Post)) { - self.op_dir().insert((name.clone(), Fixity::Post), op_data.clone()); - } - } else if arity == 2 { - if let Some(op_data) = submodule.op_dir.get(&(name.clone(), Fixity::In)) { - self.op_dir().insert((name.clone(), Fixity::In), op_data.clone()); - } + if arity == 1 { + if let Some(op_data) = submodule.op_dir.get(&(name.clone(), Fixity::Pre)) { + self.op_dir().insert((name.clone(), Fixity::Pre), op_data.clone()); } - if self.code_dir().contains_key(&(name.clone(), arity)) { - println!("warning: overwriting {}/{}", &name, arity); + if let Some(op_data) = submodule.op_dir.get(&(name.clone(), Fixity::Post)) { + self.op_dir().insert((name.clone(), Fixity::Post), op_data.clone()); + } + } else if arity == 2 { + if let Some(op_data) = submodule.op_dir.get(&(name.clone(), Fixity::In)) { + self.op_dir().insert((name.clone(), Fixity::In), op_data.clone()); } + } - if let Some(code_data) = submodule.code_dir.get(&(name.clone(), arity)) { - self.code_dir().insert((name, arity), code_data.clone()); - } else { + if self.code_dir().contains_key(&(name.clone(), arity)) { + println!("warning: overwriting {}/{}", &name, arity); + } + + if let Some(code_data) = submodule.code_dir.get(&(name.clone(), arity)) { + self.code_dir().insert((name, arity), code_data.clone()); + true + } else { + false + } + } + + fn use_qualified_module(&mut self, submodule: &Module, exports: Vec) -> EvalSession + { + for (name, arity) in exports { + if !submodule.module_decl.exports.contains(&(name.clone(), arity)) { + continue; + } + + if !self.import_decl(name, arity, submodule) { return EvalSession::from(EvalError::ModuleDoesNotContainExport); } } EvalSession::EntrySuccess } + + fn use_module(&mut self, submodule: &Module) -> EvalSession { + for (name, arity) in submodule.module_decl.exports.iter().cloned() { + if !self.import_decl(name, arity, submodule) { + return EvalSession::from(EvalError::ModuleDoesNotContainExport); + } + } + + EvalSession::EntrySuccess + } } pub enum Declaration { Module(ModuleDecl), Op(OpDecl), - UseModule(ClauseName) + UseModule(ClauseName), + UseQualifiedModule(ClauseName, Vec) } pub enum TopLevel { diff --git a/src/prolog/io.rs b/src/prolog/io.rs index 32b94f88..70f7be5a 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -551,6 +551,8 @@ fn compile_decl(wam: &mut Machine, tl: TopLevel, queue: Vec) -> EvalSe }, TopLevel::Declaration(Declaration::UseModule(name)) => wam.use_module_in_toplevel(name), + TopLevel::Declaration(Declaration::UseQualifiedModule(name, exports)) => + wam.use_qualified_module_in_toplevel(name, exports), TopLevel::Declaration(_) => EvalSession::from(ParserError::InvalidModuleDecl), _ => { @@ -629,6 +631,18 @@ pub fn compile_listing(wam: &mut Machine, src_str: &str) -> EvalSession wam.use_module_in_toplevel(name); }, + TopLevelPacket::Decl(TopLevel::Declaration(Declaration::UseQualifiedModule(name, exports)), _) => { + if let Some(ref submodule) = wam.get_module(name.clone()) { + if let Some(ref mut module) = module { + module.use_qualified_module(submodule, exports); + continue; + } + } else { + return EvalSession::from(EvalError::ModuleNotFound); + } + + wam.use_qualified_module_in_toplevel(name, exports); + }, TopLevelPacket::Decl(TopLevel::Declaration(Declaration::Op(..)), _) => {}, TopLevelPacket::Decl(decl, queue) => { let p = code.len() + wam.code_size(); diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index 7afe46ea..c94b8a06 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -54,14 +54,14 @@ impl<'a> SubModuleUser for MachineCodeIndex<'a> { fn code_dir(&mut self) -> &mut CodeDir { self.code_dir - } + } } impl Machine { pub fn new() -> Self { let atom_tbl = Rc::new(RefCell::new(HashSet::new())); let (code, code_dir, op_dir) = default_build(); - + Machine { ms: MachineState::new(atom_tbl), call_policy: Box::new(DefaultCallPolicy {}), @@ -73,21 +73,21 @@ impl Machine { cached_query: None } } - + fn remove_module(&mut self, module_name: ClauseName) { let iter = if let Some(submodule) = self.modules.get(&module_name) { submodule.module_decl.exports.iter().cloned() } else { return; }; - + for (name, arity) in iter { let name = name.defrock_brackets(); - + match self.code_dir.get(&(name.clone(), arity)).cloned() { Some((_, ref mod_name)) if mod_name == &module_name => { self.code_dir.remove(&(name.clone(), arity)); - + // remove or respecify ops. if arity == 2 { if let Some((_, _, mod_name)) = self.op_dir.get(&(name.clone(), Fixity::In)).cloned() @@ -105,7 +105,7 @@ impl Machine { } if let Some((_, _, mod_name)) = self.op_dir.get(&(name.clone(), Fixity::Post)).cloned() - { + { if mod_name == module_name { self.op_dir.remove(&(name.clone(), Fixity::Post)); } @@ -116,7 +116,7 @@ impl Machine { }; } } - + pub fn failed(&self) -> bool { self.ms.fail } @@ -125,30 +125,46 @@ impl Machine { self.ms.atom_tbl.clone() } + pub fn use_qualified_module_in_toplevel(&mut self, name: ClauseName, exports: Vec) + -> EvalSession + { + self.remove_module(name.clone()); + + match self.modules.get(&name) { + Some(ref module) => { + let mut indices = MachineCodeIndex { code_dir: &mut self.code_dir, + op_dir: &mut self.op_dir }; + + indices.use_qualified_module(module, exports) + }, + None => EvalSession::from(EvalError::ModuleNotFound) + } + } + pub fn use_module_in_toplevel(&mut self, name: ClauseName) -> EvalSession { self.remove_module(name.clone()); - + match self.modules.get(&name) { Some(ref module) => { let mut indices = MachineCodeIndex { code_dir: &mut self.code_dir, op_dir: &mut self.op_dir }; - + indices.use_module(module) }, None => EvalSession::from(EvalError::ModuleNotFound) } } - + pub fn get_module(&self, name: ClauseName) -> Option<&Module> { self.modules.get(&name) } - + pub fn add_batched_code(&mut self, mut code: Code, code_dir: CodeDir) { self.code.append(&mut code); self.code_dir.extend(code_dir.into_iter()); } - pub fn add_batched_ops(&mut self, op_dir: OpDir) { + pub fn add_batched_ops(&mut self, op_dir: OpDir) { self.op_dir.extend(op_dir.into_iter()); } @@ -156,7 +172,7 @@ impl Machine { self.modules.insert(module.module_decl.name.clone(), module); self.code.extend(code.into_iter()); } - + pub fn add_user_code(&mut self, name: ClauseName, arity: usize, code: Code) -> EvalSession { match self.code_dir.get(&(name.clone(), arity)) { @@ -176,14 +192,14 @@ impl Machine { pub fn code_size(&self) -> usize { self.code.len() } - + fn cached_query_size(&self) -> usize { match &self.cached_query { &Some(ref query) => query.len(), _ => 0 } } - + fn execute_instr(&mut self) { let instr = match self.ms.p { @@ -291,7 +307,7 @@ impl Machine { }, &VarData::Temp(cn, _, _) if cn == chunk_num => { let r = var_data.as_reg_type(); - + if r.reg_num() != 0 { let addr = self.ms[r].clone(); heap_locs.insert(var.clone(), addr); @@ -304,8 +320,8 @@ impl Machine { fn run_query(&mut self, alloc_locs: &AllocVarDict, heap_locs: &mut HeapVarDict) { - let end_ptr = CodePtr::TopLevel(0, self.cached_query_size()); - + let end_ptr = CodePtr::TopLevel(0, self.cached_query_size()); + while self.ms.p < end_ptr { if let CodePtr::TopLevel(mut cn, p) = self.ms.p { match &self[CodePtr::TopLevel(cn, p)] { @@ -327,7 +343,7 @@ impl Machine { if heap_locs.is_empty() { self.record_var_places(0, alloc_locs, heap_locs); } - + break; } }; @@ -350,7 +366,7 @@ impl Machine { EvalSession::from(EvalError::QueryFailure) } } - + pub fn submit_query(&mut self, code: Code, alloc_locs: AllocVarDict) -> EvalSession { let mut heap_locs = HashMap::new(); diff --git a/src/prolog/parser b/src/prolog/parser index e95c45f4..54c9d939 160000 --- a/src/prolog/parser +++ b/src/prolog/parser @@ -1 +1 @@ -Subproject commit e95c45f411a278f6bacf2aca5ee2f1448fd1a915 +Subproject commit 54c9d9394b4e17bac5ce7748a483a8b6c8cbb519 -- 2.54.0