optimized up to chapter 6

This commit is contained in:
Mark Thom
2017-04-09 19:12:36 -06:00
parent d2cf8626a1
commit 19ee43e186
12 changed files with 1132 additions and 726 deletions

View File

@@ -194,6 +194,87 @@ mod tests {
assert_eq!(submit(&mut wam, "?- p(X).").failed_query(), false);
}
#[test]
fn test_queries_on_cuts() {
let mut wam = Machine::new();
// test shallow cuts.
submit(&mut wam, "memberchk(X, [X|_]) :- !.
memberchk(X, [_|Xs]) :- !, memberchk(X, Xs).");
assert_eq!(submit(&mut wam, "?- memberchk(X, [a,b,c]).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- memberchk([X,X], [a,b,c,[d,e],[d,d]]).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- memberchk([X,X], [a,b,c,[D,d],[e,e]]).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- memberchk([X,X], [a,b,c,[e,d],[f,e]]).").failed_query(), true);
assert_eq!(submit(&mut wam, "?- memberchk([X,X,Y], [a,b,c,[e,d],[f,e]]).").failed_query(), true);
assert_eq!(submit(&mut wam, "?- memberchk([X,X,Y], [a,b,c,[e,e,d],[f,e]]).").failed_query(), false);
// test deep cuts.
submit(&mut wam, "commit :- a, !.");
assert_eq!(submit(&mut wam, "?- commit.").failed_query(), true);
submit(&mut wam, "a.");
assert_eq!(submit(&mut wam, "?- commit.").failed_query(), false);
submit(&mut wam, "commit(X) :- a(X), !.");
assert_eq!(submit(&mut wam, "?- commit(X).").failed_query(), true);
submit(&mut wam, "a(x).");
assert_eq!(submit(&mut wam, "?- commit(X).").failed_query(), false);
submit(&mut wam, "a :- b, !, c. a :- d.");
assert_eq!(submit(&mut wam, "?- a.").failed_query(), true);
submit(&mut wam, "b.");
assert_eq!(submit(&mut wam, "?- a.").failed_query(), true);
submit(&mut wam, "d.");
// we've committed to the first clause since the query on b
// succeeds, so we expect failure here.
assert_eq!(submit(&mut wam, "?- a.").failed_query(), true);
submit(&mut wam, "c.");
assert_eq!(submit(&mut wam, "?- a.").failed_query(), false);
submit(&mut wam, "a(X) :- b, !, c(X). a(X) :- d(X).");
assert_eq!(submit(&mut wam, "?- a(X).").failed_query(), true);
submit(&mut wam, "c(c).");
submit(&mut wam, "d(d).");
assert_eq!(submit(&mut wam, "?- a(X).").failed_query(), false);
submit(&mut wam, "b.");
assert_eq!(submit(&mut wam, "?- a(X).").failed_query(), false);
wam.clear();
assert_eq!(submit(&mut wam, "?- c(X).").failed_query(), true);
submit(&mut wam, "a(X) :- b, c(X), !. a(X) :- d(X).");
submit(&mut wam, "b.");
assert_eq!(submit(&mut wam, "?- a(X).").failed_query(), true);
submit(&mut wam, "d(d).");
assert_eq!(submit(&mut wam, "?- a(X).").failed_query(), false);
submit(&mut wam, "c(c).");
assert_eq!(submit(&mut wam, "?- a(X).").failed_query(), false);
}
#[test]
fn test_queries_on_lists() {
let mut wam = Machine::new();

View File

@@ -5,6 +5,7 @@ use std::vec::Vec;
pub struct Frame {
pub global_index: usize,
pub b0: usize,
pub e: usize,
pub cp: CodePtr,
perms: Vec<Addr>
@@ -14,6 +15,7 @@ impl Frame {
fn new(global_index: usize, e: usize, cp: CodePtr, n: usize) -> Self {
Frame {
global_index: global_index,
b0: 0,
e: e,
cp: cp,
perms: vec![Addr::HeapCell(0); n]

View File

@@ -119,13 +119,27 @@ pub enum Term {
Var(Cell<VarReg>, Var)
}
pub struct Rule {
pub head: (Term, Term),
pub clauses: Vec<Term>
pub enum TermOrCut {
Cut,
Term(Term)
}
impl Rule {
pub fn last_clause(&self) -> &Term {
impl TermOrCut {
pub fn arity(&self) -> usize {
match self {
&TermOrCut::Term(ref term) => term.arity(),
_ => 0
}
}
}
pub struct Rule {
pub head: (Term, TermOrCut),
pub clauses: Vec<TermOrCut>
}
impl Rule {
pub fn last_clause(&self) -> &TermOrCut {
match self.clauses.last() {
None => &self.head.1,
Some(clause) => clause
@@ -141,12 +155,22 @@ pub enum TermRef<'a> {
Var(Level, &'a Cell<VarReg>, &'a Var)
}
pub enum ChoiceInstruction {
RetryMeElse(usize),
TrustMe,
pub enum ChoiceInstruction {
RetryMeElse(usize),
TrustMe,
TryMeElse(usize)
}
pub enum Terminal {
Terminal, Non
}
pub enum CutInstruction {
Cut(Terminal),
GetLevel,
NeckCut(Terminal)
}
pub enum IndexedChoiceInstruction {
Retry(usize),
Trust(usize),
@@ -223,6 +247,7 @@ pub type CompiledQuery = Vec<QueryInstruction>;
pub enum Line {
Choice(ChoiceInstruction),
Control(ControlInstruction),
Cut(CutInstruction),
Fact(CompiledFact),
Indexing(IndexingInstruction),
IndexedChoice(IndexedChoiceInstruction),
@@ -373,18 +398,18 @@ impl Term {
match self {
&Term::Clause(_, _, ref terms) =>
terms.first().map(|bt| bt.as_ref()),
_ => None
_ => None
}
}
pub fn is_clause(&self) -> bool {
if let &Term::Clause(_, _, _) = self {
true
} else {
false
}
}
}
pub fn subterms(&self) -> usize {
match self {
&Term::Clause(_, _, ref terms) => terms.len(),

View File

@@ -441,9 +441,9 @@ impl CodeOffsets {
IntIndex::External(o) => o + prelude_len + 1,
IntIndex::Fail => 0,
IntIndex::Internal(_) => prelude_len - lst_offset + 1
}
}
}
fn add_indices(self, code: &mut Code, mut code_body: Code)
{
if self.no_indices() {
@@ -470,11 +470,11 @@ impl CodeOffsets {
_ => {}
}
}
let str_loc = Self::switch_on_str_offset_from(str_loc, prelude.len(), con_loc);
let con_loc = Self::switch_on_con_offset_from(con_loc, prelude.len());
let lst_loc = Self::switch_on_lst_offset_from(lst_loc, prelude.len(), lst_offset);
let switch_instr = IndexingInstruction::SwitchOnTerm(prelude.len() + 1,
con_loc,
lst_loc,
@@ -752,21 +752,40 @@ impl<'a> CodeGenerator<'a> {
}
}
fn mark_perm_vars(rule: &'a Rule) -> VariableFixtures {
fn mark_perm_vars(rule: &'a Rule) -> (VariableFixtures, bool)
{
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
let mut vs = HashMap::new();
let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
Self::mark_vars_in_term(iter, &mut vs, 0);
for (i, term) in clauses.iter().enumerate() {
Self::mark_vars_in_term(term.breadth_first_iter(), &mut vs, i + 1);
let mut vs = HashMap::new();
match p1 {
&TermOrCut::Cut => {
let iter = p0.breadth_first_iter();
Self::mark_vars_in_term(iter, &mut vs, 0);
},
&TermOrCut::Term(ref p1) => {
let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
Self::mark_vars_in_term(iter, &mut vs, 0);
}
}
for (i, term) in clauses.iter().enumerate() {
if let &TermOrCut::Term(ref term) = term {
Self::mark_vars_in_term(term.breadth_first_iter(), &mut vs, i + 1)
}
}
let mut deep_cuts = false;
for term in clauses {
if let &TermOrCut::Cut = term {
deep_cuts = true;
break;
}
}
Self::set_perm_vals(&vs);
vs
(vs, deep_cuts)
}
fn add_conditional_call(compiled_query: &mut Code, term: &Term, pvs: usize)
@@ -787,8 +806,8 @@ impl<'a> CodeGenerator<'a> {
fn vars_above_threshold(vs: &VariableFixtures, index: usize) -> usize {
let mut var_count = 0;
for &(term_status, _) in vs.values() {
if let VarStatus::Permanent(i) = term_status {
for &(var_status, _) in vs.values() {
if let VarStatus::Permanent(i) = var_status {
if i > index {
var_count += 1;
}
@@ -803,8 +822,8 @@ impl<'a> CodeGenerator<'a> {
let mut dealloc_index = body.len() - 1;
match rule.last_clause() {
&Term::Clause(_, ref name, _)
| &Term::Constant(_, Constant::Atom(ref name)) => {
&TermOrCut::Term(Term::Clause(_, ref name, _))
| &TermOrCut::Term(Term::Constant(_, Constant::Atom(ref name))) => {
if let &mut Line::Control(ref mut ctrl) = body.last_mut().unwrap() {
*ctrl = ControlInstruction::Execute(name.clone(), last_arity);
}
@@ -863,8 +882,9 @@ impl<'a> CodeGenerator<'a> {
}
}
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
let vs = Self::mark_perm_vars(&rule);
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code
{
let (vs, deep_cuts) = Self::mark_perm_vars(&rule);
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
let perm_vars = Self::vars_above_threshold(&vs, 0);
@@ -872,23 +892,63 @@ impl<'a> CodeGenerator<'a> {
if clauses.len() > 0 {
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
if deep_cuts {
body.push(Line::Cut(CutInstruction::GetLevel));
}
}
let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
self.update_var_count(iter);
match p1 {
&TermOrCut::Cut => {
let iter = p0.breadth_first_iter();
self.update_var_count(iter);
},
&TermOrCut::Term(ref p1) => {
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)));
if p0.is_clause() {
body.push(Line::Fact(self.compile_target(p0, false)));
}
Self::add_conditional_call(&mut body, p1, perm_vars);
match p1 {
&TermOrCut::Cut => {
let term = if clauses.is_empty() {
Terminal::Terminal
} else {
Terminal::Non
};
body.push(Line::Cut(CutInstruction::NeckCut(term)));
},
&TermOrCut::Term(ref p1) => {
self.marker.advance_at_head(p1);
if p1.is_clause() {
body.push(Line::Query(self.compile_target(p1, false)));
}
Self::add_conditional_call(&mut body, p1, perm_vars);
}
};
body = clauses.iter().enumerate()
.map(|(i, term)| {
let num_vars = Self::vars_above_threshold(&vs, i+1);
self.compile_internal_query(term, num_vars)
match term {
&TermOrCut::Cut if i + 1 < clauses.len() =>
vec![Line::Cut(CutInstruction::Cut(Terminal::Non))],
&TermOrCut::Cut =>
vec![Line::Cut(CutInstruction::Cut(Terminal::Terminal))],
&TermOrCut::Term(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);

View File

@@ -145,6 +145,19 @@ impl fmt::Display for IndexingInstruction {
}
}
impl fmt::Display for CutInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&CutInstruction::Cut(_) =>
write!(f, "cut"),
&CutInstruction::NeckCut(_) =>
write!(f, "neck_cut"),
&CutInstruction::GetLevel =>
write!(f, "get_level")
}
}
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@@ -198,6 +211,8 @@ pub fn print_code(code: &Code) {
for fact_instr in fact {
println!("{}", fact_instr);
},
&Line::Cut(ref cut) =>
println!("{}", cut),
&Line::Choice(ref choice) =>
println!("{}", choice),
&Line::Control(ref control) =>

View File

@@ -19,6 +19,7 @@ struct MachineState {
s: usize,
p: CodePtr,
b: usize,
b0: usize,
e: usize,
num_of_args: usize,
cp: CodePtr,
@@ -82,7 +83,7 @@ impl Machine {
pub fn add_fact(&mut self, fact: &Term, mut code: Code) {
if let Some(name) = fact.name() {
let p = self.code.len();
let p = self.code.len();
let arity = fact.arity();
self.code.append(&mut code);
@@ -121,6 +122,8 @@ impl Machine {
match instr {
&Line::Choice(ref choice_instr) =>
self.ms.execute_choice_instr(choice_instr),
&Line::Cut(ref cut_instr) =>
self.ms.execute_cut_instr(cut_instr),
&Line::Control(ref control_instr) =>
self.ms.execute_ctrl_instr(&self.code_dir, control_instr),
&Line::Fact(ref fact) => {
@@ -132,7 +135,7 @@ 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) =>
@@ -146,21 +149,29 @@ impl Machine {
self.ms.execute_query_instr(&query_instr);
}
self.ms.p += 1;
}
}
}
if self.failed() {
let p = self.ms
.or_stack
.top()
.map(|fr| fr.bp)
.unwrap_or_default();
let b0 = self.ms
.or_stack
.top()
.map(|fr| fr.b0)
.unwrap_or(0);
let p = if self.ms.b > 0 {
let b = self.ms.b - 1;
self.ms.or_stack[b].bp
} else {
CodePtr::TopLevel
};
if let CodePtr::TopLevel = p {
return false;
} else {
self.ms.fail = false;
self.ms.p = p;
self.ms.b0 = b0;
}
}
@@ -267,7 +278,11 @@ impl Machine {
pub fn continue_query(&mut self) -> EvalResult
{
if !self.or_stack_is_empty() {
let b = self.ms.b;
if self.ms.b == 0 {
return EvalResult::QueryFailure;
}
let b = self.ms.b - 1;
self.ms.p = self.ms.or_stack[b].bp;
let succeeded = if let CodePtr::DirEntry(p) = self.ms.p {
@@ -286,6 +301,13 @@ impl Machine {
}
}
#[allow(dead_code)]
pub fn clear(&mut self) {
self.reset();
self.code.clear();
self.code_dir.clear();
}
pub fn reset(&mut self) {
self.ms.reset();
}
@@ -297,6 +319,7 @@ impl MachineState {
s: 0,
p: CodePtr::TopLevel,
b: 0,
b0: 0,
e: 0,
num_of_args: 0,
cp: CodePtr::TopLevel,
@@ -419,7 +442,12 @@ impl MachineState {
Ref::StackCell(fr, _) => {
let fr_gi = self.and_stack[fr].global_index;
let b_gi = if !self.or_stack.is_empty() {
self.or_stack[self.b].global_index
if self.b > 0 {
let b = self.b - 1;
self.or_stack[b].global_index
} else {
0
}
} else {
0
};
@@ -443,6 +471,48 @@ impl MachineState {
}
}
fn tidy_trail(&mut self) {
if self.b == 0 {
return;
}
let b = self.b - 1;
let mut i = self.or_stack[b].tr;
while i < self.tr {
let tr_i = self.trail[i];
let hb = self.hb;
match tr_i {
Ref::HeapCell(tr_i) =>
if tr_i < hb { //|| ((h < tr_i) && tr_i < b) {
i += 1;
} else {
let tr = self.tr;
let val = self.trail[tr - 1];
self.trail[i] = val;
},
Ref::StackCell(fr, _) => {
let b = self.b - 1;
let fr_gi = self.and_stack[fr].global_index;
let b_gi = if !self.or_stack.is_empty() {
self.or_stack[b].global_index
} else {
0
};
if fr_gi < b_gi {
i += 1;
} else {
let tr = self.tr;
let val = self.trail[tr - 1];
self.trail[i] = val;
}
}
};
}
}
fn execute_fact_instr(&mut self, instr: &FactInstruction) {
match instr {
&FactInstruction::GetConstant(_, ref constant, reg) => {
@@ -703,12 +773,12 @@ impl MachineState {
match offset {
0 => self.fail = true,
o => self.p += o
};
o => self.p += o
};
}
};
}
fn execute_query_instr(&mut self, instr: &QueryInstruction) {
match instr {
&QueryInstruction::PutConstant(_, ref constant, reg) =>
@@ -821,6 +891,7 @@ impl MachineState {
Some(compiled_tl_index) => {
self.cp = self.p + 1;
self.num_of_args = arity;
self.b0 = self.b;
self.p = CodePtr::DirEntry(compiled_tl_index);
},
None => self.fail = true
@@ -841,7 +912,8 @@ impl MachineState {
match compiled_tl_index {
Some(compiled_tl_index) => {
self.num_of_args = arity;
self.p = CodePtr::DirEntry(compiled_tl_index);
self.b0 = self.b;
self.p = CodePtr::DirEntry(compiled_tl_index);
},
None => self.fail = true
};
@@ -865,10 +937,11 @@ impl MachineState {
self.p + 1,
self.tr,
self.h,
self.b0,
self.num_of_args);
self.b = self.or_stack.len() - 1;
let b = self.b;
self.b = self.or_stack.len();
let b = self.b - 1;
for i in 1 .. n + 1 {
self.or_stack[b][i] = self.registers[i].clone();
@@ -878,7 +951,7 @@ impl MachineState {
self.p += l;
},
&IndexedChoiceInstruction::Retry(l) => {
let b = self.b;
let b = self.b - 1;
let n = self.or_stack[b].num_args();
for i in 1 .. n + 1 {
@@ -905,7 +978,7 @@ impl MachineState {
self.p += l;
},
&IndexedChoiceInstruction::Trust(l) => {
let b = self.b;
let b = self.b - 1;
let n = self.or_stack[b].num_args();
for i in 1 .. n + 1 {
@@ -935,10 +1008,10 @@ impl MachineState {
}
};
}
fn execute_choice_instr(&mut self, instr: &ChoiceInstruction)
{
match instr {
match instr {
&ChoiceInstruction::TryMeElse(offset) => {
let n = self.num_of_args;
let num_frames = self.num_frames();
@@ -950,10 +1023,11 @@ impl MachineState {
self.p + offset,
self.tr,
self.h,
self.b0,
self.num_of_args);
self.b = self.or_stack.len() - 1;
let b = self.b;
self.b = self.or_stack.len();
let b = self.b - 1;
for i in 1 .. n + 1 {
self.or_stack[b][i] = self.registers[i].clone();
@@ -961,9 +1035,9 @@ impl MachineState {
self.hb = self.h;
self.p += 1;
},
},
&ChoiceInstruction::RetryMeElse(offset) => {
let b = self.b;
let b = self.b - 1;
let n = self.or_stack[b].num_args();
for i in 1 .. n + 1 {
@@ -990,7 +1064,7 @@ impl MachineState {
self.p += 1;
},
&ChoiceInstruction::TrustMe => {
let b = self.b;
let b = self.b - 1;
let n = self.or_stack[b].num_args();
for i in 1 .. n + 1 {
@@ -1021,11 +1095,55 @@ impl MachineState {
}
}
fn execute_cut_instr(&mut self, instr: &CutInstruction) {
match instr {
&CutInstruction::Cut(ref term) => {
let b = self.b;
let e = self.e;
let b0 = self.and_stack[e].b0; // STACK[E+2+1]
if b > b0 {
self.b = b0;
self.tidy_trail();
}
if let &Terminal::Terminal = term {
self.p = CodePtr::TopLevel;
} else {
self.p += 1;
}
},
&CutInstruction::GetLevel => {
let b0 = self.b0;
let e = self.e;
self.and_stack[e].b0 = b0;
self.p += 1;
},
&CutInstruction::NeckCut(ref term) => {
let b = self.b;
let b0 = self.b0;
if b > b0 {
self.b = b0;
self.tidy_trail();
}
if let &Terminal::Terminal = term {
self.p = CodePtr::TopLevel;
} else {
self.p += 1;
}
}
}
}
fn reset(&mut self) {
self.h = 0;
self.hb = 0;
self.e = 0;
self.b = 0;
self.b0 = 0;
self.s = 0;
self.tr = 0;
self.p = CodePtr::TopLevel;
@@ -1039,5 +1157,5 @@ impl MachineState {
self.and_stack.clear();
self.or_stack.clear();
self.registers = vec![Addr::HeapCell(0); 64];
}
}
}

View File

@@ -7,10 +7,11 @@ pub struct Frame {
pub global_index: usize,
pub e: usize,
pub cp: CodePtr,
pub b: usize,
pub b: usize,
pub bp: CodePtr,
pub tr: usize,
pub h: usize,
pub b0: usize,
args: Vec<Addr>
}
@@ -22,6 +23,7 @@ impl Frame {
bp: CodePtr,
tr: usize,
h: usize,
b0: usize,
n: usize)
-> Self
{
@@ -33,6 +35,7 @@ impl Frame {
bp: bp,
tr: tr,
h: h,
b0: b0,
args: vec![Addr::HeapCell(0); n]
}
}
@@ -57,9 +60,10 @@ impl OrStack {
bp: CodePtr,
tr: usize,
h: usize,
b0: usize,
n: usize)
{
self.0.push(Frame::new(global_index, e, cp, b, bp, tr, h, n));
self.0.push(Frame::new(global_index, e, cp, b, bp, tr, h, b0, n));
}
pub fn len(&self) -> usize {

View File

@@ -57,19 +57,25 @@ PredicateClause : PredicateClause = {
};
Rule : Rule = {
<c:Clause> ":-" <h:Term> <cs: ("," <Term>)*> =>
<c:Clause> ":-" <h:TermOrCut> <cs: ("," <TermOrCut>)*> =>
Rule { head: (c, h), clauses: cs },
<a:Atom> ":-" <h:Term> <cs: ("," <Term>)*> =>
Rule { head: (Term::Constant(Cell::default(), Constant::Atom(a)), h),
<a:Atom> ":-" <h:TermOrCut> <cs: ("," <TermOrCut>)*> =>
Rule { head: (Term::Constant(Cell::default(), Constant::Atom(a)),
h),
clauses: cs }
};
TermOrCut : TermOrCut = {
"!" => TermOrCut::Cut,
<Term> => TermOrCut::Term(<>)
};
Term : Term = {
<Atom> => Term::Constant(Cell::default(), Constant::Atom(<>)),
<Clause> => <>,
<List> => <>,
<Var> => Term::Var(Cell::default(), <>),
"_" => Term::AnonVar
"_" => Term::AnonVar
};
Var : Var = {

File diff suppressed because it is too large Load Diff