]> Repositorios git - scryer-prolog.git/commitdiff
initial commit for merge
authorMark Thom <[email protected]>
Fri, 27 Mar 2020 04:01:23 +0000 (22:01 -0600)
committerMark Thom <[email protected]>
Fri, 27 Mar 2020 04:01:23 +0000 (22:01 -0600)
25 files changed:
Cargo.lock
Cargo.toml
src/prolog/arithmetic.rs
src/prolog/forms.rs
src/prolog/heap_iter.rs
src/prolog/heap_print.rs
src/prolog/indexing.rs
src/prolog/instructions.rs
src/prolog/machine/arithmetic_ops.rs [new file with mode: 0644]
src/prolog/machine/attributed_variables.rs
src/prolog/machine/copier.rs
src/prolog/machine/dynamic_database.rs
src/prolog/machine/heap.rs
src/prolog/machine/machine_errors.rs
src/prolog/machine/machine_indices.rs
src/prolog/machine/machine_state.rs
src/prolog/machine/machine_state_impl.rs
src/prolog/machine/mod.rs
src/prolog/machine/partial_string.rs
src/prolog/machine/streams.rs
src/prolog/machine/system_calls.rs
src/prolog/macros.rs
src/prolog/read.rs
src/prolog/toplevel.pl
src/prolog/write.rs

index 61c28d82f916c71300faa3963d643bfc8181d5d8..0fe6e3f4cfa52e4504fe399eea7c67c56e000762 100644 (file)
@@ -444,8 +444,7 @@ dependencies = [
 
 [[package]]
 name = "prolog_parser"
-version = "0.8.47"
-source = "registry+https://github.com/rust-lang/crates.io-index"
+version = "0.8.48"
 dependencies = [
  "lexical 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-rug-adapter 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -580,7 +579,7 @@ dependencies = [
  "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-rug-adapter 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "prolog_parser 0.8.47 (registry+https://github.com/rust-lang/crates.io-index)",
+ "prolog_parser 0.8.48",
  "ref_thread_local 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rug 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustyline 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -791,7 +790,6 @@ dependencies = [
 "checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc"
 "checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1"
 "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
-"checksum prolog_parser 0.8.47 (registry+https://github.com/rust-lang/crates.io-index)" = "2cf575fc8b91b2d9a9487f13655cffb6f697a5294b74de9b24b6a5fbe987e229"
 "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
 "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
 "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
index 50f1882b74e3ea46733461b8a41e5a59864ea14e..7c3338f3bcd7cd4c2f3617ed5feae526832ffcc6 100644 (file)
@@ -25,7 +25,7 @@ libc = "0.2.62"
 nix = "0.15.0"
 num-rug-adapter = { optional = true, version = "0.1.1" }
 ordered-float = "0.5.0"
-prolog_parser = { version = "0.8.47", default-features = false }
+prolog_parser = { version = "0.8.48", path = "../prolog_parser", default-features = false }
 ref_thread_local = "0.0.0"
 rug = { version = "1.4.0", optional = true }
 rustyline = "6.0.0"
index 4c126d774d530b317a0e24e75015b9595f48ad0a..3520cb1baa0e153043340a0585632f0dd088d0c7 100644 (file)
@@ -399,23 +399,27 @@ impl Add<Number> for Number {
 
     fn add(self, rhs: Number) -> Self::Output {
         match (self, rhs) {
-            (Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Integer(n1 + n2)), // add_i
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                Ok(Number::Integer(Rc::new(Integer::from(&*n1) + &*n2))) // add_i
+            }
             (Number::Integer(n1), Number::Float(OrderedFloat(n2)))
-            | (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
+          | (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
                 Ok(Number::Float(add_f(float_i_to_f(&n1)?, n2)?))
             }
             (Number::Integer(n1), Number::Rational(n2))
-            | (Number::Rational(n2), Number::Integer(n1)) => {
-                Ok(Number::Rational(Rational::from(n1) + n2))
+          | (Number::Rational(n2), Number::Integer(n1)) => {
+                Ok(Number::Rational(Rc::new(Rational::from(&*n1) + &*n2)))
             }
             (Number::Rational(n1), Number::Float(OrderedFloat(n2)))
-            | (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
+          | (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
                 Ok(Number::Float(add_f(float_r_to_f(&n1)?, n2)?))
             }
             (Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
                 Ok(Number::Float(add_f(f1, f2)?))
             }
-            (Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Rational(r1 + r2)),
+            (Number::Rational(r1), Number::Rational(r2)) => {
+                Ok(Number::Rational(Rc::new(Rational::from(&*r1) + &*r2)))
+            }
         }
     }
 }
@@ -425,9 +429,9 @@ impl Neg for Number {
 
     fn neg(self) -> Self::Output {
         match self {
-            Number::Integer(n) => Number::Integer(-n),
+            Number::Integer(n) => Number::Integer(Rc::new(-Integer::from(&*n))),
             Number::Float(OrderedFloat(f)) => Number::Float(OrderedFloat(-f)),
-            Number::Rational(r) => Number::Rational(-r),
+            Number::Rational(r) => Number::Rational(Rc::new(-Rational::from(&*r))),
         }
     }
 }
@@ -445,14 +449,16 @@ impl Mul<Number> for Number {
 
     fn mul(self, rhs: Number) -> Self::Output {
         match (self, rhs) {
-            (Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Integer(n1 * n2)), // mul_i
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                Ok(Number::Integer(Rc::new(Integer::from(&*n1) * &*n2))) // mul_i
+            }
             (Number::Integer(n1), Number::Float(OrderedFloat(n2)))
             | (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
                 Ok(Number::Float(mul_f(float_i_to_f(&n1)?, n2)?))
             }
             (Number::Integer(n1), Number::Rational(n2))
             | (Number::Rational(n2), Number::Integer(n1)) => {
-                Ok(Number::Rational(Rational::from(n1) * n2))
+                Ok(Number::Rational(Rc::new(Rational::from(&*n1) * &*n2)))
             }
             (Number::Rational(n1), Number::Float(OrderedFloat(n2)))
             | (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
@@ -461,7 +467,9 @@ impl Mul<Number> for Number {
             (Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
                 Ok(Number::Float(mul_f(f1, f2)?))
             }
-            (Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Rational(r1 * r2)),
+            (Number::Rational(r1), Number::Rational(r2)) => {
+                Ok(Number::Rational(Rc::new(Rational::from(&*r1) * &*r2)))
+            }
         }
     }
 }
@@ -539,8 +547,8 @@ impl Ord for Number {
 }
 
 // Computes n ^ power. Ignores the sign of power.
-pub fn binary_pow(mut n: Integer, power: Integer) -> Integer {
-    let mut power = power.abs();
+pub fn binary_pow(mut n: Integer, power: &Integer) -> Integer {
+    let mut power = Integer::from(power.abs_ref());
 
     if power == 0 {
         return Integer::from(1);
index 9c402bd2a8683042cbb37126a17e88dd3d6f2518..4f56922712730f1bdf3bef3a69fe6c02c363c3ca 100644 (file)
@@ -576,8 +576,8 @@ pub struct Module {
 #[derive(Clone, PartialEq, Eq)]
 pub enum Number {
     Float(OrderedFloat<f64>),
-    Integer(Integer),
-    Rational(Rational),
+    Integer(Rc<Integer>),
+    Rational(Rc<Rational>),
 }
 
 impl Default for Number {
@@ -586,48 +586,51 @@ impl Default for Number {
     }
 }
 
-impl Number {
-    pub fn to_constant(self) -> Constant {
+impl Into<HeapCellValue> for Number {
+    #[inline]
+    fn into(self) -> HeapCellValue {
         match self {
-            Number::Integer(n) => Constant::Integer(n),
-            Number::Float(f) => Constant::Float(f),
-            Number::Rational(r) => Constant::Rational(r),
+            Number::Integer(n) => HeapCellValue::Integer(n),
+            Number::Float(f) => HeapCellValue::Addr(Addr::Float(f)),
+            Number::Rational(r) => HeapCellValue::Rational(r),
         }
     }
+}
 
+impl Number {    
     #[inline]
     pub fn is_positive(&self) -> bool {
         match self {
-            &Number::Integer(ref n) => n > &0,
+            &Number::Integer(ref n) => &**n > &0,
             &Number::Float(OrderedFloat(f)) => f.is_sign_positive(),
-            &Number::Rational(ref r) => r > &0,
+            &Number::Rational(ref r) => &**r > &0,
         }
     }
 
     #[inline]
     pub fn is_negative(&self) -> bool {
         match self {
-            &Number::Integer(ref n) => n < &0,
+            &Number::Integer(ref n) => &**n < &0,
             &Number::Float(OrderedFloat(f)) => f.is_sign_negative(),
-            &Number::Rational(ref r) => r < &0,
+            &Number::Rational(ref r) => &**r < &0,
         }
     }
 
     #[inline]
     pub fn is_zero(&self) -> bool {
         match self {
-            &Number::Integer(ref n) => n == &0,
+            &Number::Integer(ref n) => &**n == &0,
             &Number::Float(f) => f == OrderedFloat(0f64),
-            &Number::Rational(ref r) => r == &0,
+            &Number::Rational(ref r) => &**r == &0,
         }
     }
 
     #[inline]
     pub fn abs(self) -> Self {
         match self {
-            Number::Integer(n) => Number::Integer(n.abs()),
+            Number::Integer(n) => Number::Integer(Rc::new(Integer::from(n.abs_ref()))),
             Number::Float(f) => Number::Float(OrderedFloat(f.abs())),
-            Number::Rational(r) => Number::Rational(r.abs()),
+            Number::Rational(r) => Number::Rational(Rc::new(Rational::from(r.abs_ref()))),
         }
     }
 }
index 0c3630e673f742705130e8cf4673be9078d613eb..57434fd93458f1c01036cd7f1062d93f82cd31ac 100644 (file)
@@ -1,5 +1,3 @@
-use prolog_parser::ast::*;
-
 use crate::prolog::machine::machine_indices::*;
 use crate::prolog::machine::machine_state::*;
 
@@ -22,25 +20,33 @@ impl<'a> HCPreOrderIterator<'a> {
         }
     }
 
+    #[inline]
     pub fn machine_st(&self) -> &MachineState {
         &self.machine_st
     }
 
     fn follow_heap(&mut self, h: usize) -> Addr {
         match &self.machine_st.heap[h] {
-            HeapCellValue::NamedStr(arity, _, _) => {
-                for idx in (1..arity + 1).rev() {
+            &HeapCellValue::NamedStr(arity, _, _) => {
+                for idx in (1 .. arity + 1).rev() {
                     self.state_stack.push(Addr::HeapCell(h + idx));
                 }
 
                 Addr::Str(h)
             }
-            HeapCellValue::Addr(ref a) => {
-                self.follow(a.clone())
+            &HeapCellValue::Addr(a) => {
+                self.follow(a)
             }
             HeapCellValue::PartialString(_) => {
                 self.follow(Addr::PStrLocation(h, 0))
             }
+            HeapCellValue::Atom(..) | HeapCellValue::DBRef(_)
+          | HeapCellValue::Integer(_) | HeapCellValue::Rational(_) => {
+                Addr::Con(h)
+            }
+            HeapCellValue::Stream(_) => {
+                Addr::Stream(h)
+            }
         }
     }
 
@@ -51,30 +57,6 @@ impl<'a> HCPreOrderIterator<'a> {
         let da = self.machine_st.store(self.machine_st.deref(addr));
 
         match da {
-            Addr::Con(Constant::String(n, s)) => {
-                if !self.machine_st.machine_flags().double_quotes.is_atom() {
-                    if s.len() > n {
-                        if let Some(c) = s[n ..].chars().next() {
-                            let o = c.len_utf8();
-
-                            self.state_stack.push(Addr::Con(Constant::String(n+o, s.clone())));
-
-                            if self.machine_st.machine_flags().double_quotes.is_codes() {
-                                self.state_stack.push(Addr::Con(Constant::CharCode(c as u32)));
-                            } else {
-                                self.state_stack.push(Addr::Con(Constant::Char(c)));
-                            }
-                        }
-                    } else {
-                        return Addr::Con(Constant::EmptyList);
-                    }
-                }
-
-                Addr::Con(Constant::String(n, s))
-            }
-            Addr::Con(_) | Addr::DBRef(_) | Addr::Stream(_) => {
-                da
-            }
             Addr::Lis(a) => {
                 self.state_stack.push(Addr::HeapCell(a + 1));
                 self.state_stack.push(Addr::HeapCell(a));
@@ -83,16 +65,14 @@ impl<'a> HCPreOrderIterator<'a> {
             }
             Addr::PStrLocation(h, n) => {
                 if let HeapCellValue::PartialString(ref pstr) = &self.machine_st.heap[h] {
-                    let s = pstr.block_as_str();
-                    
-                    if let Some(c) = s[n ..].chars().next() {
-                        if pstr.len() > n + c.len_utf8() {                        
+                    if let Some(c) = pstr.range_from(n ..).next() {
+                        if !pstr.at_end(n + c.len_utf8()) {
                             self.state_stack.push(Addr::PStrLocation(h, n + c.len_utf8()));
                         } else {
                             self.state_stack.push(Addr::HeapCell(h + 1));
                         }
 
-                        self.state_stack.push(Addr::Con(Constant::Char(c)));
+                        self.state_stack.push(Addr::Char(c));
                     } else {
                         unreachable!()
                     }
@@ -102,66 +82,52 @@ impl<'a> HCPreOrderIterator<'a> {
 
                 Addr::PStrLocation(h, n)
             }
-            Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => {
-                da
-            }
             Addr::Str(s) => {
                 self.follow_heap(s) // record terms of structure.
             }
+            Addr::Con(h) => {
+                if let HeapCellValue::PartialString(_) = &self.machine_st.heap[h] {
+                    self.state_stack.push(Addr::HeapCell(h + 1));
+                }
+
+                Addr::Con(h)
+            }
+            da => {
+                da
+            }
         }
     }
 }
 
 impl<'a> Iterator for HCPreOrderIterator<'a> {
-    type Item = HeapCellValue;
+    type Item = Addr;
 
     fn next(&mut self) -> Option<Self::Item> {
-        self.state_stack.pop().map(|a| match self.follow(a) {
-            Addr::HeapCell(h) => {
-                HeapCellValue::Addr(self.machine_st.heap[h].as_addr(h))
-            }
-            Addr::Str(s) => {
-                match &self.machine_st.heap[s] {
-                    val @ HeapCellValue::NamedStr(..) => {
-                        val.clone()
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                }
-            }
-            Addr::StackCell(fr, sc) => {
-                HeapCellValue::Addr(self.machine_st.stack.index_and_frame(fr)[sc].clone())
-            }
-            da => {
-                HeapCellValue::Addr(da)
-            }
-        })
+        self.state_stack.pop().map(|a| self.follow(a))
     }
 }
 
 pub trait MutStackHCIterator
-where
-    Self: Iterator<Item = HeapCellValue>,
+where Self: Iterator<Item = Addr>
 {
     fn stack(&mut self) -> &mut Vec<Addr>;
 }
 
-pub struct HCPostOrderIterator<HCIter> {
-    base_iter: HCIter,
-    parent_stack: Vec<(usize, HeapCellValue)>, // number of children, parent node.
+pub struct HCPostOrderIterator<'a> {
+    base_iter: HCPreOrderIterator<'a>,
+    parent_stack: Vec<(usize, Addr)>, // number of children, parent node.
 }
 
-impl<HCIter> Deref for HCPostOrderIterator<HCIter> {
-    type Target = HCIter;
+impl<'a> Deref for HCPostOrderIterator<'a> {
+    type Target = HCPreOrderIterator<'a>;
 
     fn deref(&self) -> &Self::Target {
         &self.base_iter
     }
 }
 
-impl<HCIter: Iterator<Item = HeapCellValue>> HCPostOrderIterator<HCIter> {
-    pub fn new(base_iter: HCIter) -> Self {
+impl<'a> HCPostOrderIterator<'a> {
+    pub fn new(base_iter: HCPreOrderIterator<'a>) -> Self {
         HCPostOrderIterator {
             base_iter,
             parent_stack: vec![],
@@ -169,8 +135,8 @@ impl<HCIter: Iterator<Item = HeapCellValue>> HCPostOrderIterator<HCIter> {
     }
 }
 
-impl<HCIter: Iterator<Item = HeapCellValue>> Iterator for HCPostOrderIterator<HCIter> {
-    type Item = HeapCellValue;
+impl<'a> Iterator for HCPostOrderIterator<'a> {
+    type Item = Addr;
 
     fn next(&mut self) -> Option<Self::Item> {
         loop {
@@ -183,15 +149,23 @@ impl<HCIter: Iterator<Item = HeapCellValue>> Iterator for HCPostOrderIterator<HC
             }
 
             if let Some(item) = self.base_iter.next() {
-                match item {
-                    HeapCellValue::NamedStr(arity, name, fix) => self
-                        .parent_stack
-                        .push((arity, HeapCellValue::NamedStr(arity, name, fix))),
-                    HeapCellValue::Addr(Addr::Lis(a)) => self
-                        .parent_stack
-                        .push((2, HeapCellValue::Addr(Addr::Lis(a)))),
-                    child_node => {
-                        return Some(child_node);
+                match self.base_iter.machine_st.heap.index_addr(&item).as_ref() {
+                    &HeapCellValue::NamedStr(arity, ..) => {
+                        self.parent_stack.push((arity, item));
+                    }
+                    &HeapCellValue::Addr(Addr::Lis(a)) => {
+                        self.parent_stack.push((2, Addr::Lis(a)));
+                    }
+                    &HeapCellValue::Addr(Addr::PStrLocation(h, n)) => {
+                        if let HeapCellValue::PartialString(ref pstr) = &self.machine_st.heap[h] {
+                            let c = pstr.range_from(n ..).next().unwrap();
+                            self.parent_stack.push((2, Addr::PStrLocation(h, n + c.len_utf8())));
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        return Some(item);
                     }
                 }
             } else {
@@ -201,21 +175,16 @@ impl<HCIter: Iterator<Item = HeapCellValue>> Iterator for HCPostOrderIterator<HC
     }
 }
 
-pub type HCProperPostOrderIterator<'a> = HCPostOrderIterator<HCPreOrderIterator<'a>>;
-
 impl MachineState {
     pub fn pre_order_iter<'a>(&'a self, a: Addr) -> HCPreOrderIterator<'a> {
         HCPreOrderIterator::new(self, a)
     }
 
-    pub fn post_order_iter<'a>(&'a self, a: Addr) -> HCProperPostOrderIterator<'a> {
+    pub fn post_order_iter<'a>(&'a self, a: Addr) -> HCPostOrderIterator<'a> {
         HCPostOrderIterator::new(HCPreOrderIterator::new(self, a))
     }
 
-    pub fn acyclic_pre_order_iter<'a>(
-        &'a self,
-        a: Addr,
-    ) -> HCAcyclicIterator<HCPreOrderIterator<'a>> {
+    pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr,) -> HCAcyclicIterator<'a> {
         HCAcyclicIterator::new(HCPreOrderIterator::new(self, a))
     }
 
@@ -223,7 +192,7 @@ impl MachineState {
         &'a self,
         a1: Addr,
         a2: Addr,
-    ) -> HCZippedAcyclicIterator<HCPreOrderIterator<'a>> {
+    ) -> HCZippedAcyclicIterator<'a> {
         HCZippedAcyclicIterator::new(
             HCPreOrderIterator::new(self, a1),
             HCPreOrderIterator::new(self, a2),
@@ -237,13 +206,13 @@ impl<'a> MutStackHCIterator for HCPreOrderIterator<'a> {
     }
 }
 
-pub struct HCAcyclicIterator<HCIter> {
-    iter: HCIter,
+pub struct HCAcyclicIterator<'a> {
+    iter: HCPreOrderIterator<'a>,
     seen: IndexSet<Addr>,
 }
 
-impl<HCIter: MutStackHCIterator> HCAcyclicIterator<HCIter> {
-    pub fn new(iter: HCIter) -> Self {
+impl<'a> HCAcyclicIterator<'a> {
+    pub fn new(iter: HCPreOrderIterator<'a>) -> Self {
         HCAcyclicIterator {
             iter,
             seen: IndexSet::new(),
@@ -251,19 +220,17 @@ impl<HCIter: MutStackHCIterator> HCAcyclicIterator<HCIter> {
     }
 }
 
-impl<HCIter> Deref for HCAcyclicIterator<HCIter> {
-    type Target = HCIter;
+impl<'a> Deref for HCAcyclicIterator<'a> {
+    type Target = HCPreOrderIterator<'a>;
 
     fn deref(&self) -> &Self::Target {
         &self.iter
     }
 }
 
-impl<HCIter> Iterator for HCAcyclicIterator<HCIter>
-where
-    HCIter: Iterator<Item = HeapCellValue> + MutStackHCIterator,
+impl<'a> Iterator for HCAcyclicIterator<'a>
 {
-    type Item = HeapCellValue;
+    type Item = Addr;
 
     fn next(&mut self) -> Option<Self::Item> {
         while let Some(addr) = self.iter.stack().pop() {
@@ -279,15 +246,15 @@ where
     }
 }
 
-pub struct HCZippedAcyclicIterator<HCIter> {
-    i1: HCIter,
-    i2: HCIter,
+pub struct HCZippedAcyclicIterator<'a> {
+    i1: HCPreOrderIterator<'a>,
+    i2: HCPreOrderIterator<'a>,
     seen: IndexSet<(Addr, Addr)>,
     pub first_to_expire: Ordering,
 }
 
-impl<HCIter: MutStackHCIterator> HCZippedAcyclicIterator<HCIter> {
-    pub fn new(i1: HCIter, i2: HCIter) -> Self {
+impl<'a> HCZippedAcyclicIterator<'a> {
+    pub fn new(i1: HCPreOrderIterator<'a>, i2: HCPreOrderIterator<'a>) -> Self {
         HCZippedAcyclicIterator {
             i1,
             i2,
@@ -297,11 +264,9 @@ impl<HCIter: MutStackHCIterator> HCZippedAcyclicIterator<HCIter> {
     }
 }
 
-impl<HCIter> Iterator for HCZippedAcyclicIterator<HCIter>
-where
-    HCIter: Iterator<Item = HeapCellValue> + MutStackHCIterator,
+impl<'a> Iterator for HCZippedAcyclicIterator<'a>
 {
-    type Item = (HeapCellValue, HeapCellValue);
+    type Item = (Addr, Addr);
 
     fn next(&mut self) -> Option<Self::Item> {
         while let (Some(a1), Some(a2)) = (self.i1.stack().pop(), self.i2.stack().pop()) {
@@ -324,7 +289,9 @@ where
                 self.first_to_expire = Ordering::Less;
                 None
             }
-            _ => None,
+            _ => {
+                None
+            }
         }
     }
 }
index eeb53794bdec2de8c26f2cc2b03ebd03c537c1b6..0ccc155b732be67afa4195948d1fee9a4c815279 100644 (file)
@@ -3,6 +3,7 @@ use prolog_parser::ast::*;
 use crate::prolog::clause_types::*;
 use crate::prolog::forms::*;
 use crate::prolog::heap_iter::*;
+use crate::prolog::machine::heap::*;
 use crate::prolog::machine::machine_indices::*;
 use crate::prolog::machine::machine_state::*;
 use crate::prolog::ordered_float::OrderedFloat;
@@ -88,14 +89,17 @@ impl<'a> HCPreOrderIterator<'a> {
      */
     fn leftmost_leaf_has_property<P>(&self, property_check: P) -> bool
     where
-        P: Fn(Constant) -> bool,
+        P: Fn(Addr, &Heap) -> bool,
     {
         let mut addr = match self.state_stack.last().cloned() {
             Some(addr) => addr,
             None => return false,
         };
 
-        let mut parent_spec = DirectedOp::Left(clause_name!("-"), SharedOpDesc::new(200, FY));
+        let mut parent_spec = DirectedOp::Left(
+            clause_name!("-"),
+            SharedOpDesc::new(200, FY),
+        );
 
         loop {
             match self.machine_st.store(self.machine_st.deref(addr)) {
@@ -110,29 +114,28 @@ impl<'a> HCPreOrderIterator<'a> {
                             parent_spec = DirectedOp::Right(name.clone(), spec.clone());
                         }
                     }
-                    _ => return false,
+                    _ => {
+                        return false;
+                    }
                 },
-                Addr::Con(Constant::Integer(n)) => return property_check(Constant::Integer(n)),
-                Addr::Con(Constant::Float(n)) => return property_check(Constant::Float(n)),
-                Addr::Con(Constant::Rational(n)) => return property_check(Constant::Rational(n)),
-                _ => return false,
+                addr => {
+                    return property_check(addr, &self.machine_st.heap);
+                }
             }
         }
     }
 
     fn immediate_leaf_has_property<P>(&self, property_check: P) -> bool
     where
-        P: Fn(Constant) -> bool,
+        P: Fn(Addr, &Heap) -> bool,
     {
         let addr = match self.state_stack.last().cloned() {
             Some(addr) => addr,
             None => return false,
         };
 
-        match self.machine_st.store(self.machine_st.deref(addr)) {
-            Addr::Con(c) => property_check(c),
-            _ => false,
-        }
+        let addr = self.machine_st.store(self.machine_st.deref(addr));
+        property_check(addr, &self.machine_st.heap)
     }
 }
 
@@ -261,13 +264,29 @@ fn is_numbered_var(ct: &ClauseType, arity: usize) -> bool {
 #[inline]
 fn negated_op_needs_bracketing(iter: &HCPreOrderIterator, op: &Option<DirectedOp>) -> bool {
     if let &Some(ref op) = op {
-        op.is_negative_sign()
-            && iter.leftmost_leaf_has_property(|c| match c {
-                Constant::Integer(n) => n > 0,
-                Constant::Float(f) => f > OrderedFloat(0f64),
-                Constant::Rational(r) => r > 0,
-                _ => false,
-            })
+        op.is_negative_sign() && iter.leftmost_leaf_has_property(|addr, heap| {
+            match addr {
+                Addr::Con(h) => {
+                    match &heap[h] {
+                        HeapCellValue::Integer(ref n) => {
+                            &**n > &0
+                        }
+                        &HeapCellValue::Rational(ref r) => {
+                            &**r > &0
+                        }
+                        _ => {
+                            false
+                        }
+                    }
+                }
+                Addr::Float(f) => {
+                    f > OrderedFloat(0f64)
+                }
+                _ => {
+                    false
+                }
+            }
+        })
     } else {
         false
     }
@@ -293,8 +312,16 @@ fn numbervar(n: Integer) -> Var {
 impl MachineState {
     pub fn numbervar(&self, offset: &Integer, addr: Addr) -> Option<Var> {
         match self.store(self.deref(addr)) {
-            Addr::Con(Constant::Integer(ref n)) if n >= &0 => {
-                Some(numbervar(Integer::from(offset + n)))
+            Addr::Con(h) => {
+                if let &HeapCellValue::Integer(ref n) = &self.heap[h] {
+                    if &**n >= &0 {
+                        Some(numbervar(Integer::from(offset + &**n)))
+                    } else {
+                        None
+                    }
+                } else {
+                    None
+                }
             }
             _ => {
                 None
@@ -823,7 +850,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
     fn check_for_seen(
         &mut self,
         iter: &mut HCPreOrderIterator,
-    ) -> Option<HeapCellValue> {
+    ) -> Option<Addr> {
         iter.stack().last().cloned().and_then(|addr| {
             let addr = self.machine_st.store(self.machine_st.deref(addr));
 
@@ -848,7 +875,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                 }
                 None => {
                     let offset = match functor_location(&addr) {
-                        Some(offset) => offset,
+                        Some(offset) => {
+                            offset
+                        }
                         None => {
                             return iter.next();
                         }
@@ -969,7 +998,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
     }
 
     fn print_char(&mut self, is_quoted: bool, c: char)
-    {        
+    {
         if non_quoted_token(once(c)) {
             let c = char_to_string(false, c);
 
@@ -993,178 +1022,130 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         }
     }
 
-    fn print_constant(
+    fn print_string_as_str(
         &mut self,
-        iter: &mut HCPreOrderIterator,
-        max_depth: usize,
-        c: Constant,
-        op: &Option<DirectedOp>,
+        mut h: usize,
+        mut offset: usize,
+        quoted: bool,
     ) {
-        match c {
-            Constant::Atom(atom, spec) => {
-                if let Some(_) = fetch_atom_op_spec(atom.clone(), spec, self.op_dir) {
-                    let mut result = String::new();
-
-                    if let Some(ref op) = op {
-                        if self.outputter.ends_with(&format!(" {}", op.as_str())) {
-                            result.push(' ');
-                        }
-
-                        result.push('(');
-                    }
-
-                    result += &self.print_op_addendum(atom.as_str());
-
-                    if op.is_some() {
-                        result.push(')');
-                    }
+        self.push_char('"');
 
-                    push_space_if_amb!(self, &result, {
-                        self.append_str(&result);
-                    });
-                } else {
-                    push_space_if_amb!(self, atom.as_str(), {
-                        self.print_atom(&atom);
-                    });
-                }
-            }
-            Constant::CharCode(c) => {
-                self.append_str(&format!("{}", c as u32));
-            }
-            Constant::Char(c) => {
-                self.print_char(self.quoted, c);
-            }
-            Constant::CutPoint(b) => {
-                self.append_str(&format!("{}", b));
-            }
-            Constant::EmptyList => {
-                self.append_str("[]");
-            }
-            Constant::Integer(n) => {
-                self.print_number(Number::Integer(n), op);
-            }
-            Constant::Float(n) => {
-                self.print_number(Number::Float(n), op);
-            }
-            Constant::Rational(n) => {
-                self.print_number(Number::Rational(n), op);
-            }
-            Constant::String(n, s) if self.print_strings_as_strs => {
-                self.print_string_as_str(iter, n, s);
-            }
-            Constant::String(n, s) => {
-                self.print_string(iter, max_depth, n, s);
-            }
-            Constant::Usize(i) => {
-                self.append_str(&format!("u{}", i));
-            }
+        while let HeapCellValue::PartialString(ref pstr) = &self.machine_st.heap[h] {
+            let atom = String::from_iter(pstr.range_from(offset ..).map(|c| {
+                char_to_string(quoted, c)
+            }));
+                                         
+            self.append_str(&atom);
+            
+            h += 2;
+            offset = 0;
         }
-    }
-
-    fn print_string_as_str(
-        &mut self,
-        iter: &mut HCPreOrderIterator,
-        offset: usize,
-        s: Rc<String>,
-    ) {
-         let atom = String::from_iter(s[offset ..].chars().map(|c| {
-            char_to_string(true, c)
-        }));
 
         self.push_char('"');
-        self.append_str(&atom);
-        self.push_char('"');
-
-        // eliminate lingering elements on the iterator stack (the
-        // head and tail) which are there to treat the string as a
-        // list.
-        iter.stack().pop();
-        iter.stack().pop();
     }
 
     fn print_string(
         &mut self,
-        iter: &mut HCPreOrderIterator,
         mut max_depth: usize,
-        offset: usize,
-        s: Rc<String>)
+        mut h: usize,
+        mut offset: usize,
+    )
     {
         if !self.machine_st.machine_flags().double_quotes.is_atom() {
             if self.check_max_depth(&mut max_depth) {
                 self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
                 return;
             }
+            
+            while let HeapCellValue::PartialString(ref pstr) = &self.machine_st.heap[h] {                
+                if pstr.at_end(offset) && !self.at_cdr("") {
+                    if let HeapCellValue::Addr(Addr::EmptyList) = &self.machine_st.heap[h+1] {
+                        self.append_str("[]");
+                        break;
+                    } else {
+                        h += 2;
+                        offset = 0;
+                    }
+                } else if self.ignore_ops {
+                    let iter: Box<dyn Iterator<Item=char>> =
+                        if self.max_depth == 0 {
+                            Box::new(pstr.range_from(offset ..))
+                        } else {
+                            Box::new(pstr.range_from(offset ..).take(max_depth))
+                        };
 
-            if s.len() <= offset && !self.at_cdr("") {
-                self.append_str("[]");
-            } else if self.ignore_ops {
-                let mut char_count = 0;
-                let mut byte_len = 0;
+                    let mut char_count = 0;
+                    let mut byte_len = 0;
 
-                let iter: Box<dyn Iterator<Item=char>> =
-                    if self.max_depth == 0 {
-                        Box::new(s[offset ..].chars())
-                    } else {
-                        Box::new(s[offset ..].chars().take(max_depth))
-                    };
+                    for c in iter {
+                        self.print_char(self.quoted, '.');
+                        self.push_char('(');
 
-                for c in iter {
-                    self.print_char(self.quoted, '.');
-                    self.push_char('(');
+                        self.print_char(self.quoted, c);
+                        self.push_char(',');
 
-                    self.print_char(self.quoted, c);
-                    self.push_char(',');
+                        char_count += 1;
+                        byte_len += c.len_utf8();
+                    }
 
-                    char_count += 1;
-                    byte_len += c.len_utf8();
-                }
+                    let mut at_end = false;
+                    
+                    if self.max_depth > 0 && !pstr.at_end(offset + byte_len) {
+                        self.append_str("...");
+                        at_end = true;
+                    }  else {
+                        if let HeapCellValue::Addr(Addr::EmptyList) = &self.machine_st.heap[h+1] {
+                            self.append_str("[]");
+                            at_end = true;
+                        }
+                    }
 
-                if self.max_depth > 0 && byte_len < s[offset ..].len() {
-                    self.append_str("...");
-                }  else {
-                    self.append_str("[]");
-                }
+                    for _ in 0 .. char_count {
+                        self.push_char(')');
+                    }
 
-                for _ in 0 .. char_count {
-                    self.push_char(')');
-                }
-            } else {
-                self.push_char('[');
+                    if at_end {
+                        break;
+                    }
 
-                let iter: Box<dyn Iterator<Item=char>> =
-                    if self.max_depth == 0 {
-                        Box::new(s[offset ..].chars())
-                    } else {
-                        Box::new(s[offset ..].chars().take(max_depth))
-                    };
+                    max_depth -= char_count;
+                } else {
+                    self.push_char('[');
 
-                let mut byte_len = 0;
+                    let iter: Box<dyn Iterator<Item=char>> =
+                        if self.max_depth == 0 {
+                            Box::new(pstr.range_from(offset ..))
+                        } else {
+                            Box::new(pstr.range_from(offset ..).take(max_depth))
+                        };
 
-                for c in iter {
-                    self.print_char(false, c);
-                    self.push_char(',');
+                    let mut byte_len = 0;
+                    let mut char_count = 0;
 
-                    byte_len += c.len_utf8();
-                }
+                    for c in iter {
+                        self.print_char(false, c);
+                        self.push_char(',');
+
+                        byte_len += c.len_utf8();
+                        char_count += 1;
+                    }
 
-                if self.max_depth > 0 && byte_len < s[offset ..].len() {
-                    self.append_str("...|...]");
-                }  else {
-                    self.outputter.truncate(self.outputter.len() - ','.len_utf8());
-                    self.push_char(']');
+                    if self.max_depth > 0 && !pstr.at_end(offset + byte_len) {
+                        self.append_str("...|...]");
+                        break;
+                    }  else {
+                        self.outputter.truncate(self.outputter.len() - ','.len_utf8());
+                        self.push_char(']');
+                    }
+
+                    max_depth -= char_count;
                 }
-            }
 
-            iter.stack().pop();
-            iter.stack().pop();
+                h += 2;
+                offset = 0;                
+            }
         } else {
-            let atom = String::from_iter(s[offset ..].chars().map(|c| {
-                char_to_string(self.quoted, c)
-            }));
-
-            self.push_char('"');
-            self.append_str(&atom);
-            self.push_char('"');
+            self.print_string_as_str(h, 0, self.quoted);
         }
     }
 
@@ -1217,21 +1198,22 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         max_depth: usize,
     ) {
         let add_brackets = if !self.ignore_ops {
-            negated_operand
-                || if let Some(ref op) = op {
-                    if self.numbervars && arity == 1 && name.as_str() == "$VAR" {
-                        !iter.immediate_leaf_has_property(|c| match c {
-                            Constant::Integer(n) => n >= 0,
-                            Constant::Float(f) => f >= OrderedFloat(0f64),
-                            Constant::Rational(r) => r >= 0,
-                            _ => false,
-                        }) && needs_bracketing(&spec, op)
-                    } else {
-                        needs_bracketing(&spec, op)
-                    }
+            negated_operand || if let Some(ref op) = op {
+                if self.numbervars && arity == 1 && name.as_str() == "$VAR" {
+                    !iter.immediate_leaf_has_property(|addr, heap| {
+                        match heap.index_addr(&addr).as_ref() {
+                            &HeapCellValue::Integer(ref n) => &**n >= &0,
+                            &HeapCellValue::Addr(Addr::Float(f)) => f >= OrderedFloat(0f64),
+                            &HeapCellValue::Rational(ref r) => &**r >= &0,
+                            _ => false
+                        }
+                    }) && needs_bracketing(&spec, op)
                 } else {
-                    is_functor_redirect && spec.prec() >= 1000
+                    needs_bracketing(&spec, op)
                 }
+            } else {
+                is_functor_redirect && spec.prec() >= 1000
+            }
         } else {
             false
         };
@@ -1264,49 +1246,134 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
     ) {
         let negated_operand = negated_op_needs_bracketing(iter, &op);
 
-        let heap_val = match self.check_for_seen(iter) {
-            Some(heap_val) => heap_val,
+        let addr = match self.check_for_seen(iter) {
+            Some(addr) => addr,
             None => return,
         };
-
-        match heap_val {
-            HeapCellValue::NamedStr(arity, name, spec) => {
+        
+        match self.machine_st.heap.index_addr(&addr).as_ref() {
+            &HeapCellValue::NamedStr(arity, ref name, ref spec) => {
                 let spec = fetch_op_spec(name.clone(), arity, spec.clone(), self.op_dir);
 
                 if let Some(spec) = spec {
                     self.handle_op_as_struct(
-                        name,
+                        name.clone(),
                         arity,
                         iter,
                         &op,
                         is_functor_redirect,
-                        spec,
+                        spec.clone(),
                         negated_operand,
                         max_depth,
                     );
                 } else {
                     push_space_if_amb!(self, name.as_str(), {
-                        let ct = ClauseType::from(name, arity, spec);
+                        let ct = ClauseType::from(name.clone(), arity, spec);
                         self.format_clause(iter, max_depth, arity, ct);
                     });
                 }
             }
-            HeapCellValue::Addr(Addr::Con(Constant::EmptyList)) => {
+            &HeapCellValue::Atom(ref atom, ref spec) => {
+                if let Some(_) = fetch_atom_op_spec(atom.clone(), spec.clone(), self.op_dir) {
+                    let mut result = String::new();
+
+                    if let Some(ref op) = op {
+                        if self.outputter.ends_with(&format!(" {}", op.as_str())) {
+                            result.push(' ');
+                        }
+
+                        result.push('(');
+                    }
+
+                    result += &self.print_op_addendum(atom.as_str());
+
+                    if op.is_some() {
+                        result.push(')');
+                    }
+
+                    push_space_if_amb!(self, &result, {
+                        self.append_str(&result);
+                    });
+                } else {
+                    push_space_if_amb!(self, atom.as_str(), {
+                        self.print_atom(&atom);
+                    });
+                }
+            }
+            &HeapCellValue::Addr(Addr::CharCode(c)) => {
+                self.append_str(&format!("{}", c as u32));
+            }
+            &HeapCellValue::Addr(Addr::Char(c)) => {
+                self.print_char(self.quoted, c);
+            }
+            &HeapCellValue::Addr(Addr::CutPoint(b)) => {
+                self.append_str(&format!("{}", b));
+            }
+            &HeapCellValue::Addr(Addr::EmptyList) => {
                 if !self.at_cdr("") {
                     self.append_str("[]");
                 }
             }
-            HeapCellValue::Addr(Addr::Con(c)) => {
-                self.print_constant(iter, max_depth, c, &op);
+            &HeapCellValue::Addr(Addr::Float(n)) => {
+                self.print_number(Number::Float(n), &op);
+            }
+            &HeapCellValue::Addr(Addr::Usize(u)) => {
+                self.append_str(&format!("{}", u));
             }
-            HeapCellValue::Addr(Addr::Lis(_)) | HeapCellValue::Addr(Addr::PStrLocation(..)) => {
+            &HeapCellValue::Addr(Addr::PStrLocation(..))
+                if !self.machine_st.flags.double_quotes.is_atom() => {
+                    if self.ignore_ops {
+                        self.format_struct(iter, max_depth, 2, clause_name!("."));
+                    } else {
+                        self.push_list(iter, max_depth);
+                    }
+                }
+            &HeapCellValue::Addr(Addr::PStrLocation(h, n)) => {
+                if let HeapCellValue::PartialString(_) = &self.machine_st.heap[h] {
+                    self.print_string(max_depth, h, n);
+
+                    iter.stack().pop();
+                    iter.stack().pop();
+                } else {
+                    unreachable!()
+                }
+            }
+            &HeapCellValue::Addr(Addr::Lis(_)) => {
                 if self.ignore_ops {
                     self.format_struct(iter, max_depth, 2, clause_name!("."));
                 } else {
                     self.push_list(iter, max_depth);
                 }
             }
-            HeapCellValue::Addr(Addr::Stream(stream)) => {
+            &HeapCellValue::Addr(addr) => {
+                if let Some(offset_str) = self.offset_as_string(iter, addr) {
+                    push_space_if_amb!(self, &offset_str, {
+                        self.append_str(offset_str.as_str());
+                    })
+                }
+            }
+            &HeapCellValue::Integer(ref n) => {
+                self.print_number(Number::Integer(n.clone()), &op);
+            }
+            &HeapCellValue::Rational(ref n) => {
+                self.print_number(Number::Rational(n.clone()), &op);
+            }
+            &HeapCellValue::PartialString(_)
+                if self.print_strings_as_strs => {
+                    if let Addr::Con(h) = addr {
+                        self.print_string_as_str(h, 0, true);
+                    } else {
+                        unreachable!()
+                    }
+                }
+            &HeapCellValue::PartialString(_) => {
+                if let Addr::Con(h) = addr {
+                    self.print_string(max_depth, h, 0);
+                } else {
+                    unreachable!()
+                }
+            }
+            &HeapCellValue::Stream(ref stream) => {
                 if let Some(alias) = &stream.options.alias {
                     self.print_atom(alias);
                 } else {
@@ -1317,17 +1384,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                     }
                 }
             }
-            HeapCellValue::Addr(addr) => {
-                if let Some(offset_str) = self.offset_as_string(iter, addr) {
-                    push_space_if_amb!(self, &offset_str, {
-                        self.append_str(offset_str.as_str());
-                    })
-                }
-            }
             _ => {
-                // This is the partial string case. We never clone a partial string
-                // for printing purposes, so.. this.
-                unreachable!()
             }
         }
     }
index fe694d19ea1ca3b0fde98c8c9a25e4df3f27b3d8..2bcfc6c3de0963dede20caedeec72ae9e76f073c 100644 (file)
@@ -63,11 +63,11 @@ impl CodeOffsets {
                 let is_initial_index = self.lists.is_empty();
                 self.lists.push(Self::add_index(is_initial_index, index));
             }
-            &Term::Constant(_, Constant::String(n, ref s)) => {
+            &Term::Constant(_, Constant::String(ref s)) => {
                 let is_initial_index = self.lists.is_empty();
                 self.lists.push(Self::add_index(is_initial_index, index));
 
-                let constant = Constant::String(n, s.clone());
+                let constant = Constant::String(s.clone());
                 let code = self.constants.entry(constant).or_insert(Vec::new());
 
                 let is_initial_index = code.is_empty();
@@ -75,9 +75,11 @@ impl CodeOffsets {
             }
             &Term::Constant(_, ref constant) => {
                 if let Constant::Atom(ref name, _) = constant {
-                    if !name.as_str().is_empty() && name.as_str().chars().skip(1).next().is_none() {
+                    if name.is_char() {
                         let c = name.as_str().chars().next().unwrap();
-                        let code = self.constants.entry(Constant::Char(c)).or_insert(vec![]);
+                        let code = self.constants
+                            .entry(Constant::Char(c))
+                            .or_insert(vec![]);
                         
                         code.push(Self::add_index(code.is_empty(), index));
                     }
index 31d9dc4ac173945660c3d26e2882c87a29ce9e31..eb6db521f9aeb970bbc9f53f43f48b1bfaaa049f 100644 (file)
@@ -2,28 +2,29 @@ use prolog_parser::ast::*;
 
 use crate::prolog::clause_types::*;
 use crate::prolog::forms::*;
+use crate::prolog::machine::heap::*;
 use crate::prolog::machine::machine_errors::MachineStub;
 use crate::prolog::machine::machine_indices::*;
-
 use crate::prolog::rug::Integer;
 
 use indexmap::IndexMap;
 
 use std::collections::VecDeque;
+use std::rc::Rc;
 
 fn reg_type_into_functor(r: RegType) -> MachineStub {
     match r {
-        RegType::Temp(r) => functor!("x", 1, [heap_integer!(Integer::from(r))]),
-        RegType::Perm(r) => functor!("y", 1, [heap_integer!(Integer::from(r))]),
+        RegType::Temp(r) => functor!("x", [integer(r)]),
+        RegType::Perm(r) => functor!("y", [integer(r)]),
     }
 }
 
 impl Level {
     fn into_functor(self) -> MachineStub {
         match self {
-            Level::Root => functor!("level", 1, [heap_atom!("root")]),
-            Level::Shallow => functor!("level", 1, [heap_atom!("shallow")]),
-            Level::Deep => functor!("level", 1, [heap_atom!("deep")]),
+            Level::Root => functor!("level", [atom("root")]),
+            Level::Shallow => functor!("level", [atom("shallow")]),
+            Level::Deep => functor!("level", [atom("deep")]),
         }
     }
 }
@@ -31,11 +32,15 @@ impl Level {
 impl ArithmeticTerm {
     fn into_functor(&self) -> MachineStub {
         match self {
-            &ArithmeticTerm::Reg(r) => reg_type_into_functor(r),
+            &ArithmeticTerm::Reg(r) => {
+                reg_type_into_functor(r)
+            }
             &ArithmeticTerm::Interm(i) => {
-                functor!("intermediate", 1, [heap_integer!(Integer::from(i))])
+                functor!("intermediate", [integer(i)])
+            }
+            &ArithmeticTerm::Number(ref n) => {
+                vec![n.clone().into()]
             }
-            &ArithmeticTerm::Number(ref n) => vec![heap_con!(n.clone().to_constant())],
         }
     }
 }
@@ -52,18 +57,20 @@ impl ChoiceInstruction {
     pub fn to_functor(&self) -> MachineStub {
         match self {
             &ChoiceInstruction::TryMeElse(offset) => {
-                functor!("try_me_else", 1, [heap_integer!(Integer::from(offset))])
+                functor!("try_me_else", [integer(offset)])
             }
             &ChoiceInstruction::RetryMeElse(offset) => {
-                functor!("retry_me_else", 1, [heap_integer!(Integer::from(offset))])
+                functor!("retry_me_else", [integer(offset)])
+            }
+            &ChoiceInstruction::TrustMe => {
+                functor!("trust_me")
+            }
+            &ChoiceInstruction::DefaultRetryMeElse(offset) => {
+                functor!("default_retry_me_else", [integer(offset)])
+            }
+            &ChoiceInstruction::DefaultTrustMe => {
+                functor!("default_trust_me")
             }
-            &ChoiceInstruction::TrustMe => vec![heap_atom!("trust_me")],
-            &ChoiceInstruction::DefaultRetryMeElse(offset) => functor!(
-                "default_retry_me_else",
-                1,
-                [heap_integer!(Integer::from(offset))]
-            ),
-            &ChoiceInstruction::DefaultTrustMe => vec![heap_atom!("default_trust_me")],
         }
     }
 }
@@ -79,21 +86,20 @@ impl CutInstruction {
     pub fn to_functor(&self, h: usize) -> MachineStub {
         match self {
             &CutInstruction::Cut(r) => {
-                let mut stub = functor!("cut", 1, [heap_str!(h + 2)]);
-                stub.append(&mut reg_type_into_functor(r));
-                stub
+                let rt_stub = reg_type_into_functor(r);
+                functor!("cut", [aux(h, 0)], [rt_stub])
             }
             &CutInstruction::GetLevel(r) => {
-                let mut stub = functor!("get_level", 1, [heap_str!(h + 2)]);
-                stub.append(&mut reg_type_into_functor(r));
-                stub
+                let rt_stub = reg_type_into_functor(r);
+                functor!("get_level", [aux(h, 0)], [rt_stub])
             }
             &CutInstruction::GetLevelAndUnify(r) => {
-                let mut stub = functor!("get_level_and_unify", 1, [heap_str!(h + 2)]);
-                stub.append(&mut reg_type_into_functor(r));
-                stub
+                let rt_stub = reg_type_into_functor(r);
+                functor!("get_level_and_unify", [aux(h, 0)], [rt_stub])
+            }
+            &CutInstruction::NeckCut => {
+                functor!("neck_cut")
             }
-            &CutInstruction::NeckCut => vec![heap_atom!("neck_cut")],
         }
     }
 }
@@ -122,13 +128,13 @@ impl IndexedChoiceInstruction {
     pub fn to_functor(&self) -> MachineStub {
         match self {
             &IndexedChoiceInstruction::Try(offset) => {
-                functor!("try", 1, [heap_integer!(Integer::from(offset))])
+                functor!("try", [integer(offset)])
             }
             &IndexedChoiceInstruction::Trust(offset) => {
-                functor!("trust", 1, [heap_integer!(Integer::from(offset))])
+                functor!("trust", [integer(offset)])
             }
             &IndexedChoiceInstruction::Retry(offset) => {
-                functor!("retry", 1, [heap_integer!(Integer::from(offset))])
+                functor!("retry", [integer(offset)])
             }
         }
     }
@@ -219,15 +225,12 @@ fn arith_instr_unary_functor(
     t: usize,
 ) -> MachineStub {
     let at_stub = at.into_functor();
-
-    let mut stub = functor!(
+    
+    functor!(
         name,
-        2,
-        [heap_cell!(h + 4), heap_integer!(Integer::from(t))]
-    );
-
-    stub.extend(at_stub.into_iter());
-    stub
+        [aux(h, 0), integer(t)],
+        [at_stub]
+    )
 }
 
 fn arith_instr_bin_functor(
@@ -240,20 +243,11 @@ fn arith_instr_bin_functor(
     let at_1_stub = at_1.into_functor();
     let at_2_stub = at_2.into_functor();
 
-    let mut stub = functor!(
-        name,
-        3,
-        [
-            heap_cell!(h + 4),
-            heap_cell!(h + 4 + at_1_stub.len()),
-            heap_integer!(Integer::from(t))
-        ]
-    );
-
-    stub.extend(at_1_stub.into_iter());
-    stub.extend(at_2_stub.into_iter());
-
-    stub
+    functor!(
+        name,        
+        [aux(h, 0), aux(h, 1), integer(t)],
+        [at_1_stub, at_2_stub]
+    )
 }
 
 impl ArithmeticInstruction {
@@ -319,17 +313,39 @@ impl ArithmeticInstruction {
             &ArithmeticInstruction::Gcd(ref at_1, ref at_2, t) => {
                 arith_instr_bin_functor(h, "gcd", at_1, at_2, t)
             }
-            &ArithmeticInstruction::Sign(ref at, t) => arith_instr_unary_functor(h, "sign", at, t),
-            &ArithmeticInstruction::Cos(ref at, t) => arith_instr_unary_functor(h, "cos", at, t),
-            &ArithmeticInstruction::Sin(ref at, t) => arith_instr_unary_functor(h, "sin", at, t),
-            &ArithmeticInstruction::Tan(ref at, t) => arith_instr_unary_functor(h, "tan", at, t),
-            &ArithmeticInstruction::Log(ref at, t) => arith_instr_unary_functor(h, "log", at, t),
-            &ArithmeticInstruction::Exp(ref at, t) => arith_instr_unary_functor(h, "exp", at, t),
-            &ArithmeticInstruction::ACos(ref at, t) => arith_instr_unary_functor(h, "acos", at, t),
-            &ArithmeticInstruction::ASin(ref at, t) => arith_instr_unary_functor(h, "asin", at, t),
-            &ArithmeticInstruction::ATan(ref at, t) => arith_instr_unary_functor(h, "atan", at, t),
-            &ArithmeticInstruction::Sqrt(ref at, t) => arith_instr_unary_functor(h, "sqrt", at, t),
-            &ArithmeticInstruction::Abs(ref at, t) => arith_instr_unary_functor(h, "abs", at, t),
+            &ArithmeticInstruction::Sign(ref at, t) => { 
+                arith_instr_unary_functor(h, "sign", at, t)
+            }
+            &ArithmeticInstruction::Cos(ref at, t)  => {
+                arith_instr_unary_functor(h, "cos", at, t)
+            }
+            &ArithmeticInstruction::Sin(ref at, t)  => {
+                arith_instr_unary_functor(h, "sin", at, t)
+            }
+            &ArithmeticInstruction::Tan(ref at, t)  => {
+                arith_instr_unary_functor(h, "tan", at, t)
+            }
+            &ArithmeticInstruction::Log(ref at, t)  => {
+                arith_instr_unary_functor(h, "log", at, t)
+            }
+            &ArithmeticInstruction::Exp(ref at, t)  => {
+                arith_instr_unary_functor(h, "exp", at, t)
+            }
+            &ArithmeticInstruction::ACos(ref at, t) => {
+                arith_instr_unary_functor(h, "acos", at, t)
+            }
+            &ArithmeticInstruction::ASin(ref at, t) => {
+                arith_instr_unary_functor(h, "asin", at, t)
+            }
+            &ArithmeticInstruction::ATan(ref at, t) => {
+                arith_instr_unary_functor(h, "atan", at, t)
+            }
+            &ArithmeticInstruction::Sqrt(ref at, t) => {
+                arith_instr_unary_functor(h, "sqrt", at, t)
+            }
+            &ArithmeticInstruction::Abs(ref at, t) => {
+                arith_instr_unary_functor(h, "abs", at, t)
+            }
             &ArithmeticInstruction::Float(ref at, t) => {
                 arith_instr_unary_functor(h, "float", at, t)
             }
@@ -345,8 +361,12 @@ impl ArithmeticInstruction {
             &ArithmeticInstruction::Floor(ref at, t) => {
                 arith_instr_unary_functor(h, "floor", at, t)
             }
-            &ArithmeticInstruction::Neg(ref at, t) => arith_instr_unary_functor(h, "-", at, t),
-            &ArithmeticInstruction::Plus(ref at, t) => arith_instr_unary_functor(h, "+", at, t),
+            &ArithmeticInstruction::Neg(ref at, t) => {
+                arith_instr_unary_functor(h, "-", at, t)
+            }
+            &ArithmeticInstruction::Plus(ref at, t) => {
+                arith_instr_unary_functor(h, "+", at, t)
+            }
             &ArithmeticInstruction::BitwiseComplement(ref at, t) => {
                 arith_instr_unary_functor(h, "\\", at, t)
             }
@@ -378,29 +398,23 @@ impl ControlInstruction {
     pub fn to_functor(&self) -> MachineStub {
         match self {
             &ControlInstruction::Allocate(num_frames) => {
-                functor!("allocate", 1, [heap_integer!(Integer::from(num_frames))])
-            }
-            &ControlInstruction::CallClause(ref ct, arity, _, false, _) => functor!(
-                "call",
-                2,
-                [
-                    heap_con!(Constant::Atom(ct.name(), None)),
-                    heap_integer!(Integer::from(arity))
-                ]
-            ),
-            &ControlInstruction::CallClause(ref ct, arity, _, true, _) => functor!(
-                "execute",
-                2,
-                [
-                    heap_con!(Constant::Atom(ct.name(), None)),
-                    heap_integer!(Integer::from(arity))
-                ]
-            ),
-            &ControlInstruction::Deallocate => vec![heap_atom!("deallocate")],
+                functor!("allocate", [integer(num_frames)])
+            }
+            &ControlInstruction::CallClause(ref ct, arity, _, false, _) => {
+                functor!("call", [clause_name(ct.name()), integer(arity)])
+            }
+            &ControlInstruction::CallClause(ref ct, arity, _, true, _) => {
+                functor!("execute", [clause_name(ct.name()), integer(arity)])
+            }
+            &ControlInstruction::Deallocate => {
+                functor!("deallocate")
+            }
             &ControlInstruction::JmpBy(_, offset, ..) => {
-                functor!("jmp_by", 1, [heap_integer!(Integer::from(offset))])
+                functor!("jmp_by", [integer(offset)])
+            }
+            &ControlInstruction::Proceed => {
+                functor!("proceed")
             }
-            &ControlInstruction::Proceed => vec![heap_atom!("proceed")],
         }
     }
 }
@@ -420,26 +434,27 @@ impl From<IndexingInstruction> for Line {
 impl IndexingInstruction {
     pub fn to_functor(&self) -> MachineStub {
         match self {
-            &IndexingInstruction::SwitchOnTerm(vars, constants, lists, structures) => functor!(
-                "switch_on_term",
-                4,
-                [
-                    heap_integer!(Integer::from(vars)),
-                    heap_integer!(Integer::from(constants)),
-                    heap_integer!(Integer::from(lists)),
-                    heap_integer!(Integer::from(structures))
-                ]
-            ),
-            &IndexingInstruction::SwitchOnConstant(constants, _) => functor!(
-                "switch_on_constant",
-                1,
-                [heap_integer!(Integer::from(constants))]
-            ),
-            &IndexingInstruction::SwitchOnStructure(structures, _) => functor!(
-                "switch_on_structure",
-                1,
-                [heap_integer!(Integer::from(structures))]
-            ),
+            &IndexingInstruction::SwitchOnTerm(vars, constants, lists, structures) => {
+                functor!(
+                    "switch_on_term",
+                    [integer(vars),
+                      integer(constants),
+                      integer(lists),
+                      integer(structures)]
+                )
+            }
+            &IndexingInstruction::SwitchOnConstant(constants, _) => {
+                functor!(
+                    "switch_on_constant",
+                    [integer(constants)]
+                )
+            }
+            &IndexingInstruction::SwitchOnStructure(structures, _) => {
+                functor!(
+                    "switch_on_structure",
+                    [integer(structures)]
+                )
+            }
         }
     }
 }
@@ -461,86 +476,85 @@ pub enum FactInstruction {
 impl FactInstruction {
     pub fn to_functor(&self, h: usize) -> MachineStub {
         match self {
-            &FactInstruction::GetConstant(lvl, ref constant, r) => {
-                let mut stub = functor!(
+            &FactInstruction::GetConstant(lvl, ref c, r) => {
+                let lvl_stub = lvl.into_functor();
+                let rt_stub  = reg_type_into_functor(r);
+                
+                functor!(
                     "get_constant",
-                    3,
-                    [
-                        heap_str!(h + 4),
-                        heap_con!(constant.clone()),
-                        heap_str!(h + 6)
-                    ]
-                );
-
-                stub.append(&mut lvl.into_functor());
-                stub.append(&mut reg_type_into_functor(r));
-
-                stub
+                    [aux(h, 0), constant(c), aux(h, 1)],
+                    [lvl_stub, rt_stub]
+                )
             }
             &FactInstruction::GetList(lvl, r) => {
-                let mut stub = functor!("get_list", 2, [heap_str!(h + 3), heap_str!(h + 5)]);
-                stub.append(&mut lvl.into_functor());
-                stub.append(&mut reg_type_into_functor(r));
+                let lvl_stub = lvl.into_functor();
+                let rt_stub  = reg_type_into_functor(r);
 
-                stub
+                functor!(
+                    "get_list",
+                    [aux(h, 0), aux(h, 1)],
+                    [lvl_stub, rt_stub]
+                )
             }
             &FactInstruction::GetStructure(ref ct, arity, r) => {
-                let mut stub = functor!(
-                    "get_structure",
-                    3,
-                    [
-                        heap_con!(Constant::Atom(ct.name(), None)),
-                        heap_integer!(Integer::from(arity)),
-                        heap_str!(h + 4)
-                    ]
-                );
-                stub.append(&mut reg_type_into_functor(r));
+                let rt_stub = reg_type_into_functor(r);
 
-                stub
+                functor!(
+                    "get_structure",
+                    [clause_name(ct.name()), integer(arity), aux(h, 0)],
+                    [rt_stub]
+                )
             }
             &FactInstruction::GetValue(r, arg) => {
-                let mut stub = functor!(
-                    "get_value",
-                    2,
-                    [heap_str!(h + 3), heap_integer!(Integer::from(arg))]
-                );
-                stub.append(&mut reg_type_into_functor(r));
+                let rt_stub = reg_type_into_functor(r);
 
-                stub
+                functor!(
+                    "get_value",
+                    [aux(h, 0), integer(arg)],
+                    [rt_stub]
+                )
             }
             &FactInstruction::GetVariable(r, arg) => {
-                let mut stub = functor!(
-                    "get_variable",
-                    2,
-                    [heap_str!(h + 3), heap_integer!(Integer::from(arg))]
-                );
-                stub.append(&mut reg_type_into_functor(r));
+                let rt_stub = reg_type_into_functor(r);
 
-                stub
+                functor!(
+                    "get_variable",
+                    [aux(h, 0), integer(arg)],
+                    [rt_stub]
+                )
             }
-            &FactInstruction::UnifyConstant(ref constant) => {
-                functor!("unify_constant", 1, [heap_con!(constant.clone())])
+            &FactInstruction::UnifyConstant(ref c) => {
+                functor!("unify_constant", [constant(c)], [])
             }
             &FactInstruction::UnifyLocalValue(r) => {
-                let mut stub = functor!("unify_local_value", 1, [heap_str!(h + 2)]);
-                stub.append(&mut reg_type_into_functor(r));
-
-                stub
+                let rt_stub = reg_type_into_functor(r);
+                
+                functor!(
+                    "unify_local_value",
+                    [aux(h, 0)],
+                    [rt_stub]
+                )
             }
             &FactInstruction::UnifyVariable(r) => {
-                let mut stub = functor!("unify_variable", 1, [heap_str!(h + 2)]);
-                stub.append(&mut reg_type_into_functor(r));
+                let rt_stub = reg_type_into_functor(r);
 
-                stub
+                functor!(
+                    "unify_variable",
+                    [aux(h, 0)],
+                    [rt_stub]
+                )
             }
             &FactInstruction::UnifyValue(r) => {
-                let mut stub = functor!("unify_value", 1, [heap_str!(h + 2)]);
-                stub.append(&mut reg_type_into_functor(r));
-
-                stub
+                let rt_stub = reg_type_into_functor(r);
+                
+                functor!(
+                    "unify_value",
+                    [aux(h, 0)],
+                    [rt_stub]
+                )
             }
             &FactInstruction::UnifyVoid(vars) => {
-                functor!("unify_void", 1, [heap_integer!(Integer::from(vars))])
+                functor!("unify_void", [integer(vars)])
             }
         }
     }
@@ -567,103 +581,96 @@ impl QueryInstruction {
         match self {
             &QueryInstruction::PutUnsafeValue(norm, arg) => functor!(
                 "put_unsafe_value",
-                2,
-                [
-                    heap_integer!(Integer::from(norm)),
-                    heap_integer!(Integer::from(arg))
-                ]
+                [integer(norm), integer(arg)]
             ),
-            &QueryInstruction::PutConstant(lvl, ref constant, r) => {
-                let mut stub = functor!(
-                    "put_constant",
-                    3,
-                    [
-                        heap_str!(h + 4),
-                        heap_con!(constant.clone()),
-                        heap_str!(h + 6)
-                    ]
-                );
-
-                stub.append(&mut lvl.into_functor());
-                stub.append(&mut reg_type_into_functor(r));
+            &QueryInstruction::PutConstant(lvl, ref c, r) => {
+                let lvl_stub = lvl.into_functor();
+                let rt_stub  = reg_type_into_functor(r);
 
-                stub
+                functor!(
+                    "put_constant",
+                    [aux(h, 0), constant(c), aux(h, 1)],
+                    [lvl_stub, rt_stub]
+                )
             }
             &QueryInstruction::PutList(lvl, r) => {
-                let mut stub = functor!("put_list", 2, [heap_str!(h + 3), heap_str!(h + 5)]);
-
-                stub.append(&mut lvl.into_functor());
-                stub.append(&mut reg_type_into_functor(r));
-
-                stub
+                let lvl_stub = lvl.into_functor();
+                let rt_stub  = reg_type_into_functor(r);
+                
+                functor!(
+                    "put_list",
+                    [aux(h, 0), aux(h, 1)],
+                    [lvl_stub, rt_stub]
+                )
             }
             &QueryInstruction::PutStructure(ref ct, arity, r) => {
-                let mut stub = functor!(
-                    "put_structure",
-                    3,
-                    [
-                        heap_con!(Constant::Atom(ct.name(), None)),
-                        heap_integer!(Integer::from(arity)),
-                        heap_str!(h + 4)
-                    ]
-                );
+                let rt_stub = reg_type_into_functor(r);
 
-                stub.append(&mut reg_type_into_functor(r));
-                stub
+                functor!(
+                    "put_structure",
+                    [clause_name(ct.name()), integer(arity), aux(h, 0)],
+                    [rt_stub]
+                )
             }
             &QueryInstruction::PutValue(r, arg) => {
-                let mut stub = functor!(
-                    "put_value",
-                    2,
-                    [heap_str!(h + 3), heap_integer!(Integer::from(arg))]
-                );
+                let rt_stub = reg_type_into_functor(r);
 
-                stub.append(&mut reg_type_into_functor(r));
-                stub
+                functor!(
+                    "put_value",                
+                    [aux(h, 0), integer(arg)],
+                    [rt_stub]
+                )
             }
             &QueryInstruction::GetVariable(r, arg) => {
-                let mut stub = functor!(
-                    "get_variable",
-                    2,
-                    [heap_str!(h + 3), heap_integer!(Integer::from(arg))]
-                );
+                let rt_stub = reg_type_into_functor(r);
 
-                stub.append(&mut reg_type_into_functor(r));
-                stub
+                functor!(
+                    "get_variable",
+                    [aux(h, 0), integer(arg)],
+                    [rt_stub]
+                )
             }
             &QueryInstruction::PutVariable(r, arg) => {
-                let mut stub = functor!(
-                    "put_variable",
-                    2,
-                    [heap_str!(h + 3), heap_integer!(Integer::from(arg))]
-                );
+                let rt_stub = reg_type_into_functor(r);
 
-                stub.append(&mut reg_type_into_functor(r));
-                stub
+                functor!(
+                    "put_variable",
+                    [aux(h, 0), integer(arg)],
+                    [rt_stub]
+                )
             }
-            &QueryInstruction::SetConstant(ref constant) => {
-                functor!("set_constant", 1, [heap_con!(constant.clone())])
+            &QueryInstruction::SetConstant(ref c) => {
+                functor!("set_constant", [constant(c)], [])
             }
             &QueryInstruction::SetLocalValue(r) => {
-                let mut stub = functor!("set_local_value", 1, [heap_str!(h + 2)]);
-
-                stub.append(&mut reg_type_into_functor(r));
-                stub
+                let rt_stub = reg_type_into_functor(r);
+                
+                functor!(
+                    "set_local_value",
+                    [aux(h, 0)],
+                    [rt_stub]
+                )
             }
             &QueryInstruction::SetVariable(r) => {
-                let mut stub = functor!("set_variable", 1, [heap_str!(h + 2)]);
+                let rt_stub = reg_type_into_functor(r);
 
-                stub.append(&mut reg_type_into_functor(r));
-                stub
+                functor!(
+                    "set_variable",
+                    [aux(h, 0)],
+                    [rt_stub]
+                )
             }
             &QueryInstruction::SetValue(r) => {
-                let mut stub = functor!("set_value", 1, [heap_str!(h + 2)]);
+                let rt_stub = reg_type_into_functor(r);
 
-                stub.append(&mut reg_type_into_functor(r));
-                stub
+                functor!(
+                    "set_value",
+                    [aux(h, 0)],
+                    [rt_stub]
+                )
             }
             &QueryInstruction::SetVoid(vars) => {
-                functor!("set_void", 1, [heap_integer!(Integer::from(vars))])
+                functor!("set_void", [integer(vars)])
             }
         }
     }
diff --git a/src/prolog/machine/arithmetic_ops.rs b/src/prolog/machine/arithmetic_ops.rs
new file mode 100644 (file)
index 0000000..647c88b
--- /dev/null
@@ -0,0 +1,786 @@
+use crate::prolog_parser::ast::*;
+
+use crate::prolog::arithmetic::*;
+use crate::prolog::clause_types::*;
+use crate::prolog::forms::*;
+use crate::prolog::machine::machine_errors::*;
+use crate::prolog::machine::machine_indices::*;
+use crate::prolog::machine::machine_state::*;
+use crate::prolog::ordered_float::*;
+use crate::prolog::rug::{Integer, Rational};
+
+use std::cmp;
+use std::f64;
+use std::mem;
+use std::rc::Rc;
+
+#[macro_export]
+macro_rules! try_numeric_result {
+    ($s: ident, $e: expr, $caller: expr) => (
+        match $e {
+            Ok(val) => {
+                Ok(val)
+            }
+            Err(e) => {
+                let caller_copy =
+                    $caller.iter().map(|v| v.context_free_clone()).collect();
+                
+                Err($s.error_form(MachineError::evaluation_error(e), caller_copy))
+            }
+        }
+    );
+}
+
+impl MachineState {
+    pub(crate)
+    fn get_number(&mut self, at: &ArithmeticTerm) -> Result<Number, MachineStub> {
+        match at {
+            &ArithmeticTerm::Reg(r) => {
+                self.arith_eval_by_metacall(r)
+            }
+            &ArithmeticTerm::Interm(i) => Ok(mem::replace(
+                &mut self.interms[i - 1],
+                Number::Integer(Rc::new(Integer::from(0))),
+            )),
+            &ArithmeticTerm::Number(ref n) => {
+                Ok(n.clone())
+            }
+        }
+    }
+
+    pub(super)
+    fn rational_from_number(
+        &self,
+        n: Number,
+    ) -> Result<Rc<Rational>, MachineError> {
+        match n {
+            Number::Rational(r) => {
+                Ok(r)
+            }
+            Number::Float(OrderedFloat(f)) => {
+                match Rational::from_f64(f) {
+                    Some(r) => {
+                        Ok(Rc::new(r))
+                    }
+                    None => {
+                        Err(MachineError::instantiation_error())
+                    }
+                }
+            }
+            Number::Integer(n) => {
+                Ok(Rc::new(Rational::from(&*n)))
+            }
+        }
+    }
+
+    pub(crate)
+    fn get_rational(
+        &mut self,
+        at: &ArithmeticTerm,
+        caller: MachineStub,
+    ) -> Result<(Rc<Rational>, MachineStub), MachineStub> {
+        let n = self.get_number(at)?;
+        
+        match self.rational_from_number(n) {
+            Ok(r) => Ok((r, caller)),
+            Err(e) => Err(self.error_form(e, caller))
+        }
+    }
+
+    pub(crate)
+    fn arith_eval_by_metacall(&self, r: RegType) -> Result<Number, MachineStub> {
+        let a = self[r].clone();
+
+        let caller = MachineError::functor_stub(clause_name!("(is)"), 2);
+        let mut interms: Vec<Number> = Vec::with_capacity(64);
+
+        for addr in self.post_order_iter(a) {
+            match self.heap.index_addr(&addr).as_ref() {
+                &HeapCellValue::NamedStr(2, ref name, _) => {
+                    let a2 = interms.pop().unwrap();
+                    let a1 = interms.pop().unwrap();
+
+                    match name.as_str() {
+                        "+" => interms.push(try_numeric_result!(self, a1 + a2, caller)?),
+                        "-" => interms.push(try_numeric_result!(self, a1 - a2, caller)?),
+                        "*" => interms.push(try_numeric_result!(self, a1 * a2, caller)?),
+                        "/" => interms.push(self.div(a1, a2)?),
+                        "**" => interms.push(self.pow(a1, a2, "(is)")?),
+                        "^" => interms.push(self.int_pow(a1, a2)?),
+                        "max" => interms.push(self.max(a1, a2)?),
+                        "min" => interms.push(self.min(a1, a2)?),
+                        "rdiv" => {
+                            let r1 = self.rational_from_number(a1);
+                            let r2 = r1.and_then(|r1| {
+                                self.rational_from_number(a2).map(|r2| (r1, r2))
+                            });
+                                                 
+                            match r2 {
+                                Ok((r1, r2)) => {
+                                    let result = Number::Rational(Rc::new(self.rdiv(r1, r2)?));
+                                    interms.push(result);
+                                }
+                                Err(e) => {
+                                    return Err(self.error_form(e, caller));
+                                }
+                            }                            
+                        }
+                        "//" => interms.push(Number::Integer(Rc::new(self.idiv(a1, a2)?))),
+                        "div" => interms.push(Number::Integer(Rc::new(self.int_floor_div(a1, a2)?))),
+                        ">>" => interms.push(Number::Integer(Rc::new(self.shr(a1, a2)?))),
+                        "<<" => interms.push(Number::Integer(Rc::new(self.shl(a1, a2)?))),
+                        "/\\" => interms.push(Number::Integer(Rc::new(self.and(a1, a2)?))),
+                        "\\/" => interms.push(Number::Integer(Rc::new(self.or(a1, a2)?))),
+                        "xor" => interms.push(Number::Integer(Rc::new(self.xor(a1, a2)?))),
+                        "mod" => interms.push(Number::Integer(Rc::new(self.modulus(a1, a2)?))),
+                        "rem" => interms.push(Number::Integer(Rc::new(self.remainder(a1, a2)?))),
+                        "atan2" => interms.push(Number::Float(OrderedFloat(self.atan2(a1, a2)?))),
+                        "gcd" => interms.push(Number::Integer(Rc::new(self.gcd(a1, a2)?))),
+                        _ => {
+                            return Err(self.error_form(MachineError::instantiation_error(), caller))
+                        }
+                    }
+                }
+                &HeapCellValue::NamedStr(1, ref name, _) => {
+                    let a1 = interms.pop().unwrap();
+
+                    match name.as_str() {
+                        "-" => interms.push(-a1),
+                        "+" => interms.push(a1),
+                        "cos" => interms.push(Number::Float(OrderedFloat(self.cos(a1)?))),
+                        "sin" => interms.push(Number::Float(OrderedFloat(self.sin(a1)?))),
+                        "tan" => interms.push(Number::Float(OrderedFloat(self.tan(a1)?))),
+                        "sqrt" => interms.push(Number::Float(OrderedFloat(self.sqrt(a1)?))),
+                        "log" => interms.push(Number::Float(OrderedFloat(self.log(a1)?))),
+                        "exp" => interms.push(Number::Float(OrderedFloat(self.exp(a1)?))),
+                        "acos" => interms.push(Number::Float(OrderedFloat(self.acos(a1)?))),
+                        "asin" => interms.push(Number::Float(OrderedFloat(self.asin(a1)?))),
+                        "atan" => interms.push(Number::Float(OrderedFloat(self.atan(a1)?))),
+                        "abs" => interms.push(a1.abs()),
+                        "float" => interms.push(Number::Float(OrderedFloat(self.float(a1)?))),
+                        "truncate" => interms.push(Number::Integer(Rc::new(self.truncate(a1)))),
+                        "round" => interms.push(Number::Integer(Rc::new(self.round(a1)?))),
+                        "ceiling" => interms.push(Number::Integer(Rc::new(self.ceiling(a1)))),
+                        "floor" => interms.push(Number::Integer(Rc::new(self.floor(a1)))),
+                        "\\" => interms.push(Number::Integer(Rc::new(self.bitwise_complement(a1)?))),
+                        "sign" => interms.push(Number::Integer(Rc::new(self.sign(a1)))),
+                        _ => {
+                            return Err(self.error_form(MachineError::instantiation_error(), caller));
+                        }
+                    }
+                }
+                &HeapCellValue::Integer(ref n) => {
+                    interms.push(Number::Integer(n.clone()))
+                }
+                &HeapCellValue::Addr(Addr::Float(n)) => {
+                    interms.push(Number::Float(n))
+                }
+                &HeapCellValue::Rational(ref n) => {
+                    interms.push(Number::Rational(n.clone()))
+                }
+                &HeapCellValue::Atom(ref name, _) if name.as_str() == "pi" => {
+                    interms.push(Number::Float(OrderedFloat(f64::consts::PI)))
+                }
+                _ => {
+                    return Err(self.error_form(
+                        MachineError::instantiation_error(),
+                        caller,
+                    ));
+                }
+            }
+        }
+
+        Ok(interms.pop().unwrap())
+    }
+
+    pub(crate)
+    fn rdiv(&self, r1: Rc<Rational>, r2: Rc<Rational>) -> Result<Rational, MachineStub> {
+        if &*r2 == &0 {
+            let stub = MachineError::functor_stub(clause_name!("(rdiv)"), 2);
+            Err(self.error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
+        } else {
+            Ok(Rational::from(&*r1 / &*r2))
+        }
+    }
+
+    pub(crate)
+    fn int_floor_div(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
+        match n1 / n2 {
+            Ok(result) => Ok(rnd_i(&result).to_owned()),
+            Err(e) => {
+                let stub = MachineError::functor_stub(clause_name!("(div)"), 2);
+                Err(self.error_form(
+                    MachineError::evaluation_error(
+                        e
+                    ),
+                    stub
+                ))
+            }
+        }
+    }
+
+    pub(crate)
+    fn idiv(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                if &*n2 == &0 {
+                    let stub = MachineError::functor_stub(clause_name!("(//)"), 2);
+
+                    Err(self.error_form(
+                        MachineError::evaluation_error(
+                            EvalError::ZeroDivisor
+                        ),
+                        stub,
+                    ))
+                } else {
+                    Ok(<(Integer, Integer)>::from(n1.div_rem_ref(&*n2)).0)
+                }
+            }
+            (Number::Integer(_), n2) => {
+                let stub = MachineError::functor_stub(clause_name!("(//)"), 2);
+
+                Err(self.error_form(
+                    MachineError::type_error(
+                        self.heap.h(),
+                        ValidType::Integer,
+                        n2,
+                    ),
+                    stub,
+                ))
+            }
+            (n1, _) => {
+                let stub = MachineError::functor_stub(clause_name!("(//)"), 2);
+
+                Err(self.error_form(
+                    MachineError::type_error(
+                        self.heap.h(),
+                        ValidType::Integer,
+                        n1,
+                    ),
+                    stub,
+                ))
+            }
+        }
+    }
+
+    pub(crate)
+    fn div(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(/)"), 2);
+
+        if n2.is_zero() {
+            Err(self.error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
+        } else {
+            try_numeric_result!(self, n1 / n2, stub)
+        }
+    }
+
+    pub(crate)
+    fn atan2(&self, n1: Number, n2: Number) -> Result<f64, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
+
+        if n1.is_zero() && n2.is_zero() {
+            Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub))
+        } else {
+            let f1 = self.float(n1)?;
+            let f2 = self.float(n2)?;
+
+            self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.atan2(f2))
+        }
+    }
+
+    pub(crate)
+    fn int_pow(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
+        if n1.is_zero() && n2.is_negative() {
+            let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
+            return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub));
+        }
+
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                if &*n1 != &1 && &*n2 < &0 {
+                    let n = Number::Integer(n1);
+                    let stub = MachineError::functor_stub(clause_name!("^"), 2);
+
+                    Err(self.error_form(
+                        MachineError::type_error(
+                            self.heap.h(),
+                            ValidType::Float,
+                            n
+                        ),
+                        stub,
+                    ))
+                } else {
+                    Ok(Number::Integer(Rc::new(binary_pow(n1.as_ref().clone(), n2.as_ref()))))
+                }
+            }
+            (n1, Number::Integer(n2)) => {
+                let f1 = self.float(n1)?;
+                let f2 = self.float(Number::Integer(n2))?;
+
+                self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.powf(f2))
+                    .map(|f| Number::Float(OrderedFloat(f)))
+            }
+            (n1, n2) => {
+                let f2 = self.float(n2)?;
+
+                if n1.is_negative() && f2 != f2.floor() {
+                    let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
+                    return Err(
+                        self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub)
+                    );
+                }
+
+                let f1 = self.float(n1)?;
+                self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.powf(f2))
+                    .map(|f| Number::Float(OrderedFloat(f)))
+            }
+        }
+    }
+
+    pub(crate)
+    fn gcd(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                Ok(Integer::from(n1.gcd_ref(&n2)))
+            }
+            (Number::Float(f), _) | (_, Number::Float(f)) => {
+                let n = Number::Float(f);
+                let stub = MachineError::functor_stub(clause_name!("gcd"), 2);
+
+                Err(self.error_form(
+                    MachineError::type_error(
+                        self.heap.h(),
+                        ValidType::Integer,
+                        n
+                    ),
+                    stub,
+                ))
+            }
+            (Number::Rational(r), _) | (_, Number::Rational(r)) => {
+                let n = Number::Rational(r);
+                let stub = MachineError::functor_stub(clause_name!("gcd"), 2);
+
+                Err(self.error_form(
+                    MachineError::type_error(
+                        self.heap.h(),
+                        ValidType::Integer,
+                        n,
+                    ),
+                    stub,
+                ))
+            }
+        }
+    }
+
+    pub(crate)
+    fn float_pow(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
+        let f1 = result_f(&n1, rnd_f);
+        let f2 = result_f(&n2, rnd_f);
+
+        let stub = MachineError::functor_stub(clause_name!("(**)"), 2);
+
+        let f1 = try_numeric_result!(self, f1, stub)?;
+        let f2 = try_numeric_result!(self, f2, stub)?;
+
+        let result = result_f(&Number::Float(OrderedFloat(f1.powf(f2))), rnd_f);
+
+        Ok(Number::Float(OrderedFloat(try_numeric_result!(
+            self, result, stub
+        )?)))
+    }
+
+    pub(crate)
+    fn pow(&self, n1: Number, n2: Number, culprit: &'static str) -> Result<Number, MachineStub> {
+        if n2.is_negative() && n1.is_zero() {
+            let stub = MachineError::functor_stub(clause_name!(culprit), 2);
+            return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub));
+        }
+
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                Ok(Number::Integer(Rc::new(binary_pow(n1.as_ref().clone(), &*n2))))
+            }
+            (n1, n2) => {
+                self.float_pow(n1, n2)
+            }
+        }
+    }
+
+    pub(crate)
+    fn unary_float_fn_template<FloatFn>(&self, n1: Number, f: FloatFn) -> Result<f64, MachineStub>
+    where
+        FloatFn: Fn(f64) -> f64,
+    {
+        let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
+
+        let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub)?;
+        let f1 = result_f(&Number::Float(OrderedFloat(f(f1))), rnd_f);
+
+        try_numeric_result!(self, f1, stub)
+    }
+
+    pub(crate)
+    fn sin(&self, n1: Number) -> Result<f64, MachineStub> {
+        self.unary_float_fn_template(n1, |f| f.sin())
+    }
+
+    pub(crate)
+    fn cos(&self, n1: Number) -> Result<f64, MachineStub> {
+        self.unary_float_fn_template(n1, |f| f.cos())
+    }
+
+    pub(crate)
+    fn tan(&self, n1: Number) -> Result<f64, MachineStub> {
+        self.unary_float_fn_template(n1, |f| f.tan())
+    }
+
+    pub(crate)
+    fn log(&self, n1: Number) -> Result<f64, MachineStub> {
+        self.unary_float_fn_template(n1, |f| f.log(f64::consts::E))
+    }
+
+    pub(crate)
+    fn exp(&self, n1: Number) -> Result<f64, MachineStub> {
+        self.unary_float_fn_template(n1, |f| f.exp())
+    }
+
+    pub(crate)
+    fn asin(&self, n1: Number) -> Result<f64, MachineStub> {
+        self.unary_float_fn_template(n1, |f| f.asin())
+    }
+
+    pub(crate)
+    fn acos(&self, n1: Number) -> Result<f64, MachineStub> {
+        self.unary_float_fn_template(n1, |f| f.acos())
+    }
+
+    pub(crate)
+    fn atan(&self, n1: Number) -> Result<f64, MachineStub> {
+        self.unary_float_fn_template(n1, |f| f.atan())
+    }
+
+    pub(crate)
+    fn sqrt(&self, n1: Number) -> Result<f64, MachineStub> {
+        if n1.is_negative() {
+            let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
+            return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub));
+        }
+
+        self.unary_float_fn_template(n1, |f| f.sqrt())
+    }
+
+    pub(crate)
+    fn float(&self, n: Number) -> Result<f64, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
+        try_numeric_result!(self, result_f(&n, rnd_f), stub)
+    }
+
+    pub(crate)
+    fn floor(&self, n1: Number) -> Integer {
+        rnd_i(&n1).to_owned()
+    }
+
+    pub(crate)
+    fn ceiling(&self, n1: Number) -> Integer {
+        -self.floor(-n1)
+    }
+
+    pub(crate)
+    fn truncate(&self, n: Number) -> Integer {
+        if n.is_negative() {
+            -self.floor(n.abs())
+        } else {
+            self.floor(n)
+        }
+    }
+
+    pub(crate)
+    fn round(&self, n: Number) -> Result<Integer, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
+
+        let result = n + Number::Float(OrderedFloat(0.5f64));
+        let result = try_numeric_result!(self, result, stub)?;
+
+        Ok(self.floor(result))
+    }
+
+    pub(crate)
+    fn shr(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(>>)"), 2);
+
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) =>
+                match n2.to_u32() {
+                    Some(n2) => Ok(Integer::from(&*n1 >> n2)),
+                    _ => Ok(Integer::from(&*n1 >> u32::max_value())),
+                },
+            (Number::Integer(_), n2) => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n2,
+                ),
+                stub,
+            )),
+            (n1, _) => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n1,
+                ),
+                stub,
+            )),
+        }
+    }
+
+    pub(crate)
+    fn shl(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(<<)"), 2);
+
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
+                Some(n2) => Ok(Integer::from(&*n1 << n2)),
+                _ => Ok(Integer::from(&*n1 << u32::max_value())),
+            },
+            (Number::Integer(_), n2) => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n2,
+                ),
+                stub,
+            )),
+            (n1, _) => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n1,
+                ),
+                stub,
+            )),
+        }
+    }
+
+    pub(crate)
+    fn bitwise_complement(&self, n1: Number) -> Result<Integer, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(\\)"), 2);
+
+        match n1 {
+            Number::Integer(n1) => Ok(Integer::from(!&*n1)),
+            _ => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n1,
+                ),
+                stub,
+            )),
+        }
+    }
+
+    pub(crate)
+    fn xor(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(xor)"), 2);
+
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                Ok(Integer::from(&*n1 ^ &*n2))
+            }
+            (Number::Integer(_), n2) => {
+                Err(self.error_form(
+                    MachineError::type_error(
+                        self.heap.h(),
+                        ValidType::Integer,
+                        n2
+                    ),
+                    stub,
+                ))
+            }
+            (n1, _) => {
+                Err(self.error_form(
+                    MachineError::type_error(
+                        self.heap.h(),
+                        ValidType::Integer,
+                        n1
+                    ),
+                    stub,
+                ))
+            }
+        }
+    }
+
+    pub(crate)
+    fn and(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(/\\)"), 2);
+
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => Ok(Integer::from(&*n1 & &*n2)),
+            (Number::Integer(_), n2) => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n2,
+                ),
+                stub,
+            )),
+            (n1, _) => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n1,
+                ),
+                stub,
+            )),
+        }
+    }
+
+    pub(crate)
+    fn modulus(&self, x: Number, y: Number) -> Result<Integer, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(mod)"), 2);
+
+        match (x, y) {
+            (Number::Integer(x), Number::Integer(y)) => {
+                if &*y == &0 {
+                    Err(self.error_form(
+                        MachineError::evaluation_error(EvalError::ZeroDivisor),
+                        stub,
+                    ))
+                } else {
+                    Ok(<(Integer, Integer)>::from(x.div_rem_floor_ref(&*y)).1)
+                }
+            }
+            (Number::Integer(_), n2) => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n2,
+                ),
+                stub,
+            )),
+            (n1, _) => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n1,
+                ),
+                stub,
+            )),
+        }
+    }
+
+    pub(crate)
+    fn max(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                if n1 > n2 {
+                    Ok(Number::Integer(n1))
+                } else {
+                    Ok(Number::Integer(n2))
+                }
+            }
+            (n1, n2) => {
+                let stub = MachineError::functor_stub(clause_name!("max"), 2);
+
+                let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub)?;
+                let f2 = try_numeric_result!(self, result_f(&n2, rnd_f), stub)?;
+
+                Ok(Number::Float(cmp::max(OrderedFloat(f1), OrderedFloat(f2))))
+            }
+        }
+    }
+
+    pub(crate)
+    fn min(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                if n1 < n2 {
+                    Ok(Number::Integer(n1))
+                } else {
+                    Ok(Number::Integer(n2))
+                }
+            }
+            (n1, n2) => {
+                let stub = MachineError::functor_stub(clause_name!("max"), 2);
+
+                let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub)?;
+                let f2 = try_numeric_result!(self, result_f(&n2, rnd_f), stub)?;
+
+                Ok(Number::Float(cmp::min(OrderedFloat(f1), OrderedFloat(f2))))
+            }
+        }
+    }
+
+    pub(crate)
+    fn sign(&self, n: Number) -> Integer {
+        if n.is_positive() {
+            Integer::from(1)
+        } else if n.is_negative() {
+            Integer::from(-1)
+        } else {
+            Integer::from(0)
+        }
+    }
+
+    pub(crate)
+    fn remainder(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(rem)"), 2);
+
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                if &*n2 == &0 {
+                    Err(self
+                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
+                } else {
+                    Ok(Integer::from(&*n1 % &*n2))
+                }
+            }
+            (Number::Integer(_), n2) => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n2,
+                ),
+                stub,
+            )),
+            (n1, _) => Err(self.error_form(
+                MachineError::type_error(
+                    self.heap.h(),
+                    ValidType::Integer,
+                    n1,
+                ),
+                stub,
+            )),
+        }
+    }
+
+    pub(crate)
+    fn or(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
+        let stub = MachineError::functor_stub(clause_name!("(\\/)"), 2);
+
+        match (n1, n2) {
+            (Number::Integer(n1), Number::Integer(n2)) => {
+                Ok(Integer::from(&*n1 | &*n2))
+            }
+            (Number::Integer(_), n2) => {
+                Err(self.error_form(
+                    MachineError::type_error(
+                        self.heap.h(),
+                        ValidType::Integer,
+                        n2,
+                    ),
+                    stub,
+                ))
+            }
+            (n1, _) => {
+                Err(self.error_form(
+                    MachineError::type_error(
+                        self.heap.h(),
+                        ValidType::Integer,
+                        n1
+                    ),
+                    stub,
+                ))
+            }
+        }
+    }
+}
index 44ebbe0b3ac5b3576fba429c76a846f21a2fb2ee..f21c892652671e922039fd3d1d3f3fff3c7f4393 100644 (file)
@@ -1,5 +1,6 @@
 use crate::prolog::machine::*;
 
+use std::cmp::Ordering;
 use std::vec::IntoIter;
 
 pub static VERIFY_ATTRS: &str = include_str!("attributed_variables.pl");
@@ -66,7 +67,7 @@ impl MachineState {
             .attr_var_init
             .bindings
             .iter()
-            .map(|(ref h, _)| Addr::AttrVar(*h));
+            .map(|(ref h, _)| HeapCellValue::Addr(Addr::AttrVar(*h)));
 
         let var_list_addr = Addr::HeapCell(self.heap.to_list(iter));
 
@@ -74,7 +75,7 @@ impl MachineState {
             .attr_var_init
             .bindings
             .drain(0 ..)
-            .map(|(_, addr)| addr);
+            .map(|(_, addr)| HeapCellValue::Addr(addr));
 
         let value_list_addr = Addr::HeapCell(self.heap.to_list(iter));
         (var_list_addr, value_list_addr)
@@ -100,7 +101,9 @@ impl MachineState {
             })
             .collect();
 
-        attr_vars.sort_unstable_by(|a1, a2| self.compare_term_test(a1, a2));
+        attr_vars.sort_unstable_by(|a1, a2| {
+            self.compare_term_test(a1, a2).unwrap_or(Ordering::Less)
+        });
 
         self.term_dedup(&mut attr_vars);
         attr_vars.into_iter()
@@ -117,9 +120,9 @@ impl MachineState {
         }
 
         self.stack.index_and_frame_mut(e)[self.num_of_args + 1] =
-            Addr::Con(Constant::CutPoint(self.b0));
+            Addr::CutPoint(self.b0);
         self.stack.index_and_frame_mut(e)[self.num_of_args + 2] =
-            Addr::Con(Constant::Usize(self.num_of_args));
+            Addr::Usize(self.num_of_args);
 
         self.verify_attributes();
 
index a08cb87a839af60a4bd216cb9ea0e79dc3ea5ca9..724b6c54a2e338dbaf099683c4602e6878725491 100644 (file)
@@ -1,6 +1,8 @@
 use crate::prolog::machine::machine_indices::*;
 use crate::prolog::machine::stack::*;
+use crate::prolog::machine::streams::*;
 
+use std::mem;
 use std::ops::IndexMut;
 
 type Trail = Vec<(Ref, HeapCellValue)>;
@@ -11,12 +13,13 @@ pub enum AttrVarPolicy {
     StripAttributes
 }
 
-pub(crate) trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
-    fn threshold(&self) -> usize;
-    fn push(&mut self, val: HeapCellValue);
-    fn store(&self, val: Addr) -> Addr;
+pub(crate)
+trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
     fn deref(&self, val: Addr) -> Addr;
+    fn push(&mut self, val: HeapCellValue);
     fn stack(&mut self) -> &mut Stack;
+    fn store(&self, val: Addr) -> Addr;
+    fn threshold(&self) -> usize;
 }
 
 pub(crate)
@@ -75,15 +78,15 @@ impl<T: CopierTarget> CopyTermState<T> {
         *self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(threshold));
 
         let ra = self.target[addr].as_addr(threshold);
-        let rd = self.target.store(self.target.deref(ra.clone()));
+        let rd = self.target.store(self.target.deref(ra));
 
-        self.target.push(HeapCellValue::Addr(ra.clone()));
+        self.target.push(HeapCellValue::Addr(ra));
 
         let hcv = HeapCellValue::Addr(self.target[addr + 1].as_addr(addr + 1));
 
         self.target.push(hcv);
 
-        match rd.clone() {
+        match rd {
             Addr::AttrVar(h) | Addr::HeapCell(h)
                 if h >= self.old_h => {
                     self.target[threshold] = HeapCellValue::Addr(rd)
@@ -129,18 +132,18 @@ impl<T: CopierTarget> CopyTermState<T> {
 
     fn copy_partial_string(&mut self, addr: usize, n: usize) {
         let threshold = self.target.threshold();
+        let tail_addr = self.target[addr + 1].as_addr(addr + 1);
+
+        let trail_item = mem::replace(
+            &mut self.target[addr + 1],
+            HeapCellValue::Addr(Addr::PStrLocation(threshold, 0)),
+        );
 
         self.trail.push((
             Ref::HeapCell(addr + 1),
-            self.target[addr + 1].clone(),
+            trail_item,
         ));
 
-        let tail_addr = self.target[addr + 1].as_addr(addr + 1);
-
-        self.target[addr + 1] = HeapCellValue::Addr(
-            Addr::PStrLocation(threshold, 0)
-        );
-
         let pstr =
             match &self.target[addr] {
                 HeapCellValue::PartialString(ref pstr) => {
@@ -205,7 +208,7 @@ impl<T: CopierTarget> CopyTermState<T> {
                 if let AttrVarPolicy::DeepCopy = self.attr_var_policy {
                     self.target.push(HeapCellValue::Addr(Addr::AttrVar(threshold)));
 
-                    let list_val = self.target[h + 1].clone();
+                    let list_val = self.target[h + 1].context_free_clone();
                     self.target.push(list_val);
                 }
             }
@@ -214,9 +217,9 @@ impl<T: CopierTarget> CopyTermState<T> {
     }
 
     fn copy_var(&mut self, addr: Addr) {
-        let rd = self.target.store(self.target.deref(addr.clone()));
+        let rd = self.target.store(self.target.deref(addr));
 
-        match rd.clone() {
+        match rd {
             Addr::AttrVar(h) | Addr::HeapCell(h) if h >= self.old_h => {
                 *self.value_at_scan() = HeapCellValue::Addr(rd);
                 self.scan += 1;
@@ -232,7 +235,7 @@ impl<T: CopierTarget> CopyTermState<T> {
     }
 
     fn copy_structure(&mut self, addr: usize) {
-        match self.target[addr].clone() {
+        match self.target[addr].context_free_clone() {
             HeapCellValue::NamedStr(arity, name, fixity) => {
                 let threshold = self.target.threshold();
 
@@ -247,7 +250,7 @@ impl<T: CopierTarget> CopyTermState<T> {
                 self.target.push(HeapCellValue::NamedStr(arity, name, fixity));
 
                 for i in 0..arity {
-                    let hcv = self.target[addr + 1 + i].clone();
+                    let hcv = self.target[addr + 1 + i].context_free_clone();
                     self.target.push(hcv);
                 }
             }
@@ -266,13 +269,18 @@ impl<T: CopierTarget> CopyTermState<T> {
 
         while self.scan < self.target.threshold() {
             match self.value_at_scan() {
-                HeapCellValue::NamedStr(..) => {
-                    self.scan += 1;
-                }
-                HeapCellValue::Addr(ref addr) => {
-                    match addr.clone() {
-                        Addr::Lis(addr) => {
-                            self.copy_list(addr);
+                &mut HeapCellValue::Addr(addr) => {
+                    match addr {
+                        Addr::Con(h) => {
+                            self.target.push(self.target[h].context_free_clone());
+                            self.scan += 1;
+                        }
+                        Addr::Stream(_) => {
+                            self.target.push(HeapCellValue::Stream(Stream::null_stream()));
+                            self.scan += 1;
+                        }
+                        Addr::Lis(h) => {
+                            self.copy_list(h);
                         }
                         addr @ Addr::AttrVar(_)
                       | addr @ Addr::HeapCell(_)
@@ -285,12 +293,12 @@ impl<T: CopierTarget> CopyTermState<T> {
                         Addr::PStrLocation(addr, n) => {
                             self.copy_partial_string_from(addr, n);
                         }
-                        Addr::Con(_) | Addr::DBRef(_) | Addr::Stream(_) => {
+                        _ => {
                             self.scan += 1;
                         }
                     }
                 }
-                HeapCellValue::PartialString(_) => {
+                _ => {
                     self.scan += 1;
                 }
             }
index e5151589bdb9b092182a99edd2658e948ccc866e..e82aaa048099396ebf69dbfe3e67940135da9f24 100644 (file)
@@ -41,12 +41,22 @@ impl Machine {
         let arity = self.machine_st[arity].clone();
 
         let name = match self.machine_st.store(self.machine_st.deref(name)) {
-            Addr::Con(Constant::Atom(name, _)) => name,
+            Addr::Con(h) =>
+                if let HeapCellValue::Atom(ref name, _) = &self.machine_st.heap[h] {
+                    name.clone()
+                } else {
+                    unreachable!()
+                },
             _ => unreachable!(),
         };
 
         let arity = match self.machine_st.store(self.machine_st.deref(arity)) {
-            Addr::Con(Constant::Integer(arity)) => arity.to_usize().unwrap(),
+            Addr::Con(h) =>
+                if let HeapCellValue::Integer(ref arity) = &self.machine_st.heap[h] {
+                    arity.to_usize().unwrap()
+                } else {
+                    unreachable!()
+                },
             _ => unreachable!(),
         };
 
@@ -91,7 +101,7 @@ impl Machine {
         let (name, arity) = self.get_predicate_key(name, arity);
 
         self.make_undefined(name.clone(), arity);
-
+        
         self.indices.remove_code_index((name.clone(), arity));
         self.indices.remove_clause_subsection(name.owning_module(), name, arity);
     }
@@ -101,16 +111,21 @@ impl Machine {
         let module_addr = self.machine_st[module].clone();
 
         let module_name = match self.machine_st.store(self.machine_st.deref(module_addr)) {
-            Addr::Con(Constant::Atom(module, _)) => match self.indices.modules.get_mut(&module) {
-                Some(ref mut module) => {
-                    module.code_dir.remove(&(name.clone(), arity));
-                    module.module_decl.name.clone()
-                }
-                _ => {
-                    self.machine_st.fail = true;
-                    return;
-                }
-            },
+            Addr::Con(h) =>
+                if let HeapCellValue::Atom(ref module, _) = &self.machine_st.heap[h] {
+                    match self.indices.modules.get_mut(module) {
+                        Some(ref mut module) => {
+                            module.code_dir.remove(&(name.clone(), arity));
+                            module.module_decl.name.clone()
+                        }
+                        _ => {
+                            self.machine_st.fail = true;
+                            return;
+                        }
+                    }
+                } else {
+                    unreachable!()
+                },
             _ => unreachable!(),
         };
 
@@ -162,21 +177,34 @@ impl Machine {
                 place.push_to_queue(&mut addrs, added_clause);
                 self.print_new_dynamic_clause(addrs, name.clone(), arity)
             }
-            Err(err) => return self.machine_st.throw_exception(err),
+            Err(err) => {
+                return self.machine_st.throw_exception(err);
+            }
         };
 
-        self.handle_eval_result_from_dynamic_compile(pred_str, name, arity, place.predicate_name());
+        self.handle_eval_result_from_dynamic_compile(
+            pred_str,
+            name,
+            arity,
+            place.predicate_name(),
+        );
     }
 
     fn set_module_atom_tbl(&mut self, module_addr: Addr, name: &mut ClauseName) -> bool {
         let atom_tbl = match self.machine_st.store(self.machine_st.deref(module_addr)) {
-            Addr::Con(Constant::Atom(module, _)) => match self.indices.modules.get(&module) {
-                Some(ref module) => module.atom_tbl.clone(),
-                None => {
+            Addr::Con(h) =>
+                if let HeapCellValue::Atom(ref module, _) = &self.machine_st.heap[h] {
+                    match self.indices.modules.get(module) {
+                        Some(ref module) => module.atom_tbl.clone(),
+                        None => {
+                            self.machine_st.fail = true;
+                            return false;
+                        }
+                    }
+                } else {
                     self.machine_st.fail = true;
                     return false;
-                }
-            },
+                },
             _ => unreachable!(),
         };
 
@@ -204,7 +232,12 @@ impl Machine {
     fn retract_from_dynamic_predicate_in_module(&mut self) {
         let index = self.machine_st[temp_v!(3)].clone();
         let index = match self.machine_st.store(self.machine_st.deref(index)) {
-            Addr::Con(Constant::Integer(n)) => n.to_usize().unwrap(),
+            Addr::Con(h) =>
+                if let HeapCellValue::Integer(ref n) = &self.machine_st.heap[h] {
+                    n.to_usize().unwrap()
+                } else {
+                    unreachable!()
+                },
             _ => unreachable!(),
         };
 
@@ -224,7 +257,9 @@ impl Machine {
                     
                     self.print_new_dynamic_clause(addrs, name.clone(), arity)
                 }
-                Err(err) => return self.machine_st.throw_exception(err),
+                Err(err) => {
+                    return self.machine_st.throw_exception(err);
+                }
             };
 
             self.handle_eval_result_from_dynamic_compile(
@@ -239,8 +274,15 @@ impl Machine {
     fn retract_from_dynamic_predicate(&mut self) {
         let index = self.machine_st[temp_v!(3)].clone();
         let index = match self.machine_st.store(self.machine_st.deref(index)) {
-            Addr::Con(Constant::Integer(n)) => n.to_usize().unwrap(),
-            _ => unreachable!(),
+            Addr::Con(h) =>
+                if let HeapCellValue::Integer(n) = &self.machine_st.heap[h] {
+                    n.to_usize().unwrap()
+                } else {
+                    unreachable!()
+                },
+            _ => {
+                unreachable!()
+            }
         };
 
         let (name, arity) = self.get_predicate_key(temp_v!(1), temp_v!(2));
@@ -257,7 +299,9 @@ impl Machine {
                  
                 self.print_new_dynamic_clause(addrs, name.clone(), arity)
             }
-            Err(err) => return self.machine_st.throw_exception(err),
+            Err(err) => {
+                return self.machine_st.throw_exception(err);
+            }
         };
 
         self.handle_eval_result_from_dynamic_compile(
index 17ccd3878f1179a7e328b7cecfa747b75aff84ee..d8d39036c7f2f88c5bda84513ef0cad3260c48d0 100644 (file)
@@ -1,6 +1,6 @@
 use core::marker::PhantomData;
 
-use crate::prolog_parser::ast::*;
+use crate::prolog_parser::ast::Constant;
 
 use crate::prolog::machine::machine_indices::*;
 use crate::prolog::machine::partial_string::*;
@@ -140,17 +140,175 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
 
     #[inline]
     pub(crate)
-    fn push(&mut self, val: HeapCellValue) {
+    fn clone(&self, h: usize) -> HeapCellValue {
+        match &self[h] {
+            &HeapCellValue::Addr(addr) => {
+                HeapCellValue::Addr(addr)
+            }
+            &HeapCellValue::Atom(ref name, ref op) => {
+                HeapCellValue::Atom(name.clone(), op.clone())
+            }
+            &HeapCellValue::DBRef(ref db_ref) => {
+                HeapCellValue::DBRef(db_ref.clone())
+            }
+            &HeapCellValue::Integer(ref n) => {
+                HeapCellValue::Integer(n.clone())
+            }
+            &HeapCellValue::NamedStr(arity, ref name, ref op) => {
+                HeapCellValue::NamedStr(arity, name.clone(), op.clone())
+            }
+            &HeapCellValue::Rational(ref r) => {
+                HeapCellValue::Rational(r.clone())
+            }
+            &HeapCellValue::PartialString(_) => {
+                HeapCellValue::Addr(Addr::PStrLocation(h, 0))
+            }
+            &HeapCellValue::Stream(_) => {
+                HeapCellValue::Addr(Addr::Stream(h))
+            }
+        }
+    }
+
+    #[inline]
+    pub(crate)
+    fn put_constant(&mut self, c: Constant) -> Addr {
+        match c {
+            Constant::Atom(name, op) => {
+                Addr::Con(self.push(HeapCellValue::Atom(name, op)))
+            }
+            Constant::Char(c) => {
+                self.push(HeapCellValue::Addr(Addr::Char(c)));
+                Addr::Char(c)
+            }
+            Constant::CharCode(c) => {
+                self.push(HeapCellValue::Addr(Addr::CharCode(c)));
+                Addr::CharCode(c)
+            }
+            Constant::CutPoint(cp) => {
+                self.push(HeapCellValue::Addr(Addr::CutPoint(cp)));
+                Addr::CutPoint(cp)
+            }
+            Constant::EmptyList => {
+                self.push(HeapCellValue::Addr(Addr::EmptyList));
+                Addr::EmptyList
+            }
+            Constant::Integer(n) => {
+                Addr::Con(self.push(HeapCellValue::Integer(n)))                
+            }
+            Constant::Rational(r) => {
+                Addr::Con(self.push(HeapCellValue::Rational(r)))
+            }
+            Constant::Float(f) => {
+                self.push(HeapCellValue::Addr(Addr::Float(f)));
+                Addr::Float(f)
+            }
+            Constant::String(s) => {
+                let addr = self.allocate_pstr(&s);
+                let h = self.h();
+                
+                self[h - 1] = HeapCellValue::Addr(Addr::EmptyList);
+                addr
+            }
+            Constant::Usize(n) => {
+                self.push(HeapCellValue::Addr(Addr::Usize(n)));
+                Addr::Usize(n)
+            }
+        }
+    }
+
+    #[inline]
+    pub(crate)
+    fn push(&mut self, val: HeapCellValue) -> usize {
+        let h = self.h();
+
         unsafe {
             let new_top = self.buf.new_block(mem::size_of::<HeapCellValue>());
             ptr::write(self.buf.top as *mut _, val);
             self.buf.top = new_top;
         }
+
+        h
+    }
+
+    #[inline]
+    pub(crate)
+    fn rational_at(&self, h: usize) -> bool {
+        if let HeapCellValue::Rational(_) = &self[h] {
+            true
+        } else {
+            false
+        }
+    }
+    
+    #[inline]
+    pub(crate)
+    fn integer_at(&self, h: usize) -> bool {
+        if let HeapCellValue::Integer(_) = &self[h] {
+            true
+        } else {
+            false
+        }
+    }
+
+    #[inline]
+    pub(crate)
+    fn atom_at(&self, h: usize) -> bool {
+        if let HeapCellValue::Atom(..) = &self[h] {
+            true
+        } else {
+            false
+        }
     }
 
     #[inline]
     pub(crate)
-    fn allocate_pstr(&mut self, mut src: &str) -> Option<Addr> {
+    fn to_unifiable(&mut self, non_heap_value: HeapCellValue) -> Addr {
+        match non_heap_value {
+            HeapCellValue::Addr(addr) => {
+                addr
+            }
+            val @ HeapCellValue::Atom(..)
+          | val @ HeapCellValue::Integer(_)
+          | val @ HeapCellValue::DBRef(_)
+          | val @ HeapCellValue::Rational(_) => {
+                Addr::Con(self.push(val))
+            }
+            val @ HeapCellValue::NamedStr(..) => {
+                Addr::Str(self.push(val))
+            }
+            val @ HeapCellValue::Stream(..) => {
+                Addr::Stream(self.push(val))
+            }
+            val @ HeapCellValue::PartialString(_) => {
+                let h = self.push(val);
+                self.push(HeapCellValue::Addr(Addr::EmptyList));
+
+                Addr::Con(h)
+            }
+        }
+    }
+
+    #[inline]
+    pub(crate)
+    fn allocate_pstr(&mut self, src: &str) -> Addr {
+        self.write_pstr(src)
+            .unwrap_or_else(|| {
+                let h = self.h();
+
+                self.push(HeapCellValue::PartialString(
+                    PartialString::empty()
+                ));
+
+                self.push(HeapCellValue::Addr(
+                    Addr::HeapCell(h + 1)
+                ));
+
+                Addr::PStrLocation(h, 0)
+            })
+    }
+
+    #[inline]
+    fn write_pstr(&mut self, mut src: &str) -> Option<Addr> {
         let orig_h = self.h();
 
         loop {
@@ -245,17 +403,21 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
     }
 
     pub(crate)
-    fn to_list<Iter: Iterator<Item = Addr>>(&mut self, values: Iter) -> usize {
+    fn to_list<Iter, SrcT>(&mut self, values: Iter) -> usize
+        where Iter: Iterator<Item = SrcT>,
+              SrcT: Into<HeapCellValue>
+    {
         let head_addr = self.h();
+        let mut h = head_addr;
 
-        for value in values {
-            let h = self.h();
-
+        for value in values.map(|v| v.into()) {
             self.push(HeapCellValue::Addr(Addr::Lis(h + 1)));
-            self.push(HeapCellValue::Addr(value));
+            self.push(value);
+
+            h += mem::size_of::<HeapCellValue>() * 2;
         }
 
-        self.push(HeapCellValue::Addr(Addr::Con(Constant::EmptyList)));
+        self.push(HeapCellValue::Addr(Addr::EmptyList));
 
         head_addr
     }
@@ -286,8 +448,8 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
     pub(crate)
     fn to_local_code_ptr(&self, addr: &Addr) -> Option<LocalCodePtr> {
         let extract_integer = |s: usize| -> Option<usize> {
-            match self[s].as_addr(s) {
-                Addr::Con(Constant::Integer(n)) => n.to_usize(),
+            match &self[s] {
+                &HeapCellValue::Integer(ref n) => n.to_usize(),
                 _ => None
             }
         };
@@ -327,6 +489,19 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
             _ => None
         }
     }
+
+    #[inline]
+    pub
+    fn index_addr<'a>(&'a self, addr: &Addr) -> RefOrOwned<'a, HeapCellValue> {
+        match addr {
+            &Addr::Con(h) | &Addr::Str(h) | &Addr::Stream(h) => {
+                RefOrOwned::Borrowed(&self[h])
+            }
+            addr => {
+                RefOrOwned::Owned(HeapCellValue::Addr(*addr))
+            }
+        }
+    }
 }
 
 impl<T: RawBlockTraits> Index<usize> for HeapTemplate<T> {
index 315e1eeec7ecc32cac40ab06f7b1d33a48768348..0a352a660ee64ef02b3bd58894deed8986b41c7d 100644 (file)
@@ -1,6 +1,7 @@
 use prolog_parser::ast::*;
 
-use crate::prolog::forms::PredicateKey;
+use crate::prolog::forms::{Number, PredicateKey};
+use crate::prolog::machine::heap::*;
 use crate::prolog::machine::machine_indices::*;
 use crate::prolog::machine::machine_state::*;
 use crate::prolog::rug::Integer;
@@ -21,35 +22,118 @@ pub(super) struct MachineError {
     from: ErrorProvenance,
 }
 
-impl MachineError {
-    pub(super) fn functor_stub(name: ClauseName, arity: usize) -> MachineStub {
-        let name = HeapCellValue::Addr(Addr::Con(Constant::Atom(name, None)));
-        functor!(
-            "/",
-            2,
-            [name, heap_integer!(Integer::from(arity))],
-            SharedOpDesc::new(400, YFX)
-        )
+pub(super)
+trait TypeError {
+    fn type_error(self, h: usize, valid_type: ValidType) -> MachineError;
+}
+
+impl TypeError for Addr {
+    fn type_error(self, _: usize, valid_type: ValidType) -> MachineError {
+        let stub = functor!(
+            "type_error",
+            [atom(valid_type.as_str()), addr(self)]
+        );
+
+        MachineError {
+            stub,
+            location: None,
+            from: ErrorProvenance::Received
+        }
     }
+}
+
+impl TypeError for MachineStub {
+    fn type_error(self, h: usize, valid_type: ValidType) -> MachineError {
+        let stub = functor!(
+            "type_error",
+            [atom(valid_type.as_str()), aux(h, 0)],
+            [self]
+        );
 
-    pub(super) fn evaluation_error(eval_error: EvalError) -> Self {
-        let stub = functor!("evaluation_error", 1, [heap_atom!(eval_error.as_str())]);
         MachineError {
             stub,
             location: None,
-            from: ErrorProvenance::Received,
+            from: ErrorProvenance::Constructed
         }
     }
+}
 
-    pub(super)
-    fn type_error(valid_type: ValidType, culprit: Addr) -> Self {
+impl TypeError for Number {
+    fn type_error(self, _h: usize, valid_type: ValidType) -> MachineError {
         let stub = functor!(
             "type_error",
-            2,
-            [
-                heap_atom!(valid_type.as_str()),
-                HeapCellValue::Addr(culprit)
-            ]
+            [atom(valid_type.as_str()), number(self)]
+        );
+
+        MachineError {
+            stub,
+            location: None,
+            from: ErrorProvenance::Received
+        }
+    }
+}
+
+pub(super)
+trait PermissionError {
+    fn permission_error(self, h: usize, index_str: &'static str, perm: Permission) -> MachineError;
+}
+
+impl PermissionError for Addr {
+    fn permission_error(self, _: usize, index_str: &'static str, perm: Permission) -> MachineError {
+        let stub = functor!(
+            "permission_error",
+            [atom(perm.as_str()), atom(index_str), addr(self)]
+        );
+
+        MachineError {
+            stub,
+            location: None,
+            from: ErrorProvenance::Received
+        }
+    }
+}
+
+impl PermissionError for MachineStub {
+    fn permission_error(self, h: usize, index_str: &'static str, perm: Permission) -> MachineError {
+        let stub = functor!(
+            "permission_error",
+            [atom(perm.as_str()), atom(index_str), aux(h, 0)],
+            [self]
+        );
+
+        MachineError {
+            stub,
+            location: None,
+            from: ErrorProvenance::Constructed
+        }
+    }
+}
+
+pub(super)
+trait DomainError {
+    fn domain_error(self, error: DomainErrorType) -> MachineError;
+}
+
+impl DomainError for Addr {
+    fn domain_error(self, error: DomainErrorType) -> MachineError {
+        let stub = functor!(
+            "domain_error",
+            [atom(error.as_str()), addr(self)]
+        );
+
+        MachineError {
+            stub,
+            location: None,
+            from: ErrorProvenance::Received,
+        }
+    }
+}
+
+impl DomainError for Number {
+    fn domain_error(self, error: DomainErrorType) -> MachineError {
+        let stub = functor!(
+            "domain_error",
+            [atom(error.as_str()), number(self)]
         );
 
         MachineError {
@@ -58,6 +142,31 @@ impl MachineError {
             from: ErrorProvenance::Received,
         }
     }
+}
+
+impl MachineError {
+    pub(super) fn functor_stub(name: ClauseName, arity: usize) -> MachineStub {
+        functor!(
+            "/",
+            SharedOpDesc::new(400, YFX),
+            [clause_name(name), integer(arity)]
+        )
+    }
+
+    pub(super) fn evaluation_error(eval_error: EvalError) -> Self {
+        let stub = functor!("evaluation_error", [atom(eval_error.as_str())]);
+
+        MachineError {
+            stub,
+            location: None,
+            from: ErrorProvenance::Received,
+        }
+    }
+
+    pub(super)
+    fn type_error<T: TypeError>(h: usize, valid_type: ValidType, culprit: T) -> Self {
+        culprit.type_error(h, valid_type)
+    }
 
     pub(super)
     fn module_resolution_error(
@@ -66,31 +175,24 @@ impl MachineError {
         name: ClauseName,
         arity: usize,
     ) -> Self {
-        let mod_name = HeapCellValue::Addr(Addr::Con(Constant::Atom(mod_name, None)));
-        let name = HeapCellValue::Addr(Addr::Con(Constant::Atom(name, None)));
-
-        let mut stub = functor!(
-            "evaluation_error",
-            1,
-            [HeapCellValue::Addr(Addr::HeapCell(h + 2))]
+        let res_stub = functor!(
+            ":",
+            SharedOpDesc::new(600, XFY),
+            [clause_name(mod_name), clause_name(name)]
         );
 
-        stub.append(&mut functor!(
+        let ind_stub = functor!(
             "/",
-            2,
-            [
-                HeapCellValue::Addr(Addr::HeapCell(h + 2 + 3)),
-                heap_integer!(Integer::from(arity))
-            ],
-            SharedOpDesc::new(400, YFX)
-        ));
-        
-        stub.append(&mut functor!(
-            ":",
-            2,
-            [mod_name, name],
-            SharedOpDesc::new(600, XFY)
-        ));
+            SharedOpDesc::new(400, YFX),
+            [aux(h + 2, 0), integer(arity)],
+            [res_stub]
+        );
+
+        let stub = functor!(
+            "evaluation_error",
+            [aux(h, 0)],
+            [ind_stub]
+        );
 
         MachineError {
             stub,
@@ -103,23 +205,29 @@ impl MachineError {
     fn existence_error(h: usize, err: ExistenceError) -> Self {
         match err {
             ExistenceError::Module(name) => {
-                let name = HeapCellValue::Addr(Addr::Con(Constant::Atom(name, None)));
-                let stub = functor!("existence_error", 2, [heap_atom!("module"), name]);
+                let stub = functor!(
+                    "existence_error",
+                    [atom("module"), clause_name(name)]
+                );
 
                 MachineError {
                     stub,
                     location: None,
-                    from: ErrorProvenance::Constructed,
+                    from: ErrorProvenance::Received,
                 }
             }
             ExistenceError::Procedure(name, arity) => {
-                let mut stub = functor!(
-                    "existence_error",
-                    2,
-                    [heap_atom!("procedure"), heap_str!(3 + h)]
+                let culprit = functor!(
+                    "/",
+                    SharedOpDesc::new(400, YFX),
+                    [clause_name(name), integer(arity)]
                 );
                 
-                stub.append(&mut Self::functor_stub(name, arity));
+                let stub = functor!(
+                    "existence_error",
+                    [atom("procedure"), aux(h, 0)],
+                    [culprit]
+                );
 
                 MachineError {
                     stub,
@@ -127,96 +235,112 @@ impl MachineError {
                     from: ErrorProvenance::Constructed,
                 }
             }
-            ExistenceError::Stream(addr) => {
-                let culprit = HeapCellValue::Addr(addr);
-                let stub = functor!("existence_error", 2, [heap_atom!("stream"), culprit]);
+            ExistenceError::Stream(culprit) => {
+                let stub = functor!(
+                    "existence_error",
+                    [atom("stream"), addr(culprit)]
+                );
 
                 MachineError {
                     stub,
                     location: None,
-                    from: ErrorProvenance::Constructed,
+                    from: ErrorProvenance::Received,
                 }
             }
         }
     }
 
     pub(super)
-    fn session_error(h: usize, err: SessionError) -> Self {
+    fn permission_error<T: PermissionError>(
+        h: usize,
+        err: Permission,
+        index_str: &'static str,
+        culprit: T,
+    ) -> Self {
+        culprit.permission_error(
+            h,
+            index_str,
+            err,
+        )
+    }
+
+    fn arithmetic_error(h: usize, err: ArithmeticError) -> Self {
         match err {
-            SessionError::ParserError(err) => Self::syntax_error(h, err),
-            SessionError::CannotOverwriteBuiltIn(pred_str)
-          | SessionError::CannotOverwriteImport(pred_str) => {
-                Self::permission_error(
-                    PermissionError::Modify, 
-                    "private_procedure", 
-                    Addr::Con(Constant::Atom(pred_str, None)),
-                )
+            ArithmeticError::UninstantiatedVar => {
+                Self::instantiation_error()
             }
-            SessionError::InvalidFileName(filename) => {
-                Self::existence_error(h, ExistenceError::Module(filename))
-            }
-            SessionError::ModuleDoesNotContainExport(..) => Self::permission_error(
-                PermissionError::Access,
-                "private_procedure",
-                Addr::Con(atom!("module_does_not_contain_claimed_export")),
-            ),
-            SessionError::ModuleNotFound => Self::permission_error(
-                PermissionError::Access,
-                "private_procedure",
-                Addr::Con(atom!("module_does_not_exist")),
-            ),
-            SessionError::OpIsInfixAndPostFix(op) => {
-                Self::permission_error(
-                    PermissionError::Create,
-                    "operator",
-                    Addr::Con(Constant::Atom(op, None)),
-                )
+            ArithmeticError::NonEvaluableFunctor(name, arity) => {
+                let culprit = functor!(
+                    "/",
+                    SharedOpDesc::new(400, YFX),
+                    [constant(h, &name), integer(arity)]
+                );
+
+                Self::type_error(h, ValidType::Evaluable, culprit)
             }
-            _ => unreachable!(),
         }
     }
 
+    #[inline]
     pub(super)
-    fn permission_error(
-        err: PermissionError,
-        index_str: &'static str,
-        culprit: Addr,
-    ) -> Self {
-        let culprit = HeapCellValue::Addr(culprit);
-
-        let err = vec![heap_atom!(err.as_str()), heap_atom!(index_str), culprit];
-        let mut stub = functor!("permission_error", 3);
-
-        stub.extend(err.into_iter());
+    fn domain_error<T: DomainError>(error: DomainErrorType, culprit: T) -> Self {
+        culprit.domain_error(error)
+    }
+    
+    pub(super)
+    fn instantiation_error() -> Self {
+        let stub = functor!("instantiation_error");
 
         MachineError {
             stub,
             location: None,
-            from: ErrorProvenance::Constructed,
+            from: ErrorProvenance::Received,
         }
     }
 
-    fn arithmetic_error(h: usize, err: ArithmeticError) -> Self {
+    pub(super)
+    fn session_error(h: usize, err: SessionError) -> Self {
         match err {
-            ArithmeticError::UninstantiatedVar => Self::instantiation_error(),
-            ArithmeticError::NonEvaluableFunctor(name, arity) => {
-                let name = HeapCellValue::Addr(Addr::Con(name));
-                let culprit = functor!(
-                    "/",
-                    2,
-                    [name, heap_integer!(Integer::from(arity))],
-                    SharedOpDesc::new(400, YFX)
-                );
-
-                let mut stub = Self::type_error(ValidType::Evaluable, Addr::HeapCell(3 + h)).stub;
-                stub.extend(culprit.into_iter());
-
-                MachineError {
-                    stub,
-                    location: None,
-                    from: ErrorProvenance::Constructed,
-                }
+            SessionError::ParserError(err) => {
+                Self::syntax_error(h, err)
             }
+            SessionError::CannotOverwriteBuiltIn(pred_str)
+          | SessionError::CannotOverwriteImport(pred_str) => {
+                Self::permission_error(
+                    h,
+                    Permission::Modify,
+                    "private_procedure",
+                    functor!(clause_name(pred_str)),
+                )
+            }
+            SessionError::InvalidFileName(filename) => {
+                Self::existence_error(h, ExistenceError::Module(filename))
+            }
+            SessionError::ModuleDoesNotContainExport(..) => {
+                Self::permission_error(
+                    h,
+                    Permission::Access,
+                    "private_procedure",
+                    functor!("module_does_not_contain_claimed_export"),
+                )
+            }
+            SessionError::ModuleNotFound => {
+                Self::permission_error(
+                    h,
+                    Permission::Access,
+                    "private_procedure",
+                    functor!("modules_does_not_exist"),
+                )
+            }
+            SessionError::OpIsInfixAndPostFix(op) => {
+                Self::permission_error(
+                    h,
+                    Permission::Create,
+                    "operator",
+                    functor!(clause_name(op)),
+                )
+            }
+            _ => unreachable!(),
         }
     }
 
@@ -227,50 +351,25 @@ impl MachineError {
         }
 
         let location = err.line_and_col_num();
-        let err = vec![heap_atom!(err.as_str())];
-
-        let mut stub = if err.len() == 1 {
-            functor!("syntax_error", 1)
-        } else {
-            functor!("syntax_error", 1, [heap_str!(h + 2)])
-        };
-
-        stub.extend(err.into_iter());
-
-        MachineError {
-            stub,
-            location,
-            from: ErrorProvenance::Constructed,
-        }
-    }
-
-    pub(super)
-    fn domain_error(error: DomainError, culprit: Addr) -> Self {
+        let stub = functor!(err.as_str());
+        
         let stub = functor!(
-            "domain_error",
-            2,
-            [heap_atom!(error.as_str()), HeapCellValue::Addr(culprit)]
+            "syntax_error",
+            [aux(h, 0)],
+            [stub]
         );
-        MachineError {
-            stub,
-            location: None,
-            from: ErrorProvenance::Received,
-        }
-    }
 
-    pub(super)
-    fn instantiation_error() -> Self {
-        let stub = functor!("instantiation_error");
         MachineError {
             stub,
-            location: None,
-            from: ErrorProvenance::Received,
+            location,
+            from: ErrorProvenance::Constructed,
         }
     }
 
     pub(super)
     fn representation_error(flag: RepFlag) -> Self {
-        let stub = functor!("representation_error", 1, [heap_atom!(flag.as_str())]);
+        let stub = functor!("representation_error", [atom(flag.as_str())]);
+
         MachineError {
             stub,
             location: None,
@@ -296,7 +395,7 @@ impl MachineError {
 }
 
 #[derive(Clone, Copy)]
-pub enum PermissionError {
+pub enum Permission {
     Access,
     Create,
     InputStream,
@@ -304,14 +403,14 @@ pub enum PermissionError {
     OutputStream,
 }
 
-impl PermissionError {
+impl Permission {
     pub fn as_str(self) -> &'static str {
         match self {
-            PermissionError::Access => "access",
-            PermissionError::Create => "create",
-            PermissionError::InputStream => "input",
-            PermissionError::Modify => "modify",
-            PermissionError::OutputStream => "output",
+            Permission::Access => "access",
+            Permission::Create => "create",
+            Permission::InputStream => "input",
+            Permission::Modify => "modify",
+            Permission::OutputStream => "output",
         }
     }
 }
@@ -363,18 +462,18 @@ impl ValidType {
 }
 
 #[derive(Clone, Copy)]
-pub enum DomainError {
+pub enum DomainErrorType {
     NotLessThanZero,
     Stream,
     StreamOrAlias,
 }
 
-impl DomainError {
+impl DomainErrorType {
     pub fn as_str(self) -> &'static str {
         match self {
-            DomainError::NotLessThanZero => "not_less_than_zero",
-            DomainError::Stream => "stream",
-            DomainError::StreamOrAlias => "stream_or_alias",
+            DomainErrorType::NotLessThanZero => "not_less_than_zero",
+            DomainErrorType::Stream => "stream",
+            DomainErrorType::StreamOrAlias => "stream_or_alias",
         }
     }
 }
@@ -424,20 +523,20 @@ impl EvalError {
 }
 
 // used by '$skip_max_list'.
+#[derive(Clone, Copy)]
 pub(super) enum CycleSearchResult {
     EmptyList,
     NotList,
-    PartialList(usize, Ref),  // the list length (up to max), and an offset into the heap.
-    ProperList(usize),          // the list length.
-    CompleteString(usize, Rc<String>), // the string length (in bytes), the string.
-    UntouchedString(usize, Rc<String>),    // the cut off, past which is the untouched string.
+    PartialList(usize, Ref),           // the list length (up to max), and an offset into the heap.
+    ProperList(usize),                 // the list length.
     PStrLocation(usize, usize, usize), // the list length (up to max), the heap offset, byte offset into the string.
-    UntouchedList(usize),       // the address of an uniterated Addr::Lis(address).
+    UntouchedList(usize),              // the address of an uniterated Addr::Lis(address).
 }
 
 impl MachineState {
     // see 8.4.3 of Draft Technical Corrigendum 2.
-    pub(super) fn check_sort_errors(&self) -> CallResult {
+    pub(super)
+    fn check_sort_errors(&self) -> CallResult {
         let stub = MachineError::functor_stub(clause_name!("sort"), 2);
         let list = self.store(self.deref(self[temp_v!(1)].clone()));
         let sorted = self.store(self.deref(self[temp_v!(2)].clone()));
@@ -447,14 +546,14 @@ impl MachineState {
                 return Err(self.error_form(MachineError::instantiation_error(), stub))
             }
             CycleSearchResult::NotList => {
-                return Err(self.error_form(MachineError::type_error(ValidType::List, list), stub))
+                return Err(self.error_form(MachineError::type_error(0, ValidType::List, list), stub))
             }
             _ => {}
         };
 
         match self.detect_cycles(sorted.clone()) {
             CycleSearchResult::NotList if !sorted.is_ref() => {
-                Err(self.error_form(MachineError::type_error(ValidType::List, sorted), stub))
+                Err(self.error_form(MachineError::type_error(0, ValidType::List, sorted), stub))
             }
             _ => Ok(()),
         }
@@ -465,7 +564,7 @@ impl MachineState {
 
         match self.detect_cycles(list.clone()) {
             CycleSearchResult::NotList if !list.is_ref() => {
-                Err(self.error_form(MachineError::type_error(ValidType::List, list), stub))
+                Err(self.error_form(MachineError::type_error(0, ValidType::List, list), stub))
             }
             _ => {
                 let mut addr = list;
@@ -474,18 +573,23 @@ impl MachineState {
                     let mut new_l = l;
 
                     loop {
-                        match self.heap[new_l].clone() {
-                            HeapCellValue::Addr(Addr::Str(l)) => new_l = l,
+                        match self.heap.clone(new_l) {
+                            HeapCellValue::Addr(Addr::Str(l)) => {
+                                new_l = l;
+                            }
                             HeapCellValue::NamedStr(2, ref name, Some(_))
-                                if name.as_str() == "-" =>
-                            {
-                                break
+                                if name.as_str() == "-" => {
+                                break;
+                            }
+                            HeapCellValue::Addr(Addr::HeapCell(_)) => {
+                                break;
+                            }
+                            HeapCellValue::Addr(Addr::StackCell(..)) => {
+                                break;
                             }
-                            HeapCellValue::Addr(Addr::HeapCell(_)) => break,
-                            HeapCellValue::Addr(Addr::StackCell(..)) => break,
                             _ => {
                                 return Err(self.error_form(
-                                    MachineError::type_error(ValidType::Pair, Addr::HeapCell(l)),
+                                    MachineError::type_error(0, ValidType::Pair, Addr::HeapCell(l)),
                                     stub,
                                 ))
                             }
@@ -501,9 +605,11 @@ impl MachineState {
     }
 
     // see 8.4.4 of Draft Technical Corrigendum 2.
-    pub(super) fn check_keysort_errors(&self) -> CallResult {
+    pub(super)
+    fn check_keysort_errors(&self) -> CallResult {
         let stub = MachineError::functor_stub(clause_name!("keysort"), 2);
-        let pairs = self.store(self.deref(self[temp_v!(1)].clone()));
+        
+        let pairs  = self.store(self.deref(self[temp_v!(1)].clone()));
         let sorted = self.store(self.deref(self[temp_v!(2)].clone()));
 
         match self.detect_cycles(pairs.clone()) {
@@ -511,7 +617,7 @@ impl MachineState {
                 Err(self.error_form(MachineError::instantiation_error(), stub))
             }
             CycleSearchResult::NotList => {
-                Err(self.error_form(MachineError::type_error(ValidType::List, pairs), stub))
+                Err(self.error_form(MachineError::type_error(0, ValidType::List, pairs), stub))
             }
             _ => Ok(()),
         }?;
@@ -519,7 +625,8 @@ impl MachineState {
         self.check_for_list_pairs(sorted)
     }
 
-    pub(super) fn error_form(&self, err: MachineError, src: MachineStub) -> MachineStub {
+    pub(super)
+    fn error_form(&self, err: MachineError, src: MachineStub) -> MachineStub {
         let location = err.location;
         let err_len = err.len();
 
@@ -535,21 +642,17 @@ impl MachineState {
         if let Some((line_num, _)) = location {
             let colon_op_desc = Some(SharedOpDesc::new(600, XFY));
 
-            stub.extend(
-                vec![
-                    HeapCellValue::NamedStr(2, clause_name!(":"), colon_op_desc),
-                    HeapCellValue::Addr(Addr::HeapCell(h + 6 + err_len)),
-                    heap_integer!(Integer::from(line_num)),
-                ]
-                .into_iter(),
-            );
+            stub.push(HeapCellValue::NamedStr(2, clause_name!(":"), colon_op_desc));
+            stub.push(HeapCellValue::Addr(Addr::HeapCell(h + 6 + err_len)));
+            stub.push(HeapCellValue::Integer(Rc::new(Integer::from(line_num))));
         }
 
         stub.extend(src.into_iter());
         stub
     }
 
-    pub(super) fn throw_exception(&mut self, err: MachineStub) {
+    pub(super)
+    fn throw_exception(&mut self, err: MachineStub) {
         let h = self.heap.h();
 
         self.ball.boundary = 0;
@@ -602,4 +705,5 @@ impl From<ParserError> for EvalSession {
     fn from(err: ParserError) -> Self {
         EvalSession::from(SessionError::ParserError(err))
     }
+
 }
index e0c26d5a1b84cd04a616d8b6988a447644d9dd97..b2dff1592bd3f460e3cbf831e22fc1144436bb98 100644 (file)
@@ -7,11 +7,13 @@ use crate::prolog::forms::*;
 use crate::prolog::machine::code_repo::CodeRepo;
 use crate::prolog::machine::Ball;
 use crate::prolog::machine::heap::*;
+use crate::prolog::machine::machine_state::*;
 use crate::prolog::machine::partial_string::*;
 use crate::prolog::machine::raw_block::RawBlockTraits;
 use crate::prolog::machine::streams::Stream;
 use crate::prolog::instructions::*;
-use crate::prolog::rug::Integer;
+use crate::prolog::ordered_float::OrderedFloat;
+use crate::prolog::rug::{Integer, Rational};
 
 use indexmap::IndexMap;
 
@@ -39,20 +41,35 @@ pub enum DBRef {
     ),
 }
 
-#[derive(Clone, PartialEq, Eq, Hash)]
+// 7.2
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum TermOrderCategory {
+    Variable,
+    FloatingPoint,
+    Integer,
+    Atom,
+    Compound,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub enum Addr {
     AttrVar(usize),
-    Con(Constant),
-    DBRef(DBRef),
+    Char(char),
+    CharCode(u32),
+    Con(usize),
+    CutPoint(usize),
+    EmptyList,
+    Float(OrderedFloat<f64>),
     Lis(usize),
     HeapCell(usize),
+    PStrLocation(usize, usize), // location of pstr in heap, offset into string in bytes.
     StackCell(usize, usize),
     Str(usize),
-    PStrLocation(usize, usize), // location of pstr in heap, offset into string in bytes.
-    Stream(Stream),
+    Stream(usize),
+    Usize(usize),
 }
 
-#[derive(Clone, Copy, Hash, Eq, PartialEq)]
+#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd)]
 pub enum Ref {
     AttrVar(usize),
     HeapCell(usize),
@@ -69,6 +86,28 @@ impl Ref {
     }
 }
 
+impl Ord for Ref {
+    fn cmp(&self, other: &Ref) -> Ordering {
+        match (self, other) {
+            (Ref::AttrVar(h1), Ref::AttrVar(h2))
+          | (Ref::HeapCell(h1), Ref::HeapCell(h2))
+          | (Ref::HeapCell(h1), Ref::AttrVar(h2))
+          | (Ref::AttrVar(h1), Ref::HeapCell(h2)) => {
+                h1.cmp(&h2)
+            }
+            (Ref::StackCell(fr1, sc1), Ref::StackCell(fr2, sc2)) => {
+                fr1.cmp(&fr2).then_with(|| sc1.cmp(&sc2))
+            }
+            (Ref::StackCell(..), _) => {
+                Ordering::Greater
+            }
+            (_, Ref::StackCell(..)) => {
+                Ordering::Less
+            }
+        }
+    }
+}
+
 impl PartialEq<Ref> for Addr {
     fn eq(&self, r: &Ref) -> bool {
         self.as_var() == Some(*r)
@@ -133,6 +172,83 @@ impl Addr {
         }
     }
 
+    pub(super)
+    fn order_category(&self, heap: &Heap) -> Option<TermOrderCategory> {                
+        match self {
+            Addr::HeapCell(_) | Addr::AttrVar(_) | Addr::StackCell(..) => {
+                Some(TermOrderCategory::Variable)
+            }
+            Addr::Float(_) => {
+                Some(TermOrderCategory::FloatingPoint)
+            }
+            &Addr::Con(h) => {
+                match &heap[h] {
+                    HeapCellValue::Atom(..) => {
+                        Some(TermOrderCategory::Atom)
+                    }
+                    HeapCellValue::Integer(_) => {
+                        Some(TermOrderCategory::Integer)
+                    }
+                    HeapCellValue::Rational(_) => {
+                        Some(TermOrderCategory::Integer)
+                    }
+                    HeapCellValue::DBRef(_) => {
+                        None
+                    }
+                    _ => {
+                        unreachable!()
+                    }
+                }
+            }
+            Addr::Char(_) | Addr::CharCode(_) | Addr::EmptyList => {
+                Some(TermOrderCategory::Atom)
+            }
+            Addr::Lis(_) | Addr::PStrLocation(..) | Addr::Str(_) => {
+                Some(TermOrderCategory::Compound)
+            }
+            Addr::CutPoint(_) | Addr::Usize(_) | Addr::Stream(_) => {
+                None
+            }
+        }
+    }
+    
+    pub fn as_constant(&self, machine_st: &MachineState) -> Option<Constant> {
+        match self {
+            &Addr::Char(c) => {
+                Some(Constant::Char(c))
+            }
+            &Addr::CharCode(c) => {
+                Some(Constant::CharCode(c))
+            }
+            &Addr::Con(h) => {
+                match &machine_st.heap[h] {
+                    &HeapCellValue::Atom(ref name, ref op) => {
+                        Some(Constant::Atom(name.clone(), op.clone()))
+                    }
+                    &HeapCellValue::Integer(ref n) => {
+                        Some(Constant::Integer(n.clone()))
+                    }
+                    &HeapCellValue::Rational(ref n) => {
+                        Some(Constant::Rational(n.clone()))
+                    }
+                    _ => {
+                        None
+                    }
+                }
+            }
+            &Addr::Float(f) => {
+                Some(Constant::Float(f))
+            }
+            &Addr::PStrLocation(h, n) => {
+                machine_st.to_complete_string(h, n)
+                          .map(|s| Constant::String(Rc::new(s)))
+            }
+            _ => {
+                None
+            }
+        }
+    }
+
     pub fn is_protected(&self, e: usize) -> bool {
         match self {
             &Addr::StackCell(addr, _) if addr >= e => false,
@@ -209,29 +325,78 @@ impl From<Ref> for TrailRef {
     }
 }
 
-#[derive(Clone, PartialEq)]
 pub enum HeapCellValue {
     Addr(Addr),
+    Atom(ClauseName, Option<SharedOpDesc>),
+    DBRef(DBRef),
+    Integer(Rc<Integer>),
     NamedStr(usize, ClauseName, Option<SharedOpDesc>), // arity, name, precedence/Specifier if it has one.
+    Rational(Rc<Rational>),    
     PartialString(PartialString),
+    Stream(Stream),
 }
 
 impl HeapCellValue {
+    #[inline]
     pub fn as_addr(&self, focus: usize) -> Addr {
         match self {
             HeapCellValue::Addr(ref a) => {
                 a.clone()
             }
+            HeapCellValue::Atom(..) | HeapCellValue::DBRef(..) | HeapCellValue::Integer(..) |
+            HeapCellValue::Rational(..) => {
+                Addr::Con(focus)
+            }
             HeapCellValue::NamedStr(_, _, _) => {
                 Addr::Str(focus)
             }
             HeapCellValue::PartialString(_) => {
                 Addr::PStrLocation(focus, 0)
             }
+            HeapCellValue::Stream(_) => {
+                Addr::Stream(focus)
+            }
+        }
+    }
+
+    #[inline]
+    pub fn context_free_clone(&self) -> HeapCellValue {
+        match self {
+            &HeapCellValue::Addr(addr) => {
+                HeapCellValue::Addr(addr)
+            }
+            &HeapCellValue::Atom(ref name, ref op) => {
+                HeapCellValue::Atom(name.clone(), op.clone())
+            }
+            &HeapCellValue::DBRef(ref db_ref) => {
+                HeapCellValue::DBRef(db_ref.clone())
+            }
+            &HeapCellValue::Integer(ref n) => {
+                HeapCellValue::Integer(n.clone())
+            }
+            &HeapCellValue::NamedStr(arity, ref name, ref op) => {
+                HeapCellValue::NamedStr(arity, name.clone(), op.clone())
+            }
+            &HeapCellValue::Rational(ref r) => {
+                HeapCellValue::Rational(r.clone())
+            }
+            &HeapCellValue::PartialString(ref pstr) => {
+                HeapCellValue::PartialString(pstr.clone())
+            }
+            &HeapCellValue::Stream(_) => {
+                HeapCellValue::Stream(Stream::null_stream())
+            }
         }
     }
 }
 
+impl From<Addr> for HeapCellValue {
+    #[inline]
+    fn from(value: Addr) -> HeapCellValue {
+        HeapCellValue::Addr(value)
+    }
+}
+
 #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
 pub enum IndexPtr {
     DynamicUndefined, // a predicate, declared as dynamic, whose location in code is as yet undefined.
@@ -407,37 +572,31 @@ impl LocalCodePtr {
             LocalCodePtr::DirEntry(p) => {
                 heap.append(functor!(
                     "dir_entry",
-                    1,
-                    [heap_integer!(Integer::from(*p))]
+                    [integer(*p)]
                 ));
             }
             LocalCodePtr::InSituDirEntry(p) => {
                 heap.append(functor!(
                     "in_situ_dir_entry",
-                    1,
-                    [heap_integer!(Integer::from(*p))]
+                    [integer(*p)]
                 ));
             }
             LocalCodePtr::TopLevel(chunk_num, offset) => {
                 heap.append(functor!(
                     "top_level",
-                    2,
-                    [heap_integer!(Integer::from(*chunk_num)),
-                     heap_integer!(Integer::from(*offset))]
+                    [integer(*chunk_num), integer(*offset)]
                 ));
             }
             LocalCodePtr::UserGoalExpansion(p) => {
                 heap.append(functor!(
                     "user_goal_expansion",
-                    1,
-                    [heap_integer!(Integer::from(*p))]
+                    [integer(*p)]
                 ));
             }
             LocalCodePtr::UserTermExpansion(p) => {
                 heap.append(functor!(
                     "user_term_expansion",
-                    1,
-                    [heap_integer!(Integer::from(*p))]
+                    [integer(*p)]
                 ));
             }
         }
@@ -449,8 +608,12 @@ impl LocalCodePtr {
 impl PartialOrd<CodePtr> for CodePtr {
     fn partial_cmp(&self, other: &CodePtr) -> Option<Ordering> {
         match (self, other) {
-            (&CodePtr::Local(ref l1), &CodePtr::Local(ref l2)) => l1.partial_cmp(l2),
-            _ => Some(Ordering::Greater),
+            (&CodePtr::Local(ref l1), &CodePtr::Local(ref l2)) => {
+                l1.partial_cmp(l2)
+            }
+            _ => {
+                Some(Ordering::Greater)
+            }
         }
     }
 }
@@ -465,8 +628,12 @@ impl PartialOrd<LocalCodePtr> for LocalCodePtr {
           | (&LocalCodePtr::TopLevel(_, p1), &LocalCodePtr::TopLevel(_, ref p2)) => {
                 p1.partial_cmp(p2)
             }
-            (_, &LocalCodePtr::TopLevel(_, _)) => Some(Ordering::Less),
-            _ => Some(Ordering::Greater),
+            (_, &LocalCodePtr::TopLevel(_, _)) => {
+                Some(Ordering::Less)
+            }
+            _ => {
+                Some(Ordering::Greater)
+            }
         }
     }
 }
index ec6efb5f5c533a1028e43b0b908dcde26ea73297..2c4a536751ef93c0285dc2db3ad0751285f9cb06 100644 (file)
@@ -18,7 +18,6 @@ use std::cmp::Ordering;
 use std::io::Write;
 use std::mem;
 use std::ops::{Index, IndexMut};
-use std::rc::Rc;
 
 pub struct Ball {
     pub(super) boundary: usize,
@@ -58,11 +57,11 @@ impl Ball {
 
         for heap_value in self.stub.iter_from(0) {
             stub.push(match heap_value {
-                HeapCellValue::Addr(ref addr) => {
-                    HeapCellValue::Addr(addr.clone() - diff)
+                &HeapCellValue::Addr(addr) => {
+                    HeapCellValue::Addr(addr - diff)
                 }
                 heap_value => {
-                    heap_value.clone()
+                    heap_value.context_free_clone()
                 }
             });
         }
@@ -185,8 +184,12 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
                 let index = h - self.heap_boundary;
                 self.stub[index].as_addr(h)
             }
-            Addr::StackCell(fr, sc) => self.stack.index_and_frame(fr)[sc].clone(),
-            addr => addr,
+            Addr::StackCell(fr, sc) => {
+                self.stack.index_and_frame(fr)[sc].clone()
+            }
+            addr => {
+                addr
+            }
         }
     }
 
@@ -250,8 +253,6 @@ pub(super) enum HeapPtr {
     HeapCell(usize),
     PStrChar(usize, usize),
     PStrLocation(usize, usize),
-    StringChar(usize, Rc<String>),
-    StringLocation(usize, Rc<String>),
 }
 
 impl HeapPtr {
@@ -259,30 +260,23 @@ impl HeapPtr {
     pub(super)
     fn read(&self, heap: &Heap) -> Addr {
         match self {
-            &HeapPtr::HeapCell(h) =>
-                Addr::HeapCell(h),
-            &HeapPtr::PStrChar(h, n) =>
+            &HeapPtr::HeapCell(h) => {
+                Addr::HeapCell(h)
+            }
+            &HeapPtr::PStrChar(h, n) => {
                 if let HeapCellValue::PartialString(ref pstr) = &heap[h] {
-                    let s = pstr.block_as_str();
-
-                    if let Some(c) = s[n ..].chars().next() {
-                        Addr::Con(Constant::Char(c))
+                    if let Some(c) = pstr.range_from(n ..).next() {
+                        Addr::Char(c)
                     } else {
                         Addr::HeapCell(h + 1)
                     }
                 } else {
                     unreachable!()
-                },
-            &HeapPtr::PStrLocation(h, n) =>
-                Addr::PStrLocation(h, n),
-            &HeapPtr::StringChar(n, ref s) =>
-                if let Some(c) = s[n ..].chars().next() {
-                    Addr::Con(Constant::Char(c))
-                } else {
-                    Addr::Con(Constant::EmptyList)
-                },
-            &HeapPtr::StringLocation(n, ref s) =>
-                Addr::Con(Constant::String(n, s.clone())),
+                }
+            }
+            &HeapPtr::PStrLocation(h, n) => {
+                Addr::PStrLocation(h, n)
+            }
         }
     }
 }
@@ -330,29 +324,27 @@ impl MachineState {
             let addr = self.store(self.deref(addr.clone()));
 
             match addr {
-                Addr::Con(Constant::String(n, ref s))
-                    if self.flags.double_quotes.is_chars() => {
-                        if s.len() < n {
-                            chars += &s[n ..];
-                        }
-
-                        if iter.next().is_some() {
-                            return Err(MachineError::type_error(ValidType::Character, addr.clone()));
-                        }
-                    }
-                Addr::Con(Constant::Char(c)) => {
+                Addr::Char(c) => {
                     chars.push(c);
+                    continue;
                 }
-                Addr::Con(Constant::Atom(ref name, _))
-                    if name.as_str().len() == 1 => {
-                        chars += name.as_str();
+                Addr::Con(h) => {
+                    if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
+                        if name.is_char() {
+                            chars += name.as_str();
+                            continue;
+                        }
                     }
+                }
                 _ => {
-                    return Err(
-                        MachineError::type_error(ValidType::Character, addr.clone())
-                    );
                 }
-            }
+            };
+
+            let h = self.heap.h();
+
+            return Err(
+                MachineError::type_error(h, ValidType::Character, addr)
+            );
         }
 
         Ok(chars)
@@ -738,32 +730,36 @@ pub(crate) trait CallPolicy: Any {
                 let a2 = machine_st[temp_v!(2)].clone();
                 let a3 = machine_st[temp_v!(3)].clone();
 
-                let c = match machine_st.compare_term_test(&a2, &a3) {
-                    Ordering::Greater => {
+                let atom = match machine_st.compare_term_test(&a2, &a3) {
+                    Some(Ordering::Greater) => {
                         let spec = fetch_atom_op_spec(clause_name!(">"), None, &indices.op_dir);
-                        Addr::Con(Constant::Atom(clause_name!(">"), spec))
+                        HeapCellValue::Atom(clause_name!(">"), spec)
                     }
-                    Ordering::Equal => {
+                    Some(Ordering::Equal) => {
                         let spec = fetch_atom_op_spec(clause_name!("="), None, &indices.op_dir);
-                        Addr::Con(Constant::Atom(clause_name!("="), spec))
+                        HeapCellValue::Atom(clause_name!("="), spec)
                     }
-                    Ordering::Less => {
+                    None | Some(Ordering::Less) => {
                         let spec = fetch_atom_op_spec(clause_name!("<"), None, &indices.op_dir);
-                        Addr::Con(Constant::Atom(clause_name!("<"), spec))
+                        HeapCellValue::Atom(clause_name!("<"), spec)
                     }
                 };
 
-                machine_st.unify(a1, c);
+                let h = machine_st.heap.h();
+
+                machine_st.heap.push(atom);
+                machine_st.unify(a1, Addr::Con(h));
+
                 return_from_clause!(machine_st.last_call, machine_st)
             }
             &BuiltInClauseType::CompareTerm(qt) => {
                 machine_st.compare_term(qt);
                 return_from_clause!(machine_st.last_call, machine_st)
             }
-            &BuiltInClauseType::Nl => {                
+            &BuiltInClauseType::Nl => {
                 write!(current_output_stream, "\n").unwrap();
                 current_output_stream.flush().unwrap();
-                
+
                 return_from_clause!(machine_st.last_call, machine_st)
             }
             &BuiltInClauseType::Read => {
@@ -811,11 +807,12 @@ pub(crate) trait CallPolicy: Any {
                 let a1 = machine_st[temp_v!(1)].clone();
                 let a2 = machine_st[temp_v!(2)].clone();
 
-                machine_st.fail = if let Ordering::Equal = machine_st.compare_term_test(&a1, &a2) {
-                    true
-                } else {
-                    false
-                };
+                machine_st.fail =
+                    if let Some(Ordering::Equal) = machine_st.compare_term_test(&a1, &a2) {
+                        true
+                    } else {
+                        false
+                    };
 
                 return_from_clause!(machine_st.last_call, machine_st)
             }
@@ -825,7 +822,10 @@ pub(crate) trait CallPolicy: Any {
                 let stub = MachineError::functor_stub(clause_name!("sort"), 2);
                 let mut list = machine_st.try_from_list(temp_v!(1), stub)?;
 
-                list.sort_unstable_by(|a1, a2| machine_st.compare_term_test(a1, a2));
+                list.sort_unstable_by(|a1, a2| {
+                    machine_st.compare_term_test(a1, a2).unwrap_or(Ordering::Less)
+                });
+
                 machine_st.term_dedup(&mut list);
 
                 let heap_addr = Addr::HeapCell(machine_st.heap.to_list(list.into_iter()));
@@ -847,7 +847,9 @@ pub(crate) trait CallPolicy: Any {
                     key_pairs.push((key, val.clone()));
                 }
 
-                key_pairs.sort_by(|a1, a2| machine_st.compare_term_test(&a1.0, &a2.0));
+                key_pairs.sort_by(|a1, a2| {
+                    machine_st.compare_term_test(&a1.0, &a2.0).unwrap_or(Ordering::Less)
+                });
 
                 let key_pairs = key_pairs.into_iter().map(|kp| kp.1);
                 let heap_addr = Addr::HeapCell(machine_st.heap.to_list(key_pairs));
@@ -859,9 +861,11 @@ pub(crate) trait CallPolicy: Any {
             }
             &BuiltInClauseType::Is(r, ref at) => {
                 let a1 = machine_st[r].clone();
-                let a2 = machine_st.get_number(at)?;
+                let n2 = machine_st.get_number(at)?;
+
+                let n2 = Addr::Con(machine_st.heap.push(n2.into()));
+                machine_st.unify(a1, n2);
 
-                machine_st.unify(a1, Addr::Con(a2.to_constant()));
                 return_from_clause!(machine_st.last_call, machine_st)
             }
         }
@@ -935,11 +939,13 @@ pub(crate) trait CallPolicy: Any {
                     }
                 }
                 ClauseType::Hook(_) | ClauseType::System(_) => {
-                    let name = Addr::Con(Constant::Atom(name, None));
+                    let name = functor!(clause_name(name));
                     let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
 
-                    return Err(machine_st
-                               .error_form(MachineError::type_error(ValidType::Callable, name), stub));
+                    return Err(machine_st.error_form(
+                        MachineError::type_error(machine_st.heap.h(), ValidType::Callable, name),
+                        stub,
+                    ));
                 }
             };
         }
@@ -997,7 +1003,7 @@ impl CallPolicy for CWILCallPolicy {
             current_input_stream,
             current_output_stream
         )?;
-        
+
         self.increment(machine_st)
     }
 
@@ -1016,7 +1022,7 @@ impl CallPolicy for CWILCallPolicy {
             current_input_stream,
             current_output_stream,
         )?;
-        
+
         self.increment(machine_st)
     }
 }
@@ -1035,7 +1041,8 @@ pub(crate) struct CWILCallPolicy {
 }
 
 impl CWILCallPolicy {
-    pub(crate) fn new_in_place(policy: &mut Box<dyn CallPolicy>) {
+    pub(crate)
+    fn new_in_place(policy: &mut Box<dyn CallPolicy>) {
         let mut prev_policy: Box<dyn CallPolicy> = Box::new(DefaultCallPolicy {});
         mem::swap(&mut prev_policy, policy);
 
@@ -1045,6 +1052,7 @@ impl CWILCallPolicy {
             limits: vec![],
             inference_limit_exceeded: false,
         };
+
         *policy = Box::new(new_policy);
     }
 
@@ -1056,10 +1064,10 @@ impl CWILCallPolicy {
         if let Some(&(ref limit, bp)) = self.limits.last() {
             if self.count == *limit {
                 self.inference_limit_exceeded = true;
+
                 return Err(functor!(
                     "inference_limit_exceeded",
-                    1,
-                    [HeapCellValue::Addr(Addr::Con(Constant::Usize(bp)))]
+                    [addr(Addr::Usize(bp))]
                 ));
             } else {
                 self.count += 1;
@@ -1069,7 +1077,8 @@ impl CWILCallPolicy {
         Ok(())
     }
 
-    pub(crate) fn add_limit(&mut self, mut limit: Integer, b: usize) -> &Integer {
+    pub(crate)
+    fn add_limit(&mut self, mut limit: Integer, b: usize) -> &Integer {
         limit += &self.count;
 
         match self.limits.last().cloned() {
@@ -1080,7 +1089,8 @@ impl CWILCallPolicy {
         &self.count
     }
 
-    pub(crate) fn remove_limit(&mut self, b: usize) -> &Integer {
+    pub(crate)
+    fn remove_limit(&mut self, b: usize) -> &Integer {
         if let Some((_, bp)) = self.limits.last().cloned() {
             if bp == b {
                 self.limits.pop();
@@ -1090,11 +1100,13 @@ impl CWILCallPolicy {
         &self.count
     }
 
-    pub(crate) fn is_empty(&self) -> bool {
+    pub(crate)
+    fn is_empty(&self) -> bool {
         self.limits.is_empty()
     }
 
-    pub(crate) fn into_inner(&mut self) -> Box<dyn CallPolicy> {
+    pub(crate)
+    fn into_inner(&mut self) -> Box<dyn CallPolicy> {
         let mut new_inner: Box<dyn CallPolicy> = Box::new(DefaultCallPolicy {});
         mem::swap(&mut self.prev_policy, &mut new_inner);
         new_inner
@@ -1108,11 +1120,11 @@ pub(crate) trait CutPolicy: Any {
 
 downcast!(dyn CutPolicy);
 
-fn cut_body(machine_st: &mut MachineState, addr: Addr) -> bool {
+fn cut_body(machine_st: &mut MachineState, addr: &Addr) -> bool {
     let b = machine_st.b;
 
     match addr {
-        Addr::Con(Constant::CutPoint(b0)) | Addr::Con(Constant::Usize(b0)) => {
+        &Addr::CutPoint(b0) | &Addr::Usize(b0) => {
             if b > b0 {
                 machine_st.b = b0;
                 machine_st.tidy_trail();
@@ -1131,13 +1143,13 @@ pub(crate) struct DefaultCutPolicy {}
 
 pub(super) fn deref_cut(machine_st: &mut MachineState, r: RegType) {
     let addr = machine_st.store(machine_st.deref(machine_st[r].clone()));
-    cut_body(machine_st, addr);
+    cut_body(machine_st, &addr);
 }
 
 impl CutPolicy for DefaultCutPolicy {
     fn cut(&mut self, machine_st: &mut MachineState, r: RegType) -> bool {
         let addr = machine_st[r].clone();
-        cut_body(machine_st, addr)
+        cut_body(machine_st, &addr)
     }
 }
 
@@ -1175,7 +1187,7 @@ impl SCCCutPolicy {
                 let (idx, arity) = if machine_st.block < prev_block {
                     (dir_entry!(self.r_c_w_h), 0)
                 } else {
-                    machine_st[temp_v!(1)] = Addr::Con(Constant::Usize(b_cutoff));
+                    machine_st[temp_v!(1)] = Addr::Usize(b_cutoff);
                     (dir_entry!(self.r_c_wo_h), 1)
                 };
 
@@ -1198,7 +1210,7 @@ impl CutPolicy for SCCCutPolicy {
         let b = machine_st.b;
 
         match machine_st[r].clone() {
-            Addr::Con(Constant::Usize(b0)) | Addr::Con(Constant::CutPoint(b0)) => {
+            Addr::Usize(b0) | Addr::CutPoint(b0) => {
                 if b > b0 {
                     machine_st.b = b0;
                     machine_st.tidy_trail();
index b8a4d69c6e5f44107b0d2da7a69d0afb6efc125e..fd721fa1c7651002c60c6418f0eb85b2ab0e1afb 100644 (file)
@@ -1,7 +1,6 @@
 use prolog_parser::ast::*;
 use prolog_parser::tabled_rc::*;
 
-use crate::prolog::arithmetic::*;
 use crate::prolog::clause_types::*;
 use crate::prolog::forms::*;
 use crate::prolog::heap_iter::*;
@@ -17,25 +16,13 @@ use crate::prolog::machine::machine_state::*;
 use crate::prolog::machine::stack::*;
 use crate::prolog::machine::streams::*;
 use crate::prolog::ordered_float::*;
-use crate::prolog::rug::{Integer, Rational};
+use crate::prolog::rug::Integer;
 
 use indexmap::{IndexMap, IndexSet};
 
-use std::cmp::{max, min, Ordering};
-use std::f64;
-use std::iter::FromIterator;
-use std::mem;
+use std::cmp::Ordering;
 use std::rc::Rc;
 
-macro_rules! try_numeric_result {
-    ($s: ident, $e: expr, $caller: expr) => {{
-        match $e {
-            Ok(val) => Ok(val),
-            Err(e) => Err($s.error_form(MachineError::evaluation_error(e), $caller)),
-        }
-    }};
-}
-
 macro_rules! try_or_fail {
     ($s:ident, $e:expr) => {{
         match $e {
@@ -132,7 +119,7 @@ impl MachineState {
             }
             Addr::PStrLocation(h, n) => {
                 if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] {
-                    if pstr.len() > n {
+                    if !pstr.at_end(n) {
                         Addr::PStrLocation(h, n)
                     } else {
                         Addr::HeapCell(h + 1)
@@ -217,38 +204,14 @@ impl MachineState {
         }
     }
 
-    fn deconstruct_chars(
-        &mut self,
-        s: Rc<String>,
-        string_offset: usize,
-        list_cell: usize,
-        pdl: &mut Vec<Addr>,
-    ) -> bool {
-        if s.len() > string_offset {
-            if let Some(c) = s[string_offset ..].chars().next() {
-                pdl.push(Addr::Con(Constant::String(string_offset + c.len_utf8(), s)));
-                pdl.push(Addr::HeapCell(list_cell + 1));
-
-                pdl.push(Addr::Con(Constant::Char(c)));
-                pdl.push(Addr::HeapCell(list_cell));
-
-                return true;
-            }
-        }
-
-        false
-    }
-
     fn bind_with_occurs_check(&mut self, r: Ref, addr: Addr) {
         let mut fail = false;
 
-        for value in self.acyclic_pre_order_iter(addr.clone()) {
-            if let HeapCellValue::Addr(addr) = value {
-                if let Some(inner_r) = addr.as_var() {
-                    if r == inner_r {
-                        fail = true;
-                        break;
-                    }
+        for addr in self.acyclic_pre_order_iter(addr) {
+            if let Some(inner_r) = addr.as_var() {
+                if r == inner_r {
+                    fail = true;
+                    break;
                 }
             }
         }
@@ -303,91 +266,54 @@ impl MachineState {
 
                         self.fail = true;
                     }
-                    (Addr::Lis(a1), Addr::Con(Constant::String(n, s)))
-                  | (Addr::Con(Constant::String(n, s)), Addr::Lis(a1))
-                        if !self.flags.double_quotes.is_atom() =>
-                    {
-                        if self.deconstruct_chars(s, n, a1, &mut pdl) {
-                            continue;
-                        }
-
-                        self.fail = true;
-                    }
-                    (Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(n, ref s)))
-                  | (Addr::Con(Constant::String(n, ref s)), Addr::Con(Constant::EmptyList))
-                        if !self.flags.double_quotes.is_atom() =>
-                    {
-                        if n >= s.len() {
-                            continue;
-                        }
-
-                        self.fail = true;
-                    }
                     (Addr::PStrLocation(h, n), Addr::Lis(l))
                   | (Addr::Lis(l), Addr::PStrLocation(h, n)) => {
                         if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] {
-                            let s = pstr.block_as_str();
-
-                            if let Some(c) = s[n ..].chars().next() {
+                            if let Some(c) = pstr.range_from(n ..).next() {
                                 pdl.push(Addr::PStrLocation(h, n + c.len_utf8()));
                                 pdl.push(Addr::HeapCell(l + 1));
 
-                                pdl.push(Addr::Con(Constant::Char(c)));
+                                pdl.push(Addr::Char(c));
                                 pdl.push(Addr::HeapCell(l));
+                            } else {
+                                unreachable!()
                             }
+                        } else {
+                            unreachable!()
                         }
                     }
-                    (Addr::PStrLocation(h, n), Addr::Con(Constant::String(n1, s)))
-                  | (Addr::Con(Constant::String(n1, s)), Addr::PStrLocation(h, n))
-                        if self.flags.double_quotes.is_chars() => {
-                            if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] {
-                                let pstr_s = pstr.block_as_str();
-
-                                let pstr_len = pstr_s[n ..].len();
-                                let s_len = s[n1 ..].len();
-                                let m_len = std::cmp::min(s_len, pstr_len);
-
-                                if pstr_s[n .. n + m_len] == s[n1 .. n1 + m_len] {
-                                    if s_len <= pstr_len {
-                                        pdl.push(Addr::Con(Constant::EmptyList));
-                                        pdl.push(Addr::PStrLocation(h, n + m_len));
-                                    } else {
-                                        pdl.push(Addr::Con(Constant::String(n1 + m_len, s)));
-                                        pdl.push(Addr::HeapCell(h + 1));
-                                    }
-
-                                    continue;
-                                }
-
-                                self.fail = true;
-                                break;
-                            }
-                        }
                     (Addr::PStrLocation(h1, n1), Addr::PStrLocation(h2, n2)) => {
                         if let HeapCellValue::PartialString(ref pstr1) = &self.heap[h1] {
                             if let HeapCellValue::PartialString(ref pstr2) = &self.heap[h2] {
-                                let pstr_s1 = pstr1.block_as_str();
-                                let pstr_s2 = pstr2.block_as_str();
+                                let pstr1_iter = pstr1.range_from(n1 ..);
+                                let pstr2_iter = pstr2.range_from(n2 ..);
 
-                                let pstr_s1_len = pstr_s1[n1 ..].len();
-                                let pstr_s2_len = pstr_s2[n2 ..].len();
-                                let m_len = std::cmp::min(pstr_s1_len, pstr_s2_len);
+                                let mut m_len = 0;
 
-                                if pstr_s1[n1 .. n1 + m_len] == pstr_s2[n2 .. n2 + m_len] {
-                                    if pstr_s1_len <= pstr_s2_len {
-                                        pdl.push(Addr::HeapCell(h1 + 1));
-                                        pdl.push(Addr::PStrLocation(h2, n2 + m_len));
-                                    } else {
-                                        pdl.push(Addr::HeapCell(h2 + 1));
-                                        pdl.push(Addr::PStrLocation(h1, n1 + m_len));
+                                for (c1, c2) in pstr1_iter.zip(pstr2_iter) {
+                                    if c1 != c2 {
+                                        self.fail = true;
+                                        return;
                                     }
 
-                                    continue;
+                                    m_len += 1;
+                                }
+
+                                if pstr1.at_end(n1 + m_len) {
+                                    pdl.push(Addr::HeapCell(h1 + 1));
+                                    pdl.push(Addr::PStrLocation(h2, n2 + m_len));
+                                } else {
+                                    pdl.push(Addr::HeapCell(h2 + 1));
+                                    pdl.push(Addr::PStrLocation(h1, n1 + m_len));
                                 }
 
                                 self.fail = true;
                                 break;
+                            } else {
+                                unreachable!()
                             }
+                        } else {
+                            unreachable!()
                         }
                     }
                     (Addr::Lis(a1), Addr::Lis(a2)) => {
@@ -397,7 +323,7 @@ impl MachineState {
                         pdl.push(Addr::HeapCell(a1 + 1));
                         pdl.push(Addr::HeapCell(a2 + 1));
                     }
-                    (Addr::Con(ref c1), Addr::Con(ref c2)) => {
+                    (Addr::Con(c1), Addr::Con(c2)) => {
                         if c1 != c2 {
                             self.fail = true;
                         }
@@ -474,85 +400,45 @@ impl MachineState {
 
                         self.fail = true;
                     }
-                    (Addr::Lis(a1), Addr::Con(Constant::String(n, s)))
-                  | (Addr::Con(Constant::String(n, s)), Addr::Lis(a1))
-                        if !self.flags.double_quotes.is_atom() => {
-                            if self.deconstruct_chars(s, n, a1, &mut pdl) {
-                                continue;
-                            }
-
-                            self.fail = true;
-                        }
-                    (Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(n, ref s)))
-                  | (Addr::Con(Constant::String(n, ref s)), Addr::Con(Constant::EmptyList))
-                        if !self.flags.double_quotes.is_atom() =>
-                    {
-                        if n >= s.len() {
-                            continue;
-                        }
-
-                        self.fail = true;
-                    }
                     (Addr::PStrLocation(h, n), Addr::Lis(l))
                   | (Addr::Lis(l), Addr::PStrLocation(h, n)) => {
-                        if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] {
-                            let s = pstr.block_as_str();
-
-                            if let Some(c) = s[n ..].chars().next() {
-                                pdl.push(Addr::PStrLocation(h, n + c.len_utf8()));
-                                pdl.push(Addr::HeapCell(l + 1));
-
-                                pdl.push(Addr::Con(Constant::Char(c)));
-                                pdl.push(Addr::HeapCell(l));
-                            }
-                        }
+                      if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] {
+                          if let Some(c) = pstr.range_from(n ..).next() {
+                              pdl.push(Addr::PStrLocation(h, n + c.len_utf8()));
+                              pdl.push(Addr::HeapCell(l + 1));
+
+                              pdl.push(Addr::Char(c));
+                              pdl.push(Addr::HeapCell(l));
+                          } else {
+                              unreachable!()
+                          }
+                      } else {
+                          unreachable!()
+                      }
                     }
-                    (Addr::PStrLocation(h, n), Addr::Con(Constant::String(n1, s)))
-                  | (Addr::Con(Constant::String(n1, s)), Addr::PStrLocation(h, n))
-                        if self.flags.double_quotes.is_chars() => {
-                            if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] {
-                                let pstr_s = pstr.block_as_str();
-
-                                let pstr_len = pstr_s[n ..].len();
-                                let s_len = s[n1 ..].len();
-                                let m_len = std::cmp::min(s_len, pstr_len);
-
-                                if pstr_s[n .. n + m_len] == s[n1 .. n1 + m_len] {
-                                    if s_len <= pstr_len {
-                                        pdl.push(Addr::Con(Constant::EmptyList));
-                                        pdl.push(Addr::PStrLocation(h, n + m_len));
-                                    } else {
-                                        pdl.push(Addr::Con(Constant::String(n1 + m_len, s)));
-                                        pdl.push(Addr::HeapCell(h + 1));
-                                    }
-
-                                    continue;
-                                }
-
-                                self.fail = true;
-                                break;
-                            }
-                        }
                     (Addr::PStrLocation(h1, n1), Addr::PStrLocation(h2, n2)) => {
                         if let HeapCellValue::PartialString(ref pstr1) = &self.heap[h1] {
                             if let HeapCellValue::PartialString(ref pstr2) = &self.heap[h2] {
-                                let pstr_s1 = pstr1.block_as_str();
-                                let pstr_s2 = pstr2.block_as_str();
+                                let pstr1_iter = pstr1.range_from(n1 ..);
+                                let pstr2_iter = pstr2.range_from(n2 ..);
 
-                                let pstr_s1_len = pstr_s1[n1 ..].len();
-                                let pstr_s2_len = pstr_s2[n2 ..].len();
-                                let m_len = std::cmp::min(pstr_s1_len, pstr_s2_len);
+                                let mut m_len = 0;
 
-                                if pstr_s1[n1 .. n1 + m_len] == pstr_s2[n2 .. n2 + m_len] {
-                                    if pstr_s1_len <= pstr_s2_len {
-                                        pdl.push(Addr::HeapCell(h1 + 1));
-                                        pdl.push(Addr::PStrLocation(h2, n2 + m_len));
-                                    } else {
-                                        pdl.push(Addr::HeapCell(h2 + 1));
-                                        pdl.push(Addr::PStrLocation(h1, n1 + m_len));
+                                for (c1, c2) in pstr1_iter.zip(pstr2_iter) {
+                                    if c1 != c2 {
+                                        self.fail = true;
+                                        return;
                                     }
 
-                                    continue;
+                                    m_len += 1;
+                                }
+
+                                if pstr1.at_end(n1 + m_len) {
+                                    pdl.push(Addr::HeapCell(h1 + 1));
+                                    pdl.push(Addr::PStrLocation(h2, n2 + m_len));
+                                } else {
+                                    pdl.push(Addr::HeapCell(h2 + 1));
+                                    pdl.push(Addr::PStrLocation(h1, n1 + m_len));
                                 }
 
                                 self.fail = true;
@@ -567,7 +453,7 @@ impl MachineState {
                         pdl.push(Addr::HeapCell(a1 + 1));
                         pdl.push(Addr::HeapCell(a2 + 1));
                     }
-                    (Addr::Con(ref c1), Addr::Con(ref c2)) => {
+                    (Addr::Con(c1), Addr::Con(c2)) => {
                         if c1 != c2 {
                             self.fail = true;
                         }
@@ -597,6 +483,45 @@ impl MachineState {
         }
     }
 
+    pub(super)
+    fn to_complete_string(&self, h: usize, n: usize) -> Option<String> {
+        if self.is_cyclic_term(Addr::PStrLocation(h, n)) {
+            return None;
+        }
+
+        let mut addr = Addr::PStrLocation(h, n);
+        let mut result = String::new();
+
+        loop {
+            match addr {
+                Addr::EmptyList => {
+                    break;
+                }
+                Addr::Lis(l) if !self.flags.double_quotes.is_atom() => {
+                    if let &HeapCellValue::Addr(Addr::Char(c)) = &self.heap[l] {
+                        result.push(c);
+                        addr = self.store(self.deref(Addr::HeapCell(l + 1)));
+                    } else {
+                        return None;
+                    }
+                }
+                Addr::PStrLocation(h, n) => {
+                    if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] {
+                        result.extend(pstr.range_from(n ..));
+                        addr = self.store(self.deref(Addr::HeapCell(h + 1)));
+                    } else {
+                        unreachable!()
+                    }
+                }
+                _ => {
+                    return None;
+                }
+            }
+        }
+
+        Some(result)
+    }
+
     pub(super)
     fn trail(&mut self, r: TrailRef) {
         match r {
@@ -638,29 +563,21 @@ impl MachineState {
             HeapPtr::HeapCell(ref mut h) => {
                 *h += rhs;
             }
-            HeapPtr::PStrChar(h, n) | HeapPtr::PStrLocation(h, n) => {
-                match &self.heap[*h] {
+            &mut HeapPtr::PStrChar(h, ref mut n)
+          | &mut HeapPtr::PStrLocation(h, ref mut n) => {
+                match &self.heap[h] {
                     HeapCellValue::PartialString(ref pstr) => {
-                        let s = pstr.block_as_str();
-
-                        for c in s[*n ..].chars().take(rhs) {
+                        for c in pstr.range_from(*n ..).take(rhs) {
                             *n += c.len_utf8();
                         }
 
-                        self.s = HeapPtr::PStrLocation(*h, *n);
+                        self.s = HeapPtr::PStrLocation(h, *n);
                     }
                     _ => {
                         unreachable!()
                     }
                 }
             }
-            HeapPtr::StringChar(n, s) | HeapPtr::StringLocation(n, s) => {
-                for c in s[*n ..].chars().take(rhs) {
-                    *n += c.len_utf8();
-                }
-
-                self.s = HeapPtr::StringLocation(*n, s.clone());
-            }
         }
     }
 
@@ -726,665 +643,162 @@ impl MachineState {
         self.trail.truncate(self.tr);
     }
 
-    pub(super) fn write_constant_to_var(&mut self, addr: Addr, c: Constant) {
+    pub(super)
+    fn write_constant_to_var(&mut self, addr: Addr, c: &Constant) {
         match self.store(self.deref(addr)) {
-            Addr::Con(c1) =>
-                self.fail = self.eq_test(Addr::Con(c), Addr::Con(c1)),
-            Addr::Lis(l) =>
-                self.unify(Addr::Lis(l), Addr::Con(c)),
-            addr => {
-                if let Some(r) = addr.as_var() {
-                    self.bind(r, Addr::Con(c));
+            Addr::Con(c1) => {
+                self.fail = match &self.heap[c1] {
+                    HeapCellValue::Atom(ref n1, ref op1) => {
+                        if let Constant::Atom(ref n2, ref op2) = c {
+                            !(n1 == n2 && op1 == op2)
+                        } else {
+                            true
+                        }
+                    }
+                    HeapCellValue::Integer(ref n1) => {
+                        if let Constant::Integer(ref n2) = c {
+                            n1 != n2
+                        } else {
+                            true
+                        }
+                    }
+                    HeapCellValue::Rational(ref r1) => {
+                        if let Constant::Rational(ref r2) = c {
+                            r1 != r2
+                        } else {
+                            true
+                        }
+                    }
+                    HeapCellValue::PartialString(_) => {
+                        self.to_complete_string(c1, 0).map(|s1| {
+                            if let Constant::String(ref s2) = c {
+                                &s1 != &**s2
+                            } else {
+                                true
+                            }
+                        }).unwrap_or(true)
+                    }
+                    _ => {
+                        unreachable!()
+                    }
+                };
+            }
+            Addr::EmptyList => {
+                if let Constant::EmptyList = c {
                 } else {
                     self.fail = true;
                 }
             }
-        };
-    }
-
-    pub(super) fn get_number(&mut self, at: &ArithmeticTerm) -> Result<Number, MachineStub> {
-        match at {
-            &ArithmeticTerm::Reg(r) => self.arith_eval_by_metacall(r),
-            &ArithmeticTerm::Interm(i) => Ok(mem::replace(
-                &mut self.interms[i - 1],
-                Number::Integer(Integer::from(0)),
-            )),
-            &ArithmeticTerm::Number(ref n) => Ok(n.clone()),
-        }
-    }
-
-    fn rational_from_number(
-        &self,
-        n: Number,
-        caller: &MachineStub,
-    ) -> Result<Rational, MachineStub> {
-        match n {
-            Number::Rational(r) => Ok(r),
-            Number::Float(OrderedFloat(f)) => Rational::from_f64(f).ok_or_else(|| {
-                self.error_form(MachineError::instantiation_error(), caller.clone())
-            }),
-            Number::Integer(n) => Ok(Rational::from(n)),
-        }
-    }
-
-    fn get_rational(
-        &mut self,
-        at: &ArithmeticTerm,
-        caller: &MachineStub,
-    ) -> Result<Rational, MachineStub> {
-        let n = self.get_number(at)?;
-        self.rational_from_number(n, caller)
-    }
-
-    pub(super) fn arith_eval_by_metacall(&self, r: RegType) -> Result<Number, MachineStub> {
-        let a = self[r].clone();
-
-        let caller = MachineError::functor_stub(clause_name!("(is)"), 2);
-        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, _) => {
-                    let a2 = interms.pop().unwrap();
-                    let a1 = interms.pop().unwrap();
-
-                    match name.as_str() {
-                        "+" => interms.push(try_numeric_result!(self, a1 + a2, caller.clone())?),
-                        "-" => interms.push(try_numeric_result!(self, a1 - a2, caller.clone())?),
-                        "*" => interms.push(try_numeric_result!(self, a1 * a2, caller.clone())?),
-                        "/" => interms.push(self.div(a1, a2)?),
-                        "**" => interms.push(self.pow(a1, a2, "(is)")?),
-                        "^" => interms.push(self.int_pow(a1, a2)?),
-                        "max" => interms.push(self.max(a1, a2)?),
-                        "min" => interms.push(self.min(a1, a2)?),
-                        "rdiv" => {
-                            let r1 = self.rational_from_number(a1, &caller)?;
-                            let r2 = self.rational_from_number(a2, &caller)?;
-
-                            let result = Number::Rational(self.rdiv(r1, r2)?);
-                            interms.push(result)
-                        }
-                        "//" => interms.push(Number::Integer(self.idiv(a1, a2)?)),
-                        "div" => interms.push(Number::Integer(self.int_floor_div(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)?)),
-                        "atan2" => interms.push(Number::Float(OrderedFloat(self.atan2(a1, a2)?))),
-                        "gcd" => interms.push(Number::Integer(self.gcd(a1, a2)?)),
-                        _ => {
-                            return Err(self.error_form(MachineError::instantiation_error(), caller))
-                        }
-                    }
-                }
-                HeapCellValue::NamedStr(1, name, _) => {
-                    let a1 = interms.pop().unwrap();
-
-                    match name.as_str() {
-                        "-" => interms.push(-a1),
-                        "+" => interms.push(a1),
-                        "cos" => interms.push(Number::Float(OrderedFloat(self.cos(a1)?))),
-                        "sin" => interms.push(Number::Float(OrderedFloat(self.sin(a1)?))),
-                        "tan" => interms.push(Number::Float(OrderedFloat(self.tan(a1)?))),
-                        "sqrt" => interms.push(Number::Float(OrderedFloat(self.sqrt(a1)?))),
-                        "log" => interms.push(Number::Float(OrderedFloat(self.log(a1)?))),
-                        "exp" => interms.push(Number::Float(OrderedFloat(self.exp(a1)?))),
-                        "acos" => interms.push(Number::Float(OrderedFloat(self.acos(a1)?))),
-                        "asin" => interms.push(Number::Float(OrderedFloat(self.asin(a1)?))),
-                        "atan" => interms.push(Number::Float(OrderedFloat(self.atan(a1)?))),
-                        "abs" => interms.push(a1.abs()),
-                        "float" => interms.push(Number::Float(OrderedFloat(self.float(a1)?))),
-                        "truncate" => interms.push(Number::Integer(self.truncate(a1))),
-                        "round" => interms.push(Number::Integer(self.round(a1)?)),
-                        "ceiling" => interms.push(Number::Integer(self.ceiling(a1))),
-                        "floor" => interms.push(Number::Integer(self.floor(a1))),
-                        "\\" => interms.push(Number::Integer(self.bitwise_complement(a1)?)),
-                        "sign" => interms.push(Number::Integer(self.sign(a1))),
-                        _ => {
-                            return Err(self.error_form(MachineError::instantiation_error(), caller))
-                        }
+            Addr::Lis(l) if !self.flags.double_quotes.is_atom() => {
+                let addr = self.heap.put_constant(c.clone());
+                self.unify(Addr::Lis(l), addr);
+            }
+            Addr::PStrLocation(h, n) => {
+                self.fail = self.to_complete_string(h, n).map(|s1| {
+                    if let Constant::String(ref s2) = c {
+                        &s1 != &**s2
+                    } else {
+                        true
                     }
-                }
-                HeapCellValue::Addr(Addr::Con(Constant::Integer(n))) => {
-                    interms.push(Number::Integer(n))
-                }
-                HeapCellValue::Addr(Addr::Con(Constant::Float(n))) => {
-                    interms.push(Number::Float(n))
-                }
-                HeapCellValue::Addr(Addr::Con(Constant::Rational(n))) => {
-                    interms.push(Number::Rational(n))
-                }
-                HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _)))
-                    if name.as_str() == "pi" =>
-                {
-                    interms.push(Number::Float(OrderedFloat(f64::consts::PI)))
-                }
-                _ => {
-                    return Err(self.error_form(MachineError::instantiation_error(), caller));
-                }
+                }).unwrap_or(true);
             }
-        }
-
-        Ok(interms.pop().unwrap())
-    }
-
-    fn rdiv(&self, r1: Rational, r2: Rational) -> Result<Rational, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(rdiv)"), 2);
-
-        if r2 == 0 {
-            Err(self.error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-        } else {
-            Ok(r1 / r2)
-        }
-    }
-
-    fn int_floor_div(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(div)"), 2);
-
-        match n1 / n2 {
-            Ok(result) => Ok(rnd_i(&result).to_owned()),
-            Err(e) => Err(self.error_form(MachineError::evaluation_error(e), stub)),
-        }
-    }
-
-    fn idiv(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(//)"), 2);
-
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                if n2 == 0 {
-                    Err(self
-                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
+            addr => {
+                if let Some(r) = addr.as_var() {
+                    let addr = self.heap.put_constant(c.clone());
+                    self.bind(r, addr);
                 } else {
-                    Ok(n1.div_rem(n2).0)
+                    self.fail = true;
                 }
             }
-            (Number::Integer(_), n2) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())),
-                stub,
-            )),
-            (n1, _) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())),
-                stub,
-            )),
-        }
-    }
-
-    fn div(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(/)"), 2);
-
-        if n2.is_zero() {
-            Err(self.error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-        } else {
-            try_numeric_result!(self, n1 / n2, stub)
-        }
+        };
     }
 
-    fn atan2(&self, n1: Number, n2: Number) -> Result<f64, MachineStub> {
+    pub(super)
+    fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
         let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
 
-        if n1.is_zero() && n2.is_zero() {
-            Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub))
-        } else {
-            let f1 = self.float(n1)?;
-            let f2 = self.float(n2)?;
-
-            self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.atan2(f2))
-        }
-    }
-
-    fn int_pow(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        if n1.is_zero() && n2.is_negative() {
-            let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
-            return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub));
-        }
+        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));
 
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                if n1 != 1 && n2 < 0 {
-                    let n = Addr::Con(Constant::Integer(n1));
-                    let stub = MachineError::functor_stub(clause_name!("^"), 2);
+                self.interms[t - 1] = try_or_fail!(self, try_numeric_result!(self, n1 + n2, stub));
+                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));
 
-                    Err(self.error_form(MachineError::type_error(ValidType::Float, n), stub))
-                } else {
-                    Ok(Number::Integer(binary_pow(n1, n2)))
-                }
+                self.interms[t - 1] = try_or_fail!(self, try_numeric_result!(self, n1 - n2, stub));
+                self.p += 1;
             }
-            (n1, Number::Integer(n2)) => {
-                let f1 = self.float(n1)?;
-                let f2 = self.float(Number::Integer(n2))?;
+            &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.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.powf(f2))
-                    .map(|f| Number::Float(OrderedFloat(f)))
+                self.interms[t - 1] = try_or_fail!(self, try_numeric_result!(self, n1 * n2, stub));
+                self.p += 1;
             }
-            (n1, n2) => {
-                let f2 = self.float(n2)?;
+            &ArithmeticInstruction::Max(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));
 
-                if n1.is_negative() && f2 != f2.floor() {
-                    let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
-                    return Err(
-                        self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub)
-                    );
-                }
+                self.interms[t - 1] = try_or_fail!(self, self.max(n1, n2));
+                self.p += 1;
+            }
+            &ArithmeticInstruction::Min(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));
 
-                let f1 = self.float(n1)?;
-                self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.powf(f2))
-                    .map(|f| Number::Float(OrderedFloat(f)))
+                self.interms[t - 1] = try_or_fail!(self, self.min(n1, n2));
+                self.p += 1;
             }
-        }
-    }
+            &ArithmeticInstruction::IntPow(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));
 
-    fn gcd(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                Ok(n1.gcd(&n2))
+                self.interms[t - 1] = try_or_fail!(self, self.int_pow(n1, n2));
+                self.p += 1;
             }
-            (Number::Float(f), _) | (_, Number::Float(f)) => {
-                let n = Addr::Con(Constant::Float(f));
-                let stub = MachineError::functor_stub(clause_name!("gcd"), 2);
+            &ArithmeticInstruction::Gcd(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));
 
-                Err(self.error_form(MachineError::type_error(ValidType::Integer, n), stub))
+                self.interms[t - 1] =
+                    Number::Integer(Rc::new(try_or_fail!(self, self.gcd(n1, n2))));
+                self.p += 1;
             }
-            (Number::Rational(r), _) | (_, Number::Rational(r)) => {
-                let n = Addr::Con(Constant::Rational(r));
-                let stub = MachineError::functor_stub(clause_name!("gcd"), 2);
+            &ArithmeticInstruction::Pow(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));
 
-                Err(self.error_form(MachineError::type_error(ValidType::Integer, n), stub))
+                self.interms[t - 1] = try_or_fail!(self, self.pow(n1, n2, "(**)"));
+                self.p += 1;
             }
-        }
-    }
+            &ArithmeticInstruction::RDiv(ref a1, ref a2, t) => {
+                let stub = MachineError::functor_stub(clause_name!("(rdiv)"), 2);
 
-    fn float_pow(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        let f1 = result_f(&n1, rnd_f);
-        let f2 = result_f(&n2, rnd_f);
+                let (r1, stub) = try_or_fail!(self, self.get_rational(a1, stub));
+                let (r2, _)    = try_or_fail!(self, self.get_rational(a2, stub));
 
-        let stub = MachineError::functor_stub(clause_name!("(**)"), 2);
+                self.interms[t - 1] =
+                    Number::Rational(Rc::new(try_or_fail!(self, self.rdiv(r1, r2))));
+                self.p += 1;
+            }
+            &ArithmeticInstruction::IntFloorDiv(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));
 
-        let f1 = try_numeric_result!(self, f1, stub.clone())?;
-        let f2 = try_numeric_result!(self, f2, stub.clone())?;
-
-        let result = result_f(&Number::Float(OrderedFloat(f1.powf(f2))), rnd_f);
-
-        Ok(Number::Float(OrderedFloat(try_numeric_result!(
-            self, result, stub
-        )?)))
-    }
-
-    fn pow(&self, n1: Number, n2: Number, culprit: &'static str) -> Result<Number, MachineStub> {
-        if n2.is_negative() && n1.is_zero() {
-            let stub = MachineError::functor_stub(clause_name!(culprit), 2);
-            return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub));
-        }
-
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Integer(binary_pow(n1, n2))),
-            (n1, n2) => self.float_pow(n1, n2),
-        }
-    }
-
-    fn unary_float_fn_template<FloatFn>(&self, n1: Number, f: FloatFn) -> Result<f64, MachineStub>
-    where
-        FloatFn: Fn(f64) -> f64,
-    {
-        let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
-
-        let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub.clone())?;
-        let f1 = result_f(&Number::Float(OrderedFloat(f(f1))), rnd_f);
-
-        try_numeric_result!(self, f1, stub)
-    }
-
-    fn sin(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.sin())
-    }
-
-    fn cos(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.cos())
-    }
-
-    fn tan(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.tan())
-    }
-
-    fn log(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.log(f64::consts::E))
-    }
-
-    fn exp(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.exp())
-    }
-
-    fn asin(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.asin())
-    }
-
-    fn acos(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.acos())
-    }
-
-    fn atan(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.atan())
-    }
-
-    fn sqrt(&self, n1: Number) -> Result<f64, MachineStub> {
-        if n1.is_negative() {
-            let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
-            return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub));
-        }
-
-        self.unary_float_fn_template(n1, |f| f.sqrt())
-    }
-
-    fn float(&self, n: Number) -> Result<f64, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
-        try_numeric_result!(self, result_f(&n, rnd_f), stub)
-    }
-
-    fn floor(&self, n1: Number) -> Integer {
-        rnd_i(&n1).to_owned()
-    }
-
-    fn ceiling(&self, n1: Number) -> Integer {
-        -self.floor(-n1)
-    }
-
-    fn truncate(&self, n: Number) -> Integer {
-        if n.is_negative() {
-            -self.floor(n.abs())
-        } else {
-            self.floor(n)
-        }
-    }
-
-    fn round(&self, n: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
-
-        let result = n + Number::Float(OrderedFloat(0.5f64));
-        let result = try_numeric_result!(self, result, stub)?;
-
-        Ok(self.floor(result))
-    }
-
-    fn shr(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(>>)"), 2);
-
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
-                Some(n2) => Ok(n1 >> n2),
-                _ => Ok(n1 >> u32::max_value()),
-            },
-            (Number::Integer(_), n2) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())),
-                stub,
-            )),
-            (n1, _) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())),
-                stub,
-            )),
-        }
-    }
-
-    fn shl(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(<<)"), 2);
-
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
-                Some(n2) => Ok(n1 << n2),
-                _ => Ok(n1 << u32::max_value()),
-            },
-            (Number::Integer(_), n2) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())),
-                stub,
-            )),
-            (n1, _) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())),
-                stub,
-            )),
-        }
-    }
-
-    fn bitwise_complement(&self, n1: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(\\)"), 2);
-
-        match n1 {
-            Number::Integer(n1) => Ok(!n1),
-            _ => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())),
-                stub,
-            )),
-        }
-    }
-
-    fn xor(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(xor)"), 2);
-
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => Ok(n1 ^ n2),
-            (Number::Integer(_), n2) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())),
-                stub,
-            )),
-            (n1, _) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())),
-                stub,
-            )),
-        }
-    }
-
-    fn and(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(/\\)"), 2);
-
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => Ok(n1 & n2),
-            (Number::Integer(_), n2) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())),
-                stub,
-            )),
-            (n1, _) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())),
-                stub,
-            )),
-        }
-    }
-
-    fn modulus(&self, x: Number, y: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(mod)"), 2);
-
-        match (x, y) {
-            (Number::Integer(x), Number::Integer(y)) => {
-                if y == 0 {
-                    Err(self
-                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-                } else {
-                    Ok(x.div_rem_floor(y).1)
-                }
-            }
-            (Number::Integer(_), n2) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())),
-                stub,
-            )),
-            (n1, _) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())),
-                stub,
-            )),
-        }
-    }
-
-    fn max(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                if n1 > n2 {
-                    Ok(Number::Integer(n1))
-                } else {
-                    Ok(Number::Integer(n2))
-                }
-            }
-            (n1, n2) => {
-                let stub = MachineError::functor_stub(clause_name!("max"), 2);
-
-                let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub.clone())?;
-                let f2 = try_numeric_result!(self, result_f(&n2, rnd_f), stub)?;
-
-                Ok(Number::Float(max(OrderedFloat(f1), OrderedFloat(f2))))
-            }
-        }
-    }
-
-    fn min(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                if n1 < n2 {
-                    Ok(Number::Integer(n1))
-                } else {
-                    Ok(Number::Integer(n2))
-                }
-            }
-            (n1, n2) => {
-                let stub = MachineError::functor_stub(clause_name!("max"), 2);
-
-                let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub.clone())?;
-                let f2 = try_numeric_result!(self, result_f(&n2, rnd_f), stub)?;
-
-                Ok(Number::Float(min(OrderedFloat(f1), OrderedFloat(f2))))
-            }
-        }
-    }
-
-    fn sign(&self, n: Number) -> Integer {
-        if n.is_positive() {
-            Integer::from(1)
-        } else if n.is_negative() {
-            Integer::from(-1)
-        } else {
-            Integer::from(0)
-        }
-    }
-
-    fn remainder(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(rem)"), 2);
-
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                if n2 == 0 {
-                    Err(self
-                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-                } else {
-                    Ok(n1 % n2)
-                }
-            }
-            (Number::Integer(_), n2) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())),
-                stub,
-            )),
-            (n1, _) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())),
-                stub,
-            )),
-        }
-    }
-
-    fn or(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(\\/)"), 2);
-
-        match (n1, n2) {
-            (Number::Integer(n1), Number::Integer(n2)) => Ok(n1 | n2),
-            (Number::Integer(_), n2) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())),
-                stub,
-            )),
-            (n1, _) => Err(self.error_form(
-                MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())),
-                stub,
-            )),
-        }
-    }
-
-    pub(super)
-    fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
-        let stub = MachineError::functor_stub(clause_name!("(is)"), 2);
-
-        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] = try_or_fail!(self, try_numeric_result!(self, n1 + n2, stub));
-                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] = try_or_fail!(self, try_numeric_result!(self, n1 - n2, stub));
-                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] = try_or_fail!(self, try_numeric_result!(self, n1 * n2, stub));
-                self.p += 1;
-            }
-            &ArithmeticInstruction::Max(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.max(n1, n2));
-                self.p += 1;
-            }
-            &ArithmeticInstruction::Min(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.min(n1, n2));
-                self.p += 1;
-            }
-            &ArithmeticInstruction::IntPow(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.int_pow(n1, n2));
-                self.p += 1;
-            }
-            &ArithmeticInstruction::Gcd(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.gcd(n1, n2)));
-                self.p += 1;
-            }
-            &ArithmeticInstruction::Pow(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.pow(n1, n2, "(**)"));
-                self.p += 1;
-            }
-            &ArithmeticInstruction::RDiv(ref a1, ref a2, t) => {
-                let stub = MachineError::functor_stub(clause_name!("(rdiv)"), 2);
-
-                let r1 = try_or_fail!(self, self.get_rational(a1, &stub));
-                let r2 = try_or_fail!(self, self.get_rational(a2, &stub));
-
-                self.interms[t - 1] = Number::Rational(try_or_fail!(self, self.rdiv(r1, r2)));
-                self.p += 1;
-            }
-            &ArithmeticInstruction::IntFloorDiv(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(Rc::new(try_or_fail!(self, self.int_floor_div(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.int_floor_div(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)));
+                    Number::Integer(Rc::new(try_or_fail!(self, self.idiv(n1, n2))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Abs(ref a1, t) => {
@@ -1396,7 +810,7 @@ impl MachineState {
             &ArithmeticInstruction::Sign(ref a1, t) => {
                 let n = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = Number::Integer(self.sign(n));
+                self.interms[t - 1] = Number::Integer(Rc::new(self.sign(n)));
                 self.p += 1;
             }
             &ArithmeticInstruction::Neg(ref a1, t) => {
@@ -1409,7 +823,7 @@ impl MachineState {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
                 self.interms[t - 1] =
-                    Number::Integer(try_or_fail!(self, self.bitwise_complement(n1)));
+                   Number::Integer(Rc::new(try_or_fail!(self, self.bitwise_complement(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Div(ref a1, ref a2, t) => {
@@ -1423,49 +837,56 @@ impl MachineState {
                 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.interms[t - 1] =
+                   Number::Integer(Rc::new(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.interms[t - 1] =
+                   Number::Integer(Rc::new(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.interms[t - 1] =
+       Number::Integer(Rc::new(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.interms[t - 1] =
+                   Number::Integer(Rc::new(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.interms[t - 1] =
+       Number::Integer(Rc::new(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.interms[t - 1] =
+       Number::Integer(Rc::new(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.interms[t - 1] =
+       Number::Integer(Rc::new(try_or_fail!(self, self.remainder(n1, n2))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Cos(ref a1, t) => {
@@ -1544,25 +965,29 @@ impl MachineState {
             &ArithmeticInstruction::Truncate(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = Number::Integer(self.truncate(n1));
+                self.interms[t - 1] =
+                   Number::Integer(Rc::new(self.truncate(n1)));
                 self.p += 1;
             }
             &ArithmeticInstruction::Round(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.round(n1)));
+                self.interms[t - 1] =
+                   Number::Integer(Rc::new(try_or_fail!(self, self.round(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Ceiling(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = Number::Integer(self.ceiling(n1));
+                self.interms[t - 1] =
+                   Number::Integer(Rc::new(self.ceiling(n1)));
                 self.p += 1;
             }
             &ArithmeticInstruction::Floor(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = Number::Integer(self.floor(n1));
+                self.interms[t - 1] =
+                   Number::Integer(Rc::new(self.floor(n1)));
                 self.p += 1;
             }
             &ArithmeticInstruction::Plus(ref a1, t) => {
@@ -1579,23 +1004,12 @@ impl MachineState {
         match instr {
             &FactInstruction::GetConstant(_, ref c, reg) => {
                 let addr = self[reg].clone();
-                self.write_constant_to_var(addr, c.clone());
+                self.write_constant_to_var(addr, c);
             }
             &FactInstruction::GetList(_, reg) => {
                 let addr = self.store(self.deref(self[reg].clone()));
 
                 match addr {
-                    Addr::Con(Constant::String(n, s)) =>
-                        match self.flags.double_quotes {
-                            DoubleQuotes::Chars | DoubleQuotes::Codes
-                                if s.len() > n => {
-                                    self.s = HeapPtr::StringChar(n, s);
-                                    self.mode = MachineMode::Read;
-                                }
-                            _ => {
-                                self.fail = true
-                            }
-                        },
                     Addr::PStrLocation(h, n) => {
                         self.s = HeapPtr::PStrChar(h, n);
                         self.mode = MachineMode::Read;
@@ -1647,7 +1061,9 @@ impl MachineState {
                     _ => self.fail = true,
                 };
             }
-            &FactInstruction::GetVariable(norm, arg) => self[norm] = self.registers[arg].clone(),
+            &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();
@@ -1658,10 +1074,10 @@ impl MachineState {
                 match self.mode {
                     MachineMode::Read => {
                         let addr = self.s.read(&self.heap);
-                        self.write_constant_to_var(addr, c.clone());
+                        self.write_constant_to_var(addr, c);
                     }
                     MachineMode::Write => {
-                        self.heap.push(HeapCellValue::Addr(Addr::Con(c.clone())));
+                        self.heap.put_constant(c.clone());
                     }
                 };
 
@@ -1692,7 +1108,7 @@ impl MachineState {
 
                         if let Addr::HeapCell(hc) = addr {
                             if hc < h {
-                                let val = self.heap[hc].clone();
+                                let val = self.heap.clone(hc);
 
                                 self.heap.push(val);
                                 self.increment_s_ptr(1);
@@ -1711,7 +1127,7 @@ impl MachineState {
             &FactInstruction::UnifyValue(reg) => {
                 match self.mode {
                     MachineMode::Read => {
-                        let reg_addr = self[reg].clone();
+                        let reg_addr = self[reg];
                         self.unify(reg_addr, self.s.read(&self.heap));
                     }
                     MachineMode::Write => {
@@ -1741,38 +1157,31 @@ impl MachineState {
     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 addr = self[temp_v!(1)];
+                let addr = self.store(self.deref(addr));
 
                 let offset = match addr {
                     Addr::HeapCell(_) | Addr::StackCell(..)
                   | Addr::AttrVar(..) | Addr::Stream(_) => {
-                        v
-                    }
-                    Addr::Con(Constant::String(n, ref s)) => {
+                      v
+                  }
+                    Addr::PStrLocation(..) => {
                         if !self.flags.double_quotes.is_atom() {
-                            if n >= s.len() {
-                                c
-                            } else {
-                                l
-                            }
+                            l
                         } else {
                             c
                         }
                     }
-                    Addr::Con(_) => {
-                        c
-                    }
-                    Addr::Lis(_) | Addr::PStrLocation(..) => {
+                    Addr::Char(_) | Addr::CharCode(_) | Addr::Con(_) | Addr::CutPoint(_)
+                        | Addr::EmptyList | Addr::Float(_) | Addr::Usize(_) => {
+                            c
+                        }
+                    Addr::Lis(_) => {
                         l
                     }
                     Addr::Str(_) => {
                         s
                     }
-                    Addr::DBRef(_) => {
-                        self.fail = true;
-                        return;
-                    }
                 };
 
                 match offset {
@@ -1781,16 +1190,21 @@ impl MachineState {
                 };
             }
             &IndexingInstruction::SwitchOnConstant(_, ref hm) => {
-                let a1 = self.registers[1].clone();
-                let addr = self.store(self.deref(a1));
+                let addr = self[temp_v!(1)];
+                let addr = self.store(self.deref(addr));
 
-                let offset = match addr {
-                    Addr::Con(constant) => match hm.get(&constant) {
-                        Some(offset) => *offset,
-                        _ => 0,
-                    },
-                    _ => 0,
-                };
+                let offset =
+                    match addr.as_constant(&self) {
+                        Some(c) => {
+                            match hm.get(&c) {
+                                Some(offset) => *offset,
+                                _ => 0,
+                            }
+                        }
+                        None => {
+                            0
+                        }
+                    };
 
                 match offset {
                     0 => self.fail = true,
@@ -1812,7 +1226,9 @@ impl MachineState {
                             0
                         }
                     }
-                    _ => 0,
+                    _ => {
+                        0
+                    }
                 };
 
                 match offset {
@@ -1825,11 +1241,15 @@ impl MachineState {
 
     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::GetVariable(norm, arg) => {
+                self[norm] = self.registers[arg].clone();
+            }
+            &QueryInstruction::PutConstant(_, ref c, reg) => {
+                self[reg] = self.heap.put_constant(c.clone());
+            }
+            &QueryInstruction::PutList(_, reg) => {
+                self[reg] = Addr::Lis(self.heap.h());
             }
-            &QueryInstruction::PutList(_, reg) => self[reg] = Addr::Lis(self.heap.h()),
             &QueryInstruction::PutStructure(ref ct, arity, reg) => {
                 let h = self.heap.h();
 
@@ -1851,14 +1271,16 @@ impl MachineState {
                     self.registers[arg] = self.heap[h].as_addr(h);
                 }
             }
-            &QueryInstruction::PutValue(norm, arg) => self.registers[arg] = self[norm].clone(),
+            &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();
+                        self.registers[arg] = self[norm];
                     }
                     RegType::Temp(_) => {
                         let h = self.heap.h();
@@ -1870,10 +1292,10 @@ impl MachineState {
                 };
             }
             &QueryInstruction::SetConstant(ref c) => {
-                self.heap.push(HeapCellValue::Addr(Addr::Con(c.clone())));
+                self.heap.put_constant(c.clone());
             }
             &QueryInstruction::SetLocalValue(reg) => {
-                let addr = self.deref(self[reg].clone());
+                let addr = self.deref(self[reg]);
                 let h = self.heap.h();
 
                 if addr < Ref::HeapCell(h) {
@@ -1933,14 +1355,15 @@ impl MachineState {
     }
 
     pub(super) fn setup_call_n(&mut self, arity: usize) -> Option<PredicateKey> {
-        let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
         let addr = self.store(self.deref(self.registers[arity].clone()));
 
         let (name, narity) = match addr {
             Addr::Str(a) => {
-                let result = self.heap[a].clone();
+                let result = self.heap.clone(a);
 
                 if let HeapCellValue::NamedStr(narity, name, _) = result {
+                    let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
+
                     if narity + arity > 63 {
                         let representation_error = self.error_form(
                             MachineError::representation_error(RepFlag::MaxArity),
@@ -1965,19 +1388,34 @@ impl MachineState {
                     return None;
                 }
             }
-            Addr::Con(Constant::Atom(name, _)) => (name, 0),
+            Addr::Con(h) =>
+                match &self.heap[h] {
+                    HeapCellValue::Atom(ref name, _) => {
+                        (name.clone(), 0)
+                    }
+                    _ => {
+                        self.fail = true;
+                        return None;
+                    }
+                }
             Addr::HeapCell(_) | Addr::StackCell(_, _) => {
-                let instantiation_error =
-                    self.error_form(MachineError::instantiation_error(), stub);
-                self.throw_exception(instantiation_error);
+                let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
+                let instantiation_error = self.error_form(
+                    MachineError::instantiation_error(),
+                    stub,
+                );
 
+                self.throw_exception(instantiation_error);
                 return None;
             }
             _ => {
-                let type_error =
-                    self.error_form(MachineError::type_error(ValidType::Callable, addr), stub);
-                self.throw_exception(type_error);
+                let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
+                let type_error = self.error_form(
+                    MachineError::type_error(self.heap.h(), ValidType::Callable, addr),
+                    stub,
+                );
 
+                self.throw_exception(type_error);
                 return None;
             }
         };
@@ -2016,116 +1454,109 @@ impl MachineState {
     // arg(+N, +Term, ?Arg)
     pub(super) fn try_arg(&mut self) -> CallResult {
         let stub = MachineError::functor_stub(clause_name!("arg"), 3);
-        let n = self.store(self.deref(self[temp_v!(1)].clone()));
+        let n = self.store(self.deref(self[temp_v!(1)]));
 
         match n {
-            Addr::HeapCell(_) | Addr::StackCell(..) =>
-            // 8.5.2.3 a)
-            {
+            Addr::HeapCell(_) | Addr::StackCell(..) => { // 8.5.2.3 a)
                 return Err(self.error_form(MachineError::instantiation_error(), stub))
             }
-            Addr::Con(Constant::Integer(n)) => {
-                if n < 0 {
-                    // 8.5.2.3 e)
-                    let n = Addr::Con(Constant::Integer(n));
-                    let dom_err = MachineError::domain_error(DomainError::NotLessThanZero, n);
-
-                    return Err(self.error_form(dom_err, stub));
-                }
+            Addr::Con(h) => {
+                if let HeapCellValue::Integer(n) = self.heap.clone(h) {
+                    if &*n < &0 { // 8.5.2.3 e)
+                        let n = Number::Integer(n);
+                        let dom_err = MachineError::domain_error(
+                            DomainErrorType::NotLessThanZero,
+                            n,
+                        );
 
-                let n = match n.to_usize() {
-                    Some(n) => n,
-                    None => {
-                        self.fail = true;
-                        return Ok(());
+                        return Err(self.error_form(dom_err, stub));
                     }
-                };
 
-                let term = self.store(self.deref(self[temp_v!(2)].clone()));
+                    let n = match n.to_usize() {
+                        Some(n) => n,
+                        None => {
+                            self.fail = true;
+                            return Ok(());
+                        }
+                    };
 
-                match term {
-                    Addr::HeapCell(_) | Addr::StackCell(..) =>
-                    // 8.5.2.3 b)
-                    {
-                        return Err(self.error_form(MachineError::instantiation_error(), stub))
-                    }
-                    Addr::Str(o) => match self.heap[o].clone() {
-                        HeapCellValue::NamedStr(arity, _, _) if 1 <= n && n <= arity => {
-                            let a3 = self[temp_v!(3)].clone();
-                            let h_a = Addr::HeapCell(o + n);
+                    let term = self.store(self.deref(self[temp_v!(2)].clone()));
 
-                            self.unify(a3, h_a);
+                    match term {
+                        Addr::HeapCell(_) | Addr::StackCell(..) => { // 8.5.2.3 b)
+                            return Err(self.error_form(MachineError::instantiation_error(), stub))
                         }
-                        _ => self.fail = true,
-                    },
-                    Addr::Lis(l) => {
-                        if n == 1 || n == 2 {
-                            let a3 = self[temp_v!(3)].clone();
-                            let h_a = Addr::HeapCell(l + n - 1);
+                        Addr::Str(o) => match self.heap.clone(o) {
+                            HeapCellValue::NamedStr(arity, _, _) if 1 <= n && n <= arity => {
+                                let a3 = self[temp_v!(3)].clone();
+                                let h_a = Addr::HeapCell(o + n);
 
-                            self.unify(a3, h_a);
-                        } else {
-                            self.fail = true;
-                        }
-                    }
-                    Addr::PStrLocation(h, offset) => {
-                        if n == 1 || n == 2 {
-                            let a3 = self[temp_v!(3)].clone();
-                            let h_a =
-                                if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] {
-                                    let s = pstr.block_as_str();
+                                self.unify(a3, h_a);
+                            }
+                            _ => self.fail = true,
+                        },
+                        Addr::Lis(l) => {
+                            if n == 1 || n == 2 {
+                                let a3 = self[temp_v!(3)].clone();
+                                let h_a = Addr::HeapCell(l + n - 1);
 
-                                    if let Some(c) = s[offset ..].chars().next() {
-                                        if n == 1 {
-                                            Addr::Con(Constant::Char(c))
+                                self.unify(a3, h_a);
+                            } else {
+                                self.fail = true;
+                            }
+                        }
+                        Addr::PStrLocation(h, offset) => {
+                            if n == 1 || n == 2 {
+                                let a3 = self[temp_v!(3)].clone();
+                                let h_a =
+                                    if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] {
+                                        if let Some(c) = pstr.range_from(offset ..).next() {
+                                            if n == 1 {
+                                                Addr::Char(c)
+                                            } else {
+                                                Addr::PStrLocation(h, offset + c.len_utf8())
+                                            }
                                         } else {
-                                            Addr::PStrLocation(h, offset + c.len_utf8())
+                                            unreachable!()
                                         }
                                     } else {
                                         unreachable!()
-                                    }
-                                } else {
-                                    unreachable!()
-                                };
+                                    };
 
-                            self.unify(a3, h_a);
-                        } else {
-                            self.fail = true;
-                        }
-                    }
-                    Addr::Con(Constant::String(o, ref s))
-                        if !self.flags.double_quotes.is_atom() && !s[o ..].is_empty() =>
-                    {
-                        if n == 1 || n == 2 {
-                            let a3 = self[temp_v!(3)].clone();
-                            let c = s[o ..].chars().next().unwrap();
-
-                            let h_a = if n == 1 {
-                                Addr::Con(Constant::Char(c))
+                                self.unify(a3, h_a);
                             } else {
-                                Addr::Con(Constant::String(o + c.len_utf8(), s.clone()))
-                            };
-
-                            self.unify(a3, h_a);
-                        } else {
-                            self.fail = true;
+                                self.fail = true;
+                            }
+                        }
+                        _ => { // 8.5.2.3 d)
+                            return Err(self.error_form(
+                                MachineError::type_error(
+                                    self.heap.h(),
+                                    ValidType::Compound,
+                                    term,
+                                ),
+                                stub,
+                            ))
                         }
                     }
-                    _ =>
-                    // 8.5.2.3 d)
-                    {
-                        return Err(self.error_form(
-                            MachineError::type_error(ValidType::Compound, term),
-                            stub,
-                        ))
-                    }
+                } else {
+                    return Err(self.error_form(
+                        MachineError::type_error(
+                            self.heap.h(),
+                            ValidType::Integer,
+                            Addr::Con(h),
+                        ),
+                        stub,
+                    ))
                 }
             }
-            _ =>
-            // 8.5.2.3 c)
-            {
+            _ => { // 8.5.2.3 c)
                 return Err(self.error_form(
-                    MachineError::type_error(ValidType::Integer, n),
+                    MachineError::type_error(
+                        self.heap.h(),
+                        ValidType::Integer,
+                        n,
+                    ),
                     stub,
                 ))
             }
@@ -2155,18 +1586,21 @@ impl MachineState {
         let a2 = self[temp_v!(2)].clone();
 
         match self.compare_term_test(&a1, &a2) {
-            Ordering::Greater => match qt {
+            Some(Ordering::Greater) => match qt {
                 CompareTermQT::GreaterThan | CompareTermQT::GreaterThanOrEqual => return,
                 _ => self.fail = true,
             },
-            Ordering::Equal => match qt {
+            Some(Ordering::Equal) => match qt {
                 CompareTermQT::GreaterThanOrEqual | CompareTermQT::LessThanOrEqual => return,
                 _ => self.fail = true,
             },
-            Ordering::Less => match qt {
+            Some(Ordering::Less) => match qt {
                 CompareTermQT::LessThan | CompareTermQT::LessThanOrEqual => return,
                 _ => self.fail = true,
             },
+            None => {
+                self.fail = true;
+            }
         };
     }
 
@@ -2177,35 +1611,74 @@ impl MachineState {
 
         while let Some((v1, v2)) = iter.next() {
             match (v1, v2) {
-                (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) =>
-                    if ar1 != ar2 || n1 != n2 {
-                        return true;
-                    },
-                (HeapCellValue::Addr(Addr::PStrLocation(..)),
-                 HeapCellValue::Addr(Addr::PStrLocation(..))) =>
-                    continue,
-                (HeapCellValue::Addr(Addr::PStrLocation(..)),
-                 HeapCellValue::Addr(Addr::Con(Constant::String(..))))
-              | (HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                 HeapCellValue::Addr(Addr::PStrLocation(..)))
-                    if self.flags.double_quotes.is_chars() => {
+                (Addr::Str(s1), Addr::Str(s2)) => {
+                    if let HeapCellValue::NamedStr(ar1, n1, _) = &self.heap[s1] {
+                        if let HeapCellValue::NamedStr(ar2, n2, _) = &self.heap[s2] {
+                            if ar1 != ar2 || n1 != n2 {
+                                return true;
+                            }
+                        } else {
+                            unreachable!()
+                        }
+                    } else {
+                        unreachable!()
+                    }
+                }
+                (Addr::PStrLocation(..), Addr::Lis(_))
+              | (Addr::Lis(_), Addr::PStrLocation(..)) 
+                    if !self.flags.double_quotes.is_atom() => {
                         continue;
                     }
-                (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Lis(_))) =>
-                    continue,
-                (HeapCellValue::Addr(Addr::Con(Constant::EmptyList)),
-                 HeapCellValue::Addr(Addr::Con(Constant::String(n, s))))
-              | (HeapCellValue::Addr(Addr::Con(Constant::String(n, s))),
-                 HeapCellValue::Addr(Addr::Con(Constant::EmptyList))) =>
-                    return match self.flags.double_quotes {
-                        DoubleQuotes::Atom => true,
-                        _ => n < s.len()
-                    },
-                (HeapCellValue::Addr(a1), HeapCellValue::Addr(a2)) =>
+                (Addr::PStrLocation(..), Addr::PStrLocation(..)) => {
+                    continue;
+                }
+                (Addr::Lis(_), Addr::Lis(_)) => {
+                    continue;
+                }
+                (Addr::Con(h1), Addr::Con(h2)) => {
+                    match (&self.heap[h1], &self.heap[h2]) {
+                        (
+                            &HeapCellValue::Integer(ref n1),
+                            &HeapCellValue::Integer(ref n2),
+                        ) => {
+                            if n1 != n2 {
+                                return true;
+                            }
+                        }
+                        (
+                            &HeapCellValue::Rational(ref n1),
+                            &HeapCellValue::Rational(ref n2),
+                        ) => {
+                            if n1 != n2 {
+                                return true;
+                            }
+                        }
+                        (
+                            &HeapCellValue::DBRef(ref db_ref_1),
+                            &HeapCellValue::DBRef(ref db_ref_2),
+                        ) => {
+                            if db_ref_1 != db_ref_2 {
+                                return true;
+                            }
+                        }
+                        (
+                            &HeapCellValue::Atom(ref n1, ref spec_1),
+                            &HeapCellValue::Atom(ref n2, ref spec_2),
+                        ) => {
+                            if n1 != n2 || spec_1 != spec_2 {
+                                return true;
+                            }
+                        }
+                        _ => {
+                            return true;
+                        }
+                    }
+                }
+                (a1, a2) => {
                     if a1 != a2 {
                         return true;
-                    },
-                _ => return true,
+                    }
+                }
             }
         }
 
@@ -2213,340 +1686,383 @@ impl MachineState {
         iter.first_to_expire != Ordering::Equal
     }
 
-    pub(super) fn compare_term_test(&self, a1: &Addr, a2: &Addr) -> Ordering {
+    pub(super) fn compare_term_test(&self, a1: &Addr, a2: &Addr) -> Option<Ordering> {
         let mut iter = self.zipped_acyclic_pre_order_iter(a1.clone(), a2.clone());
 
         while let Some((v1, v2)) = iter.next() {
-            match (v1, v2) {
-                (
-                    HeapCellValue::Addr(Addr::Lis(_)),
-                    HeapCellValue::Addr(Addr::PStrLocation(..)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::PStrLocation(..)),
-                    HeapCellValue::Addr(Addr::Lis(_)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::PStrLocation(..)),
-                    HeapCellValue::Addr(Addr::PStrLocation(..)),
-                ) => {
-                }
-                (
-                    HeapCellValue::Addr(Addr::PStrLocation(..)),
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                    HeapCellValue::Addr(Addr::PStrLocation(..)),
-                ) if self.flags.double_quotes.is_chars() => {
-                }
-                (
-                    HeapCellValue::Addr(Addr::Lis(_)),
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                    HeapCellValue::Addr(Addr::Lis(_)),
-                ) if !self.flags.double_quotes.is_atom() => {
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::EmptyList)),
-                    HeapCellValue::Addr(Addr::Con(Constant::String(n, ref s))),
-                ) if !self.flags.double_quotes.is_atom() => {
-                    if s[n ..].is_empty() {
-                        return Ordering::Equal;
-                    } else {
-                        return Ordering::Greater;
-                    }
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Char(c1))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Char(c2))),
-                ) => {
-                    if c1 != c2 {
-                        return c1.cmp(&c2);
-                    }
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(atom, _))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Char(c))),
-                ) => {
-                    return if atom.as_str().chars().count() == 1 {
-                        atom.as_str().chars().next().cmp(&Some(c))
-                    } else {
-                        Ordering::Greater
-                    }
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Char(c))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(atom, _))),
-                ) => {
-                    return if atom.as_str().chars().count() == 1 {
-                        Some(c).cmp(&atom.as_str().chars().next())
-                    } else {
-                        Ordering::Less
+            let order_cat_v1 = v1.order_category(&self.heap);
+            let order_cat_v2 = v2.order_category(&self.heap);
+
+            if order_cat_v1 != order_cat_v1 {
+                return Some(order_cat_v1.cmp(&order_cat_v2));
+            }
+
+            match order_cat_v1 {
+                Some(TermOrderCategory::Variable) => {
+                    let v1 = v1.as_var().unwrap();
+                    let v2 = v2.as_var().unwrap();
+
+                    if v1 != v2 {
+                        return Some(v1.cmp(&v2));
                     }
                 }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(n, ref s))),
-                    HeapCellValue::Addr(Addr::Con(Constant::EmptyList)),
-                ) if !self.flags.double_quotes.is_atom() => {
-                    if s[n ..].is_empty() {
-                        return Ordering::Equal;
+                Some(TermOrderCategory::FloatingPoint) => {
+                    if let Addr::Float(f1) = v1 {
+                        if let Addr::Float(f2) = v2 {
+                            return Some(f1.cmp(&f2));
+                        } else {
+                            unreachable!()
+                        }
                     } else {
-                        return Ordering::Less;
-                    }
-                }
-                (
-                    HeapCellValue::Addr(Addr::HeapCell(hc1)),
-                    HeapCellValue::Addr(Addr::HeapCell(hc2)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::AttrVar(hc1)),
-                    HeapCellValue::Addr(Addr::HeapCell(hc2)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::HeapCell(hc1)),
-                    HeapCellValue::Addr(Addr::AttrVar(hc2)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::AttrVar(hc1)),
-                    HeapCellValue::Addr(Addr::AttrVar(hc2)),
-                ) => {
-                    if hc1 != hc2 {
-                        return hc1.cmp(&hc2);
-                    }
-                }
-                (HeapCellValue::Addr(Addr::HeapCell(_)), _)
-              | (HeapCellValue::Addr(Addr::AttrVar(_)), _) => {
-                    return Ordering::Less;
-                }
-                (
-                    HeapCellValue::Addr(Addr::StackCell(fr1, sc1)),
-                    HeapCellValue::Addr(Addr::StackCell(fr2, sc2)),
-                ) => {
-                    if fr1 > fr2 {
-                        return Ordering::Greater;
-                    } else if fr1 < fr2 || sc1 < sc2 {
-                        return Ordering::Less;
-                    } else if sc1 > sc2 {
-                        return Ordering::Greater;
-                    }
-                }
-                (
-                    HeapCellValue::Addr(Addr::StackCell(..)),
-                    HeapCellValue::Addr(Addr::HeapCell(_)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::StackCell(..)),
-                    HeapCellValue::Addr(Addr::AttrVar(_)),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (HeapCellValue::Addr(Addr::StackCell(..)), _) => {
-                    return Ordering::Less;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Integer(..))),
-                    HeapCellValue::Addr(Addr::HeapCell(_)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::Con(Constant::Integer(..))),
-                    HeapCellValue::Addr(Addr::AttrVar(_)),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Integer(..))),
-                    HeapCellValue::Addr(Addr::StackCell(..)),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Integer(n1))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Integer(n2))),
-                ) => {
-                    if n1 != n2 {
-                        return n1.cmp(&n2);
-                    }
-                }
-                (HeapCellValue::Addr(Addr::Con(Constant::Integer(_))), _) => {
-                    return Ordering::Less;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Float(..))),
-                    HeapCellValue::Addr(Addr::HeapCell(_)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::Con(Constant::Float(..))),
-                    HeapCellValue::Addr(Addr::AttrVar(_)),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Float(..))),
-                    HeapCellValue::Addr(Addr::StackCell(..)),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Float(n1))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Float(n2))),
-                ) => {
-                    if n1 != n2 {
-                        return n1.cmp(&n2);
-                    }
-                }
-                (HeapCellValue::Addr(Addr::Con(Constant::Float(_))), _) => return Ordering::Less,
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Rational(..))),
-                    HeapCellValue::Addr(Addr::HeapCell(_)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::Con(Constant::Rational(..))),
-                    HeapCellValue::Addr(Addr::AttrVar(_)),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Rational(..))),
-                    HeapCellValue::Addr(Addr::StackCell(..)),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Rational(n1))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Rational(n2))),
-                ) => {
-                    if n1 != n2 {
-                        return n1.cmp(&n2);
+                        unreachable!()
                     }
                 }
-                (HeapCellValue::Addr(Addr::Con(Constant::Rational(_))), _) => {
-                    return Ordering::Less
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                    HeapCellValue::Addr(Addr::HeapCell(_)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                    HeapCellValue::Addr(Addr::AttrVar(_)),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                    HeapCellValue::Addr(Addr::StackCell(..)),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Integer(_))),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Rational(_))),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Float(_))),
-                ) => {
-                    return Ordering::Greater;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(n1, s1))),
-                    HeapCellValue::Addr(Addr::Con(Constant::String(n2, s2))),
-                ) => {
-                    return s1[n1 ..].cmp(&s2[n2 ..]);
-                }
-                (HeapCellValue::Addr(Addr::Con(Constant::String(..))), _) => {
-                    return Ordering::Less;
-                }
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(..))),
-                    HeapCellValue::Addr(Addr::HeapCell(_)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(..))),
-                    HeapCellValue::Addr(Addr::AttrVar(_)),
-                ) => return Ordering::Greater,
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(..))),
-                    HeapCellValue::Addr(Addr::StackCell(..)),
-                ) => return Ordering::Greater,
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(..))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Float(_))),
-                ) => return Ordering::Greater,
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(..))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Integer(_))),
-                ) => return Ordering::Greater,
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(..))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Rational(_))),
-                ) => return Ordering::Greater,
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(..))),
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
-                ) => return Ordering::Greater,
-                (
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(s1, _))),
-                    HeapCellValue::Addr(Addr::Con(Constant::Atom(s2, _))),
-                ) => {
-                    if s1 != s2 {
-                        return s1.cmp(&s2);
+                Some(TermOrderCategory::Integer) => {
+                    match (v1, v2) {
+                        (
+                            Addr::Con(h1),
+                            Addr::Con(h2),
+                        ) => {
+                            match (&self.heap[h1], &self.heap[h2]) {
+                                (
+                                    HeapCellValue::Integer(ref n1),
+                                    HeapCellValue::Integer(ref n2),
+                                ) => {
+                                    if &*n1 != &*n2 {
+                                        return Some(n1.cmp(&*n2));
+                                    }
+                                }
+                                (
+                                    HeapCellValue::Rational(ref n1),
+                                    HeapCellValue::Integer(ref n2),
+                                ) => {
+                                    if &**n1 != &**n2 {
+                                        return n1.as_ref().partial_cmp(n2.as_ref());
+                                    }
+                                }
+                                (
+                                    HeapCellValue::Integer(ref n1),
+                                    HeapCellValue::Rational(ref n2),
+                                ) => {
+                                    if &**n1 != &**n2 {
+                                        return n1.as_ref().partial_cmp(n2.as_ref());
+                                    }
+                                }
+                                (
+                                    HeapCellValue::Rational(ref r1),
+                                    HeapCellValue::Rational(ref r2),
+                                ) => {
+                                    if &*r1 != &*r2 {
+                                        return Some(r1.cmp(r2));
+                                    }
+                                }
+                                _ => {
+                                    unreachable!()
+                                }
+                            }
+                        }
+                        _ => {
+                            unreachable!()
+                        }
                     }
                 }
-                (HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), _) => {
-                    return Ordering::Less;
-                }
-                (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) => {
-                    if ar1 < ar2 {
-                        return Ordering::Less;
-                    } else if ar1 > ar2 {
-                        return Ordering::Greater;
-                    } else if n1 != n2 {
-                        return n1.cmp(&n2);
+                Some(TermOrderCategory::Atom) => {
+                    match (v1, v2) {
+                        (
+                            Addr::Con(h1),
+                            Addr::Con(h2),
+                        ) => {
+                            if let HeapCellValue::Atom(ref n1, _) = &self.heap[h1] {
+                                if let HeapCellValue::Atom(ref n2, _) = &self.heap[h2] {
+                                    if n1 != n2 {
+                                        return Some(n1.cmp(&n2));
+                                    }
+                                } else {
+                                    unreachable!()
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::Con(h1),
+                            Addr::Char(c),
+                        ) => {
+                            if let HeapCellValue::Atom(ref n1, _) = &self.heap[h1] {
+                                if n1.is_char() {
+                                    if n1.as_str().chars().next() != Some(c) {
+                                        return Some(n1.as_str().chars().next().cmp(&Some(c)));
+                                    }
+                                } else {
+                                    return Some(Ordering::Greater);
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::Char(c),
+                            Addr::Con(h1),
+                        ) => {
+                            if let HeapCellValue::Atom(ref n1, _) = &self.heap[h1] {
+                                if n1.is_char() {
+                                    if n1.as_str().chars().next() != Some(c) {
+                                        return Some(Some(c).cmp(&n1.as_str().chars().next()));
+                                    }
+                                } else {
+                                    return Some(Ordering::Less);
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::Con(h),
+                            Addr::CharCode(c),
+                        ) => {
+                            let c = std::char::from_u32(c).unwrap();
+
+                            if let HeapCellValue::Atom(ref n1, _) = &self.heap[h] {
+                                if n1.is_char() {
+                                    if n1.as_str().chars().next() != Some(c) {
+                                        return Some(n1.as_str().chars().next().cmp(&Some(c)));
+                                    }
+                                } else {
+                                    return Some(Ordering::Greater);
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::CharCode(c),
+                            Addr::Con(h),
+                        ) => {
+                            let c = std::char::from_u32(c).unwrap();
+
+                            if let HeapCellValue::Atom(ref n1, _) = &self.heap[h] {
+                                if n1.is_char() {
+                                    if n1.as_str().chars().next() != Some(c) {
+                                        return Some(Some(c).cmp(&n1.as_str().chars().next()));
+                                    }
+                                } else {
+                                    return Some(Ordering::Less);
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::EmptyList,
+                            Addr::Con(h),
+                        ) => {
+                            if let HeapCellValue::Atom(ref n1, _) = &self.heap[h] {
+                                if "[]" != n1.as_str() {
+                                    return Some("[]".cmp(n1.as_str()));
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::Con(h),
+                            Addr::EmptyList,
+                        ) => {
+                            if let HeapCellValue::Atom(ref n1, _) = &self.heap[h] {
+                                if "[]" != n1.as_str() {
+                                    return Some(n1.as_str().cmp("[]"));
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::Char(c1),
+                            Addr::Char(c2),
+                        ) => {
+                            if c1 != c2 {
+                                return Some(c1.cmp(&c2));
+                            }
+                        }
+                        (
+                            Addr::CharCode(c1),
+                            Addr::CharCode(c2),
+                        ) => {
+                            if c1 != c2 {
+                                return Some(c1.cmp(&c2));
+                            }
+                        }
+                        (
+                            Addr::Char(c1),
+                            Addr::CharCode(c2),
+                        ) => {
+                            let c2 = std::char::from_u32(c2).unwrap();
+
+                            if c1 != c2 {
+                                return Some(c1.cmp(&c2));
+                            }
+                        }
+                        (
+                            Addr::CharCode(c1),
+                            Addr::Char(c2),
+                        ) => {
+                            let c1 = std::char::from_u32(c1).unwrap();
+
+                            if c1 != c2 {
+                                return Some(c1.cmp(&c2));
+                            }
+                        }
+                        (
+                            Addr::Char(c),
+                            Addr::EmptyList,
+                        ) => {
+                            return if c == '[' {
+                                Some(Ordering::Less)
+                            } else {
+                                Some(c.cmp(&'['))
+                            };
+                        }
+                        (
+                            Addr::EmptyList,
+                            Addr::Char(c),
+                        ) => {
+                            return if c == '[' {
+                                Some(Ordering::Greater)
+                            } else {
+                                Some('['.cmp(&c))
+                            };
+                        }
+                        (
+                            Addr::CharCode(c),
+                            Addr::EmptyList,
+                        ) => {
+                            let c = std::char::from_u32(c).unwrap();
+
+                            return if c == '[' {
+                                Some(Ordering::Less)
+                            } else {
+                                Some(c.cmp(&'['))
+                            };
+                        }
+                        (
+                            Addr::EmptyList,
+                            Addr::CharCode(c),
+                        ) => {
+                            let c = std::char::from_u32(c).unwrap();
+
+                            return if c == '[' {
+                                Some(Ordering::Greater)
+                            } else {
+                                Some('['.cmp(&c))
+                            };
+                        }
+                        _ => {
+                            return None;
+                        }
                     }
                 }
-                (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Lis(_))) => {
-                    continue;
-                }
-                (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::NamedStr(ar, n, _))
-              | (HeapCellValue::NamedStr(ar, n, _), HeapCellValue::Addr(Addr::Lis(_))) => {
-                    if ar == 2 && n.as_str() == "." {
-                        continue;
-                    } else if ar < 2 {
-                        return Ordering::Greater;
-                    } else if ar > 2 {
-                        return Ordering::Less;
-                    } else {
-                        return n.as_str().cmp(".");
+                Some(TermOrderCategory::Compound) => {
+                    match (v1, v2) {
+                        (
+                            Addr::Lis(_),
+                            Addr::Lis(_),
+                        ) => {
+                        }
+                        (
+                            Addr::PStrLocation(..),
+                            Addr::PStrLocation(..),
+                        ) => {
+                        }
+                        (
+                            Addr::Str(h1),
+                            Addr::Str(h2),
+                        ) => {
+                            if let HeapCellValue::NamedStr(a1, ref n1, _) = &self.heap[h1] {
+                                if let HeapCellValue::NamedStr(a2, ref n2, _) = &self.heap[h2] {
+                                    if a1 != a2 || n1.as_str() != n2.as_str() {
+                                        return Some(a1.cmp(&a2).then_with(|| n1.as_str().cmp(n2.as_str())));
+                                    }
+                                } else {
+                                    unreachable!()
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::Lis(_),
+                            Addr::PStrLocation(..),
+                        ) |
+                        (
+                            Addr::PStrLocation(..),
+                            Addr::Lis(_),
+                        ) if !self.flags.double_quotes.is_atom() => {
+                        }
+                        (
+                            Addr::Lis(_),
+                            Addr::Str(s),
+                        ) => {
+                            if let &HeapCellValue::NamedStr(a1, ref n1, _) = &self.heap[s] {
+                                if a1 != 2 || n1.as_str() != "." {
+                                    return Some(a1.cmp(&2).then_with(|| n1.as_str().cmp(".")));
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::Str(s),
+                            Addr::Lis(_),
+                        ) => {
+                            if let &HeapCellValue::NamedStr(a1, ref n1, _) = &self.heap[s] {
+                                if a1 != 2 || n1.as_str() != "." {
+                                    return Some(2.cmp(&a1).then_with(|| ".".cmp(n1.as_str())));
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::PStrLocation(..),
+                            Addr::Str(s),
+                        ) if !self.flags.double_quotes.is_atom() => {
+                            if let &HeapCellValue::NamedStr(a1, ref n1, _) = &self.heap[s] {
+                                if a1 != 2 || n1.as_str() != "." {
+                                    return Some(a1.cmp(&2).then_with(|| n1.as_str().cmp(".")));
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        (
+                            Addr::Str(s),
+                            Addr::PStrLocation(..),
+                        ) if !self.flags.double_quotes.is_atom() => {
+                            if let &HeapCellValue::NamedStr(a1, ref n1, _) = &self.heap[s] {
+                                if a1 != 2 || n1.as_str() != "." {
+                                    return Some(2.cmp(&a1).then_with(|| ".".cmp(n1.as_str())));
+                                }
+                            } else {
+                                unreachable!()
+                            }
+                        }
+                        _ => {
+                            return None;
+                        }
                     }
                 }
-                (HeapCellValue::NamedStr(..), _) => {
-                    return Ordering::Greater;
-                }
-                (HeapCellValue::Addr(Addr::Lis(_)), _) => {
-                    return Ordering::Greater;
+                None => {
+                    return None;
                 }
-                _ => {}
             }
         }
 
-        iter.first_to_expire
+        Some(iter.first_to_expire)
     }
 
     pub(super) fn reset_block(&mut self, addr: Addr) {
         match self.store(addr) {
-            Addr::Con(Constant::Usize(b)) => self.block = b,
+            Addr::Usize(b) => self.block = b,
             _ => self.fail = true,
         };
     }
@@ -2563,8 +2079,14 @@ impl MachineState {
                 let d = self.store(self.deref(self[r1].clone()));
 
                 match d {
-                    Addr::Con(Constant::Atom(..)) | Addr::Con(Constant::Char(_)) => self.p += 1,
-                    Addr::Con(Constant::EmptyList) => self.p += 1,
+                    Addr::Con(h) =>
+                        if let HeapCellValue::Atom(..) = &self.heap[h] {
+                            self.p += 1;
+                        } else {
+                            self.fail = true;
+                        },
+                    Addr::Char(_) => self.p += 1,
+                    Addr::EmptyList => self.p += 1,
                     _ => self.fail = true,
                 };
             }
@@ -2580,22 +2102,36 @@ impl MachineState {
                 let d = self.store(self.deref(self[r1].clone()));
 
                 match d {
-                    Addr::Con(Constant::Integer(_)) => self.p += 1,
-                    Addr::Con(Constant::CharCode(_)) => self.p += 1,
-                    Addr::Con(Constant::Char(_))
-                      if self.flags.double_quotes.is_codes() => self.p += 1,
-                    Addr::Con(Constant::Rational(r)) => {
-                        if r.denom() == &1 {
-                            self.p += 1;
-                        } else {
-                            self.fail = true;
+                    Addr::Con(h) => {
+                        match &self.heap[h] {
+                            HeapCellValue::Integer(_) => {
+                                self.p += 1;
+                            }
+                            HeapCellValue::Rational(ref r) => {
+                                if r.denom() == &1 {
+                                    self.p += 1;
+                                } else {
+                                    self.fail = true;
+                                }
+                            }
+                            _ => {
+                                self.fail = true;
+                            }
                         }
                     }
-                    _ => self.fail = true,
+                    Addr::CharCode(_) => {
+                        self.p += 1;
+                    }
+                    Addr::Char(_) if self.flags.double_quotes.is_codes() => {
+                        self.p += 1;
+                    }
+                    _ => {
+                        self.fail = true;
+                    }
                 };
             }
             &InlinedClauseType::IsCompound(r1) => {
-                let d = self.store(self.deref(self[r1].clone()));
+                let d = self.store(self.deref(self[r1]));
 
                 match d {
                     Addr::Str(_) | Addr::Lis(_) | Addr::PStrLocation(..) => self.p += 1,
@@ -2606,7 +2142,7 @@ impl MachineState {
                 let d = self.store(self.deref(self[r1].clone()));
 
                 match d {
-                    Addr::Con(Constant::Float(_)) => self.p += 1,
+                    Addr::Float(_) => self.p += 1,
                     _ => self.fail = true,
                 };
             }
@@ -2614,15 +2150,23 @@ impl MachineState {
                 let d = self.store(self.deref(self[r1].clone()));
 
                 match d {
-                    Addr::Con(Constant::Rational(_)) => self.p += 1,
-                    _ => self.fail = true,
+                    Addr::Con(h) => {
+                        if let HeapCellValue::Rational(_) = &self.heap[h] {
+                            self.p += 1;
+                        } else {
+                            self.fail = true;
+                        }
+                    }
+                    _ => {
+                        self.fail = true;
+                    }
                 };
             }
             &InlinedClauseType::IsString(r1) => {
                 let d = self.store(self.deref(self[r1].clone()));
 
                 match d {
-                    Addr::Con(Constant::String(..)) => self.p += 1,
+                    Addr::PStrLocation(..) => self.p += 1,
                     _ => self.fail = true,
                 };
             }
@@ -2653,29 +2197,31 @@ impl MachineState {
         }
     }
 
-    fn try_functor_unify_components(&mut self, name: Addr, arity: Addr) {
-        let a2 = self[temp_v!(2)].clone();
-        let a3 = self[temp_v!(3)].clone();
-
-        self.unify(a2, name);
-
-        if !self.fail {
-            self.unify(a3, arity);
-        }
-    }
-
     fn try_functor_compound_case(
         &mut self,
         name: ClauseName,
         arity: usize,
         spec: Option<SharedOpDesc>,
     ) {
-        let name = Addr::Con(Constant::Atom(name, spec));
-        let arity = Addr::Con(Constant::Integer(Integer::from(arity)));
-
+        let name = self.heap.to_unifiable(HeapCellValue::Atom(name, spec));
         self.try_functor_unify_components(name, arity);
     }
 
+    fn try_functor_unify_components(
+        &mut self,
+        name: Addr,
+        arity: usize,
+    ) {
+        let a2 = self[temp_v!(2)];
+        let a3 = self[temp_v!(3)];
+
+        self.unify(a2, name);
+
+        if !self.fail {
+            self.unify(a3, Addr::Usize(arity));
+        }
+    }
+
     fn try_functor_fabricate_struct(
         &mut self,
         name: ClauseName,
@@ -2689,10 +2235,7 @@ impl MachineState {
         let f_a = if name.as_str() == "." && arity == 2 {
             Addr::Lis(self.heap.h())
         } else {
-            let h = self.heap.h();
-            self.heap.push(HeapCellValue::NamedStr(arity as usize, name, spec));
-
-            Addr::Str(h)
+            self.heap.to_unifiable(HeapCellValue::NamedStr(arity as usize, name, spec))
         };
 
         for _ in 0..arity {
@@ -2703,41 +2246,56 @@ impl MachineState {
         self.bind(r, f_a);
     }
 
-    pub(super) fn try_functor(&mut self, indices: &IndexStore) -> CallResult {
+    pub(super)
+    fn try_functor(&mut self, indices: &IndexStore) -> CallResult {
         let stub = MachineError::functor_stub(clause_name!("functor"), 3);
-        let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
+        let a1 = self.store(self.deref(self[temp_v!(1)]));
 
-        match a1.clone() {
-            Addr::Stream(_) | Addr::DBRef(_) => self.fail = true,
-            Addr::Con(Constant::String(n, ref s))
-                if !self.flags.double_quotes.is_atom() && !s[n ..].is_empty() =>
-            {
-                let shared_op_desc = fetch_op_spec(clause_name!("."), 2, None, &indices.op_dir);
-                self.try_functor_compound_case(clause_name!("."), 2, shared_op_desc)
-            }
-            Addr::Con(_) => self
-                .try_functor_unify_components(a1, Addr::Con(Constant::Integer(Integer::from(0)))),
-            Addr::Str(o) => match self.heap[o].clone() {
+        match a1 {
+            Addr::Stream(_) => {
+                self.fail = true;
+            }
+            Addr::Char(_) | Addr::CharCode(_) | Addr::Con(_) | Addr::Float(_) => {
+                self.try_functor_unify_components(a1, 0);
+            }
+            Addr::Str(o) => match self.heap.clone(o) {
                 HeapCellValue::NamedStr(arity, name, spec) => {
                     let spec = fetch_op_spec(name.clone(), arity, spec, &indices.op_dir);
                     self.try_functor_compound_case(name, arity, spec)
                 }
-                _ => self.fail = true,
+                _ => {
+                    self.fail = true;
+                }
             },
             Addr::Lis(_) | Addr::PStrLocation(..) => {
-                let shared_op_desc = fetch_op_spec(clause_name!("."), 2, None, &indices.op_dir);
-                self.try_functor_compound_case(clause_name!("."), 2, shared_op_desc)
+                let spec = fetch_op_spec(clause_name!("."), 2, None, &indices.op_dir);
+                self.try_functor_compound_case(clause_name!("."), 2, spec)
             }
             Addr::AttrVar(..) | 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()));
+                let name  = self.store(self.deref(self[temp_v!(2)]));
+                let arity = self.store(self.deref(self[temp_v!(3)]));
 
                 if name.is_ref() || arity.is_ref() {
                     // 8.5.1.3 a) & 8.5.1.3 b)
                     return Err(self.error_form(MachineError::instantiation_error(), stub));
                 }
 
-                if let Addr::Con(Constant::Integer(arity)) = arity {
+                if let Addr::Con(h) = arity {
+                    let arity = if let HeapCellValue::Integer(ref n) = &self.heap[h] {
+                        n.clone()
+                    } else {
+                        return Err(
+                            self.error_form(
+                                MachineError::type_error(
+                                    self.heap.h(),
+                                    ValidType::Integer,
+                                    arity,
+                                ),
+                                stub,
+                            )
+                        );
+                    };
+
                     let arity = match arity.to_isize() {
                         Some(arity) => arity,
                         None => {
@@ -2747,56 +2305,78 @@ impl MachineState {
                     };
 
                     if arity > MAX_ARITY as isize {
-                        let rep_err = MachineError::representation_error(RepFlag::MaxArity);
                         // 8.5.1.3 f)
+                        let rep_err = MachineError::representation_error(RepFlag::MaxArity);
                         return Err(self.error_form(rep_err, stub));
                     } else if arity < 0 {
                         // 8.5.1.3 g)
-                        let arity = Integer::from(arity);
+                        let arity = Number::Integer(Rc::new(Integer::from(arity)));
                         let dom_err = MachineError::domain_error(
-                            DomainError::NotLessThanZero,
-                            Addr::Con(Constant::Integer(arity)),
+                            DomainErrorType::NotLessThanZero,
+                            arity,
                         );
+
                         return Err(self.error_form(dom_err, stub));
                     }
 
                     match name {
-                        Addr::Con(_) if arity == 0 => self.unify(a1, name),
-                        Addr::Con(Constant::Atom(name, spec)) => self.try_functor_fabricate_struct(
-                            name,
-                            arity,
-                            spec,
-                            &indices.op_dir,
-                            a1.as_var().unwrap(),
-                        ),
-                        Addr::Con(Constant::Char(c)) => {
-                            let name = clause_name!(c.to_string(), indices.atom_tbl);
+                        Addr::Con(_) if arity == 0 => {
+                            self.unify(a1, name);
+                        }
+                        Addr::Con(h) => {
+                            if let HeapCellValue::Atom(name, spec) = self.heap.clone(h) {
+                                self.try_functor_fabricate_struct(
+                                    name,
+                                    arity,
+                                    spec,
+                                    &indices.op_dir,
+                                    a1.as_var().unwrap(),
+                                );
+                            } else {
+                                // 8.5.1.3 e)
+                                return Err(self.error_form(
+                                    MachineError::type_error(
+                                        self.heap.h(),
+                                        ValidType::Atom,
+                                        name,
+                                    ),
+                                    stub,
+                                ))
+                            }
+                        }
+                        Addr::Char(c) => {
                             self.try_functor_fabricate_struct(
-                                name,
+                                clause_name!(c.to_string(), indices.atom_tbl),
                                 arity,
                                 None,
                                 &indices.op_dir,
                                 a1.as_var().unwrap(),
                             );
                         }
-                        Addr::Con(_) => {
-                            return Err(self
-                                .error_form(MachineError::type_error(ValidType::Atom, name), stub))
-                        } // 8.5.1.3 e)
                         _ => {
                             return Err(self.error_form(
-                                MachineError::type_error(ValidType::Atomic, name),
+                                MachineError::type_error(self.heap.h(), ValidType::Atomic, name),
                                 stub,
-                            ))
+                            ));
                         } // 8.5.1.3 c)
-                    };
+                    }
                 } else if !arity.is_ref() {
                     // 8.5.1.3 d)
                     return Err(
-                        self.error_form(MachineError::type_error(ValidType::Integer, arity), stub)
+                        self.error_form(
+                            MachineError::type_error(
+                                self.heap.h(),
+                                ValidType::Integer,
+                                arity,
+                            ),
+                            stub,
+                        )
                     );
                 }
             }
+            _ => {
+                self.fail = true;
+            }
         };
 
         Ok(())
@@ -2805,14 +2385,14 @@ impl MachineState {
     pub(super) fn term_dedup(&self, list: &mut Vec<Addr>) {
         let mut result = vec![];
 
-        for a2 in list.iter().cloned() {
-            if let Some(a1) = result.last().cloned() {
-                if self.compare_term_test(&a1, &a2) == Ordering::Equal {
+        for a2 in list.iter() {
+            if let Some(a1) = result.last() {
+                if self.compare_term_test(&a1, &a2) == Some(Ordering::Equal) {
                     continue;
                 }
             }
 
-            result.push(a2);
+            result.push(*a2);
         }
 
         *list = result;
@@ -2824,33 +2404,26 @@ impl MachineState {
         r: RegType,
         caller: MachineStub,
     ) -> Result<Vec<Addr>, MachineStub> {
-        let a1 = self.store(self.deref(self[r].clone()));
+        let a1 = self.store(self.deref(self[r]));
 
         match a1.clone() {
             Addr::Lis(l) => {
                 self.try_from_inner_list(vec![], l, caller, a1)
             }
-            Addr::Con(Constant::String(n, ref s))
-                if !self.flags.double_quotes.is_atom() => {
-                    if s.len() > n {
-                        Ok(Vec::from_iter(s[n ..].chars().map(|c| {
-                            Addr::Con(Constant::Char(c))
-                        })))
-                    } else {
-                        Ok(vec![])
-                    }
-                }
             Addr::PStrLocation(h, n) => {
                 self.try_from_partial_string(vec![], h, n, caller, a1)
             }
-            Addr::HeapCell(_) | Addr::StackCell(..) => {
+            Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) => {
                 Err(self.error_form(MachineError::instantiation_error(), caller))
             }
-            Addr::Con(Constant::EmptyList) => {
+            Addr::EmptyList => {
                 Ok(vec![])
             }
             _ => {
-                Err(self.error_form(MachineError::type_error(ValidType::List, a1), caller))
+                Err(self.error_form(
+                    MachineError::type_error(self.heap.h(), ValidType::List, a1),
+                    caller,
+                ))
             }
         }
     }
@@ -2873,19 +2446,13 @@ impl MachineState {
                             result.push(self.heap[hcp].as_addr(hcp));
                             l = hcp + 1;
                         }
-                        Addr::Con(Constant::String(n, ref s))
-                            if !self.flags.double_quotes.is_atom() =>
-                        {
-                            result.push(Addr::Con(Constant::String(n, s.clone())));
-                            break;
-                        }
                         Addr::PStrLocation(h, n) => {
                             return self.try_from_partial_string(result, h, n, caller, a1);
                         }
-                        Addr::Con(Constant::EmptyList) => {
+                        Addr::EmptyList => {
                             break;
                         }
-                        Addr::HeapCell(_) | Addr::StackCell(..) => {
+                        Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) => {
                             return Err(self.error_form(
                                 MachineError::instantiation_error(),
                                 caller,
@@ -2893,14 +2460,14 @@ impl MachineState {
                         }
                         _ => {
                             return Err(self.error_form(
-                                MachineError::type_error(ValidType::List, a1),
+                                MachineError::type_error(self.heap.h(), ValidType::List, a1),
                                 caller,
                             ))
                         }
                     },
                 _ => {
                     return Err(self.error_form(
-                        MachineError::type_error(ValidType::List, a1),
+                        MachineError::type_error(self.heap.h(), ValidType::List, a1),
                         caller,
                     ))
                 }
@@ -2920,20 +2487,12 @@ impl MachineState {
     ) -> Result<Vec<Addr>, MachineStub> {
         loop {
             if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] {
-                let s = pstr.block_as_str();
-
-                chars.extend(s[n ..].chars().map(|c| Addr::Con(Constant::Char(c))));
+                chars.extend(pstr.range_from(n ..).map(Addr::Char));
 
                 let tail = self.heap[h + 1].as_addr(h + 1);
 
                 match self.store(self.deref(tail)) {
-                    Addr::Con(Constant::EmptyList) => {
-                        return Ok(chars);
-                    }
-                    Addr::Con(Constant::String(n, ref s))
-                        if !self.flags.double_quotes.is_atom() =>
-                    {
-                        chars.push(Addr::Con(Constant::String(n, s.clone())));
+                    Addr::EmptyList => {
                         return Ok(chars);
                     }
                     Addr::Lis(l) => {
@@ -2945,7 +2504,7 @@ impl MachineState {
                     }
                     _ => {
                         return Err(self.error_form(
-                            MachineError::type_error(ValidType::List, a1),
+                            MachineError::type_error(self.heap.h(), ValidType::List, a1),
                             caller,
                         ))
                     }
@@ -2964,16 +2523,34 @@ impl MachineState {
             Addr::HeapCell(_) | Addr::StackCell(..) => {
                 Err(self.error_form(MachineError::instantiation_error(), stub))
             }
-            Addr::Str(s) => match self.heap[s].clone() {
-                HeapCellValue::NamedStr(2, ref name, Some(_)) if *name == clause_name!("-") => {
-                    Ok(Addr::HeapCell(s + 1))
+            Addr::Str(s) => {
+                match self.heap.clone(s) {
+                    HeapCellValue::NamedStr(2, ref name, Some(_))
+                        if *name == clause_name!("-") => {
+                            Ok(Addr::HeapCell(s + 1))
+                        }
+                    _ => {
+                        Err(self.error_form(
+                            MachineError::type_error(
+                                self.heap.h(),
+                                ValidType::Pair,
+                                self.heap[s].as_addr(s),
+                            ),
+                            stub,
+                        ))
+                    }
                 }
-                _ => Err(self.error_form(
-                    MachineError::type_error(ValidType::Pair, self.heap[s].as_addr(s)),
+            }
+            a => {
+                Err(self.error_form(
+                    MachineError::type_error(
+                        self.heap.h(),
+                        ValidType::Pair,
+                        a,
+                    ),
                     stub,
-                )),
-            },
-            a => Err(self.error_form(MachineError::type_error(ValidType::Pair, a), stub)),
+                ))
+            }
         }
     }
 
@@ -2984,100 +2561,167 @@ impl MachineState {
         let a2 = self[temp_v!(2)].clone();
 
         copy_term(CopyTerm::new(self), a1, attr_var_policy);
+
         self.unify(Addr::HeapCell(old_h), a2);
     }
 
     // returns true on failure.
     pub(super) fn structural_eq_test(&self) -> bool {
-        let a1 = self[temp_v!(1)].clone();
-        let a2 = self[temp_v!(2)].clone();
+        let a1 = self[temp_v!(1)];
+        let a2 = self[temp_v!(2)];
 
         let mut var_pairs = IndexMap::new();
 
         let iter = self.zipped_acyclic_pre_order_iter(a1, a2);
 
         for (v1, v2) in iter {
-            match (v1, v2) {
+            match (
+                self.heap.index_addr(&v1).as_ref(),
+                self.heap.index_addr(&v2).as_ref(),
+            ) {
                 (
                     HeapCellValue::Addr(Addr::Lis(_)),
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
+                    HeapCellValue::Addr(Addr::PStrLocation(..)),
                 )
                 | (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(..))),
+                    HeapCellValue::Addr(Addr::PStrLocation(..)),
                     HeapCellValue::Addr(Addr::Lis(_)),
                 ) if !self.flags.double_quotes.is_atom() => {
-                    continue;
                 }
                 (
-                    HeapCellValue::Addr(Addr::Con(Constant::String(n, ref s))),
-                    HeapCellValue::Addr(Addr::Con(Constant::EmptyList)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::Con(Constant::EmptyList)),
-                    HeapCellValue::Addr(Addr::Con(Constant::String(n, ref s))),
-                ) if !self.flags.double_quotes.is_atom() => {
-                    if !s[n ..].is_empty() {
-                        return true;
-                    }
-                }
-                (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) => {
+                    HeapCellValue::NamedStr(ar1, n1, _),
+                    HeapCellValue::NamedStr(ar2, n2, _),
+                ) => {
                     if ar1 != ar2 || n1 != n2 {
                         return true;
                     }
                 }
-                (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Lis(_))) => {
-                    continue;
+                (
+                    HeapCellValue::Addr(Addr::Lis(_)),
+                    HeapCellValue::Addr(Addr::Lis(_)),
+                ) => {
                 }
                 (
-                    HeapCellValue::Addr(v1 @ Addr::HeapCell(_)),
-                    HeapCellValue::Addr(v2 @ Addr::AttrVar(_)),
+                    &HeapCellValue::Addr(v1 @ Addr::HeapCell(_)),
+                    &HeapCellValue::Addr(v2 @ Addr::AttrVar(_)),
                 )
                 | (
-                    HeapCellValue::Addr(v1 @ Addr::StackCell(..)),
-                    HeapCellValue::Addr(v2 @ Addr::AttrVar(_)),
+                    &HeapCellValue::Addr(v1 @ Addr::StackCell(..)),
+                    &HeapCellValue::Addr(v2 @ Addr::AttrVar(_)),
                 )
                 | (
-                    HeapCellValue::Addr(v1 @ Addr::AttrVar(_)),
-                    HeapCellValue::Addr(v2 @ Addr::AttrVar(_)),
+                    &HeapCellValue::Addr(v1 @ Addr::AttrVar(_)),
+                    &HeapCellValue::Addr(v2 @ Addr::AttrVar(_)),
                 )
                 | (
-                    HeapCellValue::Addr(v1 @ Addr::AttrVar(_)),
-                    HeapCellValue::Addr(v2 @ Addr::HeapCell(_)),
+                    &HeapCellValue::Addr(v1 @ Addr::AttrVar(_)),
+                    &HeapCellValue::Addr(v2 @ Addr::HeapCell(_)),
                 )
                 | (
-                    HeapCellValue::Addr(v1 @ Addr::AttrVar(_)),
-                    HeapCellValue::Addr(v2 @ Addr::StackCell(..)),
+                    &HeapCellValue::Addr(v1 @ Addr::AttrVar(_)),
+                    &HeapCellValue::Addr(v2 @ Addr::StackCell(..)),
                 )
                 | (
-                    HeapCellValue::Addr(v1 @ Addr::HeapCell(_)),
-                    HeapCellValue::Addr(v2 @ Addr::HeapCell(_)),
+                    &HeapCellValue::Addr(v1 @ Addr::HeapCell(_)),
+                    &HeapCellValue::Addr(v2 @ Addr::HeapCell(_)),
                 )
                 | (
-                    HeapCellValue::Addr(v1 @ Addr::HeapCell(_)),
-                    HeapCellValue::Addr(v2 @ Addr::StackCell(..)),
+                    &HeapCellValue::Addr(v1 @ Addr::HeapCell(_)),
+                    &HeapCellValue::Addr(v2 @ Addr::StackCell(..)),
                 )
                 | (
-                    HeapCellValue::Addr(v1 @ Addr::StackCell(..)),
-                    HeapCellValue::Addr(v2 @ Addr::StackCell(..)),
+                    &HeapCellValue::Addr(v1 @ Addr::StackCell(..)),
+                    &HeapCellValue::Addr(v2 @ Addr::StackCell(..)),
                 )
                 | (
-                    HeapCellValue::Addr(v1 @ Addr::StackCell(..)),
-                    HeapCellValue::Addr(v2 @ Addr::HeapCell(_)),
+                    &HeapCellValue::Addr(v1 @ Addr::StackCell(..)),
+                    &HeapCellValue::Addr(v2 @ Addr::HeapCell(_)),
                 ) =>
-                    match (var_pairs.get(&v1).cloned(), var_pairs.get(&v2).cloned()) {
-                        (Some(ref v2_p), Some(ref v1_p)) if *v1_p == v1 && *v2_p == v2 => continue,
-                        (Some(_), _) | (_, Some(_)) => return true,
+                    match (var_pairs.get(&v1), var_pairs.get(&v2)) {
+                        (Some(ref v2_p), Some(ref v1_p)) if **v1_p == v1 && **v2_p == v2 => {
+                            continue;
+                        }
+                        (Some(_), _) | (_, Some(_)) => {
+                            return true;
+                        }
                         (None, None) => {
-                            var_pairs.insert(v1.clone(), v2.clone());
+                            var_pairs.insert(v1, v2);
                             var_pairs.insert(v2, v1);
                         }
                     },
-                (HeapCellValue::Addr(a1), HeapCellValue::Addr(a2)) => {
+                (
+                    HeapCellValue::PartialString(ref pstr1),
+                    HeapCellValue::PartialString(ref pstr2),
+                ) => {
+                    let pstr1_iter = pstr1.range_from(0 ..);
+                    let pstr2_iter = pstr2.range_from(0 ..);
+
+                    for (c1, c2) in pstr1_iter.zip(pstr2_iter) {
+                        if c1 != c2 {
+                            return true;
+                        }
+                    }
+                }
+                (
+                    HeapCellValue::Addr(Addr::PStrLocation(..)),
+                    HeapCellValue::Addr(Addr::PStrLocation(..)),
+                ) => {
+                }
+                (
+                    HeapCellValue::Integer(n1),
+                    HeapCellValue::Integer(n2),
+                ) => {
+                    if &*n1 != &*n2 {
+                        return true;
+                    }
+                }
+                (
+                    HeapCellValue::Rational(n1),
+                    HeapCellValue::Rational(n2),
+                ) => {
+                    if &*n1 != &*n2 {
+                        return true;
+                    }
+                }
+                (
+                    HeapCellValue::Integer(ref n1),
+                    HeapCellValue::Rational(ref n2),
+                ) |
+                (
+                    HeapCellValue::Rational(ref n2),
+                    HeapCellValue::Integer(ref n1),
+                ) => {
+                    if n1.as_ref().partial_cmp(&**n2) == Some(Ordering::Equal) {
+                        return true;
+                    }
+                }
+                (
+                    HeapCellValue::Atom(ref n1, ref spec_1),
+                    HeapCellValue::Atom(ref n2, ref spec_2),
+                ) => {
+                    if n1 != n2 || spec_1 != spec_2 {
+                        return true;
+                    }
+                }
+                (
+                    HeapCellValue::DBRef(ref db_ref_1),
+                    HeapCellValue::DBRef(ref db_ref_2),
+                ) => {
+                    if db_ref_1 != db_ref_2 {
+                        return true;
+                    }
+                }
+                (
+                    HeapCellValue::Addr(a1),
+                    HeapCellValue::Addr(a2),
+                ) => {
                     if a1 != a2 {
                         return true;
                     }
                 }
-                _ => return true,
+                _ => {
+                    return true;
+                }
             }
         }
 
@@ -3091,9 +2735,9 @@ impl MachineState {
 
         for v in self.acyclic_pre_order_iter(a) {
             match v {
-                HeapCellValue::Addr(Addr::HeapCell(..)) => return true,
-                HeapCellValue::Addr(Addr::StackCell(..)) => return true,
-                HeapCellValue::Addr(Addr::AttrVar(..)) => return true,
+                Addr::HeapCell(..) => return true,
+                Addr::StackCell(..) => return true,
+                Addr::AttrVar(..) => return true,
                 _ => {}
             }
         }
@@ -3367,7 +3011,7 @@ impl MachineState {
             &CutInstruction::GetLevel(r) => {
                 let b0 = self.b0;
 
-                self[r] = Addr::Con(Constant::CutPoint(b0));
+                self[r] = Addr::CutPoint(b0);
                 self.p += 1;
             }
             &CutInstruction::GetLevelAndUnify(r) => {
index 8ee6413653755d5f2609456409bff9441afad356..7271c150bef064b550a9ec73b753dff3bc3ab2cb 100644 (file)
@@ -19,13 +19,15 @@ pub mod machine_errors;
 pub mod machine_indices;
 pub(super) mod machine_state;
 pub mod modules;
-mod partial_string;
+pub mod partial_string;
 mod raw_block;
 mod stack;
 pub(crate) mod streams;
 pub(super) mod term_expansion;
 pub mod toplevel;
 
+#[macro_use]
+mod arithmetic_ops;
 #[macro_use]
 mod machine_state_impl;
 mod system_calls;
@@ -340,8 +342,8 @@ impl Machine {
        // the first of these is the path to the scryer-prolog executable, so skip
        // it.
        for filename in env::args().skip(1) {
-           let atom = atom!(filename, self.indices.atom_tbl);
-           filename_atoms.push(Addr::Con(atom));
+           let atom = clause_name!(filename, self.indices.atom_tbl);
+           filename_atoms.push(HeapCellValue::Atom(atom, None));
        }
 
        let list_addr =
@@ -547,14 +549,14 @@ impl Machine {
                         HeapCellValue::NamedStr(arity, ref name, _)
                             if *arity == 2 && name.as_str() == "/" => {
                                let name = match &self.machine_st.heap[s+1] {
-                                   &HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) =>
+                                   &HeapCellValue::Atom(ref name, _) =>
                                        name.clone(),
                                    _ =>
                                        unreachable!()
                                };
 
                                let arity = match &self.machine_st.heap[s+2] {
-                                   &HeapCellValue::Addr(Addr::Con(Constant::Integer(ref arity))) =>
+                                   &HeapCellValue::Integer(ref arity) =>
                                        arity.to_usize().unwrap(),
                                    _ =>
                                        unreachable!()
@@ -565,21 +567,21 @@ impl Machine {
                         HeapCellValue::NamedStr(arity, ref name, _)
                             if *arity == 3 && name.as_str() == "op" => {
                                 let name = match &self.machine_st.heap[s+3] {
-                                   &HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) =>
+                                   &HeapCellValue::Atom(ref name, _) =>
                                        name.clone(),
                                    _ =>
                                        unreachable!()
                                };
 
                                 let spec = match &self.machine_st.heap[s+2] {
-                                   &HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) =>
+                                   &HeapCellValue::Atom(ref name, _) =>
                                        name.clone(),
                                    _ =>
                                        unreachable!()
                                };
 
                                let prec = match &self.machine_st.heap[s+1] {
-                                   &HeapCellValue::Addr(Addr::Con(Constant::Integer(ref arity))) =>
+                                   &HeapCellValue::Integer(ref arity) =>
                                        arity.to_usize().unwrap(),
                                    _ =>
                                        unreachable!()
@@ -610,9 +612,13 @@ impl Machine {
        let cached_query = mem::replace(&mut self.code_repo.cached_query, vec![]);
 
        let module_spec = self.machine_st[temp_v!(1)].clone();
-       let name = match self.machine_st.store(self.machine_st.deref(module_spec)) {
-           Addr::Con(Constant::Atom(name, _)) => name,
-           _ => unreachable!()
+       let name = {
+            let addr = self.machine_st.store(self.machine_st.deref(module_spec));
+
+            match self.machine_st.heap.index_addr(&addr).as_ref() {
+                HeapCellValue::Atom(name, _) => name.clone(),
+               _ => unreachable!(),
+            }
        };
 
        let load_result = match to_src(name) {
@@ -653,9 +659,13 @@ impl Machine {
        let cached_query = mem::replace(&mut self.code_repo.cached_query, vec![]);
 
        let module_spec = self.machine_st[temp_v!(1)].clone();
-       let name = match self.machine_st.store(self.machine_st.deref(module_spec)) {
-           Addr::Con(Constant::Atom(name, _)) => name,
-           _ => unreachable!()
+       let name = {
+            let addr = self.machine_st.store(self.machine_st.deref(module_spec));
+
+            match self.machine_st.heap.index_addr(&addr).as_ref() {
+                HeapCellValue::Atom(name, _) => name.clone(),
+               _ => unreachable!(),
+            }
        };
 
        let exports = match self.extract_module_export_list() {
index 49be0f6eb175f0f08aac1f219a7d843ab79df213..320150308977a6192e2809253e1a17eeb9d70ca1 100644 (file)
@@ -1,26 +1,11 @@
-use crate::prolog::machine::raw_block::*;
-
+use std::alloc;
 use std::mem;
 use std::ptr;
-use std::slice;
+use std::ops::{Range, RangeFrom};
 use std::str;
 
-pub(crate) struct PartialStringTraits {}
-
-impl RawBlockTraits for PartialStringTraits {
-    #[inline]
-    fn init_size() -> usize {
-        0
-    }
-
-    #[inline]
-    fn align() -> usize {
-        mem::align_of::<char>()
-    }
-}
-
 pub struct PartialString {
-    pub(super) buf: RawBlock<PartialStringTraits>,
+    buf: *const u8,
 }
 
 impl Clone for PartialString {
@@ -30,17 +15,10 @@ impl Clone for PartialString {
     }
 }
 
-impl PartialEq for PartialString {
-    #[inline]
-    fn eq(&self, other: &Self) -> bool {
-        self as *const _ == other as *const _
-    }
-}
-
-fn scan_for_terminator(src: &str) -> usize {
+fn scan_for_terminator<Iter: Iterator<Item = char>>(iter: Iter) -> usize {
     let mut terminator_idx = 0;
 
-    for c in src.chars() {
+    for c in iter {
         if c == '\u{0}' {
             break;
         }
@@ -51,11 +29,82 @@ fn scan_for_terminator(src: &str) -> usize {
     terminator_idx
 }
 
+pub struct PStrIter {
+    buf: *const u8,
+}
+
+impl PStrIter {
+    #[inline]
+    fn from(buf: *const u8, idx: usize) -> Self {
+        PStrIter {
+            buf: (buf as usize + idx) as *const _
+        }
+    }
+}
+
+impl Iterator for PStrIter {
+    type Item = char;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            let b = ptr::read(self.buf);
+
+            if b == 0u8 {
+                return None;
+            }
+
+            let c = ptr::read(self.buf as *const char);
+            self.buf = self.buf.offset(c.len_utf8() as isize);
+
+            Some(c)
+        }
+    }
+}
+
+pub struct PStrIterBounded {
+    buf: *const u8,
+    end: *const u8,
+}
+
+impl PStrIterBounded {
+    #[inline]
+    fn from(buf: *const u8, start: usize, end: usize) -> Self {
+        PStrIterBounded {
+            buf: (buf as usize + start) as *const _,
+            end: (buf as usize + end) as *const _,
+        }
+    }
+}
+
+impl Iterator for PStrIterBounded {
+    type Item = char;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            if self.buf >= self.end {
+                return None;
+            }
+
+            let b = ptr::read(self.buf);
+
+            if b == 0u8 {
+                return None;
+            }
+
+            let c = ptr::read(self.buf as *const char);
+            self.buf = self.buf.offset(c.len_utf8() as isize);
+
+            Some(c)
+        }
+    }
+}
+
 impl PartialString {
+    #[inline]
     pub(super)
     fn new(src: &str) -> Option<(Self, &str)> {
         let pstr = PartialString {
-            buf: RawBlock::with_capacity(src.len() + '\u{0}'.len_utf8()),
+            buf: ptr::null_mut(),
         };
 
         unsafe {
@@ -63,22 +112,34 @@ impl PartialString {
         }
     }
 
+    #[inline]
+    pub(super)
+    fn empty() -> Self {
+        PartialString {
+            buf: "\u{0}".as_bytes()[0] as *const _,
+        }
+    }
+
     unsafe fn append_chars(mut self, src: &str) -> Option<(Self, &str)> {
-        let terminator_idx = scan_for_terminator(src);
+        let terminator_idx = scan_for_terminator(src.chars());
 
         if terminator_idx == 0 {
             return None;
         }
 
-        let new_top = self.buf.new_block(terminator_idx + '\u{0}'.len_utf8());
+        let layout = alloc::Layout::from_size_align_unchecked(
+            src.len() + '\u{0}'.len_utf8(),
+            mem::align_of::<u8>(),
+        );
+
+        self.buf = alloc::alloc(layout) as *const _;
 
         ptr::copy(
             src.as_ptr(),
-            self.buf.top as *mut _,
+            self.buf as *mut _,
             terminator_idx,
         );
 
-        self.buf.top = (new_top as usize - '\u{0}'.len_utf8()) as *const _;
         self.write_terminator_at(terminator_idx);
 
         Some(if terminator_idx != src.len() {
@@ -88,26 +149,40 @@ impl PartialString {
         })
     }
 
+    #[inline]
+    pub(crate)
+    fn iter(&self) -> PStrIter {
+        PStrIter {
+            buf: self.buf,
+        }
+    }
+
     pub(super)
     fn clone_from_offset(&self, n: usize) -> Self {
         let mut pstr = PartialString {
-            buf: RawBlock::with_capacity(self.len() + '\u{0}'.len_utf8()),
+            buf: ptr::null_mut(),
         };
 
         unsafe {
-            let len = if self.len() > n { self.len() - n } else { 0 };
-            let new_top = pstr.buf.new_block(len + '\u{0}'.len_utf8());
+            let len = scan_for_terminator(self.range_from(0 ..));
+            let len = if len > n { len - n } else { 0 };
+
+            let layout = alloc::Layout::from_size_align_unchecked(
+                len + '\u{0}'.len_utf8(),
+                mem::align_of::<u8>(),
+            );
+
+            pstr.buf = alloc::alloc(layout);
 
             if len > 0 {
                 ptr::copy(
-                    (self.buf.base as usize + n) as *mut u8,
-                    pstr.buf.base as *mut _,
+                    (self.buf as usize + n) as *const u8,
+                    pstr.buf as *mut _,
                     len,
                 );
             }
 
             pstr.write_terminator_at(len);
-            pstr.buf.top = (new_top as usize - '\u{0}'.len_utf8()) as *const _;
         }
 
         pstr
@@ -118,23 +193,26 @@ impl PartialString {
     fn write_terminator_at(&mut self, index: usize) {
         unsafe {
             ptr::write(
-                (self.buf.base as usize + index) as *mut u8,
+                (self.buf as usize + index) as *mut u8,
                 0u8,
             );
         }
     }
 
     #[inline]
-    pub(crate)
-    fn block_as_str(&self) -> &str {
-        unsafe {
-            let slice = slice::from_raw_parts(self.buf.base, self.len());
-            str::from_utf8(slice).unwrap()
-        }
+    pub fn range(&self, index: Range<usize>) -> PStrIterBounded {
+        PStrIterBounded::from(self.buf, index.start, index.end)
     }
 
     #[inline]
-    pub fn len(&self) -> usize {
-        self.buf.top as usize - self.buf.base as usize
+    pub fn range_from(&self, index: RangeFrom<usize>) -> PStrIter {
+        PStrIter::from(self.buf, index.start)
+    }
+
+    #[inline]
+    pub fn at_end(&self, end_n: usize) -> bool {
+        unsafe {
+            ptr::read((self.buf as usize + end_n) as *const u8) == 0u8
+        }
     }
 }
index b8cd4247f72d059bbce0dae46abdf6698649cfc0..1653defb2b97667929eb3850584b751cc6e769a4 100644 (file)
@@ -29,7 +29,8 @@ pub enum EOFAction {
 pub enum StreamInstance {
     Bytes(Cursor<Vec<u8>>),
     DynReadSource(Box<dyn Read>),
-    File(File),    
+    File(File),
+    Null,
     ReadlineStream(ReadlineStream),
     Stdin,
     Stdout,
@@ -201,6 +202,17 @@ impl Stream {
         }
     }
 
+    #[inline]
+    pub(crate)
+    fn null_stream() -> Self {
+        Stream {
+            options: StreamOptions::default(), // TODO: null_options?
+            stream_inst: WrappedStreamInstance::new(
+                StreamInstance::Null
+            ),
+        }
+    }
+
     #[inline]
     pub(crate)
     fn is_stdout(&self) -> bool {
@@ -233,7 +245,7 @@ impl Stream {
         match *self.stream_inst.0.borrow() {
             StreamInstance::Stdin
           | StreamInstance::TcpStream(_)
-          | StreamInstance::Bytes(_) 
+          | StreamInstance::Bytes(_)
           | StreamInstance::ReadlineStream(_)
           | StreamInstance::DynReadSource(_)
           | StreamInstance::File(_) => {
@@ -251,7 +263,7 @@ impl Stream {
         match *self.stream_inst.0.borrow() {
             StreamInstance::Stdout
           | StreamInstance::TcpStream(_)
-          | StreamInstance::Bytes(_) 
+          | StreamInstance::Bytes(_)
           | StreamInstance::File(_) => {
                 true
            }
@@ -283,7 +295,7 @@ impl Read for Stream {
             StreamInstance::Stdin => {
                 stdin().read(buf)
             }
-            StreamInstance::Stdout => {
+            StreamInstance::Stdout | StreamInstance::Null => {
                 Err(std::io::Error::new(
                     ErrorKind::PermissionDenied,
                     StreamError::ReadFromOutputStream,
index c222ea11505e6f2084aeb96ae8c07fe8c73f36ea..9f927da249ec7aa186a2867e00fe3b015b174c6f 100644 (file)
@@ -22,6 +22,7 @@ use crate::ref_thread_local::RefThreadLocal;
 
 use indexmap::{IndexMap, IndexSet};
 
+use std::cmp;
 use std::io::{stdout, Write};
 use std::iter::once;
 use std::mem;
@@ -111,7 +112,7 @@ impl BrentAlgState {
             Addr::PStrLocation(h, n) => {
                 CycleSearchResult::PStrLocation(self.steps, h, n)
             }
-            Addr::Con(Constant::EmptyList) => {
+            Addr::EmptyList => {
                 CycleSearchResult::ProperList(self.steps)
             }
             _ => {
@@ -131,8 +132,8 @@ fn is_builtin_predicate(name: &ClauseName) -> bool {
 impl MachineState {
     // a step in Brent's algorithm.
     fn brents_alg_step(&self, brent_st: &mut BrentAlgState) -> Option<CycleSearchResult> {
-        match self.store(self.deref(brent_st.hare.clone())) {
-            Addr::Con(Constant::EmptyList) => {
+        match self.store(self.deref(brent_st.hare)) {
+            Addr::EmptyList => {
                 Some(CycleSearchResult::ProperList(brent_st.steps))
             }
             addr @ Addr::HeapCell(_) | addr @ Addr::StackCell(..) | addr @ Addr::AttrVar(_) => {
@@ -141,19 +142,10 @@ impl MachineState {
                     addr.as_var().unwrap(),
                 ))
             }
-            Addr::Con(Constant::String(n, s)) if !self.flags.double_quotes.is_atom() => {
-                if let Some(c) = s.chars().next() {
-                    brent_st.step(Addr::Con(Constant::String(n + c.len_utf8(), s)))
-                } else {
-                    Some(CycleSearchResult::CompleteString(s.len(), s))
-                }
-            }
             Addr::PStrLocation(h, n) => {
                 match &self.heap[h] {
                     HeapCellValue::PartialString(ref pstr) => {
-                        let s = pstr.block_as_str();
-
-                        if let Some(c) = s[n ..].chars().next() {
+                        if let Some(c) = pstr.range_from(n ..).next() {
                             brent_st.step(Addr::PStrLocation(h, n + c.len_utf8()))
                         } else {
                             unreachable!()
@@ -188,34 +180,29 @@ impl MachineState {
             Addr::PStrLocation(h, _) => {
                 return CycleSearchResult::UntouchedList(h);
             }
-            Addr::Con(Constant::EmptyList) => {
+            Addr::EmptyList => {
                 return CycleSearchResult::EmptyList;
             }
-            Addr::Con(Constant::String(0, ref s))
-                if max_steps > 0 && !self.flags.double_quotes.is_atom() => {
-                    if max_steps >= s.len() {
-                        return CycleSearchResult::CompleteString(s.len(), s.clone());
+            Addr::Con(h) if max_steps > 0 => {
+                if let HeapCellValue::PartialString(_) = &self.heap[h] {
+                    if !self.flags.double_quotes.is_atom() {
+                        Addr::PStrLocation(h, 0)
                     } else {
-                        return CycleSearchResult::UntouchedString(max_steps, s.clone());
+                        return CycleSearchResult::NotList;
                     }
+                } else {
+                    return CycleSearchResult::NotList;
                 }
-            Addr::Con(Constant::String(0, ref s))
-                if !self.flags.double_quotes.is_atom() => {
-                    return CycleSearchResult::UntouchedString(0, s.clone());
-                }
-            Addr::Con(Constant::String(n, ref s))
-                if max_steps > 0 && !self.flags.double_quotes.is_atom() => {
-                    if max_steps >= s.len() - n {
-                        let s = Rc::new(String::from(&s[n ..]));
-                        return CycleSearchResult::CompleteString(s.len(), s);
-                    } else {
-                        return CycleSearchResult::UntouchedString(max_steps, s.clone());
+            }
+            Addr::Con(h) => {
+                if let HeapCellValue::PartialString(_) = &self.heap[h] {
+                    if !self.flags.double_quotes.is_atom() {
+                        return CycleSearchResult::UntouchedList(h);
                     }
                 }
-            Addr::Con(Constant::String(n, ref s))
-                if !self.flags.double_quotes.is_atom() => {
-                    return CycleSearchResult::UntouchedString(n, s.clone());
-                }
+
+                return CycleSearchResult::NotList;
+            }
             _ => {
                 return CycleSearchResult::NotList;
             }
@@ -241,18 +228,22 @@ impl MachineState {
             Addr::Lis(offset) => {
                 Addr::Lis(offset)
             }
-            Addr::Con(Constant::EmptyList) => {
+            Addr::EmptyList => {
                 return CycleSearchResult::EmptyList;
             }
             Addr::PStrLocation(h, n) => {
                 Addr::PStrLocation(h, n)
             }
-            Addr::Con(Constant::String(0, ref s)) if !self.flags.double_quotes.is_atom() => {
-                return CycleSearchResult::CompleteString(s.len(), s.clone());
-            }
-            Addr::Con(Constant::String(n, ref s)) if !self.flags.double_quotes.is_atom() => {
-                let s = Rc::new(String::from(&s[n ..]));
-                return CycleSearchResult::CompleteString(s.len(), s);
+            Addr::Con(h) => {
+                if let HeapCellValue::PartialString(_) = &self.heap[h] {
+                    if !self.flags.double_quotes.is_atom() {
+                        Addr::PStrLocation(h, 0)
+                    } else {
+                        return CycleSearchResult::NotList;
+                    }
+                } else {
+                    return CycleSearchResult::NotList;
+                }
             }
             _ => {
                 return CycleSearchResult::NotList;
@@ -269,76 +260,86 @@ impl MachineState {
     }
 
     fn finalize_skip_max_list(&mut self, n: usize, addr: Addr) {
-        let target_n = self[temp_v!(1)].clone();
-        self.unify(Addr::Con(Constant::Integer(Integer::from(n))), target_n);
+        let target_n = self[temp_v!(1)];
+        self.unify(Addr::Usize(n), target_n);
 
         if !self.fail {
-            let xs = self[temp_v!(4)].clone();
+            let xs = self[temp_v!(4)];
             self.unify(addr, xs);
         }
     }
 
-    pub(super) fn skip_max_list(&mut self) -> CallResult {
-        let max_steps = self.store(self.deref(self[temp_v!(2)].clone()));
+    fn skip_max_list_result(&mut self, max_steps: &Integer) {
+        let search_result =
+            if let Some(max_steps) = max_steps.to_isize() {
+                if max_steps == -1 {
+                    self.detect_cycles(self[temp_v!(3)])
+                } else {
+                    self.detect_cycles_with_max(
+                        max_steps as usize,
+                        self[temp_v!(3)],
+                    )
+                }
+            } else {
+                self.detect_cycles(self[temp_v!(3)])
+            };
 
-        match max_steps {
-            Addr::Con(Constant::Integer(ref max_steps)) => {
-                if max_steps.to_isize().map(|i| i >= -1).unwrap_or(false) {
-                    let n = self.store(self.deref(self[temp_v!(1)].clone()));
+        match search_result {
+            CycleSearchResult::PStrLocation(steps, h, n) => {
+                self.finalize_skip_max_list(steps, Addr::PStrLocation(h, n));
+            }
+            CycleSearchResult::UntouchedList(l) => {
+                self.finalize_skip_max_list(0, Addr::Lis(l))
+            }
+            CycleSearchResult::EmptyList => {
+                self.finalize_skip_max_list(0, Addr::EmptyList)
+            }
+            CycleSearchResult::PartialList(n, r) => {
+                self.finalize_skip_max_list(n, r.as_addr())
+            }
+            CycleSearchResult::ProperList(steps) => {
+                self.finalize_skip_max_list(steps, Addr::EmptyList)
+            }
+            CycleSearchResult::NotList => {
+                let xs0 = self[temp_v!(3)];
+                self.finalize_skip_max_list(0, xs0);
+            }
+        };
+    }
 
-                    match n {
-                        Addr::Con(Constant::Integer(ref n)) if n == &0 => {
-                            let xs0 = self[temp_v!(3)].clone();
-                            let xs = self[temp_v!(4)].clone();
+    pub(super) fn skip_max_list(&mut self) -> CallResult {
+        let max_steps = self.store(self.deref(self[temp_v!(2)]));
 
-                            self.unify(xs0, xs);
-                        }
-                        _ => {
-                            let search_result =
-                                if let Some(max_steps) = max_steps.to_isize() {
-                                    if max_steps == -1 {
-                                        self.detect_cycles(self[temp_v!(3)].clone())
+        match max_steps {
+            Addr::Con(h) if self.heap.integer_at(h) => {
+                if let HeapCellValue::Integer(ref max_steps) = self.heap.clone(h) {
+                    if max_steps.to_isize().map(|i| i >= -1).unwrap_or(false) {
+                        let n = self.store(self.deref(self[temp_v!(1)]));
+
+                        match n {
+                            Addr::Con(h) if self.heap.integer_at(h) => {
+                                if let HeapCellValue::Integer(ref n) = &self.heap[h] {
+                                    if n.as_ref() == &0 {
+                                        let xs0 = self[temp_v!(3)];
+                                        let xs  = self[temp_v!(4)];
+
+                                        self.unify(xs0, xs);
                                     } else {
-                                        self.detect_cycles_with_max(
-                                            max_steps as usize,
-                                            self[temp_v!(3)].clone(),
-                                        )
+                                        self.skip_max_list_result(max_steps.as_ref());
                                     }
                                 } else {
-                                    self.detect_cycles(self[temp_v!(3)].clone())
-                                };
-
-                            match search_result {
-                                CycleSearchResult::PStrLocation(steps, h, n) => {
-                                    self.finalize_skip_max_list(steps, Addr::PStrLocation(h, n));
-                                }
-                                CycleSearchResult::CompleteString(n, _) => {
-                                    self.finalize_skip_max_list(n, Addr::Con(Constant::EmptyList));
-                                }
-                                CycleSearchResult::UntouchedString(n, s) => {
-                                    self.finalize_skip_max_list(0, Addr::Con(Constant::String(n, s)));
-                                }
-                                CycleSearchResult::UntouchedList(l) => {
-                                    self.finalize_skip_max_list(0, Addr::Lis(l))
-                                }
-                                CycleSearchResult::EmptyList => {
-                                    self.finalize_skip_max_list(0, Addr::Con(Constant::EmptyList))
-                                }
-                                CycleSearchResult::PartialList(n, r) => {
-                                    self.finalize_skip_max_list(n, r.as_addr())
-                                }
-                                CycleSearchResult::ProperList(steps) => {
-                                    self.finalize_skip_max_list(steps, Addr::Con(Constant::EmptyList))
-                                }
-                                CycleSearchResult::NotList => {
-                                    let xs0 = self[temp_v!(3)].clone();
-                                    self.finalize_skip_max_list(0, xs0);
+                                    unreachable!()
                                 }
                             }
+                            _ => {
+                                self.skip_max_list_result(max_steps.as_ref());
+                            }
                         }
+                    } else {
+                        self.fail = true;
                     }
                 } else {
-                    self.fail = true;
+                    unreachable!()
                 }
             }
             Addr::HeapCell(_) | Addr::StackCell(..) => {
@@ -348,7 +349,14 @@ impl MachineState {
             addr => {
                 let stub = MachineError::functor_stub(clause_name!("$skip_max_list"), 4);
                 return Err(
-                    self.error_form(MachineError::type_error(ValidType::Integer, addr), stub)
+                    self.error_form(
+                        MachineError::type_error(
+                            self.heap.h(),
+                            ValidType::Integer,
+                            addr
+                        ),
+                        stub,
+                    )
                 );
             }
         };
@@ -357,50 +365,60 @@ impl MachineState {
     }
 
     fn get_stream_or_alias(
-        &self,
+        &mut self,
         addr: Addr,
         indices: &IndexStore,
         caller: &'static str,
     ) -> Result<Stream, MachineStub>
     {
         Ok(match addr {
-            Addr::Con(Constant::Atom(atom, op_spec)) => {
-                match indices.stream_aliases.get(&atom) {
-                    Some(stream) => {
-                        stream.clone()
-                    }
-                    None => {
-                        let stub = MachineError::functor_stub(clause_name!(caller), 1);
-                        let addr = Addr::Con(Constant::Atom(atom, op_spec));
+            Addr::Con(h) if self.heap.atom_at(h) => {
+               if let HeapCellValue::Atom(ref atom, ref spec) = self.heap.clone(h) {
+                    match indices.stream_aliases.get(atom) {
+                        Some(stream) => {
+                            stream.clone()
+                        }
+                        None => {
+                            let stub = MachineError::functor_stub(clause_name!(caller), 1);
+                            let h = self.heap.h();
 
-                        let h = self.heap.h();
-                        
-                        return Err(self.error_form(
-                            MachineError::existence_error(h, ExistenceError::Stream(addr)),
-                            stub,
-                        ));
+                            let addr = self.heap.to_unifiable(
+                                HeapCellValue::Atom(atom.clone(), spec.clone())
+                            );
+
+                            return Err(self.error_form(
+                                MachineError::existence_error(h + 1, ExistenceError::Stream(addr)),
+                                stub,
+                            ));
+                        }
                     }
+                } else {
+                    unreachable!()
                 }
             }
-            Addr::Stream(stream) => {
-                stream
+            Addr::Stream(h) => {
+                if let HeapCellValue::Stream(ref stream) = &self.heap[h] {
+                    stream.clone()
+                } else {
+                    unreachable!()
+                }
             }
             _ => {
                 let stub = MachineError::functor_stub(clause_name!(caller), 1);
-                
+
                 return Err(self.error_form(
-                    MachineError::domain_error(DomainError::StreamOrAlias, addr),
+                    MachineError::domain_error(DomainErrorType::StreamOrAlias, addr),
                     stub,
                 ));
             }
-        })     
+        })
     }
 
-    fn read_term(&mut self,
-                 current_input_stream: &mut Stream,
-                 indices: &mut IndexStore)
-                 -> CallResult
-    {
+    fn read_term(
+        &mut self,
+        current_input_stream: &mut Stream,
+        indices: &mut IndexStore,
+    ) -> CallResult {
         match self.read(
             &mut parsing_stream(current_input_stream.clone()),
             indices.atom_tbl.clone(),
@@ -418,13 +436,12 @@ impl MachineState {
 
                 for (var, binding) in term_write_result.var_dict.into_iter().rev() {
                     let var_atom = clause_name!(var.to_string(), indices.atom_tbl);
-                    let var_atom = Constant::Atom(var_atom, None);
 
                     let h = self.heap.h();
                     let spec = fetch_atom_op_spec(clause_name!("="), None, &indices.op_dir);
 
                     self.heap.push(HeapCellValue::NamedStr(2, clause_name!("="), spec));
-                    self.heap.push(HeapCellValue::Addr(Addr::Con(var_atom)));
+                    self.heap.push(HeapCellValue::Atom(var_atom, None));
                     self.heap.push(HeapCellValue::Addr(binding));
 
                     list_of_var_eqs.push(Addr::Str(h));
@@ -460,7 +477,7 @@ impl MachineState {
         let c = Constant::Usize(self.block);
         let addr = self[r].clone();
 
-        self.write_constant_to_var(addr, c);
+        self.write_constant_to_var(addr, &c);
         self.block
     }
 
@@ -497,7 +514,7 @@ impl MachineState {
         AddrConstr: Fn(usize) -> Addr,
     {
         match self.store(self.deref(self[temp_v!(1)].clone())) {
-            Addr::Con(Constant::Usize(lh_offset)) => {
+            Addr::Usize(lh_offset) => {
                 if lh_offset >= self.lifted_heap.h() {
                     self.lifted_heap.truncate(lh_offset);
                 } else {
@@ -528,9 +545,22 @@ impl MachineState {
                     let a2 = self[temp_v!(2)].clone();
 
                     if let Some(r) = a2.as_var() {
-                        let spec =
-                            get_clause_spec(name.clone(), *arity, composite_op!(&indices.op_dir));
-                        self.bind(r, Addr::DBRef(DBRef::NamedPred(name.clone(), *arity, spec)));
+                        let spec = get_clause_spec(
+                            name.clone(),
+                            *arity,
+                            composite_op!(&indices.op_dir),
+                        );
+
+                        let addr = self.heap.to_unifiable(HeapCellValue::DBRef(
+                            DBRef::NamedPred(
+                                name.clone(),
+                                *arity,
+                                spec,
+                            )
+                        ));
+
+                        self.bind(r, addr);
+
                         return;
                     }
                 }
@@ -551,16 +581,19 @@ impl MachineState {
                         let a2 = self[temp_v!(2)].clone();
 
                         if let Some(r) = a2.as_var() {
-                            self.bind(
-                                r,
-                                Addr::DBRef(DBRef::Op(
-                                    *priority,
-                                    *spec,
-                                    name.clone(),
-                                    op_dir.clone(),
-                                    SharedOpDesc::new(*priority, *spec),
-                                )),
+                            let addr = self.heap.to_unifiable(
+                                HeapCellValue::DBRef(
+                                    DBRef::Op(
+                                        *priority,
+                                        *spec,
+                                        name.clone(),
+                                        op_dir.clone(),
+                                        SharedOpDesc::new(*priority, *spec)
+                                    ),
+                                ),
                             );
+
+                            self.bind(r, addr);
                         } else {
                             self.fail = true;
                         }
@@ -572,7 +605,7 @@ impl MachineState {
     }
 
     fn int_to_char_code(
-        &mut self,
+        &self,
         n: &Integer,
         stub: &'static str,
         arity: usize,
@@ -627,16 +660,19 @@ impl MachineState {
                 return Err(self.error_form(err, stub));
             }
             Ok(Term::Constant(_, Constant::Rational(n))) => {
-                self.unify(nx, Addr::Con(Constant::Rational(n)))
+                let addr = self.heap.put_constant(Constant::Rational(n));
+                self.unify(nx, addr);
             }
             Ok(Term::Constant(_, Constant::Float(n))) => {
-                self.unify(nx, Addr::Con(Constant::Float(n)))
+                let addr = self.heap.put_constant(Constant::Float(n));
+                self.unify(nx, addr);
             }
             Ok(Term::Constant(_, Constant::Integer(n))) => {
-                self.unify(nx, Addr::Con(Constant::Integer(n)))
+                let addr = self.heap.put_constant(Constant::Integer(n));
+                self.unify(nx, addr);
             }
             Ok(Term::Constant(_, Constant::CharCode(c))) => {
-                self.unify(nx, Addr::Con(Constant::CharCode(c)))
+                self.unify(nx, Addr::CharCode(c))
             }
             _ => {
                 let err = ParserError::ParseBigInt(0, 0);
@@ -652,7 +688,11 @@ impl MachineState {
     }
 
     fn fetch_attribute_goals(&mut self, mut attr_goals: Vec<Addr>) {
-        attr_goals.sort_unstable_by(|a1, a2| self.compare_term_test(a1, a2));
+        attr_goals.sort_unstable_by(|a1, a2| {
+            self.compare_term_test(a1, a2)
+                .unwrap_or(cmp::Ordering::Less)
+        });
+
         self.term_dedup(&mut attr_goals);
 
         let attr_goals = Addr::HeapCell(self.heap.to_list(attr_goals.into_iter()));
@@ -684,8 +724,8 @@ impl MachineState {
 
                         // adjust cut point to occur after call_continuation.
                         if num_cells > 0 {
-                            if let Addr::Con(Constant::CutPoint(_)) = self.heap[s+2].as_addr(s+2) {
-                                and_frame[1] = Addr::Con(Constant::CutPoint(self.b));
+                            if let Addr::CutPoint(_) = self.heap[s+2].as_addr(s+2) {
+                                and_frame[1] = Addr::CutPoint(self.b);
                             } else {
                                 and_frame[1] = self.heap[s+2].as_addr(s+2);
                             }
@@ -735,7 +775,12 @@ impl MachineState {
             &SystemClauseType::BindFromRegister => {
                 let reg = self.store(self.deref(self[temp_v!(2)].clone()));
                 let n = match reg {
-                    Addr::Con(Constant::Integer(n)) => n.to_usize(),
+                    Addr::Con(h) =>
+                        if let HeapCellValue::Integer(ref n) = &self.heap[h] {
+                            n.to_usize()
+                        } else {
+                            unreachable!()
+                        }
                     _ => unreachable!()
                 };
 
@@ -766,15 +811,21 @@ impl MachineState {
                 return Ok(());
             }
             &SystemClauseType::CurrentInput => {
-                let addr = self.store(self.deref(self[temp_v!(1)].clone()));
+                let addr = self.store(self.deref(self[temp_v!(1)]));
+
                 let stream = current_input_stream.clone();
 
                 match addr {
                     addr if addr.is_ref() => {
-                        self.unify(Addr::Stream(stream), addr);
+                        let stream = self.heap.to_unifiable(HeapCellValue::Stream(stream));
+                        self.unify(stream, addr);
                     }
                     Addr::Stream(other_stream) => {
-                        self.fail = stream != other_stream;
+                        if let HeapCellValue::Stream(ref other_stream) = &self.heap[other_stream] {
+                            self.fail = current_input_stream != other_stream;
+                        } else {
+                            unreachable!()
+                        }
                     }
                     addr => {
                         let stub = MachineError::functor_stub(
@@ -783,7 +834,7 @@ impl MachineState {
                         );
 
                         let err = MachineError::domain_error(
-                            DomainError::Stream,
+                            DomainErrorType::Stream,
                             addr,
                         );
 
@@ -797,10 +848,15 @@ impl MachineState {
 
                 match addr {
                     addr if addr.is_ref() => {
-                        self.unify(Addr::Stream(stream), addr);
+                        let stream = self.heap.to_unifiable(HeapCellValue::Stream(stream));
+                        self.unify(stream, addr);
                     }
                     Addr::Stream(other_stream) => {
-                        self.fail = stream != other_stream;
+                        if let HeapCellValue::Stream(ref other_stream) = &self.heap[other_stream] {
+                            self.fail = current_output_stream != other_stream;
+                        } else {
+                            unreachable!()
+                        }
                     }
                     addr => {
                         let stub = MachineError::functor_stub(
@@ -809,7 +865,7 @@ impl MachineState {
                         );
 
                         let err = MachineError::domain_error(
-                            DomainError::Stream,
+                            DomainErrorType::Stream,
                             addr,
                         );
 
@@ -826,55 +882,72 @@ impl MachineState {
                 let a1 = self[temp_v!(1)].clone();
 
                 match self.store(self.deref(a1)) {
-                    Addr::Con(Constant::Char(c)) => {
-                        let iter = once(Addr::Con(Constant::Char(c)));
+                    Addr::Char(c) => {
+                        let iter = once(Addr::Char(c));
                         let list_of_chars = Addr::HeapCell(self.heap.to_list(iter));
 
-                        let a2 = self[temp_v!(2)].clone();
+                        let a2 = self[temp_v!(2)];
                         self.unify(a2, list_of_chars);
                     }
-                    Addr::Con(Constant::Atom(name, _)) => {
-                        let iter = name.as_str().chars().map(|c| Addr::Con(Constant::Char(c)));
-                        let list_of_chars = Addr::HeapCell(self.heap.to_list(iter));
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                       if let HeapCellValue::Atom(name, _) = self.heap.clone(h) {
+                            let iter = name.as_str().chars().map(|c| Addr::Char(c));
+                            let list_of_chars = Addr::HeapCell(self.heap.to_list(iter));
 
-                        let a2 = self[temp_v!(2)].clone();
+                            let a2 = self[temp_v!(2)];
 
-                        match self.store(self.deref(a2)) {
-                            Addr::Con(Constant::String(..))
-                                if !self.flags.double_quotes.is_chars() => {
-                                    self.fail = true;
+                            match self.store(self.deref(a2)) {
+                                Addr::PStrLocation(..)
+                                    if !self.flags.double_quotes.is_chars() => {
+                                        self.fail = true;
+                                    }
+                                a2 => {
+                                    self.unify(a2, list_of_chars);
                                 }
-                            a2 => {
-                                self.unify(a2, list_of_chars);
                             }
+                        } else {
+                            unreachable!()
                         }
                     }
-                    Addr::Con(Constant::EmptyList) => {
+                    Addr::EmptyList => {
                         let a2 = self[temp_v!(2)].clone();
                         let chars = vec![
-                            Addr::Con(Constant::Char('[')),
-                            Addr::Con(Constant::Char(']')),
+                            Addr::Char('['),
+                            Addr::Char(']'),
                         ];
 
-                        let list_of_chars = Addr::HeapCell(self.heap.to_list(chars.into_iter()));
+                        let list_of_chars =
+                            Addr::HeapCell(self.heap.to_list(chars.into_iter()));
 
                         self.unify(a2, list_of_chars);
                     }
-                    ref addr if addr.is_ref() => {
+                    addr if addr.is_ref() => {
                         let stub = MachineError::functor_stub(clause_name!("atom_chars"), 2);
 
-                        match self.try_from_list(temp_v!(2), stub.clone()) {
-                            Err(e) => return Err(e),
-                            Ok(addrs) => match self.try_char_list(addrs) {
-                                Ok(string) => {
-                                    let chars = clause_name!(string, indices.atom_tbl);
-                                    self.unify(
-                                        addr.clone(),
-                                        Addr::Con(Constant::Atom(chars, None)),
-                                    );
+                        match self.try_from_list(temp_v!(2), stub) {
+                            Err(e) => {
+                                return Err(e);
+                            }
+                            Ok(addrs) => {
+                                match self.try_char_list(addrs) {
+                                    Ok(string) => {
+                                        let chars = clause_name!(string, indices.atom_tbl);
+                                        let atom  = self.heap.to_unifiable(
+                                            HeapCellValue::Atom(chars, None)
+                                        );
+
+                                        self.unify(addr, atom);
+                                    }
+                                    Err(err) => {
+                                        let stub = MachineError::functor_stub(
+                                            clause_name!("atom_chars"),
+                                            2,
+                                        );
+
+                                        return Err(self.error_form(err, stub));
+                                    }
                                 }
-                                Err(err) => return Err(self.error_form(err, stub)),
-                            },
+                            }
                         }
                     }
                     _ => unreachable!(),
@@ -884,47 +957,51 @@ impl MachineState {
                 let a1 = self[temp_v!(1)].clone();
 
                 match self.store(self.deref(a1)) {
-                    Addr::Con(Constant::Char(c)) => {
-                        let iter = once(Addr::Con(Constant::CharCode(c as u32)));
+                    Addr::Char(c) => {
+                        let iter = once(Addr::CharCode(c as u32));
                         let list_of_codes = Addr::HeapCell(self.heap.to_list(iter));
 
                         let a2 = self[temp_v!(2)].clone();
                         self.unify(a2, list_of_codes);
                     }
-                    Addr::Con(Constant::Atom(name, _)) => {
-                        let a2 = self[temp_v!(2)].clone();
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                       if let HeapCellValue::Atom(name, _) = self.heap.clone(h) {
+                            let a2 = self[temp_v!(2)];
 
-                        match self.store(self.deref(a2)) {
-                            a2 @ Addr::Con(Constant::String(..)) => {
-                                if !self.flags.double_quotes.is_codes() {
-                                    self.fail = true;
-                                } else {
+                            match self.store(self.deref(a2)) {
+                                a2 @ Addr::PStrLocation(..) => {
+                                    if !self.flags.double_quotes.is_codes() {
+                                        self.fail = true;
+                                    } else {
+                                        let iter = name
+                                            .as_str()
+                                            .chars()
+                                            .map(|c| Addr::Char(c));
+
+                                        let list_of_codes = Addr::HeapCell(self.heap.to_list(iter));
+
+                                        self.unify(a2, list_of_codes);
+                                    }
+                                }
+                                a2 => {
                                     let iter = name
                                         .as_str()
                                         .chars()
-                                        .map(|c| Addr::Con(Constant::Char(c)));
+                                        .map(|c| Addr::CharCode(c as u32));
 
                                     let list_of_codes = Addr::HeapCell(self.heap.to_list(iter));
 
                                     self.unify(a2, list_of_codes);
                                 }
                             }
-                            a2 => {
-                                let iter = name
-                                    .as_str()
-                                    .chars()
-                                    .map(|c| Addr::Con(Constant::CharCode(c as u32)));
-
-                                let list_of_codes = Addr::HeapCell(self.heap.to_list(iter));
-
-                                self.unify(a2, list_of_codes);
-                            }
+                        } else {
+                            unreachable!()
                         }
                     }
-                    Addr::Con(Constant::EmptyList) => {
+                    Addr::EmptyList => {
                         let chars = vec![
-                            Addr::Con(Constant::CharCode('[' as u32)),
-                            Addr::Con(Constant::CharCode(']' as u32)),
+                            Addr::CharCode('[' as u32),
+                            Addr::CharCode(']' as u32),
                         ];
 
                         let list_of_codes = Addr::HeapCell(self.heap.to_list(chars.into_iter()));
@@ -932,35 +1009,48 @@ impl MachineState {
 
                         self.unify(a2, list_of_codes);
                     }
-                    ref addr if addr.is_ref() => {
+                    addr if addr.is_ref() => {
                         let stub = MachineError::functor_stub(clause_name!("atom_codes"), 2);
 
-                        match self.try_from_list(temp_v!(2), stub.clone()) {
+                        match self.try_from_list(temp_v!(2), stub) {
                             Err(e) => return Err(e),
                             Ok(addrs) => {
                                 let mut chars = String::new();
 
                                 for addr in addrs {
                                     match addr {
-                                        Addr::Con(Constant::Integer(n)) => {
-                                            let c = self.int_to_char_code(&n, "atom_codes", 2)?;
-                                            chars.push(std::char::from_u32(c).unwrap());
+                                        Addr::Con(h) if self.heap.integer_at(h) => {
+                                            if let HeapCellValue::Integer(ref n) = &self.heap[h] {
+                                                let c = self.int_to_char_code(&n, "atom_codes", 2)?;
+                                                chars.push(std::char::from_u32(c).unwrap());
+                                            } else {
+                                                unreachable!()
+                                            }
                                         }
-                                        Addr::Con(Constant::CharCode(c)) => {
+                                        Addr::CharCode(c) => {
                                             chars.push(std::char::from_u32(c).unwrap());
                                         }
                                         _ => {
+                                            let stub = MachineError::functor_stub(
+                                                clause_name!("atom_codes"),
+                                                2,
+                                            );
+
                                             let err = MachineError::type_error(
+                                                self.heap.h(),
                                                 ValidType::Integer,
-                                                addr.clone(),
+                                                addr,
                                             );
+
                                             return Err(self.error_form(err, stub));
                                         }
                                     }
                                 }
 
                                 let chars = clause_name!(chars, indices.atom_tbl);
-                                self.unify(addr.clone(), Addr::Con(Constant::Atom(chars, None)));
+                                let chars = self.heap.to_unifiable(HeapCellValue::Atom(chars, None));
+
+                                self.unify(addr, chars);
                             }
                         }
                     }
@@ -968,19 +1058,33 @@ impl MachineState {
                 };
             }
             &SystemClauseType::AtomLength => {
-                let a1 = self[temp_v!(1)].clone();
+                let a1 = self.store(self.deref(self[temp_v!(1)]));
 
                 let atom = match self.store(self.deref(a1)) {
-                    Addr::Con(Constant::Atom(name, _)) => name,
-                    Addr::Con(Constant::EmptyList) => clause_name!("[]"),
-                    Addr::Con(Constant::Char(c)) => clause_name!(c.to_string(), indices.atom_tbl),
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
+                            name.clone()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    Addr::EmptyList => {
+                        clause_name!("[]")
+                    }
+                    Addr::Char(c) => {
+                        clause_name!(c.to_string(), indices.atom_tbl)
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
 
                 let len = Integer::from(atom.as_str().chars().count());
-                let a2 = self[temp_v!(2)].clone();
+                let len = self.heap.to_unifiable(HeapCellValue::Integer(Rc::new(len)));
+
+                let a2 = self[temp_v!(2)];
 
-                self.unify(a2, Addr::Con(Constant::Integer(len)));
+                self.unify(a2, len);
             }
             &SystemClauseType::CallAttributeGoals => {
                 let p = self.attr_var_init.project_attrs_loc;
@@ -1018,18 +1122,36 @@ impl MachineState {
             &SystemClauseType::CharsToNumber => {
                 let stub = MachineError::functor_stub(clause_name!("number_chars"), 2);
 
-                match self.try_from_list(temp_v!(1), stub.clone()) {
-                    Err(e) => return Err(e),
-                    Ok(addrs) => match self.try_char_list(addrs) {
-                        Ok(string) => self.parse_number_from_string(string, indices, stub)?,
-                        Err(err) => return Err(self.error_form(err, stub)),
-                    },
+                match self.try_from_list(temp_v!(1), stub) {
+                    Err(e) => {
+                        return Err(e);
+                    }
+                    Ok(addrs) => {
+                        match self.try_char_list(addrs) {
+                            Ok(string) => {
+                                let stub = MachineError::functor_stub(clause_name!("number_chars"), 2);
+                                self.parse_number_from_string(string, indices, stub)?;
+                            }
+                            Err(err) => {
+                                let stub = MachineError::functor_stub(
+                                    clause_name!("number_chars"),
+                                    2,
+                                );
+
+                                return Err(self.error_form(err, stub));
+                            }
+                        }
+                    }
                 }
             }
             &SystemClauseType::CreatePartialString => {
-                let atom = match self.store(self.deref(self[temp_v!(1)].clone())) {
-                    Addr::Con(Constant::Atom(atom, _)) => {
-                        atom
+                let atom = match self.store(self.deref(self[temp_v!(1)])) {
+                    Addr::Con(h) => {
+                        if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
+                            name.clone()
+                        } else {
+                            unreachable!()
+                        }
                     }
                     _ => {
                         unreachable!()
@@ -1037,27 +1159,14 @@ impl MachineState {
                 };
 
                 let h = self.heap.h();
-                let pstr =
-                    match self.heap.allocate_pstr(atom.as_str()) {
-                        Some(pstr_addr) => {
-                            pstr_addr
-                        }
-                        None => {
-                            let stub = MachineError::functor_stub(clause_name!("partial_string"), 3);
-                            let err = MachineError::representation_error(
-                                RepFlag::Character,
-                            );
-
-                            return Err(self.error_form(err, stub));
-                        }
-                    };
 
+                let pstr = self.heap.allocate_pstr(atom.as_str());
                 let pstr_tail = self.heap[h + 1].as_addr(h + 1);
-                
-                self.unify(self[temp_v!(2)].clone(), pstr);
+
+                self.unify(self[temp_v!(2)], pstr);
 
                 if !self.fail {
-                    self.unify(self[temp_v!(3)].clone(), pstr_tail);
+                    self.unify(self[temp_v!(3)], pstr_tail);
                 }
             }
             &SystemClauseType::IsPartialString => {
@@ -1072,13 +1181,13 @@ impl MachineState {
                 }
             }
             &SystemClauseType::PartialStringTail => {
-                let pstr = self.store(self.deref(self[temp_v!(1)].clone()));
+                let pstr = self.store(self.deref(self[temp_v!(1)]));
 
                 match pstr {
                     Addr::PStrLocation(h, _) => {
                         let tail = self.heap[h + 1].as_addr(h + 1);
-                        let target = self[temp_v!(2)].clone();
-                        
+                        let target = self[temp_v!(2)];
+
                         self.unify(tail, target);
                     }
                     _ => {
@@ -1087,34 +1196,55 @@ impl MachineState {
                 }
             }
             &SystemClauseType::NumberToChars => {
-                let n = self[temp_v!(1)].clone();
-                let chs = self[temp_v!(2)].clone();
+                let n = self[temp_v!(1)];
+                let chs = self[temp_v!(2)];
 
                 let string = match self.store(self.deref(n)) {
-                    Addr::Con(Constant::Float(OrderedFloat(n))) => format!("{0:<20?}", n),
-                    Addr::Con(Constant::Integer(n)) => n.to_string(),
-                    _ => unreachable!(),
+                    Addr::Float(OrderedFloat(n)) => {
+                        format!("{0:<20?}", n)
+                    }
+                    Addr::Con(h) if self.heap.integer_at(h) => {
+                        if let HeapCellValue::Integer(ref n) = &self.heap[h] {
+                            n.to_string()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
 
-                let chars = string.trim().chars().map(|c| Addr::Con(Constant::Char(c)));
+                let chars = string.trim().chars().map(|c| Addr::Char(c));
                 let char_list = Addr::HeapCell(self.heap.to_list(chars));
 
                 self.unify(char_list, chs);
             }
             &SystemClauseType::NumberToCodes => {
-                let n = self[temp_v!(1)].clone();
-                let chs = self[temp_v!(2)].clone();
+                let n = self[temp_v!(1)];
+                let chs = self[temp_v!(2)];
 
                 let string = match self.store(self.deref(n)) {
-                    Addr::Con(Constant::Float(OrderedFloat(n))) => format!("{0:<20?}", n),
-                    Addr::Con(Constant::Integer(n)) => n.to_string(),
-                    _ => unreachable!(),
+                    Addr::Float(OrderedFloat(n)) => {
+                        format!("{0:<20?}", n)
+                    }
+                    Addr::Con(h) if self.heap.integer_at(h) => {
+                        if let HeapCellValue::Integer(ref n) = &self.heap[h] {
+                            n.to_string()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
 
                 let codes = string
                     .trim()
                     .chars()
-                    .map(|c| Addr::Con(Constant::CharCode(c as u32)));
+                    .map(|c| Addr::CharCode(c as u32));
+
                 let codes_list = Addr::HeapCell(self.heap.to_list(codes));
 
                 self.unify(codes_list, chs);
@@ -1122,14 +1252,26 @@ impl MachineState {
             &SystemClauseType::CodesToNumber => {
                 let stub = MachineError::functor_stub(clause_name!("number_codes"), 2);
 
-                match self.try_from_list(temp_v!(1), stub.clone()) {
-                    Err(e) => return Err(e),
-                    Ok(addrs) => match self.try_char_list(addrs) {
-                        Ok(chars) => {
-                            self.parse_number_from_string(chars, indices, stub)?
+                match self.try_from_list(temp_v!(1), stub) {
+                    Err(e) => {
+                        return Err(e);
+                    }
+                    Ok(addrs) => {
+                        match self.try_char_list(addrs) {
+                            Ok(chars) => {
+                                let stub = MachineError::functor_stub(clause_name!("number_codes"), 2);
+                                self.parse_number_from_string(chars, indices, stub)?;
+                            }
+                            Err(err) => {
+                                let stub = MachineError::functor_stub(
+                                    clause_name!("number_codes"),
+                                    2,
+                                );
+
+                                return Err(self.error_form(err, stub));
+                            }
                         }
-                        Err(err) => return Err(self.error_form(err, stub)),
-                    },
+                    }
                 }
             }
             &SystemClauseType::ModuleAssertDynamicPredicateToFront => {
@@ -1148,43 +1290,55 @@ impl MachineState {
             }
             &SystemClauseType::LiftedHeapLength => {
                 let a1 = self[temp_v!(1)].clone();
-                let lh_len = Addr::Con(Constant::Usize(self.lifted_heap.h()));
+                let lh_len = Addr::Usize(self.lifted_heap.h());
 
                 self.unify(a1, lh_len);
             }
             &SystemClauseType::CharCode => {
-                let a1 = self[temp_v!(1)].clone();
+                let a1 = self[temp_v!(1)];
 
                 match self.store(self.deref(a1)) {
-                    Addr::Con(Constant::Atom(name, _)) => {
-                        let c = name.as_str().chars().next().unwrap();
-                        let a2 = self[temp_v!(2)].clone();
-                        let c = Integer::from(c as u32);
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        let c =
+                            if let HeapCellValue::Atom(name, _) = &self.heap[h] {
+                                if name.is_char() {
+                                    name.as_str().chars().next().unwrap()
+                                } else {
+                                    self.fail = true;
+                                    return Ok(());
+                                }
+                            } else {
+                                unreachable!()
+                            };
 
-                        self.unify(Addr::Con(Constant::Integer(c)), a2);
+                        let a2 = self[temp_v!(2)];
+                        self.unify(Addr::CharCode(c as u32), a2);
                     }
-                    Addr::Con(Constant::Char(c)) => {
-                        let a2 = self[temp_v!(2)].clone();
-                        let c = Integer::from(c as u32);
-
-                        self.unify(Addr::Con(Constant::Integer(c)), a2);
+                    Addr::Char(c) => {
+                        let a2 = self[temp_v!(2)];
+                        self.unify(Addr::CharCode(c as u32), a2);
                     }
                     addr if addr.is_ref() => {
-                        let a2 = self[temp_v!(2)].clone();
+                        let a2 = self[temp_v!(2)];
 
                         match self.store(self.deref(a2)) {
-                            Addr::Con(Constant::CharCode(code)) => {
+                            Addr::CharCode(code) => {
                                 if let Some(c) = std::char::from_u32(code) {
-                                    self.unify(Addr::Con(Constant::Char(c)), addr);
+                                    self.unify(Addr::Char(c), addr);
                                 } else {
                                     self.fail = true;
                                 }
                             }
-                            Addr::Con(Constant::Integer(n)) => {
-                                let c = self.int_to_char_code(&n, "char_code", 2)?;
+                            Addr::Con(h) if self.heap.integer_at(h) => {
+                                let c =
+                                    if let HeapCellValue::Integer(n) = &self.heap[h] {
+                                        self.int_to_char_code(&n, "char_code", 2)?
+                                    } else {
+                                        unreachable!()
+                                    };
 
                                 if let Some(c) = std::char::from_u32(c) {
-                                    self.unify(Addr::Con(Constant::Char(c)), addr);
+                                    self.unify(Addr::Char(c), addr);
                                 } else {
                                     self.fail = true;
                                 }
@@ -1199,7 +1353,7 @@ impl MachineState {
                 let addr = self.store(self.deref(self[temp_v!(1)].clone()));
 
                 match addr {
-                    Addr::Con(Constant::Usize(old_b)) | Addr::Con(Constant::CutPoint(old_b)) => {
+                    Addr::Usize(old_b) | Addr::CutPoint(old_b) => {
                         let prev_b = self.stack.index_or_frame(self.b).prelude.b;
                         let prev_b = self.stack.index_or_frame(prev_b).prelude.b;
 
@@ -1214,14 +1368,22 @@ impl MachineState {
                 self.copy_term(AttrVarPolicy::StripAttributes);
             }
             &SystemClauseType::FetchGlobalVar => {
-                let key = self[temp_v!(1)].clone();
+                let key = self[temp_v!(1)];
 
                 let key = match self.store(self.deref(key)) {
-                    Addr::Con(Constant::Atom(atom, _)) => atom,
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
+                            atom.clone()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
 
-                let addr = self[temp_v!(2)].clone();
+                let addr = self[temp_v!(2)];
 
                 match indices.global_variables.get_mut(&key) {
                     Some((ref mut ball, None)) => {
@@ -1241,11 +1403,19 @@ impl MachineState {
                 let key = self[temp_v!(1)].clone();
 
                 let key = match self.store(self.deref(key)) {
-                    Addr::Con(Constant::Atom(atom, _)) => atom,
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
+                            atom.clone()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
 
-                let addr = self[temp_v!(2)].clone();
+                let addr = self[temp_v!(2)];
 
                 match indices.global_variables.get_mut(&key) {
                     Some((ref mut ball, ref mut offset @ None)) => {
@@ -1260,7 +1430,7 @@ impl MachineState {
                     Some((_, Some(h))) => {
                         let offset = self[temp_v!(3)].clone();
 
-                        self.unify(offset, Addr::Con(Constant::Usize(*h)));
+                        self.unify(offset, Addr::Usize(*h));
 
                         if !self.fail {
                             self.unify(addr, Addr::HeapCell(*h));
@@ -1278,10 +1448,16 @@ impl MachineState {
                 let a1 = self[temp_v!(1)].clone();
 
                 match result {
-                    Some(Ok(b)) => self.unify(Addr::Con(Constant::Char(b as char)), a1),
+                    Some(Ok(b)) => {
+                        self.unify(Addr::Char(b as char), a1);
+                    }
                     Some(Err(_)) => {
-                        let end_of_file = clause_name!("end_of_file");
-                        self.unify(a1, Addr::Con(Constant::Atom(end_of_file, None)));
+                        let end_of_file = self.heap.to_unifiable(HeapCellValue::Atom(
+                            clause_name!("end_of_file"),
+                            None,
+                        ));
+
+                        self.unify(a1, end_of_file);
                     }
                     None => {
                         let stub = MachineError::functor_stub(clause_name!("get_char"), 1);
@@ -1293,11 +1469,17 @@ impl MachineState {
                 }
             }
             &SystemClauseType::GetModuleClause => {
-                let module = self[temp_v!(3)].clone();
-                let head = self[temp_v!(1)].clone();
+                let module = self[temp_v!(3)];
+                let head = self[temp_v!(1)];
 
                 let module = match self.store(self.deref(module)) {
-                    Addr::Con(Constant::Atom(module, _)) => module,
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(module, _) = &self.heap[h] {
+                            module.clone()
+                        } else {
+                            unreachable!()
+                        }
+                    }
                     _ => {
                         self.fail = true;
                         return Ok(());
@@ -1305,35 +1487,52 @@ impl MachineState {
                 };
 
                 let subsection = match self.store(self.deref(head)) {
-                    Addr::Str(s) => match self.heap[s].clone() {
-                        HeapCellValue::NamedStr(arity, name, ..) => {
-                            indices.get_clause_subsection(module, name, arity)
+                    Addr::Str(s) => match &self.heap[s] {
+                        &HeapCellValue::NamedStr(arity, ref name, ..) => {
+                            indices.get_clause_subsection(module, name.clone(), arity)
+                        }
+                        _ => {
+                            unreachable!()
                         }
-                        _ => unreachable!(),
                     },
-                    Addr::Con(Constant::Atom(name, _)) => {
-                        indices.get_clause_subsection(module, name, 0)
+                    Addr::Con(h) => {
+                       if let HeapCellValue::Atom(name, _) = &self.heap[h] {
+                            indices.get_clause_subsection(module, name.clone(), 0)
+                        } else {
+                            unreachable!()
+                        }
+                    }
+
+                    _ => {
+                        unreachable!()
                     }
-                    _ => unreachable!(),
                 };
 
                 match subsection {
                     Some(dynamic_predicate_info) => {
                         self.execute_at_index(
                             2,
-                            dir_entry!(dynamic_predicate_info.clauses_subsection_p)
+                            dir_entry!(dynamic_predicate_info.clauses_subsection_p),
                         );
+
                         return Ok(());
                     }
-                    None => self.fail = true,
+                    None => {
+                        self.fail = true;
+                    }
                 }
             }
             &SystemClauseType::ModuleHeadIsDynamic => {
-                let module = self[temp_v!(2)].clone();
-                let head = self[temp_v!(1)].clone();
+                let module = self[temp_v!(2)];
+                let head = self[temp_v!(1)];
 
                 let module = match self.store(self.deref(module)) {
-                    Addr::Con(Constant::Atom(module, _)) => module,
+                    Addr::Con(h) if self.heap.atom_at(h) =>
+                        if let HeapCellValue::Atom(module, _) = &self.heap[h] {
+                            module.clone()
+                        } else {
+                            unreachable!()
+                        }
                     _ => {
                         self.fail = true;
                         return Ok(());
@@ -1341,14 +1540,20 @@ impl MachineState {
                 };
 
                 self.fail = !match self.store(self.deref(head)) {
-                    Addr::Str(s) => match self.heap[s].clone() {
-                        HeapCellValue::NamedStr(arity, name, ..) => {
-                            indices.get_clause_subsection(module, name, arity).is_some()
+                    Addr::Str(s) => match &self.heap[s] {
+                        &HeapCellValue::NamedStr(arity, ref name, ..) => {
+                            indices.get_clause_subsection(module, name.clone(), arity)
+                                   .is_some()
                         }
                         _ => unreachable!(),
                     },
-                    Addr::Con(Constant::Atom(name, _)) => {
-                        indices.get_clause_subsection(module, name, 0).is_some()
+                    Addr::Con(h) => {
+                       if let HeapCellValue::Atom(name, _) = &self.heap[h] {
+                            indices.get_clause_subsection(module, name.clone(), 0)
+                                   .is_some()
+                        } else {
+                            unreachable!()
+                        }
                     }
                     _ => unreachable!(),
                 };
@@ -1357,22 +1562,29 @@ impl MachineState {
                 let head = self[temp_v!(1)].clone();
 
                 self.fail = !match self.store(self.deref(head)) {
-                    Addr::Str(s) => match self.heap[s].clone() {
-                        HeapCellValue::NamedStr(arity, name, ..) => indices
-                            .get_clause_subsection(name.owning_module(), name, arity)
+                    Addr::Str(s) => match &self.heap[s] {
+                        &HeapCellValue::NamedStr(arity, ref name, ..) => indices
+                            .get_clause_subsection(name.owning_module(), name.clone(), arity)
                             .is_some(),
                         _ => unreachable!(),
                     },
-                    Addr::Con(Constant::Atom(name, _)) => indices
-                        .get_clause_subsection(name.owning_module(), name, 0)
-                        .is_some(),
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                       if let HeapCellValue::Atom(name, _) = &self.heap[h] {
+                            indices.get_clause_subsection(name.owning_module(), name.clone(), 0)
+                                   .is_some()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
             }
             &SystemClauseType::CopyToLiftedHeap => {
-                match self.store(self.deref(self[temp_v!(1)].clone())) {
-                    Addr::Con(Constant::Usize(lh_offset)) => {
-                        let copy_target = self[temp_v!(2)].clone();
+                match self.store(self.deref(self[temp_v!(1)])) {
+                    Addr::Usize(lh_offset) => {
+                        let copy_target = self[temp_v!(2)];
 
                         let old_threshold = self.copy_findall_solution(lh_offset, copy_target);
                         let new_threshold = self.lifted_heap.h() - lh_offset;
@@ -1389,17 +1601,19 @@ impl MachineState {
                             }
                         }
                     }
-                    _ => self.fail = true,
+                    _ => {
+                        self.fail = true;
+                    }
                 }
             }
             &SystemClauseType::DeleteAttribute => {
-                let ls0 = self.store(self.deref(self[temp_v!(1)].clone()));
+                let ls0 = self.store(self.deref(self[temp_v!(1)]));
 
                 if let Addr::Lis(l1) = ls0 {
                     if let Addr::Lis(l2) = self.store(self.deref(Addr::HeapCell(l1 + 1))) {
                         let old_addr = self.heap[l1 + 1].as_addr(l1 + 1);
-
                         let tail = self.store(self.deref(Addr::HeapCell(l2 + 1)));
+
                         let tail = if tail.is_ref() {
                             Addr::HeapCell(l1 + 1)
                         } else {
@@ -1418,11 +1632,11 @@ impl MachineState {
                 }
             }
             &SystemClauseType::DeleteHeadAttribute => {
-                let addr = self.store(self.deref(self[temp_v!(1)].clone()));
+                let addr = self.store(self.deref(self[temp_v!(1)]));
 
                 match addr {
                     Addr::AttrVar(h) => {
-                        let addr = self.heap[h + 1].as_addr(h + 1).clone();
+                        let addr = self.heap[h + 1].as_addr(h + 1);
                         let addr = self.store(self.deref(addr));
 
                         match addr {
@@ -1444,52 +1658,81 @@ impl MachineState {
                 }
             }
             &SystemClauseType::DynamicModuleResolution(narity) => {
-                let module_name = self.store(self.deref(self[temp_v!(1 + narity)].clone()));
-
-                if let Addr::Con(Constant::Atom(module_name, _)) = module_name {
-                    match self.store(self.deref(self[temp_v!(2 + narity)].clone())) {
-                        Addr::Str(a) => {
-                            if let HeapCellValue::NamedStr(arity, name, _) = self.heap[a].clone() {
-                                for i in (arity + 1 .. arity + narity + 1).rev() {
-                                    self.registers[i] = self.registers[i - arity].clone();
-                                }
+                let module_name = self.store(self.deref(self[temp_v!(1 + narity)]));
 
-                                for i in 1 .. arity + 1 {
-                                    self.registers[i] = self.heap[a + i].as_addr(a + i);
-                                }
+                let module_name = match module_name {
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref module_name, _) = self.heap[h] {
+                            module_name.clone()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
+                };
 
-                                return self.module_lookup(
-                                    indices,
-                                    (name, arity + narity),
-                                    module_name,
-                                    true,
-                                );
+                match self.store(self.deref(self[temp_v!(2 + narity)])) {
+                    Addr::Str(a) => {
+                        if let HeapCellValue::NamedStr(arity, name, _) = self.heap.clone(a) {
+                            for i in (arity + 1 .. arity + narity + 1).rev() {
+                                self.registers[i] = self.registers[i - arity];
                             }
+
+                            for i in 1 .. arity + 1 {
+                                self.registers[i] = self.heap[a + i].as_addr(a + i);
+                            }
+
+                            return self.module_lookup(
+                                indices,
+                                (name, arity + narity),
+                                module_name,
+                                true,
+                            );
+                        } else {
+                            unreachable!()
                         }
-                        Addr::Con(Constant::Atom(name, _)) => {
-                            return self.module_lookup(indices, (name, narity), module_name, true)
+                    }
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                       if let HeapCellValue::Atom(name, _) = self.heap.clone(h) {
+                            return self.module_lookup(
+                                indices,
+                                (name.clone(), narity),
+                                module_name,
+                                true,
+                            );
+                        } else {
+                            unreachable!()
                         }
-                        addr => {
-                            let stub = MachineError::functor_stub(clause_name!("(:)"), 2);
+                    }
+                    addr => {
+                        let stub = MachineError::functor_stub(clause_name!("(:)"), 2);
 
-                            let type_error = MachineError::type_error(ValidType::Callable, addr);
-                            let type_error = self.error_form(type_error, stub);
+                        let type_error = MachineError::type_error(
+                            self.heap.h(),
+                            ValidType::Callable,
+                            addr,
+                        );
 
-                            return Err(type_error);
-                        }
+                        let type_error = self.error_form(type_error, stub);
+                        return Err(type_error);
                     }
-                };
+                }
             }
             &SystemClauseType::EnqueueAttributeGoal => {
-                let addr = self[temp_v!(1)].clone();
+                let addr = self[temp_v!(1)];
                 self.attr_var_init.attribute_goals.push(addr);
             }
             &SystemClauseType::EnqueueAttributedVar => {
-                let addr = self[temp_v!(1)].clone();
+                let addr = self[temp_v!(1)];
 
                 match self.store(self.deref(addr)) {
-                    Addr::AttrVar(h) => self.attr_var_init.attr_var_queue.push(h),
-                    _ => {}
+                    Addr::AttrVar(h) => {
+                        self.attr_var_init.attr_var_queue.push(h);
+                    }
+                    _ => {
+                    }
                 }
             }
             &SystemClauseType::ExpandGoal => {
@@ -1501,12 +1744,12 @@ impl MachineState {
                 return Ok(());
             }
             &SystemClauseType::GetNextDBRef => {
-                let a1 = self[temp_v!(1)].clone();
+                let a1 = self[temp_v!(1)];
 
                 match self.store(self.deref(a1)) {
                     addr @ Addr::HeapCell(_)
-                    | addr @ Addr::StackCell(..)
-                    | addr @ Addr::AttrVar(_) => {
+                  | addr @ Addr::StackCell(..)
+                  | addr @ Addr::AttrVar(_) => {
                         let mut iter = indices.code_dir.iter();
 
                         while let Some(((name, arity), _)) = iter.next() {
@@ -1519,29 +1762,46 @@ impl MachineState {
                                 *arity,
                                 composite_op!(&indices.op_dir),
                             );
+
                             let db_ref = DBRef::NamedPred(name.clone(), *arity, spec);
                             let r = addr.as_var().unwrap();
 
-                            self.bind(r, Addr::DBRef(db_ref));
+                            let addr = self.heap.to_unifiable(
+                                HeapCellValue::DBRef(db_ref)
+                            );
+
+                            self.bind(r, addr);
+
                             return return_from_clause!(self.last_call, self);
                         }
 
                         self.fail = true;
                     }
-                    Addr::DBRef(DBRef::Op(..)) => self.fail = true,
-                    Addr::DBRef(ref db_ref) => self.get_next_db_ref(&indices, db_ref),
+                    Addr::Con(h) => {
+                        match self.heap.clone(h) {
+                            HeapCellValue::DBRef(DBRef::Op(..)) => {
+                                self.fail = true;
+                            }
+                            HeapCellValue::DBRef(ref db_ref) => {
+                                self.get_next_db_ref(indices, db_ref);
+                            }
+                            _ => {
+                                self.fail = true;
+                            }
+                        }
+                    }
                     _ => {
                         self.fail = true;
                     }
-                };
+                }
             }
             &SystemClauseType::GetNextOpDBRef => {
-                let a1 = self[temp_v!(1)].clone();
+                let a1 = self[temp_v!(1)];
 
                 match self.store(self.deref(a1)) {
                     addr @ Addr::HeapCell(_)
-                    | addr @ Addr::StackCell(..)
-                    | addr @ Addr::AttrVar(_) => {
+                  | addr @ Addr::StackCell(..)
+                  | addr @ Addr::AttrVar(_) => {
                         let mut unossified_op_dir = OssifiedOpDir::new();
 
                         unossified_op_dir.extend(indices.op_dir.iter().filter_map(
@@ -1571,9 +1831,13 @@ impl MachineState {
                                     ossified_op_dir.clone(),
                                     SharedOpDesc::new(*priority, *spec),
                                 );
+
                                 let r = addr.as_var().unwrap();
+                                let addr = self.heap.to_unifiable(
+                                    HeapCellValue::DBRef(db_ref)
+                                );
 
-                                self.bind(r, Addr::DBRef(db_ref));
+                                self.bind(r, addr);
                             }
                             None => {
                                 self.fail = true;
@@ -1581,106 +1845,159 @@ impl MachineState {
                             }
                         }
                     }
-                    Addr::DBRef(DBRef::NamedPred(..)) => self.fail = true,
-                    Addr::DBRef(ref db_ref) => self.get_next_db_ref(&indices, db_ref),
+                    Addr::Con(h) => {
+                        match self.heap.clone(h) {
+                            HeapCellValue::DBRef(DBRef::NamedPred(..)) => {
+                                self.fail = true;
+                            }
+                            HeapCellValue::DBRef(ref db_ref) => {
+                                self.get_next_db_ref(indices, db_ref);
+                            }
+                            _ => {
+                                self.fail = true;
+                            }
+                        }
+                    }
                     _ => {
                         self.fail = true;
                     }
                 }
             }
             &SystemClauseType::LookupDBRef => {
-                let a1 = self[temp_v!(1)].clone();
+                let a1 = self[temp_v!(1)];
 
                 match self.store(self.deref(a1)) {
-                    Addr::DBRef(db_ref) => match db_ref {
-                        DBRef::NamedPred(name, arity, spec) => {
-                            let a2 = self[temp_v!(2)].clone();
-                            let a3 = self[temp_v!(3)].clone();
-
-                            let arity = Integer::from(arity);
+                    Addr::Con(h) => {
+                        match self.heap.clone(h) {
+                            HeapCellValue::DBRef(DBRef::NamedPred(name, arity, spec)) => {
+                                let a2 = self[temp_v!(2)];
+                                let a3 = self[temp_v!(3)];
+
+                                let atom = self.heap.to_unifiable(
+                                    HeapCellValue::Atom(name, spec)
+                                );
 
-                            self.unify(a2, Addr::Con(Constant::Atom(name, spec)));
+                                self.unify(a2, atom);
 
-                            if !self.fail {
-                                self.unify(a3, Addr::Con(Constant::Integer(arity)));
+                                if !self.fail {
+                                    self.unify(a3, Addr::Usize(arity));
+                                }
+                            }
+                            _ => {
+                                self.fail = true;
                             }
                         }
-                        _ => self.fail = true,
-                    },
-                    _ => self.fail = true,
+                    }
+                    _ => {
+                        self.fail = true;
+                    }
                 }
             }
             &SystemClauseType::LookupOpDBRef => {
-                let a1 = self[temp_v!(1)].clone();
+                let a1 = self[temp_v!(1)];
 
                 match self.store(self.deref(a1)) {
-                    Addr::DBRef(db_ref) => match db_ref {
-                        DBRef::Op(priority, spec, name, _, shared_op_desc) => {
-                            let prec = self[temp_v!(2)].clone();
-                            let specifier = self[temp_v!(3)].clone();
-                            let op = self[temp_v!(4)].clone();
-
-                            let spec = match spec {
-                                FX => "fx",
-                                FY => "fy",
-                                XF => "xf",
-                                YF => "yf",
-                                XFX => "xfx",
-                                XFY => "xfy",
-                                YFX => "yfx",
-                                _ => {
-                                    self.fail = true;
-                                    return Ok(());
-                                }
-                            };
+                    Addr::Con(h) => {
+                        match self.heap.clone(h) {
+                            HeapCellValue::DBRef(DBRef::Op(
+                                priority,
+                                spec,
+                                name,
+                                _,
+                                shared_op_desc,
+                            )) => {
+                                let prec = self[temp_v!(2)];
+                                let specifier = self[temp_v!(3)];
+                                let op = self[temp_v!(4)];
+
+                                let spec = match spec {
+                                    FX => "fx",
+                                    FY => "fy",
+                                    XF => "xf",
+                                    YF => "yf",
+                                    XFX => "xfx",
+                                    XFY => "xfy",
+                                    YFX => "yfx",
+                                    _ => {
+                                        self.fail = true;
+                                        return Ok(());
+                                    }
+                                };
+
+                                let a3 = self.heap.to_unifiable(
+                                    HeapCellValue::Atom(clause_name!(spec), None)
+                                );
 
-                            let a2 = Integer::from(priority);
-                            let a3 = Addr::Con(Constant::Atom(clause_name!(spec), None));
-                            let a4 = Addr::Con(Constant::Atom(name, Some(shared_op_desc)));
+                                let a4 = self.heap.to_unifiable(
+                                    HeapCellValue::Atom(name, Some(shared_op_desc))
+                                );
 
-                            self.unify(Addr::Con(Constant::Integer(a2)), prec);
+                                self.unify(Addr::Usize(priority), prec);
 
-                            if !self.fail {
-                                self.unify(a3, specifier);
-                            }
+                                if !self.fail {
+                                    self.unify(a3, specifier);
+                                }
 
-                            if !self.fail {
-                                self.unify(a4, op);
+                                if !self.fail {
+                                    self.unify(a4, op);
+                                }
+                            }
+                            _ => {
+                                self.fail = true;
                             }
                         }
-                        _ => self.fail = true,
-                    },
-                    _ => self.fail = true,
+                    }
+                    _ => {
+                        self.fail = true;
+                    }
                 }
             }
             &SystemClauseType::Maybe => {
                 let result = {
                     let mut rand = RANDOM_STATE.borrow_mut();
-
                     rand.bits(1) == 0
                 };
 
                 self.fail = result;
             }
             &SystemClauseType::OpDeclaration => {
-                let priority = self[temp_v!(1)].clone();
-                let specifier = self[temp_v!(2)].clone();
-                let op = self[temp_v!(3)].clone();
+                let priority = self[temp_v!(1)];
+                let specifier = self[temp_v!(2)];
+                let op = self[temp_v!(3)];
 
                 let priority = match self.store(self.deref(priority)) {
-                    Addr::Con(Constant::Integer(n)) => n.to_usize().unwrap(),
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.integer_at(h) =>
+                        if let HeapCellValue::Integer(ref n) = &self.heap[h] {
+                            n.to_usize().unwrap()
+                        } else {
+                            unreachable!()
+                        },
+                    _ =>
+                        unreachable!(),
                 };
 
                 let specifier = match self.store(self.deref(specifier)) {
-                    Addr::Con(Constant::Atom(name, _)) => name,
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.atom_at(h) =>
+                        if let HeapCellValue::Atom(ref specifier, _) = &self.heap[h] {
+                            specifier.clone()
+                        } else {
+                            unreachable!()
+                        },
+                    _ =>
+                        unreachable!(),
                 };
 
                 let op = match self.store(self.deref(op)) {
-                    Addr::Con(Constant::Atom(name, _)) => name,
-                    Addr::Con(Constant::Char(c)) => clause_name!(c.to_string(), indices.atom_tbl),
-                    _ => unreachable!(),
+                    Addr::Char(c) =>
+                        clause_name!(c.to_string(), indices.atom_tbl),
+                    Addr::Con(h) if self.heap.atom_at(h) =>
+                        if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
+                            name.clone()
+                        } else {
+                            unreachable!()
+                        },
+                    _ =>
+                        unreachable!(),
                 };
 
                 let module = op.owning_module();
@@ -1697,7 +2014,8 @@ impl MachineState {
                     });
 
                 match result {
-                    Ok(()) => {}
+                    Ok(()) => {
+                    }
                     Err(e) => {
                         // 8.14.3.3 l)
                         let e = MachineError::session_error(self.heap.h(), e);
@@ -1712,7 +2030,7 @@ impl MachineState {
                 self.truncate_if_no_lifted_heap_diff(|h| Addr::HeapCell(h))
             }
             &SystemClauseType::TruncateIfNoLiftedHeapGrowth => {
-                self.truncate_if_no_lifted_heap_diff(|_| Addr::Con(Constant::EmptyList))
+                self.truncate_if_no_lifted_heap_diff(|_| Addr::EmptyList)
             }
             &SystemClauseType::ClearAttributeGoals => {
                 self.attr_var_init.attribute_goals.clear();
@@ -1726,90 +2044,101 @@ impl MachineState {
                 self.fetch_attribute_goals(attr_goals);
             }
             &SystemClauseType::GetAttributedVariableList => {
-                let attr_var = self.store(self.deref(self[temp_v!(1)].clone()));
-                let attr_var_list = match attr_var {
-                    Addr::AttrVar(h) => h + 1,
-                    attr_var @ Addr::HeapCell(_) | attr_var @ Addr::StackCell(..) => {
-                        // create an AttrVar in the heap.
-                        let h = self.heap.h();
+                let attr_var = self.store(self.deref(self[temp_v!(1)]));
+                let attr_var_list =
+                    match attr_var {
+                        Addr::AttrVar(h) => {
+                            h + 1
+                        }
+                        attr_var @ Addr::HeapCell(_)
+                      | attr_var @ Addr::StackCell(..) => {
+                          // create an AttrVar in the heap.
+                          let h = self.heap.h();
 
-                        self.heap.push(HeapCellValue::Addr(Addr::AttrVar(h)));
-                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h + 1)));
+                          self.heap.push(HeapCellValue::Addr(Addr::AttrVar(h)));
+                          self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h + 1)));
 
-                        self.bind(Ref::AttrVar(h), attr_var);
-                        h + 1
-                    }
-                    _ => {
-                        self.fail = true;
-                        return Ok(());
-                    }
-                };
+                          self.bind(Ref::AttrVar(h), attr_var);
+                          h + 1
+                        }
+                        _ => {
+                            self.fail = true;
+                            return Ok(());
+                        }
+                    };
 
-                let list_addr = self[temp_v!(2)].clone();
+                let list_addr = self[temp_v!(2)];
                 self.unify(Addr::HeapCell(attr_var_list), list_addr);
             }
             &SystemClauseType::GetAttrVarQueueDelimiter => {
-                let addr = self[temp_v!(1)].clone();
-                let value = Addr::Con(Constant::Usize(self.attr_var_init.attr_var_queue.len()));
+                let addr = self[temp_v!(1)];
+                let value = Addr::Usize(self.attr_var_init.attr_var_queue.len());
 
                 self.unify(addr, value);
             }
             &SystemClauseType::GetAttrVarQueueBeyond => {
-                let addr = self[temp_v!(1)].clone();
+                let addr = self[temp_v!(1)];
 
                 match self.store(self.deref(addr)) {
-                    Addr::Con(Constant::Usize(b)) => {
+                    Addr::Usize(b) => {
                         let iter = self.gather_attr_vars_created_since(b);
 
                         let var_list_addr = Addr::HeapCell(self.heap.to_list(iter));
-                        let list_addr = self[temp_v!(2)].clone();
+                        let list_addr = self[temp_v!(2)];
 
                         self.unify(var_list_addr, list_addr);
                     }
-                    Addr::Con(Constant::Integer(n)) => {
-                        if let Some(b) = n.to_usize() {
-                            let iter = self.gather_attr_vars_created_since(b);
+                    Addr::Con(h) if self.heap.integer_at(h) => {
+                        if let HeapCellValue::Integer(n) = self.heap.clone(h) {
+                            if let Some(b) = n.to_usize() {
+                                let iter = self.gather_attr_vars_created_since(b);
 
-                            let var_list_addr = Addr::HeapCell(self.heap.to_list(iter));
-                            let list_addr = self[temp_v!(2)].clone();
+                                let var_list_addr = Addr::HeapCell(self.heap.to_list(iter));
+                                let list_addr = self[temp_v!(2)].clone();
 
-                            self.unify(var_list_addr, list_addr);
+                                self.unify(var_list_addr, list_addr);
+                            } else {
+                                self.fail = true;
+                            }
                         } else {
-                            self.fail = true;
+                            unreachable!()
                         }
                     }
-                    _ => self.fail = true,
+                    _ => {
+                        self.fail = true;
+                    }
                 }
             }
             &SystemClauseType::GetContinuationChunk => {
-                let e = self.store(self.deref(self[temp_v!(1)].clone()));
+                let e = self.store(self.deref(self[temp_v!(1)]));
 
-                let e = if let Addr::Con(Constant::Usize(e)) = e {
+                let e = if let Addr::Usize(e) = e {
                     e
                 } else {
                     self.fail = true;
                     return Ok(());
                 };
 
-                let p_functor = self.store(self.deref(self[temp_v!(2)].clone()));
+                let p_functor = self.store(self.deref(self[temp_v!(2)]));
                 let p = self.heap.to_local_code_ptr(&p_functor).unwrap();
 
-                let num_cells = match code_repo.lookup_instr(self.last_call, &CodePtr::Local(p)) {
-                    Some(line) => {
-                        let perm_vars = match line.as_ref() {
-                            Line::Control(ref ctrl_instr) => ctrl_instr.perm_vars(),
-                            _ => None
-                        };
+                let num_cells =
+                    match code_repo.lookup_instr(self.last_call, &CodePtr::Local(p)) {
+                        Some(line) => {
+                            let perm_vars = match line.as_ref() {
+                                Line::Control(ref ctrl_instr) => ctrl_instr.perm_vars(),
+                                _ => None
+                            };
 
-                        perm_vars.unwrap()
-                    }
-                    _ => unreachable!()
-                };
+                            perm_vars.unwrap()
+                        }
+                        _ => unreachable!()
+                    };
 
                 let mut addrs = vec![];
 
                 for index in 1 .. num_cells + 1 {
-                    addrs.push(self.stack.index_and_frame(e)[index].clone());
+                    addrs.push(self.stack.index_and_frame(e)[index]);
                 }
 
                 let chunk = Addr::HeapCell(self.heap.h());
@@ -1823,19 +2152,19 @@ impl MachineState {
                 self.heap.push(HeapCellValue::Addr(p_functor));
                 self.heap.extend(addrs.into_iter().map(HeapCellValue::Addr));
 
-                self.unify(self[temp_v!(3)].clone(), chunk);
+                self.unify(self[temp_v!(3)], chunk);
             }
             &SystemClauseType::GetLiftedHeapFromOffsetDiff => {
-                let lh_offset = self[temp_v!(1)].clone();
+                let lh_offset = self[temp_v!(1)];
 
                 match self.store(self.deref(lh_offset)) {
-                    Addr::Con(Constant::Usize(lh_offset)) => {
+                    Addr::Usize(lh_offset) => {
                         if lh_offset >= self.lifted_heap.h() {
-                            let solutions = self[temp_v!(2)].clone();
-                            let diff = self[temp_v!(3)].clone();
+                            let solutions = self[temp_v!(2)];
+                            let diff = self[temp_v!(3)];
 
-                            self.unify(solutions, Addr::Con(Constant::EmptyList));
-                            self.unify(diff, Addr::Con(Constant::EmptyList));
+                            self.unify(solutions, Addr::EmptyList);
+                            self.unify(diff, Addr::EmptyList);
                         } else {
                             let h = self.heap.h();
                             let mut last_index = h;
@@ -1845,14 +2174,10 @@ impl MachineState {
 
                                 match value {
                                     HeapCellValue::Addr(ref addr) => {
-                                        self.heap.push(HeapCellValue::Addr(addr.clone() + h));
-                                    }
-                                    HeapCellValue::PartialString(ref pstr) => {
-                                        let new_pstr = pstr.clone();
-                                        self.heap.push(HeapCellValue::PartialString(new_pstr));
+                                        self.heap.push(HeapCellValue::Addr(*addr + h));
                                     }
                                     value => {
-                                        self.heap.push(value.clone());
+                                        self.heap.push(value.context_free_clone());
                                     }
                                 }
                             }
@@ -1860,72 +2185,90 @@ impl MachineState {
                             if last_index < self.heap.h() {
                                 let addr_opt =
                                     if let HeapCellValue::Addr(ref addr) = &self.heap[last_index] {
-                                        Some(addr.clone())
+                                        Some(*addr)
                                     } else {
                                         None
                                     };
 
                                 addr_opt.map(|addr| {
-                                    let diff = self[temp_v!(3)].clone();
+                                    let diff = self[temp_v!(3)];
                                     self.unify(diff, addr);
                                 });
                             }
 
                             self.lifted_heap.truncate(lh_offset);
 
-                            let solutions = self[temp_v!(2)].clone();
+                            let solutions = self[temp_v!(2)];
                             self.unify(Addr::HeapCell(h), solutions);
                         }
                     }
-                    _ => self.fail = true,
+                    _ => {
+                        self.fail = true;
+                    }
                 }
             }
             &SystemClauseType::GetLiftedHeapFromOffset => {
-                let lh_offset = self[temp_v!(1)].clone();
+                let lh_offset = self[temp_v!(1)];
 
                 match self.store(self.deref(lh_offset)) {
-                    Addr::Con(Constant::Usize(lh_offset)) => {
+                    Addr::Usize(lh_offset) => {
                         if lh_offset >= self.lifted_heap.h() {
-                            let solutions = self[temp_v!(2)].clone();
-                            self.unify(solutions, Addr::Con(Constant::EmptyList));
+                            let solutions = self[temp_v!(2)];
+                            self.unify(solutions, Addr::EmptyList);
                         } else {
                             let h = self.heap.h();
 
                             for addr in self.lifted_heap.iter_from(lh_offset) {
                                 match addr {
                                     HeapCellValue::Addr(ref addr) => {
-                                        self.heap.push(HeapCellValue::Addr(addr.clone() + h))
-                                    }
-                                    HeapCellValue::PartialString(ref pstr) => {
-                                        let new_pstr = pstr.clone();
-                                        self.heap.push(HeapCellValue::PartialString(new_pstr));
+                                        self.heap.push(HeapCellValue::Addr(*addr + h));
                                     }
                                     value => {
-                                        self.heap.push(value.clone());
+                                        self.heap.push(value.context_free_clone());
                                     }
                                 }
                             }
 
                             self.lifted_heap.truncate(lh_offset);
 
-                            let solutions = self[temp_v!(2)].clone();
+                            let solutions = self[temp_v!(2)];
                             self.unify(Addr::HeapCell(h), solutions);
                         }
                     }
-                    _ => self.fail = true,
+                    _ => {
+                        self.fail = true;
+                    }
                 }
             }
             &SystemClauseType::GetDoubleQuotes => {
-                let a1 = self[temp_v!(1)].clone();
+                let a1 = self[temp_v!(1)];
 
                 match self.flags.double_quotes {
-                    DoubleQuotes::Chars => self.unify(a1, Addr::Con(atom!("chars"))),
-                    DoubleQuotes::Atom => self.unify(a1, Addr::Con(atom!("atom"))),
-                    DoubleQuotes::Codes => self.unify(a1, Addr::Con(atom!("codes"))),
+                    DoubleQuotes::Chars => {
+                        let atom = self.heap.to_unifiable(
+                            HeapCellValue::Atom(clause_name!("chars"), None)
+                        );
+
+                        self.unify(a1, atom);
+                    }
+                    DoubleQuotes::Atom  => {
+                        let atom = self.heap.to_unifiable(
+                            HeapCellValue::Atom(clause_name!("atom"), None)
+                        );
+
+                        self.unify(a1, atom);
+                    }
+                    DoubleQuotes::Codes => {
+                        let atom = self.heap.to_unifiable(
+                            HeapCellValue::Atom(clause_name!("codes"), None)
+                        );
+
+                        self.unify(a1, atom);
+                    }
                 }
             }
             &SystemClauseType::GetSCCCleaner => {
-                let dest = self[temp_v!(1)].clone();
+                let dest = self[temp_v!(1)];
 
                 match cut_policy.downcast_mut::<SCCCutPolicy>().ok() {
                     Some(sgc_policy) => {
@@ -1936,7 +2279,7 @@ impl MachineState {
                                 self.block = prev_b;
 
                                 if let Some(r) = dest.as_var() {
-                                    self.bind(r, addr.clone());
+                                    self.bind(r, addr);
                                     return return_from_clause!(self.last_call, self);
                                 }
                             } else {
@@ -1949,9 +2292,11 @@ impl MachineState {
 
                 self.fail = true;
             }
-            &SystemClauseType::Halt => std::process::exit(0),
+            &SystemClauseType::Halt => {
+                std::process::exit(0);
+            }
             &SystemClauseType::InstallSCCCleaner => {
-                let addr = self[temp_v!(1)].clone();
+                let addr = self[temp_v!(1)];
                 let b = self.b;
                 let prev_block = self.block;
 
@@ -1973,65 +2318,104 @@ impl MachineState {
             }
             &SystemClauseType::InstallInferenceCounter => {
                 // A1 = B, A2 = L
-                let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
-                let a2 = self.store(self.deref(self[temp_v!(2)].clone()));
+                let a1 = self.store(self.deref(self[temp_v!(1)]));
+                let a2 = self.store(self.deref(self[temp_v!(2)]));
 
                 if call_policy.downcast_ref::<CWILCallPolicy>().is_err() {
                     CWILCallPolicy::new_in_place(call_policy);
                 }
 
-                match (a1, a2.clone()) {
-                    (Addr::Con(Constant::Usize(bp)), Addr::Con(Constant::Integer(n)))
-                  | (Addr::Con(Constant::CutPoint(bp)), Addr::Con(Constant::Integer(n))) => {
-                        match call_policy.downcast_mut::<CWILCallPolicy>().ok() {
-                            Some(call_policy) => {
-                                let count = call_policy.add_limit(n, bp);
-                                let count = Addr::Con(Constant::Integer(count.clone()));
+                match (a1, a2) {
+                    (Addr::Usize(bp), Addr::Con(h))
+                        | (Addr::CutPoint(bp), Addr::Con(h))
+                        if self.heap.integer_at(h) => {
+                            if let HeapCellValue::Integer(n) = self.heap.clone(h) {
+                                match call_policy.downcast_mut::<CWILCallPolicy>().ok() {
+                                    Some(call_policy) => {
+                                        let count = call_policy.add_limit(Integer::from(&*n), bp);
+                                        let count = self.heap.to_unifiable(
+                                            HeapCellValue::Integer(Rc::new(count.clone()))
+                                        );
 
-                                let a3 = self[temp_v!(3)].clone();
+                                        let a3 = self[temp_v!(3)];
 
-                                self.unify(a3, count);
+                                        self.unify(a3, count);
+                                    }
+                                    None => {
+                                        panic!(
+                                            "install_inference_counter: should have installed \\
+                                             CWILCallPolicy."
+                                        )
+                                    }
+                                }
+                            } else {
+                                unreachable!()
                             }
-                            None => panic!(
-                                "install_inference_counter: should have installed \\
-                                 CWILCallPolicy."
-                            ),
                         }
-                    }
                     _ => {
                         let stub = MachineError::functor_stub(
                             clause_name!("call_with_inference_limit"),
                             3,
                         );
-                        let type_error =
-                            self.error_form(MachineError::type_error(ValidType::Integer, a2), stub);
+
+                        let type_error = self.error_form(
+                            MachineError::type_error(
+                                self.heap.h(),
+                                ValidType::Integer,
+                                a2,
+                            ),
+                            stub,
+                        );
+
                         self.throw_exception(type_error)
                     }
                 };
             }
             &SystemClauseType::ModuleExists => {
-                let module = self.store(self.deref(self[temp_v!(1)].clone()));
+                let module = self.store(self.deref(self[temp_v!(1)]));
 
                 match module {
-                    Addr::Con(Constant::Atom(ref name, _)) => {
-                        self.fail = !indices.modules.contains_key(name);
+                    Addr::Con(h) => {
+                       if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
+                            self.fail = !indices.modules.contains_key(name);
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
                     }
-                    _ => unreachable!()
                 };
             }
             &SystemClauseType::ModuleOf => {
-                let module = self.store(self.deref(self[temp_v!(2)].clone()));
+                let module = self.store(self.deref(self[temp_v!(2)]));
 
                 match module {
-                    Addr::Con(Constant::Atom(name, _)) => {
-                        let module = Addr::Con(Constant::Atom(name.owning_module(), None));
-                        let target = self[temp_v!(1)].clone();
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                       if let HeapCellValue::Atom(name, _) = self.heap.clone(h) {
+                            let module = self.heap.to_unifiable(
+                                HeapCellValue::Atom(
+                                    name.owning_module(),
+                                    None
+                                ),
+                            );
 
-                        self.unify(target, module);
+                            let target = self[temp_v!(1)];
+
+                            self.unify(target, module);
+                        } else {
+                            unreachable!()
+                        }
                     }
-                    Addr::Str(s) => match self.heap[s].clone() {
+                    Addr::Str(s) => match self.heap.clone(s) {
                         HeapCellValue::NamedStr(_, name, ..) => {
-                            let module = Addr::Con(Constant::Atom(name.owning_module(), None));
+                            let module = self.heap.to_unifiable(
+                                HeapCellValue::Atom(
+                                    name.owning_module(),
+                                    None
+                                ),
+                            );
+
                             let target = self[temp_v!(1)].clone();
 
                             self.unify(target, module);
@@ -2040,28 +2424,36 @@ impl MachineState {
                             unreachable!()
                         }
                     },
-                    _ => self.fail = true,
+                    _ => {
+                        self.fail = true;
+                    }
                 };
             }
             &SystemClauseType::NoSuchPredicate => {
-                let head = self[temp_v!(1)].clone();
+                let head = self[temp_v!(1)];
 
                 self.fail = match self.store(self.deref(head)) {
-                    Addr::Str(s) => match self.heap[s].clone() {
-                        HeapCellValue::NamedStr(arity, name, op_spec) => {
+                    Addr::Str(s) => match &self.heap[s] {
+                        &HeapCellValue::NamedStr(arity, ref name, ref spec) => {
                             let module = name.owning_module();
-                            indices.predicate_exists(name, module, arity, op_spec)
+                            indices.predicate_exists(name.clone(), module, arity, spec.clone())
+                        }
+                        _ => {
+                            unreachable!()
                         }
-                        _ => unreachable!(),
                     },
-                    Addr::Con(Constant::Atom(name, spec)) => {
-                        let module = name.owning_module();
-                        let spec = fetch_atom_op_spec(name.clone(), spec, &indices.op_dir);
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                       if let &HeapCellValue::Atom(ref name, ref spec) = &self.heap[h] {
+                            let module = name.owning_module();
+                            let spec = fetch_atom_op_spec(name.clone(), spec.clone(), &indices.op_dir);
 
-                        indices.predicate_exists(name, module, 0, spec)
+                            indices.predicate_exists(name.clone(), module, 0, spec)
+                        } else {
+                            unreachable!()
+                        }
                     }
                     head => {
-                        let err = MachineError::type_error(ValidType::Callable, head);
+                        let err = MachineError::type_error(self.heap.h(), ValidType::Callable, head);
                         let stub = MachineError::functor_stub(clause_name!("clause"), 2);
 
                         return Err(self.error_form(err, stub));
@@ -2069,8 +2461,8 @@ impl MachineState {
                 };
             }
             &SystemClauseType::RedoAttrVarBinding => {
-                let var = self.store(self.deref(self[temp_v!(1)].clone()));
-                let value = self.store(self.deref(self[temp_v!(2)].clone()));
+                let var = self.store(self.deref(self[temp_v!(1)]));
+                let value = self.store(self.deref(self[temp_v!(2)]));
 
                 match var {
                     Addr::AttrVar(h) => {
@@ -2090,75 +2482,92 @@ impl MachineState {
                             self.heap[h] = HeapCellValue::Addr(value);
                         }
                     }
-                    _ => unreachable!()
+                    _ => {
+                        unreachable!()
+                    }
                 }
             }
             &SystemClauseType::ResetGlobalVarAtKey => {
-                let key = self[temp_v!(1)].clone();
+                let key = self[temp_v!(1)];
 
-                let key = match self.store(self.deref(key)) {
-                    Addr::Con(Constant::Atom(atom, _)) => atom,
-                    _ => unreachable!(),
-                };
-
-                indices.global_variables.swap_remove(&key);
+                match self.store(self.deref(key)) {
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref key, _) = &self.heap[h] {
+                            indices.global_variables.swap_remove(key);
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
+                }
             }
             &SystemClauseType::ResetGlobalVarAtOffset => {
-                let key = self[temp_v!(1)].clone();
+                let key = self[temp_v!(1)];
 
                 let key = match self.store(self.deref(key)) {
-                    Addr::Con(Constant::Atom(atom, _)) => atom,
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref key, _) = &self.heap[h] {
+                            key.clone()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
 
-                let value = self[temp_v!(2)].clone();
+                let value = self[temp_v!(2)];
                 let mut ball = Ball::new();
-                let h = self.heap.h();
 
-                ball.boundary = h;
+                ball.boundary = self.heap.h();
+
                 copy_term(
                     CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut ball.stub),
                     value,
                     AttrVarPolicy::DeepCopy,
                 );
 
-                let offset = self[temp_v!(3)].clone();
+                let offset = self[temp_v!(3)];
 
                 match self.store(self.deref(offset)) {
-                    Addr::Con(Constant::Usize(offset)) => {
-                        indices.global_variables.insert(key, (ball, Some(offset)))
+                    Addr::Usize(offset) => {
+                        indices.global_variables.insert(key, (ball, Some(offset)));
                     }
                     _ => {
-                        indices.global_variables.insert(key, (ball, None))
+                        indices.global_variables.insert(key, (ball, None));
                     }
-                };
+                }
             },
             &SystemClauseType::ResetAttrVarState => {
                 self.attr_var_init.reset();
             }
             &SystemClauseType::RemoveCallPolicyCheck => {
-                let restore_default = match call_policy.downcast_mut::<CWILCallPolicy>().ok() {
-                    Some(call_policy) => {
-                        let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
-
-                        match a1 {
-                            Addr::Con(Constant::Usize(bp)) | Addr::Con(Constant::CutPoint(bp)) => {
-                                if call_policy.is_empty() && bp == self.b {
-                                    Some(call_policy.into_inner())
-                                } else {
-                                    None
+                let restore_default =
+                    match call_policy.downcast_mut::<CWILCallPolicy>().ok() {
+                        Some(call_policy) => {
+                            let a1 = self.store(self.deref(self[temp_v!(1)]));
+
+                            match a1 {
+                                Addr::Usize(bp) | Addr::CutPoint(bp) => {
+                                    if call_policy.is_empty() && bp == self.b {
+                                        Some(call_policy.into_inner())
+                                    } else {
+                                        None
+                                    }
+                                }
+                                _ => {
+                                    panic!("remove_call_policy_check: expected Usize in A1.");
                                 }
-                            }
-                            _ => {
-                                panic!("remove_call_policy_check: expected Usize in A1.");
                             }
                         }
-                    }
-                    None => panic!(
-                        "remove_call_policy_check: requires \\
-                         CWILCallPolicy."
-                    ),
-                };
+                        None => panic!(
+                            "remove_call_policy_check: requires \\
+                             CWILCallPolicy."
+                        ),
+                    };
 
                 if let Some(new_policy) = restore_default {
                     *call_policy = new_policy;
@@ -2167,14 +2576,16 @@ impl MachineState {
             &SystemClauseType::RemoveInferenceCounter => {
                 match call_policy.downcast_mut::<CWILCallPolicy>().ok() {
                     Some(call_policy) => {
-                        let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
+                        let a1 = self.store(self.deref(self[temp_v!(1)]));
 
                         match a1 {
-                            Addr::Con(Constant::Usize(bp)) | Addr::Con(Constant::CutPoint(bp)) => {
+                            Addr::Usize(bp) | Addr::CutPoint(bp) => {
                                 let count = call_policy.remove_limit(bp);
-                                let count = Addr::Con(Constant::Integer(count.clone()));
+                                let count = self.heap.to_unifiable(
+                                    HeapCellValue::Integer(Rc::new(count.clone())),
+                                );
 
-                                let a2 = self[temp_v!(2)].clone();
+                                let a2 = self[temp_v!(2)];
 
                                 self.unify(a2, count);
                             }
@@ -2189,7 +2600,9 @@ impl MachineState {
                     ),
                 }
             }
-            &SystemClauseType::REPL(repl_code_ptr) => return self.repl_redirect(repl_code_ptr),
+            &SystemClauseType::REPL(repl_code_ptr) => {
+                return self.repl_redirect(repl_code_ptr);
+            }
             &SystemClauseType::ModuleRetractClause => {
                 let p = self.cp;
                 let trans_type = DynamicTransactionType::ModuleRetract;
@@ -2209,14 +2622,14 @@ impl MachineState {
                 let frame_len = self.stack.index_and_frame(e).prelude.univ_prelude.num_cells;
 
                 for i in 1 .. frame_len - 1 {
-                    self[RegType::Temp(i)] = self.stack.index_and_frame(e)[i].clone();
+                    self[RegType::Temp(i)] = self.stack.index_and_frame(e)[i];
                 }
 
-                if let &Addr::Con(Constant::CutPoint(b0)) = &self.stack.index_and_frame(e)[frame_len - 1] {
+                if let &Addr::CutPoint(b0) = &self.stack.index_and_frame(e)[frame_len - 1] {
                     self.b0 = b0;
                 }
 
-                if let &Addr::Con(Constant::Usize(num_of_args)) = &self.stack.index_and_frame(e)[frame_len] {
+                if let &Addr::Usize(num_of_args) = &self.stack.index_and_frame(e)[frame_len] {
                     self.num_of_args = num_of_args;
                 }
 
@@ -2246,32 +2659,50 @@ impl MachineState {
                 deref_cut(self, r)
             }
             &SystemClauseType::SetInput => {
-                let addr = self.store(self.deref(self[temp_v!(1)].clone()));
+                let addr = self.store(self.deref(self[temp_v!(1)]));
                 let stream = self.get_stream_or_alias(addr, indices, "set_input")?;
 
                 if stream.is_output_stream() {
-                    let stub = MachineError::functor_stub(clause_name!("set_input"), 1);
+                    let stub = MachineError::functor_stub(
+                        clause_name!("set_input"),
+                        1,
+                    );
+
+                    let user_alias = self.heap.to_unifiable(
+                        HeapCellValue::Atom(clause_name!("user"), None),
+                    );
+
                     let err = MachineError::permission_error(
-                        PermissionError::InputStream,
+                        self.heap.h(),
+                        Permission::InputStream,
                         "stream",
-                        Addr::Stream(stream),
+                        user_alias,
                     );
 
                     return Err(self.error_form(err, stub));
                 }
-                
+
                 *current_input_stream = stream;
             }
             &SystemClauseType::SetOutput => {
-                let addr = self.store(self.deref(self[temp_v!(1)].clone()));
+                let addr = self.store(self.deref(self[temp_v!(1)]));
                 let stream = self.get_stream_or_alias(addr, indices, "set_output")?;
 
                 if stream.is_input_stream() {
-                    let stub = MachineError::functor_stub(clause_name!("set_input"), 1);
+                    let stub = MachineError::functor_stub(
+                        clause_name!("set_input"),
+                        1,
+                    );
+
+                    let user_alias = self.heap.to_unifiable(
+                        HeapCellValue::Atom(clause_name!("user"), None),
+                    );
+
                     let err = MachineError::permission_error(
-                        PermissionError::OutputStream,
+                        self.heap.h(),
+                        Permission::OutputStream,
                         "stream",
-                        Addr::Stream(stream),
+                        user_alias,
                     );
 
                     return Err(self.error_form(err, stub));
@@ -2279,55 +2710,80 @@ impl MachineState {
 
                 *current_output_stream = stream;
             }
-            &SystemClauseType::SetDoubleQuotes => match self[temp_v!(1)].clone() {
-                Addr::Con(Constant::Atom(ref atom, _)) if atom.as_str() == "chars" => {
-                    self.flags.double_quotes = DoubleQuotes::Chars
-                }
-                Addr::Con(Constant::Atom(ref atom, _)) if atom.as_str() == "atom" => {
-                    self.flags.double_quotes = DoubleQuotes::Atom
-                }
-                Addr::Con(Constant::Atom(ref atom, _)) if atom.as_str() == "codes" => {
-                    self.flags.double_quotes = DoubleQuotes::Codes
+            &SystemClauseType::SetDoubleQuotes => {
+                match self[temp_v!(1)] {
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
+                            self.flags.double_quotes =
+                                match atom.as_str() {
+                                    "atom"  => DoubleQuotes::Atom,
+                                    "chars" => DoubleQuotes::Chars,
+                                    "codes" => DoubleQuotes::Codes,
+                                    _ => {
+                                        self.fail = true;
+                                        return Ok(());
+                                    }
+                                };
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        self.fail = true;
+                    }
                 }
-                _ => self.fail = true,
-            },
+            }
             &SystemClauseType::InferenceLevel => {
-                let a1 = self[temp_v!(1)].clone();
-                let a2 = self.store(self.deref(self[temp_v!(2)].clone()));
+                let a1 = self[temp_v!(1)];
+                let a2 = self.store(self.deref(self[temp_v!(2)]));
 
                 match a2 {
-                    Addr::Con(Constant::CutPoint(bp)) | Addr::Con(Constant::Usize(bp)) => {
+                    Addr::CutPoint(bp) | Addr::Usize(bp) => {
                         let prev_b = self.stack.index_or_frame(self.b).prelude.b;
 
                         if prev_b <= bp {
-                            let a2 = Addr::Con(atom!("!"));
+                            let a2 = self.heap.to_unifiable(
+                                HeapCellValue::Atom(clause_name!("!"), None)
+                            );
+
                             self.unify(a1, a2);
                         } else {
-                            let a2 = Addr::Con(atom!("true"));
+                            let a2 = self.heap.to_unifiable(
+                                HeapCellValue::Atom(clause_name!("true"), None)
+                            );
+
                             self.unify(a1, a2);
                         }
                     }
-                    _ => self.fail = true,
-                };
+                    _ => {
+                        self.fail = true;
+                    }
+                }
             }
             &SystemClauseType::CleanUpBlock => {
-                let nb = self.store(self.deref(self[temp_v!(1)].clone()));
+                let nb = self.store(self.deref(self[temp_v!(1)]));
 
                 match nb {
-                    Addr::Con(Constant::Usize(nb)) => {
+                    Addr::Usize(nb) => {
                         let b = self.b;
 
                         if nb > 0 && self.stack.index_or_frame(b).prelude.b == nb {
                             self.b = self.stack.index_or_frame(nb).prelude.b;
                         }
                     }
-                    _ => self.fail = true,
+                    _ => {
+                        self.fail = true;
+                    }
                 };
             }
-            &SystemClauseType::EraseBall => self.ball.reset(),
-            &SystemClauseType::Fail => self.fail = true,
+            &SystemClauseType::EraseBall => {
+                self.ball.reset();
+            }
+            &SystemClauseType::Fail => {
+                self.fail = true;
+            }
             &SystemClauseType::GetBall => {
-                let addr = self.store(self.deref(self[temp_v!(1)].clone()));
+                let addr = self.store(self.deref(self[temp_v!(1)]));
                 let h = self.heap.h();
 
                 if self.ball.stub.h() > 0 {
@@ -2347,46 +2803,65 @@ impl MachineState {
             }
             &SystemClauseType::GetCurrentBlock => {
                 let c = Constant::Usize(self.block);
-                let addr = self[temp_v!(1)].clone();
+                let addr = self[temp_v!(1)];
 
-                self.write_constant_to_var(addr, c);
+                self.write_constant_to_var(addr, &c);
             }
             &SystemClauseType::GetBValue => {
-                let a1 = self[temp_v!(1)].clone();
-                let a2 = Addr::Con(Constant::Usize(self.b));
+                let a1 = self[temp_v!(1)];
+                let a2 = Addr::Usize(self.b);
 
                 self.unify(a1, a2);
             }
             &SystemClauseType::GetClause => {
-                let head = self[temp_v!(1)].clone();
+                let head = self[temp_v!(1)];
 
                 let subsection = match self.store(self.deref(head)) {
-                    Addr::Str(s) => match self.heap[s].clone() {
-                        HeapCellValue::NamedStr(arity, name, ..) => {
-                            indices.get_clause_subsection(name.owning_module(), name, arity)
+                    Addr::Str(s) => match &self.heap[s] {
+                        &HeapCellValue::NamedStr(arity, ref name, ..) => {
+                            indices.get_clause_subsection(
+                                name.owning_module(),
+                                name.clone(),
+                                arity,
+                            )
+                        }
+                        _ => {
+                            unreachable!()
                         }
-                        _ => unreachable!(),
                     },
-                    Addr::Con(Constant::Atom(name, _)) => {
-                        indices.get_clause_subsection(name.owning_module(), name, 0)
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                       if let &HeapCellValue::Atom(ref name, _) = &self.heap[h] {
+                            indices.get_clause_subsection(
+                                name.owning_module(),
+                                name.clone(),
+                                0,
+                            )
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
                     }
-                    _ => unreachable!(),
                 };
 
                 match subsection {
                     Some(dynamic_predicate_info) => {
                         self.execute_at_index(
                             2,
-                            dir_entry!(dynamic_predicate_info.clauses_subsection_p)
+                            dir_entry!(dynamic_predicate_info.clauses_subsection_p),
                         );
+
                         return Ok(());
                     }
-                    _ => unreachable!(),
+                    _ => {
+                        unreachable!()
+                    }
                 }
             }
             &SystemClauseType::GetCutPoint => {
-                let a1 = self[temp_v!(1)].clone();
-                let a2 = Addr::Con(Constant::CutPoint(self.b0));
+                let a1 = self[temp_v!(1)];
+                let a2 = Addr::CutPoint(self.b0);
 
                 self.unify(a1, a2);
             }
@@ -2410,34 +2885,41 @@ impl MachineState {
                     ContinueResult::PrintWithMaxDepth => 'p',
                 };
 
-                let target = self[temp_v!(1)].clone();
-                self.unify(Addr::Con(Constant::Char(c)), target);
+                let target = self[temp_v!(1)];                
+                self.unify(Addr::Char(c), target);
             }
             &SystemClauseType::NextEP => {
-                let first_arg = self.store(self.deref(self[temp_v!(1)].clone()));
+                let first_arg = self.store(self.deref(self[temp_v!(1)]));
 
                 match first_arg {
-                    Addr::Con(Constant::Atom(ref name, _))
-                        if name.as_str() == "first" => {
-                            if self.e == 0 {
-                                self.fail = true;
-                                return Ok(());
-                            }
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref name, _) = self.heap.clone(h) {
+                            if name.as_str() == "first" {
+                                if self.e == 0 {
+                                    self.fail = true;
+                                    return Ok(());
+                                }
 
-                            let cp = (self.stack.index_and_frame(self.e).prelude.cp - 1).unwrap();
+                                let cp = (self.stack.index_and_frame(self.e).prelude.cp - 1).unwrap();
 
-                            let e = self.stack.index_and_frame(self.e).prelude.e;
-                            let e = Addr::Con(Constant::Usize(e));
+                                let e = self.stack.index_and_frame(self.e).prelude.e;
+                                let e = Addr::Usize(e);
 
-                            let p = cp.as_functor(&mut self.heap);
+                                let p = cp.as_functor(&mut self.heap);
 
-                            self.unify(self[temp_v!(2)].clone(), e);
+                                self.unify(self[temp_v!(2)], e);
 
-                            if !self.fail {
-                                self.unify(self[temp_v!(3)].clone(), p);
+                                if !self.fail {
+                                    self.unify(self[temp_v!(3)], p);
+                                }
+                            } else {
+                                unreachable!()
                             }
-                        },
-                    Addr::Con(Constant::Usize(e)) => {
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    Addr::Usize(e) => {
                         if e == 0 {
                             self.fail = true;
                             return Ok(());
@@ -2450,22 +2932,26 @@ impl MachineState {
                         let p = cp.as_functor(&mut self.heap);
                         let e = self.stack.index_and_frame(e).prelude.e;
 
-                        let e = Addr::Con(Constant::Usize(e));
+                        let e = Addr::Usize(e);
 
-                        self.unify(self[temp_v!(2)].clone(), e);
+                        self.unify(self[temp_v!(2)], e);
 
                         if !self.fail {
-                            self.unify(self[temp_v!(3)].clone(), p);
+                            self.unify(self[temp_v!(3)], p);
                         }
                     }
-                    _ => unreachable!()
+                    _ => {
+                        unreachable!()
+                    }
                 }
             }
             &SystemClauseType::PointsToContinuationResetMarker => {
-                let addr = self.store(self.deref(self[temp_v!(1)].clone()));
+                let addr = self.store(self.deref(self[temp_v!(1)]));
 
                 let p = match self.heap.to_local_code_ptr(&addr) {
-                    Some(p) => p + 1,
+                    Some(p) => {
+                        p + 1
+                    }
                     None => {
                         self.fail = true;
                         return Ok(());
@@ -2480,10 +2966,10 @@ impl MachineState {
                 return Ok(());
             }
             &SystemClauseType::QuotedToken => {
-                let addr = self.store(self.deref(self[temp_v!(1)].clone()));
+                let addr = self.store(self.deref(self[temp_v!(1)]));
 
                 match addr {
-                    Addr::Con(Constant::CharCode(c)) => {
+                    Addr::CharCode(c) => {
                         self.fail = match std::char::from_u32(c) {
                             Some(c) => {
                                 non_quoted_token(once(c))
@@ -2493,11 +2979,13 @@ impl MachineState {
                             }
                         };
                     }
-                    Addr::Con(Constant::Char(c)) => {
+                    Addr::Char(c) => {
                         self.fail = non_quoted_token(once(c));
                     }
-                    Addr::Con(Constant::Atom(atom, _)) => {
-                        self.fail = non_quoted_token(atom.as_str().chars());
+                    Addr::Con(h) => {
+                       if let HeapCellValue::Atom(atom, _) = &self.heap[h] {
+                            self.fail = non_quoted_token(atom.as_str().chars());
+                        }
                     }
                     _ => {
                         self.fail = true;
@@ -2516,33 +3004,46 @@ impl MachineState {
                 self.read_term(current_input_stream, indices)?;
             }
             &SystemClauseType::ResetBlock => {
-                let addr = self.deref(self[temp_v!(1)].clone());
+                let addr = self.deref(self[temp_v!(1)]);
                 self.reset_block(addr);
             }
             &SystemClauseType::ResetContinuationMarker => {
-                self[temp_v!(3)] = Addr::Con(Constant::Atom(clause_name!("none"), None));
-
                 let h = self.heap.h();
-                self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
 
+                self[temp_v!(3)] = self.heap.to_unifiable(
+                    HeapCellValue::Atom(clause_name!("none"), None)
+                );
+
+                self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
                 self[temp_v!(4)] = Addr::HeapCell(h);
             }
-            &SystemClauseType::SetBall =>
-                self.set_ball(),
+            &SystemClauseType::SetBall => {
+                self.set_ball();
+            }
             &SystemClauseType::SetSeed => {
-                let seed = self.store(self.deref(self[temp_v!(1)].clone()));
+                let seed = self.store(self.deref(self[temp_v!(1)]));
 
                 let seed = match seed {
-                    Addr::Con(Constant::Integer(n)) =>
-                        n,
-                    Addr::Con(Constant::CharCode(c)) =>
-                        Integer::from(c),
-                    Addr::Con(Constant::Rational(r)) => {
-                        if r.denom() == &1 {
-                            r.numer().clone()
+                    Addr::CharCode(c) => {
+                        Integer::from(c)
+                    }
+                    Addr::Con(h) if self.heap.integer_at(h) => {
+                        if let HeapCellValue::Integer(ref n) = &self.heap[h] {
+                            Integer::from(&**n)
                         } else {
-                            self.fail = true;
-                            return Ok(());
+                            unreachable!()
+                        }
+                    }
+                    Addr::Con(h) if self.heap.rational_at(h) => {
+                        if let HeapCellValue::Rational(r) = &self.heap[h] {
+                            if r.denom() == &1 {
+                                r.numer().clone()
+                            } else {
+                                self.fail = true;
+                                return Ok(());
+                            }
+                        } else {
+                            unreachable!()
                         }
                     }
                     _ => {
@@ -2559,17 +3060,26 @@ impl MachineState {
                     return Err(err);
                 },
             &SystemClauseType::StoreGlobalVar => {
-                let key = self[temp_v!(1)].clone();
+                let key = self[temp_v!(1)];
 
                 let key = match self.store(self.deref(key)) {
-                    Addr::Con(Constant::Atom(atom, _)) => atom,
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
+                            atom.clone()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
 
-                let value = self[temp_v!(2)].clone();
+                let value = self[temp_v!(2)];
                 let mut ball = Ball::new();
 
                 ball.boundary = self.heap.h();
+                
                 copy_term(
                     CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut ball.stub),
                     value,
@@ -2579,18 +3089,27 @@ impl MachineState {
                 indices.global_variables.insert(key, (ball, None));
             }
             &SystemClauseType::StoreGlobalVarWithOffset => {
-                let key = self[temp_v!(1)].clone();
+                let key = self[temp_v!(1)];
 
                 let key = match self.store(self.deref(key)) {
-                    Addr::Con(Constant::Atom(atom, _)) => atom,
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
+                            atom.clone()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
 
-                let value = self[temp_v!(2)].clone();
+                let value = self[temp_v!(2)];
                 let mut ball = Ball::new();
                 let h = self.heap.h();
 
                 ball.boundary = h;
+
                 copy_term(
                     CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut ball.stub),
                     value.clone(),
@@ -2599,40 +3118,39 @@ impl MachineState {
 
                 let stub = ball.copy_and_align(h);
                 self.heap.extend(stub.into_iter());
+
                 indices.global_variables.insert(key, (ball, Some(h)));
 
                 self.unify(value, Addr::HeapCell(h));
             }
-            &SystemClauseType::Succeed => {}
+            &SystemClauseType::Succeed => {
+            }
             &SystemClauseType::TermVariables => {
-                let a1 = self[temp_v!(1)].clone();
+                let a1 = self[temp_v!(1)];
                 let mut seen_vars = IndexSet::new();
 
-                for item in self.acyclic_pre_order_iter(a1) {
-                    match item {
-                        HeapCellValue::Addr(addr) => {
-                            if addr.is_ref() {
-                                seen_vars.insert(addr);
-                            }
-                        }
-                        _ => {}
+                for addr in self.acyclic_pre_order_iter(a1) {
+                    if addr.is_ref() {
+                        seen_vars.insert(addr);
                     }
                 }
 
                 let outcome = Addr::HeapCell(self.heap.to_list(seen_vars.into_iter()));
 
-                let a2 = self[temp_v!(2)].clone();
+                let a2 = self[temp_v!(2)];
                 self.unify(a2, outcome);
             }
             &SystemClauseType::TruncateLiftedHeapTo => {
-                match self.store(self.deref(self[temp_v!(1)].clone())) {
-                    Addr::Con(Constant::Usize(lh_offset)) => self.lifted_heap.truncate(lh_offset),
-                    _ => self.fail = true,
+                match self.store(self.deref(self[temp_v!(1)])) {
+                    Addr::Usize(lh_offset) =>
+                        self.lifted_heap.truncate(lh_offset),
+                    _ =>
+                        self.fail = true,
                 }
             }
             &SystemClauseType::UnifyWithOccursCheck => {
-                let a1 = self[temp_v!(1)].clone();
-                let a2 = self[temp_v!(2)].clone();
+                let a1 = self[temp_v!(1)];
+                let a2 = self[temp_v!(2)];
 
                 self.unify_with_occurs_check(a1, a2);
             }
@@ -2652,20 +3170,40 @@ impl MachineState {
                     e = self.stack.index_and_frame(e).prelude.e;
                 }
             }
-            &SystemClauseType::UnwindStack => self.unwind_stack(),
-            &SystemClauseType::Variant => self.fail = self.structural_eq_test(),
+            &SystemClauseType::UnwindStack => {
+                self.unwind_stack();
+            }
+            &SystemClauseType::Variant => {
+                self.fail = self.structural_eq_test();
+            }
             &SystemClauseType::WAMInstructions => {
-                let name = self[temp_v!(1)].clone();
-                let arity = self[temp_v!(2)].clone();
+                let name  = self[temp_v!(1)];
+                let arity = self[temp_v!(2)];
 
                 let name = match self.store(self.deref(name)) {
-                    Addr::Con(Constant::Atom(name, _)) => name,
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.atom_at(h) => {
+                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
+                            atom.clone()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
 
                 let arity = match self.store(self.deref(arity)) {
-                    Addr::Con(Constant::Integer(n)) => n,
-                    _ => unreachable!(),
+                    Addr::Con(h) if self.heap.integer_at(h) => {
+                        if let HeapCellValue::Integer(ref n) = &self.heap[h] {
+                            n.clone()
+                        } else {
+                            unreachable!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
                 };
 
                 let first_idx = match indices
@@ -2684,6 +3222,7 @@ impl MachineState {
                                 h,
                                 ExistenceError::Procedure(name, arity),
                             );
+
                             let err = self.error_form(err, stub);
 
                             self.throw_exception(err);
@@ -2699,6 +3238,7 @@ impl MachineState {
                             h,
                             ExistenceError::Procedure(name, arity),
                         );
+                        
                         let err = self.error_form(err, stub);
 
                         self.throw_exception(err);
@@ -2727,39 +3267,55 @@ impl MachineState {
                 self.unify(listing, listing_var);
             }
             &SystemClauseType::WriteTerm => {
-                let addr = self[temp_v!(1)].clone();
+                let addr = self[temp_v!(1)];
 
-                let ignore_ops = self.store(self.deref(self[temp_v!(2)].clone()));
-                let numbervars = self.store(self.deref(self[temp_v!(3)].clone()));
-                let quoted = self.store(self.deref(self[temp_v!(4)].clone()));
-                let max_depth = self.store(self.deref(self[temp_v!(6)].clone()));
+                let ignore_ops = self.store(self.deref(self[temp_v!(2)]));
+                let numbervars = self.store(self.deref(self[temp_v!(3)]));
+                let quoted = self.store(self.deref(self[temp_v!(4)]));
+                let max_depth = self.store(self.deref(self[temp_v!(6)]));
 
                 let mut printer = HCPrinter::new(&self, &indices.op_dir, PrinterOutputter::new());
 
-                if let &Addr::Con(Constant::Atom(ref name, ..)) = &ignore_ops {
-                    printer.ignore_ops = name.as_str() == "true";
+                if let &Addr::Con(h) = &ignore_ops {
+                   if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
+                        printer.ignore_ops = name.as_str() == "true";
+                    } else {
+                        unreachable!()
+                    }
                 }
 
-                if let &Addr::Con(Constant::Atom(ref name, ..)) = &numbervars {
-                    printer.numbervars = name.as_str() == "true";
+                if let &Addr::Con(h) = &numbervars {
+                   if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
+                        printer.numbervars = name.as_str() == "true";
+                    } else {
+                        unreachable!()
+                    }
                 }
 
-                if let &Addr::Con(Constant::Atom(ref name, ..)) = &quoted {
-                    printer.quoted = name.as_str() == "true";
+                if let &Addr::Con(h) = &quoted {
+                   if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
+                        printer.quoted = name.as_str() == "true";
+                    } else {
+                        unreachable!()
+                    }
                 }
 
-                if let &Addr::Con(Constant::Integer(ref n)) = &max_depth {
-                    if let Some(n) = n.to_usize() {
-                        printer.max_depth = n;
+                if let &Addr::Con(h) = &max_depth {
+                    if let HeapCellValue::Integer(ref n) = &self.heap[h] {
+                        if let Some(n) = n.to_usize() {
+                            printer.max_depth = n;
+                        } else {
+                            self.fail = true;
+                            return Ok(());
+                        }
                     } else {
-                        self.fail = true;
-                        return Ok(());
+                        unreachable!()
                     }
                 }
 
                 let stub = MachineError::functor_stub(clause_name!("write_term"), 2);
 
-                match self.try_from_list(temp_v!(5), stub.clone()) {
+                match self.try_from_list(temp_v!(5), stub) {
                     Ok(addrs) => {
                         let mut var_names: IndexMap<Addr, String> = IndexMap::new();
 
@@ -2773,8 +3329,14 @@ impl MachineState {
                                         let var = self.heap[s + 2].as_addr(s + 2);
 
                                         let atom = match self.store(self.deref(atom)) {
-                                            Addr::Con(Constant::Atom(atom, _)) => atom.to_string(),
-                                            Addr::Con(Constant::Char(c)) => c.to_string(),
+                                            Addr::Con(h) => {
+                                                if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
+                                                    atom.to_string()
+                                                } else {
+                                                    unreachable!()
+                                                }
+                                            }
+                                            Addr::Char(c) => c.to_string(),
                                             _ => unreachable!(),
                                         };
 
index 7968aa34954a9c64541d9e8d2155cbdd008aea5a..a024d60647d4597563b5563df468f42587884dfa 100644 (file)
@@ -4,54 +4,180 @@ macro_rules! interm {
     };
 }
 
-macro_rules! heap_str {
-    ($s:expr) => {
-        HeapCellValue::Addr(Addr::Str($s))
-    };
-}
-
-macro_rules! heap_integer {
-    ($i:expr) => {
-        HeapCellValue::Addr(Addr::Con(Constant::Integer($i)))
-    };
-}
-
-macro_rules! heap_cell {
-    ($i:expr) => {
-        HeapCellValue::Addr(Addr::HeapCell($i))
-    };
+/* A simple macro to count the arguments in a variadic list
+ * of token trees.
+ */
+macro_rules! count_tt {
+    () => { 0 };
+    ($odd:tt $($a:tt $b:tt)*) => { (count_tt!($($a)*) << 1) | 1 };
+    ($($a:tt $even:tt)*) => { count_tt!($($a)*) << 1 };
 }
 
-macro_rules! heap_con {
-    ($i:expr) => {
-        HeapCellValue::Addr(Addr::Con($i))
-    };
-}
+macro_rules! functor {
+    ($name:expr, $fixity:expr, [$($dt:ident($($value:expr),*)),+], [$($aux:ident),*]) => ({
+        {
+            #[allow(unused_variables, unused_mut)]
+            let mut addendum = Heap::new();
+            let arity = count_tt!($($dt) +);
+            let aux_lens = [$($aux.len()),*];
+
+            let mut result =
+                vec![ HeapCellValue::NamedStr(arity, clause_name!($name), Some($fixity)),
+                      $(functor_term!( $dt($($value),*), arity, aux_lens, addendum ),)+ ];
+
+            $(
+                result.extend($aux.into_iter());
+            )*
+
+            result.extend(addendum.into_iter());
+            result
+        }
+    });
+    ($name:expr, $fixity:expr, [$($dt:ident($($value:expr),*)),+]) => ({
+        {
+            #[allow(unused_variables, unused_mut)]
+            let mut addendum = Heap::new();
+            let arity = count_tt!($($dt) +);
+
+            let mut result =
+                vec![ HeapCellValue::NamedStr(arity, clause_name!($name), Some($fixity)),
+                      $(functor_term!( $dt($($value),*), arity, [], addendum ),)+ ];
+
+            result.extend(addendum.into_iter());
+            result
+        }
+    });
+    ($name:expr, [$($dt:ident($($value:expr),*)),+], [$($aux:ident),*]) => ({
+        {
+            #[allow(unused_variables, unused_mut)]
+            let mut addendum = Heap::new();
+            let arity = count_tt!($($dt) +);
+            let aux_lens = [$($aux.len()),*];
+
+            let mut result =
+                vec![ HeapCellValue::NamedStr(arity, clause_name!($name), None),
+                      $(functor_term!( $dt($($value),*), arity, aux_lens, addendum ),)+ ];
+
+            $(
+                result.extend($aux.into_iter());
+            )*
+
+            result.extend(addendum.into_iter());
+            result
+        }
+    });
+    ($name:expr, [$($dt:ident($($value:expr),*)),+]) => ({
+        {
+            let arity = count_tt!($($dt) +);
 
-macro_rules! heap_atom {
-    ($name:expr) => {
-        HeapCellValue::Addr(Addr::Con(atom!($name)))
-    };
-    ($name:expr, $tbl:expr) => {
-        HeapCellValue::Addr(Addr::Con(atom!($name, $tbl)))
-    };
+            vec![ HeapCellValue::NamedStr(arity, clause_name!($name), None),
+                  $(functor_term!( $dt($($value),*), arity, [], addendum ),)+ ]
+        }
+    });
+    ($name:expr, $fixity:expr) => (
+        vec![ HeapCellValue::Atom(clause_name!($name), Some($fixity)) ]
+    );
+    (clause_name($name:expr)) => (
+        vec![ HeapCellValue::Atom($name, None) ]
+    );
+    ($name:expr) => (
+        vec![ HeapCellValue::Atom(clause_name!($name), None) ]
+    );
 }
 
-macro_rules! functor {
-    ($name:expr) => (
-        vec![ heap_atom!($name) ]
+macro_rules! functor_term {
+    (aux(0), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
+        HeapCellValue::Addr(Addr::HeapCell($arity + 1))
+    });
+    (aux($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
+        let len: usize = $aux_lens[0 .. $e].iter().sum();
+        HeapCellValue::Addr(Addr::HeapCell($arity + 1 + len))
+    });
+    (aux($h:expr, 0), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
+        HeapCellValue::Addr(Addr::HeapCell($arity + $h + 1))
+    });
+    (aux($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
+        let len: usize = $aux_lens[0 .. $e].iter().sum();
+        HeapCellValue::Addr(Addr::HeapCell($arity + $h + 1 + len))
+    });
+    (addr($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
+        HeapCellValue::Addr($e)
+    );
+    (constant($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
+        from_constant!($e, $h, $arity, $aux_lens, $addendum)
+    );    
+    (constant($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
+        from_constant!($e, 0, $arity, $aux_lens, $addendum)
+    );
+    (number($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
+        $e.into()
+    );
+    /*
+    (string($s:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => ({
+        let len: usize = $aux_lens.iter().sum();
+        let h = len + $arity + 1 + $addendum.h();
+
+        $addendum.allocate_pstr(&$s);
+
+        HeapCell::PStrLocation(h, 0)
+    });
+    */
+    (integer($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => (
+        HeapCellValue::Integer(Rc::new(Integer::from($e)))
     );
-    ($name:expr, $len:expr) => (
-        vec![ HeapCellValue::NamedStr($len, clause_name!($name), None) ]
+    (clause_name($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => (
+        HeapCellValue::Atom($e, None)
     );
-    ($name:expr, $len:expr, [$($args:expr),*]) => (
-        vec![ HeapCellValue::NamedStr($len, clause_name!($name), None), $($args),* ]
+    (atom($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => (
+        HeapCellValue::Atom(clause_name!($e), None)
     );
-    ($name:expr, $len:expr, [$($args:expr),*], $fix: expr) => (
-        vec![ HeapCellValue::NamedStr($len, clause_name!($name), Some($fix)), $($args),* ]
+    ($e:expr, $arity:expr, $aux_lens:expr, $addendum:ident) => (
+        $e
     );
 }
 
+macro_rules! from_constant {
+    ($e:expr, $over_h:expr, $arity:expr, $aux_lens:expr, $addendum:ident) => ({
+        match $e {
+            &Constant::Atom(ref name, ref op) => {
+                HeapCellValue::Atom(name.clone(), op.clone())
+            }
+            &Constant::Char(c) => {
+                HeapCellValue::Addr(Addr::Char(c))
+            }
+            &Constant::CharCode(c) => {
+                HeapCellValue::Addr(Addr::CharCode(c))
+            }
+            &Constant::CutPoint(cp) => {
+                HeapCellValue::Addr(Addr::CutPoint(cp))
+            }
+            &Constant::Integer(ref n) => {
+                HeapCellValue::Integer(n.clone())
+            }
+            &Constant::Rational(ref r) => {
+                HeapCellValue::Rational(r.clone())
+            }
+            &Constant::Float(f) => {
+                HeapCellValue::Addr(Addr::Float(f))
+            }
+            &Constant::String(ref s) => {
+                let len: usize = $aux_lens.iter().sum();
+                let h = len + $arity + 1 + $addendum.h() + $over_h;
+
+                $addendum.put_constant(Constant::String(s.clone()));
+                
+                HeapCellValue::Addr(Addr::PStrLocation(h, 0))
+            }
+            &Constant::Usize(u) => {
+                HeapCellValue::Addr(Addr::Usize(u))
+            }
+            &Constant::EmptyList => {
+                HeapCellValue::Addr(Addr::EmptyList)
+            }
+        }
+    })
+}
+
 macro_rules! is_atom {
     ($r:expr) => {
         call_clause!(ClauseType::Inlined(InlinedClauseType::IsAtom($r)), 1, 0)
index 32d45224bbbece2e6e2763256943cef32955a7a0..33bea7cd0ee88177fdc07accef36106e7350b17f 100644 (file)
@@ -4,6 +4,7 @@ use prolog_parser::tabled_rc::TabledData;
 
 use crate::prolog::forms::*;
 use crate::prolog::iterators::*;
+use crate::prolog::machine::heap::Heap;
 use crate::prolog::machine::machine_indices::*;
 use crate::prolog::machine::machine_state::MachineState;
 use crate::prolog::machine::streams::Stream;
@@ -13,12 +14,20 @@ use std::collections::VecDeque;
 type SubtermDeque = VecDeque<(usize, usize)>;
 
 impl<'a> TermRef<'a> {
-    fn as_addr(&self, h: usize) -> Addr {
+    fn as_addr(&self, heap: &mut Heap, h: usize) -> Addr {
         match self {
-            &TermRef::AnonVar(_) | &TermRef::Var(..) => Addr::HeapCell(h),
-            &TermRef::Cons(..) => Addr::HeapCell(h),
-            &TermRef::Constant(_, _, c) => Addr::Con(c.clone()),
-            &TermRef::Clause(..) => Addr::Str(h),
+            &TermRef::AnonVar(_) | &TermRef::Var(..) => {
+                Addr::HeapCell(h)
+            }
+            &TermRef::Cons(..) => {
+                Addr::HeapCell(h)
+            }
+            &TermRef::Constant(_, _, c) => {
+                heap.put_constant(c.clone())
+            }
+            &TermRef::Clause(..) => {
+                Addr::Str(h)
+            }
         }
     }
 }
@@ -131,7 +140,7 @@ fn modify_head_of_queue(
     h: usize,
 ) {
     if let Some((arity, site_h)) = queue.pop_front() {
-        machine_st.heap[site_h] = HeapCellValue::Addr(term.as_addr(h));
+        machine_st.heap[site_h] = HeapCellValue::Addr(term.as_addr(&mut machine_st.heap, h));
 
         if arity > 1 {
             queue.push_front((arity - 1, site_h + 1));
@@ -180,10 +189,12 @@ pub(crate) fn write_term_to_heap(term: &Term, machine_st: &mut MachineState) ->
                 }
             }
             &TermRef::AnonVar(Level::Root) | &TermRef::Constant(Level::Root, ..) => {
-                machine_st.heap.push(HeapCellValue::Addr(term.as_addr(h)))
+                let value = HeapCellValue::Addr(term.as_addr(&mut machine_st.heap, h));
+                machine_st.heap.push(value);
             }
             &TermRef::Var(Level::Root, ..) => {
-                machine_st.heap.push(HeapCellValue::Addr(term.as_addr(h)))
+                let value = HeapCellValue::Addr(term.as_addr(&mut machine_st.heap, h));
+                machine_st.heap.push(value);
             }
             &TermRef::AnonVar(_) => {
                 if let Some((arity, site_h)) = queue.pop_front() {
index 3abadd7cf278b3d352fff080ffdc6e708ac2fef8..659a3ae63062fae2a30e8de70e04cd99ae2031f2 100644 (file)
@@ -43,6 +43,7 @@
     ).
 
 '$submit_query_and_print_results'(Term0, VarList) :-
+    write('oh brother'), nl,
     (  expand_goals(Term0, Term) -> true
     ;  Term0 = Term
     ),
index dd91a4b01f3a4f0423184e7d2a916ab6f4204bd4..e3e018c79e5b2c7474cbbe8489a92c153931b16d 100644 (file)
@@ -143,6 +143,10 @@ impl fmt::Display for HeapCellValue {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             &HeapCellValue::Addr(ref addr) => write!(f, "{}", addr),
+            &HeapCellValue::Atom(ref atom, _) => write!(f, "{}", atom.as_str()),
+            &HeapCellValue::DBRef(ref db_ref) => write!(f, "{}", db_ref),
+            &HeapCellValue::Integer(ref n) => write!(f, "{}", n),
+            &HeapCellValue::Rational(ref n) => write!(f, "{}", n),
             &HeapCellValue::NamedStr(arity, ref name, Some(ref cell)) => write!(
                 f,
                 "{}/{} (op, priority: {}, spec: {})",
@@ -155,7 +159,10 @@ impl fmt::Display for HeapCellValue {
                 write!(f, "{}/{}", name.as_str(), arity)
             }
             &HeapCellValue::PartialString(ref pstr) => {
-                write!(f, "pstr ( buf: {} )", pstr.block_as_str())
+                write!(f, "pstr ( buf: 0x{:x} )", (pstr as *const _) as usize)
+            }
+            &HeapCellValue::Stream(ref stream) => {
+                write!(f, "$stream({})", stream.as_ptr() as usize)
             }
         }
     }
@@ -175,15 +182,20 @@ impl fmt::Display for DBRef {
 impl fmt::Display for Addr {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
+            &Addr::Char(c) => write!(f, "Addr::Char({})", c),
+            &Addr::CharCode(c) => write!(f, "Addr::CharCode({})", c),
+            &Addr::EmptyList => write!(f, "Addr::EmptyList"),
+            &Addr::Float(fl) => write!(f, "Addr::Float({})", fl),
+            &Addr::CutPoint(cp) => write!(f, "Addr::CutPoint({})", cp),
             &Addr::Con(ref c) => write!(f, "Addr::Con({})", c),
-            &Addr::DBRef(ref db_ref) => write!(f, "Addr::DBRef({})", db_ref),
             &Addr::Lis(l) => write!(f, "Addr::Lis({})", l),
             &Addr::AttrVar(h) => write!(f, "Addr::AttrVar({})", h),
             &Addr::HeapCell(h) => write!(f, "Addr::HeapCell({})", h),
             &Addr::StackCell(fr, sc) => write!(f, "Addr::StackCell({}, {})", fr, sc),
             &Addr::Str(s) => write!(f, "Addr::Str({})", s),
             &Addr::PStrLocation(h, n) => write!(f, "Addr::PStrLocation({}, {})", h, n),
-            &Addr::Stream(ref stream) => write!(f, "Addr::Stream({})", stream.as_ptr() as usize),
+            &Addr::Stream(stream) => write!(f, "Addr::Stream({})", stream),
+            &Addr::Usize(cp) => write!(f, "Addr::Usize({})", cp),
         }
     }
 }