From: Mark Thom Date: Thu, 30 Mar 2017 06:51:23 +0000 (-0600) Subject: optimized up to section 5.11 X-Git-Tag: v0.8.110~749 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=79ae4abb81c31bef5de01b1af0553b80a318d83f;p=scryer-prolog.git optimized up to section 5.11 --- diff --git a/Cargo.toml b/Cargo.toml index 067651e7..ac297e78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusty-wam" -version = "0.5.10" +version = "0.5.11" authors = ["Mark Thom"] build = "build.rs" diff --git a/README.md b/README.md index 816e2949..7ad4cf3c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ pure Prolog. Pure Prolog is implemented as a simple REPL. "Pure Prolog" is Prolog without cut, meta- or extra-logical operators, or side effects of any kind. In terms of the tutorial pacing, the work has progressed to the -end of section 5.9, skipping past 5.4. Atoms and lists are the only +end of section 5.10, skipping past 5.4. Atoms and lists are the only two data types currently supported. While proper environment trimming code is emitted by the code diff --git a/src/main.rs b/src/main.rs index e83ddb73..949abd37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -239,6 +239,154 @@ mod tests { assert_eq!(submit(&mut wam, "?- member([X, Y, Y], [a, [b, c], [b, b], [Z, x], [d, f]]).").failed_query(), true); assert_eq!(submit(&mut wam, "?- member([X, Y, Z], [a, [b, c], [b, b], [Z, x], [d, f]]).").failed_query(), true); } + + #[test] + fn test_queries_on_indexed_predicates() { + let mut wam = Machine::new(); + + submit(&mut wam, "p(a) :- a. + p(b) :- b, f(X). + p(c) :- c, g(X). + p(f(a)) :- a. + p(g(b, c)) :- b. + p(g(b)) :- b. + p([a|b]) :- a. + p([]). + p(X) :- x. + p([c, d, e])."); + + assert_eq!(submit(&mut wam, "?- p(a).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(b).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(c).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(f(a)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(g(b, X)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(g(Y, X)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(g(Y, c)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(g(b)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p([]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c, d, e]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c, d | X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c|X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([Y|X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([Y|[d|Xs]]).").failed_query(), false); + + submit(&mut wam, "a."); + + assert_eq!(submit(&mut wam, "?- p(a).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(b).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(c).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(f(a)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(b, X)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(g(Y, X)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(g(Y, c)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(g(b)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p([]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c, d, e]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c, d | X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c|X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([Y|X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([Y|[d|Xs]]).").failed_query(), false); + + submit(&mut wam, "b."); + submit(&mut wam, "f(x)."); + + assert_eq!(submit(&mut wam, "?- p(a).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(b).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(c).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- p(f(a)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(b, X)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(Y, X)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(Y, c)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(b)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c, d, e]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c, d | X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c|X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([Y|X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([Y|[d|Xs]]).").failed_query(), false); + + submit(&mut wam, "c."); + submit(&mut wam, "g(X)."); + + assert_eq!(submit(&mut wam, "?- p(a).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(b).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(c).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(f(a)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(b, X)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(Y, X)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(Y, c)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(b)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c, d, e]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c, d | X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c|X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([Y|X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([Y|[d|Xs]]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(blah).").failed_query(), true); + + submit(&mut wam, "x."); + + assert_eq!(submit(&mut wam, "?- p(a).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(b).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(c).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(f(a)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(b, X)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(Y, X)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(Y, c)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(g(b)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c, d, e]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c, d | X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([c|X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([Y|X]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p([Y|[d|Xs]]).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- p(blah).").failed_query(), false); + + submit(&mut wam, "call(or(X, Y)) :- call(X). + call(trace) :- trace. + call(or(X, Y)) :- call(Y). + call(notrace) :- notrace. + call(nl) :- nl. + call(X) :- builtin(X). + call(X) :- extern(X). + call(call(X)) :- call(X). + call(repeat). + call(repeat) :- call(repeat). + call(true)."); + + assert_eq!(submit(&mut wam, "?- call(repeat).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(true).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(call(repeat)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(call(true)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(notrace).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- call(nl).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- call(builtin(X)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- call(extern(X)).").failed_query(), true); + + submit(&mut wam, "notrace."); + submit(&mut wam, "nl."); + + assert_eq!(submit(&mut wam, "?- call(repeat).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(true).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(call(repeat)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(call(true)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(notrace).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(nl).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(builtin(X)).").failed_query(), true); + assert_eq!(submit(&mut wam, "?- call(extern(X)).").failed_query(), true); + + submit(&mut wam, "builtin(X)."); + submit(&mut wam, "extern(x)."); + + assert_eq!(submit(&mut wam, "?- call(repeat).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(true).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(call(repeat)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(call(true)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(notrace).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(nl).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(builtin(X)).").failed_query(), false); + assert_eq!(submit(&mut wam, "?- call(extern(X)).").failed_query(), false); + } } fn prolog_repl() { diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index d3540a78..88290795 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -1,6 +1,5 @@ use std::cell::Cell; -use std::cmp::Ordering; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::ops::{Add, AddAssign}; use std::vec::Vec; @@ -21,6 +20,13 @@ impl PredicateClause { } } + pub fn first_arg(&self) -> Option<&Term> { + match self { + &PredicateClause::Fact(ref t) => t.first_arg(), + &PredicateClause::Rule(ref rule) => rule.head.0.first_arg() + } + } + pub fn arity(&self) -> usize { match self { &PredicateClause::Fact(ref t) => t.arity(), @@ -99,7 +105,7 @@ impl Default for VarReg { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Hash, PartialEq, Eq)] pub enum Constant { Atom(Atom), EmptyList @@ -118,7 +124,7 @@ pub struct Rule { pub clauses: Vec } -impl Rule { +impl Rule { pub fn last_clause(&self) -> &Term { match self.clauses.last() { None => &self.head.1, @@ -135,6 +141,54 @@ pub enum TermRef<'a> { Var(Level, &'a Cell, &'a Var) } +pub enum ChoiceInstruction { + RetryMeElse(usize), + TrustMe, + TryMeElse(usize) +} + +pub enum IndexedChoiceInstruction { + Retry(usize), + Trust(usize), + Try(usize) +} + +impl From for Line { + fn from(i: IndexedChoiceInstruction) -> Self { + Line::IndexedChoice(i) + } +} + +impl IndexedChoiceInstruction { + pub fn offset(&self) -> usize { + match self { + &IndexedChoiceInstruction::Retry(offset) => offset, + &IndexedChoiceInstruction::Trust(offset) => offset, + &IndexedChoiceInstruction::Try(offset) => offset + } + } +} + +pub enum ControlInstruction { + Allocate(usize), + Call(Atom, usize, usize), + Deallocate, + Execute(Atom, usize), + Proceed +} + +pub enum IndexingInstruction { + SwitchOnTerm(usize, usize, usize, usize), + SwitchOnConstant(usize, HashMap), + SwitchOnStructure(usize, HashMap<(Atom, usize), usize>) +} + +impl From for Line { + fn from(i: IndexingInstruction) -> Self { + Line::Indexing(i) + } +} + pub enum FactInstruction { GetConstant(Level, Constant, RegType), GetList(Level, RegType), @@ -162,20 +216,6 @@ pub enum QueryInstruction { SetVoid(usize) } -pub enum ChoiceInstruction { - RetryMeElse(usize), - TrustMe, - TryMeElse(usize) -} - -pub enum ControlInstruction { - Allocate(usize), - Call(Atom, usize, usize), - Deallocate, - Execute(Atom, usize), - Proceed -} - pub type CompiledFact = Vec; pub type CompiledQuery = Vec; @@ -184,6 +224,8 @@ pub enum Line { Choice(ChoiceInstruction), Control(ControlInstruction), Fact(CompiledFact), + Indexing(IndexingInstruction), + IndexedChoice(IndexedChoiceInstruction), Query(CompiledQuery) } @@ -198,8 +240,11 @@ impl<'a> From<&'a Line> for LineOrCodeOffset<'a> { } } +pub type ThirdLevelIndex = Vec; + pub type Code = Vec; +pub type CodeDeque = VecDeque; #[derive(Clone, PartialEq)] pub enum Addr { @@ -287,26 +332,6 @@ impl HeapCellValue { } } -impl PartialOrd for Ref { - fn partial_cmp(&self, other: &Self) -> Option { - match (*self, *other) { - (Ref::HeapCell(hc1), Ref::HeapCell(hc2)) => - Some(hc1.cmp(&hc2)), - (Ref::HeapCell(_), _) => - Some(Ordering::Less), - (Ref::StackCell(fr1, sc1), Ref::StackCell(fr2, sc2)) => - if fr1 < fr2 { - Some(Ordering::Less) - } else if fr1 == fr2 { - Some(sc1.cmp(&sc2)) - } else { - Some(Ordering::Greater) - }, - _ => Some(Ordering::Greater) - } - } -} - #[derive(Clone, Copy)] pub enum CodePtr { DirEntry(usize), @@ -344,6 +369,14 @@ pub type Heap = Vec; pub type Registers = Vec; impl Term { + pub fn first_arg(&self) -> Option<&Term> { + match self { + &Term::Clause(_, _, ref terms) => + terms.first().map(|bt| bt.as_ref()), + _ => None + } + } + pub fn is_clause(&self) -> bool { if let &Term::Clause(_, _, _) = self { true @@ -362,7 +395,7 @@ impl Term { pub fn name(&self) -> Option<&Atom> { match self { &Term::Constant(_, Constant::Atom(ref atom)) - | &Term::Clause(_, ref atom, _) => Some(atom), + | &Term::Clause(_, ref atom, _) => Some(atom), _ => None } } diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index bd694ac1..e861f115 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -3,7 +3,8 @@ use prolog::iterators::{FactIterator, QueryIterator}; use std::cell::Cell; use std::cmp::max; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; +use std::hash::Hash; use std::vec::Vec; trait CompilationTarget<'a> { @@ -244,6 +245,239 @@ impl<'a> TermMarker<'a> { } } +#[derive(Clone, Copy)] +enum IntIndex { + External(usize), Fail, Internal(usize) +} + +impl Into for IntIndex { + fn into(self) -> usize { + match self { + IntIndex::Internal(i) => i, + _ => 0 + } + } +} + +struct CodeOffsets { + constants: HashMap, + lists: ThirdLevelIndex, + structures: HashMap<(Atom, usize), ThirdLevelIndex> +} + +impl CodeOffsets { + fn new() -> Self { + CodeOffsets { + constants: HashMap::new(), + lists: Vec::new(), + structures: HashMap::new() + } + } + + fn cap_choice_seq_with_trust(prelude: &mut ThirdLevelIndex) { + prelude.last_mut().map(|instr| { + match instr { + &mut IndexedChoiceInstruction::Retry(i) => + *instr = IndexedChoiceInstruction::Trust(i), + _ => {} + }; + }); + } + + fn add_index(is_first_index: bool, index: usize) -> IndexedChoiceInstruction { + if is_first_index { + IndexedChoiceInstruction::Try(index) + } else { + IndexedChoiceInstruction::Retry(index) + } + } + + fn index_term(&mut self, first_arg: &Term, index: usize) { + match first_arg { + &Term::Clause(_, ref name, ref terms) => { + let code = self.structures.entry((name.clone(), terms.len())) + .or_insert(Vec::new()); + + let is_initial_index = code.is_empty(); + code.push(Self::add_index(is_initial_index, index)); + }, + &Term::Cons(_, _, _) => { + let is_initial_index = self.lists.is_empty(); + self.lists.push(Self::add_index(is_initial_index, index)); + }, + &Term::Constant(_, ref constant) => { + let code = self.constants.entry(constant.clone()) + .or_insert(Vec::new()); + + let is_initial_index = code.is_empty(); + code.push(Self::add_index(is_initial_index, index)); + }, + _ => {} + }; + } + + fn second_level_index(indices: HashMap, + prelude: &mut CodeDeque) + -> HashMap + where Index: Eq + Hash + { + let mut index_locs = HashMap::new(); + + for (key, mut code) in indices.into_iter() { + if code.len() > 1 { + index_locs.insert(key, IntIndex::Internal(prelude.len())); + Self::cap_choice_seq_with_trust(&mut code); + prelude.extend(code.into_iter().map(|code| Line::from(code))); + } else { + code.first().map(|i| { + index_locs.insert(key, IntIndex::External(i.offset())); + }); + } + } + + index_locs + } + + fn no_indices(&self) -> bool { + let no_constants = self.constants.is_empty(); + let no_structures = self.structures.is_empty(); + let no_lists = self.lists.is_empty(); + + no_constants && no_structures && no_lists + } + + fn flatten_index(index: HashMap, len: usize) + -> HashMap + where Index: Eq + Hash + { + let mut flattened_index = HashMap::new(); + + for (key, int_index) in index.into_iter() { + match int_index { + IntIndex::External(offset) => { + flattened_index.insert(key, offset + len + 1); + }, + IntIndex::Internal(offset) => { + flattened_index.insert(key, offset + 1); + }, + _ => {} + }; + } + + flattened_index + } + + fn switch_on_constant(con_ind: HashMap, + prelude: &mut CodeDeque) + -> IntIndex + { + let con_ind = Self::second_level_index(con_ind, prelude); + + if con_ind.len() > 1 { + let index = Self::flatten_index(con_ind, prelude.len()); + let instr = IndexingInstruction::SwitchOnConstant(index.len(), index); + + prelude.push_front(Line::from(instr)); + + IntIndex::Internal(1) + } else { + con_ind.values().next() + .map(|i| *i) + .unwrap_or(IntIndex::Fail) + } + } + + fn switch_on_list(mut lists: ThirdLevelIndex, prelude: &mut CodeDeque) -> IntIndex + { + if lists.len() > 1 { + Self::cap_choice_seq_with_trust(&mut lists); + prelude.extend(lists.into_iter().map(|i| Line::from(i))); + IntIndex::Internal(0) + } else { + lists.first() + .map(|i| IntIndex::External(i.offset())) + .unwrap_or(IntIndex::Fail) + } + } + + fn switch_on_structure(str_ind: HashMap<(Atom, usize), ThirdLevelIndex>, + prelude: &mut CodeDeque) + -> IntIndex + { + let str_ind = Self::second_level_index(str_ind, prelude); + + if str_ind.len() > 1 { + let index = Self::flatten_index(str_ind, prelude.len()); + let instr = IndexingInstruction::SwitchOnStructure(index.len(), index); + + prelude.push_front(Line::from(instr)); + + IntIndex::Internal(1) + } else { + str_ind.values().next() + .map(|i| *i) + .unwrap_or(IntIndex::Fail) + } + } + + fn add_indices(self, code: &mut Code, mut code_body: Code) + { + if self.no_indices() { + *code = code_body; + return; + } + + let mut prelude = VecDeque::new(); + + let lst_step = Self::switch_on_list(self.lists, &mut prelude); + let lst_offset = prelude.len(); + + let str_step = Self::switch_on_structure(self.structures, &mut prelude); + let con_step = Self::switch_on_constant(self.constants, &mut prelude); + + let prelude_length = prelude.len(); + + for (index, line) in prelude.iter_mut().enumerate() { + match line { + &mut Line::IndexedChoice(IndexedChoiceInstruction::Try(ref mut i)) + | &mut Line::IndexedChoice(IndexedChoiceInstruction::Retry(ref mut i)) + | &mut Line::IndexedChoice(IndexedChoiceInstruction::Trust(ref mut i)) => + *i += prelude_length - index, + _ => {} + } + } + + let str_step = match str_step { + IntIndex::External(o) => o + prelude.len() + 1, + IntIndex::Fail => 0, + IntIndex::Internal(_) => match con_step { + IntIndex::Internal(_) => 2, + _ => 1 + } + }; + let con_step = match con_step { + IntIndex::External(offset) => offset + prelude.len() + 1, + IntIndex::Fail => 0, + IntIndex::Internal(offset) => offset, + }; + let lst_step = match lst_step { + IntIndex::External(o) => o + prelude.len() + 1, + IntIndex::Fail => 0, + IntIndex::Internal(_) => prelude.len() - lst_offset + 1 + }; + + let switch_instr = IndexingInstruction::SwitchOnTerm(prelude.len() + 1, + con_step, + lst_step, + str_step); + + prelude.push_front(Line::from(switch_instr)); + + *code = Vec::from(prelude); + code.append(&mut code_body); + } +} + #[derive(Copy, Clone)] enum VarStatus { New, Old, Permanent(usize) @@ -643,7 +877,7 @@ impl<'a> CodeGenerator<'a> { Self::add_conditional_call(&mut body, p1, perm_vars); body = clauses.iter().enumerate() - .map(|(i, ref term)| { + .map(|(i, term)| { let num_vars = Self::vars_above_threshold(&vs, i+1); self.compile_internal_query(term, num_vars) }) @@ -705,13 +939,13 @@ impl<'a> CodeGenerator<'a> { self.update_var_count(term.breadth_first_iter()); let mut code = Vec::new(); - + if term.is_clause() { - let mut compiled_fact = self.compile_target(term, false); + let mut compiled_fact = self.compile_target(term, false); Self::mark_unsafe_fact_vars(&mut compiled_fact, self.vars()); code.push(Line::Fact(compiled_fact)); } - + let proceed = Line::Control(ControlInstruction::Proceed); code.push(proceed); @@ -725,11 +959,11 @@ impl<'a> CodeGenerator<'a> { let mut code = Vec::new(); - if term.is_clause() { + if term.is_clause() { let compiled_query = Line::Query(self.compile_target(term, false)); code.push(compiled_query); } - + Self::add_conditional_call(&mut code, term, index); code @@ -741,19 +975,45 @@ impl<'a> CodeGenerator<'a> { let mut code = Vec::new(); - if term.is_clause() { + if term.is_clause() { let compiled_query = Line::Query(self.compile_target(term, false)); code.push(compiled_query); } - + Self::add_conditional_call(&mut code, term, 0); code } - pub fn compile_predicate(&mut self, clauses: &'a Vec) -> Code + fn split_predicate(clauses: &Vec) -> Vec<(usize, usize)> { - let mut code = Vec::new(); + let mut subseqs = Vec::new(); + let mut left_index = 0; + + for (right_index, clause) in clauses.iter().enumerate() { + if let Some(&Term::Var(_, _)) = clause.first_arg() { + if left_index < right_index { + subseqs.push((left_index, right_index)); + } + + subseqs.push((right_index, right_index + 1)); + left_index = right_index + 1; + } + } + + if left_index < clauses.len() { + subseqs.push((left_index, clauses.len())); + } + + subseqs + } + + fn compile_pred_subseq(&mut self, clauses: &'a [PredicateClause]) -> Code + { + let mut code_body = Vec::new(); + let mut code_offsets = CodeOffsets::new(); + + let multi_clause = clauses.len() > 1; for (i, clause) in clauses.iter().enumerate() { self.marker.reset(); @@ -765,14 +1025,50 @@ impl<'a> CodeGenerator<'a> { self.compile_rule(rule) }; - let choice = match i { - 0 => ChoiceInstruction::TryMeElse(clause_code.len() + 1), - _ if i == clauses.len() - 1 => ChoiceInstruction::TrustMe, - _ => ChoiceInstruction::RetryMeElse(clause_code.len() + 1) - }; + if multi_clause { + let choice = match i { + 0 => ChoiceInstruction::TryMeElse(clause_code.len() + 1), + _ if i == clauses.len() - 1 => ChoiceInstruction::TrustMe, + _ => ChoiceInstruction::RetryMeElse(clause_code.len() + 1) + }; + + code_body.push(Line::Choice(choice)); + } + + clause.first_arg().map(|arg| { + let index = code_body.len(); + code_offsets.index_term(arg, index); + }); + + code_body.append(&mut clause_code); + } + + let mut code = Vec::new(); + + code_offsets.add_indices(&mut code, code_body); + code + } + + pub fn compile_predicate(&mut self, clauses: &'a Vec) -> Code + { + let mut code = Vec::new(); + let split_pred = Self::split_predicate(clauses); + let multi_seq = split_pred.len() > 1; + + for &(l, r) in split_pred.iter() { + let mut code_segment = self.compile_pred_subseq(&clauses[l .. r]); + + if multi_seq { + let choice = match l { + 0 => ChoiceInstruction::TryMeElse(code_segment.len() + 1), + _ if r == clauses.len() => ChoiceInstruction::TrustMe, + _ => ChoiceInstruction::RetryMeElse(code_segment.len() + 1) + }; + + code.push(Line::Choice(choice)); + } - code.push(Line::Choice(choice)); - code.append(&mut clause_code); + code.append(&mut code_segment); } code diff --git a/src/prolog/io.rs b/src/prolog/io.rs index 5530c64f..890091c4 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -106,19 +106,45 @@ impl fmt::Display for ControlInstruction { } } -impl fmt::Display for ChoiceInstruction { +impl fmt::Display for IndexedChoiceInstruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + &IndexedChoiceInstruction::Try(offset) => + write!(f, "try {}", offset), + &IndexedChoiceInstruction::Retry(offset) => + write!(f, "retry {}", offset), + &IndexedChoiceInstruction::Trust(offset) => + write!(f, "trust {}", offset) + } + } +} + +impl fmt::Display for ChoiceInstruction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { &ChoiceInstruction::TryMeElse(offset) => write!(f, "try_me_else {}", offset), &ChoiceInstruction::RetryMeElse(offset) => - write!(f, "retry_me_else {}", offset), + write!(f, "retry_me_else {}", offset), &ChoiceInstruction::TrustMe => write!(f, "trust_me") } } } +impl fmt::Display for IndexingInstruction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &IndexingInstruction::SwitchOnTerm(v, c, l, s) => + write!(f, "switch_on_term {}, {}, {}, {}", v, c, l, s), + &IndexingInstruction::SwitchOnConstant(num_cs, _) => + write!(f, "switch_on_constant {}", num_cs), + &IndexingInstruction::SwitchOnStructure(num_ss, _) => + write!(f, "switch_on_structure {}", num_ss) + } + } +} + impl fmt::Display for Level { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -176,6 +202,10 @@ pub fn print_code(code: &Code) { println!("{}", choice), &Line::Control(ref control) => println!("{}", control), + &Line::IndexedChoice(ref choice) => + println!("{}", choice), + &Line::Indexing(ref indexing) => + println!("{}", indexing), &Line::Query(ref query) => for query_instr in query { println!("{}", query_instr); @@ -218,7 +248,7 @@ pub fn eval(wam: &mut Machine, buffer: &str) -> EvalResult match &result { &Ok(TopLevel::Predicate(ref clauses)) => { if is_consistent(clauses) { - let compiled_pred = cg.compile_predicate(clauses); + let compiled_pred = cg.compile_predicate(clauses); wam.add_predicate(clauses, compiled_pred); EvalResult::EntrySuccess @@ -231,17 +261,17 @@ Each predicate must have the same name and arity."; } }, &Ok(TopLevel::Fact(ref fact)) => { - let compiled_fact = cg.compile_fact(&fact); + let compiled_fact = cg.compile_fact(&fact); wam.add_fact(fact, compiled_fact); EvalResult::EntrySuccess }, &Ok(TopLevel::Rule(ref rule)) => { - let compiled_rule = cg.compile_rule(&rule); + let compiled_rule = cg.compile_rule(&rule); wam.add_rule(rule, compiled_rule); EvalResult::EntrySuccess }, &Ok(TopLevel::Query(ref query)) => { - let compiled_query = cg.compile_query(&query); + let compiled_query = cg.compile_query(&query); wam.run_query(compiled_query, &cg) }, &Err(_) => { diff --git a/src/prolog/iterators.rs b/src/prolog/iterators.rs index a33382e1..cf71f4f2 100644 --- a/src/prolog/iterators.rs +++ b/src/prolog/iterators.rs @@ -106,8 +106,8 @@ impl<'a> Iterator for QueryIterator<'a> { }, IteratorState::InitialCons(lvl, cell, head, tail) => { self.push_final_cons(lvl, cell, head, tail); - self.push_subterm(Level::Deep, head); self.push_subterm(Level::Deep, tail); + self.push_subterm(Level::Deep, head); }, IteratorState::FinalCons(lvl, cell, head, tail) => return Some(TermRef::Cons(lvl, cell, head, tail)), diff --git a/src/prolog/machine.rs b/src/prolog/machine.rs index 25da3fb0..0563401d 100644 --- a/src/prolog/machine.rs +++ b/src/prolog/machine.rs @@ -110,7 +110,7 @@ impl Machine { self.code_dir.insert((name, arity), p); } - fn execute_instr<'a>(&mut self, instr_src: LineOrCodeOffset<'a>) -> bool + fn execute_instr<'b>(&mut self, instr_src: LineOrCodeOffset<'b>) -> bool { let mut instr = match instr_src { LineOrCodeOffset::Instruction(instr) => instr, @@ -121,6 +121,8 @@ impl Machine { match instr { &Line::Choice(ref choice_instr) => self.ms.execute_choice_instr(choice_instr), + &Line::Control(ref control_instr) => + self.ms.execute_ctrl_instr(&self.code_dir, control_instr), &Line::Fact(ref fact) => { for fact_instr in fact { if self.failed() { @@ -130,7 +132,11 @@ impl Machine { self.ms.execute_fact_instr(&fact_instr); } self.ms.p += 1; - }, + }, + &Line::Indexing(ref indexing_instr) => + self.ms.execute_indexing_instr(&indexing_instr), + &Line::IndexedChoice(ref choice_instr) => + self.ms.execute_indexed_choice_instr(choice_instr), &Line::Query(ref query) => { for query_instr in query { if self.failed() { @@ -140,9 +146,7 @@ impl Machine { self.ms.execute_query_instr(&query_instr); } self.ms.p += 1; - }, - &Line::Control(ref control_instr) => - self.ms.execute_ctrl_instr(&self.code_dir, control_instr), + } } if self.failed() { @@ -556,6 +560,8 @@ impl MachineState { self.h += 1; } }; + + self.s += 1; }, &FactInstruction::UnifyVariable(reg) => { match self.mode { @@ -640,6 +646,69 @@ impl MachineState { }; } + fn execute_indexing_instr(&mut self, instr: &IndexingInstruction) { + match instr { + &IndexingInstruction::SwitchOnTerm(v, c, l, s) => { + let a1 = self.registers[1].clone(); + let addr = self.store(self.deref(a1)); + + let offset = match addr { + Addr::HeapCell(_) | Addr::StackCell(_, _) => v, + Addr::Con(_) => c, + Addr::Lis(_) => l, + Addr::Str(_) => s + }; + + match offset { + 0 => self.fail = true, + o => self.p += o + }; + }, + &IndexingInstruction::SwitchOnConstant(_, ref hm) => { + let a1 = self.registers[1].clone(); + let addr = self.store(self.deref(a1)); + + let offset = match addr { + Addr::Con(constant) => { + match hm.get(&constant) { + Some(offset) => *offset, + _ => 0 + } + }, + _ => 0 + }; + + match offset { + 0 => self.fail = true, + o => self.p += o, + }; + }, + &IndexingInstruction::SwitchOnStructure(_, ref hm) => { + let a1 = self.registers[1].clone(); + let addr = self.store(self.deref(a1)); + + let offset = match addr { + Addr::Str(s) => { + if let &HeapCellValue::NamedStr(arity, ref name) = &self.heap[s] { + match hm.get(&(name.clone(), arity)) { + Some(offset) => *offset, + _ => 0 + } + } else { + 0 + } + }, + _ => 0 + }; + + match offset { + 0 => self.fail = true, + o => self.p += o + }; + } + }; + } + fn execute_query_instr(&mut self, instr: &QueryInstruction) { match instr { &QueryInstruction::PutConstant(_, ref constant, reg) => @@ -782,9 +851,94 @@ impl MachineState { }; } - fn execute_choice_instr(&mut self, instr: &ChoiceInstruction) + fn execute_indexed_choice_instr(&mut self, instr: &IndexedChoiceInstruction) { match instr { + &IndexedChoiceInstruction::Try(l) => { + let n = self.num_of_args; + let num_frames = self.num_frames(); + + self.or_stack.push(num_frames + 1, + self.e, + self.cp, + self.b, + self.p + 1, + self.tr, + self.h, + self.num_of_args); + + self.b = self.or_stack.len() - 1; + let b = self.b; + + for i in 1 .. n + 1 { + self.or_stack[b][i] = self.registers[i].clone(); + } + + self.hb = self.h; + self.p += l; + }, + &IndexedChoiceInstruction::Retry(l) => { + let b = self.b; + let n = self.or_stack[b].num_args(); + + for i in 1 .. n + 1 { + self.registers[i] = self.or_stack[b][i].clone(); + } + + self.e = self.or_stack[b].e; + self.cp = self.or_stack[b].cp; + + self.or_stack[b].bp = self.p + 1; + + let old_tr = self.or_stack[b].tr; + let curr_tr = self.tr; + + self.unwind_trail(old_tr, curr_tr); + self.tr = self.or_stack[b].tr; + + self.trail.truncate(self.tr); + self.heap.truncate(self.or_stack[b].h); + + self.h = self.or_stack[b].h; + self.hb = self.h; + + self.p += l; + }, + &IndexedChoiceInstruction::Trust(l) => { + let b = self.b; + let n = self.or_stack[b].num_args(); + + for i in 1 .. n + 1 { + self.registers[i] = self.or_stack[b][i].clone(); + } + + self.e = self.or_stack[b].e; + self.cp = self.or_stack[b].cp; + + let old_tr = self.or_stack[b].tr; + let curr_tr = self.tr; + + self.unwind_trail(old_tr, curr_tr); + + self.tr = self.or_stack[b].tr; + self.trail.truncate(self.tr); + + self.h = self.or_stack[b].h; + self.heap.truncate(self.h); + + self.b = self.or_stack[b].b; + + self.or_stack.pop(); + + self.hb = self.h; + self.p += l; + } + }; + } + + fn execute_choice_instr(&mut self, instr: &ChoiceInstruction) + { + match instr { &ChoiceInstruction::TryMeElse(offset) => { let n = self.num_of_args; let num_frames = self.num_frames(); @@ -807,7 +961,7 @@ impl MachineState { self.hb = self.h; self.p += 1; - }, + }, &ChoiceInstruction::RetryMeElse(offset) => { let b = self.b; let n = self.or_stack[b].num_args(); diff --git a/src/prolog/prolog_parser.lalrpop b/src/prolog/prolog_parser.lalrpop index be557c7d..163b28f3 100644 --- a/src/prolog/prolog_parser.lalrpop +++ b/src/prolog/prolog_parser.lalrpop @@ -12,7 +12,7 @@ pub TopLevel: TopLevel = { }; Atom : Atom = { - r"[a-z][a-z0-9_]*" => <>.trim().to_string(), + r"[a-z][A-Za-z0-9_]*" => <>.trim().to_string(), }; BoxedTerm : Box = { @@ -73,5 +73,5 @@ Term : Term = { }; Var : Var = { - r"[A-Z][a-z0-9_]*" => <>.trim().to_string() + r"[A-Z][A-Za-z0-9_]*" => <>.trim().to_string() }; diff --git a/src/prolog/prolog_parser.rs b/src/prolog/prolog_parser.rs index 9d29dbaa..1703c177 100644 --- a/src/prolog/prolog_parser.rs +++ b/src/prolog/prolog_parser.rs @@ -21,8 +21,8 @@ mod __parse__TopLevel { Term_22_5d_22(&'input str), Term_22___22(&'input str), Term_22_7c_22(&'input str), - Termr_23_22_5bA_2dZ_5d_5ba_2dz0_2d9___5d_2a_22_23(&'input str), - Termr_23_22_5ba_2dz_5d_5ba_2dz0_2d9___5d_2a_22_23(&'input str), + Termr_23_22_5bA_2dZ_5d_5bA_2dZa_2dz0_2d9___5d_2a_22_23(&'input str), + Termr_23_22_5ba_2dz_5d_5bA_2dZa_2dz0_2d9___5d_2a_22_23(&'input str), Termerror(__lalrpop_util::ErrorRecovery), Nt_28_22_2c_22_20_3cTerm_3e_29(Term), Nt_28_22_2c_22_20_3cTerm_3e_29_2a(::std::vec::Vec), @@ -719,8 +719,8 @@ mod __parse__TopLevel { r###""]""###, r###""_""###, r###""|""###, - r###"r#"[A-Z][a-z0-9_]*"#"###, - r###"r#"[a-z][a-z0-9_]*"#"###, + r###"r#"[A-Z][A-Za-z0-9_]*"#"###, + r###"r#"[a-z][A-Za-z0-9_]*"#"###, ]; __ACTION[(__state * 14)..].iter().zip(__TERMINAL).filter_map(|(&state, terminal)| { if state == 0 { @@ -822,11 +822,11 @@ mod __parse__TopLevel { _ => unreachable!(), }, 11 => match __lookahead.1 { - (11, __tok0) => __Symbol::Termr_23_22_5bA_2dZ_5d_5ba_2dz0_2d9___5d_2a_22_23(__tok0), + (11, __tok0) => __Symbol::Termr_23_22_5bA_2dZ_5d_5bA_2dZa_2dz0_2d9___5d_2a_22_23(__tok0), _ => unreachable!(), }, 12 => match __lookahead.1 { - (12, __tok0) => __Symbol::Termr_23_22_5ba_2dz_5d_5ba_2dz0_2d9___5d_2a_22_23(__tok0), + (12, __tok0) => __Symbol::Termr_23_22_5ba_2dz_5d_5bA_2dZa_2dz0_2d9___5d_2a_22_23(__tok0), _ => unreachable!(), }, _ => unreachable!(), @@ -1028,8 +1028,8 @@ mod __parse__TopLevel { 7 } 14 => { - // Atom = r#"[a-z][a-z0-9_]*"# => ActionFn(5); - let __sym0 = __pop_Termr_23_22_5ba_2dz_5d_5ba_2dz0_2d9___5d_2a_22_23(__symbols); + // Atom = r#"[a-z][A-Za-z0-9_]*"# => ActionFn(5); + let __sym0 = __pop_Termr_23_22_5ba_2dz_5d_5bA_2dZa_2dz0_2d9___5d_2a_22_23(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); let __nt = super::__action5::<>(input, __sym0); @@ -1333,8 +1333,8 @@ mod __parse__TopLevel { 17 } 39 => { - // Var = r#"[A-Z][a-z0-9_]*"# => ActionFn(23); - let __sym0 = __pop_Termr_23_22_5bA_2dZ_5d_5ba_2dz0_2d9___5d_2a_22_23(__symbols); + // Var = r#"[A-Z][A-Za-z0-9_]*"# => ActionFn(23); + let __sym0 = __pop_Termr_23_22_5bA_2dZ_5d_5bA_2dZa_2dz0_2d9___5d_2a_22_23(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); let __nt = super::__action23::<>(input, __sym0); @@ -1468,23 +1468,23 @@ mod __parse__TopLevel { _ => panic!("symbol type mismatch") } } - fn __pop_Termr_23_22_5bA_2dZ_5d_5ba_2dz0_2d9___5d_2a_22_23< + fn __pop_Termr_23_22_5bA_2dZ_5d_5bA_2dZa_2dz0_2d9___5d_2a_22_23< 'input, >( __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)> ) -> (usize, &'input str, usize) { match __symbols.pop().unwrap() { - (__l, __Symbol::Termr_23_22_5bA_2dZ_5d_5ba_2dz0_2d9___5d_2a_22_23(__v), __r) => (__l, __v, __r), + (__l, __Symbol::Termr_23_22_5bA_2dZ_5d_5bA_2dZa_2dz0_2d9___5d_2a_22_23(__v), __r) => (__l, __v, __r), _ => panic!("symbol type mismatch") } } - fn __pop_Termr_23_22_5ba_2dz_5d_5ba_2dz0_2d9___5d_2a_22_23< + fn __pop_Termr_23_22_5ba_2dz_5d_5bA_2dZa_2dz0_2d9___5d_2a_22_23< 'input, >( __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)> ) -> (usize, &'input str, usize) { match __symbols.pop().unwrap() { - (__l, __Symbol::Termr_23_22_5ba_2dz_5d_5ba_2dz0_2d9___5d_2a_22_23(__v), __r) => (__l, __v, __r), + (__l, __Symbol::Termr_23_22_5ba_2dz_5d_5bA_2dZa_2dz0_2d9___5d_2a_22_23(__v), __r) => (__l, __v, __r), _ => panic!("symbol type mismatch") } } @@ -1845,6 +1845,11 @@ mod __intern_token { __current_state = 16; continue; } + 65 ... 90 => { + __current_match = Some((11, __index + __ch.len_utf8())); + __current_state = 16; + continue; + } 95 => /* '_' */ { __current_match = Some((11, __index + 1)); __current_state = 16; @@ -1897,6 +1902,11 @@ mod __intern_token { __current_state = 18; continue; } + 65 ... 90 => { + __current_match = Some((12, __index + __ch.len_utf8())); + __current_state = 18; + continue; + } 95 => /* '_' */ { __current_match = Some((12, __index + 1)); __current_state = 18; @@ -1952,6 +1962,11 @@ mod __intern_token { __current_state = 16; continue; } + 65 ... 90 => { + __current_match = Some((11, __index + __ch.len_utf8())); + __current_state = 16; + continue; + } 95 => /* '_' */ { __current_match = Some((11, __index + 1)); __current_state = 16; @@ -1983,6 +1998,11 @@ mod __intern_token { __current_state = 18; continue; } + 65 ... 90 => { + __current_match = Some((12, __index + __ch.len_utf8())); + __current_state = 18; + continue; + } 95 => /* '_' */ { __current_match = Some((12, __index + 1)); __current_state = 18;