fix recursive calls to call/N
This commit is contained in:
29
src/main.rs
29
src/main.rs
@@ -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!")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -210,7 +210,6 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
||||
*ctrl = ControlInstruction::ExecuteN(terms.len());
|
||||
},
|
||||
_ => dealloc_index = body.len()
|
||||
|
||||
};
|
||||
|
||||
dealloc_index
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user