]> Repositorios git - scryer-prolog.git/commitdiff
cleanup code generation, add throw/catch, copy_term, var, atomic, not, false builtins
authorMark Thom <[email protected]>
Mon, 14 Aug 2017 05:53:01 +0000 (23:53 -0600)
committerMark Thom <[email protected]>
Mon, 14 Aug 2017 05:53:01 +0000 (23:53 -0600)
14 files changed:
Cargo.lock
Cargo.toml
README.md
src/main.rs
src/prolog/allocator.rs
src/prolog/ast.rs
src/prolog/builtins.rs [new file with mode: 0644]
src/prolog/codegen.rs
src/prolog/fixtures.rs
src/prolog/io.rs
src/prolog/iterators.rs
src/prolog/machine.rs
src/prolog/macros.rs [new file with mode: 0644]
src/prolog/mod.rs

index 49a55684e2fff0583ea8c9f2d1562d89c490c079..d1a41907899f29425053f44fb1e64a0074d8de15 100644 (file)
@@ -1,6 +1,6 @@
 [root]
 name = "rusty-wam"
-version = "0.6.6"
+version = "0.6.7"
 dependencies = [
  "lalrpop 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "lalrpop-util 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
index 3f24d6106d7444194b4d3e61735149dd7731d72c..186ab5475cdd580d2f54f2cfd6ecf83de7432ee4 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "rusty-wam"
-version = "0.6.6"
+version = "0.6.7"
 authors = ["Mark Thom"]
 
 build = "build.rs"
index e877002066481f8e929336d445edb0ce1c687eb1..90a5b0e71dc08f776380aad252ce4b983ba259c0 100644 (file)
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ argument indexing, and conjunctive queries.
 Extend rusty-wam to include the following, among other features:
 
 * call/N as a built-in meta-predicate (_done_).
-* ISO Prolog compliant throw/catch.
+* ISO Prolog compliant throw/catch (_done_).
 * Built-in and user-defined operators of all fixities,
   with custom associativity and precedence.
 * Bignum and floating point arithmetic.
index e11b6fb7dd0cc09b38a3bcb1b770aa254972ce56..23d713386d81db46cfb451cf26b3633f143b94d4 100644 (file)
@@ -671,6 +671,36 @@ mod tests {
         assert_eq!(submit(&mut wam, "?- call(call(call(call(call(call)))))."), false);
         assert_eq!(submit(&mut wam, "?- call(call(call(call(call(call(p(X)))))))."), true);
     }
+
+    #[test]
+    fn test_queries_on_exceptions()
+    {
+        let mut wam = Machine::new();
+
+        submit(&mut wam, "f(a). f(_) :- throw(stuff).");
+        submit(&mut wam, "handle(stuff).");
+
+        assert_eq!(submit(&mut wam, "?- catch(f(X), Exception, handle(Exception))."), true);
+        
+        submit(&mut wam, "f(a). f(X) :- g(X).");
+        submit(&mut wam, "g(x). g(y). g(z).");
+        submit(&mut wam, "handle(x). handle(y).");
+        
+        assert_eq!(submit(&mut wam, "?- catch(f(X), X, handle(X))."), true);
+        assert_eq!(submit(&mut wam, "?- catch(f(a), _, handle(X))."), true);
+        assert_eq!(submit(&mut wam, "?- catch(f(b), _, handle(X))."), false);
+
+        submit(&mut wam, "g(x). g(X) :- throw(x).");
+        
+        assert_eq!(submit(&mut wam, "?- catch(f(X), x, handle(X))."), true);
+        assert_eq!(submit(&mut wam, "?- catch(f(X), x, handle(z))."), true);
+        assert_eq!(submit(&mut wam, "?- catch(f(z), x, handle(x))."), true);
+        assert_eq!(submit(&mut wam, "?- catch(f(z), x, handle(y))."), true);
+        assert_eq!(submit(&mut wam, "?- catch(f(z), x, handle(z))."), false);
+        
+        //TODO: write more tests: multi-layered throw/catch, catch
+        // within catch, throw within catch, etc.
+    }
 }
 
 fn process_buffer(wam: &mut Machine, buffer: &str)
index b5a05ef1090e87eb575b769fa80896a587c36957..d985219ae7527d4eff8fccca3b928dcb988d93af 100644 (file)
@@ -25,7 +25,7 @@ pub trait Allocator<'a>
     fn bindings_mut(&mut self) -> &mut AllocVarDict<'a>;
 
     fn take_bindings(self) -> AllocVarDict<'a>;
-    
+
     fn drain_var_data(&mut self, vs: VariableFixtures<'a>) -> VariableFixtures<'a>
     {
         let mut perm_vs = VariableFixtures::new();
index 72b0c2fa44271e5508b4cb24dcd23bbedc6b09a7..bf1a3fef0ab871e99a7538392e9c1a732a877233 100644 (file)
@@ -20,7 +20,7 @@ impl GenContext {
             GenContext::Head => 0,
             GenContext::Mid(cn) | GenContext::Last(cn) => cn
         }
-    }        
+    }
 }
 
 pub enum PredicateClause {
@@ -77,7 +77,7 @@ impl TopLevel {
                                                           ref mut clauses })
                             =>
                         {
-                            iter = Box::new(once(head));
+                            iter = Box::new(iter.chain(once(head)));
                             iter = Box::new(iter.chain(clauses.iter_mut()));
                         },
                         _ => {}
@@ -85,7 +85,7 @@ impl TopLevel {
                 },
             _ => {}
         }
-        
+
         iter
     }
 }
@@ -145,6 +145,7 @@ impl Default for VarReg {
 #[derive(Clone, Hash, PartialEq, Eq)]
 pub enum Constant {
     Atom(Atom),
+    UInt64(usize),
     EmptyList
 }
 
@@ -163,14 +164,6 @@ pub enum QueryTerm {
 }
 
 impl QueryTerm {
-    pub fn arity(&self) -> usize {
-        match self {
-            &QueryTerm::Term(ref term) => term.arity(),
-            &QueryTerm::CallN(ref terms) => terms.len(),
-            _ => 0
-        }
-    }
-
     pub fn to_ref(&self) -> QueryTermRef {
         match self {
             &QueryTerm::CallN(ref terms) =>
@@ -178,7 +171,7 @@ impl QueryTerm {
             &QueryTerm::Cut =>
                 QueryTermRef::Cut,
             &QueryTerm::Term(ref term) =>
-                QueryTermRef::Term(term)
+                QueryTermRef::Term(term),
         }
     }
 }
@@ -207,7 +200,7 @@ impl<'a> ClauseType<'a> {
 
 #[derive(Clone, Copy)]
 pub enum TermRef<'a> {
-    AnonVar(Level),    
+    AnonVar(Level),
     Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
     Constant(Level, &'a Cell<RegType>, &'a Constant),
     Clause(ClauseType<'a>, &'a Vec<Box<Term>>),
@@ -219,7 +212,7 @@ impl<'a> TermRef<'a> {
         match self {
             TermRef::AnonVar(lvl)
           | TermRef::Cons(lvl, _, _, _)
-          | TermRef::Constant(lvl, _, _)          
+          | TermRef::Constant(lvl, _, _)
           | TermRef::Var(lvl, _, _) => lvl,
             TermRef::Clause(ClauseType::Root, _) => Level::Shallow,
             TermRef::Clause(ClauseType::Deep(lvl, _, _), _) => lvl,
@@ -232,26 +225,17 @@ impl<'a> TermRef<'a> {
 pub enum QueryTermRef<'a> {
     CallN(&'a Vec<Box<Term>>),
     Cut,
-    Term(&'a Term)
+    Term(&'a Term),
 }
 
 impl<'a> QueryTermRef<'a> {
     pub fn arity(self) -> usize {
         match self {
-            QueryTermRef::Term(term) => term.arity(),
             QueryTermRef::CallN(terms) => terms.len(),
-            _ => 0
+            QueryTermRef::Cut => 0,
+            QueryTermRef::Term(term) => term.arity(),
         }
     }
-    
-    pub fn is_callable(self) -> bool {
-        match self {
-            QueryTermRef::Term(&Term::Clause(_, _, _))
-          | QueryTermRef::Term(&Term::Constant(_, Constant::Atom(_)))
-          | QueryTermRef::CallN(_) => true,
-            _ => false
-        }
-    }    
 }
 
 pub enum ChoiceInstruction {
@@ -292,17 +276,30 @@ impl IndexedChoiceInstruction {
     }
 }
 
-pub enum BuiltInInstruction {
-    InternalCallN
+pub enum BuiltInInstruction {    
+    CleanUpBlock,
+    CopyTerm,
+    Fail,
+    GetBall,
+    GetCurrentBlock,
+    Goto(usize, usize),
+    InstallNewBlock,
+    InternalCallN,
+    IsAtomic,
+    IsVar,
+    ResetBlock,
+    SetBall, 
+    Unify, 
+    UnwindStack
 }
 
 pub enum ControlInstruction {
     Allocate(usize),
     Call(Atom, usize, usize),
     CallN(usize),
-    ExecuteN(usize),
     Deallocate,
     Execute(Atom, usize),
+    ExecuteN(usize),
     Proceed
 }
 
@@ -547,7 +544,7 @@ impl Term {
             _ => None
         }
     }
-
+    
     pub fn arity(&self) -> usize {
         match self {
             &Term::Clause(_, _, ref child_terms) => child_terms.len(),
@@ -555,4 +552,3 @@ impl Term {
         }
     }
 }
-
diff --git a/src/prolog/builtins.rs b/src/prolog/builtins.rs
new file mode 100644 (file)
index 0000000..daa339f
--- /dev/null
@@ -0,0 +1,125 @@
+use prolog::ast::*;
+
+use std::collections::HashMap;
+
+pub type PredicateKey = (Atom, usize); // name, arity, type.
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub enum PredicateKeyType {
+    BuiltIn,
+    User
+}
+
+pub type CodeDir = HashMap<PredicateKey, (PredicateKeyType, usize)>;
+     
+fn get_builtins() -> Code {    
+    vec![internal_call_n!(), // callN/N, 0.
+         is_atomic!(), // atomic/1, 1.
+         proceed!(),
+         is_var!(),    // var/1, 3.
+         proceed!(),
+         allocate!(4), // catch/3, 5.
+         query![get_var_in_query!(perm_v!(2), 1),
+                get_var_in_query!(perm_v!(3), 2),
+                get_var_in_query!(perm_v!(1), 3),
+                put_var!(perm_v!(4), 1)],
+         get_current_block!(),
+         query![put_value!(perm_v!(2), 1),
+                put_value!(perm_v!(3), 2),
+                put_value!(perm_v!(1), 3),
+                put_unsafe_value!(4, 4)],
+         deallocate!(),
+         goto!(11, 4),
+         try_me_else!(9), // catch/4, 11.
+         allocate!(3),
+         query![get_var_in_query!(perm_v!(3), 1),
+                get_var_in_query!(perm_v!(2), 4),
+                put_var!(perm_v!(1), 1)],
+         install_new_block!(),
+         query![put_value!(perm_v!(3), 1)],
+         call_n!(1),
+         query![put_value!(perm_v!(2), 1),
+                put_unsafe_value!(1, 2)],
+         deallocate!(),
+         goto!(42, 2), // goto end_block/2 at 42.
+         trust_me!(),
+         allocate!(4),
+         query![get_var_in_query!(perm_v!(2), 2),
+                get_var_in_query!(perm_v!(1), 3),
+                get_var_in_query!(temp_v!(2), 1),
+                put_value!(temp_v!(4), 1)],
+         reset_block!(),
+         query![put_var!(perm_v!(4), 1)],
+         get_ball!(),
+         query![put_value!(perm_v!(4), 1),
+                put_var!(perm_v!(3), 2)],
+         copy_term!(),
+         query![put_unsafe_value!(3, 1),
+                put_value!(perm_v!(2), 2),
+                put_value!(perm_v!(1), 3)],
+         deallocate!(),
+         goto!(31, 2), // goto handle_ball/2.
+         try_me_else!(10), // handle_ball/2, 31.
+         allocate!(2),
+         get_level!(),
+         query![get_var_in_query!(perm_v!(2), 3)],
+         unify!(),
+         cut!(non_terminal!()),
+         query![put_value!(perm_v!(2), 1)],
+         deallocate!(),
+         execute_n!(1),
+         trust_me!(),
+         unwind_stack!(),
+         try_me_else!(8), // end_block/2, 42.
+         allocate!(1),
+         query![get_var_in_query!(perm_v!(1), 1),
+                put_value!(temp_v!(2), 1)],
+         clean_up_block!(),
+         query![put_value!(perm_v!(1), 1)],
+         deallocate!(),
+         reset_block!(),
+         proceed!(),
+         trust_me!(),
+         allocate!(0),
+         query![get_var_in_query!(temp_v!(3), 1),
+                put_value!(temp_v!(2), 1)],
+         reset_block!(),
+         deallocate!(),
+         goto!(58, 0), // goto false.
+         set_ball!(), // throw/1, 56.
+         unwind_stack!(),         
+         fail!(), // false/0, 58.
+         proceed!(),
+         try_me_else!(7), // not/1, 60.
+         allocate!(1),
+         get_level!(),
+         call_n!(1),
+         cut!(non_terminal!()),
+         deallocate!(),
+         goto!(58, 0), // goto false.
+         trust_me!(),
+         proceed!(),
+         copy_term!(), // copy_term/2, 69.
+         proceed!()]
+}
+
+pub fn build_code_dir() -> (Code, CodeDir) {
+    let mut code_dir = HashMap::new();
+    let builtin_code = get_builtins();
+    
+    // there are 63 registers in the VM, so call/N is defined for all 0 <= N <= 62
+    // (an extra register is needed for the predicate name)
+    for arity in 0 .. 63 {
+        code_dir.insert((String::from("call"), arity), (PredicateKeyType::BuiltIn, 0));
+    }
+
+    code_dir.insert((String::from("atomic"), 1), (PredicateKeyType::BuiltIn, 1));
+    code_dir.insert((String::from("var"), 1), (PredicateKeyType::BuiltIn, 3));
+    code_dir.insert((String::from("false"), 0), (PredicateKeyType::BuiltIn, 58));
+    code_dir.insert((String::from("not"), 1), (PredicateKeyType::BuiltIn, 60));
+    code_dir.insert((String::from("copy_term"), 2), (PredicateKeyType::BuiltIn, 69));
+    code_dir.insert((String::from("catch"), 3), (PredicateKeyType::BuiltIn, 5));
+    code_dir.insert((String::from("throw"), 1), (PredicateKeyType::BuiltIn, 56));
+
+    (builtin_code, code_dir)
+}
index abe274e01f3e0e3b946d29cf15a9b2f380fd000e..71b9f4352fe0610443b05ff4dedfafa595d87b90 100644 (file)
@@ -21,6 +21,34 @@ pub enum EvalSession<'a> {
     SubsequentQuerySuccess,
 }
 
+pub struct ConjunctInfo<'a> {
+    pub perm_vs: VariableFixtures<'a>,
+    pub num_of_chunks: usize,
+    pub has_deep_cut: bool,
+    pub has_catch: bool
+}
+
+impl<'a> ConjunctInfo<'a>
+{
+    fn new(perm_vs: VariableFixtures<'a>, num_of_chunks: usize, has_deep_cut: bool, has_catch: bool)
+        -> Self
+    {
+        ConjunctInfo { perm_vs, num_of_chunks, has_deep_cut, has_catch }
+    }
+
+    fn allocates(&self) -> bool {
+        self.perm_vs.size() > 0 || self.num_of_chunks > 1 || self.has_deep_cut || self.has_catch
+    }
+
+    fn perm_vars(&self) -> usize {
+        self.perm_vs.size() + self.perm_var_offset()
+    }
+
+    fn perm_var_offset(&self) -> usize {
+        self.has_deep_cut as usize + 2 * (self.has_catch as usize)
+    }
+}
+
 impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
 {
     pub fn new() -> Self {
@@ -47,7 +75,6 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
         *self.var_count.get(var).unwrap()
     }
 
-
     fn add_or_increment_void_instr<Target>(target: &mut Vec<Target>)
         where Target: CompilationTarget<'a>
     {
@@ -145,15 +172,14 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
         target
     }
 
-    fn collect_var_data(&mut self, iter: ChunkedIterator<'a>) -> (VariableFixtures<'a>, bool)
+    fn collect_var_data(&mut self, mut iter: ChunkedIterator<'a>) -> ConjunctInfo<'a>
     {
         let mut vs = VariableFixtures::new();
-        let has_deep_cut = iter.contains_deep_cut();
-        let has_head = iter.at_head();
+        let at_rule_head = iter.at_rule_head();
 
-        for (chunk_num, (last_term_arity, terms)) in iter.enumerate() {
+        while let Some((chunk_num, last_term_arity, terms)) = iter.next() {
             for (i, term_or_cut_ref) in terms.iter().enumerate() {
-                let term_loc = if chunk_num == 0 && i == 0 && has_head {
+                let term_loc = if chunk_num == 0 && i == 0 && at_rule_head {
                     GenContext::Head
                 } else if i < terms.len() - 1 {
                     GenContext::Mid(chunk_num)
@@ -169,15 +195,21 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
             }
         }
 
+        let num_of_chunks = iter.chunk_num();
+        let has_deep_cut  = iter.encountered_deep_cut();
+        let has_catch     = iter.encountered_catch();
+
         vs.populate_restricting_sets();
-        vs.set_perm_vals(has_deep_cut);
+        vs.set_perm_vals(has_deep_cut, has_catch);
 
-        (vs, has_deep_cut)
+        let vs = self.marker.drain_var_data(vs);
+
+        ConjunctInfo::new(vs, num_of_chunks, has_deep_cut, has_catch)
     }
 
     fn add_conditional_call(compiled_query: &mut Code, qt: QueryTermRef, pvs: usize)
     {
-        match qt {
+        match qt {            
             QueryTermRef::CallN(terms) => {
                 let call = ControlInstruction::CallN(terms.len());
                 compiled_query.push(Line::Control(call));
@@ -194,18 +226,18 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
         }
     }
 
-    fn lco(body: &mut Code, toc: &QueryTerm) -> usize
+    fn lco(body: &mut Code, toc: QueryTermRef<'a>) -> usize
     {
         let last_arity = toc.arity();
         let mut dealloc_index = body.len() - 1;
 
         match toc {
-            &QueryTerm::Term(Term::Clause(_, ref name, _))
-          | &QueryTerm::Term(Term::Constant(_, Constant::Atom(ref name))) =>
+            QueryTermRef::Term(&Term::Clause(_, ref name, _))
+          | QueryTermRef::Term(&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);
                 },
-            &QueryTerm::CallN(ref terms) =>
+            QueryTermRef::CallN(terms) =>
                 if let &mut Line::Control(ref mut ctrl) = body.last_mut().unwrap() {
                     *ctrl = ControlInstruction::ExecuteN(terms.len());
                 },
@@ -216,102 +248,71 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
     }
 
     fn compile_seq(&mut self,
-                   clauses: &'a [QueryTerm],
-                   vs: &VariableFixtures<'a>,
+                   iter: ChunkedIterator<'a>,
+                   conjunct_info: &ConjunctInfo<'a>,
                    body: &mut Code,
                    is_exposed: bool)
     {
-        let iter = ChunkedIterator::from_term_sequence(clauses);
-
-        for (chunk_num, (_, terms)) in iter.enumerate() {
-            self.marker.reset_contents();
-
-            for (i, term) in terms.iter().enumerate() {
+        for (chunk_num, _, terms) in iter {
+            for (i, term) in terms.iter().enumerate()
+            {
                 let term_loc = if i + 1 < terms.len() {
                     GenContext::Mid(chunk_num)
                 } else {
                     GenContext::Last(chunk_num)
                 };
 
-                let mut body_appendage = match term {
-                    &QueryTermRef::Cut if i + 1 < terms.len() =>
-                        vec![Line::Cut(CutInstruction::Cut(Terminal::Non))],
-                    &QueryTermRef::Cut =>
-                        vec![Line::Cut(CutInstruction::Cut(Terminal::Terminal))],
+                match term {
+                    &QueryTermRef::Cut if i + 1 < terms.len() => {
+                        body.push(if chunk_num == 0 {
+                            Line::Cut(CutInstruction::NeckCut(Terminal::Non))
+                        } else {
+                            Line::Cut(CutInstruction::Cut(Terminal::Non))
+                        });
+                    },
+                    &QueryTermRef::Cut => {
+                        body.push(if chunk_num == 0 {
+                            Line::Cut(CutInstruction::NeckCut(Terminal::Terminal))
+                        } else {
+                            Line::Cut(CutInstruction::Cut(Terminal::Terminal))
+                        });
+                    },
+                    _ if chunk_num == 0 => {
+                        self.marker.advance(GenContext::Head, *term);
+
+                        let iter = term.post_order_iter();
+                        body.push(Line::Query(self.compile_target(iter, term_loc, is_exposed)));
+                        Self::add_conditional_call(body, *term, conjunct_info.perm_vars());
+                    },
                     _ => {
-                        let num_vars = vs.vars_above_threshold(i + 1);
-                        self.compile_query_line(*term, term_loc, num_vars, is_exposed)
-                    }
+                        let num_vars = conjunct_info.perm_vs.vars_above_threshold(i + 1);
+                        self.compile_query_line(*term, term_loc, body, num_vars, is_exposed);
+                    },
                 };
 
-                body.append(&mut body_appendage);
+                self.marker.reset_contents();
             }
         }
     }
 
-    fn compile_seq_prelude(&mut self,
-                           num_clauses: usize,
-                           vs: &VariableFixtures<'a>,
-                           deep_cuts: bool,
-                           body: &mut Code)
-                           -> usize
+    fn compile_seq_prelude(&mut self, conjunct_info: &ConjunctInfo, body: &mut Code)
     {
-        if num_clauses > 0 {
-            let perm_vars = vs.vars_above_threshold(0) + deep_cuts as usize;
+        if conjunct_info.allocates() {
+            let perm_vars = conjunct_info.perm_vars();
 
             body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
 
-            if deep_cuts {
+            if conjunct_info.has_deep_cut {
                 body.push(Line::Cut(CutInstruction::GetLevel));
             }
-
-            return perm_vars;
         }
-
-        0
     }
 
-    fn compile_neck_cut_or(&mut self,
-                           p1: &'a QueryTerm,
-                           body: &mut Code,
-                           perm_vars: usize,
-                           is_exposed: bool,
-                           at_end: bool)
-    {
-        match p1 {
-            &QueryTerm::Cut => {
-                let term = if at_end {
-                    Terminal::Terminal
-                } else {
-                    Terminal::Non
-                };
-
-                body.push(Line::Cut(CutInstruction::NeckCut(term)));
-            },
-            _ => {
-                let p1 = p1.to_ref();
-
-                self.marker.advance(GenContext::Head, p1);
-
-                let term_loc = if p1.is_callable() {
-                    GenContext::Last(0)
-                } else {
-                    GenContext::Mid(0)
-                };
-
-                let iter = p1.post_order_iter();
-                body.push(Line::Query(self.compile_target(iter, term_loc, is_exposed)));
-
-                Self::add_conditional_call(body, p1, perm_vars);
-            }
-        };
-    }
-
-    fn compile_cleanup(body: &mut Code, num_clauses: usize, toc: &QueryTerm)
+    fn compile_cleanup(body: &mut Code, conjunct_info: &ConjunctInfo, toc: QueryTermRef<'a>)
     {
         let dealloc_index = Self::lco(body, toc);
 
-        if num_clauses > 0 {
+        if conjunct_info.allocates() {
             body.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate));
         }
     }
@@ -319,25 +320,23 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
     pub fn compile_rule<'b: 'a>(&mut self, rule: &'b Rule) -> Code
     {
         let iter = ChunkedIterator::from_rule(rule);
-        let (mut vs, deep_cuts) = self.collect_var_data(iter);
-        vs = self.marker.drain_var_data(vs);
+        let conjunct_info = self.collect_var_data(iter);
 
         let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
         let mut code = Vec::new();
 
         self.marker.advance(GenContext::Head, QueryTermRef::Term(p0));
-
-        let perm_vars = self.compile_seq_prelude(clauses.len(), &vs, deep_cuts, &mut code);
+        self.compile_seq_prelude(&conjunct_info, &mut code);
 
         if p0.is_clause() {
             let iter = FactInstruction::iter(p0);
             code.push(Line::Fact(self.compile_target(iter, GenContext::Head, false)));
         }
 
-        self.compile_neck_cut_or(p1, &mut code, perm_vars, false, clauses.len() == 0);
-        self.compile_seq(clauses, &vs, &mut code, false);
+        let iter = ChunkedIterator::from_rule_body(p1, clauses);
+        self.compile_seq(iter, &conjunct_info, &mut code, false);
 
-        if perm_vars > 0 {
+        if conjunct_info.allocates() {
             let index = if let &Line::Control(_) = code.last().unwrap() {
                 code.len() - 2
             } else {
@@ -345,12 +344,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
             };
 
             if let &mut Line::Query(ref mut query) = &mut code[index] {
-                vs.mark_unsafe_vars_in_rule(p0, query);
+                conjunct_info.perm_vs.mark_unsafe_vars_in_rule(p0, query);
             }
         }
 
-        Self::compile_cleanup(&mut code, clauses.len(), clauses.last().unwrap_or(p1));
-
+        Self::compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1).to_ref());
         code
     }
 
@@ -383,10 +381,9 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
 
     pub fn compile_fact<'b: 'a>(&mut self, term: &'b Term) -> Code
     {
-        let iter = ChunkedIterator::from_term(term, true);
-        let (vs, _) = self.collect_var_data(iter);
-        self.marker.drain_var_data(vs);
-
+        let iter = ChunkedIterator::from_fact(term);
+        
+        self.collect_var_data(iter);
         self.marker.advance(GenContext::Head, QueryTermRef::Term(term));
 
         let mut code = Vec::new();
@@ -408,42 +405,32 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
     fn compile_query_line(&mut self,
                           term: QueryTermRef<'a>,
                           term_loc: GenContext,
+                          code: &mut Code,
                           index: usize,
                           is_exposed: bool)
-                          -> Code
     {
         self.marker.advance(term_loc, term);
 
-        let mut code = Vec::new();
-
         let iter = term.post_order_iter();
         let compiled_query = Line::Query(self.compile_target(iter, term_loc, is_exposed));
 
         code.push(compiled_query);
 
-        Self::add_conditional_call(&mut code, term, index);
-
-        code
+        Self::add_conditional_call(code, term, index);
     }
 
     pub fn compile_query(&mut self, query: &'a Vec<QueryTerm>) -> Code
     {
         let iter = ChunkedIterator::from_term_sequence(query);
-        let (mut vs, deep_cuts) = self.collect_var_data(iter);
-        let p1 = query.first().unwrap();
-
-        vs = self.marker.drain_var_data(vs);
+        let conjunct_info = self.collect_var_data(iter);
 
-        let mut code  = Vec::new();
-        let perm_vars = self.compile_seq_prelude(query.len() - 1,
-                                                 &vs,
-                                                 deep_cuts,
-                                                 &mut code);
+        let mut code = Vec::new();
+        self.compile_seq_prelude(&conjunct_info, &mut code);
 
-        self.compile_neck_cut_or(p1, &mut code, perm_vars, true, query.len() == 1);
-        self.compile_seq(&query[1 .. ], &vs, &mut code, true);
+        let iter = ChunkedIterator::from_term_sequence(query);
+        self.compile_seq(iter, &conjunct_info, &mut code, true);
 
-        if perm_vars > 0 {
+        if conjunct_info.allocates() {
             let index = if let &Line::Control(_) = code.last().unwrap() {
                 code.len() - 2
             } else {
@@ -451,12 +438,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
             };
 
             if let &mut Line::Query(ref mut query) = &mut code[index] {
-                vs.mark_unsafe_vars_in_query(query);
+                conjunct_info.perm_vs.mark_unsafe_vars_in_query(query);
             }
         }
 
-        Self::compile_cleanup(&mut code, query.len() - 1, query.last().unwrap());
-
+        Self::compile_cleanup(&mut code, &conjunct_info, query.last().unwrap().to_ref());
         code
     }
 
index a1284cca34b06f10b14743e007faeaee4b3d4dcb..cc2412e405b1993dd22b6c2b1d88892b45ce6cd5 100644 (file)
@@ -214,7 +214,11 @@ impl<'a> VariableFixtures<'a>
         self.0.values()
     }
 
-    pub fn set_perm_vals(&self, has_deep_cuts: bool)
+    pub fn size(&self) -> usize {
+        self.0.len()
+    }
+    
+    pub fn set_perm_vals(&self, has_deep_cuts: bool, has_catch: bool)
     {
         let mut values_vec : Vec<_> = self.values()
             .filter_map(|ref v| {
@@ -227,7 +231,7 @@ impl<'a> VariableFixtures<'a>
 
         values_vec.sort_by_key(|ref v| v.0);
 
-        let offset = has_deep_cuts as usize;
+        let offset = has_deep_cuts as usize + 2 * has_catch as usize;
 
         for (i, (_, cells)) in values_vec.into_iter().rev().enumerate() {
             for cell in cells {
index ef844cd7766f60365599a26b837d360bd7e23c1c..a45718e1a7d31e2ce6b4ee481debd42eba276ee6 100644 (file)
@@ -18,7 +18,9 @@ impl fmt::Display for Constant {
             &Constant::Atom(ref atom) =>
                 write!(f, "{}", atom),
             &Constant::EmptyList =>
-                write!(f, "[]")
+                write!(f, "[]"),
+            &Constant::UInt64(integer) =>
+                write!(f, "u{}", integer)
         }
     }
 }
@@ -197,27 +199,70 @@ impl fmt::Display for RegType {
     }
 }
 
+/*
+// Wait until constexprs are supported in stable before trying to
+// switch to this.
+
+struct ClauseRewriter { field: fn(&mut Vec<Box<Term>>) -> QueryTerm }
+
+impl Clone for ClauseRewriter {
+    fn clone(&self) -> Self {
+        ClauseRewriter { field: self.field }
+    }
+}
+
+struct ClauseRewriters {
+    rewriter_map: HashMap<&'static str, ClauseRewriter>
+}
+
+impl ClauseRewriters {
+    fn new() -> Self {
+        let mut rewriter_map =
+            [("call", ClauseRewriter { field: rewrite_call_N })]//,
+        //("catch", rewrite_catch),
+        //("throw", rewrite_throw)]
+            .iter().cloned().collect();
+
+        ClauseRewriters { rewriter_map: rewriter_map }
+    }
+
+    fn get(&self, name: &str) -> Option<&ClauseRewriter> {
+        self.rewriter_map.get(name)
+    }
+}
+*/
+
+fn rewrite_call_n(terms: &mut Vec<Box<Term>>) -> QueryTerm {
+    let mut new_terms = Vec::with_capacity(0);
+    swap(&mut new_terms, terms);
+
+    QueryTerm::CallN(new_terms)
+}
+
+fn rewrite_clause(name: &Atom, terms: &mut Vec<Box<Term>>) -> Option<QueryTerm>
+{
+    if name == "call" {
+        Some(rewrite_call_n(terms))
+    } else {
+        None
+    }
+}
+
 pub fn parse_code(input: &str) -> Option<TopLevel>
 {
     match parse_TopLevel(input) {
         Ok(mut tl) => {
             for query in tl.query_iter_mut() {
-                let cts = match query {
-                    &mut QueryTerm::Term(Term::Clause(_, ref name, ref mut cts)) => {     
-                        if name == "call" {
-                            let mut new_cts = Vec::with_capacity(0);
-                            swap(&mut new_cts, cts);
-                            
-                            Some(new_cts)
-                        } else {
-                            None
-                        }
-                    },
+                let new_query = match query {
+                    &mut QueryTerm::Term(Term::Clause(_, ref name, ref mut cts)) =>
+                        rewrite_clause(name, cts),
+                    &mut QueryTerm::Term(Term::Var(_, _)) =>
+                        Some(QueryTerm::CallN(Vec::new())),
                     _ => None
                 };
 
-                if let Some(cts) = cts {
-                    swap(&mut QueryTerm::CallN(cts), query);
+                if let Some(mut new_query) = new_query {
+                    swap(&mut new_query, query);
                 }
             }
 
@@ -244,11 +289,11 @@ fn is_consistent(predicate: &Vec<PredicateClause>) -> bool {
 pub fn print_code(code: &Code) {
     for clause in code {
         match clause {
-            &Line::BuiltIn(_) => {},
             &Line::Fact(ref fact) =>
                 for fact_instr in fact {
                     println!("{}", fact_instr);
                 },
+            &Line::BuiltIn(_) => {},
             &Line::Cut(ref cut) =>
                 println!("{}", cut),
             &Line::Choice(ref choice) =>
@@ -301,11 +346,11 @@ pub fn eval<'a, 'b: 'a>(wam: &'a mut Machine, tl: &'b TopLevel) -> EvalSession<'
 
             if is_consistent(clauses) {
                 let compiled_pred = cg.compile_predicate(clauses);
-                wam.add_predicate(clauses, compiled_pred)                
+                wam.add_predicate(clauses, compiled_pred)
             } else {
                 let msg = r"Error: predicate is inconsistent.
 Each predicate must have the same name and arity.";
-                
+
                 EvalSession::EntryFailure(String::from(msg))
             }
         },
index 83a0178b3b9ab478554bbecd8f0cbbd8524dc278..8230fae9f46df7cf51caa27b9b486e1c0412c32a 100644 (file)
@@ -53,13 +53,16 @@ impl<'a> QueryIterator<'a> {
     fn from_term(term: &'a Term) -> Self {
         let state = match term {
             &Term::AnonVar =>
-                IteratorState::AnonVar(Level::Shallow),
+            //IteratorState::AnonVar(Level::Shallow),
+                return QueryIterator { state_stack: vec![] },
             &Term::Clause(_, _, ref terms) =>
                 IteratorState::Clause(0, ClauseType::Root, terms),
-            &Term::Cons(ref cell, ref head, ref tail) =>
-                IteratorState::InitialCons(Level::Shallow, cell, head.as_ref(), tail.as_ref()),
-            &Term::Constant(ref cell, ref constant) =>
-                IteratorState::Constant(Level::Shallow, cell, constant),
+            &Term::Cons(_, _, _) =>
+            //IteratorState::InitialCons(Level::Shallow, cell, head.as_ref(), tail.as_ref()),
+                return QueryIterator { state_stack: vec![] },
+            &Term::Constant(_, _) =>
+            //IteratorState::Constant(Level::Shallow, cell, constant),
+                return QueryIterator { state_stack: vec![] },
             &Term::Var(ref cell, ref var) =>
                 IteratorState::Var(Level::Shallow, cell, var)
         };
@@ -169,7 +172,7 @@ impl<'a> Iterator for FactIterator<'a> {
                         self.push_subterm(ct.level_of_subterms(), child_term);
                     }
 
-                    match ct {                        
+                    match ct {
                         ClauseType::Deep(_, _, _) =>
                             return Some(TermRef::Clause(ct, child_terms)),
                         _ =>
@@ -206,22 +209,24 @@ impl Term {
 
 pub struct ChunkedIterator<'a>
 {
-    at_head: bool,
+    term_loc: GenContext,
     iter: Box<Iterator<Item=QueryTermRef<'a>> + 'a>,
-    deep_cut_encountered: bool
+    deep_cut_encountered: bool,
+    catch_encountered: bool
 }
 
 impl<'a> ChunkedIterator<'a>
 {
-    pub fn from_term(term: &'a Term, at_head: bool) -> Self
+    pub fn from_fact(term: &'a Term) -> Self
     {
         let inner_iter: Box<Iterator<Item=QueryTermRef<'a>>> =
             Box::new(once(QueryTermRef::Term(term)));
 
         ChunkedIterator {
-            at_head: at_head,
+            term_loc: GenContext::Head,
             iter: inner_iter,
-            deep_cut_encountered: false
+            deep_cut_encountered: false,
+            catch_encountered: false
         }
     }
 
@@ -230,93 +235,124 @@ impl<'a> ChunkedIterator<'a>
         let iter = terms.iter().map(|c| c.to_ref());
 
         ChunkedIterator {
-            at_head: false,
+            term_loc: GenContext::Last(0),
             iter: Box::new(iter),
-            deep_cut_encountered: false
+            deep_cut_encountered: false,
+            catch_encountered: false
         }
     }
 
-    pub fn from_rule(rule: &'a Rule) -> Self
+    fn iterate_over_query_term(p1: &'a QueryTerm) -> Box<Iterator<Item=QueryTermRef<'a>> + 'a>
     {
-        let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
-        let iter = once(QueryTermRef::Term(p0));
-
-        let inner_iter : Box<Iterator<Item=QueryTermRef<'a>>> = match p1 {
+        match p1 {
             &QueryTerm::CallN(ref child_terms) =>
                 Box::new(once(QueryTermRef::CallN(child_terms))),
             &QueryTerm::Term(ref p1) =>
                 Box::new(once(QueryTermRef::Term(p1))),
-            _ => Box::new(empty())
-        };
+            &QueryTerm::Cut =>
+                Box::new(once(QueryTermRef::Cut))
+        }
+    }
+
+    pub fn from_rule_body(p1: &'a QueryTerm, clauses: &'a Vec<QueryTerm>) -> Self
+    {
+        let inner_iter = Self::iterate_over_query_term(p1);
+        let iter = inner_iter.chain(clauses.iter().map(|c| c.to_ref()));
+
+        ChunkedIterator {
+            term_loc: GenContext::Last(0),
+            iter: Box::new(iter),
+            deep_cut_encountered: false,
+            catch_encountered: false
+        }
+    }
+
+    pub fn from_rule(rule: &'a Rule) -> Self
+    {
+        let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
+        let iter = once(QueryTermRef::Term(p0));
 
+        let inner_iter = Self::iterate_over_query_term(p1);
         let iter = iter.chain(inner_iter.chain(clauses.iter().map(|c| c.to_ref())));
 
         ChunkedIterator {
-            at_head: true,
+            term_loc: GenContext::Head,
             iter: Box::new(iter),
-            deep_cut_encountered: false
+            deep_cut_encountered: false,
+            catch_encountered: false
         }
     }
 
-    pub fn contains_deep_cut(&self) -> bool {
+    pub fn encountered_deep_cut(&self) -> bool {
         self.deep_cut_encountered
     }
 
-    pub fn at_head(&self) -> bool {
-        self.at_head
+    pub fn encountered_catch(&self) -> bool {
+        self.catch_encountered
+    }
+
+    pub fn at_rule_head(&self) -> bool {
+        self.term_loc == GenContext::Head
+    }
+
+    pub fn chunk_num(&self) -> usize {
+        self.term_loc.chunk_num()
     }
 
-    fn take_chunk(&mut self, term: QueryTermRef<'a>) -> (usize, Vec<QueryTermRef<'a>>)
+    fn take_chunk(&mut self, term: QueryTermRef<'a>, mut result: Vec<QueryTermRef<'a>>)
+                  -> (usize, usize, Vec<QueryTermRef<'a>>)
     {
-        let mut result = vec![term];
-        let mut arity = 0;
+        let mut arity  = 0;
+        let mut item   = Some(term);
 
-        while let Some(term) = self.iter.next() {
+        while let Some(term) = item {
             match term {
                 QueryTermRef::Term(inner_term) => {
-                    result.push(term);
+                    if let GenContext::Head = self.term_loc {
+                        result.push(term);
+                        self.term_loc = GenContext::Last(0);
+                    } else {
+                        result.push(term);
 
-                    if inner_term.is_callable() {
-                        arity = inner_term.arity();
-                        break;
+                        if inner_term.is_callable() {
+                            arity = inner_term.arity();
+                            break;
+                        }
                     }
                 },
                 QueryTermRef::CallN(child_terms) => {
                     result.push(term);
                     arity = child_terms.len() + 1;
                     break;
-                },              
-                _ => {
+                },
+                QueryTermRef::Cut => {
                     result.push(term);
-                    self.deep_cut_encountered = true;
-                }
+
+                    if self.term_loc.chunk_num() > 0 {
+                        self.deep_cut_encountered = true;
+                    }
+                },
             };
+
+            item = self.iter.next();
         }
 
-        (arity, result)
+        let chunk_num = self.term_loc.chunk_num();
+
+        if let &mut GenContext::Last(ref mut chunk_num) = &mut self.term_loc {
+            *chunk_num += 1;
+        }
+
+        (chunk_num, arity, result)
     }
 }
 
 impl<'a> Iterator for ChunkedIterator<'a>
 {
-    // the last term arity, and the reference.
-    type Item = (usize, Vec<QueryTermRef<'a>>);
+    // the chunk number, last term arity, and vector of references.
+    type Item = (usize, usize, Vec<QueryTermRef<'a>>);
 
     fn next(&mut self) -> Option<Self::Item> {
-        loop {
-            match self.iter.next() {
-                None => return None,
-                Some(QueryTermRef::Term(term)) if self.at_head => {
-                    self.at_head = false;
-                    return Some(self.take_chunk(QueryTermRef::Term(term)));
-                },
-                Some(QueryTermRef::Term(term)) if term.is_callable() =>
-                    return Some((term.arity(), vec![QueryTermRef::Term(term)])),
-                Some(QueryTermRef::CallN(child_terms)) =>
-                    return Some((child_terms.len() + 1, vec![QueryTermRef::CallN(child_terms)])),
-                Some(term_or_cut_ref) =>
-                    return Some(self.take_chunk(term_or_cut_ref))
-            }
-        }
+        self.iter.next().map(|term| self.take_chunk(term, Vec::new()))
     }
 }
index 2f42b6317045331bafd8e062b330d8b84f77e446..04077fe26cce0e13373ff82ae1c1eec58204ccdc 100644 (file)
@@ -1,4 +1,5 @@
 use prolog::ast::*;
+use prolog::builtins::*;
 use prolog::codegen::*;
 use prolog::heapview::*;
 use prolog::and_stack::*;
@@ -33,18 +34,10 @@ struct MachineState {
     trail: Vec<Ref>,
     tr: usize,
     hb: usize,
+    block: usize, // an offset into the OR stack.
+    ball: Addr
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-enum PredicateKeyType {
-    BuiltIn,
-    User
-}
-
-type PredicateKey = (Atom, usize); // name, arity, type.
-
-type CodeDir = HashMap<PredicateKey, (PredicateKeyType, usize)>;
-
 pub struct Machine {
     ms: MachineState,
     code: Code,
@@ -96,14 +89,7 @@ impl Index<CodePtr> for Machine {
 
 impl Machine {
     pub fn new() -> Self {
-        let mut code_dir = HashMap::new();
-        let code = vec![Line::BuiltIn(BuiltInInstruction::InternalCallN)];                        
-
-        // there are 64 registers in the VM, so call/N is defined for all 0 <= N <= 63
-        // (an extra register is needed for the predicate name)
-        for arity in 0 .. 64 {
-            code_dir.insert((String::from("call"), arity), (PredicateKeyType::BuiltIn, 0));
-        }
+        let (code, code_dir) = build_code_dir();
 
         Machine {
             ms: MachineState::new(),
@@ -196,8 +182,8 @@ impl Machine {
         };
 
         match instr {
-            &Line::BuiltIn(ref builtin_instr) =>
-                self.ms.execute_builtin_instr(&self.code_dir, builtin_instr),
+            &Line::BuiltIn(ref built_in_instr) =>
+                self.ms.execute_built_in_instr(&self.code_dir, built_in_instr),
             &Line::Choice(ref choice_instr) =>
                 self.ms.execute_choice_instr(choice_instr),
             &Line::Cut(ref cut_instr) =>
@@ -382,14 +368,20 @@ impl Machine {
 
             while let Some(view) = viewer.next() {
                 match view {
+                    CellView::Con(&Constant::UInt64(integer)) =>
+                        result += integer.to_string().as_str(),
                     CellView::Con(&Constant::EmptyList) =>
                         result += "[]",
                     CellView::Con(&Constant::Atom(ref atom)) =>
                         result += atom.as_str(),
-                    CellView::HeapVar(cell_num) | CellView::StackVar(_, cell_num) => {
+                    CellView::HeapVar(cell_num) => {
                         result += "_";
                         result += cell_num.to_string().as_str();
                     },
+                    CellView::StackVar(_, cell_num) => {
+                        result += "s_";
+                        result += cell_num.to_string().as_str();
+                    },
                     CellView::Str(_, ref name) =>
                         result += name.as_str(),
                     CellView::TToken(TToken::Bar) => {
@@ -449,6 +441,8 @@ impl MachineState {
                        trail: Vec::new(),
                        tr: 0,
                        hb: 0,
+                       block: 0,
+                       ball: Addr::Con(Constant::UInt64(0))
         }
     }
 
@@ -630,27 +624,32 @@ impl MachineState {
         }
     }
 
+    fn write_constant_to_var(&mut self, addr: Addr, c: &Constant) {
+        let addr = self.deref(addr);
+
+        match self.store(addr) {
+            Addr::HeapCell(hc) => {
+                self.heap[hc] = HeapCellValue::Con(c.clone());
+                self.trail(Ref::HeapCell(hc));
+            },
+            Addr::StackCell(fr, sc) => {
+                self.and_stack[fr][sc] = Addr::Con(c.clone());
+                self.trail(Ref::StackCell(fr, sc));
+            },
+            Addr::Con(c1) => {
+                if c1 != *c {
+                    self.fail = true;
+                }
+            },
+            _ => self.fail = true
+        };
+    }
+
     fn execute_fact_instr(&mut self, instr: &FactInstruction) {
         match instr {
-            &FactInstruction::GetConstant(_, ref constant, reg) => {
-                let addr = self.deref(self[reg].clone());
-
-                match self.store(addr) {
-                    Addr::HeapCell(hc) => {
-                        self.heap[hc] = HeapCellValue::Con(constant.clone());
-                        self.trail(Ref::HeapCell(hc));
-                    },
-                    Addr::StackCell(fr, sc) => {
-                        self.and_stack[fr][sc] = Addr::Con(constant.clone());
-                        self.trail(Ref::StackCell(fr, sc));
-                    },
-                    Addr::Con(c) => {
-                        if c != *constant {
-                            self.fail = true;
-                        }
-                    },
-                    _ => self.fail = true
-                };
+            &FactInstruction::GetConstant(_, ref c, reg) => {
+                let addr = self[reg].clone();
+                self.write_constant_to_var(addr, c);
             },
             &FactInstruction::GetList(_, reg) => {
                 let addr = self.deref(self[reg].clone());
@@ -722,24 +721,8 @@ impl MachineState {
             &FactInstruction::UnifyConstant(ref c) => {
                 match self.mode {
                     MachineMode::Read  => {
-                        let addr = self.deref(Addr::HeapCell(self.s));
-
-                        match self.store(addr) {
-                            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;
-                                }
-                            },
-                            _ => self.fail = true
-                        };
+                        let addr = Addr::HeapCell(self.s);
+                        self.write_constant_to_var(addr, c);
                     },
                     MachineMode::Write => {
                         self.heap.push(HeapCellValue::Con(c.clone()));
@@ -1009,7 +992,7 @@ impl MachineState {
     fn try_execute_predicate(&mut self, code_dir: &CodeDir, name: Atom, arity: usize)
     {
         let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1);
-        
+
         match compiled_tl_index {
             Some(compiled_tl_index) => {
                 self.num_of_args = arity;
@@ -1020,6 +1003,96 @@ impl MachineState {
         };
     }
 
+    // copy_term(L1, L2) uses Cheney's algorithm to copy the term at
+    // L1 to L2. forwarding_terms is kept to restore the innards of L1
+    // after it's been copied to L2.
+    fn copy_term(&mut self, a: Addr)
+    {
+        let mut forward_trail = Vec::new(); // list of (Ref, HeapCellValue) items.
+        let mut scan  = self.h;
+        let old_h = self.h;
+
+        self.heap.push(HeapCellValue::from(a));
+        self.h += 1;
+
+        while scan < self.h {
+            match self.heap[scan].clone() {
+                HeapCellValue::Con(_) | HeapCellValue::NamedStr(_, _) =>
+                    scan += 1,
+                HeapCellValue::Lis(a) => {
+                    let hcv = self.heap[a].clone();
+                    self.heap.push(hcv);
+
+                    let hcv = self.heap[a+1].clone();
+                    self.heap.push(hcv);
+
+                    self.heap[scan] = HeapCellValue::Lis(self.h);
+
+                    self.h += 2;
+                    scan += 1;
+                },
+                HeapCellValue::Ref(r) => {
+                    let ra = Addr::from(r);
+                    let rd = self.store(self.deref(ra.clone()));
+
+                    match rd {
+                        Addr::HeapCell(hc) if hc >= old_h => {
+                            self.heap[scan] = HeapCellValue::Ref(Ref::HeapCell(hc));
+                            scan += 1;
+                        },
+                        _ if ra == rd => {
+                            self.heap[scan] = HeapCellValue::Ref(Ref::HeapCell(scan));
+
+                            match r {
+                                Ref::HeapCell(hc) =>
+                                    self.heap[hc] = HeapCellValue::Ref(Ref::HeapCell(scan)),
+                                Ref::StackCell(fr, sc) =>
+                                    self.and_stack[fr][sc] = Addr::HeapCell(scan),
+                            };
+
+                            forward_trail.push((r, HeapCellValue::Ref(r)));
+                            scan += 1;
+                        },
+                        _ => self.heap[scan] = HeapCellValue::from(rd)
+                    }
+                },
+                HeapCellValue::Str(s) => {
+                    match self.heap[s].clone() {
+                        HeapCellValue::NamedStr(arity, name) => {
+                            self.heap[scan] = HeapCellValue::Str(self.h);
+                            self.heap[s] = HeapCellValue::Str(self.h);
+
+                            forward_trail.push((Ref::HeapCell(s),
+                                                HeapCellValue::NamedStr(arity, name.clone())));
+
+                            self.heap.push(HeapCellValue::NamedStr(arity, name));
+                            self.h += 1;
+
+                            for i in 0 .. arity {
+                                let hcv = self.heap[s + 1 + i].clone();
+                                self.heap.push(hcv);
+                                self.h += 1;
+                            }
+
+                        },
+                        HeapCellValue::Str(o) =>
+                            self.heap[scan] = HeapCellValue::Str(o),
+                        _ => {}
+                    }
+
+                    scan += 1;
+                }
+            };
+        }
+
+        for (r, hcv) in forward_trail {
+            match r {
+                Ref::HeapCell(hc) => self.heap[hc] = hcv,
+                Ref::StackCell(fr, sc) => self.and_stack[fr][sc] = hcv.as_addr(0)
+            }
+        }
+    }
+
     fn handle_internal_call_n(&mut self, code_dir: &CodeDir)
     {
         let arity = self.num_of_args + 1;
@@ -1032,7 +1105,7 @@ impl MachineState {
         if arity > 1 {
             self.registers[arity - 1] = pred;
 
-            if let Some((name, arity)) = self.setup_call_n(arity - 1) {                
+            if let Some((name, arity)) = self.setup_call_n(arity - 1) {
                 self.try_execute_predicate(code_dir, name, arity);
             }
         } else {
@@ -1073,6 +1146,133 @@ impl MachineState {
         Some((name, arity + narity - 1))
     }
 
+    fn has_null_ball(&self) -> bool
+    {
+        if let &Addr::Con(Constant::UInt64(0)) = &self.ball {
+            true
+        } else {
+            false
+        }
+    }
+
+    fn execute_built_in_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction)
+    {
+        match instr {
+            &BuiltInInstruction::CopyTerm => {
+                let old_h = self.h;
+                let a = self[temp_v!(1)].clone();
+                self.copy_term(a);
+
+                let a2 = self[temp_v!(2)].clone();
+                self.unify(Addr::HeapCell(old_h), a2);
+
+                self.p += 1;
+            },
+            &BuiltInInstruction::GetCurrentBlock => {
+                let c = Constant::UInt64(self.block);
+                let addr = self[temp_v!(1)].clone();
+
+                self.write_constant_to_var(addr, &c);
+                self.p += 1;
+            },
+            &BuiltInInstruction::Unify => {
+                let a1 = self[temp_v!(1)].clone();
+                let a2 = self[temp_v!(2)].clone();
+
+                self.unify(a1, a2);
+                self.p += 1;
+            },
+            &BuiltInInstruction::GetBall => {
+                let addr = self.store(self.deref(self[temp_v!(1)].clone()));
+                let ball = self.ball.clone();
+
+                if self.has_null_ball() {
+                    self.fail = true;
+                    return;
+                }
+
+                match addr.as_ref() {
+                    Some(r) => {
+                        self.bind(r, ball);
+                        self.p += 1;
+                    },
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::SetBall => {
+                self.ball = self[temp_v!(1)].clone();
+                self.p += 1;
+            },
+            &BuiltInInstruction::CleanUpBlock => {
+                let nb = self.store(self.deref(self[temp_v!(1)].clone()));
+
+                match nb {
+                    Addr::Con(Constant::UInt64(nb)) => {
+                        let b = self.b - 1;
+
+                        if nb > 0 && self.or_stack[b].b == nb {
+                            self.b = self.or_stack[nb - 1].b;
+                        }
+
+                        self.p += 1;
+                    },
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::InstallNewBlock => {
+                self.block = self.b;
+                let c = Constant::UInt64(self.block);
+                let addr = self[temp_v!(1)].clone();
+
+                self.write_constant_to_var(addr, &c);
+                self.p += 1;
+            },
+            &BuiltInInstruction::ResetBlock => {
+                let addr = self.deref(self[temp_v!(1)].clone());
+
+                match self.store(addr) {
+                    Addr::Con(Constant::UInt64(b)) => {
+                        self.block = b;
+                        self.p += 1;
+                    },
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::UnwindStack => {
+                self.b = self.block;
+                self.fail = true;
+            },
+            &BuiltInInstruction::IsAtomic => {
+                let d = self.deref(self[temp_v!(1)].clone());
+
+                match d {
+                    Addr::Con(_) => self.p += 1,
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::IsVar => {
+                let d = self.deref(self[temp_v!(1)].clone());
+
+                match d {
+                    Addr::HeapCell(_) | Addr::StackCell(_,_) =>
+                        self.p += 1,
+                    _ => self.fail = true
+                };
+            },
+            &BuiltInInstruction::InternalCallN =>
+                self.handle_internal_call_n(code_dir),
+            &BuiltInInstruction::Fail => {
+                self.fail = true;
+                self.p += 1;
+            },
+            &BuiltInInstruction::Goto(p, arity) => {
+                self.num_of_args = arity;
+                self.b0 = self.b;
+                self.p  = CodePtr::DirEntry(p);
+            }
+        };
+    }
+
     fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
     {
         match instr {
@@ -1195,13 +1395,6 @@ impl MachineState {
         };
     }
 
-    fn execute_builtin_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction)
-    {
-        match instr {
-            &BuiltInInstruction::InternalCallN => self.handle_internal_call_n(code_dir)
-        }
-    }
-
     fn execute_choice_instr(&mut self, instr: &ChoiceInstruction)
     {
         match instr {
@@ -1220,7 +1413,7 @@ impl MachineState {
                                    self.num_of_args);
 
                 self.b = self.or_stack.len();
-                let b = self.b - 1;
+                let b  = self.b - 1;
 
                 for i in 1 .. n + 1 {
                     self.or_stack[b][i] = self.registers[i].clone();
diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs
new file mode 100644 (file)
index 0000000..557fa87
--- /dev/null
@@ -0,0 +1,193 @@
+macro_rules! internal_call_n {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::InternalCallN)
+    )
+}
+
+macro_rules! allocate {
+    ($cells:expr) => (
+        Line::Control(ControlInstruction::Allocate($cells))
+    )
+}
+
+macro_rules! deallocate {
+    () => (
+        Line::Control(ControlInstruction::Deallocate)
+    )
+}
+
+macro_rules! query {
+    [$($x:expr),+] => (
+        Line::Query(vec![$($x),+])
+    )
+}
+
+macro_rules! temp_v {
+    ($x:expr) => (
+        RegType::Temp($x)
+    )
+}
+
+macro_rules! perm_v {
+    ($x:expr) => (
+        RegType::Perm($x)
+    )
+}
+
+macro_rules! get_var_in_query {
+    ($r:expr, $arg:expr) => (
+        QueryInstruction::GetVariable($r, $arg)
+    )
+}
+
+macro_rules! put_var {
+    ($r:expr, $arg:expr) => (
+        QueryInstruction::PutVariable($r, $arg)
+    )
+}
+
+macro_rules! put_value {
+    ($r:expr, $arg:expr) => (
+        QueryInstruction::PutValue($r, $arg)
+    )
+}
+
+macro_rules! put_unsafe_value {
+    ($r:expr, $arg:expr) => (
+        QueryInstruction::PutUnsafeValue($r, $arg)
+    )
+}
+
+macro_rules! try_me_else {
+    ($o:expr) => (
+        Line::Choice(ChoiceInstruction::TryMeElse($o))
+    )
+}
+
+macro_rules! is_atomic {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::IsAtomic)
+    )
+}
+
+macro_rules! is_var {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::IsVar)
+    )
+}
+
+/*
+macro_rules! retry_me_else {
+    ($o:expr) => (
+        Line::Choice(ChoiceInstruction::RetryMeElse($o))
+    )
+}
+ */
+
+macro_rules! trust_me {
+    () => (
+        Line::Choice(ChoiceInstruction::TrustMe)
+    )
+}
+
+macro_rules! call_n {
+    ($arity:expr) => (
+        Line::Control(ControlInstruction::CallN($arity))
+    )
+}
+
+macro_rules! execute_n {
+    ($arity:expr) => (
+        Line::Control(ControlInstruction::ExecuteN($arity))
+    )
+}
+
+macro_rules! proceed {
+    () => (
+        Line::Control(ControlInstruction::Proceed)
+    )
+}
+
+macro_rules! non_terminal {
+    () => (
+        Terminal::Non
+    )
+}
+
+macro_rules! cut {
+    ($term:expr) => (
+        Line::Cut(CutInstruction::Cut($term))
+    )
+}
+
+macro_rules! get_current_block {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::GetCurrentBlock)
+    )
+}
+
+macro_rules! install_new_block {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::InstallNewBlock)
+    )
+}
+
+macro_rules! goto {
+    ($line:expr, $arity:expr) => (
+        Line::BuiltIn(BuiltInInstruction::Goto($line, $arity))
+    )
+}
+
+macro_rules! reset_block {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::ResetBlock)
+    )
+}
+
+macro_rules! get_ball {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::GetBall)
+    )
+}
+
+macro_rules! unify {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::Unify)
+    )
+}
+
+macro_rules! unwind_stack {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::UnwindStack)
+    )
+}
+
+macro_rules! clean_up_block {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::CleanUpBlock)
+    )
+}
+
+macro_rules! set_ball {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::SetBall)
+    )
+}
+
+macro_rules! fail {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::Fail)
+    )
+}
+
+macro_rules! copy_term {
+    () => (
+        Line::BuiltIn(BuiltInInstruction::CopyTerm)
+    )
+}
+
+macro_rules! get_level {
+    () => (
+        Line::Cut(CutInstruction::GetLevel)
+    )
+}
index 98634a7d33e334266a1e3870dee045a1ce3f539e..86a697bdfc25c5f54b2ae23648a0d12642fe07c9 100644 (file)
@@ -8,8 +8,12 @@ pub mod heapview;
 pub mod indexing;
 pub mod io;
 pub mod iterators;
+#[macro_use]
+pub mod macros;
 pub mod naive_allocator;
 pub mod prolog_parser;
 pub mod machine;
 pub mod or_stack;
 pub mod targets;
+
+pub mod builtins;