optimized up to section 5.10
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
|||||||
[root]
|
[root]
|
||||||
name = "rusty-wam"
|
name = "rusty-wam"
|
||||||
version = "0.5.7"
|
version = "0.5.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lalrpop 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"lalrpop-util 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rusty-wam"
|
name = "rusty-wam"
|
||||||
version = "0.5.7"
|
version = "0.5.10"
|
||||||
authors = ["Mark Thom"]
|
authors = ["Mark Thom"]
|
||||||
|
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ pure Prolog.
|
|||||||
Pure Prolog is implemented as a simple REPL. "Pure Prolog" is 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
|
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
|
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
|
end of section 5.9, skipping past 5.4. Atoms and lists are the only
|
||||||
only two data types currently supported.
|
two data types currently supported.
|
||||||
|
|
||||||
While proper environment trimming code is emitted by the code
|
While proper environment trimming code is emitted by the code
|
||||||
generator, it has no effect on the bytecode WAM, which lacks
|
generator, it has no effect on the bytecode WAM, which lacks
|
||||||
@@ -21,7 +21,7 @@ frames.
|
|||||||
|
|
||||||
## Tutorial
|
## Tutorial
|
||||||
To enter a multi-clause predicate, the brackets ":{" and "}:" are used
|
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,
|
For example,
|
||||||
```
|
```
|
||||||
|
|||||||
33
src/main.rs
33
src/main.rs
@@ -8,13 +8,13 @@ use prolog::machine::*;
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use prolog::ast::*;
|
use prolog::ast::*;
|
||||||
|
|
||||||
fn submit(wam: &mut Machine, buffer: &str) -> EvalResult {
|
fn submit(wam: &mut Machine, buffer: &str) -> EvalResult {
|
||||||
let result = eval(wam, buffer);
|
let result = eval(wam, buffer);
|
||||||
wam.reset();
|
wam.reset();
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_queries_on_facts() {
|
fn test_queries_on_facts() {
|
||||||
let mut wam = Machine::new();
|
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(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(Z, Y, X).").failed_query(), false);
|
||||||
assert_eq!(submit(&mut wam, "?- p(f(X, Y, Z), Y, h).").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]
|
#[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(c, d, X).").failed_query(), false);
|
||||||
assert_eq!(submit(&mut wam, "?- p(a, a, a).").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);
|
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).");
|
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(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(a, X).").failed_query(), false);
|
||||||
assert_eq!(submit(&mut wam, "?- p(b, a).").failed_query(), true);
|
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).");
|
p(a, b, Z) :- q(Z).");
|
||||||
|
|
||||||
submit(&mut wam, "q(x).");
|
submit(&mut wam, "q(x).");
|
||||||
@@ -154,7 +161,7 @@ mod tests {
|
|||||||
|
|
||||||
submit(&mut wam, "s(x, t).");
|
submit(&mut wam, "s(x, t).");
|
||||||
submit(&mut wam, "t(y, u).");
|
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(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(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, X, X).").failed_query(), false);
|
||||||
assert_eq!(submit(&mut wam, "?- p(X, Y, 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);
|
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]
|
#[test]
|
||||||
@@ -224,7 +243,7 @@ mod tests {
|
|||||||
|
|
||||||
fn prolog_repl() {
|
fn prolog_repl() {
|
||||||
let mut wam = Machine::new();
|
let mut wam = Machine::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
print!("prolog> ");
|
print!("prolog> ");
|
||||||
|
|
||||||
@@ -239,7 +258,7 @@ fn prolog_repl() {
|
|||||||
|
|
||||||
let result = eval(&mut wam, buffer.trim());
|
let result = eval(&mut wam, buffer.trim());
|
||||||
print(&mut wam, result);
|
print(&mut wam, result);
|
||||||
|
|
||||||
wam.reset();
|
wam.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
@@ -40,7 +41,7 @@ pub enum Level {
|
|||||||
Deep, Shallow
|
Deep, Shallow
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum RegType {
|
pub enum RegType {
|
||||||
Perm(usize),
|
Perm(usize),
|
||||||
Temp(usize)
|
Temp(usize)
|
||||||
@@ -83,7 +84,7 @@ impl VarReg {
|
|||||||
pub fn is_temp(self) -> bool {
|
pub fn is_temp(self) -> bool {
|
||||||
!self.norm().is_perm()
|
!self.norm().is_perm()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root_register(self) -> usize {
|
pub fn root_register(self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
VarReg::ArgAndNorm(_, root) => root,
|
VarReg::ArgAndNorm(_, root) => root,
|
||||||
@@ -139,8 +140,9 @@ pub enum FactInstruction {
|
|||||||
GetList(Level, RegType),
|
GetList(Level, RegType),
|
||||||
GetStructure(Level, Atom, usize, RegType),
|
GetStructure(Level, Atom, usize, RegType),
|
||||||
GetValue(RegType, usize),
|
GetValue(RegType, usize),
|
||||||
GetVariable(RegType, usize),
|
GetVariable(RegType, usize),
|
||||||
UnifyConstant(Constant),
|
UnifyConstant(Constant),
|
||||||
|
UnifyLocalValue(RegType),
|
||||||
UnifyVariable(RegType),
|
UnifyVariable(RegType),
|
||||||
UnifyValue(RegType),
|
UnifyValue(RegType),
|
||||||
UnifyVoid(usize)
|
UnifyVoid(usize)
|
||||||
@@ -150,9 +152,11 @@ pub enum QueryInstruction {
|
|||||||
PutConstant(Level, Constant, RegType),
|
PutConstant(Level, Constant, RegType),
|
||||||
PutList(Level, RegType),
|
PutList(Level, RegType),
|
||||||
PutStructure(Level, Atom, usize, RegType),
|
PutStructure(Level, Atom, usize, RegType),
|
||||||
|
PutUnsafeValue(usize, usize),
|
||||||
PutValue(RegType, usize),
|
PutValue(RegType, usize),
|
||||||
PutVariable(RegType, usize),
|
PutVariable(RegType, usize),
|
||||||
SetConstant(Constant),
|
SetConstant(Constant),
|
||||||
|
SetLocalValue(RegType),
|
||||||
SetVariable(RegType),
|
SetVariable(RegType),
|
||||||
SetValue(RegType),
|
SetValue(RegType),
|
||||||
SetVoid(usize)
|
SetVoid(usize)
|
||||||
@@ -196,6 +200,7 @@ impl<'a> From<&'a Line> for LineOrCodeOffset<'a> {
|
|||||||
|
|
||||||
pub type Code = Vec<Line>;
|
pub type Code = Vec<Line>;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Addr {
|
pub enum Addr {
|
||||||
Con(Constant),
|
Con(Constant),
|
||||||
@@ -220,6 +225,13 @@ impl Addr {
|
|||||||
_ => None
|
_ => 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 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)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum CodePtr {
|
pub enum CodePtr {
|
||||||
DirEntry(usize),
|
DirEntry(usize),
|
||||||
|
|||||||
@@ -250,7 +250,8 @@ enum VarStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct CodeGenerator<'a> {
|
pub struct CodeGenerator<'a> {
|
||||||
marker: TermMarker<'a>
|
marker: TermMarker<'a>,
|
||||||
|
var_count: HashMap<&'a Var, usize>
|
||||||
}
|
}
|
||||||
|
|
||||||
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
|
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
|
||||||
@@ -258,37 +259,41 @@ type VariableFixtures<'a> = HashMap<&'a Var, VariableFixture<'a>>;
|
|||||||
|
|
||||||
impl<'a> CodeGenerator<'a> {
|
impl<'a> CodeGenerator<'a> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
CodeGenerator { marker: TermMarker::new() }
|
CodeGenerator { marker: TermMarker::new(),
|
||||||
|
var_count: HashMap::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vars(&self) -> &HashMap<&Var, VarReg> {
|
pub fn vars(&self) -> &HashMap<&Var, VarReg> {
|
||||||
&self.marker.bindings
|
&self.marker.bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
fn update_var_count<Iter>(&mut self, iter: Iter)
|
||||||
fn count_vars(term: &Term) -> HashMap<&Var, usize> {
|
where Iter : Iterator<Item=TermRef<'a>>
|
||||||
|
{
|
||||||
let mut var_count = HashMap::new();
|
let mut var_count = HashMap::new();
|
||||||
|
|
||||||
for term in term.breadth_first_iter() {
|
for term in iter {
|
||||||
if let TermRef::Var(_, _, ref var) = term {
|
if let TermRef::Var(_, _, var) = term {
|
||||||
let entry = var_count.entry(*var).or_insert(0);
|
if self.marker.contains_var(var) {
|
||||||
|
var_count.insert(var, 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry = var_count.entry(var).or_insert(0);
|
||||||
*entry += 1;
|
*entry += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var_count
|
self.var_count = var_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
fn all_singleton_vars(&self, terms: &Vec<Box<Term>>) -> bool
|
||||||
fn all_singleton_vars(terms: &Vec<Box<Term>>,
|
|
||||||
var_count: &HashMap<&Var, usize>)
|
|
||||||
-> bool
|
|
||||||
{
|
{
|
||||||
for term in terms {
|
for term in terms {
|
||||||
match term.as_ref() {
|
match term.as_ref() {
|
||||||
&Term::AnonVar => {},
|
&Term::AnonVar => {},
|
||||||
&Term::Var(ref cell, ref var) if cell.get().is_temp() =>
|
&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;
|
||||||
},
|
},
|
||||||
_ => return false
|
_ => return false
|
||||||
@@ -298,7 +303,6 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn void_subterms<Target>(subterms: usize) -> Target
|
fn void_subterms<Target>(subterms: usize) -> Target
|
||||||
where Target: CompilationTarget<'a>
|
where Target: CompilationTarget<'a>
|
||||||
{
|
{
|
||||||
@@ -416,7 +420,6 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
{
|
{
|
||||||
let iter = Target::iter(term);
|
let iter = Target::iter(term);
|
||||||
let mut target = Vec::new();
|
let mut target = Vec::new();
|
||||||
let var_count = Self::count_vars(term);
|
|
||||||
|
|
||||||
for term in iter {
|
for term in iter {
|
||||||
match term {
|
match term {
|
||||||
@@ -424,7 +427,7 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
target.push(self.to_structure(lvl, cell, atom, terms.len()));
|
target.push(self.to_structure(lvl, cell, atom, terms.len()));
|
||||||
|
|
||||||
if !has_exposed_vars {
|
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()));
|
target.push(Self::void_subterms(terms.len()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -547,7 +550,7 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
|
|
||||||
for &(term_status, _) in vs.values() {
|
for &(term_status, _) in vs.values() {
|
||||||
if let VarStatus::Permanent(i) = term_status {
|
if let VarStatus::Permanent(i) = term_status {
|
||||||
if i >= index {
|
if i > index {
|
||||||
var_count += 1;
|
var_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -556,36 +559,7 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
var_count
|
var_count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
|
fn lco(body: &mut Code, rule: &'a Rule) -> usize {
|
||||||
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.
|
|
||||||
let last_arity = rule.last_clause().arity();
|
let last_arity = rule.last_clause().arity();
|
||||||
let mut dealloc_index = body.len() - 1;
|
let mut dealloc_index = body.len() - 1;
|
||||||
|
|
||||||
@@ -599,6 +573,103 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
_ => dealloc_index = body.len()
|
_ => 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, 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 {
|
if clauses.len() > 0 {
|
||||||
body.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate));
|
body.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate));
|
||||||
}
|
}
|
||||||
@@ -606,18 +677,51 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
body
|
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 {
|
pub fn compile_fact(&mut self, term: &'a Term) -> Code {
|
||||||
self.marker.advance(term);
|
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);
|
let proceed = Line::Control(ControlInstruction::Proceed);
|
||||||
|
|
||||||
compiled_fact.push(proceed);
|
compiled_fact.push(proceed);
|
||||||
compiled_fact
|
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.marker.advance(term);
|
||||||
|
self.update_var_count(term.breadth_first_iter());
|
||||||
|
|
||||||
let mut compiled_query = vec![Line::Query(self.compile_target(term, false))];
|
let mut compiled_query = vec![Line::Query(self.compile_target(term, false))];
|
||||||
Self::add_conditional_call(&mut compiled_query, term, index);
|
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 {
|
pub fn compile_query(&mut self, term: &'a Term) -> Code {
|
||||||
self.marker.advance(term);
|
self.marker.advance(term);
|
||||||
|
self.update_var_count(term.breadth_first_iter());
|
||||||
|
|
||||||
let mut compiled_query = vec![Line::Query(self.compile_target(term, true))];
|
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
|
compiled_query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ impl fmt::Display for FactInstruction {
|
|||||||
write!(f, "unify_constant {}", constant),
|
write!(f, "unify_constant {}", constant),
|
||||||
&FactInstruction::UnifyVariable(ref r) =>
|
&FactInstruction::UnifyVariable(ref r) =>
|
||||||
write!(f, "unify_variable {}", r),
|
write!(f, "unify_variable {}", r),
|
||||||
|
&FactInstruction::UnifyLocalValue(ref r) =>
|
||||||
|
write!(f, "unify_local_value {}", r),
|
||||||
&FactInstruction::UnifyValue(ref r) =>
|
&FactInstruction::UnifyValue(ref r) =>
|
||||||
write!(f, "unify_value {}", r),
|
write!(f, "unify_value {}", r),
|
||||||
&FactInstruction::UnifyVoid(n) =>
|
&FactInstruction::UnifyVoid(n) =>
|
||||||
@@ -67,12 +69,16 @@ impl fmt::Display for QueryInstruction {
|
|||||||
write!(f, "put_structure {}/{}, {}", name, arity, r),
|
write!(f, "put_structure {}/{}, {}", name, arity, r),
|
||||||
&QueryInstruction::PutStructure(Level::Shallow, ref name, ref arity, ref r) =>
|
&QueryInstruction::PutStructure(Level::Shallow, ref name, ref arity, ref r) =>
|
||||||
write!(f, "put_structure {}/{}, A{}", name, arity, r.reg_num()),
|
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) =>
|
&QueryInstruction::PutValue(ref x, ref a) =>
|
||||||
write!(f, "put_value {}, A{}", x, a),
|
write!(f, "put_value {}, A{}", x, a),
|
||||||
&QueryInstruction::PutVariable(ref x, ref a) =>
|
&QueryInstruction::PutVariable(ref x, ref a) =>
|
||||||
write!(f, "put_variable {}, A{}", x, a),
|
write!(f, "put_variable {}, A{}", x, a),
|
||||||
&QueryInstruction::SetConstant(ref constant) =>
|
&QueryInstruction::SetConstant(ref constant) =>
|
||||||
write!(f, "set_constant {}", constant),
|
write!(f, "set_constant {}", constant),
|
||||||
|
&QueryInstruction::SetLocalValue(ref r) =>
|
||||||
|
write!(f, "set_local_value {}", r),
|
||||||
&QueryInstruction::SetVariable(ref r) =>
|
&QueryInstruction::SetVariable(ref r) =>
|
||||||
write!(f, "set_variable {}", r),
|
write!(f, "set_variable {}", r),
|
||||||
&QueryInstruction::SetValue(ref r) =>
|
&QueryInstruction::SetValue(ref r) =>
|
||||||
@@ -213,6 +219,7 @@ pub fn eval(wam: &mut Machine, buffer: &str) -> EvalResult
|
|||||||
&Ok(TopLevel::Predicate(ref clauses)) => {
|
&Ok(TopLevel::Predicate(ref clauses)) => {
|
||||||
if is_consistent(clauses) {
|
if is_consistent(clauses) {
|
||||||
let compiled_pred = cg.compile_predicate(clauses);
|
let compiled_pred = cg.compile_predicate(clauses);
|
||||||
|
print_code(&compiled_pred);
|
||||||
wam.add_predicate(clauses, compiled_pred);
|
wam.add_predicate(clauses, compiled_pred);
|
||||||
|
|
||||||
EvalResult::EntrySuccess
|
EvalResult::EntrySuccess
|
||||||
@@ -226,16 +233,18 @@ Each predicate must have the same name and arity.";
|
|||||||
},
|
},
|
||||||
&Ok(TopLevel::Fact(ref fact)) => {
|
&Ok(TopLevel::Fact(ref fact)) => {
|
||||||
let compiled_fact = cg.compile_fact(&fact);
|
let compiled_fact = cg.compile_fact(&fact);
|
||||||
|
print_code(&compiled_fact);
|
||||||
wam.add_fact(fact, compiled_fact);
|
wam.add_fact(fact, compiled_fact);
|
||||||
EvalResult::EntrySuccess
|
EvalResult::EntrySuccess
|
||||||
},
|
},
|
||||||
&Ok(TopLevel::Rule(ref rule)) => {
|
&Ok(TopLevel::Rule(ref rule)) => {
|
||||||
let compiled_rule = cg.compile_rule(&rule);
|
let compiled_rule = cg.compile_rule(&rule);
|
||||||
|
print_code(&compiled_rule);
|
||||||
wam.add_rule(rule, compiled_rule);
|
wam.add_rule(rule, compiled_rule);
|
||||||
EvalResult::EntrySuccess
|
EvalResult::EntrySuccess
|
||||||
},
|
},
|
||||||
&Ok(TopLevel::Query(ref query)) => {
|
&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)
|
wam.run_query(compiled_query, &cg)
|
||||||
},
|
},
|
||||||
&Err(_) => {
|
&Err(_) => {
|
||||||
|
|||||||
@@ -463,6 +463,7 @@ impl MachineState {
|
|||||||
_ => self.fail = true
|
_ => self.fail = true
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
&FactInstruction::GetList(_, reg) => {
|
&FactInstruction::GetList(_, reg) => {
|
||||||
let addr = self.deref(self[reg].clone());
|
let addr = self.deref(self[reg].clone());
|
||||||
|
|
||||||
@@ -536,10 +537,14 @@ impl MachineState {
|
|||||||
let addr = self.deref(Addr::HeapCell(self.s));
|
let addr = self.deref(Addr::HeapCell(self.s));
|
||||||
|
|
||||||
match self.store(addr) {
|
match self.store(addr) {
|
||||||
Addr::HeapCell(hc) =>
|
Addr::HeapCell(hc) => {
|
||||||
self.heap[hc] = HeapCellValue::Con(c.clone()),
|
self.heap[hc] = HeapCellValue::Con(c.clone());
|
||||||
Addr::StackCell(fr, sc) =>
|
self.trail(Ref::HeapCell(hc));
|
||||||
self.and_stack[fr][sc] = Addr::Con(c.clone()),
|
},
|
||||||
|
Addr::StackCell(fr, sc) => {
|
||||||
|
self.and_stack[fr][sc] = Addr::Con(c.clone());
|
||||||
|
self.trail(Ref::StackCell(fr, sc));
|
||||||
|
},
|
||||||
Addr::Con(c1) => {
|
Addr::Con(c1) => {
|
||||||
if c1 != *c {
|
if c1 != *c {
|
||||||
self.fail = true;
|
self.fail = true;
|
||||||
@@ -569,6 +574,39 @@ impl MachineState {
|
|||||||
|
|
||||||
self.s += 1;
|
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) => {
|
&FactInstruction::UnifyValue(reg) => {
|
||||||
let s = self.s;
|
let s = self.s;
|
||||||
|
|
||||||
@@ -615,21 +653,63 @@ impl MachineState {
|
|||||||
self[reg] = Addr::Str(self.h);
|
self[reg] = Addr::Str(self.h);
|
||||||
self.h += 1;
|
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) =>
|
&QueryInstruction::PutValue(norm, arg) =>
|
||||||
self.registers[arg] = self[norm].clone(),
|
self.registers[arg] = self[norm].clone(),
|
||||||
&QueryInstruction::PutVariable(norm, arg) => {
|
&QueryInstruction::PutVariable(norm, arg) => {
|
||||||
let h = self.h;
|
match norm {
|
||||||
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
|
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[norm] = Addr::HeapCell(h);
|
||||||
self.registers[arg] = Addr::HeapCell(h);
|
self.registers[arg] = Addr::HeapCell(h);
|
||||||
|
|
||||||
self.h += 1;
|
self.h += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
&QueryInstruction::SetConstant(ref constant) => {
|
&QueryInstruction::SetConstant(ref constant) => {
|
||||||
self.heap.push(HeapCellValue::Con(constant.clone()));
|
self.heap.push(HeapCellValue::Con(constant.clone()));
|
||||||
self.h += 1;
|
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) => {
|
&QueryInstruction::SetVariable(reg) => {
|
||||||
let h = self.h;
|
let h = self.h;
|
||||||
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
|
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
|
||||||
@@ -681,7 +761,7 @@ impl MachineState {
|
|||||||
},
|
},
|
||||||
&ControlInstruction::Deallocate => {
|
&ControlInstruction::Deallocate => {
|
||||||
let e = self.e;
|
let e = self.e;
|
||||||
|
|
||||||
self.cp = self.and_stack[e].cp;
|
self.cp = self.and_stack[e].cp;
|
||||||
self.e = self.and_stack[e].e;
|
self.e = self.and_stack[e].e;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user