From: Mark Thom Date: Mon, 14 Aug 2017 05:53:01 +0000 (-0600) Subject: cleanup code generation, add throw/catch, copy_term, var, atomic, not, false builtins X-Git-Tag: v0.8.110~707 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=a06d99a31379919cc8310454b539692d158408e5;p=scryer-prolog.git cleanup code generation, add throw/catch, copy_term, var, atomic, not, false builtins --- diff --git a/Cargo.lock b/Cargo.lock index 49a55684..d1a41907 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", diff --git a/Cargo.toml b/Cargo.toml index 3f24d610..186ab547 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusty-wam" -version = "0.6.6" +version = "0.6.7" authors = ["Mark Thom"] build = "build.rs" diff --git a/README.md b/README.md index e8770020..90a5b0e7 100644 --- 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. diff --git a/src/main.rs b/src/main.rs index e11b6fb7..23d71338 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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) diff --git a/src/prolog/allocator.rs b/src/prolog/allocator.rs index b5a05ef1..d985219a 100644 --- a/src/prolog/allocator.rs +++ b/src/prolog/allocator.rs @@ -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(); diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index 72b0c2fa..bf1a3fef 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -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, &'a Term, &'a Term), Constant(Level, &'a Cell, &'a Constant), Clause(ClauseType<'a>, &'a Vec>), @@ -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>), 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 index 00000000..daa339f9 --- /dev/null +++ b/src/prolog/builtins.rs @@ -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; + +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) +} diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index abe274e0..71b9f435 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -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: &mut Vec) 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) -> 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 } diff --git a/src/prolog/fixtures.rs b/src/prolog/fixtures.rs index a1284cca..cc2412e4 100644 --- a/src/prolog/fixtures.rs +++ b/src/prolog/fixtures.rs @@ -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 { diff --git a/src/prolog/io.rs b/src/prolog/io.rs index ef844cd7..a45718e1 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -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>) -> 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>) -> 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>) -> Option +{ + if name == "call" { + Some(rewrite_call_n(terms)) + } else { + None + } +} + pub fn parse_code(input: &str) -> Option { 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) -> 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)) } }, diff --git a/src/prolog/iterators.rs b/src/prolog/iterators.rs index 83a0178b..8230fae9 100644 --- a/src/prolog/iterators.rs +++ b/src/prolog/iterators.rs @@ -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> + '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>> = 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> + 'a> { - let &Rule { head: (ref p0, ref p1), ref clauses } = rule; - let iter = once(QueryTermRef::Term(p0)); - - let inner_iter : Box>> = 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) -> 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>) + fn take_chunk(&mut self, term: QueryTermRef<'a>, mut result: Vec>) + -> (usize, usize, Vec>) { - 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>); + // the chunk number, last term arity, and vector of references. + type Item = (usize, usize, Vec>); fn next(&mut self) -> Option { - 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())) } } diff --git a/src/prolog/machine.rs b/src/prolog/machine.rs index 2f42b631..04077fe2 100644 --- a/src/prolog/machine.rs +++ b/src/prolog/machine.rs @@ -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, 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; - pub struct Machine { ms: MachineState, code: Code, @@ -96,14 +89,7 @@ impl Index 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 index 00000000..557fa871 --- /dev/null +++ b/src/prolog/macros.rs @@ -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) + ) +} diff --git a/src/prolog/mod.rs b/src/prolog/mod.rs index 98634a7d..86a697bd 100644 --- a/src/prolog/mod.rs +++ b/src/prolog/mod.rs @@ -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;