add operator exports to module declarations, treat them separately from predicate exports (#230)"
This commit is contained in:
@@ -179,7 +179,7 @@ pub enum Declaration {
|
|||||||
NonCountedBacktracking(ClauseName, usize), // name, arity
|
NonCountedBacktracking(ClauseName, usize), // name, arity
|
||||||
Op(OpDecl),
|
Op(OpDecl),
|
||||||
UseModule(ModuleSource),
|
UseModule(ModuleSource),
|
||||||
UseQualifiedModule(ModuleSource, Vec<PredicateKey>),
|
UseQualifiedModule(ModuleSource, Vec<ModuleExport>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Declaration {
|
impl Declaration {
|
||||||
@@ -216,15 +216,20 @@ impl OpDecl {
|
|||||||
self.insert_into_op_dir(clause_name!(""), op_dir, 0);
|
self.insert_into_op_dir(clause_name!(""), op_dir, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_into_op_dir(&self, module: ClauseName, op_dir: &mut OpDir, prec: usize) {
|
#[inline]
|
||||||
let (spec, name) = (self.1, self.2.clone());
|
pub fn fixity(&self) -> Fixity {
|
||||||
|
match self.1 {
|
||||||
let fixity = match spec {
|
|
||||||
XFY | XFX | YFX => Fixity::In,
|
XFY | XFX | YFX => Fixity::In,
|
||||||
XF | YF => Fixity::Post,
|
XF | YF => Fixity::Post,
|
||||||
FX | FY => Fixity::Pre,
|
FX | FY => Fixity::Pre,
|
||||||
_ => return,
|
_ => unreachable!()
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_into_op_dir(&self, module: ClauseName, op_dir: &mut OpDir, prec: usize) {
|
||||||
|
let (spec, name) = (self.1, self.2.clone());
|
||||||
|
|
||||||
|
let fixity = self.fixity();
|
||||||
|
|
||||||
match op_dir.get(&(name.clone(), fixity)) {
|
match op_dir.get(&(name.clone(), fixity)) {
|
||||||
Some(cell) => {
|
Some(cell) => {
|
||||||
@@ -322,10 +327,16 @@ pub fn fetch_op_spec(
|
|||||||
|
|
||||||
pub type ModuleDir = IndexMap<ClauseName, Module>;
|
pub type ModuleDir = IndexMap<ClauseName, Module>;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum ModuleExport {
|
||||||
|
OpDecl(OpDecl),
|
||||||
|
PredicateKey(PredicateKey),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ModuleDecl {
|
pub struct ModuleDecl {
|
||||||
pub name: ClauseName,
|
pub name: ClauseName,
|
||||||
pub exports: Vec<PredicateKey>,
|
pub exports: Vec<ModuleExport>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
:- module(atts, [attribute/1, call_residue_vars/2, '$absent_attr'/2,
|
:- module(atts, [op(1199, fx, attribute), call_residue_vars/2,
|
||||||
'$copy_attr_list'/2, '$get_attr'/2, '$put_attr'/2,
|
'$absent_attr'/2, '$copy_attr_list'/2, '$get_attr'/2,
|
||||||
'$absent_from_list'/2, '$get_from_list'/3,
|
'$put_attr'/2, '$absent_from_list'/2,
|
||||||
'$add_to_list'/3, '$del_attr'/3, '$del_attr_step'/3,
|
'$get_from_list'/3, '$add_to_list'/3, '$del_attr'/3,
|
||||||
'$del_attr_buried'/4, '$default_attr_list'/4]).
|
'$del_attr_step'/3, '$del_attr_buried'/4,
|
||||||
|
'$default_attr_list'/4]).
|
||||||
|
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
:- use_module(library(terms)).
|
:- use_module(library(terms)).
|
||||||
|
|
||||||
:- op(1199, fx, attribute).
|
|
||||||
|
|
||||||
/* represent the list of attributes belonging to a variable,
|
/* represent the list of attributes belonging to a variable,
|
||||||
of a particular module, as a list of terms of the form
|
of a particular module, as a list of terms of the form
|
||||||
Module:put_atts(V, ListOfAtts). */
|
Module:put_atts(V, ListOfAtts). */
|
||||||
|
|||||||
@@ -1,35 +1,19 @@
|
|||||||
:- op(400, yfx, /).
|
:- op(400, yfx, /).
|
||||||
|
|
||||||
/* this is an implementation specific declarative operator used to implement call_with_inference_limit/3
|
|
||||||
and setup_call_cleanup/3. switches to the default trust_me and retry_me_else. Indexing choice
|
|
||||||
instructions are unchanged. */
|
|
||||||
:- op(700, fx, non_counted_backtracking).
|
|
||||||
|
|
||||||
:- module(builtins, [(=)/2, (\=)/2, (\+)/1, (^)/2, (\)/1, (+)/1,
|
|
||||||
(+)/2, (**)/2, (*)/2, (-)/1, (-)/2, (/)/2, (/\)/2, (\/)/2,
|
|
||||||
(is)/2, (xor)/2, (div)/2, (//)/2, (rdiv)/2, (<<)/2, (>>)/2,
|
|
||||||
(mod)/2, (rem)/2, (>)/2, (<)/2, (=\=)/2, (=:=)/2, (>=)/2,
|
|
||||||
(=<)/2, (',')/2, (->)/2, (;)/2, (=..)/2, (==)/2, (\==)/2,
|
|
||||||
(@=<)/2, (@>=)/2, (@<)/2, (@>)/2, (:)/2, abolish/1, asserta/1,
|
|
||||||
assertz/1, atom_chars/2, atom_codes/2, atom_concat/3,
|
|
||||||
atom_length/2, bagof/3, catch/3, char_code/2, clause/2,
|
|
||||||
current_op/3, current_predicate/1, current_prolog_flag/2,
|
|
||||||
expand_goal/2, expand_term/2, fail/0, false/0, findall/3,
|
|
||||||
findall/4, get_char/1, halt/0, number_chars/2, number_codes/2,
|
|
||||||
once/1, op/3, read_term/2, repeat/0, retract/1,
|
|
||||||
set_prolog_flag/2, setof/3, sub_atom/5, subsumes_term/2,
|
|
||||||
term_variables/2, throw/1, true/0, unify_with_occurs_check/2,
|
|
||||||
write/1, write_canonical/1, write_term/2, writeq/1]).
|
|
||||||
|
|
||||||
% module resolution operator.
|
% module resolution operator.
|
||||||
:- op(600, xfy, :).
|
:- op(600, xfy, :).
|
||||||
|
|
||||||
user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :-
|
user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :-
|
||||||
expand_op_list([Op | OtherOps], Pred, Spec, OpResults).
|
'$expand_op_list'([Op | OtherOps], Pred, Spec, OpResults).
|
||||||
|
|
||||||
expand_op_list([], _, _, []).
|
'$expand_op_list'([], _, _, []).
|
||||||
expand_op_list([Op | OtherOps], Pred, Spec, [(:- op(Pred, Spec, Op)) | OtherResults]) :-
|
'$expand_op_list'([Op | OtherOps], Pred, Spec, [(:- op(Pred, Spec, Op)) | OtherResults]) :-
|
||||||
expand_op_list(OtherOps, Pred, Spec, OtherResults).
|
'$expand_op_list'(OtherOps, Pred, Spec, OtherResults).
|
||||||
|
|
||||||
|
/* this is an implementation specific declarative operator used to implement call_with_inference_limit/3
|
||||||
|
and setup_call_cleanup/3. switches to the default trust_me and retry_me_else. Indexing choice
|
||||||
|
instructions are unchanged. */
|
||||||
|
:- op(700, fx, non_counted_backtracking).
|
||||||
|
|
||||||
% arithmetic operators.
|
% arithmetic operators.
|
||||||
:- op(700, xfx, is).
|
:- op(700, xfx, is).
|
||||||
@@ -37,8 +21,7 @@ expand_op_list([Op | OtherOps], Pred, Spec, [(:- op(Pred, Spec, Op)) | OtherResu
|
|||||||
:- op(400, yfx, *).
|
:- op(400, yfx, *).
|
||||||
:- op(200, xfy, [**, ^]).
|
:- op(200, xfy, [**, ^]).
|
||||||
:- op(500, yfx, [/\, \/, xor]).
|
:- op(500, yfx, [/\, \/, xor]).
|
||||||
:- op(400, yfx, [div, //, rdiv]).
|
:- op(400, yfx, [div, //, rdiv, <<, >>, mod, rem]).
|
||||||
:- op(400, yfx, [<<, >>, mod, rem]).
|
|
||||||
:- op(200, fy, [+, -, \]).
|
:- op(200, fy, [+, -, \]).
|
||||||
|
|
||||||
% arithmetic comparison operators.
|
% arithmetic comparison operators.
|
||||||
@@ -47,9 +30,6 @@ expand_op_list([Op | OtherOps], Pred, Spec, [(:- op(Pred, Spec, Op)) | OtherResu
|
|||||||
% term comparison.
|
% term comparison.
|
||||||
:- op(700, xfx, [==, \==, @=<, @>=, @<, @>]).
|
:- op(700, xfx, [==, \==, @=<, @>=, @<, @>]).
|
||||||
|
|
||||||
% the maximum arity flag. needs to be replaced with current_prolog_flag(max_arity, MAX_ARITY).
|
|
||||||
max_arity(255).
|
|
||||||
|
|
||||||
% conditional operators.
|
% conditional operators.
|
||||||
:- op(1050, xfy, ->).
|
:- op(1050, xfy, ->).
|
||||||
:- op(1100, xfy, ;).
|
:- op(1100, xfy, ;).
|
||||||
@@ -58,6 +38,24 @@ max_arity(255).
|
|||||||
:- op(700, xfx, [=, =.., \=]).
|
:- op(700, xfx, [=, =.., \=]).
|
||||||
:- op(900, fy, \+).
|
:- op(900, fy, \+).
|
||||||
|
|
||||||
|
:- module(builtins, [(=)/2, (\=)/2, (\+)/1, (',')/2, (->)/2, (;)/2,
|
||||||
|
(=..)/2, (:)/2, abolish/1, asserta/1, assertz/1,
|
||||||
|
atom_chars/2, atom_codes/2, atom_concat/3,
|
||||||
|
atom_length/2, bagof/3, catch/3, char_code/2,
|
||||||
|
clause/2, current_op/3, current_predicate/1,
|
||||||
|
current_prolog_flag/2, expand_goal/2,
|
||||||
|
expand_term/2, fail/0, false/0, findall/3,
|
||||||
|
findall/4, get_char/1, halt/0, max_arity/1,
|
||||||
|
number_chars/2, number_codes/2, once/1, op/3,
|
||||||
|
read_term/2, repeat/0, retract/1,
|
||||||
|
set_prolog_flag/2, setof/3, sub_atom/5,
|
||||||
|
subsumes_term/2, term_variables/2, throw/1,
|
||||||
|
true/0, unify_with_occurs_check/2, write/1,
|
||||||
|
write_canonical/1, write_term/2, writeq/1]).
|
||||||
|
|
||||||
|
% the maximum arity flag. needs to be replaced with current_prolog_flag(max_arity, MAX_ARITY).
|
||||||
|
max_arity(255).
|
||||||
|
|
||||||
% unify.
|
% unify.
|
||||||
X = X.
|
X = X.
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,9 @@
|
|||||||
Public operators.
|
Public operators.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
:- op(300, fy, ~).
|
:- module(clpb, [op(300, fy, ~),
|
||||||
:- op(500, yfx, #).
|
op(500, yfx, #),
|
||||||
|
sat/1,
|
||||||
:- module(clpb, [
|
|
||||||
sat/1,
|
|
||||||
taut/2,
|
taut/2,
|
||||||
labeling/1,
|
labeling/1,
|
||||||
sat_count/2,
|
sat_count/2,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
:- op(1200, xfx, -->).
|
|
||||||
% :- op(1105, xfy, ('|')).
|
% :- op(1105, xfy, ('|')).
|
||||||
|
|
||||||
:- module(dcgs, [phrase/2, phrase/3]).
|
:- module(dcgs, [op(1200, xfx, -->), phrase/2, phrase/3]).
|
||||||
|
|
||||||
:- use_module(library(lists), [append/3]).
|
:- use_module(library(lists), [append/3]).
|
||||||
|
|
||||||
|
|||||||
@@ -574,7 +574,7 @@ impl ListingCompiler {
|
|||||||
submodule: ClauseName,
|
submodule: ClauseName,
|
||||||
code_repo: &mut CodeRepo,
|
code_repo: &mut CodeRepo,
|
||||||
flags: MachineFlags,
|
flags: MachineFlags,
|
||||||
exports: &Vec<PredicateKey>,
|
exports: &Vec<ModuleExport>,
|
||||||
wam_indices: &mut IndexStore,
|
wam_indices: &mut IndexStore,
|
||||||
indices: &mut IndexStore,
|
indices: &mut IndexStore,
|
||||||
) -> Result<(), SessionError> {
|
) -> Result<(), SessionError> {
|
||||||
@@ -712,6 +712,24 @@ impl ListingCompiler {
|
|||||||
(len, queue_len)
|
(len, queue_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn submit_op(
|
||||||
|
&mut self,
|
||||||
|
wam: &Machine,
|
||||||
|
indices: &mut IndexStore,
|
||||||
|
op_decl: &OpDecl,
|
||||||
|
) -> Result<(), SessionError> {
|
||||||
|
let spec = get_desc(
|
||||||
|
op_decl.name(),
|
||||||
|
composite_op!(
|
||||||
|
self.module.is_some(),
|
||||||
|
&wam.indices.op_dir,
|
||||||
|
&mut indices.op_dir
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
op_decl.submit(self.get_module_name(), spec, &mut indices.op_dir)
|
||||||
|
}
|
||||||
|
|
||||||
fn process_decl(
|
fn process_decl(
|
||||||
&mut self,
|
&mut self,
|
||||||
decl: Declaration,
|
decl: Declaration,
|
||||||
@@ -730,6 +748,7 @@ impl ListingCompiler {
|
|||||||
.code_repo
|
.code_repo
|
||||||
.compile_hook(hook, flags)
|
.compile_hook(hook, flags)
|
||||||
.map_err(SessionError::from);
|
.map_err(SessionError::from);
|
||||||
|
|
||||||
wam.code_repo.truncate_terms(key, len, queue_len);
|
wam.code_repo.truncate_terms(key, len, queue_len);
|
||||||
|
|
||||||
result
|
result
|
||||||
@@ -738,16 +757,7 @@ impl ListingCompiler {
|
|||||||
Ok(self.add_non_counted_bt_flag(name, arity))
|
Ok(self.add_non_counted_bt_flag(name, arity))
|
||||||
}
|
}
|
||||||
Declaration::Op(op_decl) => {
|
Declaration::Op(op_decl) => {
|
||||||
let spec = get_desc(
|
self.submit_op(wam, indices, &op_decl)
|
||||||
op_decl.name(),
|
|
||||||
composite_op!(
|
|
||||||
self.module.is_some(),
|
|
||||||
&wam.indices.op_dir,
|
|
||||||
&mut indices.op_dir
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
op_decl.submit(self.get_module_name(), spec, &mut indices.op_dir)
|
|
||||||
}
|
}
|
||||||
Declaration::UseModule(ModuleSource::Library(name)) => {
|
Declaration::UseModule(ModuleSource::Library(name)) => {
|
||||||
let name = if !wam.indices.modules.contains_key(&name) {
|
let name = if !wam.indices.modules.contains_key(&name) {
|
||||||
@@ -779,6 +789,12 @@ impl ListingCompiler {
|
|||||||
let module_name = module_decl.name.clone();
|
let module_name = module_decl.name.clone();
|
||||||
let atom_tbl = TabledData::new(module_name.to_rc());
|
let atom_tbl = TabledData::new(module_name.to_rc());
|
||||||
|
|
||||||
|
for export in module_decl.exports.iter() {
|
||||||
|
if let ModuleExport::OpDecl(ref op_decl) = export {
|
||||||
|
self.submit_op(wam, indices, op_decl)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(self.module = Some(Module::new(module_decl, atom_tbl)))
|
Ok(self.module = Some(Module::new(module_decl, atom_tbl)))
|
||||||
} else {
|
} else {
|
||||||
Err(SessionError::from(ParserError::InvalidModuleDecl))
|
Err(SessionError::from(ParserError::InvalidModuleDecl))
|
||||||
@@ -959,8 +975,9 @@ fn compile_work_impl(
|
|||||||
if let Some(mut module) = compiler.module.take() {
|
if let Some(mut module) = compiler.module.take() {
|
||||||
if module.is_impromptu_module {
|
if module.is_impromptu_module {
|
||||||
module.module_decl.exports = indices.code_dir.keys().cloned()
|
module.module_decl.exports = indices.code_dir.keys().cloned()
|
||||||
.filter(|(name, _)| name.owning_module().as_str() != "builtins")
|
.filter(|(name, _)| name.owning_module().as_str() != "builtins")
|
||||||
.collect();
|
.map(ModuleExport::PredicateKey)
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut clause_code_generator =
|
let mut clause_code_generator =
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ use crate::prolog::machine::machine_errors::*;
|
|||||||
use crate::prolog::machine::machine_indices::*;
|
use crate::prolog::machine::machine_indices::*;
|
||||||
use crate::prolog::machine::machine_state::*;
|
use crate::prolog::machine::machine_state::*;
|
||||||
use crate::prolog::machine::modules::*;
|
use crate::prolog::machine::modules::*;
|
||||||
|
use crate::prolog::machine::toplevel::*;
|
||||||
use crate::prolog::read::PrologStream;
|
use crate::prolog::read::PrologStream;
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
@@ -139,7 +140,7 @@ impl SubModuleUser for IndexStore {
|
|||||||
code_repo: &mut CodeRepo,
|
code_repo: &mut CodeRepo,
|
||||||
flags: MachineFlags,
|
flags: MachineFlags,
|
||||||
submodule: &Module,
|
submodule: &Module,
|
||||||
exports: &Vec<PredicateKey>,
|
exports: &Vec<ModuleExport>,
|
||||||
) -> Result<(), SessionError> {
|
) -> Result<(), SessionError> {
|
||||||
use_qualified_module(self, submodule, exports)?;
|
use_qualified_module(self, submodule, exports)?;
|
||||||
submodule
|
submodule
|
||||||
@@ -400,30 +401,64 @@ impl Machine {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_predicate_indicator_list(&mut self) -> Vec<PredicateKey>
|
fn extract_module_export_list(&mut self) -> Result<Vec<ModuleExport>, ParserError>
|
||||||
{
|
{
|
||||||
let export_list = self.machine_st[temp_v!(2)].clone();
|
let mut export_list = self.machine_st[temp_v!(2)].clone();
|
||||||
let mut export_list = self.machine_st.store(self.machine_st.deref(export_list));
|
|
||||||
let mut exports = vec![];
|
let mut exports = vec![];
|
||||||
|
|
||||||
while let Addr::Lis(l) = export_list {
|
while let Addr::Lis(l) = self.machine_st.store(self.machine_st.deref(export_list)) {
|
||||||
match &self.machine_st.heap[l] {
|
match &self.machine_st.heap[l] {
|
||||||
&HeapCellValue::Addr(Addr::Str(s)) => {
|
&HeapCellValue::Addr(Addr::Str(s)) => {
|
||||||
let name = match &self.machine_st.heap[s+1] {
|
match &self.machine_st.heap[s] {
|
||||||
&HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) =>
|
HeapCellValue::NamedStr(arity, ref name, _)
|
||||||
name.clone(),
|
if *arity == 2 && name.as_str() == "/" => {
|
||||||
_ =>
|
let name = match &self.machine_st.heap[s+1] {
|
||||||
unreachable!()
|
&HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) =>
|
||||||
};
|
name.clone(),
|
||||||
|
_ =>
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let arity = match &self.machine_st.heap[s+2] {
|
||||||
|
&HeapCellValue::Addr(Addr::Con(Constant::Integer(ref arity))) =>
|
||||||
|
arity.to_usize().unwrap(),
|
||||||
|
_ =>
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.push(ModuleExport::PredicateKey((name, arity)));
|
||||||
|
}
|
||||||
|
HeapCellValue::NamedStr(arity, ref name, _)
|
||||||
|
if *arity == 3 && name.as_str() == "op" => {
|
||||||
|
let name = match &self.machine_st.heap[s+3] {
|
||||||
|
&HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) =>
|
||||||
|
name.clone(),
|
||||||
|
_ =>
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
let arity = match &self.machine_st.heap[s+2] {
|
let spec = match &self.machine_st.heap[s+2] {
|
||||||
&HeapCellValue::Addr(Addr::Con(Constant::Integer(ref arity))) =>
|
&HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) =>
|
||||||
arity.to_usize().unwrap(),
|
name.clone(),
|
||||||
_ =>
|
_ =>
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let prec = match &self.machine_st.heap[s+1] {
|
||||||
|
&HeapCellValue::Addr(Addr::Con(Constant::Integer(ref arity))) =>
|
||||||
|
arity.to_usize().unwrap(),
|
||||||
|
_ =>
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
exports.push((name, arity));
|
exports.push(ModuleExport::OpDecl(to_op_decl(
|
||||||
|
prec,
|
||||||
|
spec.as_str(),
|
||||||
|
name,
|
||||||
|
)?));
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
@@ -431,7 +466,7 @@ impl Machine {
|
|||||||
export_list = self.machine_st.heap[l+1].as_addr(l+1);
|
export_list = self.machine_st.heap[l+1].as_addr(l+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports
|
Ok(exports)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn use_module<ToSource>(&mut self, to_src: ToSource)
|
fn use_module<ToSource>(&mut self, to_src: ToSource)
|
||||||
@@ -489,7 +524,13 @@ impl Machine {
|
|||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
let exports = self.extract_predicate_indicator_list();
|
let exports = match self.extract_module_export_list() {
|
||||||
|
Ok(exports) => exports,
|
||||||
|
Err(e) => {
|
||||||
|
self.throw_session_error(SessionError::from(e), (clause_name!("use_module"), 2));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let load_result = match to_src(name) {
|
let load_result = match to_src(name) {
|
||||||
ModuleSource::Library(name) =>
|
ModuleSource::Library(name) =>
|
||||||
@@ -521,7 +562,7 @@ impl Machine {
|
|||||||
self.code_repo.cached_query = cached_query;
|
self.code_repo.cached_query = cached_query;
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
self.throw_session_error(e, (clause_name!("use_module"), 1));
|
self.throw_session_error(e, (clause_name!("use_module"), 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,64 +137,55 @@ pub trait SubModuleUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn remove_module(&mut self, mod_name: ClauseName, module: &Module) {
|
fn remove_module(&mut self, mod_name: ClauseName, module: &Module) {
|
||||||
for (name, arity) in module.module_decl.exports.iter().cloned() {
|
for export in module.module_decl.exports.iter().cloned() {
|
||||||
let name = name.defrock_brackets();
|
match export {
|
||||||
|
ModuleExport::PredicateKey((name, arity)) => {
|
||||||
|
let name = name.defrock_brackets();
|
||||||
|
|
||||||
match self.get_code_index((name.clone(), arity), mod_name.clone()) {
|
match self.get_code_index((name.clone(), arity), mod_name.clone()) {
|
||||||
Some(CodeIndex(ref code_idx)) => {
|
Some(CodeIndex(ref code_idx)) => {
|
||||||
if &code_idx.borrow().1 != &module.module_decl.name {
|
if &code_idx.borrow().1 != &module.module_decl.name {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
self.remove_code_index((name.clone(), arity));
|
|
||||||
|
|
||||||
// remove or respecify ops.
|
|
||||||
if arity == 2 {
|
|
||||||
if let Some(mod_name) = self.get_op_module_name(name.clone(), Fixity::In) {
|
|
||||||
if mod_name == module.module_decl.name {
|
|
||||||
self.op_dir().remove(&(name.clone(), Fixity::In));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if arity == 1 {
|
|
||||||
if let Some(mod_name) = self.get_op_module_name(name.clone(), Fixity::Pre) {
|
|
||||||
if mod_name == module.module_decl.name {
|
|
||||||
self.op_dir().remove(&(name.clone(), Fixity::Pre));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(mod_name) = self.get_op_module_name(name.clone(), Fixity::Post)
|
self.remove_code_index((name.clone(), arity));
|
||||||
{
|
|
||||||
if mod_name == module.module_decl.name {
|
// remove or respecify ops.
|
||||||
self.op_dir().remove(&(name.clone(), Fixity::Post));
|
if arity == 2 {
|
||||||
|
if let Some(mod_name) = self.get_op_module_name(name.clone(), Fixity::In) {
|
||||||
|
if mod_name == module.module_decl.name {
|
||||||
|
self.op_dir().remove(&(name.clone(), Fixity::In));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if arity == 1 {
|
||||||
|
if let Some(mod_name) = self.get_op_module_name(name.clone(), Fixity::Pre) {
|
||||||
|
if mod_name == module.module_decl.name {
|
||||||
|
self.op_dir().remove(&(name.clone(), Fixity::Pre));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mod_name) = self.get_op_module_name(name.clone(), Fixity::Post)
|
||||||
|
{
|
||||||
|
if mod_name == module.module_decl.name {
|
||||||
|
self.op_dir().remove(&(name.clone(), Fixity::Post));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
_ => {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
ModuleExport::OpDecl(op_decl) => {
|
||||||
|
let op_dir = self.op_dir();
|
||||||
|
op_dir.remove(&(op_decl.name(), op_decl.fixity()));
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true on successful import.
|
// returns true on successful import.
|
||||||
fn import_decl(&mut self, name: ClauseName, arity: usize, submodule: &Module) -> bool {
|
fn import_decl(&mut self, name: ClauseName, arity: usize, submodule: &Module) -> bool {
|
||||||
let name = name.defrock_brackets();
|
let name = name.defrock_brackets();
|
||||||
let mut found_op = false;
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut insert_op_dir = |fix| {
|
|
||||||
if let Some(op_data) = submodule.op_dir.get(&(name.clone(), fix)) {
|
|
||||||
self.op_dir().insert((name.clone(), fix), op_data.clone());
|
|
||||||
found_op = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if arity == 1 {
|
|
||||||
insert_op_dir(Fixity::Pre);
|
|
||||||
insert_op_dir(Fixity::Post);
|
|
||||||
} else if arity == 2 {
|
|
||||||
insert_op_dir(Fixity::In);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(code_data) = submodule.code_dir.get(&(name.clone(), arity)) {
|
if let Some(code_data) = submodule.code_dir.get(&(name.clone(), arity)) {
|
||||||
let name = name.with_table(submodule.atom_tbl.clone());
|
let name = name.with_table(submodule.atom_tbl.clone());
|
||||||
@@ -205,7 +196,7 @@ pub trait SubModuleUser {
|
|||||||
self.insert_dir_entry(name, arity, code_data.clone());
|
self.insert_dir_entry(name, arity, code_data.clone());
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
found_op || submodule.is_impromptu_module
|
submodule.is_impromptu_module
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,30 +205,58 @@ pub trait SubModuleUser {
|
|||||||
_: &mut CodeRepo,
|
_: &mut CodeRepo,
|
||||||
_: MachineFlags,
|
_: MachineFlags,
|
||||||
_: &Module,
|
_: &Module,
|
||||||
_: &Vec<PredicateKey>,
|
_: &Vec<ModuleExport>,
|
||||||
|
) -> Result<(), SessionError>;
|
||||||
|
|
||||||
|
fn use_module(
|
||||||
|
&mut self,
|
||||||
|
_: &mut CodeRepo,
|
||||||
|
_: MachineFlags,
|
||||||
|
_: &Module
|
||||||
) -> Result<(), SessionError>;
|
) -> Result<(), SessionError>;
|
||||||
fn use_module(&mut self, _: &mut CodeRepo, _: MachineFlags, _: &Module) -> Result<(), SessionError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_qualified_module<User>(
|
pub fn use_qualified_module<User>(
|
||||||
user: &mut User,
|
user: &mut User,
|
||||||
submodule: &Module,
|
submodule: &Module,
|
||||||
exports: &Vec<PredicateKey>,
|
exports: &Vec<ModuleExport>,
|
||||||
) -> Result<(), SessionError>
|
) -> Result<(), SessionError>
|
||||||
where
|
where
|
||||||
User: SubModuleUser,
|
User: SubModuleUser,
|
||||||
{
|
{
|
||||||
for (name, arity) in exports.iter().cloned() {
|
for export in exports.iter().cloned() {
|
||||||
if !submodule
|
match export {
|
||||||
.module_decl
|
ModuleExport::PredicateKey((name, arity)) => {
|
||||||
.exports
|
if !submodule
|
||||||
.contains(&(name.clone(), arity))
|
.module_decl
|
||||||
{
|
.exports
|
||||||
continue;
|
.contains(&ModuleExport::PredicateKey((name.clone(), arity)))
|
||||||
}
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if !user.import_decl(name, arity, submodule) {
|
if !user.import_decl(name, arity, submodule) {
|
||||||
return Err(SessionError::ModuleDoesNotContainExport);
|
return Err(SessionError::ModuleDoesNotContainExport);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ModuleExport::OpDecl(op_decl) => {
|
||||||
|
if !submodule
|
||||||
|
.module_decl
|
||||||
|
.exports
|
||||||
|
.contains(&ModuleExport::OpDecl(op_decl.clone()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let op_dir = user.op_dir();
|
||||||
|
let prec = op_decl.0;
|
||||||
|
|
||||||
|
op_decl.insert_into_op_dir(
|
||||||
|
submodule.module_decl.name.clone(),
|
||||||
|
op_dir,
|
||||||
|
prec,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,9 +267,23 @@ pub fn use_module<User: SubModuleUser>(
|
|||||||
user: &mut User,
|
user: &mut User,
|
||||||
submodule: &Module,
|
submodule: &Module,
|
||||||
) -> Result<(), SessionError> {
|
) -> Result<(), SessionError> {
|
||||||
for (name, arity) in submodule.module_decl.exports.iter().cloned() {
|
for export in submodule.module_decl.exports.iter().cloned() {
|
||||||
if !user.import_decl(name, arity, submodule) {
|
match export {
|
||||||
return Err(SessionError::ModuleDoesNotContainExport);
|
ModuleExport::PredicateKey((name, arity)) => {
|
||||||
|
if !user.import_decl(name, arity, submodule) {
|
||||||
|
return Err(SessionError::ModuleDoesNotContainExport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModuleExport::OpDecl(op_decl) => {
|
||||||
|
let op_dir = user.op_dir();
|
||||||
|
let prec = op_decl.0;
|
||||||
|
|
||||||
|
op_decl.insert_into_op_dir(
|
||||||
|
submodule.module_decl.name.clone(),
|
||||||
|
op_dir,
|
||||||
|
prec,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +316,7 @@ impl SubModuleUser for Module {
|
|||||||
_: &mut CodeRepo,
|
_: &mut CodeRepo,
|
||||||
_: MachineFlags,
|
_: MachineFlags,
|
||||||
submodule: &Module,
|
submodule: &Module,
|
||||||
exports: &Vec<PredicateKey>,
|
exports: &Vec<ModuleExport>,
|
||||||
) -> Result<(), SessionError> {
|
) -> Result<(), SessionError> {
|
||||||
use_qualified_module(self, submodule, exports)?;
|
use_qualified_module(self, submodule, exports)?;
|
||||||
|
|
||||||
|
|||||||
@@ -196,13 +196,13 @@ fn setup_op_decl(
|
|||||||
to_op_decl(prec, spec.as_str(), name)
|
to_op_decl(prec, spec.as_str(), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_predicate_indicator(mut term: Term) -> Result<PredicateKey, ParserError> {
|
fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, ParserError> {
|
||||||
match term {
|
match term {
|
||||||
Term::Clause(_, ref name, ref mut terms, Some(_))
|
Term::Clause(_, ref name, ref mut terms, Some(_))
|
||||||
if name.as_str() == "/" && terms.len() == 2 =>
|
if name.as_str() == "/" && terms.len() == 2 =>
|
||||||
{
|
{
|
||||||
let arity = *terms.pop().unwrap();
|
let arity = *terms.pop().unwrap();
|
||||||
let name = *terms.pop().unwrap();
|
let name = *terms.pop().unwrap();
|
||||||
|
|
||||||
let arity = arity
|
let arity = arity
|
||||||
.to_constant()
|
.to_constant()
|
||||||
@@ -210,7 +210,7 @@ fn setup_predicate_indicator(mut term: Term) -> Result<PredicateKey, ParserError
|
|||||||
.and_then(|n| n.to_usize())
|
.and_then(|n| n.to_usize())
|
||||||
.ok_or(ParserError::InvalidModuleExport)?;
|
.ok_or(ParserError::InvalidModuleExport)?;
|
||||||
|
|
||||||
let name = name
|
let name = name
|
||||||
.to_constant()
|
.to_constant()
|
||||||
.and_then(|c| c.to_atom())
|
.and_then(|c| c.to_atom())
|
||||||
.ok_or(ParserError::InvalidModuleExport)?;
|
.ok_or(ParserError::InvalidModuleExport)?;
|
||||||
@@ -221,7 +221,32 @@ fn setup_predicate_indicator(mut term: Term) -> Result<PredicateKey, ParserError
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_module_decl(mut terms: Vec<Box<Term>>) -> Result<ModuleDecl, ParserError> {
|
fn setup_module_export(
|
||||||
|
mut term: Term,
|
||||||
|
atom_tbl: TabledData<Atom>,
|
||||||
|
) -> Result<ModuleExport, ParserError> {
|
||||||
|
setup_predicate_indicator(&mut term)
|
||||||
|
.map(ModuleExport::PredicateKey)
|
||||||
|
.or_else(|_| {
|
||||||
|
if let Term::Clause(_, name, terms, _) = term {
|
||||||
|
if terms.len() == 3 && name.as_str() == "op" {
|
||||||
|
Ok(ModuleExport::OpDecl(setup_op_decl(
|
||||||
|
terms,
|
||||||
|
atom_tbl
|
||||||
|
)?))
|
||||||
|
} else {
|
||||||
|
Err(ParserError::InvalidModuleDecl)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ParserError::InvalidModuleDecl)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_module_decl(
|
||||||
|
mut terms: Vec<Box<Term>>,
|
||||||
|
atom_tbl: TabledData<Atom>,
|
||||||
|
) -> Result<ModuleDecl, ParserError> {
|
||||||
let mut export_list = *terms.pop().unwrap();
|
let mut export_list = *terms.pop().unwrap();
|
||||||
let name = terms
|
let name = terms
|
||||||
.pop()
|
.pop()
|
||||||
@@ -230,10 +255,12 @@ fn setup_module_decl(mut terms: Vec<Box<Term>>) -> Result<ModuleDecl, ParserErro
|
|||||||
.and_then(|c| c.to_atom())
|
.and_then(|c| c.to_atom())
|
||||||
.ok_or(ParserError::InvalidModuleDecl)?;
|
.ok_or(ParserError::InvalidModuleDecl)?;
|
||||||
|
|
||||||
let mut exports = Vec::new();
|
let mut exports = vec![];
|
||||||
|
|
||||||
while let Term::Cons(_, t1, t2) = export_list {
|
while let Term::Cons(_, t1, t2) = export_list {
|
||||||
exports.push(setup_predicate_indicator(*t1)?);
|
let module_export = setup_module_export(*t1, atom_tbl.clone())?;
|
||||||
|
|
||||||
|
exports.push(module_export);
|
||||||
export_list = *t2;
|
export_list = *t2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,14 +284,18 @@ fn setup_use_module_decl(mut terms: Vec<Box<Term>>) -> Result<ModuleSource, Pars
|
|||||||
.map(|c| ModuleSource::Library(c))
|
.map(|c| ModuleSource::Library(c))
|
||||||
.ok_or(ParserError::InvalidUseModuleDecl)
|
.ok_or(ParserError::InvalidUseModuleDecl)
|
||||||
}
|
}
|
||||||
Term::Constant(_, Constant::Atom(ref name, _)) => Ok(ModuleSource::File(name.clone())),
|
Term::Constant(_, Constant::Atom(ref name, _)) =>
|
||||||
|
Ok(ModuleSource::File(name.clone())),
|
||||||
_ => Err(ParserError::InvalidUseModuleDecl),
|
_ => Err(ParserError::InvalidUseModuleDecl),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type UseModuleExport = (ModuleSource, Vec<PredicateKey>);
|
type UseModuleExport = (ModuleSource, Vec<ModuleExport>);
|
||||||
|
|
||||||
fn setup_qualified_import(mut terms: Vec<Box<Term>>) -> Result<UseModuleExport, ParserError> {
|
fn setup_qualified_import(
|
||||||
|
mut terms: Vec<Box<Term>>,
|
||||||
|
atom_tbl: TabledData<Atom>,
|
||||||
|
) -> Result<UseModuleExport, ParserError> {
|
||||||
let mut export_list = *terms.pop().unwrap();
|
let mut export_list = *terms.pop().unwrap();
|
||||||
let module_src = match *terms.pop().unwrap() {
|
let module_src = match *terms.pop().unwrap() {
|
||||||
Term::Clause(_, ref name, ref mut terms, None)
|
Term::Clause(_, ref name, ref mut terms, None)
|
||||||
@@ -282,10 +313,10 @@ fn setup_qualified_import(mut terms: Vec<Box<Term>>) -> Result<UseModuleExport,
|
|||||||
_ => Err(ParserError::InvalidUseModuleDecl),
|
_ => Err(ParserError::InvalidUseModuleDecl),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let mut exports = Vec::new();
|
let mut exports = vec![];
|
||||||
|
|
||||||
while let Term::Cons(_, t1, t2) = export_list {
|
while let Term::Cons(_, t1, t2) = export_list {
|
||||||
exports.push(setup_predicate_indicator(*t1)?);
|
exports.push(setup_module_export(*t1, atom_tbl.clone())?);
|
||||||
export_list = *t2;
|
export_list = *t2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,19 +491,19 @@ fn setup_declaration<'a, 'b, 'c, R: Read>(
|
|||||||
("op", 3) =>
|
("op", 3) =>
|
||||||
Ok(Declaration::Op(setup_op_decl(terms, indices.atom_tbl())?)),
|
Ok(Declaration::Op(setup_op_decl(terms, indices.atom_tbl())?)),
|
||||||
("module", 2) =>
|
("module", 2) =>
|
||||||
Ok(Declaration::Module(setup_module_decl(terms)?)),
|
Ok(Declaration::Module(setup_module_decl(terms, indices.atom_tbl())?)),
|
||||||
("use_module", 1) =>
|
("use_module", 1) =>
|
||||||
Ok(Declaration::UseModule(setup_use_module_decl(terms)?)),
|
Ok(Declaration::UseModule(setup_use_module_decl(terms)?)),
|
||||||
("use_module", 2) => {
|
("use_module", 2) => {
|
||||||
let (name, exports) = setup_qualified_import(terms)?;
|
let (name, exports) = setup_qualified_import(terms, indices.atom_tbl())?;
|
||||||
Ok(Declaration::UseQualifiedModule(name, exports))
|
Ok(Declaration::UseQualifiedModule(name, exports))
|
||||||
}
|
}
|
||||||
("non_counted_backtracking", 1) => {
|
("non_counted_backtracking", 1) => {
|
||||||
let (name, arity) = setup_predicate_indicator(*terms.pop().unwrap())?;
|
let (name, arity) = setup_predicate_indicator(&mut *terms.pop().unwrap())?;
|
||||||
Ok(Declaration::NonCountedBacktracking(name, arity))
|
Ok(Declaration::NonCountedBacktracking(name, arity))
|
||||||
}
|
}
|
||||||
("dynamic", 1) => {
|
("dynamic", 1) => {
|
||||||
let (name, arity) = setup_predicate_indicator(*terms.pop().unwrap())?;
|
let (name, arity) = setup_predicate_indicator(&mut *terms.pop().unwrap())?;
|
||||||
Ok(Declaration::Dynamic(name, arity))
|
Ok(Declaration::Dynamic(name, arity))
|
||||||
}
|
}
|
||||||
("initialization", 1) => {
|
("initialization", 1) => {
|
||||||
|
|||||||
@@ -134,7 +134,7 @@
|
|||||||
; '$print_exception'(E)
|
; '$print_exception'(E)
|
||||||
).
|
).
|
||||||
|
|
||||||
'$predicate_indicator'(Source, PI) :-
|
'$module_export'(Source, PI) :-
|
||||||
( nonvar(PI) ->
|
( nonvar(PI) ->
|
||||||
( PI = Name / Arity ->
|
( PI = Name / Arity ->
|
||||||
( var(Name) -> throw(error(instantiation_error, Source))
|
( var(Name) -> throw(error(instantiation_error, Source))
|
||||||
@@ -145,7 +145,20 @@
|
|||||||
)
|
)
|
||||||
; throw(error(type_error(integer, Arity), Source))
|
; throw(error(type_error(integer, Arity), Source))
|
||||||
)
|
)
|
||||||
; throw(error(type_error(predicate_indicator, PI), Source))
|
; PI = op(Prec, Spec, Name) ->
|
||||||
|
( integer(Prec) ->
|
||||||
|
( \+ atom(Name) ->
|
||||||
|
throw(error(type_error(atom, Name), Source))
|
||||||
|
; Prec < 0 ->
|
||||||
|
throw(error(domain_error(not_less_than_zero, Prec), Source))
|
||||||
|
; Prec > 1200 ->
|
||||||
|
throw(error(domain_error(operator_precision, Prec), Source))
|
||||||
|
; memberchk(Spec, [xfy, yfx, xfx, fx, fy, yf, xf])
|
||||||
|
; throw(error(domain_error(operator_specification, Spec), Source))
|
||||||
|
)
|
||||||
|
; throw(error(type_error(integer, Prec), Source))
|
||||||
|
)
|
||||||
|
; throw(error(type_error(module_export, PI), Source))
|
||||||
)
|
)
|
||||||
; throw(error(instantiation_error, Source))
|
; throw(error(instantiation_error, Source))
|
||||||
).
|
).
|
||||||
@@ -167,9 +180,11 @@ use_module(Module) :-
|
|||||||
use_module(Module, QualifiedExports) :-
|
use_module(Module, QualifiedExports) :-
|
||||||
( nonvar(Module) ->
|
( nonvar(Module) ->
|
||||||
( list_si(QualifiedExports) ->
|
( list_si(QualifiedExports) ->
|
||||||
maplist('$predicate_indicator'(use_module/2), QualifiedExports), !,
|
maplist('$module_export'(use_module/2), QualifiedExports) ->
|
||||||
( Module = library(Filename) -> '$use_qualified_module'(Filename, QualifiedExports)
|
( Module = library(Filename) ->
|
||||||
; atom(Module) -> '$use_qualified_module_from_file'(Module, QualifiedExports)
|
'$use_qualified_module'(Filename, QualifiedExports)
|
||||||
|
; atom(Module) ->
|
||||||
|
'$use_qualified_module_from_file'(Module, QualifiedExports)
|
||||||
; throw(error(invalid_module_specifier, use_module/2))
|
; throw(error(invalid_module_specifier, use_module/2))
|
||||||
)
|
)
|
||||||
; throw(error(type_error(list, QualifiedExports), use_module/2))
|
; throw(error(type_error(list, QualifiedExports), use_module/2))
|
||||||
|
|||||||
Reference in New Issue
Block a user