optimized up to section 5.10

This commit is contained in:
Mark Thom
2017-03-25 01:58:54 -06:00
parent 24b60eb2c6
commit d65970ee57
8 changed files with 321 additions and 76 deletions

View File

@@ -8,13 +8,13 @@ use prolog::machine::*;
mod tests {
use super::*;
use prolog::ast::*;
fn submit(wam: &mut Machine, buffer: &str) -> EvalResult {
let result = eval(wam, buffer);
wam.reset();
result
}
#[test]
fn test_queries_on_facts() {
let mut wam = Machine::new();
@@ -90,6 +90,13 @@ mod tests {
assert_eq!(submit(&mut wam, "?- p(f(X, g(Y), Z), g(Z), h).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(Z, Y, X).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(f(X, Y, Z), Y, h).").failed_query(), false);
submit(&mut wam, "p(_, f(_, Y, _)) :- h(Y).");
submit(&mut wam, "h(y).");
assert_eq!(submit(&mut wam, "?- p(_, f(_, Y, _)).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(_, f(_, y, _)).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(_, f(_, z, _)).").failed_query(), true);
}
#[test]
@@ -110,7 +117,7 @@ mod tests {
assert_eq!(submit(&mut wam, "?- p(c, d, X).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(a, a, a).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(b, c, d).").failed_query(), true);
submit(&mut wam, "p(X, a). p(X, Y) :- q(Z), p(X, X).");
assert_eq!(submit(&mut wam, "?- p(X, Y).").failed_query(), false);
@@ -137,7 +144,7 @@ mod tests {
assert_eq!(submit(&mut wam, "?- p(a, X).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(b, a).").failed_query(), true);
submit(&mut wam, "p(X, Y, Z) :- q(X), r(Y), s(Z).
submit(&mut wam, "p(X, Y, Z) :- q(X), r(Y), s(Z).
p(a, b, Z) :- q(Z).");
submit(&mut wam, "q(x).");
@@ -154,7 +161,7 @@ mod tests {
submit(&mut wam, "s(x, t).");
submit(&mut wam, "t(y, u).");
assert_eq!(submit(&mut wam, "?- p(X).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(x).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(y).").failed_query(), false);
@@ -173,6 +180,18 @@ mod tests {
assert_eq!(submit(&mut wam, "?- p(X, X, X).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(X, Y, X).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(f(f(X)), h(f(X)), Y).").failed_query(), true);
submit(&mut wam, "p(X) :- f(Y), g(Y), i(X, Y).");
submit(&mut wam, "g(f(a)). g(f(b)). g(f(c)).");
submit(&mut wam, "f(f(a)). f(f(b)). f(f(c)).");
submit(&mut wam, "i(X, X).");
assert_eq!(submit(&mut wam, "?- p(X).").failed_query(), false);
submit(&mut wam, "p(X) :- f(f(Y)), g(Y, f(Y)), i(X, f(Y)).");
submit(&mut wam, "g(Y, f(Y)) :- g(f(Y)).");
assert_eq!(submit(&mut wam, "?- p(X).").failed_query(), false);
}
#[test]
@@ -224,7 +243,7 @@ mod tests {
fn prolog_repl() {
let mut wam = Machine::new();
loop {
print!("prolog> ");
@@ -239,7 +258,7 @@ fn prolog_repl() {
let result = eval(&mut wam, buffer.trim());
print(&mut wam, result);
wam.reset();
}
}

View File

@@ -1,4 +1,5 @@
use std::cell::Cell;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::ops::{Add, AddAssign};
use std::vec::Vec;
@@ -40,7 +41,7 @@ pub enum Level {
Deep, Shallow
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum RegType {
Perm(usize),
Temp(usize)
@@ -83,7 +84,7 @@ impl VarReg {
pub fn is_temp(self) -> bool {
!self.norm().is_perm()
}
pub fn root_register(self) -> usize {
match self {
VarReg::ArgAndNorm(_, root) => root,
@@ -139,8 +140,9 @@ pub enum FactInstruction {
GetList(Level, RegType),
GetStructure(Level, Atom, usize, RegType),
GetValue(RegType, usize),
GetVariable(RegType, usize),
GetVariable(RegType, usize),
UnifyConstant(Constant),
UnifyLocalValue(RegType),
UnifyVariable(RegType),
UnifyValue(RegType),
UnifyVoid(usize)
@@ -150,9 +152,11 @@ pub enum QueryInstruction {
PutConstant(Level, Constant, RegType),
PutList(Level, RegType),
PutStructure(Level, Atom, usize, RegType),
PutUnsafeValue(usize, usize),
PutValue(RegType, usize),
PutVariable(RegType, usize),
SetConstant(Constant),
SetLocalValue(RegType),
SetVariable(RegType),
SetValue(RegType),
SetVoid(usize)
@@ -196,6 +200,7 @@ impl<'a> From<&'a Line> for LineOrCodeOffset<'a> {
pub type Code = Vec<Line>;
#[derive(Clone, PartialEq)]
pub enum Addr {
Con(Constant),
@@ -220,6 +225,13 @@ impl Addr {
_ => None
}
}
pub fn is_protected(&self, e: usize) -> bool {
match self {
&Addr::StackCell(fr, _) if fr > e => false,
_ => true
}
}
}
impl From<Ref> for Addr {
@@ -275,6 +287,26 @@ impl HeapCellValue {
}
}
impl PartialOrd for Ref {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (*self, *other) {
(Ref::HeapCell(hc1), Ref::HeapCell(hc2)) =>
Some(hc1.cmp(&hc2)),
(Ref::HeapCell(_), _) =>
Some(Ordering::Less),
(Ref::StackCell(fr1, sc1), Ref::StackCell(fr2, sc2)) =>
if fr1 < fr2 {
Some(Ordering::Less)
} else if fr1 == fr2 {
Some(sc1.cmp(&sc2))
} else {
Some(Ordering::Greater)
},
_ => Some(Ordering::Greater)
}
}
}
#[derive(Clone, Copy)]
pub enum CodePtr {
DirEntry(usize),

View File

@@ -250,7 +250,8 @@ enum VarStatus {
}
pub struct CodeGenerator<'a> {
marker: TermMarker<'a>
marker: TermMarker<'a>,
var_count: HashMap<&'a Var, usize>
}
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
@@ -258,37 +259,41 @@ type VariableFixtures<'a> = HashMap<&'a Var, VariableFixture<'a>>;
impl<'a> CodeGenerator<'a> {
pub fn new() -> Self {
CodeGenerator { marker: TermMarker::new() }
CodeGenerator { marker: TermMarker::new(),
var_count: HashMap::new() }
}
pub fn vars(&self) -> &HashMap<&Var, VarReg> {
&self.marker.bindings
}
#[allow(dead_code)]
fn count_vars(term: &Term) -> HashMap<&Var, usize> {
fn update_var_count<Iter>(&mut self, iter: Iter)
where Iter : Iterator<Item=TermRef<'a>>
{
let mut var_count = HashMap::new();
for term in term.breadth_first_iter() {
if let TermRef::Var(_, _, ref var) = term {
let entry = var_count.entry(*var).or_insert(0);
for term in iter {
if let TermRef::Var(_, _, var) = term {
if self.marker.contains_var(var) {
var_count.insert(var, 2);
continue;
}
let entry = var_count.entry(var).or_insert(0);
*entry += 1;
}
}
var_count
self.var_count = var_count;
}
#[allow(dead_code)]
fn all_singleton_vars(terms: &Vec<Box<Term>>,
var_count: &HashMap<&Var, usize>)
-> bool
fn all_singleton_vars(&self, terms: &Vec<Box<Term>>) -> bool
{
for term in terms {
match term.as_ref() {
&Term::AnonVar => {},
&Term::Var(ref cell, ref var) if cell.get().is_temp() =>
if var_count.get(var).unwrap() != &1 {
if self.var_count.get(var).unwrap() != &1 {
return false;
},
_ => return false
@@ -298,7 +303,6 @@ impl<'a> CodeGenerator<'a> {
true
}
#[allow(dead_code)]
fn void_subterms<Target>(subterms: usize) -> Target
where Target: CompilationTarget<'a>
{
@@ -416,7 +420,6 @@ impl<'a> CodeGenerator<'a> {
{
let iter = Target::iter(term);
let mut target = Vec::new();
let var_count = Self::count_vars(term);
for term in iter {
match term {
@@ -424,7 +427,7 @@ impl<'a> CodeGenerator<'a> {
target.push(self.to_structure(lvl, cell, atom, terms.len()));
if !has_exposed_vars {
if Self::all_singleton_vars(terms, &var_count) {
if self.all_singleton_vars(terms) {
target.push(Self::void_subterms(terms.len()));
continue;
}
@@ -547,7 +550,7 @@ impl<'a> CodeGenerator<'a> {
for &(term_status, _) in vs.values() {
if let VarStatus::Permanent(i) = term_status {
if i >= index {
if i > index {
var_count += 1;
}
}
@@ -556,36 +559,7 @@ impl<'a> CodeGenerator<'a> {
var_count
}
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
let vs = Self::mark_perm_vars(&rule);
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
let perm_vars = Self::vars_above_threshold(&vs, 1);
let mut body = Vec::new();
if clauses.len() > 0 {
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
}
self.marker.advance(p0);
body.push(Line::Fact(self.compile_target(p0, false)));
self.marker.advance_at_head(p1);
body.push(Line::Query(self.compile_target(p1, false)));
Self::add_conditional_call(&mut body, p1, perm_vars);
body = clauses.iter().enumerate()
.map(|(i, ref term)| {
let num_vars = Self::vars_above_threshold(&vs, i+2);
self.compile_internal_query(term, num_vars)
})
.fold(body, |mut body, ref mut cqs| {
body.append(cqs);
body
});
// now perform LCO.
fn lco(body: &mut Code, rule: &'a Rule) -> usize {
let last_arity = rule.last_clause().arity();
let mut dealloc_index = body.len() - 1;
@@ -599,6 +573,103 @@ impl<'a> CodeGenerator<'a> {
_ => dealloc_index = body.len()
};
dealloc_index
}
fn mark_unsafe_query_vars(head: &Term,
vs: &VariableFixtures,
query: &mut CompiledQuery)
{
let mut unsafe_vars = HashMap::new();
for &(_, ref cb) in vs.values() {
if !cb.is_empty() {
let index = cb.first().unwrap().get().norm();
unsafe_vars.insert(index, false);
}
}
for term_ref in head.breadth_first_iter() {
match term_ref {
TermRef::Var(_, cell, _) => {
unsafe_vars.remove(&cell.get().norm());
},
_ => {}
};
}
for query_instr in query.iter_mut() {
match query_instr {
&mut QueryInstruction::PutValue(RegType::Perm(i), arg) =>
if let Some(found) = unsafe_vars.get_mut(&RegType::Perm(i)) {
if !*found {
*found = true;
*query_instr = QueryInstruction::PutUnsafeValue(i, arg);
}
},
&mut QueryInstruction::SetVariable(reg)
| &mut QueryInstruction::PutVariable(reg, _) =>
if let Some(found) = unsafe_vars.get_mut(&reg) {
*found = true;
},
&mut QueryInstruction::SetValue(reg) =>
if let Some(found) = unsafe_vars.get_mut(&reg) {
if !*found {
*found = true;
*query_instr = QueryInstruction::SetLocalValue(reg);
}
},
_ => {}
};
}
}
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
let vs = Self::mark_perm_vars(&rule);
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
let perm_vars = Self::vars_above_threshold(&vs, 0);
let mut body = Vec::new();
if clauses.len() > 0 {
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
}
let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
self.update_var_count(iter);
self.marker.advance(p0);
body.push(Line::Fact(self.compile_target(p0, false)));
self.marker.advance_at_head(p1);
body.push(Line::Query(self.compile_target(p1, false)));
Self::add_conditional_call(&mut body, p1, perm_vars);
body = clauses.iter().enumerate()
.map(|(i, ref term)| {
let num_vars = Self::vars_above_threshold(&vs, i+1);
self.compile_internal_query(term, num_vars)
})
.fold(body, |mut body, ref mut cqs| {
body.append(cqs);
body
});
if clauses.len() > 0 {
let mut index = body.len() - 1;
if let &Line::Control(_) = body.last().unwrap() {
index -= 1;
}
if let &mut Line::Query(ref mut query) = &mut body[index] {
Self::mark_unsafe_query_vars(p0, &vs, query);
}
}
let dealloc_index = Self::lco(&mut body, &rule);
if clauses.len() > 0 {
body.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate));
}
@@ -606,18 +677,51 @@ impl<'a> CodeGenerator<'a> {
body
}
fn mark_unsafe_fact_vars(fact: &mut CompiledFact, bindings: &HashMap<&Var, VarReg>)
{
let mut unsafe_vars = HashMap::new();
for var_reg in bindings.values() {
unsafe_vars.insert(var_reg.norm(), false);
}
for fact_instr in fact.iter_mut() {
match fact_instr {
&mut FactInstruction::UnifyValue(reg) =>
if let Some(found) = unsafe_vars.get_mut(&reg) {
if !*found {
*found = true;
*fact_instr = FactInstruction::UnifyLocalValue(reg);
}
},
&mut FactInstruction::UnifyVariable(reg) => {
if let Some(found) = unsafe_vars.get_mut(&reg) {
*found = true;
}
},
_ => {}
};
}
}
pub fn compile_fact(&mut self, term: &'a Term) -> Code {
self.marker.advance(term);
self.update_var_count(term.breadth_first_iter());
let mut compiled_fact = vec![Line::Fact(self.compile_target(term, false))];
let mut compiled_fact = self.compile_target(term, false);
Self::mark_unsafe_fact_vars(&mut compiled_fact, self.vars());
let mut compiled_fact = vec![Line::Fact(compiled_fact)];
let proceed = Line::Control(ControlInstruction::Proceed);
compiled_fact.push(proceed);
compiled_fact
}
fn compile_internal_query(&mut self, term: &'a Term, index: usize) -> Code {
fn compile_internal_query(&mut self, term: &'a Term, index: usize) -> Code
{
self.marker.advance(term);
self.update_var_count(term.breadth_first_iter());
let mut compiled_query = vec![Line::Query(self.compile_target(term, false))];
Self::add_conditional_call(&mut compiled_query, term, index);
@@ -627,9 +731,10 @@ impl<'a> CodeGenerator<'a> {
pub fn compile_query(&mut self, term: &'a Term) -> Code {
self.marker.advance(term);
self.update_var_count(term.breadth_first_iter());
let mut compiled_query = vec![Line::Query(self.compile_target(term, true))];
Self::add_conditional_call(&mut compiled_query, term, 1);
Self::add_conditional_call(&mut compiled_query, term, 0);
compiled_query
}

View File

@@ -44,6 +44,8 @@ impl fmt::Display for FactInstruction {
write!(f, "unify_constant {}", constant),
&FactInstruction::UnifyVariable(ref r) =>
write!(f, "unify_variable {}", r),
&FactInstruction::UnifyLocalValue(ref r) =>
write!(f, "unify_local_value {}", r),
&FactInstruction::UnifyValue(ref r) =>
write!(f, "unify_value {}", r),
&FactInstruction::UnifyVoid(n) =>
@@ -67,12 +69,16 @@ impl fmt::Display for QueryInstruction {
write!(f, "put_structure {}/{}, {}", name, arity, r),
&QueryInstruction::PutStructure(Level::Shallow, ref name, ref arity, ref r) =>
write!(f, "put_structure {}/{}, A{}", name, arity, r.reg_num()),
&QueryInstruction::PutUnsafeValue(y, a) =>
write!(f, "put_unsafe_value Y{}, A{}", y, a),
&QueryInstruction::PutValue(ref x, ref a) =>
write!(f, "put_value {}, A{}", x, a),
&QueryInstruction::PutVariable(ref x, ref a) =>
write!(f, "put_variable {}, A{}", x, a),
&QueryInstruction::SetConstant(ref constant) =>
write!(f, "set_constant {}", constant),
&QueryInstruction::SetLocalValue(ref r) =>
write!(f, "set_local_value {}", r),
&QueryInstruction::SetVariable(ref r) =>
write!(f, "set_variable {}", r),
&QueryInstruction::SetValue(ref r) =>
@@ -213,6 +219,7 @@ pub fn eval(wam: &mut Machine, buffer: &str) -> EvalResult
&Ok(TopLevel::Predicate(ref clauses)) => {
if is_consistent(clauses) {
let compiled_pred = cg.compile_predicate(clauses);
print_code(&compiled_pred);
wam.add_predicate(clauses, compiled_pred);
EvalResult::EntrySuccess
@@ -226,16 +233,18 @@ Each predicate must have the same name and arity.";
},
&Ok(TopLevel::Fact(ref fact)) => {
let compiled_fact = cg.compile_fact(&fact);
print_code(&compiled_fact);
wam.add_fact(fact, compiled_fact);
EvalResult::EntrySuccess
},
&Ok(TopLevel::Rule(ref rule)) => {
let compiled_rule = cg.compile_rule(&rule);
print_code(&compiled_rule);
wam.add_rule(rule, compiled_rule);
EvalResult::EntrySuccess
},
&Ok(TopLevel::Query(ref query)) => {
let compiled_query = cg.compile_query(&query);
let compiled_query = cg.compile_query(&query);
wam.run_query(compiled_query, &cg)
},
&Err(_) => {

View File

@@ -463,6 +463,7 @@ impl MachineState {
_ => self.fail = true
};
},
&FactInstruction::GetList(_, reg) => {
let addr = self.deref(self[reg].clone());
@@ -536,10 +537,14 @@ impl MachineState {
let addr = self.deref(Addr::HeapCell(self.s));
match self.store(addr) {
Addr::HeapCell(hc) =>
self.heap[hc] = HeapCellValue::Con(c.clone()),
Addr::StackCell(fr, sc) =>
self.and_stack[fr][sc] = Addr::Con(c.clone()),
Addr::HeapCell(hc) => {
self.heap[hc] = HeapCellValue::Con(c.clone());
self.trail(Ref::HeapCell(hc));
},
Addr::StackCell(fr, sc) => {
self.and_stack[fr][sc] = Addr::Con(c.clone());
self.trail(Ref::StackCell(fr, sc));
},
Addr::Con(c1) => {
if c1 != *c {
self.fail = true;
@@ -569,6 +574,39 @@ impl MachineState {
self.s += 1;
},
&FactInstruction::UnifyLocalValue(reg) => {
let s = self.s;
match self.mode {
MachineMode::Read => {
let reg_addr = self[reg].clone();
self.unify(reg_addr, Addr::HeapCell(s));
},
MachineMode::Write => {
let addr = self.deref(self[reg].clone());
let h = self.h;
if let Addr::HeapCell(hc) = addr {
if hc < h {
let val = self.heap[hc].clone();
self.heap.push(val);
self.h += 1;
self.s += 1;
return;
}
}
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
self.bind(Ref::HeapCell(h), addr);
self.h += 1;
}
};
self.s += 1;
},
&FactInstruction::UnifyValue(reg) => {
let s = self.s;
@@ -615,21 +653,63 @@ impl MachineState {
self[reg] = Addr::Str(self.h);
self.h += 1;
},
&QueryInstruction::PutUnsafeValue(n, arg) => {
let e = self.e;
let addr = self.deref(Addr::StackCell(e, n));
if addr.is_protected(e) {
self.registers[arg] = self.store(addr);
} else {
let h = self.h;
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
self.bind(Ref::HeapCell(h), addr);
self.registers[arg] = self.heap[h].as_addr(h);
self.h += 1;
}
},
&QueryInstruction::PutValue(norm, arg) =>
self.registers[arg] = self[norm].clone(),
&QueryInstruction::PutVariable(norm, arg) => {
let h = self.h;
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
match norm {
RegType::Perm(n) => {
let e = self.e;
self[norm] = Addr::StackCell(e, n);
self.registers[arg] = self[norm].clone();
},
RegType::Temp(_) => {
let h = self.h;
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
self[norm] = Addr::HeapCell(h);
self.registers[arg] = Addr::HeapCell(h);
self[norm] = Addr::HeapCell(h);
self.registers[arg] = Addr::HeapCell(h);
self.h += 1;
self.h += 1;
}
};
},
&QueryInstruction::SetConstant(ref constant) => {
self.heap.push(HeapCellValue::Con(constant.clone()));
self.h += 1;
},
&QueryInstruction::SetLocalValue(reg) => {
let addr = self.deref(self[reg].clone());
let h = self.h;
if let Addr::HeapCell(hc) = addr {
if hc < h {
self.heap.push(HeapCellValue::from(addr));
self.h += 1;
return;
}
}
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
self.bind(Ref::HeapCell(h), addr);
self.h += 1;
},
&QueryInstruction::SetVariable(reg) => {
let h = self.h;
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
@@ -681,7 +761,7 @@ impl MachineState {
},
&ControlInstruction::Deallocate => {
let e = self.e;
self.cp = self.and_stack[e].cp;
self.e = self.and_stack[e].e;