]> Repositorios git - scryer-prolog.git/commitdiff
optimized up to section 5.10
authorMark Thom <[email protected]>
Sat, 25 Mar 2017 07:58:54 +0000 (01:58 -0600)
committerMark Thom <[email protected]>
Sat, 25 Mar 2017 07:58:54 +0000 (01:58 -0600)
Cargo.lock
Cargo.toml
README.md
src/main.rs
src/prolog/ast.rs
src/prolog/codegen.rs
src/prolog/io.rs
src/prolog/machine.rs

index 3d66e8458e85deb86cf282d882dfa02b27d00fcb..133eaf7a9fbb2940dd0776c242fd3ea68a7ca10c 100644 (file)
@@ -1,6 +1,6 @@
 [root]
 name = "rusty-wam"
-version = "0.5.7"
+version = "0.5.10"
 dependencies = [
  "lalrpop 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "lalrpop-util 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
index a231cab15f91515c7376000fafc2769681000b24..067651e7b1e7938cc76dbf3fc4ed229d3220754e 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "rusty-wam"
-version = "0.5.7"
+version = "0.5.10"
 authors = ["Mark Thom"]
 
 build = "build.rs"
index 4fde54b31ed8b86e6a94019f408324c4a664a50c..816e29499cadcc8293d6e465239f5e46d1bbc364 100644 (file)
--- a/README.md
+++ b/README.md
@@ -11,8 +11,8 @@ pure Prolog.
 Pure Prolog is implemented as a simple REPL. "Pure Prolog" is Prolog
 without cut, meta- or extra-logical operators, or side effects of any
 kind. In terms of the tutorial pacing, the work has progressed to the
-to the end of section 5.7, skipping past 5.4. Atoms and lists are the
-only two data types currently supported.
+end of section 5.9, skipping past 5.4. Atoms and lists are the only
+two data types currently supported.
 
 While proper environment trimming code is emitted by the code
 generator, it has no effect on the bytecode WAM, which lacks
@@ -21,7 +21,7 @@ frames.
 
 ## Tutorial
 To enter a multi-clause predicate, the brackets ":{" and "}:" are used
-as delimiters. They must be entirely contained with their own lines.
+as delimiters. They must be contained entirely within their own lines.
 
 For example,
 ```
index 1c94c7bdf4c6b1c79765e369f8a1a7752c314b3a..e83ddb736e042e6e5168dbfbe4efa2ca16865a29 100644 (file)
@@ -8,13 +8,13 @@ use prolog::machine::*;
 mod tests {
     use super::*;
     use prolog::ast::*;
-    
+
     fn submit(wam: &mut Machine, buffer: &str) -> EvalResult {
         let result = eval(wam, buffer);
         wam.reset();
         result
     }
-    
+
     #[test]
     fn test_queries_on_facts() {
         let mut wam = Machine::new();
@@ -90,6 +90,13 @@ mod tests {
         assert_eq!(submit(&mut wam, "?- p(f(X, g(Y), Z), g(Z), h).").failed_query(), false);
         assert_eq!(submit(&mut wam, "?- p(Z, Y, X).").failed_query(), false);
         assert_eq!(submit(&mut wam, "?- p(f(X, Y, Z), Y, h).").failed_query(), false);
+
+        submit(&mut wam, "p(_, f(_, Y, _)) :- h(Y).");
+        submit(&mut wam, "h(y).");
+
+        assert_eq!(submit(&mut wam, "?- p(_, f(_, Y, _)).").failed_query(), false);
+        assert_eq!(submit(&mut wam, "?- p(_, f(_, y, _)).").failed_query(), false);
+        assert_eq!(submit(&mut wam, "?- p(_, f(_, z, _)).").failed_query(), true);
     }
 
     #[test]
@@ -110,7 +117,7 @@ mod tests {
         assert_eq!(submit(&mut wam, "?- p(c, d, X).").failed_query(), false);
         assert_eq!(submit(&mut wam, "?- p(a, a, a).").failed_query(), false);
         assert_eq!(submit(&mut wam, "?- p(b, c, d).").failed_query(), true);
-        
+
         submit(&mut wam, "p(X, a). p(X, Y) :- q(Z), p(X, X).");
 
         assert_eq!(submit(&mut wam, "?- p(X, Y).").failed_query(), false);
@@ -137,7 +144,7 @@ mod tests {
         assert_eq!(submit(&mut wam, "?- p(a, X).").failed_query(), false);
         assert_eq!(submit(&mut wam, "?- p(b, a).").failed_query(), true);
 
-        submit(&mut wam, "p(X, Y, Z) :- q(X), r(Y), s(Z). 
+        submit(&mut wam, "p(X, Y, Z) :- q(X), r(Y), s(Z).
                         p(a, b, Z) :- q(Z).");
 
         submit(&mut wam, "q(x).");
@@ -154,7 +161,7 @@ mod tests {
 
         submit(&mut wam, "s(x, t).");
         submit(&mut wam, "t(y, u).");
-        
+
         assert_eq!(submit(&mut wam, "?- p(X).").failed_query(), false);
         assert_eq!(submit(&mut wam, "?- p(x).").failed_query(), false);
         assert_eq!(submit(&mut wam, "?- p(y).").failed_query(), false);
@@ -173,6 +180,18 @@ mod tests {
         assert_eq!(submit(&mut wam, "?- p(X, X, X).").failed_query(), false);
         assert_eq!(submit(&mut wam, "?- p(X, Y, X).").failed_query(), false);
         assert_eq!(submit(&mut wam, "?- p(f(f(X)), h(f(X)), Y).").failed_query(), true);
+
+        submit(&mut wam, "p(X) :- f(Y), g(Y), i(X, Y).");
+        submit(&mut wam, "g(f(a)). g(f(b)). g(f(c)).");
+        submit(&mut wam, "f(f(a)). f(f(b)). f(f(c)).");
+        submit(&mut wam, "i(X, X).");
+
+        assert_eq!(submit(&mut wam, "?- p(X).").failed_query(), false);
+
+        submit(&mut wam, "p(X) :- f(f(Y)), g(Y, f(Y)), i(X, f(Y)).");
+        submit(&mut wam, "g(Y, f(Y)) :- g(f(Y)).");
+
+        assert_eq!(submit(&mut wam, "?- p(X).").failed_query(), false);
     }
 
     #[test]
@@ -224,7 +243,7 @@ mod tests {
 
 fn prolog_repl() {
     let mut wam = Machine::new();
-        
+
     loop {
         print!("prolog> ");
 
@@ -239,7 +258,7 @@ fn prolog_repl() {
 
         let result = eval(&mut wam, buffer.trim());
         print(&mut wam, result);
-        
+
         wam.reset();
     }
 }
index c387e7594ccc520f555abae92cf872e87cc77eaf..9a725186ef4dd70058210722195cff357e002309 100644 (file)
@@ -1,4 +1,5 @@
 use std::cell::Cell;
+use std::cmp::Ordering;
 use std::collections::HashMap;
 use std::ops::{Add, AddAssign};
 use std::vec::Vec;
@@ -40,7 +41,7 @@ pub enum Level {
     Deep, Shallow
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub enum RegType {
     Perm(usize),
     Temp(usize)
@@ -83,7 +84,7 @@ impl VarReg {
     pub fn is_temp(self) -> bool {
         !self.norm().is_perm()
     }
-    
+
     pub fn root_register(self) -> usize {
         match self {
             VarReg::ArgAndNorm(_, root) => root,
@@ -139,8 +140,9 @@ pub enum FactInstruction {
     GetList(Level, RegType),
     GetStructure(Level, Atom, usize, RegType),
     GetValue(RegType, usize),
-    GetVariable(RegType, usize),    
+    GetVariable(RegType, usize),
     UnifyConstant(Constant),
+    UnifyLocalValue(RegType),
     UnifyVariable(RegType),
     UnifyValue(RegType),
     UnifyVoid(usize)
@@ -150,9 +152,11 @@ pub enum QueryInstruction {
     PutConstant(Level, Constant, RegType),
     PutList(Level, RegType),
     PutStructure(Level, Atom, usize, RegType),
+    PutUnsafeValue(usize, usize),
     PutValue(RegType, usize),
     PutVariable(RegType, usize),
     SetConstant(Constant),
+    SetLocalValue(RegType),
     SetVariable(RegType),
     SetValue(RegType),
     SetVoid(usize)
@@ -196,6 +200,7 @@ impl<'a> From<&'a Line> for LineOrCodeOffset<'a> {
 
 pub type Code = Vec<Line>;
 
+
 #[derive(Clone, PartialEq)]
 pub enum Addr {
     Con(Constant),
@@ -220,6 +225,13 @@ impl Addr {
             _ => None
         }
     }
+
+    pub fn is_protected(&self, e: usize) -> bool {
+        match self {
+            &Addr::StackCell(fr, _) if fr > e => false,
+            _ => true
+        }
+    }
 }
 
 impl From<Ref> for Addr {
@@ -275,6 +287,26 @@ impl HeapCellValue {
     }
 }
 
+impl PartialOrd for Ref {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        match (*self, *other) {
+            (Ref::HeapCell(hc1), Ref::HeapCell(hc2)) =>
+                Some(hc1.cmp(&hc2)),
+            (Ref::HeapCell(_), _) =>
+                Some(Ordering::Less),
+            (Ref::StackCell(fr1, sc1), Ref::StackCell(fr2, sc2)) =>
+                if fr1 < fr2 {
+                    Some(Ordering::Less)
+                } else if fr1 == fr2 {
+                    Some(sc1.cmp(&sc2))
+                } else {
+                    Some(Ordering::Greater)
+                },
+            _ => Some(Ordering::Greater)
+        }
+    }
+}
+
 #[derive(Clone, Copy)]
 pub enum CodePtr {
     DirEntry(usize),
index a8e83ad59f4f79b702752a8ed0b737d1f5be7bcc..4c5592ae7135cb0721c67cccf1e6a4619c06fd3a 100644 (file)
@@ -250,7 +250,8 @@ enum VarStatus {
 }
 
 pub struct CodeGenerator<'a> {
-    marker: TermMarker<'a>
+    marker: TermMarker<'a>,
+    var_count: HashMap<&'a Var, usize>
 }
 
 type VariableFixture<'a>  = (VarStatus, Vec<&'a Cell<VarReg>>);
@@ -258,37 +259,41 @@ type VariableFixtures<'a> = HashMap<&'a Var, VariableFixture<'a>>;
 
 impl<'a> CodeGenerator<'a> {
     pub fn new() -> Self {
-        CodeGenerator { marker: TermMarker::new() }
+        CodeGenerator { marker: TermMarker::new(),
+                        var_count: HashMap::new() }
     }
 
     pub fn vars(&self) -> &HashMap<&Var, VarReg> {
         &self.marker.bindings
     }
 
-    #[allow(dead_code)]
-    fn count_vars(term: &Term) -> HashMap<&Var, usize> {
+    fn update_var_count<Iter>(&mut self, iter: Iter)
+        where Iter : Iterator<Item=TermRef<'a>>
+    {
         let mut var_count = HashMap::new();
 
-        for term in term.breadth_first_iter() {
-            if let TermRef::Var(_, _, ref var) = term {
-                let entry = var_count.entry(*var).or_insert(0);
+        for term in iter {
+            if let TermRef::Var(_, _, var) = term {
+                if self.marker.contains_var(var) {
+                    var_count.insert(var, 2);
+                    continue;
+                }
+
+                let entry = var_count.entry(var).or_insert(0);
                 *entry += 1;
             }
         }
 
-        var_count
+        self.var_count = var_count;
     }
 
-    #[allow(dead_code)]
-    fn all_singleton_vars(terms: &Vec<Box<Term>>,
-                          var_count: &HashMap<&Var, usize>)
-                          -> bool
+    fn all_singleton_vars(&self, terms: &Vec<Box<Term>>) -> bool
     {
         for term in terms {
             match term.as_ref() {
                 &Term::AnonVar => {},
                 &Term::Var(ref cell, ref var) if cell.get().is_temp() =>
-                    if var_count.get(var).unwrap() != &1 {
+                    if self.var_count.get(var).unwrap() != &1 {
                         return false;
                     },
                 _ => return false
@@ -298,7 +303,6 @@ impl<'a> CodeGenerator<'a> {
         true
     }
 
-    #[allow(dead_code)]
     fn void_subterms<Target>(subterms: usize) -> Target
         where Target: CompilationTarget<'a>
     {
@@ -416,7 +420,6 @@ impl<'a> CodeGenerator<'a> {
     {
         let iter       = Target::iter(term);
         let mut target = Vec::new();
-        let var_count  = Self::count_vars(term);
 
         for term in iter {
             match term {
@@ -424,7 +427,7 @@ impl<'a> CodeGenerator<'a> {
                     target.push(self.to_structure(lvl, cell, atom, terms.len()));
 
                     if !has_exposed_vars {
-                        if Self::all_singleton_vars(terms, &var_count) {
+                        if self.all_singleton_vars(terms) {
                             target.push(Self::void_subterms(terms.len()));
                             continue;
                         }
@@ -547,7 +550,7 @@ impl<'a> CodeGenerator<'a> {
 
         for &(term_status, _) in vs.values() {
             if let VarStatus::Permanent(i) = term_status {
-                if i >= index {
+                if i > index {
                     var_count += 1;
                 }
             }
@@ -556,17 +559,85 @@ impl<'a> CodeGenerator<'a> {
         var_count
     }
 
+    fn lco(body: &mut Code, rule: &'a Rule) -> usize {
+        let last_arity = rule.last_clause().arity();
+        let mut dealloc_index = body.len() - 1;
+
+        match rule.last_clause() {
+              &Term::Clause(_, ref name, _)
+            | &Term::Constant(_, Constant::Atom(ref name)) => {
+                if let &mut Line::Control(ref mut ctrl) = body.last_mut().unwrap() {
+                    *ctrl = ControlInstruction::Execute(name.clone(), last_arity);
+                }
+            },
+            _ => dealloc_index = body.len()
+        };
+
+        dealloc_index
+    }
+
+    fn mark_unsafe_query_vars(head: &Term,
+                              vs: &VariableFixtures,
+                              query: &mut CompiledQuery)
+    {
+        let mut unsafe_vars = HashMap::new();
+
+        for &(_, ref cb) in vs.values() {
+            if !cb.is_empty() {
+                let index = cb.first().unwrap().get().norm();
+                unsafe_vars.insert(index, false);
+            }
+        }
+
+        for term_ref in head.breadth_first_iter() {
+            match term_ref {
+                TermRef::Var(_, cell, _) => {
+                    unsafe_vars.remove(&cell.get().norm());
+                },
+                _ => {}
+            };
+        }
+
+        for query_instr in query.iter_mut() {
+            match query_instr {
+                &mut QueryInstruction::PutValue(RegType::Perm(i), arg) =>
+                    if let Some(found) = unsafe_vars.get_mut(&RegType::Perm(i)) {
+                        if !*found {
+                            *found = true;
+                            *query_instr = QueryInstruction::PutUnsafeValue(i, arg);
+                        }
+                    },
+                &mut QueryInstruction::SetVariable(reg)
+              | &mut QueryInstruction::PutVariable(reg, _) =>
+                    if let Some(found) = unsafe_vars.get_mut(&reg) {
+                        *found = true;
+                    },
+                &mut QueryInstruction::SetValue(reg) =>
+                    if let Some(found) = unsafe_vars.get_mut(&reg) {
+                        if !*found {
+                            *found = true;
+                            *query_instr = QueryInstruction::SetLocalValue(reg);
+                        }
+                    },
+                _ => {}
+            };
+        }
+    }
+
     pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
         let vs = Self::mark_perm_vars(&rule);
         let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
 
-        let perm_vars = Self::vars_above_threshold(&vs, 1);
+        let perm_vars = Self::vars_above_threshold(&vs, 0);
         let mut body = Vec::new();
 
         if clauses.len() > 0 {
             body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
         }
 
+        let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
+        self.update_var_count(iter);
+
         self.marker.advance(p0);
         body.push(Line::Fact(self.compile_target(p0, false)));
 
@@ -577,7 +648,7 @@ impl<'a> CodeGenerator<'a> {
 
         body = clauses.iter().enumerate()
             .map(|(i, ref term)| {
-                let num_vars = Self::vars_above_threshold(&vs, i+2);
+                let num_vars = Self::vars_above_threshold(&vs, i+1);
                 self.compile_internal_query(term, num_vars)
             })
             .fold(body, |mut body, ref mut cqs| {
@@ -585,19 +656,19 @@ impl<'a> CodeGenerator<'a> {
                 body
             });
 
-        // now perform LCO.
-        let last_arity = rule.last_clause().arity();
-        let mut dealloc_index = body.len() - 1;
+        if clauses.len() > 0 {
+            let mut index = body.len() - 1;
 
-        match rule.last_clause() {
-              &Term::Clause(_, ref name, _)
-            | &Term::Constant(_, Constant::Atom(ref name)) => {
-                if let &mut Line::Control(ref mut ctrl) = body.last_mut().unwrap() {
-                    *ctrl = ControlInstruction::Execute(name.clone(), last_arity);
-                }
-            },
-            _ => dealloc_index = body.len()
-        };
+            if let &Line::Control(_) = body.last().unwrap() {
+                index -= 1;
+            }
+
+            if let &mut Line::Query(ref mut query) = &mut body[index] {
+                Self::mark_unsafe_query_vars(p0, &vs, query);
+            }
+        }
+
+        let dealloc_index = Self::lco(&mut body, &rule);
 
         if clauses.len() > 0 {
             body.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate));
@@ -606,18 +677,51 @@ impl<'a> CodeGenerator<'a> {
         body
     }
 
+    fn mark_unsafe_fact_vars(fact: &mut CompiledFact, bindings: &HashMap<&Var, VarReg>)
+    {
+        let mut unsafe_vars = HashMap::new();
+
+        for var_reg in bindings.values() {
+            unsafe_vars.insert(var_reg.norm(), false);
+        }
+
+        for fact_instr in fact.iter_mut() {
+            match fact_instr {
+                &mut FactInstruction::UnifyValue(reg) =>
+                    if let Some(found) = unsafe_vars.get_mut(&reg) {
+                        if !*found {
+                            *found = true;
+                            *fact_instr = FactInstruction::UnifyLocalValue(reg);
+                        }
+                    },
+                &mut FactInstruction::UnifyVariable(reg) => {
+                    if let Some(found) = unsafe_vars.get_mut(&reg) {
+                        *found = true;
+                    }
+                },
+                _ => {}
+            };
+        }
+    }
+
     pub fn compile_fact(&mut self, term: &'a Term) -> Code {
         self.marker.advance(term);
+        self.update_var_count(term.breadth_first_iter());
 
-        let mut compiled_fact = vec![Line::Fact(self.compile_target(term, false))];
+        let mut compiled_fact = self.compile_target(term, false);
+        Self::mark_unsafe_fact_vars(&mut compiled_fact, self.vars());
+
+        let mut compiled_fact = vec![Line::Fact(compiled_fact)];
         let proceed = Line::Control(ControlInstruction::Proceed);
 
         compiled_fact.push(proceed);
         compiled_fact
     }
 
-    fn compile_internal_query(&mut self, term: &'a Term, index: usize) -> Code {
+    fn compile_internal_query(&mut self, term: &'a Term, index: usize) -> Code
+    {
         self.marker.advance(term);
+        self.update_var_count(term.breadth_first_iter());
 
         let mut compiled_query = vec![Line::Query(self.compile_target(term, false))];
         Self::add_conditional_call(&mut compiled_query, term, index);
@@ -627,9 +731,10 @@ impl<'a> CodeGenerator<'a> {
 
     pub fn compile_query(&mut self, term: &'a Term) -> Code {
         self.marker.advance(term);
+        self.update_var_count(term.breadth_first_iter());
 
         let mut compiled_query = vec![Line::Query(self.compile_target(term, true))];
-        Self::add_conditional_call(&mut compiled_query, term, 1);
+        Self::add_conditional_call(&mut compiled_query, term, 0);
 
         compiled_query
     }
index 58c5d9e7124f4049851cf9f6baf1c16567d218c8..298d5782772bdbd6d787a2a226643f23787970d6 100644 (file)
@@ -44,6 +44,8 @@ impl fmt::Display for FactInstruction {
                 write!(f, "unify_constant {}", constant),
             &FactInstruction::UnifyVariable(ref r) =>
                 write!(f, "unify_variable {}", r),
+            &FactInstruction::UnifyLocalValue(ref r) =>
+                write!(f, "unify_local_value {}", r),
             &FactInstruction::UnifyValue(ref r) =>
                 write!(f, "unify_value {}", r),
             &FactInstruction::UnifyVoid(n) =>
@@ -67,12 +69,16 @@ impl fmt::Display for QueryInstruction {
                 write!(f, "put_structure {}/{}, {}", name, arity, r),
             &QueryInstruction::PutStructure(Level::Shallow, ref name, ref arity, ref r) =>
                 write!(f, "put_structure {}/{}, A{}", name, arity, r.reg_num()),
+            &QueryInstruction::PutUnsafeValue(y, a) =>
+                write!(f, "put_unsafe_value Y{}, A{}", y, a),
             &QueryInstruction::PutValue(ref x, ref a) =>
                 write!(f, "put_value {}, A{}", x, a),
             &QueryInstruction::PutVariable(ref x, ref a) =>
                 write!(f, "put_variable {}, A{}", x, a),
             &QueryInstruction::SetConstant(ref constant) =>
                 write!(f, "set_constant {}", constant),
+            &QueryInstruction::SetLocalValue(ref r) =>
+                write!(f, "set_local_value {}", r),
             &QueryInstruction::SetVariable(ref r) =>
                 write!(f, "set_variable {}", r),
             &QueryInstruction::SetValue(ref r) =>
@@ -213,6 +219,7 @@ pub fn eval(wam: &mut Machine, buffer: &str) -> EvalResult
         &Ok(TopLevel::Predicate(ref clauses)) => {
             if is_consistent(clauses) {
                 let compiled_pred = cg.compile_predicate(clauses);
+                print_code(&compiled_pred);
                 wam.add_predicate(clauses, compiled_pred);
 
                 EvalResult::EntrySuccess
@@ -226,16 +233,18 @@ Each predicate must have the same name and arity.";
         },
         &Ok(TopLevel::Fact(ref fact)) => {
             let compiled_fact = cg.compile_fact(&fact);
+            print_code(&compiled_fact);
             wam.add_fact(fact, compiled_fact);
             EvalResult::EntrySuccess
         },
         &Ok(TopLevel::Rule(ref rule)) => {
             let compiled_rule = cg.compile_rule(&rule);
+            print_code(&compiled_rule);
             wam.add_rule(rule, compiled_rule);
             EvalResult::EntrySuccess
         },
         &Ok(TopLevel::Query(ref query)) => {
-            let compiled_query = cg.compile_query(&query);
+            let compiled_query = cg.compile_query(&query);            
             wam.run_query(compiled_query, &cg)
         },
         &Err(_) => {
index 591bec279a6710549de338ec7646940e7b442d44..1ad06e6183ad534eee13d655897682fd8f15f2d2 100644 (file)
@@ -463,6 +463,7 @@ impl MachineState {
                     _ => self.fail = true
                 };
             },
+
             &FactInstruction::GetList(_, reg) => {
                 let addr = self.deref(self[reg].clone());
 
@@ -536,10 +537,14 @@ impl MachineState {
                         let addr = self.deref(Addr::HeapCell(self.s));
 
                         match self.store(addr) {
-                            Addr::HeapCell(hc) =>
-                                self.heap[hc] = HeapCellValue::Con(c.clone()),
-                            Addr::StackCell(fr, sc) =>
-                                self.and_stack[fr][sc] = Addr::Con(c.clone()),
+                            Addr::HeapCell(hc) => {
+                                self.heap[hc] = HeapCellValue::Con(c.clone());
+                                self.trail(Ref::HeapCell(hc));
+                            },
+                            Addr::StackCell(fr, sc) => {
+                                self.and_stack[fr][sc] = Addr::Con(c.clone());
+                                self.trail(Ref::StackCell(fr, sc));
+                            },
                             Addr::Con(c1) => {
                                 if c1 != *c {
                                     self.fail = true;
@@ -569,6 +574,39 @@ impl MachineState {
 
                 self.s += 1;
             },
+            &FactInstruction::UnifyLocalValue(reg) => {
+                let s = self.s;
+
+                match self.mode {
+                    MachineMode::Read  => {
+                        let reg_addr = self[reg].clone();
+                        self.unify(reg_addr, Addr::HeapCell(s));
+                    },
+                    MachineMode::Write => {
+                        let addr = self.deref(self[reg].clone());
+                        let h    = self.h;
+
+                        if let Addr::HeapCell(hc) = addr {
+                            if hc < h {
+                                let val = self.heap[hc].clone();
+                                self.heap.push(val);
+
+                                self.h += 1;
+                                self.s += 1;
+
+                                return;
+                            }
+                        }
+
+                        self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
+                        self.bind(Ref::HeapCell(h), addr);
+
+                        self.h += 1;
+                    }
+                };
+
+                self.s += 1;
+            },
             &FactInstruction::UnifyValue(reg) => {
                 let s = self.s;
 
@@ -615,21 +653,63 @@ impl MachineState {
                 self[reg] = Addr::Str(self.h);
                 self.h += 1;
             },
+            &QueryInstruction::PutUnsafeValue(n, arg) => {
+                let e    = self.e;
+                let addr = self.deref(Addr::StackCell(e, n));
+
+                if addr.is_protected(e) {
+                    self.registers[arg] = self.store(addr);
+                } else {
+                    let h = self.h;
+
+                    self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
+                    self.bind(Ref::HeapCell(h), addr);
+
+                    self.registers[arg] = self.heap[h].as_addr(h);
+                    self.h += 1;
+                }
+            },
             &QueryInstruction::PutValue(norm, arg) =>
                 self.registers[arg] = self[norm].clone(),
             &QueryInstruction::PutVariable(norm, arg) => {
-                let h = self.h;
-                self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
+                match norm {
+                    RegType::Perm(n) => {
+                        let e = self.e;
+                        self[norm] = Addr::StackCell(e, n);
+                        self.registers[arg] = self[norm].clone();
+                    },
+                    RegType::Temp(_) => {
+                        let h = self.h;
+                        self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
 
-                self[norm] = Addr::HeapCell(h);
-                self.registers[arg] = Addr::HeapCell(h);
+                        self[norm] = Addr::HeapCell(h);
+                        self.registers[arg] = Addr::HeapCell(h);
 
-                self.h += 1;
+                        self.h += 1;
+                    }
+                };
             },
             &QueryInstruction::SetConstant(ref constant) => {
                 self.heap.push(HeapCellValue::Con(constant.clone()));
                 self.h += 1;
             },
+            &QueryInstruction::SetLocalValue(reg) => {
+                let addr = self.deref(self[reg].clone());
+                let h    = self.h;
+
+                if let Addr::HeapCell(hc) = addr {
+                    if hc < h {
+                        self.heap.push(HeapCellValue::from(addr));
+                        self.h += 1;
+                        return;
+                    }
+                }
+
+                self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
+                self.bind(Ref::HeapCell(h), addr);
+
+                self.h += 1;
+            },
             &QueryInstruction::SetVariable(reg) => {
                 let h = self.h;
                 self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
@@ -681,7 +761,7 @@ impl MachineState {
             },
             &ControlInstruction::Deallocate => {
                 let e = self.e;
-                                
+
                 self.cp = self.and_stack[e].cp;
                 self.e  = self.and_stack[e].e;