]> Repositorios git - scryer-prolog.git/commitdiff
add support for setup_call_cleanup
authorMark Thom <[email protected]>
Fri, 2 Feb 2018 06:03:57 +0000 (23:03 -0700)
committerMark Thom <[email protected]>
Fri, 2 Feb 2018 06:03:57 +0000 (23:03 -0700)
14 files changed:
README.md
src/prolog/allocator.rs
src/prolog/ast.rs
src/prolog/builtins.rs
src/prolog/codegen.rs
src/prolog/debray_allocator.rs
src/prolog/io.rs
src/prolog/iterators.rs
src/prolog/machine/machine_state.rs
src/prolog/machine/machine_state_impl.rs [new file with mode: 0644]
src/prolog/machine/mod.rs
src/prolog/macros.rs
src/prolog/parser
src/tests.rs

index 84030de3a76a15ee190ad48c1a7334309409c12b..3e609d35679452450770aa2f7d2bd58540a4856c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -100,6 +100,7 @@ The following predicates are built-in to rusty-wam.
 * `once/1`
 * `reverse/2`
 * `select/3`
+* `setup_call_cleanup/3`
 * `throw/1`
 * `true/0`
 * `var/1`
index 7aa0eb7c05c8a0e9e4e026f1e48e69543b75ed23..aa30a415788261f0cd78e882c6dc7db2a5b3358f 100644 (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);
 
index 7574e536c48be2020c9a21f3684959f25d9826e0..dc31d649a969ba4e413b5a73af88454824122edb 100644 (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,
index 672e43ffb569a692f56d6eda2e56187f968f6ea7..7cae5c8c085ab15ffa5fd16653748af5c211d057 100644 (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)
 }
index 63de49e1b0151cddf0f0d5f78492d9c5ddfcd3f9..5e6e416ad5bd4231af13a082d9a4c91f1e6b8521 100644 (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();
 
index e922d60a4e5a95929d283a408cc85503b9ddeb47..fdfba43bf3b52644ebae139fc5926389b90485c1 100644 (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;
 
index 694176de81af9c9b767190cb8d45141577313f5e..5d467db6e494adede1a367e1f2c0d091f06886e1 100644 (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)
         }
     }
 }
index 3f58f3b8eea45d0625ddcf4079e92e453c80afce..80146fc170173a3b3cdf6d7bd5a1358a86754f95 100644 (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;
index 3222866efbb048b6a9b4ed4cdf76b2461256780c..0bc19e57d5f38b1bb6c4b2e561e9fccbc2ef08c8 100644 (file)
@@ -1,25 +1,17 @@
 use prolog::and_stack::*;
 use prolog::ast::*;
-use prolog::builtins::*;
 use prolog::copier::*;
-use prolog::heap_iter::*;
-use prolog::heap_print::*;
-use prolog::num::{Integer, ToPrimitive, Zero};
-use prolog::num::bigint::{BigInt, BigUint};
-use prolog::num::rational::Ratio;
 use prolog::or_stack::*;
 use prolog::tabled_rc::*;
 
-use std::cmp::max;
 use std::ops::{Index, IndexMut};
-use std::rc::Rc;
 
-struct DuplicateTerm<'a> {
+pub(super) struct DuplicateTerm<'a> {
     state: &'a mut MachineState
 }
 
 impl<'a> DuplicateTerm<'a> {
-    fn new(state: &'a mut MachineState) -> Self {
+    pub(super) fn new(state: &'a mut MachineState) -> Self {
         DuplicateTerm { state: state }
     }
 }
@@ -65,13 +57,13 @@ impl<'a> CopierTarget for DuplicateTerm<'a> {
     }
 }
 
-struct DuplicateBallTerm<'a> {
+pub(super) struct DuplicateBallTerm<'a> {
     state: &'a mut MachineState,
     heap_boundary: usize
 }
 
 impl<'a> DuplicateBallTerm<'a> {
-    fn new(state: &'a mut MachineState) -> Self {
+    pub(super) fn new(state: &'a mut MachineState) -> Self {
         let hb = state.heap.len();
         DuplicateBallTerm { state: state, heap_boundary: hb }
     }
@@ -154,18 +146,6 @@ impl IndexMut<RegType> for MachineState {
     }
 }
 
-macro_rules! try_or_fail {
-    ($s:ident, $e:expr) => {{
-        match $e {
-            Ok(val)  => val,
-            Err(msg) => {
-                $s.throw_exception(msg);
-                return;
-            }
-        }
-    }}
-}
-
 #[derive(Clone, Copy)]
 pub(super) enum MachineMode {
     Read,
@@ -192,1745 +172,6 @@ pub struct MachineState {
     pub(super) hb: usize,
     pub(super) block: usize, // an offset into the OR stack.
     pub(super) ball: (usize, Vec<HeapCellValue>), // heap boundary, and a term copy
-    pub(super) interms: Vec<Number> // intermediate numbers.
-}
-
-impl MachineState {
-    pub(super) fn new(atom_tbl: TabledData<Atom>) -> MachineState {
-        MachineState { atom_tbl,
-                       s: 0,
-                       p: CodePtr::default(),
-                       b: 0,
-                       b0: 0,
-                       e: 0,
-                       num_of_args: 0,
-                       cp: CodePtr::default(),
-                       fail: false,
-                       heap: Heap::with_capacity(256),
-                       mode: MachineMode::Write,
-                       and_stack: AndStack::new(),
-                       or_stack: OrStack::new(),
-                       registers: vec![Addr::HeapCell(0); 64],
-                       trail: Vec::new(),
-                       tr: 0,
-                       hb: 0,
-                       block: 0,
-                       ball: (0, Vec::new()),
-                       interms: vec![Number::default(); 256],
-        }
-    }
-
-    fn next_global_index(&self) -> usize {
-        max(if self.and_stack.len() > 0 { self.and_stack[self.e].global_index } else { 0 },
-            if self.b > 0 { self.or_stack[self.b - 1].global_index } else { 0 }) + 1
-    }
-
-    pub(crate) fn store(&self, a: Addr) -> Addr {
-        match a {
-            Addr::HeapCell(r)       => self.heap[r].as_addr(r),
-            Addr::StackCell(fr, sc) => self.and_stack[fr][sc].clone(),
-            addr                    => addr
-        }
-    }
-
-    pub(crate) fn deref(&self, mut a: Addr) -> Addr {
-        loop {
-            let value = self.store(a.clone());
-
-            if value.is_ref() && value != a {
-                a = value;
-                continue;
-            }
-
-            return a;
-        };
-    }
-
-    fn bind(&mut self, r1: Ref, a2: Addr) {
-        let t2 = self.store(a2);
-
-        match r1 {
-            Ref::StackCell(fr, sc) =>
-                self.and_stack[fr][sc] = t2,
-            Ref::HeapCell(hc) =>
-                self.heap[hc] = HeapCellValue::Addr(t2)
-        };
-
-        self.trail(r1);
-    }
-
-    pub(super) fn print_term<Fmt, Outputter>(&self, a: Addr, fmt: Fmt, output: Outputter) -> Outputter
-        where Fmt: HeapCellValueFormatter, Outputter: HeapCellValueOutputter
-    {
-        let iter    = HeapCellPreOrderIterator::new(&self, a);
-        let printer = HeapCellPrinter::new(iter, fmt, output);
-
-        printer.print()
-    }
-
-    fn unify(&mut self, a1: Addr, a2: Addr) {
-        let mut pdl = vec![a1, a2];
-
-        self.fail = false;
-
-        while !(pdl.is_empty() || self.fail) {
-            let d1 = self.deref(pdl.pop().unwrap());
-            let d2 = self.deref(pdl.pop().unwrap());
-
-            if d1 != d2 {
-                match (self.store(d1.clone()), self.store(d2.clone())) {
-                    (Addr::HeapCell(hc), _) =>
-                        self.bind(Ref::HeapCell(hc), d2),
-                    (_, Addr::HeapCell(hc)) =>
-                        self.bind(Ref::HeapCell(hc), d1),
-                    (Addr::StackCell(fr, sc), _) =>
-                        self.bind(Ref::StackCell(fr, sc), d2),
-                    (_, Addr::StackCell(fr, sc)) =>
-                        self.bind(Ref::StackCell(fr, sc), d1),
-                    (Addr::Lis(a1), Addr::Lis(a2)) => {
-                        pdl.push(Addr::HeapCell(a1));
-                        pdl.push(Addr::HeapCell(a2));
-
-                        pdl.push(Addr::HeapCell(a1 + 1));
-                        pdl.push(Addr::HeapCell(a2 + 1));
-                    },
-                    (Addr::Con(c1), Addr::Con(c2)) => {
-                        if c1 != c2 {
-                            self.fail = true;
-                        }
-                    },
-                    (Addr::Str(a1), Addr::Str(a2)) => {
-                        let r1 = &self.heap[a1];
-                        let r2 = &self.heap[a2];
-
-                        if let &HeapCellValue::NamedStr(n1, ref f1, _) = r1 {
-                            if let &HeapCellValue::NamedStr(n2, ref f2, _) = r2 {
-                                if n1 == n2 && *f1 == *f2 {
-                                    for i in 1 .. n1 + 1 {
-                                        pdl.push(Addr::HeapCell(a1 + i));
-                                        pdl.push(Addr::HeapCell(a2 + i));
-                                    }
-
-                                    continue;
-                                }
-                            }
-                        }
-
-                        self.fail = true;
-                    },
-                    _ => self.fail = true
-                };
-            }
-        }
-    }
-
-    fn trail(&mut self, r: Ref) {
-        match r {
-            Ref::HeapCell(hc) => {
-                if hc < self.hb {
-                    self.trail.push(r);
-                    self.tr += 1;
-                }
-            },
-            Ref::StackCell(fr, _) => {
-                let fr_gi = self.and_stack[fr].global_index;
-                let b_gi  = if !self.or_stack.is_empty() {
-                    if self.b > 0 {
-                        let b = self.b - 1;
-                        self.or_stack[b].global_index
-                    } else {
-                        0
-                    }
-                } else {
-                    0
-                };
-
-                if fr_gi < b_gi {
-                    self.trail.push(r);
-                    self.tr += 1;
-                }
-            }
-        }
-    }
-
-    fn unwind_trail(&mut self, a1: usize, a2: usize) {
-        for i in a1 .. a2 {
-            match self.trail[i] {
-                Ref::HeapCell(r) =>
-                    self.heap[r] = HeapCellValue::Addr(Addr::HeapCell(r)),
-                Ref::StackCell(fr, sc) =>
-                    self.and_stack[fr][sc] = Addr::StackCell(fr, sc)
-            }
-        }
-    }
-
-    fn tidy_trail(&mut self) {
-        if self.b == 0 {
-            return;
-        }
-
-        let b = self.b - 1;
-        let mut i = self.or_stack[b].tr;
-
-        while i < self.tr {
-            let tr_i = self.trail[i];
-            let hb = self.hb;
-
-            match tr_i {
-                Ref::HeapCell(tr_i) =>
-                    if tr_i < hb { //|| ((h < tr_i) && tr_i < b) {
-                        i += 1;
-                    } else {
-                        let tr = self.tr;
-                        let val = self.trail[tr - 1];
-                        self.trail[i] = val;
-                    },
-                Ref::StackCell(fr, _) => {
-                    let b = self.b - 1;
-                    let fr_gi = self.and_stack[fr].global_index;
-                    let b_gi  = if !self.or_stack.is_empty() {
-                        self.or_stack[b].global_index
-                    } else {
-                        0
-                    };
-
-                    if fr_gi < b_gi {
-                        i += 1;
-                    } else {
-                        let tr = self.tr;
-                        let val = self.trail[tr - 1];
-                        self.trail[i] = val;
-                    }
-                }
-            };
-        }
-    }
-
-    fn write_constant_to_var(&mut self, addr: Addr, c: Constant) {
-        let addr = self.deref(addr);
-
-        match self.store(addr) {
-            Addr::HeapCell(hc) => {
-                self.heap[hc] = HeapCellValue::Addr(Addr::Con(c.clone()));
-                self.trail(Ref::HeapCell(hc));
-            },
-            Addr::StackCell(fr, sc) => {
-                self.and_stack[fr][sc] = Addr::Con(c.clone());
-                self.trail(Ref::StackCell(fr, sc));
-            },
-            Addr::Con(c1) => {
-                if c1 != c {
-                    self.fail = true;
-                }
-            },
-            _ => self.fail = true
-        };
-    }
-
-    fn get_number(&self, at: &ArithmeticTerm) -> Result<Number, Vec<HeapCellValue>> {
-
-        match at {
-            &ArithmeticTerm::Reg(r) => {
-                let addr = self[r].clone();
-                let item = self.store(self.deref(addr));
-
-                match item {
-                    Addr::Con(Constant::Number(n)) => Ok(n),
-                    _ => {
-                        let atom_tbl = self.atom_tbl.clone();
-                        Err(functor!(self.atom_tbl,
-                                     "instantiation_error",
-                                     1,
-                                     [heap_atom!("(is)/2", atom_tbl)]))
-                    }
-                }
-            },
-            &ArithmeticTerm::Interm(i)     => Ok(self.interms[i-1].clone()),
-            &ArithmeticTerm::Number(ref n) => Ok(n.clone()),
-        }
-    }
-
-    fn get_rational(&self, at: &ArithmeticTerm) -> Result<Rc<Ratio<BigInt>>, Vec<HeapCellValue>> {
-        let n = self.get_number(at)?;
-
-        match n {
-            Number::Rational(r) => Ok(r),
-            Number::Float(fl) =>
-                if let Some(r) = Ratio::from_float(fl.into_inner()) {
-                    Ok(Rc::new(r))
-                } else {
-                    Err(functor!(self.atom_tbl,
-                                 "instantiation_error",
-                                 1,
-                                 [heap_atom!("(is)/2", self.atom_tbl)]))
-                },
-            Number::Integer(bi) =>
-                Ok(Rc::new(Ratio::from_integer((*bi).clone())))
-        }
-    }
-
-    fn signed_bitwise_op<Op>(&self, n1: &BigInt, n2: &BigInt, f: Op) -> Rc<BigInt>
-        where Op: FnOnce(&BigUint, &BigUint) -> BigUint
-    {
-        let n1_b = n1.to_signed_bytes_le();
-        let n2_b = n2.to_signed_bytes_le();
-
-        let u_n1 = BigUint::from_bytes_le(&n1_b);
-        let u_n2 = BigUint::from_bytes_le(&n2_b);
-
-        Rc::new(BigInt::from_signed_bytes_le(&f(&u_n1, &u_n2).to_bytes_le()))
-    }
-
-    fn arith_eval_by_metacall(&self, r: RegType) -> Result<Number, Vec<HeapCellValue>>
-    {        
-        let instantiation_err = functor!(self.atom_tbl.clone(), "instantiation_error", 1,
-                                         [heap_atom!("(is)/2", self.atom_tbl.clone())]);
-
-        let a = self[r].clone();
-            
-        if let &Addr::Con(Constant::Number(ref n)) = &a {
-            return Ok(n.clone());
-        }
-
-        let mut interms: Vec<Number> = Vec::with_capacity(64);
-        
-        for heap_val in self.post_order_iter(a) {
-            match heap_val {
-                HeapCellValue::NamedStr(2, name, Some(Fixity::In)) => {
-                    let a2 = interms.pop().unwrap();
-                    let a1 = interms.pop().unwrap();
-                    
-                    match name.as_str() {
-                        "+" => interms.push(a1 + a2),
-                        "-" => interms.push(a1 - a2),
-                        "*" => interms.push(a1 * a2),
-                        "rdiv" =>
-                            match NumberPair::from(a1, a2) {
-                                NumberPair::Rational(r1, r2) =>
-                                    interms.push(Number::Rational(self.rdiv(r1, r2)?)),
-                                _ =>
-                                    return Err(instantiation_err)
-                            },
-                        "//"  => interms.push(Number::Integer(self.idiv(a1, a2)?)),
-                        "div" => interms.push(Number::Integer(self.fidiv(a1, a2)?)),
-                        ">>"  => interms.push(Number::Integer(self.shr(a1, a2)?)),
-                        "<<"  => interms.push(Number::Integer(self.shl(a1, a2)?)),
-                        "/\\" => interms.push(Number::Integer(self.and(a1, a2)?)),
-                        "\\/" => interms.push(Number::Integer(self.or(a1, a2)?)),
-                        "xor" => interms.push(Number::Integer(self.xor(a1, a2)?)),
-                        "mod" => interms.push(Number::Integer(self.modulus(a1, a2)?)),
-                        "rem" => interms.push(Number::Integer(self.remainder(a1, a2)?)),
-                        _     => return Err(instantiation_err)
-                    }
-                },
-                HeapCellValue::NamedStr(1, name, Some(Fixity::Pre)) => {              
-                    let a1 = interms.pop().unwrap();
-
-                    match name.as_str() {
-                        "-" => interms.push(- a1),
-                         _  => return Err(instantiation_err)
-                    }
-                },
-                HeapCellValue::Addr(Addr::Con(Constant::Number(n))) =>
-                    interms.push(n),
-                _ =>
-                    return Err(instantiation_err)
-            }
-        };
-
-        Ok(interms.pop().unwrap())
-    }
-
-    fn rdiv(&self, r1: Rc<Ratio<BigInt>>, r2: Rc<Ratio<BigInt>>)
-            -> Result<Rc<Ratio<BigInt>>, Vec<HeapCellValue>>
-    {
-        if *r2 == Ratio::zero() {
-            Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                         [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
-        } else {
-            Ok(Rc::new(&*r1 / &*r2))
-        }
-    }
-
-    fn fidiv(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
-    {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
-                if *n2 == BigInt::zero() {
-                    Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                                 [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
-                } else {
-                    Ok(Rc::new(n1.div_floor(&n2)))
-                },
-            _ => Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                              [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
-        }
-    }
-
-    fn idiv(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
-    {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
-                if *n2 == BigInt::zero() {
-                    Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                                 [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
-                } else {
-                    Ok(Rc::new(&*n1 / &*n2))
-                },
-            _ =>
-                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
-        }
-    }
-
-    fn div(&self, n1: Number, n2: Number) -> Result<Number, Vec<HeapCellValue>>
-    {
-        if n2.is_zero() {
-            Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                         [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
-        } else {
-            Ok(n1 / n2)
-        }
-    }
-
-    fn shr(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
-    {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
-                match n2.to_usize() {
-                    Some(n2) => Ok(Rc::new(&*n1 >> n2)),
-                    _        => Ok(Rc::new(&*n1 >> usize::max_value()))
-                },
-            _ =>
-                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
-        }
-    }
-
-    fn shl(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
-    {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
-                match n2.to_usize() {
-                    Some(n2) => Ok(Rc::new(&*n1 << n2)),
-                    _        => Ok(Rc::new(&*n1 << usize::max_value()))
-                },
-            _ =>
-                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
-        }
-    }
-
-    fn xor(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
-    {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
-                Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 ^ u_n2)),
-            _ =>
-                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
-        }
-    }
-
-    fn and(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
-    {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
-                Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 & u_n2)),
-            _ =>
-                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
-        }
-    }
-
-    fn modulus(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
-    {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
-                if *n2 == BigInt::zero() {
-                    Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                                 [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
-                } else {
-                    Ok(Rc::new(n1.mod_floor(&n2)))
-                },
-            _ =>
-                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
-        }
-    }
-
-    fn remainder(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
-    {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
-                if *n2 == BigInt::zero() {
-                    Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                                 [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
-                } else {
-                    Ok(Rc::new(&*n1 % &*n2))
-                },
-            _ =>
-                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
-        }
-    }
-    
-    fn or(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
-    {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) =>
-                Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 & u_n2)),
-            _ =>
-                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
-                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
-        }
-    }
-
-    pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
-        match instr {
-            &ArithmeticInstruction::Add(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = n1 + n2;
-                self.p += 1;
-            },
-            &ArithmeticInstruction::Sub(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = n1 - n2;
-                self.p += 1;
-            },
-            &ArithmeticInstruction::Mul(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = n1 * n2;
-                self.p += 1;
-            },
-            &ArithmeticInstruction::RDiv(ref a1, ref a2, t) => {
-                let r1 = try_or_fail!(self, self.get_rational(a1));
-                let r2 = try_or_fail!(self, self.get_rational(a2));
-
-                self.interms[t - 1] = Number::Rational(try_or_fail!(self, self.rdiv(r1, r2)));
-                self.p += 1;
-            },
-            &ArithmeticInstruction::FIDiv(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.fidiv(n1, n2)));
-                self.p += 1;
-            },
-            &ArithmeticInstruction::IDiv(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.idiv(n1, n2)));
-                self.p += 1;
-            },
-            &ArithmeticInstruction::Neg(ref a1, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-
-                self.interms[t - 1] = - n1;
-                self.p += 1;
-            },
-            &ArithmeticInstruction::Div(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = try_or_fail!(self, self.div(n1, n2));
-                self.p += 1;
-            },
-            &ArithmeticInstruction::Shr(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.shr(n1, n2)));
-                self.p += 1;
-            },
-            &ArithmeticInstruction::Shl(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.shl(n1, n2)));
-                self.p += 1;
-            },
-            &ArithmeticInstruction::Xor(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.xor(n1, n2)));
-                self.p += 1;
-            },
-            &ArithmeticInstruction::And(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.and(n1, n2)));
-                self.p += 1;
-            },
-            &ArithmeticInstruction::Or(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.or(n1, n2)));
-                self.p += 1;
-            },
-            &ArithmeticInstruction::Mod(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.modulus(n1, n2)));
-                self.p += 1;
-            },
-            &ArithmeticInstruction::Rem(ref a1, ref a2, t) => {
-                let n1 = try_or_fail!(self, self.get_number(a1));
-                let n2 = try_or_fail!(self, self.get_number(a2));
-
-                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.remainder(n1, n2)));
-                self.p += 1;
-            }
-        };
-    }
-
-    pub(super) fn execute_fact_instr(&mut self, instr: &FactInstruction) {
-        match instr {
-            &FactInstruction::GetConstant(_, ref c, reg) => {
-                let addr = self[reg].clone();
-                self.write_constant_to_var(addr, c.clone());
-            },
-            &FactInstruction::GetList(_, reg) => {
-                let addr = self.deref(self[reg].clone());
-
-                match self.store(addr.clone()) {
-                    Addr::HeapCell(hc) => {
-                        let h = self.heap.h;
-
-                        self.heap.push(HeapCellValue::Addr(Addr::Lis(h+1)));
-                        self.bind(Ref::HeapCell(hc), Addr::HeapCell(h));
-
-                        self.mode = MachineMode::Write;
-                    },
-                    Addr::StackCell(fr, sc) => {
-                        let h = self.heap.h;
-
-                        self.heap.push(HeapCellValue::Addr(Addr::Lis(h+1)));
-                        self.bind(Ref::StackCell(fr, sc), Addr::HeapCell(h));
-
-                        self.mode = MachineMode::Write;
-                    },
-                    Addr::Lis(a) => {
-                        self.s = a;
-                        self.mode = MachineMode::Read;
-                    },
-                    _ => self.fail = true
-                };
-            },
-            &FactInstruction::GetStructure(_, ref name, arity, reg, fixity) => {
-                let addr = self.deref(self[reg].clone());
-
-                match self.store(addr.clone()) {
-                    Addr::Str(a) => {
-                        let result = &self.heap[a];
-
-                        if let &HeapCellValue::NamedStr(narity, ref str, _) = result {
-                            if narity == arity && *name == *str {
-                                self.s = a + 1;
-                                self.mode = MachineMode::Read;
-                            } else {
-                                self.fail = true;
-                            }
-                        }
-                    },
-                    Addr::HeapCell(_) | Addr::StackCell(_, _) => {
-                        let h = self.heap.h;
-
-                        self.heap.push(HeapCellValue::Addr(Addr::Str(h + 1)));
-                        self.heap.push(HeapCellValue::NamedStr(arity, name.clone(), fixity));
-
-                        self.bind(addr.as_var().unwrap(), Addr::HeapCell(h));
-
-                        self.mode = MachineMode::Write;
-                    },
-                    _ => self.fail = true
-                };
-            },
-            &FactInstruction::GetVariable(norm, arg) =>
-                self[norm] = self.registers[arg].clone(),
-            &FactInstruction::GetValue(norm, arg) => {
-                let norm_addr = self[norm].clone();
-                let reg_addr  = self.registers[arg].clone();
-
-                self.unify(norm_addr, reg_addr);
-            },
-            &FactInstruction::UnifyConstant(ref c) => {
-                match self.mode {
-                    MachineMode::Read  => {
-                        let addr = Addr::HeapCell(self.s);
-                        self.write_constant_to_var(addr, c.clone());
-                    },
-                    MachineMode::Write => {
-                        self.heap.push(HeapCellValue::Addr(Addr::Con(c.clone())));
-                    }
-                };
-
-                self.s += 1;
-            },
-            &FactInstruction::UnifyVariable(reg) => {
-                match self.mode {
-                    MachineMode::Read  =>
-                        self[reg] = self.heap[self.s].as_addr(self.s),
-                    MachineMode::Write => {
-                        let h = self.heap.h;
-
-                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                        self[reg] = Addr::HeapCell(h);
-                    }
-                };
-
-                self.s += 1;
-            },
-            &FactInstruction::UnifyLocalValue(reg) => {
-                let s = self.s;
-
-                match self.mode {
-                    MachineMode::Read  => {
-                        let reg_addr = self[reg].clone();
-                        self.unify(reg_addr, Addr::HeapCell(s));
-                    },
-                    MachineMode::Write => {
-                        let addr = self.deref(self[reg].clone());
-                        let h    = self.heap.h;
-
-                        if let Addr::HeapCell(hc) = addr {
-                            if hc < h {
-                                let val = self.heap[hc].clone();
-
-                                self.heap.push(val);
-                                self.s += 1;
-
-                                return;
-                            }
-                        }
-
-                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                        self.bind(Ref::HeapCell(h), addr);
-                    }
-                };
-
-                self.s += 1;
-            },
-            &FactInstruction::UnifyValue(reg) => {
-                let s = self.s;
-
-                match self.mode {
-                    MachineMode::Read  => {
-                        let reg_addr = self[reg].clone();
-                        self.unify(reg_addr, Addr::HeapCell(s));
-                    },
-                    MachineMode::Write => {
-                        let heap_val = self.store(self[reg].clone());
-                        self.heap.push(HeapCellValue::Addr(heap_val));
-                    }
-                };
-
-                self.s += 1;
-            },
-            &FactInstruction::UnifyVoid(n) => {
-                match self.mode {
-                    MachineMode::Read =>
-                        self.s += n,
-                    MachineMode::Write => {
-                        let h = self.heap.h;
-
-                        for i in h .. h + n {
-                            self.heap.push(HeapCellValue::Addr(Addr::HeapCell(i)));
-                        }
-                    }
-                };
-            }
-        };
-    }
-
-    pub(super) fn execute_indexing_instr(&mut self, instr: &IndexingInstruction) {
-        match instr {
-            &IndexingInstruction::SwitchOnTerm(v, c, l, s) => {
-                let a1 = self.registers[1].clone();
-                let addr = self.store(self.deref(a1));
-
-                let offset = match addr {
-                    Addr::HeapCell(_) | Addr::StackCell(_, _) => v,
-                    Addr::Con(_) => c,
-                    Addr::Lis(_) => l,
-                    Addr::Str(_) => s
-                };
-
-                match offset {
-                    0 => self.fail = true,
-                    o => self.p += o
-                };
-            },
-            &IndexingInstruction::SwitchOnConstant(_, ref hm) => {
-                let a1 = self.registers[1].clone();
-                let addr = self.store(self.deref(a1));
-
-                let offset = match addr {
-                    Addr::Con(constant) => {
-                        match hm.get(&constant) {
-                            Some(offset) => *offset,
-                            _ => 0
-                        }
-                    },
-                    _ => 0
-                };
-
-                match offset {
-                    0 => self.fail = true,
-                    o => self.p += o,
-                };
-            },
-            &IndexingInstruction::SwitchOnStructure(_, ref hm) => {
-                let a1 = self.registers[1].clone();
-                let addr = self.store(self.deref(a1));
-
-                let offset = match addr {
-                    Addr::Str(s) => {
-                        if let &HeapCellValue::NamedStr(arity, ref name, _) = &self.heap[s] {
-                            match hm.get(&(name.clone(), arity)) {
-                                Some(offset) => *offset,
-                                _ => 0
-                            }
-                        } else {
-                            0
-                        }
-                    },
-                    _ => 0
-                };
-
-                match offset {
-                    0 => self.fail = true,
-                    o => self.p += o
-                };
-            }
-        };
-    }
-
-    pub(super) fn execute_query_instr(&mut self, instr: &QueryInstruction) {
-        match instr {
-            &QueryInstruction::GetVariable(norm, arg) =>
-                self[norm] = self.registers[arg].clone(),
-            &QueryInstruction::PutConstant(_, ref constant, reg) =>
-                self[reg] = Addr::Con(constant.clone()),
-            &QueryInstruction::PutList(_, reg) =>
-                self[reg] = Addr::Lis(self.heap.h),
-            &QueryInstruction::PutStructure(_, ref name, arity, reg, fixity) => {
-                let h = self.heap.h;
-
-                self.heap.push(HeapCellValue::NamedStr(arity, name.clone(), fixity));
-                self[reg] = Addr::Str(h);
-            },
-            &QueryInstruction::PutUnsafeValue(n, arg) => {
-                let e    = self.e;
-                let addr = self.deref(Addr::StackCell(e, n));
-
-                if addr.is_protected(e) {
-                    self.registers[arg] = self.store(addr);
-                } else {
-                    let h = self.heap.h;
-
-                    self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                    self.bind(Ref::HeapCell(h), addr);
-
-                    self.registers[arg] = self.heap[h].as_addr(h);
-                }
-            },
-            &QueryInstruction::PutValue(norm, arg) =>
-                self.registers[arg] = self[norm].clone(),
-            &QueryInstruction::PutVariable(norm, arg) => {
-                match norm {
-                    RegType::Perm(n) => {
-                        let e = self.e;
-
-                        self[norm] = Addr::StackCell(e, n);
-                        self.registers[arg] = self[norm].clone();
-                    },
-                    RegType::Temp(_) => {
-                        let h = self.heap.h;
-                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-
-                        self[norm] = Addr::HeapCell(h);
-                        self.registers[arg] = Addr::HeapCell(h);
-                    }
-                };
-            },
-            &QueryInstruction::SetConstant(ref c) => {
-                self.heap.push(HeapCellValue::Addr(Addr::Con(c.clone())));
-            },
-            &QueryInstruction::SetLocalValue(reg) => {
-                let addr = self.deref(self[reg].clone());
-                let h    = self.heap.h;
-
-                if let Addr::HeapCell(hc) = addr {
-                    if hc < h {
-                        self.heap.push(HeapCellValue::Addr(addr));
-                        return;
-                    }
-                }
-
-                self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                self.bind(Ref::HeapCell(h), addr);
-            },
-            &QueryInstruction::SetVariable(reg) => {
-                let h = self.heap.h;
-                self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                self[reg] = Addr::HeapCell(h);
-            },
-            &QueryInstruction::SetValue(reg) => {
-                let heap_val = self[reg].clone();
-                self.heap.push(HeapCellValue::Addr(heap_val));
-            },
-            &QueryInstruction::SetVoid(n) => {
-                let h = self.heap.h;
-
-                for i in h .. h + n {
-                    self.heap.push(HeapCellValue::Addr(Addr::HeapCell(i)));
-                }
-            }
-        }
-    }
-
-    fn try_call_predicate(&mut self, code_dir: &CodeDir, name: TabledRc<Atom>, arity: usize)
-    {
-        let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1);
-
-        match compiled_tl_index {
-            Some(compiled_tl_index) => {
-                self.cp = self.p + 1;
-                self.num_of_args = arity;
-                self.b0 = self.b;
-                self.p  = CodePtr::DirEntry(compiled_tl_index);
-            },
-            None => self.fail = true
-        };
-    }
-
-    fn try_execute_predicate(&mut self, code_dir: &CodeDir, name: TabledRc<Atom>, arity: usize)
-    {
-        let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1);
-
-        match compiled_tl_index {
-            Some(compiled_tl_index) => {
-                self.num_of_args = arity;
-                self.b0 = self.b;
-                self.p  = CodePtr::DirEntry(compiled_tl_index);
-            },
-            None => self.fail = true
-        };
-    }
-
-    fn handle_internal_call_n(&mut self, code_dir: &CodeDir)
-    {
-        let arity = self.num_of_args + 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;
-
-            if let Some((name, arity)) = self.setup_call_n(arity - 1) {
-                self.try_execute_predicate(code_dir, name, arity);
-            }
-        } else {
-            self.fail = true;
-        }
-    }
-
-    fn goto_throw(&mut self) {
-        self.num_of_args = 1;
-        self.b0 = self.b;
-        self.p  = CodePtr::DirEntry(59);
-    }
-
-    fn throw_exception(&mut self, hcv: Vec<HeapCellValue>) {
-        let h = self.heap.h;
-
-        self.registers[1] = Addr::HeapCell(h);
-
-        self.heap.append(hcv);
-        self.goto_throw();
-    }
-
-    fn setup_call_n(&mut self, arity: usize) -> Option<PredicateKey>
-    {
-        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 atom_tbl = self.atom_tbl.clone();
-                        self.throw_exception(functor!(atom_tbl,
-                                                      "representation_error",
-                                                      1,
-                                                      [heap_atom!("exceeds_max_arity", atom_tbl)]));
-                        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 atom_tbl = self.atom_tbl.clone();
-                self.throw_exception(functor!(atom_tbl, "instantiation_error", 0, []));
-                return None;
-            },
-            _ => {
-                let atom_tbl = self.atom_tbl.clone();
-                self.throw_exception(functor!(atom_tbl,
-                                              "type_error",
-                                              2,
-                                              [heap_atom!("callable", atom_tbl),
-                                               HeapCellValue::Addr(addr)]));
-                return None;
-            }
-        };
-
-        Some((name, arity + narity - 1))
-    }
-
-    pub(super) fn copy_and_align_ball_to_heap(&mut self) {
-        let diff = self.ball.0 - self.heap.h;
-
-        for heap_value in self.ball.1.iter().cloned() {
-            self.heap.push(match heap_value {
-                HeapCellValue::Addr(Addr::Con(c)) =>
-                    HeapCellValue::Addr(Addr::Con(c)),
-                HeapCellValue::Addr(Addr::Lis(a)) =>
-                    HeapCellValue::Addr(Addr::Lis(a - diff)),
-                HeapCellValue::Addr(Addr::HeapCell(hc)) =>
-                    HeapCellValue::Addr(Addr::HeapCell(hc - diff)),
-                HeapCellValue::Addr(Addr::Str(s)) =>
-                    HeapCellValue::Addr(Addr::Str(s - diff)),
-                _ => heap_value
-            });
-        }
-    }
-
-    fn try_get_arg(&mut self) -> Result<(), Vec<HeapCellValue>>
-    {
-        let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
-
-        if let Addr::Con(Constant::Number(Number::Integer(i))) = a1 {
-            let a2 = self.store(self.deref(self[temp_v!(2)].clone()));
-
-            if let Addr::Str(o) = a2 {
-                match self.heap[o].clone() {
-                    HeapCellValue::NamedStr(arity, _, _) =>
-                        match i.to_usize() {
-                            Some(i) if 1 <= i && i <= arity => {
-                                let a3  = self[temp_v!(3)].clone();
-                                let h_a = Addr::HeapCell(o + i);
-
-                                self.unify(a3, h_a);
-                            },
-                            _ => self.fail = true
-                        },
-                    _ => self.fail = true
-                };
-            } else {
-                return Err(functor!(self.atom_tbl,
-                                    "type_error",
-                                    1,
-                                    [heap_atom!("compound_expected", self.atom_tbl)]));
-            }
-        }
-
-        Ok(())
-    }
-
-    fn compare_numbers(&mut self, cmp: CompareNumberQT, n1: Number, n2: Number) {
-        self.fail = match cmp {
-            CompareNumberQT::GreaterThan if !(n1.gt(n2)) => true,
-            CompareNumberQT::GreaterThanOrEqual if !(n1.gte(n2)) => true,
-            CompareNumberQT::LessThan if !(n1.lt(n2)) => true,
-            CompareNumberQT::LessThanOrEqual if !(n1.lte(n2)) => true,
-            CompareNumberQT::NotEqual if !(n1.ne(n2)) => true,
-            CompareNumberQT::Equal if !(n1.eq(n2)) => true,
-            _ => false
-        };
-
-        self.p += 1;
-    }
-    
-    pub(super) fn execute_built_in_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction)
-    {
-        match instr {
-            &BuiltInInstruction::CompareNumber(cmp, ref at_1, ref at_2) => {
-                let n1 = try_or_fail!(self, self.get_number(at_1));
-                let n2 = try_or_fail!(self, self.get_number(at_2));
-
-                self.compare_numbers(cmp, n1, n2);
-            },
-            &BuiltInInstruction::DynamicCompareNumber(cmp) => {
-                let n1 = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(1)));
-                let n2 = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(2)));
-
-                self.compare_numbers(cmp, n1, n2);
-            },
-            &BuiltInInstruction::DynamicIs => {
-                let a = self[temp_v!(1)].clone();
-                let result = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(2)));
-
-                self.unify(a, Addr::Con(Constant::Number(result)));
-                self.p += 1;
-            },
-            &BuiltInInstruction::GetArgCall =>
-                try_or_fail!(self, {                    
-                    let val = self.try_get_arg();
-                    self.p += 1;
-                    val
-                }),
-            &BuiltInInstruction::GetArgExecute =>
-                try_or_fail!(self, {
-                    let val = self.try_get_arg();
-                    self.p = self.cp;
-                    val
-                }),
-            &BuiltInInstruction::GetCurrentBlock => {
-                let c = Constant::Usize(self.block);
-                let addr = self[temp_v!(1)].clone();
-
-                self.write_constant_to_var(addr, c);
-                self.p += 1;
-            },
-            &BuiltInInstruction::EraseBall => {
-                self.ball.0 = 0;
-                self.ball.1.truncate(0);
-                self.p += 1;
-            },
-            &BuiltInInstruction::GetBall => {
-                let addr = self.store(self.deref(self[temp_v!(1)].clone()));
-                let h = self.heap.h;
-
-                if self.ball.1.len() > 0 {
-                    self.copy_and_align_ball_to_heap();
-                } else {
-                    self.fail = true;
-                    return;
-                }
-
-                let ball = self.heap[h].as_addr(h);
-
-                match addr.as_var() {
-                    Some(r) => {
-                        self.bind(r, ball);
-                        self.p += 1;
-                    },
-                    _ => self.fail = true
-                };
-            },
-            &BuiltInInstruction::GetCutPoint(r) => {
-                let c = Constant::Usize(self.b);
-                self[r] = Addr::Con(c);
-
-                self.p += 1;
-            },            
-            &BuiltInInstruction::SetBall => {
-                let addr = self[temp_v!(1)].clone();
-                self.ball.0 = self.heap.h;
-
-                {
-                    let mut duplicator = DuplicateBallTerm::new(self);
-                    duplicator.duplicate_term(addr);
-                }
-
-                self.p += 1;
-            },
-            &BuiltInInstruction::SetCutPoint(r) => {
-                let addr = self.store(self.deref(self[r].clone()));
-
-                match addr {
-                    Addr::Con(Constant::Usize(nb)) => {
-                        if self.b > nb {
-                            self.b = nb;
-                            self.tidy_trail();
-                        }
-
-                        self.p += 1;
-                    },
-                    _ => self.fail = true
-                };
-            },
-            &BuiltInInstruction::CleanUpBlock => {
-                let nb = self.store(self.deref(self[temp_v!(1)].clone()));
-
-                match nb {
-                    Addr::Con(Constant::Usize(nb)) => {
-                        let b = self.b - 1;
-
-                        if nb > 0 && self.or_stack[b].b == nb {
-                            self.b = self.or_stack[nb - 1].b;
-                            self.or_stack.truncate(self.b);
-                        }
-
-                        self.p += 1;
-                    },
-                    _ => self.fail = true
-                };
-            },
-            &BuiltInInstruction::InstallNewBlock => {
-                self.block = self.b;
-                let c = Constant::Usize(self.block);
-                let addr = self[temp_v!(1)].clone();
-
-                self.write_constant_to_var(addr, c);
-                self.p += 1;
-            },
-            &BuiltInInstruction::ResetBlock => {
-                let addr = self.deref(self[temp_v!(1)].clone());
-
-                match self.store(addr) {
-                    Addr::Con(Constant::Usize(b)) => {
-                        self.block = b;
-                        self.p += 1;
-                    },
-                    _ => self.fail = true
-                };
-            },
-            &BuiltInInstruction::UnwindStack => {
-                self.b = self.block;
-                self.or_stack.truncate(self.b);
-
-                self.fail = true;
-            },
-            &BuiltInInstruction::IsAtomic(r) => {
-                let d = self.store(self.deref(self[r].clone()));
-
-                match d {
-                    Addr::Con(_) => self.p += 1,
-                    _ => self.fail = true
-                };
-            },
-            &BuiltInInstruction::IsInteger(r) => {
-                let d = self.store(self.deref(self[r].clone()));
-
-                match d {
-                    Addr::Con(Constant::Number(Number::Integer(_))) => self.p += 1,
-                    _ => self.fail = true
-                };
-            },
-            &BuiltInInstruction::IsVar(r) => {
-                let d = self.store(self.deref(self[r].clone()));
-
-                match d {
-                    Addr::HeapCell(_) | Addr::StackCell(_,_) => self.p += 1,
-                    _ => self.fail = true
-                };
-            },
-            &BuiltInInstruction::InternalCallN =>
-                self.handle_internal_call_n(code_dir),
-            &BuiltInInstruction::Fail => {
-                self.fail = true;
-                self.p += 1;
-            },
-            &BuiltInInstruction::Succeed => {
-                self.p += 1;
-            },
-            &BuiltInInstruction::Unify => {
-                let a1 = self[temp_v!(1)].clone();
-                let a2 = self[temp_v!(2)].clone();
-
-                self.unify(a1, a2);
-                self.p += 1;
-            }
-        };
-    }
-
-    fn try_functor(&mut self) -> Result<(), Vec<HeapCellValue>> {
-        let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
-
-        match a1.clone() {
-            Addr::Str(o) =>
-                match self.heap[o].clone() {
-                    HeapCellValue::NamedStr(arity, name, _) => {
-                        let name  = Addr::Con(Constant::Atom(name)); // A2
-                        let arity = Addr::Con(Constant::Number(rc_integer!(arity)));
-
-                        let a2 = self[temp_v!(2)].clone();
-                        self.unify(a2, name);
-
-                        if !self.fail {
-                            let a3 = self[temp_v!(3)].clone();
-                            self.unify(a3, arity);
-                        }
-                    },
-                    _ => self.fail = true
-                },
-            Addr::HeapCell(_) | Addr::StackCell(_, _) => {
-                let name  = self.store(self.deref(self[temp_v!(2)].clone()));
-                let arity = self.store(self.deref(self[temp_v!(3)].clone()));
-
-                if let Addr::Con(Constant::Atom(name)) = name {
-                    if let Addr::Con(Constant::Number(Number::Integer(arity))) = arity {
-                        let f_a = Addr::Str(self.heap.h);
-                        let arity = match arity.to_usize() {
-                            Some(arity) => arity,
-                            None => {
-                                self.fail = true;
-                                return Ok(());
-                            }
-                        };
-
-                        if arity > 0 {
-                            self.heap.push(HeapCellValue::NamedStr(arity, name, None));
-                        } else {
-                            let c = Constant::Atom(name.clone());
-                            self.heap.push(HeapCellValue::Addr(Addr::Con(c)));
-                        }
-
-                        for _ in 0 .. arity {
-                            let h = self.heap.h;
-                            self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                        }
-
-                        self.unify(a1, f_a);
-                    } else {
-                        return Err(functor!(self.atom_tbl, "instantiation_error", 0, []));
-                    }
-                } else {
-                    return Err(functor!(self.atom_tbl, "instantiation_error", 0, []));
-                }
-            },
-            _ => {
-                let a2 = self[temp_v!(2)].clone();
-                self.unify(a1, a2);
-
-                if !self.fail {
-                    let a3 = self[temp_v!(3)].clone();
-                    self.unify(a3, Addr::Con(Constant::Number(rc_integer!(0))));
-                }
-            }
-        };
-
-        Ok(())
-    }
-
-    fn duplicate_term(&mut self) {
-        let old_h = self.heap.h;
-
-        let a1 = self[temp_v!(1)].clone();
-        let a2 = self[temp_v!(2)].clone();
-
-        // drop the mutable references contained in gadget
-        // once the term has been duplicated.
-        {
-            let mut gadget = DuplicateTerm::new(self);
-            gadget.duplicate_term(a1);
-        }
-
-        self.unify(Addr::HeapCell(old_h), a2);
-    }
-
-    pub(super) fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
-    {
-        match instr {
-            &ControlInstruction::Allocate(num_cells) => {
-                let gi = self.next_global_index();
-
-                self.p += 1;
-
-                if self.e + 1 < self.and_stack.len() {
-                    let and_gi = self.and_stack[self.e].global_index;
-                    let or_gi = self.or_stack.top()
-                        .map(|or_fr| or_fr.global_index)
-                        .unwrap_or(0);
-
-                    if and_gi > or_gi {
-                        let index = self.e + 1;
-
-                        self.and_stack[index].e  = self.e;
-                        self.and_stack[index].cp = self.cp;
-                        self.and_stack[index].global_index = gi;
-
-                        self.and_stack.resize(index, num_cells);
-
-                        self.e = index;
-
-                        return;
-                    }
-                }
-
-                self.and_stack.push(gi, self.e, self.cp, num_cells);
-                self.e = self.and_stack.len() - 1;
-            },
-            &ControlInstruction::ArgCall => {
-                self.cp = self.p + 1;
-                self.num_of_args = 3;
-                self.b0 = self.b;
-                self.p  = CodePtr::DirEntry(150);
-            },
-            &ControlInstruction::ArgExecute => {
-                self.num_of_args = 3;
-                self.b0 = self.b;
-                self.p  = CodePtr::DirEntry(150);
-            },
-            &ControlInstruction::Call(ref name, arity, _) =>
-                self.try_call_predicate(code_dir, name.clone(), arity),
-            &ControlInstruction::CatchCall => {
-                self.cp = self.p + 1;
-                self.num_of_args = 3;
-                self.b0 = self.b;
-                self.p  = CodePtr::DirEntry(5);
-            },
-            &ControlInstruction::CatchExecute => {
-                self.num_of_args = 3;
-                self.b0 = self.b;
-                self.p  = CodePtr::DirEntry(5);
-            },
-            &ControlInstruction::CallN(arity) =>
-                if let Some((name, arity)) = self.setup_call_n(arity) {
-                    self.try_call_predicate(code_dir, name, arity);
-                },
-            &ControlInstruction::Deallocate => {
-                let e = self.e;
-
-                self.cp = self.and_stack[e].cp;
-                self.e  = self.and_stack[e].e;
-
-                self.p += 1;
-            },
-            &ControlInstruction::DisplayCall => {
-                let output = self.print_term(self[temp_v!(1)].clone(),
-                                             DisplayFormatter {},
-                                             PrinterOutputter::new());
-
-                println!("{}", output.result());
-
-                self.p += 1;
-            },
-            &ControlInstruction::DisplayExecute => {
-                let output = self.print_term(self[temp_v!(1)].clone(),
-                                             DisplayFormatter {},
-                                             PrinterOutputter::new());
-
-                println!("{}", output.result());
-
-                self.p = self.cp;
-            },
-            &ControlInstruction::DuplicateTermCall => {
-                self.duplicate_term();
-                self.p += 1;
-            },
-            &ControlInstruction::DuplicateTermExecute => {
-                self.duplicate_term();
-                self.p = self.cp;
-            },
-            &ControlInstruction::Execute(ref name, arity) =>
-                self.try_execute_predicate(code_dir, name.clone(), arity),
-            &ControlInstruction::ExecuteN(arity) =>
-                if let Some((name, arity)) = self.setup_call_n(arity) {
-                    self.try_execute_predicate(code_dir, name, arity);
-                },
-            &ControlInstruction::FunctorCall =>
-                try_or_fail!(self, {
-                    let val = self.try_functor();
-                    self.p += 1;
-                    val
-                }),
-            &ControlInstruction::FunctorExecute =>
-                try_or_fail!(self, {
-                    let val = self.try_functor();
-                    self.p = self.cp;
-                    val
-                }),
-            &ControlInstruction::GotoCall(p, arity) => {
-                self.cp = self.p + 1;
-                self.num_of_args = arity;
-                self.b0 = self.b;
-                self.p  = CodePtr::DirEntry(p);
-            },
-            &ControlInstruction::GotoExecute(p, arity) => {
-                self.num_of_args = arity;
-                self.b0 = self.b;
-                self.p  = CodePtr::DirEntry(p);
-            },
-            &ControlInstruction::IsCall(r, ref at) => {
-                let a1 = self[r].clone();
-                let a2 = try_or_fail!(self, self.get_number(at));
-
-                self.unify(a1, Addr::Con(Constant::Number(a2)));
-                self.p += 1;
-            },
-            &ControlInstruction::IsExecute(r, ref at) => {
-                let a1 = self[r].clone();
-                let a2 = try_or_fail!(self, self.get_number(at));
-
-                self.unify(a1, Addr::Con(Constant::Number(a2)));
-                self.p = self.cp;
-            },
-            &ControlInstruction::JmpByCall(arity, offset) => {
-                self.cp = self.p + 1;
-                self.num_of_args = arity;
-                self.b0 = self.b;
-                self.p += offset;
-            },
-            &ControlInstruction::JmpByExecute(arity, offset) => {                
-                self.num_of_args = arity;
-                self.b0 = self.b;
-                self.p += offset;
-            },
-            &ControlInstruction::Proceed =>
-                self.p = self.cp,
-            &ControlInstruction::ThrowCall => {
-                self.cp = self.p + 1;
-                self.goto_throw();
-            },
-            &ControlInstruction::ThrowExecute => {
-                self.goto_throw();
-            },
-        };
-    }
-
-    pub(super) fn execute_indexed_choice_instr(&mut self, instr: &IndexedChoiceInstruction)
-    {
-        match instr {
-            &IndexedChoiceInstruction::Try(l) => {
-                let n = self.num_of_args;
-                let gi = self.next_global_index();
-
-                self.or_stack.push(gi,
-                                   self.e,
-                                   self.cp,
-                                   self.b,
-                                   self.p + 1,
-                                   self.tr,
-                                   self.heap.h,
-                                   self.b0,
-                                   self.num_of_args);
-
-                self.b = self.or_stack.len();
-                let b = self.b - 1;
-
-                for i in 1 .. n + 1 {
-                    self.or_stack[b][i] = self.registers[i].clone();
-                }
-
-                self.hb = self.heap.h;
-                self.p += l;
-            },
-            &IndexedChoiceInstruction::Retry(l) => {
-                let b = self.b - 1;
-                let n = self.or_stack[b].num_args();
-
-                for i in 1 .. n + 1 {
-                    self.registers[i] = self.or_stack[b][i].clone();
-                }
-
-                self.e  = self.or_stack[b].e;
-                self.cp = self.or_stack[b].cp;
-
-                self.or_stack[b].bp = self.p + 1;
-
-                let old_tr  = self.or_stack[b].tr;
-                let curr_tr = self.tr;
-
-                self.unwind_trail(old_tr, curr_tr);
-                self.tr = self.or_stack[b].tr;
-
-                self.trail.truncate(self.tr);
-                self.heap.truncate(self.or_stack[b].h);
-
-                self.hb = self.heap.h;
-
-                self.p += l;
-            },
-            &IndexedChoiceInstruction::Trust(l) => {
-                let b = self.b - 1;
-                let n = self.or_stack[b].num_args();
-
-                for i in 1 .. n + 1 {
-                    self.registers[i] = self.or_stack[b][i].clone();
-                }
-
-                self.e  = self.or_stack[b].e;
-                self.cp = self.or_stack[b].cp;
-
-                let old_tr  = self.or_stack[b].tr;
-                let curr_tr = self.tr;
-
-                self.unwind_trail(old_tr, curr_tr);
-
-                self.tr = self.or_stack[b].tr;
-                self.trail.truncate(self.tr);
-
-                self.heap.truncate(self.or_stack[b].h);
-                self.b = self.or_stack[b].b;
-
-                self.or_stack.truncate(self.b);
-
-                self.hb = self.heap.h;
-                self.p += l;
-            },
-        };
-    }
-
-    pub(super) fn execute_choice_instr(&mut self, instr: &ChoiceInstruction)
-    {
-        match instr {
-            &ChoiceInstruction::TryMeElse(offset) => {
-                let n = self.num_of_args;
-                let gi = self.next_global_index();
-
-                self.or_stack.push(gi,
-                                   self.e,
-                                   self.cp,
-                                   self.b,
-                                   self.p + offset,
-                                   self.tr,
-                                   self.heap.h,
-                                   self.b0,
-                                   self.num_of_args);
-
-                self.b = self.or_stack.len();
-                let b  = self.b - 1;
-
-                for i in 1 .. n + 1 {
-                    self.or_stack[b][i] = self.registers[i].clone();
-                }
-
-                self.hb = self.heap.h;
-                self.p += 1;
-            },
-            &ChoiceInstruction::RetryMeElse(offset) => {
-                let b = self.b - 1;
-                let n = self.or_stack[b].num_args();
-
-                for i in 1 .. n + 1 {
-                    self.registers[i] = self.or_stack[b][i].clone();
-                }
-
-                self.e  = self.or_stack[b].e;
-                self.cp = self.or_stack[b].cp;
-
-                self.or_stack[b].bp = self.p + offset;
-
-                let old_tr  = self.or_stack[b].tr;
-                let curr_tr = self.tr;
-
-                self.unwind_trail(old_tr, curr_tr);
-                self.tr = self.or_stack[b].tr;
-
-                self.trail.truncate(self.tr);
-                self.heap.truncate(self.or_stack[b].h);
-
-                self.hb = self.heap.h;
-
-                self.p += 1;
-            },
-            &ChoiceInstruction::TrustMe => {
-                let b = self.b - 1;
-                let n = self.or_stack[b].num_args();
-
-                for i in 1 .. n + 1 {
-                    self.registers[i] = self.or_stack[b][i].clone();
-                }
-
-                self.e  = self.or_stack[b].e;
-                self.cp = self.or_stack[b].cp;
-
-                let old_tr  = self.or_stack[b].tr;
-                let curr_tr = self.tr;
-
-                self.unwind_trail(old_tr, curr_tr);
-
-                self.tr = self.or_stack[b].tr;
-                self.trail.truncate(self.tr);
-
-                self.heap.truncate(self.or_stack[b].h);
-
-                self.b = self.or_stack[b].b;
-
-                self.or_stack.truncate(self.b);
-
-                self.hb = self.heap.h;
-                self.p += 1;
-            }
-        }
-    }
-
-    pub(super) fn execute_cut_instr(&mut self, instr: &CutInstruction) {
-        match instr {
-            &CutInstruction::NeckCut => {
-                let b = self.b;
-                let b0 = self.b0;
-
-                if b > b0 {
-                    self.b = b0;
-                    self.tidy_trail();
-                }
-
-                self.p += 1;
-            },
-            &CutInstruction::GetLevel => {
-                let b0 = self.b0;
-                let e  = self.e;
-
-                self.and_stack[e].b0 = b0;
-                self.p += 1;
-            },
-            &CutInstruction::Cut => {
-                let b = self.b;
-                let e = self.e;
-                let b0 = self.and_stack[e].b0; // STACK[E+2+1]
-
-                if b > b0 {
-                    self.b = b0;
-                    self.tidy_trail();
-                }
-
-                self.p += 1;
-            }
-        }
-    }
-
-    pub(super) fn reset(&mut self) {
-        self.hb = 0;
-        self.e = 0;
-        self.b = 0;
-        self.b0 = 0;
-        self.s = 0;
-        self.tr = 0;
-        self.p = CodePtr::default();
-        self.cp = CodePtr::default();
-        self.num_of_args = 0;
-
-        self.fail = false;
-        self.trail.clear();
-        self.heap.clear();
-        self.mode = MachineMode::Write;
-        self.and_stack.clear();
-        self.or_stack.clear();
-        self.registers = vec![Addr::HeapCell(0); 64];
-        self.block = 0;
-        self.ball = (0, Vec::new());
-    }
+    pub(super) interms: Vec<Number>, // intermediate numbers.
+    pub(super) sgc_cps: Vec<(Addr, usize, usize)>, // locations of cleaners/cut points, prev. block
 }
diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs
new file mode 100644 (file)
index 0000000..caea4b3
--- /dev/null
@@ -0,0 +1,1827 @@
+use prolog::and_stack::*;
+use prolog::ast::*;
+use prolog::builtins::*;
+use prolog::copier::*;
+use prolog::heap_iter::*;
+use prolog::heap_print::*;
+use prolog::machine::machine_state::*;
+use prolog::num::{Integer, ToPrimitive, Zero};
+use prolog::num::bigint::{BigInt, BigUint};
+use prolog::num::rational::Ratio;
+use prolog::or_stack::*;
+use prolog::tabled_rc::*;
+
+use std::cmp::max;
+use std::rc::Rc;
+
+macro_rules! try_or_fail {
+    ($s:ident, $e:expr) => {{
+        match $e {
+            Ok(val)  => val,
+            Err(msg) => {
+                $s.throw_exception(msg);
+                return;
+            }
+        }
+    }}
+}
+
+impl MachineState {
+    pub(super) fn new(atom_tbl: TabledData<Atom>) -> MachineState {
+        MachineState { atom_tbl,
+                       s: 0,
+                       p: CodePtr::default(),
+                       b: 0,
+                       b0: 0,
+                       e: 0,
+                       num_of_args: 0,
+                       cp: CodePtr::default(),
+                       fail: false,
+                       heap: Heap::with_capacity(256),
+                       mode: MachineMode::Write,
+                       and_stack: AndStack::new(),
+                       or_stack: OrStack::new(),
+                       registers: vec![Addr::HeapCell(0); 64],
+                       trail: Vec::new(),
+                       tr: 0,
+                       hb: 0,
+                       block: 0,
+                       ball: (0, Vec::new()),
+                       interms: vec![Number::default(); 256],
+                       sgc_cps: vec![]
+        }
+    }
+
+    fn next_global_index(&self) -> usize {
+        max(if self.and_stack.len() > 0 { self.and_stack[self.e].global_index } else { 0 },
+            if self.b > 0 { self.or_stack[self.b - 1].global_index } else { 0 }) + 1
+    }
+
+    pub(crate) fn store(&self, a: Addr) -> Addr {
+        match a {
+            Addr::HeapCell(r)       => self.heap[r].as_addr(r),
+            Addr::StackCell(fr, sc) => self.and_stack[fr][sc].clone(),
+            addr                    => addr
+        }
+    }
+
+    pub(crate) fn deref(&self, mut a: Addr) -> Addr {
+        loop {
+            let value = self.store(a.clone());
+
+            if value.is_ref() && value != a {
+                a = value;
+                continue;
+            }
+
+            return a;
+        };
+    }
+
+    fn bind(&mut self, r1: Ref, a2: Addr) {
+        let t2 = self.store(a2);
+
+        match r1 {
+            Ref::StackCell(fr, sc) =>
+                self.and_stack[fr][sc] = t2,
+            Ref::HeapCell(hc) =>
+                self.heap[hc] = HeapCellValue::Addr(t2)
+        };
+
+        self.trail(r1);
+    }
+
+    pub(super) fn print_term<Fmt, Outputter>(&self, a: Addr, fmt: Fmt, output: Outputter) -> Outputter
+        where Fmt: HeapCellValueFormatter, Outputter: HeapCellValueOutputter
+    {
+        let iter    = HeapCellPreOrderIterator::new(&self, a);
+        let printer = HeapCellPrinter::new(iter, fmt, output);
+
+        printer.print()
+    }
+
+    fn unify(&mut self, a1: Addr, a2: Addr) {
+        let mut pdl = vec![a1, a2];
+
+        self.fail = false;
+
+        while !(pdl.is_empty() || self.fail) {
+            let d1 = self.deref(pdl.pop().unwrap());
+            let d2 = self.deref(pdl.pop().unwrap());
+
+            if d1 != d2 {
+                match (self.store(d1.clone()), self.store(d2.clone())) {
+                    (Addr::HeapCell(hc), _) =>
+                        self.bind(Ref::HeapCell(hc), d2),
+                    (_, Addr::HeapCell(hc)) =>
+                        self.bind(Ref::HeapCell(hc), d1),
+                    (Addr::StackCell(fr, sc), _) =>
+                        self.bind(Ref::StackCell(fr, sc), d2),
+                    (_, Addr::StackCell(fr, sc)) =>
+                        self.bind(Ref::StackCell(fr, sc), d1),
+                    (Addr::Lis(a1), Addr::Lis(a2)) => {
+                        pdl.push(Addr::HeapCell(a1));
+                        pdl.push(Addr::HeapCell(a2));
+
+                        pdl.push(Addr::HeapCell(a1 + 1));
+                        pdl.push(Addr::HeapCell(a2 + 1));
+                    },
+                    (Addr::Con(c1), Addr::Con(c2)) => {
+                        if c1 != c2 {
+                            self.fail = true;
+                        }
+                    },
+                    (Addr::Str(a1), Addr::Str(a2)) => {
+                        let r1 = &self.heap[a1];
+                        let r2 = &self.heap[a2];
+
+                        if let &HeapCellValue::NamedStr(n1, ref f1, _) = r1 {
+                            if let &HeapCellValue::NamedStr(n2, ref f2, _) = r2 {
+                                if n1 == n2 && *f1 == *f2 {
+                                    for i in 1 .. n1 + 1 {
+                                        pdl.push(Addr::HeapCell(a1 + i));
+                                        pdl.push(Addr::HeapCell(a2 + i));
+                                    }
+
+                                    continue;
+                                }
+                            }
+                        }
+
+                        self.fail = true;
+                    },
+                    _ => self.fail = true
+                };
+            }
+        }
+    }
+
+    fn trail(&mut self, r: Ref) {
+        match r {
+            Ref::HeapCell(hc) => {
+                if hc < self.hb {
+                    self.trail.push(r);
+                    self.tr += 1;
+                }
+            },
+            Ref::StackCell(fr, _) => {
+                let fr_gi = self.and_stack[fr].global_index;
+                let b_gi  = if !self.or_stack.is_empty() {
+                    if self.b > 0 {
+                        let b = self.b - 1;
+                        self.or_stack[b].global_index
+                    } else {
+                        0
+                    }
+                } else {
+                    0
+                };
+
+                if fr_gi < b_gi {
+                    self.trail.push(r);
+                    self.tr += 1;
+                }
+            }
+        }
+    }
+
+    fn unwind_trail(&mut self, a1: usize, a2: usize) {
+        for i in a1 .. a2 {
+            match self.trail[i] {
+                Ref::HeapCell(r) =>
+                    self.heap[r] = HeapCellValue::Addr(Addr::HeapCell(r)),
+                Ref::StackCell(fr, sc) =>
+                    self.and_stack[fr][sc] = Addr::StackCell(fr, sc)
+            }
+        }
+    }
+
+    fn tidy_trail(&mut self) {
+        if self.b == 0 {
+            return;
+        }
+
+        let b = self.b - 1;
+        let mut i = self.or_stack[b].tr;
+
+        while i < self.tr {
+            let tr_i = self.trail[i];
+            let hb = self.hb;
+
+            match tr_i {
+                Ref::HeapCell(tr_i) =>
+                    if tr_i < hb { //|| ((h < tr_i) && tr_i < b) {
+                        i += 1;
+                    } else {
+                        let tr = self.tr;
+                        let val = self.trail[tr - 1];
+                        self.trail[i] = val;
+                    },
+                Ref::StackCell(fr, _) => {
+                    let b = self.b - 1;
+                    let fr_gi = self.and_stack[fr].global_index;
+                    let b_gi  = if !self.or_stack.is_empty() {
+                        self.or_stack[b].global_index
+                    } else {
+                        0
+                    };
+
+                    if fr_gi < b_gi {
+                        i += 1;
+                    } else {
+                        let tr = self.tr;
+                        let val = self.trail[tr - 1];
+                        self.trail[i] = val;
+                    }
+                }
+            };
+        }
+    }
+
+    fn write_constant_to_var(&mut self, addr: Addr, c: Constant) {
+        let addr = self.deref(addr);
+
+        match self.store(addr) {
+            Addr::HeapCell(hc) => {
+                self.heap[hc] = HeapCellValue::Addr(Addr::Con(c.clone()));
+                self.trail(Ref::HeapCell(hc));
+            },
+            Addr::StackCell(fr, sc) => {
+                self.and_stack[fr][sc] = Addr::Con(c.clone());
+                self.trail(Ref::StackCell(fr, sc));
+            },
+            Addr::Con(c1) => {
+                if c1 != c {
+                    self.fail = true;
+                }
+            },
+            _ => self.fail = true
+        };
+    }
+
+    fn get_number(&self, at: &ArithmeticTerm) -> Result<Number, Vec<HeapCellValue>> {
+
+        match at {
+            &ArithmeticTerm::Reg(r) => {
+                let addr = self[r].clone();
+                let item = self.store(self.deref(addr));
+
+                match item {
+                    Addr::Con(Constant::Number(n)) => Ok(n),
+                    _ => {
+                        let atom_tbl = self.atom_tbl.clone();
+                        Err(functor!(self.atom_tbl,
+                                     "instantiation_error",
+                                     1,
+                                     [heap_atom!("(is)/2", atom_tbl)]))
+                    }
+                }
+            },
+            &ArithmeticTerm::Interm(i)     => Ok(self.interms[i-1].clone()),
+            &ArithmeticTerm::Number(ref n) => Ok(n.clone()),
+        }
+    }
+
+    fn get_rational(&self, at: &ArithmeticTerm) -> Result<Rc<Ratio<BigInt>>, Vec<HeapCellValue>> {
+        let n = self.get_number(at)?;
+
+        match n {
+            Number::Rational(r) => Ok(r),
+            Number::Float(fl) =>
+                if let Some(r) = Ratio::from_float(fl.into_inner()) {
+                    Ok(Rc::new(r))
+                } else {
+                    Err(functor!(self.atom_tbl,
+                                 "instantiation_error",
+                                 1,
+                                 [heap_atom!("(is)/2", self.atom_tbl)]))
+                },
+            Number::Integer(bi) =>
+                Ok(Rc::new(Ratio::from_integer((*bi).clone())))
+        }
+    }
+
+    fn signed_bitwise_op<Op>(&self, n1: &BigInt, n2: &BigInt, f: Op) -> Rc<BigInt>
+        where Op: FnOnce(&BigUint, &BigUint) -> BigUint
+    {
+        let n1_b = n1.to_signed_bytes_le();
+        let n2_b = n2.to_signed_bytes_le();
+
+        let u_n1 = BigUint::from_bytes_le(&n1_b);
+        let u_n2 = BigUint::from_bytes_le(&n2_b);
+
+        Rc::new(BigInt::from_signed_bytes_le(&f(&u_n1, &u_n2).to_bytes_le()))
+    }
+
+    fn arith_eval_by_metacall(&self, r: RegType) -> Result<Number, Vec<HeapCellValue>>
+    {
+        let instantiation_err = functor!(self.atom_tbl.clone(), "instantiation_error", 1,
+                                         [heap_atom!("(is)/2", self.atom_tbl.clone())]);
+
+        let a = self[r].clone();
+
+        if let &Addr::Con(Constant::Number(ref n)) = &a {
+            return Ok(n.clone());
+        }
+
+        let mut interms: Vec<Number> = Vec::with_capacity(64);
+
+        for heap_val in self.post_order_iter(a) {
+            match heap_val {
+                HeapCellValue::NamedStr(2, name, Some(Fixity::In)) => {
+                    let a2 = interms.pop().unwrap();
+                    let a1 = interms.pop().unwrap();
+
+                    match name.as_str() {
+                        "+" => interms.push(a1 + a2),
+                        "-" => interms.push(a1 - a2),
+                        "*" => interms.push(a1 * a2),
+                        "rdiv" =>
+                            match NumberPair::from(a1, a2) {
+                                NumberPair::Rational(r1, r2) =>
+                                    interms.push(Number::Rational(self.rdiv(r1, r2)?)),
+                                _ =>
+                                    return Err(instantiation_err)
+                            },
+                        "//"  => interms.push(Number::Integer(self.idiv(a1, a2)?)),
+                        "div" => interms.push(Number::Integer(self.fidiv(a1, a2)?)),
+                        ">>"  => interms.push(Number::Integer(self.shr(a1, a2)?)),
+                        "<<"  => interms.push(Number::Integer(self.shl(a1, a2)?)),
+                        "/\\" => interms.push(Number::Integer(self.and(a1, a2)?)),
+                        "\\/" => interms.push(Number::Integer(self.or(a1, a2)?)),
+                        "xor" => interms.push(Number::Integer(self.xor(a1, a2)?)),
+                        "mod" => interms.push(Number::Integer(self.modulus(a1, a2)?)),
+                        "rem" => interms.push(Number::Integer(self.remainder(a1, a2)?)),
+                        _     => return Err(instantiation_err)
+                    }
+                },
+                HeapCellValue::NamedStr(1, name, Some(Fixity::Pre)) => {
+                    let a1 = interms.pop().unwrap();
+
+                    match name.as_str() {
+                        "-" => interms.push(- a1),
+                         _  => return Err(instantiation_err)
+                    }
+                },
+                HeapCellValue::Addr(Addr::Con(Constant::Number(n))) =>
+                    interms.push(n),
+                _ =>
+                    return Err(instantiation_err)
+            }
+        };
+
+        Ok(interms.pop().unwrap())
+    }
+
+    fn rdiv(&self, r1: Rc<Ratio<BigInt>>, r2: Rc<Ratio<BigInt>>)
+            -> Result<Rc<Ratio<BigInt>>, Vec<HeapCellValue>>
+    {
+        if *r2 == Ratio::zero() {
+            Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                         [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
+        } else {
+            Ok(Rc::new(&*r1 / &*r2))
+        }
+    }
+
+    fn fidiv(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
+    {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                if *n2 == BigInt::zero() {
+                    Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                                 [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
+                } else {
+                    Ok(Rc::new(n1.div_floor(&n2)))
+                },
+            _ => Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                              [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
+        }
+    }
+
+    fn idiv(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
+    {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                if *n2 == BigInt::zero() {
+                    Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                                 [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
+                } else {
+                    Ok(Rc::new(&*n1 / &*n2))
+                },
+            _ =>
+                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
+        }
+    }
+
+    fn div(&self, n1: Number, n2: Number) -> Result<Number, Vec<HeapCellValue>>
+    {
+        if n2.is_zero() {
+            Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                         [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
+        } else {
+            Ok(n1 / n2)
+        }
+    }
+
+    fn shr(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
+    {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                match n2.to_usize() {
+                    Some(n2) => Ok(Rc::new(&*n1 >> n2)),
+                    _        => Ok(Rc::new(&*n1 >> usize::max_value()))
+                },
+            _ =>
+                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
+        }
+    }
+
+    fn shl(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
+    {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                match n2.to_usize() {
+                    Some(n2) => Ok(Rc::new(&*n1 << n2)),
+                    _        => Ok(Rc::new(&*n1 << usize::max_value()))
+                },
+            _ =>
+                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
+        }
+    }
+
+    fn xor(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
+    {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 ^ u_n2)),
+            _ =>
+                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
+        }
+    }
+
+    fn and(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
+    {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 & u_n2)),
+            _ =>
+                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
+        }
+    }
+
+    fn modulus(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
+    {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                if *n2 == BigInt::zero() {
+                    Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                                 [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
+                } else {
+                    Ok(Rc::new(n1.mod_floor(&n2)))
+                },
+            _ =>
+                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
+        }
+    }
+
+    fn remainder(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
+    {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                if *n2 == BigInt::zero() {
+                    Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                                 [heap_atom!("zero_divisor", self.atom_tbl.clone())]))
+                } else {
+                    Ok(Rc::new(&*n1 % &*n2))
+                },
+            _ =>
+                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
+        }
+    }
+
+    fn or(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, Vec<HeapCellValue>>
+    {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 & u_n2)),
+            _ =>
+                Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1,
+                             [heap_atom!("expected_integer_args", self.atom_tbl.clone())]))
+        }
+    }
+
+    pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
+        match instr {
+            &ArithmeticInstruction::Add(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = n1 + n2;
+                self.p += 1;
+            },
+            &ArithmeticInstruction::Sub(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = n1 - n2;
+                self.p += 1;
+            },
+            &ArithmeticInstruction::Mul(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = n1 * n2;
+                self.p += 1;
+            },
+            &ArithmeticInstruction::RDiv(ref a1, ref a2, t) => {
+                let r1 = try_or_fail!(self, self.get_rational(a1));
+                let r2 = try_or_fail!(self, self.get_rational(a2));
+
+                self.interms[t - 1] = Number::Rational(try_or_fail!(self, self.rdiv(r1, r2)));
+                self.p += 1;
+            },
+            &ArithmeticInstruction::FIDiv(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.fidiv(n1, n2)));
+                self.p += 1;
+            },
+            &ArithmeticInstruction::IDiv(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.idiv(n1, n2)));
+                self.p += 1;
+            },
+            &ArithmeticInstruction::Neg(ref a1, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+
+                self.interms[t - 1] = - n1;
+                self.p += 1;
+            },
+            &ArithmeticInstruction::Div(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = try_or_fail!(self, self.div(n1, n2));
+                self.p += 1;
+            },
+            &ArithmeticInstruction::Shr(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.shr(n1, n2)));
+                self.p += 1;
+            },
+            &ArithmeticInstruction::Shl(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.shl(n1, n2)));
+                self.p += 1;
+            },
+            &ArithmeticInstruction::Xor(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.xor(n1, n2)));
+                self.p += 1;
+            },
+            &ArithmeticInstruction::And(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.and(n1, n2)));
+                self.p += 1;
+            },
+            &ArithmeticInstruction::Or(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.or(n1, n2)));
+                self.p += 1;
+            },
+            &ArithmeticInstruction::Mod(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.modulus(n1, n2)));
+                self.p += 1;
+            },
+            &ArithmeticInstruction::Rem(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.remainder(n1, n2)));
+                self.p += 1;
+            }
+        };
+    }
+
+    pub(super) fn execute_fact_instr(&mut self, instr: &FactInstruction) {
+        match instr {
+            &FactInstruction::GetConstant(_, ref c, reg) => {
+                let addr = self[reg].clone();
+                self.write_constant_to_var(addr, c.clone());
+            },
+            &FactInstruction::GetList(_, reg) => {
+                let addr = self.deref(self[reg].clone());
+
+                match self.store(addr.clone()) {
+                    Addr::HeapCell(hc) => {
+                        let h = self.heap.h;
+
+                        self.heap.push(HeapCellValue::Addr(Addr::Lis(h+1)));
+                        self.bind(Ref::HeapCell(hc), Addr::HeapCell(h));
+
+                        self.mode = MachineMode::Write;
+                    },
+                    Addr::StackCell(fr, sc) => {
+                        let h = self.heap.h;
+
+                        self.heap.push(HeapCellValue::Addr(Addr::Lis(h+1)));
+                        self.bind(Ref::StackCell(fr, sc), Addr::HeapCell(h));
+
+                        self.mode = MachineMode::Write;
+                    },
+                    Addr::Lis(a) => {
+                        self.s = a;
+                        self.mode = MachineMode::Read;
+                    },
+                    _ => self.fail = true
+                };
+            },
+            &FactInstruction::GetStructure(_, ref name, arity, reg, fixity) => {
+                let addr = self.deref(self[reg].clone());
+
+                match self.store(addr.clone()) {
+                    Addr::Str(a) => {
+                        let result = &self.heap[a];
+
+                        if let &HeapCellValue::NamedStr(narity, ref str, _) = result {
+                            if narity == arity && *name == *str {
+                                self.s = a + 1;
+                                self.mode = MachineMode::Read;
+                            } else {
+                                self.fail = true;
+                            }
+                        }
+                    },
+                    Addr::HeapCell(_) | Addr::StackCell(_, _) => {
+                        let h = self.heap.h;
+
+                        self.heap.push(HeapCellValue::Addr(Addr::Str(h + 1)));
+                        self.heap.push(HeapCellValue::NamedStr(arity, name.clone(), fixity));
+
+                        self.bind(addr.as_var().unwrap(), Addr::HeapCell(h));
+
+                        self.mode = MachineMode::Write;
+                    },
+                    _ => self.fail = true
+                };
+            },
+            &FactInstruction::GetVariable(norm, arg) =>
+                self[norm] = self.registers[arg].clone(),
+            &FactInstruction::GetValue(norm, arg) => {
+                let norm_addr = self[norm].clone();
+                let reg_addr  = self.registers[arg].clone();
+
+                self.unify(norm_addr, reg_addr);
+            },
+            &FactInstruction::UnifyConstant(ref c) => {
+                match self.mode {
+                    MachineMode::Read  => {
+                        let addr = Addr::HeapCell(self.s);
+                        self.write_constant_to_var(addr, c.clone());
+                    },
+                    MachineMode::Write => {
+                        self.heap.push(HeapCellValue::Addr(Addr::Con(c.clone())));
+                    }
+                };
+
+                self.s += 1;
+            },
+            &FactInstruction::UnifyVariable(reg) => {
+                match self.mode {
+                    MachineMode::Read  =>
+                        self[reg] = self.heap[self.s].as_addr(self.s),
+                    MachineMode::Write => {
+                        let h = self.heap.h;
+
+                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
+                        self[reg] = Addr::HeapCell(h);
+                    }
+                };
+
+                self.s += 1;
+            },
+            &FactInstruction::UnifyLocalValue(reg) => {
+                let s = self.s;
+
+                match self.mode {
+                    MachineMode::Read  => {
+                        let reg_addr = self[reg].clone();
+                        self.unify(reg_addr, Addr::HeapCell(s));
+                    },
+                    MachineMode::Write => {
+                        let addr = self.deref(self[reg].clone());
+                        let h    = self.heap.h;
+
+                        if let Addr::HeapCell(hc) = addr {
+                            if hc < h {
+                                let val = self.heap[hc].clone();
+
+                                self.heap.push(val);
+                                self.s += 1;
+
+                                return;
+                            }
+                        }
+
+                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
+                        self.bind(Ref::HeapCell(h), addr);
+                    }
+                };
+
+                self.s += 1;
+            },
+            &FactInstruction::UnifyValue(reg) => {
+                let s = self.s;
+
+                match self.mode {
+                    MachineMode::Read  => {
+                        let reg_addr = self[reg].clone();
+                        self.unify(reg_addr, Addr::HeapCell(s));
+                    },
+                    MachineMode::Write => {
+                        let heap_val = self.store(self[reg].clone());
+                        self.heap.push(HeapCellValue::Addr(heap_val));
+                    }
+                };
+
+                self.s += 1;
+            },
+            &FactInstruction::UnifyVoid(n) => {
+                match self.mode {
+                    MachineMode::Read =>
+                        self.s += n,
+                    MachineMode::Write => {
+                        let h = self.heap.h;
+
+                        for i in h .. h + n {
+                            self.heap.push(HeapCellValue::Addr(Addr::HeapCell(i)));
+                        }
+                    }
+                };
+            }
+        };
+    }
+
+    pub(super) fn execute_indexing_instr(&mut self, instr: &IndexingInstruction) {
+        match instr {
+            &IndexingInstruction::SwitchOnTerm(v, c, l, s) => {
+                let a1 = self.registers[1].clone();
+                let addr = self.store(self.deref(a1));
+
+                let offset = match addr {
+                    Addr::HeapCell(_) | Addr::StackCell(_, _) => v,
+                    Addr::Con(_) => c,
+                    Addr::Lis(_) => l,
+                    Addr::Str(_) => s
+                };
+
+                match offset {
+                    0 => self.fail = true,
+                    o => self.p += o
+                };
+            },
+            &IndexingInstruction::SwitchOnConstant(_, ref hm) => {
+                let a1 = self.registers[1].clone();
+                let addr = self.store(self.deref(a1));
+
+                let offset = match addr {
+                    Addr::Con(constant) => {
+                        match hm.get(&constant) {
+                            Some(offset) => *offset,
+                            _ => 0
+                        }
+                    },
+                    _ => 0
+                };
+
+                match offset {
+                    0 => self.fail = true,
+                    o => self.p += o,
+                };
+            },
+            &IndexingInstruction::SwitchOnStructure(_, ref hm) => {
+                let a1 = self.registers[1].clone();
+                let addr = self.store(self.deref(a1));
+
+                let offset = match addr {
+                    Addr::Str(s) => {
+                        if let &HeapCellValue::NamedStr(arity, ref name, _) = &self.heap[s] {
+                            match hm.get(&(name.clone(), arity)) {
+                                Some(offset) => *offset,
+                                _ => 0
+                            }
+                        } else {
+                            0
+                        }
+                    },
+                    _ => 0
+                };
+
+                match offset {
+                    0 => self.fail = true,
+                    o => self.p += o
+                };
+            }
+        };
+    }
+
+    pub(super) fn execute_query_instr(&mut self, instr: &QueryInstruction) {
+        match instr {
+            &QueryInstruction::GetVariable(norm, arg) =>
+                self[norm] = self.registers[arg].clone(),
+            &QueryInstruction::PutConstant(_, ref constant, reg) =>
+                self[reg] = Addr::Con(constant.clone()),
+            &QueryInstruction::PutList(_, reg) =>
+                self[reg] = Addr::Lis(self.heap.h),
+            &QueryInstruction::PutStructure(_, ref name, arity, reg, fixity) => {
+                let h = self.heap.h;
+
+                self.heap.push(HeapCellValue::NamedStr(arity, name.clone(), fixity));
+                self[reg] = Addr::Str(h);
+            },
+            &QueryInstruction::PutUnsafeValue(n, arg) => {
+                let e    = self.e;
+                let addr = self.deref(Addr::StackCell(e, n));
+
+                if addr.is_protected(e) {
+                    self.registers[arg] = self.store(addr);
+                } else {
+                    let h = self.heap.h;
+
+                    self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
+                    self.bind(Ref::HeapCell(h), addr);
+
+                    self.registers[arg] = self.heap[h].as_addr(h);
+                }
+            },
+            &QueryInstruction::PutValue(norm, arg) =>
+                self.registers[arg] = self[norm].clone(),
+            &QueryInstruction::PutVariable(norm, arg) => {
+                match norm {
+                    RegType::Perm(n) => {
+                        let e = self.e;
+
+                        self[norm] = Addr::StackCell(e, n);
+                        self.registers[arg] = self[norm].clone();
+                    },
+                    RegType::Temp(_) => {
+                        let h = self.heap.h;
+                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
+
+                        self[norm] = Addr::HeapCell(h);
+                        self.registers[arg] = Addr::HeapCell(h);
+                    }
+                };
+            },
+            &QueryInstruction::SetConstant(ref c) => {
+                self.heap.push(HeapCellValue::Addr(Addr::Con(c.clone())));
+            },
+            &QueryInstruction::SetLocalValue(reg) => {
+                let addr = self.deref(self[reg].clone());
+                let h    = self.heap.h;
+
+                if let Addr::HeapCell(hc) = addr {
+                    if hc < h {
+                        self.heap.push(HeapCellValue::Addr(addr));
+                        return;
+                    }
+                }
+
+                self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
+                self.bind(Ref::HeapCell(h), addr);
+            },
+            &QueryInstruction::SetVariable(reg) => {
+                let h = self.heap.h;
+                self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
+                self[reg] = Addr::HeapCell(h);
+            },
+            &QueryInstruction::SetValue(reg) => {
+                let heap_val = self[reg].clone();
+                self.heap.push(HeapCellValue::Addr(heap_val));
+            },
+            &QueryInstruction::SetVoid(n) => {
+                let h = self.heap.h;
+
+                for i in h .. h + n {
+                    self.heap.push(HeapCellValue::Addr(Addr::HeapCell(i)));
+                }
+            }
+        }
+    }
+
+    fn try_call_predicate(&mut self, code_dir: &CodeDir, name: TabledRc<Atom>, arity: usize)
+    {
+        let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1);
+
+        match compiled_tl_index {
+            Some(compiled_tl_index) => {
+                self.cp = self.p + 1;
+                self.num_of_args = arity;
+                self.b0 = self.b;
+                self.p  = CodePtr::DirEntry(compiled_tl_index);
+            },
+            None => self.fail = true
+        };
+    }
+
+    fn try_execute_predicate(&mut self, code_dir: &CodeDir, name: TabledRc<Atom>, arity: usize)
+    {
+        let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1);
+
+        match compiled_tl_index {
+            Some(compiled_tl_index) => {
+                self.num_of_args = arity;
+                self.b0 = self.b;
+                self.p  = CodePtr::DirEntry(compiled_tl_index);
+            },
+            None => self.fail = true
+        };
+    }
+
+    fn handle_internal_call_n(&mut self, code_dir: &CodeDir)
+    {
+        let arity = self.num_of_args + 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;
+
+            if let Some((name, arity)) = self.setup_call_n(arity - 1) {
+                self.try_execute_predicate(code_dir, name, arity);
+            }
+        } else {
+            self.fail = true;
+        }
+    }
+
+    fn goto_throw(&mut self) {
+        self.num_of_args = 1;
+        self.b0 = self.b;
+        self.p  = CodePtr::DirEntry(59);
+    }
+
+    fn throw_exception(&mut self, hcv: Vec<HeapCellValue>) {
+        let h = self.heap.h;
+
+        self.registers[1] = Addr::HeapCell(h);
+
+        self.heap.append(hcv);
+        self.goto_throw();
+    }
+
+    fn setup_call_n(&mut self, arity: usize) -> Option<PredicateKey>
+    {
+        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 atom_tbl = self.atom_tbl.clone();
+                        self.throw_exception(functor!(atom_tbl,
+                                                      "representation_error",
+                                                      1,
+                                                      [heap_atom!("exceeds_max_arity", atom_tbl)]));
+                        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 atom_tbl = self.atom_tbl.clone();
+                self.throw_exception(functor!(atom_tbl, "instantiation_error", 0, []));
+                return None;
+            },
+            _ => {
+                let atom_tbl = self.atom_tbl.clone();
+                self.throw_exception(functor!(atom_tbl,
+                                              "type_error",
+                                              2,
+                                              [heap_atom!("callable", atom_tbl),
+                                               HeapCellValue::Addr(addr)]));
+                return None;
+            }
+        };
+
+        Some((name, arity + narity - 1))
+    }
+
+    pub(super) fn copy_and_align_ball_to_heap(&mut self) {
+        let diff = self.ball.0 - self.heap.h;
+
+        for heap_value in self.ball.1.iter().cloned() {
+            self.heap.push(match heap_value {
+                HeapCellValue::Addr(Addr::Con(c)) =>
+                    HeapCellValue::Addr(Addr::Con(c)),
+                HeapCellValue::Addr(Addr::Lis(a)) =>
+                    HeapCellValue::Addr(Addr::Lis(a - diff)),
+                HeapCellValue::Addr(Addr::HeapCell(hc)) =>
+                    HeapCellValue::Addr(Addr::HeapCell(hc - diff)),
+                HeapCellValue::Addr(Addr::Str(s)) =>
+                    HeapCellValue::Addr(Addr::Str(s - diff)),
+                _ => heap_value
+            });
+        }
+    }
+
+    fn try_get_arg(&mut self) -> Result<(), Vec<HeapCellValue>>
+    {
+        let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
+
+        if let Addr::Con(Constant::Number(Number::Integer(i))) = a1 {
+            let a2 = self.store(self.deref(self[temp_v!(2)].clone()));
+
+            if let Addr::Str(o) = a2 {
+                match self.heap[o].clone() {
+                    HeapCellValue::NamedStr(arity, _, _) =>
+                        match i.to_usize() {
+                            Some(i) if 1 <= i && i <= arity => {
+                                let a3  = self[temp_v!(3)].clone();
+                                let h_a = Addr::HeapCell(o + i);
+
+                                self.unify(a3, h_a);
+                            },
+                            _ => self.fail = true
+                        },
+                    _ => self.fail = true
+                };
+            } else {
+                return Err(functor!(self.atom_tbl,
+                                    "type_error",
+                                    1,
+                                    [heap_atom!("compound_expected", self.atom_tbl)]));
+            }
+        }
+
+        Ok(())
+    }
+
+    fn compare_numbers(&mut self, cmp: CompareNumberQT, n1: Number, n2: Number) {
+        self.fail = match cmp {
+            CompareNumberQT::GreaterThan if !(n1.gt(n2)) => true,
+            CompareNumberQT::GreaterThanOrEqual if !(n1.gte(n2)) => true,
+            CompareNumberQT::LessThan if !(n1.lt(n2)) => true,
+            CompareNumberQT::LessThanOrEqual if !(n1.lte(n2)) => true,
+            CompareNumberQT::NotEqual if !(n1.ne(n2)) => true,
+            CompareNumberQT::Equal if !(n1.eq(n2)) => true,
+            _ => false
+        };
+
+        self.p += 1;
+    }
+
+    fn reset_block(&mut self, addr: Addr) {
+        // let addr = self.deref(self[temp_v!(1)].clone());
+
+        match self.store(addr) {
+            Addr::Con(Constant::Usize(b)) => {
+                self.block = b;
+                self.p += 1;
+            },
+            _ => self.fail = true
+        };
+    }
+    
+    pub(super) fn execute_built_in_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction)
+    {
+        match instr {
+            &BuiltInInstruction::CompareNumber(cmp, ref at_1, ref at_2) => {
+                let n1 = try_or_fail!(self, self.get_number(at_1));
+                let n2 = try_or_fail!(self, self.get_number(at_2));
+
+                self.compare_numbers(cmp, n1, n2);
+            },
+            &BuiltInInstruction::DynamicCompareNumber(cmp) => {
+                let n1 = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(1)));
+                let n2 = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(2)));
+
+                self.compare_numbers(cmp, n1, n2);
+            },
+            &BuiltInInstruction::DynamicIs => {
+                let a = self[temp_v!(1)].clone();
+                let result = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(2)));
+
+                self.unify(a, Addr::Con(Constant::Number(result)));
+                self.p += 1;
+            },
+            &BuiltInInstruction::GetArgCall =>
+                try_or_fail!(self, {
+                    let val = self.try_get_arg();
+                    self.p += 1;
+                    val
+                }),
+            &BuiltInInstruction::GetArgExecute =>
+                try_or_fail!(self, {
+                    let val = self.try_get_arg();
+                    self.p = self.cp;
+                    val
+                }),
+            &BuiltInInstruction::GetCurrentBlock => {
+                let c = Constant::Usize(self.block);
+                let addr = self[temp_v!(1)].clone();
+
+                self.write_constant_to_var(addr, c);
+                self.p += 1;
+            },
+            &BuiltInInstruction::EraseBall => {
+                self.ball.0 = 0;
+                self.ball.1.truncate(0);
+                self.p += 1;
+            },
+            &BuiltInInstruction::GetBall => {
+                let addr = self.store(self.deref(self[temp_v!(1)].clone()));
+                let h = self.heap.h;
+
+                if self.ball.1.len() > 0 {
+                    self.copy_and_align_ball_to_heap();
+                } else {
+                    self.fail = true;
+                    return;
+                }
+
+                let ball = self.heap[h].as_addr(h);
+
+                match addr.as_var() {
+                    Some(r) => {
+                        self.bind(r, ball);
+                        self.p += 1;
+                    },
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::GetCutPoint(r) => {
+                let c = Constant::Usize(self.b);
+                self[r] = Addr::Con(c);
+
+                self.p += 1;
+            },
+            &BuiltInInstruction::InstallCleaner => {
+                let addr = self[temp_v!(1)].clone();
+                let b = self.b;
+                let block = self.block;
+
+                self.sgc_cps.push((addr, b, block));
+                self.p += 1;
+            },
+            &BuiltInInstruction::SetBall => {
+                let addr = self[temp_v!(1)].clone();
+                self.ball.0 = self.heap.h;
+
+                {
+                    let mut duplicator = DuplicateBallTerm::new(self);
+                    duplicator.duplicate_term(addr);
+                }
+
+                self.p += 1;
+            },
+            &BuiltInInstruction::SetCutPoint(r) => {
+                let addr = self.store(self.deref(self[r].clone()));
+
+                match addr {
+                    Addr::Con(Constant::Usize(nb)) => {
+                        if self.b > nb {
+                            self.b = nb;
+                            self.tidy_trail();
+                            self.or_stack.truncate(self.b);
+                        }
+
+                        self.p += 1;
+                    },
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::CleanUpBlock => {
+                let nb = self.store(self.deref(self[temp_v!(1)].clone()));
+
+                match nb {
+                    Addr::Con(Constant::Usize(nb)) => {
+                        let b = self.b - 1;
+
+                        if nb > 0 && self.or_stack[b].b == nb {
+                            self.b = self.or_stack[nb - 1].b;
+                            self.or_stack.truncate(self.b);
+                        }
+
+                        self.p += 1;
+                    },
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::InstallNewBlock => {
+                self.block = self.b;
+                let c = Constant::Usize(self.block);
+                let addr = self[temp_v!(1)].clone();
+
+                self.write_constant_to_var(addr, c);
+                self.p += 1;
+            },
+            &BuiltInInstruction::ResetBlock => {
+                let addr = self.deref(self[temp_v!(1)].clone());
+                self.reset_block(addr);
+            },
+            &BuiltInInstruction::UnwindStack => {
+                self.b = self.block;
+                self.or_stack.truncate(self.b);
+
+                self.fail = true;
+            },
+            &BuiltInInstruction::IsAtomic(r) => {
+                let d = self.store(self.deref(self[r].clone()));
+
+                match d {
+                    Addr::Con(_) => self.p += 1,
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::IsInteger(r) => {
+                let d = self.store(self.deref(self[r].clone()));
+
+                match d {
+                    Addr::Con(Constant::Number(Number::Integer(_))) => self.p += 1,
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::IsVar(r) => {
+                let d = self.store(self.deref(self[r].clone()));
+
+                match d {
+                    Addr::HeapCell(_) | Addr::StackCell(_,_) => self.p += 1,
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::InternalCallN =>
+                self.handle_internal_call_n(code_dir),
+            &BuiltInInstruction::Fail => {
+                self.fail = true;
+                self.p += 1;
+            },
+            &BuiltInInstruction::Succeed => {
+                self.p += 1;
+            },
+            &BuiltInInstruction::Unify => {
+                let a1 = self[temp_v!(1)].clone();
+                let a2 = self[temp_v!(2)].clone();
+
+                self.unify(a1, a2);
+                self.p += 1;
+            }
+        };
+    }
+
+    fn try_functor(&mut self) -> Result<(), Vec<HeapCellValue>> {
+        let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
+
+        match a1.clone() {
+            Addr::Str(o) =>
+                match self.heap[o].clone() {
+                    HeapCellValue::NamedStr(arity, name, _) => {
+                        let name  = Addr::Con(Constant::Atom(name)); // A2
+                        let arity = Addr::Con(Constant::Number(rc_integer!(arity)));
+
+                        let a2 = self[temp_v!(2)].clone();
+                        self.unify(a2, name);
+
+                        if !self.fail {
+                            let a3 = self[temp_v!(3)].clone();
+                            self.unify(a3, arity);
+                        }
+                    },
+                    _ => self.fail = true
+                },
+            Addr::HeapCell(_) | Addr::StackCell(_, _) => {
+                let name  = self.store(self.deref(self[temp_v!(2)].clone()));
+                let arity = self.store(self.deref(self[temp_v!(3)].clone()));
+
+                if let Addr::Con(Constant::Atom(name)) = name {
+                    if let Addr::Con(Constant::Number(Number::Integer(arity))) = arity {
+                        let f_a = Addr::Str(self.heap.h);
+                        let arity = match arity.to_usize() {
+                            Some(arity) => arity,
+                            None => {
+                                self.fail = true;
+                                return Ok(());
+                            }
+                        };
+
+                        if arity > 0 {
+                            self.heap.push(HeapCellValue::NamedStr(arity, name, None));
+                        } else {
+                            let c = Constant::Atom(name.clone());
+                            self.heap.push(HeapCellValue::Addr(Addr::Con(c)));
+                        }
+
+                        for _ in 0 .. arity {
+                            let h = self.heap.h;
+                            self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
+                        }
+
+                        self.unify(a1, f_a);
+                    } else {
+                        return Err(functor!(self.atom_tbl, "instantiation_error", 0, []));
+                    }
+                } else {
+                    return Err(functor!(self.atom_tbl, "instantiation_error", 0, []));
+                }
+            },
+            _ => {
+                let a2 = self[temp_v!(2)].clone();
+                self.unify(a1, a2);
+
+                if !self.fail {
+                    let a3 = self[temp_v!(3)].clone();
+                    self.unify(a3, Addr::Con(Constant::Number(rc_integer!(0))));
+                }
+            }
+        };
+
+        Ok(())
+    }
+
+    fn duplicate_term(&mut self) {
+        let old_h = self.heap.h;
+
+        let a1 = self[temp_v!(1)].clone();
+        let a2 = self[temp_v!(2)].clone();
+
+        // drop the mutable references contained in gadget
+        // once the term has been duplicated.
+        {
+            let mut gadget = DuplicateTerm::new(self);
+            gadget.duplicate_term(a1);
+        }
+
+        self.unify(Addr::HeapCell(old_h), a2);
+    }
+
+    pub(super) fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
+    {
+        match instr {
+            &ControlInstruction::Allocate(num_cells) => {
+                let gi = self.next_global_index();
+
+                self.p += 1;
+
+                if self.e + 1 < self.and_stack.len() {
+                    let and_gi = self.and_stack[self.e].global_index;
+                    let or_gi = self.or_stack.top()
+                        .map(|or_fr| or_fr.global_index)
+                        .unwrap_or(0);
+
+                    if and_gi > or_gi {
+                        let index = self.e + 1;
+
+                        self.and_stack[index].e  = self.e;
+                        self.and_stack[index].cp = self.cp;
+                        self.and_stack[index].global_index = gi;
+
+                        self.and_stack.resize(index, num_cells);
+
+                        self.e = index;
+
+                        return;
+                    }
+                }
+
+                self.and_stack.push(gi, self.e, self.cp, num_cells);
+                self.e = self.and_stack.len() - 1;
+            },
+            &ControlInstruction::ArgCall => {
+                self.cp = self.p + 1;
+                self.num_of_args = 3;
+                self.b0 = self.b;
+                self.p  = CodePtr::DirEntry(150);
+            },
+            &ControlInstruction::ArgExecute => {
+                self.num_of_args = 3;
+                self.b0 = self.b;
+                self.p  = CodePtr::DirEntry(150);
+            },
+            &ControlInstruction::Call(ref name, arity, _) =>
+                self.try_call_predicate(code_dir, name.clone(), arity),
+            &ControlInstruction::CatchCall => {
+                self.cp = self.p + 1;
+                self.num_of_args = 3;
+                self.b0 = self.b;
+                self.p  = CodePtr::DirEntry(5);
+            },
+            &ControlInstruction::CatchExecute => {
+                self.num_of_args = 3;
+                self.b0 = self.b;
+                self.p  = CodePtr::DirEntry(5);
+            },
+            &ControlInstruction::CallN(arity) =>
+                if let Some((name, arity)) = self.setup_call_n(arity) {
+                    self.try_call_predicate(code_dir, name, arity);
+                },
+            &ControlInstruction::CheckCpExecute => {
+                let a = self.store(self.deref(self[temp_v!(2)].clone()));
+
+                match a {
+                    Addr::Con(Constant::Usize(old_b)) if self.b > old_b + 1 => {
+                        self.p = self.cp;
+                    },
+                    _ => {
+                        self.num_of_args = 2;
+                        self.b0 = self.b;
+                        self.p = CodePtr::DirEntry(361); // goto sgc_on_success/2, 361.
+                    }
+                };
+            },
+            &ControlInstruction::Deallocate => {
+                let e = self.e;
+
+                self.cp = self.and_stack[e].cp;
+                self.e  = self.and_stack[e].e;
+
+                self.p += 1;
+            },
+            &ControlInstruction::DisplayCall => {
+                let output = self.print_term(self[temp_v!(1)].clone(),
+                                             DisplayFormatter {},
+                                             PrinterOutputter::new());
+
+                println!("{}", output.result());
+
+                self.p += 1;
+            },
+            &ControlInstruction::DisplayExecute => {
+                let output = self.print_term(self[temp_v!(1)].clone(),
+                                             DisplayFormatter {},
+                                             PrinterOutputter::new());
+
+                println!("{}", output.result());
+
+                self.p = self.cp;
+            },
+            &ControlInstruction::DuplicateTermCall => {
+                self.duplicate_term();
+                self.p += 1;
+            },
+            &ControlInstruction::DuplicateTermExecute => {
+                self.duplicate_term();
+                self.p = self.cp;
+            },
+            &ControlInstruction::Execute(ref name, arity) =>
+                self.try_execute_predicate(code_dir, name.clone(), arity),
+            &ControlInstruction::ExecuteN(arity) =>
+                if let Some((name, arity)) = self.setup_call_n(arity) {
+                    self.try_execute_predicate(code_dir, name, arity);
+                },
+            &ControlInstruction::FunctorCall =>
+                try_or_fail!(self, {
+                    let val = self.try_functor();
+                    self.p += 1;
+                    val
+                }),
+            &ControlInstruction::FunctorExecute =>
+                try_or_fail!(self, {
+                    let val = self.try_functor();
+                    self.p = self.cp;
+                    val
+                }),
+            &ControlInstruction::GetCleanerCall => {                
+                let dest = self[temp_v!(1)].clone();
+                
+                if let Some((cleaner_addr, b_cutoff, prev_block)) = self.sgc_cps.pop() {
+                    self.p += 1;
+                    
+                    if self.b <= b_cutoff + 1 {
+                        self.block = prev_block;
+                        
+                        if let Some(r) = dest.as_var() {
+                            self.bind(r, cleaner_addr);
+                            return;
+                        }
+                    } else {
+                        self.sgc_cps.push((cleaner_addr, b_cutoff, prev_block));                        
+                    }
+                }
+
+                self.fail = true;
+            },
+            &ControlInstruction::GotoCall(p, arity) => {
+                self.cp = self.p + 1;
+                self.num_of_args = arity;
+                self.b0 = self.b;
+                self.p  = CodePtr::DirEntry(p);
+            },
+            &ControlInstruction::GotoExecute(p, arity) => {
+                self.num_of_args = arity;
+                self.b0 = self.b;
+                self.p  = CodePtr::DirEntry(p);
+            },
+            &ControlInstruction::IsCall(r, ref at) => {
+                let a1 = self[r].clone();
+                let a2 = try_or_fail!(self, self.get_number(at));
+
+                self.unify(a1, Addr::Con(Constant::Number(a2)));
+                self.p += 1;
+            },
+            &ControlInstruction::IsExecute(r, ref at) => {
+                let a1 = self[r].clone();
+                let a2 = try_or_fail!(self, self.get_number(at));
+
+                self.unify(a1, Addr::Con(Constant::Number(a2)));
+                self.p = self.cp;
+            },
+            &ControlInstruction::JmpByCall(arity, offset) => {
+                self.cp = self.p + 1;
+                self.num_of_args = arity;
+                self.b0 = self.b;
+                self.p += offset;
+            },
+            &ControlInstruction::JmpByExecute(arity, offset) => {
+                self.num_of_args = arity;
+                self.b0 = self.b;
+                self.p += offset;
+            },
+            &ControlInstruction::Proceed =>
+                self.p = self.cp,
+            &ControlInstruction::ThrowCall => {
+                self.cp = self.p + 1;
+                self.goto_throw();
+            },
+            &ControlInstruction::ThrowExecute => {
+                self.goto_throw();
+            },
+        };
+    }
+
+    pub(super) fn execute_indexed_choice_instr(&mut self, instr: &IndexedChoiceInstruction)
+    {
+        match instr {
+            &IndexedChoiceInstruction::Try(l) => {
+                let n = self.num_of_args;
+                let gi = self.next_global_index();
+
+                self.or_stack.push(gi,
+                                   self.e,
+                                   self.cp,
+                                   self.b,
+                                   self.p + 1,
+                                   self.tr,
+                                   self.heap.h,
+                                   self.b0,
+                                   self.num_of_args);
+
+                self.b = self.or_stack.len();
+                let b = self.b - 1;
+
+                for i in 1 .. n + 1 {
+                    self.or_stack[b][i] = self.registers[i].clone();
+                }
+
+                self.hb = self.heap.h;
+                self.p += l;
+            },
+            &IndexedChoiceInstruction::Retry(l) => {
+                let b = self.b - 1;
+                let n = self.or_stack[b].num_args();
+
+                for i in 1 .. n + 1 {
+                    self.registers[i] = self.or_stack[b][i].clone();
+                }
+
+                self.e  = self.or_stack[b].e;
+                self.cp = self.or_stack[b].cp;
+
+                self.or_stack[b].bp = self.p + 1;
+
+                let old_tr  = self.or_stack[b].tr;
+                let curr_tr = self.tr;
+
+                self.unwind_trail(old_tr, curr_tr);
+                self.tr = self.or_stack[b].tr;
+
+                self.trail.truncate(self.tr);
+                self.heap.truncate(self.or_stack[b].h);
+
+                self.hb = self.heap.h;
+
+                self.p += l;
+            },
+            &IndexedChoiceInstruction::Trust(l) => {
+                let b = self.b - 1;
+                let n = self.or_stack[b].num_args();
+
+                for i in 1 .. n + 1 {
+                    self.registers[i] = self.or_stack[b][i].clone();
+                }
+
+                self.e  = self.or_stack[b].e;
+                self.cp = self.or_stack[b].cp;
+
+                let old_tr  = self.or_stack[b].tr;
+                let curr_tr = self.tr;
+
+                self.unwind_trail(old_tr, curr_tr);
+
+                self.tr = self.or_stack[b].tr;
+                self.trail.truncate(self.tr);
+
+                self.heap.truncate(self.or_stack[b].h);
+                self.b = self.or_stack[b].b;
+
+                self.or_stack.truncate(self.b);
+
+                self.hb = self.heap.h;
+                self.p += l;
+            },
+        };
+    }
+
+    pub(super) fn execute_choice_instr(&mut self, instr: &ChoiceInstruction)
+    {
+        match instr {
+            &ChoiceInstruction::TryMeElse(offset) => {
+                let n = self.num_of_args;
+                let gi = self.next_global_index();
+
+                self.or_stack.push(gi,
+                                   self.e,
+                                   self.cp,
+                                   self.b,
+                                   self.p + offset,
+                                   self.tr,
+                                   self.heap.h,
+                                   self.b0,
+                                   self.num_of_args);
+
+                self.b = self.or_stack.len();
+                let b  = self.b - 1;
+
+                for i in 1 .. n + 1 {
+                    self.or_stack[b][i] = self.registers[i].clone();
+                }
+
+                self.hb = self.heap.h;
+                self.p += 1;
+            },
+            &ChoiceInstruction::RetryMeElse(offset) => {
+                let b = self.b - 1;
+                let n = self.or_stack[b].num_args();
+
+                for i in 1 .. n + 1 {
+                    self.registers[i] = self.or_stack[b][i].clone();
+                }
+
+                self.e  = self.or_stack[b].e;
+                self.cp = self.or_stack[b].cp;
+
+                self.or_stack[b].bp = self.p + offset;
+
+                let old_tr  = self.or_stack[b].tr;
+                let curr_tr = self.tr;
+
+                self.unwind_trail(old_tr, curr_tr);
+                self.tr = self.or_stack[b].tr;
+
+                self.trail.truncate(self.tr);
+                self.heap.truncate(self.or_stack[b].h);
+
+                self.hb = self.heap.h;
+
+                self.p += 1;
+            },
+            &ChoiceInstruction::TrustMe => {
+                let b = self.b - 1;
+                let n = self.or_stack[b].num_args();
+
+                for i in 1 .. n + 1 {
+                    self.registers[i] = self.or_stack[b][i].clone();
+                }
+
+                self.e  = self.or_stack[b].e;
+                self.cp = self.or_stack[b].cp;
+
+                let old_tr  = self.or_stack[b].tr;
+                let curr_tr = self.tr;
+
+                self.unwind_trail(old_tr, curr_tr);
+
+                self.tr = self.or_stack[b].tr;
+                self.trail.truncate(self.tr);
+
+                self.heap.truncate(self.or_stack[b].h);
+
+                self.b = self.or_stack[b].b;
+
+                self.or_stack.truncate(self.b);
+
+                self.hb = self.heap.h;
+                self.p += 1;
+            }
+        }
+    }
+
+    pub(super) fn execute_cut_instr(&mut self, instr: &CutInstruction) {
+        match instr {
+            &CutInstruction::NeckCut => {
+                let b  = self.b;
+                let b0 = self.b0;
+
+                if b > b0 {
+                    self.b = b0;
+                    self.tidy_trail();
+                    self.or_stack.truncate(self.b);
+                }
+
+                self.p += 1;
+            },
+            &CutInstruction::GetLevel(r) => {
+                let b0 = self.b0;
+
+                self[r] = Addr::Con(Constant::Usize(b0));
+                self.p += 1;
+            },
+            &CutInstruction::Cut(r) => {
+                let b = self.b;
+
+                if let Addr::Con(Constant::Usize(b0)) = self[r].clone() {
+                    if b > b0 {
+                        self.b = b0;
+                        self.tidy_trail();
+                        self.or_stack.truncate(self.b);
+                    }
+                } else {
+                    self.fail = true;
+                    return;
+                }
+
+                self.p += 1;
+                
+                if !self.sgc_cps.is_empty() {
+                    self.cp = self.p;
+                    self.num_of_args = 0;
+                    self.b0 = self.b;
+                    self.p  = CodePtr::DirEntry(349); // goto_call run_cleaners_without_handling/0.
+                }
+            }
+        }
+    }
+
+    pub(super) fn reset(&mut self) {
+        self.hb = 0;
+        self.e = 0;
+        self.b = 0;
+        self.b0 = 0;
+        self.s = 0;
+        self.tr = 0;
+        self.p = CodePtr::default();
+        self.cp = CodePtr::default();
+        self.num_of_args = 0;
+
+        self.fail = false;
+        self.trail.clear();
+        self.heap.clear();
+        self.mode = MachineMode::Write;
+        self.and_stack.clear();
+        self.or_stack.clear();
+        self.registers = vec![Addr::HeapCell(0); 64];
+        self.block = 0;
+        self.ball = (0, Vec::new());
+    }
+}
index 11ff6cab1105c1eb594c50c851ba33fbbcb02fb2..7b9a3bc751c6bc16871153658cb744c55dd969fd 100644 (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};
index 8219e59fdc4ddb8d1cd497395a50e6da8185cd9a..2195a24563a9d8f7c7369cf628b73af565edec7c 100644 (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)
+    )       
+}
index 2579afe5fd8a843398e55fa458fbf83a819d7055..5d8398b4e68def750e23d422b9b762c07a333ea8 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 2579afe5fd8a843398e55fa458fbf83a819d7055
+Subproject commit 5d8398b4e68def750e23d422b9b762c07a333ea8
index 0c715e3e282fb6f8fc67fb446a23e1dff20638e3..bf0df94b6529f62ad64d9778bb0d3e31710e8a25 100644 (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"]]);
+}