fix recursive calls to call/N

This commit is contained in:
Mark Thom
2017-07-28 17:02:08 -06:00
parent 8a99b6d3bc
commit 3aa8780086
10 changed files with 955 additions and 2753 deletions

View File

@@ -3,7 +3,6 @@ mod prolog;
use prolog::io::*;
use prolog::machine::*;
use prolog::prolog_parser::*;
#[cfg(test)]
mod tests {
@@ -13,8 +12,8 @@ mod tests {
fn submit(wam: &mut Machine, buffer: &str) -> bool {
wam.reset();
match parse_TopLevel(buffer.trim()) {
Ok(tl) =>
match parse_code(buffer.trim()) {
Some(tl) =>
match eval(wam, &tl) {
EvalSession::InitialQuerySuccess(_, _) |
EvalSession::EntrySuccess |
@@ -22,7 +21,7 @@ mod tests {
true,
_ => false
},
Err(_) => panic!("Bad parse in test case!")
None => panic!("Grammatical error of some kind!")
}
}
@@ -647,19 +646,31 @@ mod tests {
assert_eq!(submit(&mut wam, "?- call_mult(p(X), one)."), true);
assert_eq!(submit(&mut wam, "?- call_mult(p(two), one)."), false);
assert_eq!(submit(&mut wam, "?- call_mult(p(two), two)."), true);
submit(&mut wam, "f(call(f, undefined)). f(undefined).");
submit(&mut wam, "call_var(P) :- P.");
assert_eq!(submit(&mut wam, "?- f(X), call_var(X)."), true);
assert_eq!(submit(&mut wam, "?- f(call(f, Q)), call_var(call(f, Q))."), true);
assert_eq!(submit(&mut wam, "?- call_var(call(undefined, Q))."), false);
assert_eq!(submit(&mut wam, "?- call(call)."), false);
assert_eq!(submit(&mut wam, "?- call(call(call))."), false);
assert_eq!(submit(&mut wam, "?- call(call(call(call)))."), false);
assert_eq!(submit(&mut wam, "?- call(call(call(call(call))))."), false);
assert_eq!(submit(&mut wam, "?- call(call(call(call(call(call)))))."), false);
assert_eq!(submit(&mut wam, "?- call(call(call(call(call(call(p(X)))))))."), true);
}
}
fn process_buffer(wam: &mut Machine, buffer: &str)
{
match parse_TopLevel(buffer.trim()) {
Ok(tl) => {
match parse_code(buffer.trim()) {
Some(tl) => {
let result = eval(wam, &tl);
print(wam, result);
},
Err(_) => {
println!("Grammatical error of some kind!");
}
None => println!("Grammatical error!")
};
}

View File

@@ -1,6 +1,7 @@
use std::cell::Cell;
use std::cmp::Ordering;
use std::collections::{HashMap, VecDeque};
use std::iter::*;
use std::ops::{Add, AddAssign};
use std::vec::Vec;
@@ -57,6 +58,38 @@ pub enum TopLevel {
Rule(Rule)
}
impl TopLevel {
pub fn query_iter_mut<'a>(&'a mut self) -> Box<Iterator<Item=&'a mut QueryTerm> + 'a>
{
let mut iter: Box<Iterator<Item=&'a mut QueryTerm> + 'a> = Box::new(empty());
match self {
&mut TopLevel::Rule(Rule { head: (_, ref mut head), ref mut clauses }) => {
iter = Box::new(once(head));
iter = Box::new(iter.chain(clauses.iter_mut()));
},
&mut TopLevel::Query(ref mut clauses) =>
iter = Box::new(iter.chain(clauses.iter_mut())),
&mut TopLevel::Predicate(ref mut pred_clauses) =>
for pred_clause in pred_clauses.iter_mut() {
match pred_clause {
&mut PredicateClause::Rule(Rule { head: (_, ref mut head),
ref mut clauses })
=>
{
iter = Box::new(once(head));
iter = Box::new(iter.chain(clauses.iter_mut()));
},
_ => {}
}
},
_ => {}
}
iter
}
}
#[derive(Clone, Copy)]
pub enum Level {
Deep, Shallow
@@ -517,3 +550,4 @@ impl Term {
}
}
}

View File

@@ -210,7 +210,6 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
*ctrl = ControlInstruction::ExecuteN(terms.len());
},
_ => dealloc_index = body.len()
};
dealloc_index

View File

@@ -2,6 +2,7 @@ use prolog::ast::*;
use prolog::codegen::*;
use prolog::debray_allocator::*;
use prolog::machine::*;
use prolog::prolog_parser::*;
use termion::raw::IntoRawMode;
use termion::input::TermRead;
@@ -9,6 +10,7 @@ use termion::event::Key;
use std::io::{Write, stdin, stdout};
use std::fmt;
use std::mem::swap;
impl fmt::Display for Constant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -195,6 +197,35 @@ impl fmt::Display for RegType {
}
}
pub fn parse_code(input: &str) -> Option<TopLevel>
{
match parse_TopLevel(input) {
Ok(mut tl) => {
for query in tl.query_iter_mut() {
let cts = match query {
&mut QueryTerm::Term(Term::Clause(_, ref name, ref mut cts)) => {
if name == "call" {
let mut new_cts = Vec::with_capacity(0);
swap(&mut new_cts, cts);
Some(new_cts)
} else {
None
}
},
_ => None
};
if let Some(cts) = cts {
swap(&mut QueryTerm::CallN(cts), query);
}
}
Some(tl)
},
Err(_) => None
}
}
fn is_consistent(predicate: &Vec<PredicateClause>) -> bool {
let name = predicate.first().unwrap().name();
@@ -285,7 +316,7 @@ Each predicate must have the same name and arity.";
let compiled_fact = cg.compile_fact(fact);
wam.add_fact(fact, compiled_fact);
EvalSession::EntrySuccess
},
&TopLevel::Rule(ref rule) => {
@@ -300,7 +331,8 @@ Each predicate must have the same name and arity.";
let mut cg = CodeGenerator::<DebrayAllocator>::new();
let compiled_query = cg.compile_query(query);
wam.submit_query(compiled_query, cg.take_vars())
print_code(&compiled_query);
wam.submit_query(compiled_query, cg.take_vars())
}
}
}

View File

@@ -70,7 +70,7 @@ impl<'a> QueryIterator<'a> {
fn new(term: QueryTermRef<'a>) -> Self {
match term {
QueryTermRef::CallN(child_terms) => {
let state = IteratorState::Clause(0, ClauseType::CallN, child_terms);
let state = IteratorState::Clause(1, ClauseType::CallN, child_terms);
QueryIterator { state_stack: vec![state] }
},
QueryTermRef::Term(term) => Self::from_term(term),
@@ -96,7 +96,9 @@ impl<'a> Iterator for QueryIterator<'a> {
IteratorState::Clause(child_num, ct, child_terms) => {
if child_num == child_terms.len() {
match ct {
ClauseType::CallN | ClauseType::Root =>
ClauseType::CallN =>
self.push_subterm(Level::Shallow, child_terms[0].as_ref()),
ClauseType::Root =>
return None,
ClauseType::Deep(_, _, _) =>
return Some(TermRef::Clause(ct, child_terms))

View File

@@ -15,13 +15,17 @@ enum MachineMode {
Write
}
struct MachineState {
//TODO: probably.. the wrong solution. should integrate deeply with the WAM.
//type SpecialHandler<'a> = fn(&'a mut MachineState, bool, usize);
struct MachineState { //<'a> {
h: usize,
s: usize,
p: CodePtr,
b: usize,
b0: usize,
e: usize,
//special_handlers: HashMap<&'a Atom, SpecialHandler<'a>>,
num_of_args: usize,
cp: CodePtr,
fail: bool,
@@ -99,7 +103,7 @@ impl Machine {
pub fn failed(&self) -> bool {
self.ms.fail
}
pub fn add_fact(&mut self, fact: &Term, mut code: Code) {
if let Some(name) = fact.name() {
let p = self.code.len();
@@ -289,8 +293,8 @@ impl Machine {
pub fn submit_query<'a>(&mut self, code: Code, alloc_locs: AllocVarDict<'a>) -> EvalSession<'a>
{
let mut heap_locs = HashMap::new();
self.cached_query = Some(code);
self.cached_query = Some(code);
self.run_query(&alloc_locs, &mut heap_locs);
if self.failed() {
@@ -977,7 +981,76 @@ impl MachineState {
self.p = CodePtr::DirEntry(compiled_tl_index);
},
None => self.fail = true
};
};
}
fn dispatch_call_n(&mut self,
code_dir: &CodeDir,
name: Atom,
is_call: bool,
arity: &mut usize,
narity: usize)
-> bool
{
if name == "call" {
let new_pred = self.registers[1].clone();
for i in 2 .. *arity + narity {
self.registers[i-1] = self.registers[i].clone();
}
self.registers[*arity + narity - 1] = new_pred;
if *arity + narity - 1 > 0 {
*arity = *arity + narity - 1;
return true;
} else {
self.fail = true;
return false;
}
}
if is_call {
self.try_call_predicate(code_dir, name, *arity + narity - 1);
} else {
self.try_execute_predicate(code_dir, name, *arity + narity - 1);
}
false
}
fn execute_call_n(&mut self, code_dir: &CodeDir, is_call: bool, mut arity: usize)
{
loop {
let addr = self.deref(self.registers[arity].clone());
match self.store(addr) {
Addr::Str(a) => {
let result = self.heap[a].clone();
if let HeapCellValue::NamedStr(narity, name) = result {
for i in (1 .. arity).rev() {
self.registers[i + narity] = self.registers[i].clone();
}
for i in 1 .. narity + 1 {
self.registers[i] = self.heap[a + i].as_addr(a + i);
}
if self.dispatch_call_n(code_dir, name, is_call, &mut arity, narity) {
continue;
}
}
},
Addr::Con(Constant::Atom(name)) =>
if self.dispatch_call_n(code_dir, name, is_call, &mut arity, 0) {
continue;
},
_ => self.fail = true
};
break;
}
}
fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
@@ -987,35 +1060,14 @@ impl MachineState {
let num_frames = self.num_frames();
self.and_stack.push(num_frames + 1, self.e, self.cp, num_cells);
self.e = self.and_stack.len() - 1;
self.p += 1;
},
&ControlInstruction::Call(ref name, arity, _) =>
self.try_call_predicate(code_dir, name.clone(), arity),
&ControlInstruction::CallN(arity) => {
let addr = self.deref(self.registers[arity].clone());
match self.store(addr) {
Addr::Str(a) => {
let result = self.heap[a].clone();
if let HeapCellValue::NamedStr(narity, name) = result {
for i in 1 .. narity + 1 {
self.registers[i + narity] = self.registers[i].clone();
self.registers[i] = self.heap[a + i].as_addr(a + i);
}
self.try_call_predicate(code_dir, name, arity + narity - 1);
} else {
self.fail = true;
}
},
Addr::Con(Constant::Atom(name)) =>
self.try_call_predicate(code_dir, name, arity - 1),
_ => self.fail = true
};
},
&ControlInstruction::CallN(arity) =>
self.execute_call_n(code_dir, true, arity),
&ControlInstruction::Deallocate => {
let e = self.e;
@@ -1025,30 +1077,9 @@ impl MachineState {
self.p += 1;
},
&ControlInstruction::Execute(ref name, arity) =>
self.try_execute_predicate(code_dir, name.clone(), arity),
&ControlInstruction::ExecuteN(arity) => {
let addr = self.deref(self.registers[arity].clone());
match self.store(addr) {
Addr::Str(a) => {
let result = self.heap[a].clone();
if let HeapCellValue::NamedStr(narity, name) = result {
for i in 1 .. narity + 1 {
self.registers[i + narity] = self.registers[i].clone();
self.registers[i] = self.heap[a + i].as_addr(a + i);
}
self.try_execute_predicate(code_dir, name, arity + narity - 1);
} else {
self.fail = true;
}
},
Addr::Con(Constant::Atom(name)) =>
self.try_execute_predicate(code_dir, name, arity - 1),
_ => self.fail = true
};
},
self.try_execute_predicate(code_dir, name.clone(), arity),
&ControlInstruction::ExecuteN(arity) =>
self.execute_call_n(code_dir, false, arity),
&ControlInstruction::Proceed =>
self.p = self.cp,
};

View File

@@ -1,4 +1,5 @@
use prolog::ast::*;
//use prolog::prolog_parser_utils::*;
use std::cell::Cell;
@@ -19,49 +20,8 @@ BoxedTerm : Box<Term> = {
<t:Term> => Box::new(t)
};
Call : QueryTerm = {
"call" "(" <c:Call> <ts: ("," <BoxedTerm>)*> ")" => {
match c {
QueryTerm::CallN(mut terms) => {
let vt = terms.pop().unwrap();
let mut ts = ts;
terms.append(&mut ts);
terms.push(vt);
QueryTerm::CallN(terms)
},
QueryTerm::Term(Term::Clause(cell, atom, mut terms)) => {
let mut ts = ts;
terms.append(&mut ts);
QueryTerm::Term(Term::Clause(cell, atom, terms))
},
_ => c
}
},
"call" "(" <a:Atom> "(" <ts: (<BoxedTerm> ",")*> <t:BoxedTerm> ")"
<tss: ("," <BoxedTerm>)*> ")" => {
let mut ts = ts;
let mut tss = tss;
ts.push(t);
ts.append(&mut tss);
QueryTerm::Term(Term::Clause(Cell::default(), a, ts))
},
"call" "(" <a:Atom> <ts: ("," <BoxedTerm>)*> ")" =>
QueryTerm::Term(Term::Clause(Cell::default(), a, ts)),
"call" "(" <v:Var> <ts: ("," <BoxedTerm>)*> ")" => {
let mut ts = ts;
let bv = Box::new(Term::Var(Cell::default(), v));
ts.push(bv);
QueryTerm::CallN(ts)
}
};
Clause : Term = {
<a:Atom> "(" <ts: (<BoxedTerm> ",")*> <t:BoxedTerm> ")" => {
<a: Atom> "(" <ts: (<BoxedTerm> ",")*> <t:BoxedTerm> ")" => {
let mut ts = ts;
ts.push(t);
Term::Clause(Cell::default(), a, ts)
@@ -109,22 +69,20 @@ Rule : Rule = {
<c:Clause> ":-" <h:QueryTerm> <cs: ("," <QueryTerm>)*> =>
Rule { head: (c, h), clauses: cs },
<a:Atom> ":-" <h:QueryTerm> <cs: ("," <QueryTerm>)*> =>
Rule { head: (Term::Constant(Cell::default(), Constant::Atom(a)),
h),
Rule { head: (Term::Constant(Cell::default(), Constant::Atom(a)), h),
clauses: cs }
};
QueryTerm : QueryTerm = {
<Call> => <>,
"!" => QueryTerm::Cut,
<Var> => QueryTerm::CallN(vec![Box::new(Term::Var(Cell::default(), <>))]),
<Clause> => QueryTerm::Term(<>),
<Clause> => QueryTerm::Term(<>),
<Atom> => QueryTerm::Term(Term::Constant(Cell::default(), Constant::Atom(<>)))
};
Term : Term = {
<Atom> => Term::Constant(Cell::default(), Constant::Atom(<>)),
<Clause> => <>,
<Atom> => Term::Constant(Cell::default(), Constant::Atom(<>)),
<List> => <>,
<Var> => Term::Var(Cell::default(), <>),
"_" => Term::AnonVar

File diff suppressed because it is too large Load Diff