* `bb_get/2`
* `bb_put/2`
* `between/3`
-* `call/1..17`
+* `call/1..62`
* `call_cleanup/2`
* `call_with_inference_limit/3`
* `call_residue_vars/2`
AtomCodes,
AtomLength,
CallAttributeGoals,
- CallN,
CharCode,
CharsToNumber,
ClearAttrVarBindings,
LiftedHeapLength,
ModuleAssertDynamicPredicateToFront,
ModuleAssertDynamicPredicateToBack,
+ ModuleExists,
ModuleOf,
ModuleRetractClause,
NoSuchPredicate,
&SystemClauseType::AtomCodes => clause_name!("$atom_codes"),
&SystemClauseType::AtomLength => clause_name!("$atom_length"),
&SystemClauseType::CallAttributeGoals => clause_name!("$call_attribute_goals"),
- &SystemClauseType::CallN => clause_name!("$call"),
&SystemClauseType::CharCode => clause_name!("$char_code"),
&SystemClauseType::CharsToNumber => clause_name!("$chars_to_number"),
&SystemClauseType::ClearAttributeGoals => clause_name!("$clear_attribute_goals"),
clause_name!("$module_assertz")
}
&SystemClauseType::ModuleHeadIsDynamic => clause_name!("$module_head_is_dynamic"),
+ &SystemClauseType::ModuleExists => clause_name!("$module_exists"),
&SystemClauseType::ModuleOf => clause_name!("$module_of"),
&SystemClauseType::NoSuchPredicate => clause_name!("$no_such_predicate"),
&SystemClauseType::NumberToChars => clause_name!("$number_to_chars"),
("$module_assertz", 5) => Some(SystemClauseType::ModuleAssertDynamicPredicateToBack),
("$asserta", 4) => Some(SystemClauseType::AssertDynamicPredicateToFront),
("$assertz", 4) => Some(SystemClauseType::AssertDynamicPredicateToBack),
- ("$call", 1) => Some(SystemClauseType::CallN),
("$call_attribute_goals", 2) => Some(SystemClauseType::CallAttributeGoals),
("$char_code", 2) => Some(SystemClauseType::CharCode),
("$chars_to_number", 2) => Some(SystemClauseType::CharsToNumber),
("$install_inference_counter", 3) => Some(SystemClauseType::InstallInferenceCounter),
("$lh_length", 1) => Some(SystemClauseType::LiftedHeapLength),
("$maybe", 0) => Some(SystemClauseType::Maybe),
+ ("$module_exists", 1) => Some(SystemClauseType::ModuleExists),
("$module_of", 2) => Some(SystemClauseType::ModuleOf),
("$module_retract_clause", 5) => Some(SystemClauseType::ModuleRetractClause),
("$module_head_is_dynamic", 2) => Some(SystemClauseType::ModuleHeadIsDynamic),
#[derive(Clone, PartialEq, Eq)]
pub enum ClauseType {
BuiltIn(BuiltInClauseType),
+ CallN,
Hook(CompileTimeHook),
Inlined(InlinedClauseType),
Named(ClauseName, usize, CodeIndex), // name, arity, index.
pub fn name(&self) -> ClauseName {
match self {
&ClauseType::BuiltIn(ref built_in) => built_in.name(),
+ &ClauseType::CallN => clause_name!("call"),
&ClauseType::Hook(ref hook) => hook.name(),
&ClauseType::Inlined(ref inlined) => clause_name!(inlined.name()),
&ClauseType::Op(ref name, ..) => name.clone(),
.unwrap_or_else(|| {
if let Some(spec) = spec {
ClauseType::Op(name, spec, CodeIndex::default())
+ } else if name.as_str() == "call" {
+ ClauseType::CallN
} else {
ClauseType::Named(name, arity, CodeIndex::default())
}
fn new(term: &'a QueryTerm) -> Self {
match term {
+ &QueryTerm::Clause(ref cell, ClauseType::CallN, ref terms, _) => {
+ let state = TermIterState::Clause(Level::Root, 1, cell, ClauseType::CallN, terms);
+ QueryIterator {
+ state_stack: vec![state],
+ }
+ }
&QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
let state = TermIterState::Clause(Level::Root, 0, cell, ct.clone(), terms);
QueryIterator {
TermIterState::Clause(lvl, child_num, cell, ct, child_terms) => {
if child_num == child_terms.len() {
match ct {
+ ClauseType::CallN => {
+ self.push_subterm(Level::Shallow, child_terms[0].as_ref())
+ }
ClauseType::Named(..) | ClauseType::Op(..) => {
return match lvl {
Level::Root => None,
ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) => {
result.push(term)
}
+ ChunkedTerm::BodyTerm(&QueryTerm::Clause(
+ _,
+ ClauseType::CallN,
+ ref subterms,
+ _,
+ )) => {
+ result.push(term);
+ arity = subterms.len() + 1;
+ break;
+ }
ChunkedTerm::BodyTerm(qt) => {
result.push(term);
arity = qt.arity();
(:)/7, (:)/8, (:)/9, (:)/10, (:)/11, (:)/12,
abolish/1, asserta/1, assertz/1, atom_chars/2,
atom_codes/2, atom_concat/3, atom_length/2,
- bagof/3, call/1, call/2, call/3, call/4,
- call/5, call/6, call/7, call/8, call/9,
- call/10, call/11, call/12, call/13,
- call/14, call/15, call/16, call/17,
- catch/3, char_code/2, clause/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,
write_canonical/1, write_term/2, writeq/1]).
-%% call/{1..17}
-
-call(Goal) :-
- ( '$module_of'(Module, Goal),
- Module:goal_expansion(Goal, ExpandedGoal) ->
- true
- ; Goal = ExpandedGoal
- ),
- '$call'(ExpandedGoal).
-
-'$call_body'(Goal, As) :-
- Goal =.. F,
- lists:append(F, As, Fs),
- ExpandedGoal0 =.. Fs,
- ( '$module_of'(Module, ExpandedGoal0),
- Module:goal_expansion(ExpandedGoal0, ExpandedGoal1) ->
- true
- ; ExpandedGoal1 = ExpandedGoal0
- ),
- '$call'(ExpandedGoal1).
-
-call(Goal, A1) :-
- '$call_body'(Goal, [A1]).
-
-call(Goal, A1, A2) :-
- '$call_body'(Goal, [A1, A2]).
-
-call(Goal, A1, A2, A3) :-
- '$call_body'(Goal, [A1, A2, A3]).
-
-call(Goal, A1, A2, A3, A4) :-
- '$call_body'(Goal, [A1, A2, A3, A4]).
-
-call(Goal, A1, A2, A3, A4, A5) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5]).
-
-call(Goal, A1, A2, A3, A4, A5, A6) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6]).
-
-call(Goal, A1, A2, A3, A4, A5, A6, A7) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6, A7]).
-
-call(Goal, A1, A2, A3, A4, A5, A6, A7, A8) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6, A7, A8]).
-
-call(Goal, A1, A2, A3, A4, A5, A6, A7, A8, A9) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6, A7, A8, A9]).
-
-call(Goal, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]).
-
-call(Goal, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11]).
-
-call(Goal, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12]).
-
-call(Goal, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13]).
-
-call(Goal, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14]).
-
-call(Goal, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15]).
-
-call(Goal, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16) :-
- '$call_body'(Goal, [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16]).
-
% the maximum arity flag. needs to be replaced with
% current_prolog_flag(max_arity, MAX_ARITY).
max_arity(255).
);
Some(RefOrOwned::Owned(call_clause))
}
+ &CodePtr::CallN(arity, _, last_call) => {
+ let call_clause = call_clause!(ClauseType::CallN, arity, 0, last_call);
+ Some(RefOrOwned::Owned(call_clause))
+ }
&CodePtr::VerifyAttrInterrupt(p) => Some(RefOrOwned::Borrowed(&self.code[p])),
&CodePtr::DynamicTransaction(..) => None,
}
}
-
- pub(super)
- fn at_end_of_hook(&self, hook: CompileTimeHook, cp: LocalCodePtr) -> bool {
- match hook {
- CompileTimeHook::UserGoalExpansion | CompileTimeHook::GoalExpansion => {
- let len = self.goal_expanders.len();
-
- if len > 0 {
- cp == LocalCodePtr::UserGoalExpansion(len - 1)
- } else {
- true
- }
- }
- CompileTimeHook::UserTermExpansion | CompileTimeHook::TermExpansion => {
- let len = self.term_expanders.len();
-
- if len > 0 {
- cp == LocalCodePtr::UserTermExpansion(len - 1)
- } else {
- true
- }
- }
- }
- }
-
}
results.and_then(|results| compile_work_impl(&mut compiler, wam, indices, results))
.or_else(|e| {
+ wam.indices.take_module(module_name.clone());
compiler.print_error(&e);
Err(e)
})?;
],
SharedOpDesc::new(400, YFX)
));
+
stub.append(&mut functor!(
":",
2,
module_name
))))
}
-
+
#[inline]
pub fn module_name(&self) -> ClauseName {
self.0.borrow().1.clone()
#[derive(Clone, PartialEq)]
pub enum CodePtr {
BuiltInClause(BuiltInClauseType, LocalCodePtr), // local is the successor call.
+ CallN(usize, LocalCodePtr, bool), // arity, local, last call.
Local(LocalCodePtr),
DynamicTransaction(DynamicTransactionType, LocalCodePtr), // the type of transaction, the return pointer.
REPL(REPLCodePtr, LocalCodePtr), // the REPL code, the return pointer.
pub fn local(&self) -> LocalCodePtr {
match self {
&CodePtr::BuiltInClause(_, ref local)
+ | &CodePtr::CallN(_, ref local, _)
| &CodePtr::Local(ref local) => local.clone(),
&CodePtr::VerifyAttrInterrupt(p) => LocalCodePtr::DirEntry(p),
&CodePtr::REPL(_, p) | &CodePtr::DynamicTransaction(_, p) => p,
| p @ CodePtr::VerifyAttrInterrupt(_)
| p @ CodePtr::DynamicTransaction(..) => p,
CodePtr::Local(local) => CodePtr::Local(local + rhs),
- CodePtr::BuiltInClause(_, local) => {
+ CodePtr::BuiltInClause(_, local) | CodePtr::CallN(_, local, _) => {
CodePtr::Local(local + rhs)
}
}
pub(super) op_dir: OpDir,
}
-impl IndexStore {
+impl IndexStore {
pub fn predicate_exists(
&self,
name: ClauseName,
}
pub fn add_term_and_goal_expansion_indices(&mut self) {
- self.code_dir.insert((clause_name!("term_expansion"), 2),
+ self.code_dir.insert((clause_name!("term_expansion"), 2),
CodeIndex(Rc::new(RefCell::new(
(IndexPtr::UserTermExpansion,
clause_name!("user"))
))));
- self.code_dir.insert((clause_name!("goal_expansion"), 2),
+ self.code_dir.insert((clause_name!("goal_expansion"), 2),
CodeIndex(Rc::new(RefCell::new(
(IndexPtr::UserGoalExpansion,
clause_name!("user"))
))));
}
-
+
#[inline]
pub fn remove_clause_subsection(&mut self, module: ClauseName, name: ClauseName, arity: usize) {
self.dynamic_code_dir.swap_remove(&(module, name, arity));
pub(super) last_call: bool,
pub(crate) heap_locs: HeapVarDict,
pub(crate) flags: MachineFlags,
- pub(crate) at_end_of_expansion: LocalCodePtr
+ pub(crate) at_end_of_expansion: bool
}
impl MachineState {
fn call_n(
&mut self,
machine_st: &mut MachineState,
- name: ClauseName,
arity: usize,
indices: &mut IndexStore,
parsing_stream: &mut PrologStream,
) -> CallResult {
- match ClauseType::from(name.clone(), arity, None) {
- ClauseType::BuiltIn(built_in) => {
- machine_st.setup_built_in_call(built_in.clone());
- self.call_builtin(machine_st, &built_in, indices, parsing_stream)?;
- }
- ClauseType::Inlined(inlined) => {
- machine_st.execute_inlined(&inlined);
+ if let Some((name, arity)) = machine_st.setup_call_n(arity) {
+ match ClauseType::from(name.clone(), arity, None) {
+ ClauseType::BuiltIn(built_in) => {
+ machine_st.setup_built_in_call(built_in.clone());
+ self.call_builtin(machine_st, &built_in, indices, parsing_stream)?;
+ }
+ ClauseType::CallN => {
+ machine_st.handle_internal_call_n(arity);
- if machine_st.last_call {
- machine_st.p = CodePtr::Local(machine_st.cp);
+ if machine_st.fail {
+ return Ok(());
+ }
+
+ machine_st.p = CodePtr::CallN(arity, machine_st.p.local(), machine_st.last_call);
}
- }
- ClauseType::Op(..) | ClauseType::Named(..) => {
- let module = name.owning_module();
+ ClauseType::Inlined(inlined) => {
+ machine_st.execute_inlined(&inlined);
- if let Some(idx) = indices.get_code_index((name.clone(), arity), module) {
- self.context_call(machine_st, name, arity, idx, indices)?;
- } else {
- try_in_situ(machine_st, name, arity, indices, machine_st.last_call)?;
+ if machine_st.last_call {
+ machine_st.p = CodePtr::Local(machine_st.cp);
+ }
}
- }
- ClauseType::Hook(_) | ClauseType::System(_) => {
- let name = Addr::Con(Constant::Atom(name, None));
- let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
+ ClauseType::Op(..) | ClauseType::Named(..) => {
+ let module = name.owning_module();
- return Err(machine_st
- .error_form(MachineError::type_error(ValidType::Callable, name), stub));
- }
+ if let Some(idx) = indices.get_code_index((name.clone(), arity), module) {
+ self.context_call(machine_st, name, arity, idx, indices)?;
+ } else {
+ try_in_situ(machine_st, name, arity, indices, machine_st.last_call)?;
+ }
+ }
+ ClauseType::Hook(_) | ClauseType::System(_) => {
+ let name = Addr::Con(Constant::Atom(name, None));
+ let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
+
+ return Err(machine_st
+ .error_form(MachineError::type_error(ValidType::Callable, name), stub));
+ }
+ };
}
Ok(())
fn call_n(
&mut self,
machine_st: &mut MachineState,
- name: ClauseName,
arity: usize,
indices: &mut IndexStore,
parsing_stream: &mut PrologStream,
) -> CallResult {
self.prev_policy
- .call_n(machine_st, name, arity, indices, parsing_stream)?;
+ .call_n(machine_st, arity, indices, parsing_stream)?;
self.increment(machine_st)
}
}
last_call: false,
heap_locs: HeapVarDict::new(),
flags: MachineFlags::default(),
- at_end_of_expansion: LocalCodePtr::TopLevel(0, 0),
+ at_end_of_expansion: false
}
}
last_call: false,
heap_locs: HeapVarDict::new(),
flags: MachineFlags::default(),
- at_end_of_expansion: LocalCodePtr::TopLevel(0, 0),
+ at_end_of_expansion: false
}
}
);
}
+ pub(super) fn handle_internal_call_n(&mut self, arity: usize) {
+ let arity = arity + 1;
+ let pred = self.registers[1].clone();
+
+ for i in 2..arity {
+ self.registers[i - 1] = self.registers[i].clone();
+ }
+
+ if arity > 1 {
+ self.registers[arity - 1] = pred;
+ return;
+ }
+
+ self.fail = true;
+ }
+
+ pub(super) fn setup_call_n(&mut self, arity: usize) -> Option<PredicateKey> {
+ let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
+ let addr = self.store(self.deref(self.registers[arity].clone()));
+
+ let (name, narity) = match addr {
+ Addr::Str(a) => {
+ let result = self.heap[a].clone();
+
+ if let HeapCellValue::NamedStr(narity, name, _) = result {
+ if narity + arity > 63 {
+ let representation_error = self.error_form(
+ MachineError::representation_error(RepFlag::MaxArity),
+ stub,
+ );
+
+ self.throw_exception(representation_error);
+ return None;
+ }
+
+ for i in (1 .. arity).rev() {
+ self.registers[i + narity] = self.registers[i].clone();
+ }
+
+ for i in 1 .. narity + 1 {
+ self.registers[i] = self.heap[a + i].as_addr(a + i);
+ }
+
+ (name, narity)
+ } else {
+ self.fail = true;
+ return None;
+ }
+ }
+ Addr::Con(Constant::Atom(name, _)) => (name, 0),
+ Addr::HeapCell(_) | Addr::StackCell(_, _) => {
+ let instantiation_error =
+ self.error_form(MachineError::instantiation_error(), stub);
+ self.throw_exception(instantiation_error);
+
+ return None;
+ }
+ _ => {
+ let type_error =
+ self.error_form(MachineError::type_error(ValidType::Callable, addr), stub);
+ self.throw_exception(type_error);
+
+ return None;
+ }
+ };
+
+ Some((name, arity + narity - 1))
+ }
+
pub(super) fn unwind_stack(&mut self) {
self.b = self.block;
self.truncate_stack();
self,
call_policy.call_builtin(self, ct, indices, parsing_stream)
),
+ &ClauseType::CallN => try_or_fail!(
+ self,
+ call_policy.call_n(self, arity, indices, parsing_stream)
+ ),
&ClauseType::Hook(ref hook) => try_or_fail!(self, call_policy.compile_hook(self, hook)),
&ClauseType::Inlined(ref ct) => {
self.execute_inlined(ct);
self.mode = MachineMode::Write;
self.registers = vec![Addr::HeapCell(0); MAX_ARITY + 1]; // self.registers[0] is never used.
self.block = 0;
- self.at_end_of_expansion = LocalCodePtr::TopLevel(0, 0);
self.ball.reset();
self.heap_locs.clear();
return Ok(());
}
&SystemClauseType::AtEndOfExpansion => {
- self.at_end_of_expansion = self.p.local();
+ if self.cp == LocalCodePtr::TopLevel(0, 0) {
+ self.at_end_of_expansion = true;
+ }
}
&SystemClauseType::AtomChars => {
let a1 = self[temp_v!(1)].clone();
return Ok(());
}
- &SystemClauseType::CallN => {
- let (name, arity) = match self.store(self.deref(self[temp_v!(1)].clone())) {
- Addr::Str(a) => {
- let result = self.heap[a].clone();
-
- if let HeapCellValue::NamedStr(arity, name, _) = result {
- if arity > MAX_ARITY {
- let stub = MachineError::functor_stub(
- clause_name!("$call"),
- 1,
- );
-
- return Err(self.error_form(
- MachineError::representation_error(RepFlag::MaxArity),
- stub,
- ));
- }
-
- for i in 1 .. arity + 1 {
- self.registers[i] = self.heap[a + i].as_addr(a + i);
- }
-
- (name, arity)
- } else {
- unreachable!()
- }
- }
- Addr::Con(Constant::Atom(name, _)) =>
- (name, 0),
- Addr::HeapCell(_) | Addr::StackCell(_, _) => {
- let stub = MachineError::functor_stub(
- clause_name!("$call"),
- 1,
- );
-
- return Err(self.error_form(MachineError::instantiation_error(), stub));
- }
- addr => {
- let stub = MachineError::functor_stub(
- clause_name!("$call"),
- 1,
- );
-
- return Err(self.error_form(
- MachineError::type_error(ValidType::Callable, addr),
- stub,
- ));
- }
- };
-
- return call_policy.call_n(
- self,
- name,
- arity,
- indices,
- current_input_stream,
- );
- }
&SystemClauseType::CharsToNumber => {
let stub = MachineError::functor_stub(clause_name!("number_chars"), 2);
}
};
}
+ &SystemClauseType::ModuleExists => {
+ let module = self.store(self.deref(self[temp_v!(1)].clone()));
+
+ match module {
+ Addr::Con(Constant::Atom(ref name, _)) => {
+ self.fail = !indices.modules.contains_key(name);
+ }
+ _ => unreachable!()
+ };
+ }
&SystemClauseType::ModuleOf => {
let module = self.store(self.deref(self[temp_v!(2)].clone()));
wam.code_repo.cached_query = code;
self.cp = LocalCodePtr::TopLevel(0, 0);
- self.at_end_of_expansion = self.cp;
+ self.at_end_of_expansion = false;
self.query_stepper(
&mut wam.indices,
&mut readline::input_stream(),
);
- if self.fail || wam.code_repo.at_end_of_hook(hook, self.at_end_of_expansion) {
+ if self.fail || self.at_end_of_expansion {
self.reset_with_heap_preservation();
None
} else {
self.queue.push_back(clauses);
Ok(QueryTerm::Jump(stub))
}
+ ("\\+", 1) => {
+ terms.push(Box::new(Term::Constant(
+ Cell::default(),
+ Constant::Atom(clause_name!("$fail"), None)
+ )));
+
+ let conq = Term::Constant(
+ Cell::default(),
+ Constant::Atom(clause_name!("true"), None)
+ );
+
+ let prec = Term::Clause(Cell::default(), clause_name!("->"), terms, None);
+ let terms = vec![Box::new(prec), Box::new(conq)];
+
+ let term = Term::Clause(Cell::default(), clause_name!(";"), terms, None);
+ let (stub, clauses) = self.fabricate_disjunct(term);
+
+ self.queue.push_back(clauses);
+ Ok(QueryTerm::Jump(stub))
+ }
("$get_level", 1) => {
if let Term::Var(_, ref var) = *terms[0] {
Ok(QueryTerm::GetLevelAndUnify(Cell::default(), var.clone()))
Ok(QueryTerm::Clause(Cell::default(), ct, terms, false))
}
}
- arg @ Term::Var(..) => {
- let ct = ClauseType::Named(clause_name!("call"), 1, CodeIndex::default());
- Ok(QueryTerm::Clause(Cell::default(), ct, vec![Box::new(arg)], false))
- }
+ Term::Var(..) => Ok(QueryTerm::Clause(
+ Cell::default(),
+ ClauseType::CallN,
+ vec![Box::new(term)],
+ false,
+ )),
_ => Err(ParserError::InadmissibleQueryTerm),
}
}
!.
'$submit_query_and_print_results'(Term0, VarList) :-
- ( expand_goal(Term0, Term) -> true
+ ( expand_goals(Term0, Term) -> true
; Term0 = Term
),
( '$get_b_value'(B), call(Term), '$write_eqs_and_read_input'(B, VarList),
; throw(error(instantiation_error, use_module/2))
).
+
% expand goals in initialization directives.
user:term_expansion(Term0, (:- initialization(ExpandedGoals))) :-
nonvar(Term0),
Term0 = (:- initialization(Goals)),
- expand_goals(Goals, ExpandedGoals).
+ expand_goals(Goals, ExpandedGoals),
+ Goals \== ExpandedGoals.
-module_expand_goal(UnexpandedGoals, ExpandedGoals) :-
- '$module_of'(Module, UnexpandedGoals),
- Module:goal_expansion(UnexpandedGoals, ExpandedGoals).
+'$module_expand_goal'(UnexpandedGoals, ExpandedGoals) :-
+ ( '$module_of'(Module, UnexpandedGoals),
+ '$module_exists'(Module),
+ Module:goal_expansion(UnexpandedGoals, ExpandedGoals),
+ UnexpandedGoals \== ExpandedGoals ->
+ true
+ ; user:goal_expansion(UnexpandedGoals, ExpandedGoals)
+ ).
expand_goals(UnexpandedGoals, ExpandedGoals) :-
nonvar(UnexpandedGoals),
var(ExpandedGoals),
- ( expand_goal(UnexpandedGoals, Goals) ->
+ ( '$module_expand_goal'(UnexpandedGoals, Goals) ->
true
; Goals = UnexpandedGoals
),
( Goals = (Goal0, Goals0) ->
- ( expand_goal(Goal0, Goal1) ->
+ ( expand_goals(Goal0, Goal1) ->
expand_goals(Goals0, Goals1),
thread_goals(Goal1, ExpandedGoals, Goals1, (','))
; expand_goals(Goals0, Goals1),
expand_goals(Goals0, ExpandedGoals0),
expand_goals(Goals1, ExpandedGoals1),
ExpandedGoals = (ExpandedGoals0 ; ExpandedGoals1)
+ ; Goals = (\+ Goals0) ->
+ expand_goals(Goals0, Goals1),
+ ExpandedGoals = (\+ Goals1)
; thread_goals(Goals, ExpandedGoals, (','))
; Goals = ExpandedGoals
).