[root]
name = "rusty-wam"
-version = "0.5.7"
+version = "0.5.10"
dependencies = [
"lalrpop 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lalrpop-util 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
[package]
name = "rusty-wam"
-version = "0.5.7"
+version = "0.5.10"
authors = ["Mark Thom"]
build = "build.rs"
Pure Prolog is implemented as a simple REPL. "Pure Prolog" is Prolog
without cut, meta- or extra-logical operators, or side effects of any
kind. In terms of the tutorial pacing, the work has progressed to the
-to the end of section 5.7, skipping past 5.4. Atoms and lists are the
-only two data types currently supported.
+end of section 5.9, skipping past 5.4. Atoms and lists are the only
+two data types currently supported.
While proper environment trimming code is emitted by the code
generator, it has no effect on the bytecode WAM, which lacks
## Tutorial
To enter a multi-clause predicate, the brackets ":{" and "}:" are used
-as delimiters. They must be entirely contained with their own lines.
+as delimiters. They must be contained entirely within their own lines.
For example,
```
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();
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]
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);
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).");
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);
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]
fn prolog_repl() {
let mut wam = Machine::new();
-
+
loop {
print!("prolog> ");
let result = eval(&mut wam, buffer.trim());
print(&mut wam, result);
-
+
wam.reset();
}
}
use std::cell::Cell;
+use std::cmp::Ordering;
use std::collections::HashMap;
use std::ops::{Add, AddAssign};
use std::vec::Vec;
Deep, Shallow
}
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum RegType {
Perm(usize),
Temp(usize)
pub fn is_temp(self) -> bool {
!self.norm().is_perm()
}
-
+
pub fn root_register(self) -> usize {
match self {
VarReg::ArgAndNorm(_, root) => root,
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)
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)
pub type Code = Vec<Line>;
+
#[derive(Clone, PartialEq)]
pub enum Addr {
Con(Constant),
_ => None
}
}
+
+ pub fn is_protected(&self, e: usize) -> bool {
+ match self {
+ &Addr::StackCell(fr, _) if fr > e => false,
+ _ => true
+ }
+ }
}
impl From<Ref> for Addr {
}
}
+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),
}
pub struct CodeGenerator<'a> {
- marker: TermMarker<'a>
+ marker: TermMarker<'a>,
+ var_count: HashMap<&'a Var, usize>
}
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
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
true
}
- #[allow(dead_code)]
fn void_subterms<Target>(subterms: usize) -> Target
where Target: CompilationTarget<'a>
{
{
let iter = Target::iter(term);
let mut target = Vec::new();
- let var_count = Self::count_vars(term);
for term in iter {
match term {
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;
}
for &(term_status, _) in vs.values() {
if let VarStatus::Permanent(i) = term_status {
- if i >= index {
+ if i > index {
var_count += 1;
}
}
var_count
}
+ fn lco(body: &mut Code, rule: &'a Rule) -> usize {
+ let last_arity = rule.last_clause().arity();
+ let mut dealloc_index = body.len() - 1;
+
+ match rule.last_clause() {
+ &Term::Clause(_, ref name, _)
+ | &Term::Constant(_, Constant::Atom(ref name)) => {
+ if let &mut Line::Control(ref mut ctrl) = body.last_mut().unwrap() {
+ *ctrl = ControlInstruction::Execute(name.clone(), last_arity);
+ }
+ },
+ _ => dealloc_index = body.len()
+ };
+
+ dealloc_index
+ }
+
+ fn mark_unsafe_query_vars(head: &Term,
+ vs: &VariableFixtures,
+ query: &mut CompiledQuery)
+ {
+ let mut unsafe_vars = HashMap::new();
+
+ for &(_, ref cb) in vs.values() {
+ if !cb.is_empty() {
+ let index = cb.first().unwrap().get().norm();
+ unsafe_vars.insert(index, false);
+ }
+ }
+
+ for term_ref in head.breadth_first_iter() {
+ match term_ref {
+ TermRef::Var(_, cell, _) => {
+ unsafe_vars.remove(&cell.get().norm());
+ },
+ _ => {}
+ };
+ }
+
+ for query_instr in query.iter_mut() {
+ match query_instr {
+ &mut QueryInstruction::PutValue(RegType::Perm(i), arg) =>
+ if let Some(found) = unsafe_vars.get_mut(&RegType::Perm(i)) {
+ if !*found {
+ *found = true;
+ *query_instr = QueryInstruction::PutUnsafeValue(i, arg);
+ }
+ },
+ &mut QueryInstruction::SetVariable(reg)
+ | &mut QueryInstruction::PutVariable(reg, _) =>
+ if let Some(found) = unsafe_vars.get_mut(®) {
+ *found = true;
+ },
+ &mut QueryInstruction::SetValue(reg) =>
+ if let Some(found) = unsafe_vars.get_mut(®) {
+ if !*found {
+ *found = true;
+ *query_instr = QueryInstruction::SetLocalValue(reg);
+ }
+ },
+ _ => {}
+ };
+ }
+ }
+
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
let vs = Self::mark_perm_vars(&rule);
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
- let perm_vars = Self::vars_above_threshold(&vs, 1);
+ let perm_vars = Self::vars_above_threshold(&vs, 0);
let mut body = Vec::new();
if clauses.len() > 0 {
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
}
+ let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
+ self.update_var_count(iter);
+
self.marker.advance(p0);
body.push(Line::Fact(self.compile_target(p0, false)));
body = clauses.iter().enumerate()
.map(|(i, ref term)| {
- let num_vars = Self::vars_above_threshold(&vs, i+2);
+ let num_vars = Self::vars_above_threshold(&vs, i+1);
self.compile_internal_query(term, num_vars)
})
.fold(body, |mut body, ref mut cqs| {
body
});
- // now perform LCO.
- let last_arity = rule.last_clause().arity();
- let mut dealloc_index = body.len() - 1;
+ if clauses.len() > 0 {
+ let mut index = body.len() - 1;
- match rule.last_clause() {
- &Term::Clause(_, ref name, _)
- | &Term::Constant(_, Constant::Atom(ref name)) => {
- if let &mut Line::Control(ref mut ctrl) = body.last_mut().unwrap() {
- *ctrl = ControlInstruction::Execute(name.clone(), last_arity);
- }
- },
- _ => dealloc_index = body.len()
- };
+ if let &Line::Control(_) = body.last().unwrap() {
+ index -= 1;
+ }
+
+ if let &mut Line::Query(ref mut query) = &mut body[index] {
+ Self::mark_unsafe_query_vars(p0, &vs, query);
+ }
+ }
+
+ let dealloc_index = Self::lco(&mut body, &rule);
if clauses.len() > 0 {
body.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate));
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(®) {
+ if !*found {
+ *found = true;
+ *fact_instr = FactInstruction::UnifyLocalValue(reg);
+ }
+ },
+ &mut FactInstruction::UnifyVariable(reg) => {
+ if let Some(found) = unsafe_vars.get_mut(®) {
+ *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);
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
}
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) =>
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) =>
&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
},
&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(_) => {
_ => self.fail = true
};
},
+
&FactInstruction::GetList(_, reg) => {
let addr = self.deref(self[reg].clone());
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;
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;
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)));
},
&ControlInstruction::Deallocate => {
let e = self.e;
-
+
self.cp = self.and_stack[e].cp;
self.e = self.and_stack[e].e;