add support for setup_call_cleanup

This commit is contained in:
Mark Thom
2018-02-01 23:03:57 -07:00
parent d1c281ac0a
commit 7b5a2786da
14 changed files with 2076 additions and 1810 deletions

View File

@@ -18,7 +18,7 @@ pub trait Allocator<'a>
fn reset(&mut self);
fn reset_contents(&mut self) {}
fn reset_arg(&mut self, usize);
fn reset_arg_at_head(&mut self, &Term);
fn reset_arg_at_head(&mut self, &'a Term);
fn advance_arg(&mut self);

View File

@@ -381,6 +381,7 @@ pub enum QueryTerm {
Inlined(InlinedQueryTerm),
Is(Vec<Box<Term>>),
Jump(JumpStub),
SetupCallCleanup(Vec<Box<Term>>),
Term(Term),
Throw(Vec<Box<Term>>)
}
@@ -399,6 +400,7 @@ impl QueryTerm {
&QueryTerm::Jump(ref vars) => vars.len(),
&QueryTerm::CallN(ref terms) => terms.len(),
&QueryTerm::Cut => 0,
&QueryTerm::SetupCallCleanup(_) => 3,
&QueryTerm::Term(ref term) => term.arity(),
}
}
@@ -422,6 +424,7 @@ pub enum ClauseType<'a> {
Functor,
Is,
Root(&'a TabledRc<Atom>),
SetupCallCleanup,
Throw,
}
@@ -437,6 +440,7 @@ impl<'a> ClauseType<'a> {
&ClauseType::Functor => "functor",
&ClauseType::Is => "is",
&ClauseType::Root(name) => name.as_str(),
&ClauseType::SetupCallCleanup => "setup_call_cleanup",
&ClauseType::Throw => "throw"
}
}
@@ -478,8 +482,8 @@ pub enum ChoiceInstruction {
}
pub enum CutInstruction {
Cut,
GetLevel,
Cut(RegType),
GetLevel(RegType),
NeckCut
}
@@ -792,6 +796,7 @@ pub enum BuiltInInstruction {
GetCutPoint(RegType),
DynamicCompareNumber(CompareNumberQT),
DynamicIs,
InstallCleaner,
InstallNewBlock,
InternalCallN,
IsAtomic(RegType),
@@ -805,6 +810,7 @@ pub enum BuiltInInstruction {
UnwindStack
}
#[derive(Clone)]
pub enum ControlInstruction {
Allocate(usize), // num_frames.
ArgCall,
@@ -813,6 +819,7 @@ pub enum ControlInstruction {
CallN(usize), // arity.
CatchCall,
CatchExecute,
CheckCpExecute,
DisplayCall,
DisplayExecute,
Deallocate,
@@ -822,10 +829,11 @@ pub enum ControlInstruction {
ExecuteN(usize),
FunctorCall,
FunctorExecute,
JmpByCall(usize, usize), // arity, global_offset.
JmpByExecute(usize, usize),
GetCleanerCall,
GotoCall(usize, usize), // p, arity.
GotoExecute(usize, usize), // p, arity.
JmpByCall(usize, usize), // arity, global_offset.
JmpByExecute(usize, usize),
IsCall(RegType, ArithmeticTerm),
IsExecute(RegType, ArithmeticTerm),
Proceed,
@@ -852,6 +860,7 @@ impl ControlInstruction {
&ControlInstruction::FunctorExecute => true,
&ControlInstruction::ThrowCall => true,
&ControlInstruction::ThrowExecute => true,
&ControlInstruction::GetCleanerCall => true,
&ControlInstruction::GotoCall(..) => true,
&ControlInstruction::GotoExecute(..) => true,
&ControlInstruction::Proceed => true,

View File

@@ -65,10 +65,10 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
goto_execute!(32, 2), // goto handle_ball/2.
try_me_else!(10), // handle_ball/2, 32.
allocate!(2),
get_level!(),
get_level!(perm_v!(1)),
fact![get_var_in_fact!(perm_v!(2), 3)],
unify!(),
cut!(),
cut!(perm_v!(1)),
erase_ball!(),
query![put_value!(perm_v!(2), 1)],
deallocate!(),
@@ -96,9 +96,9 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
fail!(), // false/0, 61.
try_me_else!(7), // not/1, 62.
allocate!(1),
get_level!(),
get_level!(perm_v!(1)),
call_n!(1),
cut!(),
cut!(perm_v!(1)),
deallocate!(),
goto_execute!(61, 0),
trust_me!(),
@@ -145,8 +145,8 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
deallocate!(),
goto_execute!(83, 3),
retry_me_else!(10),
allocate!(1),
get_level!(),
allocate!(2),
get_level!(perm_v!(2)),
fact![get_constant!(atom!("!", atom_tbl), temp_v!(2)),
get_var_in_fact!(perm_v!(1), 3)],
neck_cut!(),
@@ -300,7 +300,7 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
proceed!(),
retry_me_else!(11),
allocate!(4),
get_level!(),
get_level!(perm_v!(1)),
fact![get_var_in_fact!(perm_v!(3), 1),
get_list!(Level::Shallow, temp_v!(2)),
unify_variable!(temp_v!(2)),
@@ -309,7 +309,7 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
query![put_value!(perm_v!(3), 1),
put_var!(perm_v!(2), 3)],
functor_call!(),
cut!(),
cut!(perm_v!(1)),
query![put_unsafe_value!(4, 1),
put_value!(perm_v!(3), 2),
put_constant!(Level::Shallow, integer!(1), temp_v!(3)),
@@ -318,7 +318,7 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
goto_execute!(236, 4), // goto get_args/4.
trust_me!(),
allocate!(5),
get_level!(),
get_level!(perm_v!(1)),
fact![get_var_in_fact!(perm_v!(3), 1),
get_list!(Level::Shallow, temp_v!(2)),
unify_variable!(perm_v!(5)),
@@ -330,7 +330,7 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
put_value!(perm_v!(5), 2),
put_value!(perm_v!(2), 3)],
functor_call!(),
cut!(),
cut!(perm_v!(1)),
query![put_unsafe_value!(4, 1),
put_value!(perm_v!(3), 2),
put_constant!(Level::Shallow, integer!(1), temp_v!(3)),
@@ -370,7 +370,7 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
query![put_value!(perm_v!(5), 1),
put_value!(perm_v!(3), 2),
put_value!(temp_v!(5), 3)],
get_arg_call!(),
get_arg_call!(),
add!(ArithmeticTerm::Reg(perm_v!(5)),
ArithmeticTerm::Number(rc_integer!(1)),
1),
@@ -389,17 +389,17 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
neck_cut!(),
query![put_value!(temp_v!(4), 1),
put_constant!(Level::Shallow, integer!(0), temp_v!(2))],
goto_execute!(281, 3), // goto length/3, 281.
goto_execute!(281, 3), // goto length/3, 281.
retry_me_else!(10),
allocate!(1),
get_level!(),
get_level!(perm_v!(1)),
fact![get_var_in_fact!(temp_v!(4), 1),
get_var_in_fact!(temp_v!(3), 2)],
is_integer!(temp_v!(3)),
is_integer!(temp_v!(3)),
query![put_value!(temp_v!(4), 1),
put_constant!(Level::Shallow, integer!(0), temp_v!(2))],
goto_call!(281, 3), // goto length/3, 281.
cut!(),
cut!(perm_v!(1)),
deallocate!(),
proceed!(),
trust_me!(),
@@ -413,9 +413,9 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
None),
set_constant!(atom!("integer_expected", atom_tbl)),
set_value!(temp_v!(4))],
goto_execute!(59, 1), // goto throw/1, 59.
goto_execute!(59, 1), // goto throw/1, 59.
switch_on_term!(1,2,5,0), // length/3, 281.
try_me_else!(3),
try_me_else!(3),
fact![get_constant!(Constant::EmptyList, temp_v!(1)),
get_var_in_fact!(temp_v!(4), 2),
get_value!(temp_v!(4), 3)],
@@ -436,7 +436,92 @@ fn get_builtins(atom_tbl: TabledData<Atom>) -> Code {
put_unsafe_value!(2, 2),
put_value!(perm_v!(3), 3)],
deallocate!(),
goto_execute!(281, 3) // goto length/3, 281.
goto_execute!(281, 3), // goto length/3, 281.
allocate!(4), // setup_call_cleanup/3, 294.
get_level!(perm_v!(1)),
fact![get_var_in_fact!(perm_v!(2), 2),
get_var_in_fact!(perm_v!(3), 3)],
call_n!(1),
cut!(perm_v!(1)),
query![put_var!(perm_v!(4), 1)],
get_current_block!(),
query![put_value!(perm_v!(3), 1),
put_unsafe_value!(4, 2),
put_value!(perm_v!(2), 3)],
deallocate!(),
jmp_execute!(3, 1),
try_me_else!(5), // 304.
is_var!(temp_v!(1)),
neck_cut!(),
query![put_constant!(Level::Shallow, atom!("instantiation_error", atom_tbl),
temp_v!(1))],
goto_execute!(59, 1),
trust_me!(),
query![get_var_in_query!(temp_v!(4), 2),
put_value!(temp_v!(3), 2),
get_var_in_query!(temp_v!(5), 3),
put_value!(temp_v!(4), 3)],
goto_execute!(312, 3),
try_me_else!(13), // sgc_helper/3, 312.
allocate!(4),
fact![get_var_in_fact!(perm_v!(4), 1),
get_var_in_fact!(perm_v!(3), 2),
get_var_in_fact!(perm_v!(2), 3)],
get_level!(perm_v!(1)),
query![put_value!(perm_v!(4), 1)],
install_cleaner!(),
query![put_var!(temp_v!(2), 1)],
install_new_block!(),
query![put_value!(perm_v!(3), 1)],
call_n!(1),
query![put_value!(perm_v!(2), 1),
put_unsafe_value!(1, 2)],
deallocate!(),
check_cp_execute!(),
retry_me_else!(10),
allocate!(1),
query![put_value!(temp_v!(3), 1)],
reset_block!(),
query![put_var!(perm_v!(1), 1)],
get_ball!(),
goto_call!(337, 0), // goto run_cleaners_with_handling/0, 337.
query![put_value!(perm_v!(1), 1)],
deallocate!(),
goto_execute!(59, 1),
trust_me!(),
goto_execute!(349, 0), // goto run_cleaners_without_handling/0, 349.
try_me_else!(10), // run_cleaners_with_handling/0, 337.
allocate!(2),
get_level!(perm_v!(1)),
query![put_var!(perm_v!(2), 1)],
get_cleaner_call!(),
query![put_value!(perm_v!(2), 1),
put_var!(temp_v!(4), 2),
put_constant!(Level::Shallow, atom!("true", atom_tbl), temp_v!(3))],
goto_call!(5, 3), // goto catch/3, 5.
cut!(perm_v!(1)),
deallocate!(),
goto_execute!(337, 0), // goto run_cleaners_with_handling/0, 337.
trust_me!(),
proceed!(),
try_me_else!(10), // run_cleaners_without_handling/1, 349.
allocate!(2),
get_level!(perm_v!(1)),
query![put_var!(perm_v!(2), 1)],
get_cleaner_call!(),
query![put_value!(perm_v!(2), 1)],
call_n!(1),
cut!(perm_v!(1)),
deallocate!(),
goto_execute!(349, 0), // goto run_cleaners_without_handling/1, 349.
trust_me!(),
proceed!(),
allocate!(1), // sgc_on_success/2, 361.
fact![get_var_in_fact!(perm_v!(1), 2)],
reset_block!(),
cut!(perm_v!(1)),
deallocate!(),
proceed!()
]
}
@@ -486,7 +571,7 @@ pub fn build_code_dir(atom_tbl: TabledData<Atom>) -> (Code, CodeDir, OpDir)
op_dir.insert((tabled_rc!("->", atom_tbl), Fixity::In), (XFY, 1050));
op_dir.insert((tabled_rc!("=..", atom_tbl), Fixity::In), (XFX, 700));
// there are 63 registers in the VM, so call/N is defined for all 0 <= N <= 62
// (an extra register is needed for the predicate name)
for arity in 0 .. 63 {
@@ -522,6 +607,7 @@ pub fn build_code_dir(atom_tbl: TabledData<Atom>) -> (Code, CodeDir, OpDir)
code_dir.insert((tabled_rc!("=..", atom_tbl), 2), (PredicateKeyType::BuiltIn, 208));
code_dir.insert((tabled_rc!("length", atom_tbl), 2), (PredicateKeyType::BuiltIn, 261));
code_dir.insert((tabled_rc!("setup_call_cleanup", atom_tbl), 3), (PredicateKeyType::BuiltIn, 294));
(builtin_code, code_dir, op_dir)
}

View File

@@ -245,6 +245,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
fn add_conditional_call(code: &mut Code, qt: &QueryTerm, pvs: usize)
{
match qt {
&QueryTerm::SetupCallCleanup(_) =>
code.push(goto_call!(294, 3)),
&QueryTerm::Arg(_) => {
let call = ControlInstruction::ArgCall;
code.push(Line::Control(call));
@@ -287,6 +289,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
match code.last_mut() {
Some(&mut Line::Control(ref mut ctrl)) =>
match ctrl.clone() {
ControlInstruction::GotoCall(p, arity) =>
*ctrl = ControlInstruction::GotoExecute(p, arity),
ControlInstruction::ArgCall =>
*ctrl = ControlInstruction::ArgExecute,
ControlInstruction::Call(name, arity, _) =>
@@ -310,7 +314,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
ControlInstruction::Proceed => {},
_ => dealloc_index += 1 // = code.len()
},
Some(&mut Line::Cut(CutInstruction::Cut)) =>
Some(&mut Line::Cut(CutInstruction::Cut(_))) =>
dealloc_index += 1,
_ => {}
};
@@ -402,7 +406,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
code.push(if chunk_num == 0 {
Line::Cut(CutInstruction::NeckCut)
} else {
Line::Cut(CutInstruction::Cut)
Line::Cut(CutInstruction::Cut(perm_v!(1)))
});
},
&QueryTerm::Is(ref terms) => {
@@ -460,7 +464,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
if conjunct_info.has_deep_cut {
body.push(Line::Cut(CutInstruction::GetLevel));
body.push(Line::Cut(CutInstruction::GetLevel(perm_v!(1))));
}
}
}
@@ -491,7 +495,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
let mut code = Vec::new();
if let &QueryTerm::Term(ref term) = p0 {
self.marker.reset_arg_at_head(term);
self.marker.reset_arg(term.arity());
self.compile_seq_prelude(&conjunct_info, &mut code);
if let &Term::Clause(..) = term {
@@ -503,6 +507,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
}
}
self.marker.reset_arg_at_head(term);
let iter = ChunkedIterator::from_rule_body(p1, clauses);
try!(self.compile_seq(iter, &conjunct_info, &mut code, false));
@@ -562,7 +568,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
vs.populate_restricting_sets();
self.marker.drain_var_data(vs);
self.marker.reset_arg_at_head(term);
self.marker.reset_arg(term.arity());
let mut code = Vec::new();

View File

@@ -346,7 +346,7 @@ impl<'a> Allocator<'a> for DebrayAllocator<'a>
self.temp_lb = arity + 1;
}
fn reset_arg_at_head(&mut self, term: &Term) {
fn reset_arg_at_head(&mut self, term: &'a Term) {
self.arg_c = 1;
self.temp_lb = term.arity() + 1;

View File

@@ -110,9 +110,11 @@ impl fmt::Display for ControlInstruction {
&ControlInstruction::CallN(arity) =>
write!(f, "call_N {}", arity),
&ControlInstruction::CatchCall =>
write!(f, "call_catch"),
write!(f, "catch_call"),
&ControlInstruction::CatchExecute =>
write!(f, "execute_catch"),
write!(f, "catch_execute"),
&ControlInstruction::CheckCpExecute =>
write!(f, "check_cp_execute"),
&ControlInstruction::DisplayCall =>
write!(f, "display_call"),
&ControlInstruction::DisplayExecute =>
@@ -131,6 +133,8 @@ impl fmt::Display for ControlInstruction {
write!(f, "deallocate"),
&ControlInstruction::Execute(ref name, arity) =>
write!(f, "execute {}/{}", name, arity),
&ControlInstruction::GetCleanerCall =>
write!(f, "get_cleaner_call"),
&ControlInstruction::GotoCall(p, arity) =>
write!(f, "goto_call {}/{}", p, arity),
&ControlInstruction::GotoExecute(p, arity) =>
@@ -189,6 +193,8 @@ impl fmt::Display for BuiltInInstruction {
write!(f, "get_current_block X1"),
&BuiltInInstruction::GetCutPoint(r) =>
write!(f, "get_cp {}", r),
&BuiltInInstruction::InstallCleaner =>
write!(f, "install_cleaner"),
&BuiltInInstruction::InstallNewBlock =>
write!(f, "install_new_block"),
&BuiltInInstruction::InternalCallN =>
@@ -307,12 +313,12 @@ impl fmt::Display for ArithmeticInstruction {
impl fmt::Display for CutInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&CutInstruction::Cut =>
write!(f, "cut"),
&CutInstruction::Cut(r) =>
write!(f, "cut {}", r),
&CutInstruction::NeckCut =>
write!(f, "neck_cut"),
&CutInstruction::GetLevel =>
write!(f, "get_level")
&CutInstruction::GetLevel(r) =>
write!(f, "get_level {}", r)
}
}
}

View File

@@ -39,7 +39,7 @@ impl<'a> QueryIterator<'a> {
&QueryTerm::Catch(ref terms) => {
let state = TermIterState::Clause(0, ClauseType::Catch, terms);
QueryIterator { state_stack: vec![state] }
},
},
&QueryTerm::DuplicateTerm(ref terms) => {
let state = TermIterState::Clause(0, ClauseType::DuplicateTerm, terms);
QueryIterator { state_stack: vec![state] }
@@ -48,6 +48,10 @@ impl<'a> QueryIterator<'a> {
let state = TermIterState::Clause(0, ClauseType::Arg, terms);
QueryIterator { state_stack: vec![state] }
},
&QueryTerm::SetupCallCleanup(ref terms) => {
let state = TermIterState::Clause(0, ClauseType::SetupCallCleanup, terms);
QueryIterator { state_stack: vec![state] }
},
&QueryTerm::Functor(ref terms) => {
let state = TermIterState::Clause(0, ClauseType::Functor, terms);
QueryIterator { state_stack: vec![state] }
@@ -60,7 +64,7 @@ impl<'a> QueryIterator<'a> {
| &QueryTerm::Is(ref terms) => {
let state = TermIterState::Clause(0, ClauseType::Is, terms);
QueryIterator { state_stack: vec![state] }
},
},
&QueryTerm::Inlined(InlinedQueryTerm::IsAtomic(ref terms))
| &QueryTerm::Inlined(InlinedQueryTerm::IsInteger(ref terms))
| &QueryTerm::Inlined(InlinedQueryTerm::IsVar(ref terms)) =>
@@ -290,6 +294,11 @@ impl<'a> ChunkedIterator<'a>
break;
}
},
&QueryTerm::SetupCallCleanup(_) => {
result.push(term);
arity = 3;
break;
},
&QueryTerm::CallN(ref child_terms) => {
result.push(term);
arity = child_terms.len() + 1;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,8 @@ use prolog::fixtures::*;
use prolog::tabled_rc::*;
pub(crate) mod machine_state;
#[macro_use]
mod machine_state_impl;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};

View File

@@ -199,8 +199,8 @@ macro_rules! proceed {
}
macro_rules! cut {
() => (
Line::Cut(CutInstruction::Cut)
($r:expr) => (
Line::Cut(CutInstruction::Cut($r))
)
}
@@ -301,8 +301,8 @@ macro_rules! duplicate_term {
}
macro_rules! get_level {
() => (
Line::Cut(CutInstruction::GetLevel)
($r:expr) => (
Line::Cut(CutInstruction::GetLevel($r))
)
}
@@ -490,6 +490,12 @@ macro_rules! jmp_call {
)
}
macro_rules! jmp_execute {
($arity:expr, $offset:expr) => (
Line::Control(ControlInstruction::JmpByExecute($arity, $offset))
)
}
macro_rules! get_list {
($lvl:expr, $r:expr) => (
FactInstruction::GetList($lvl, $r)
@@ -501,3 +507,21 @@ macro_rules! unify_constant {
FactInstruction::UnifyConstant($c)
)
}
macro_rules! install_cleaner {
() => (
Line::BuiltIn(BuiltInInstruction::InstallCleaner)
)
}
macro_rules! check_cp_execute {
() => (
Line::Control(ControlInstruction::CheckCpExecute)
)
}
macro_rules! get_cleaner_call {
() => (
Line::Control(ControlInstruction::GetCleanerCall)
)
}

View File

@@ -1168,7 +1168,7 @@ fn test_queries_on_conditionals()
submit(&mut wam, "test(X, [X]) :- (atomic(X) -> true ; throw(type_error(atomic_expected, X))).
test(_, _).");
assert_prolog_success!(&mut wam, "?- catch(test(a, [a]), type_error(E), true).",
[["E = _6"], ["E = _6"]]);
@@ -1291,3 +1291,58 @@ fn test_queries_on_builtins()
assert_prolog_success!(&mut wam, "?- duplicate_term(f(X), f(X)).",
[["X = _1"]]);
}
#[test]
fn test_queries_on_setup_call_cleanup()
{
let mut wam = Machine::new();
// Test examples from the ISO Prolog page for setup_call_catch.
assert_prolog_failure!(&mut wam, "?- setup_call_cleanup(false, _, _).");
assert_prolog_success!(&mut wam, "?- catch(setup_call_cleanup(true, throw(unthrown), _), instantiation_error, true).");
assert_prolog_success!(&mut wam, "?- setup_call_cleanup(true, true, (true ; throw(x))).");
assert_prolog_success!(&mut wam, "?- setup_call_cleanup(true, X = 1, X = 2).",
[["X = 1"]]);
assert_prolog_success!(&mut wam, "?- setup_call_cleanup(true, true, X = 2).",
[["X = 2"]]);
assert_prolog_success!(&mut wam, "?- catch(setup_call_cleanup(true, X=true, X), E, true).",
[["E = instantiation_error", "X = _1"]]);
assert_prolog_success!(&mut wam, "?- catch(setup_call_cleanup(X=throw(ex), true, X), E, true).",
[["E = ex", "X = _3"]]);
assert_prolog_success!(&mut wam, "?- setup_call_cleanup(true, true, false).");
assert_prolog_success!(&mut wam, "?- setup_call_cleanup(S = 1, G = 2, C = 3).",
[["S = 1", "G = 2", "C = 3"]]);
assert_prolog_success!(&mut wam, "?- setup_call_cleanup((S=1;S=2), G=3, C=4).",
[["S = 1", "G = 3", "C = 4"]]);
assert_prolog_success!(&mut wam, "?- setup_call_cleanup(S=1, G=2, display(S+G)).",
[["S = 1", "G = 2"]]);
assert_prolog_success!(&mut wam, "?- setup_call_cleanup(S=1, (G=2;G=3), display(S+G)).",
[["S = 1", "G = 2"],
["S = 1", "G = 3"]]);
assert_prolog_success!(&mut wam, "?- setup_call_cleanup(S=1, G=2, display(S+G>A+B)), A=3, B=4.",
[["S = 1", "G = 2", "A = 3", "B = 4"]]);
assert_prolog_success!(&mut wam,
"?- catch(setup_call_cleanup(S=1, (G=2;G=3,throw(x)), display(S+G)), E, true).",
[["S = 1", "G = 2", "E = _26"], ["G = _4", "E = x", "S = _1"]]);
assert_prolog_success!(&mut wam,
"?- setup_call_cleanup(S=1, (G=2;G=3),display(S+G>B)), B=4, !.",
[["S = 1", "B = 4", "G = 2"]]);
assert_prolog_success!(&mut wam,
"?- setup_call_cleanup(S=1,G=2,display(S+G>B)),B=3,!.",
[["S = 1", "G = 2", "B = 3"]]);
assert_prolog_success!(&mut wam,
"?- setup_call_cleanup(S=1,(G=2;false),display(S+G>B)),B=3,!.",
[["S = 1", "G = 2", "B = 3"]]);
assert_prolog_success!(&mut wam,
"?- setup_call_cleanup(S=1,(G=2;S=2),display(S+G>B)), B=3, !.",
[["S = 1", "B = 3", "G = 2"]]);
assert_prolog_failure!(&mut wam,
"?- setup_call_cleanup(S=1,(G=2;G=3), display(S+G>B)), B=4, !, throw(x).");
assert_prolog_success!(&mut wam,
"?- setup_call_cleanup(true, (X=1;X=2), display(a)), setup_call_cleanup(true,(Y=1;Y=2),display(b)), !.",
[["Y = 1", "X = 1"]]);
assert_prolog_success!(&mut wam, "?- catch(setup_call_cleanup(true,throw(goal),throw(cl)), Pat, true).",
[["Pat = goal"]]);
assert_prolog_success!(&mut wam, "?- catch(( setup_call_cleanup(true,(G=1;G=2),throw(cl)), throw(cont)), Pat, true).",
[["Pat = cont", "G = _1"]]);
}