clean up support for recursive calls, add support for (and protection of) built-in predicates.

This commit is contained in:
Mark Thom
2017-08-02 17:38:12 -06:00
parent 3aa8780086
commit 315c748c31
6 changed files with 131 additions and 110 deletions

2
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
[root] [root]
name = "rusty-wam" name = "rusty-wam"
version = "0.6.5" version = "0.6.6"
dependencies = [ dependencies = [
"lalrpop 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lalrpop-util 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop-util 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "rusty-wam" name = "rusty-wam"
version = "0.6.5" version = "0.6.6"
authors = ["Mark Thom"] authors = ["Mark Thom"]
build = "build.rs" build = "build.rs"

View File

@@ -292,6 +292,10 @@ impl IndexedChoiceInstruction {
} }
} }
pub enum BuiltInInstruction {
InternalCallN
}
pub enum ControlInstruction { pub enum ControlInstruction {
Allocate(usize), Allocate(usize),
Call(Atom, usize, usize), Call(Atom, usize, usize),
@@ -359,6 +363,7 @@ pub type CompiledFact = Vec<FactInstruction>;
pub type CompiledQuery = Vec<QueryInstruction>; pub type CompiledQuery = Vec<QueryInstruction>;
pub enum Line { pub enum Line {
BuiltIn(BuiltInInstruction),
Choice(ChoiceInstruction), Choice(ChoiceInstruction),
Control(ControlInstruction), Control(ControlInstruction),
Cut(CutInstruction), Cut(CutInstruction),

View File

@@ -14,7 +14,7 @@ pub struct CodeGenerator<'a, TermMarker> {
} }
pub enum EvalSession<'a> { pub enum EvalSession<'a> {
EntryFailure, EntryFailure(String),
EntrySuccess, EntrySuccess,
InitialQuerySuccess(AllocVarDict<'a>, HeapVarDict<'a>), InitialQuerySuccess(AllocVarDict<'a>, HeapVarDict<'a>),
QueryFailure, QueryFailure,

View File

@@ -244,6 +244,7 @@ fn is_consistent(predicate: &Vec<PredicateClause>) -> bool {
pub fn print_code(code: &Code) { pub fn print_code(code: &Code) {
for clause in code { for clause in code {
match clause { match clause {
&Line::BuiltIn(_) => {},
&Line::Fact(ref fact) => &Line::Fact(ref fact) =>
for fact_instr in fact { for fact_instr in fact {
println!("{}", fact_instr); println!("{}", fact_instr);
@@ -300,38 +301,30 @@ pub fn eval<'a, 'b: 'a>(wam: &'a mut Machine, tl: &'b TopLevel) -> EvalSession<'
if is_consistent(clauses) { if is_consistent(clauses) {
let compiled_pred = cg.compile_predicate(clauses); let compiled_pred = cg.compile_predicate(clauses);
wam.add_predicate(clauses, compiled_pred); wam.add_predicate(clauses, compiled_pred)
EvalSession::EntrySuccess
} else { } else {
let msg = r"Error: predicate is inconsistent. let msg = r"Error: predicate is inconsistent.
Each predicate must have the same name and arity."; Each predicate must have the same name and arity.";
println!("{}", msg); EvalSession::EntryFailure(String::from(msg))
EvalSession::EntryFailure
} }
}, },
&TopLevel::Fact(ref fact) => { &TopLevel::Fact(ref fact) => {
let mut cg = CodeGenerator::<DebrayAllocator>::new(); let mut cg = CodeGenerator::<DebrayAllocator>::new();
let compiled_fact = cg.compile_fact(fact); let compiled_fact = cg.compile_fact(fact);
wam.add_fact(fact, compiled_fact); wam.add_fact(fact, compiled_fact)
EvalSession::EntrySuccess
}, },
&TopLevel::Rule(ref rule) => { &TopLevel::Rule(ref rule) => {
let mut cg = CodeGenerator::<DebrayAllocator>::new(); let mut cg = CodeGenerator::<DebrayAllocator>::new();
let compiled_rule = cg.compile_rule(rule); let compiled_rule = cg.compile_rule(rule);
wam.add_rule(rule, compiled_rule); wam.add_rule(rule, compiled_rule)
EvalSession::EntrySuccess
}, },
&TopLevel::Query(ref query) => { &TopLevel::Query(ref query) => {
let mut cg = CodeGenerator::<DebrayAllocator>::new(); let mut cg = CodeGenerator::<DebrayAllocator>::new();
let compiled_query = cg.compile_query(query); let compiled_query = cg.compile_query(query);
print_code(&compiled_query);
wam.submit_query(compiled_query, cg.take_vars()) wam.submit_query(compiled_query, cg.take_vars())
} }
} }
@@ -393,6 +386,7 @@ pub fn print(wam: &mut Machine, result: EvalSession) {
write!(stdout(), ".\n").unwrap(); write!(stdout(), ".\n").unwrap();
}, },
EvalSession::QueryFailure => println!("false."), EvalSession::QueryFailure => println!("false."),
EvalSession::EntryFailure(msg) => println!("{}", msg),
_ => {} _ => {}
}; };
} }

View File

@@ -15,17 +15,13 @@ enum MachineMode {
Write Write
} }
//TODO: probably.. the wrong solution. should integrate deeply with the WAM. struct MachineState {
//type SpecialHandler<'a> = fn(&'a mut MachineState, bool, usize);
struct MachineState { //<'a> {
h: usize, h: usize,
s: usize, s: usize,
p: CodePtr, p: CodePtr,
b: usize, b: usize,
b0: usize, b0: usize,
e: usize, e: usize,
//special_handlers: HashMap<&'a Atom, SpecialHandler<'a>>,
num_of_args: usize, num_of_args: usize,
cp: CodePtr, cp: CodePtr,
fail: bool, fail: bool,
@@ -37,9 +33,18 @@ struct MachineState { //<'a> {
trail: Vec<Ref>, trail: Vec<Ref>,
tr: usize, tr: usize,
hb: usize, hb: usize,
lco: bool
} }
type CodeDir = HashMap<(Atom, usize), usize>; #[derive(Clone, Copy, PartialEq, Eq, Hash)]
enum PredicateKeyType {
BuiltIn,
User
}
type PredicateKey = (Atom, usize); // name, arity, type.
type CodeDir = HashMap<PredicateKey, (PredicateKeyType, usize)>;
pub struct Machine { pub struct Machine {
ms: MachineState, ms: MachineState,
@@ -92,10 +97,19 @@ impl Index<CodePtr> for Machine {
impl Machine { impl Machine {
pub fn new() -> Self { pub fn new() -> Self {
let mut code_dir = HashMap::new();
let code = vec![Line::BuiltIn(BuiltInInstruction::InternalCallN)];
// there are 64 registers in the VM, so call/N is defined for all 0 <= N <= 63
// (an extra register is needed for the predicate name)
for arity in 0 .. 64 {
code_dir.insert((String::from("call"), arity), (PredicateKeyType::BuiltIn, 0));
}
Machine { Machine {
ms: MachineState::new(), ms: MachineState::new(),
code: Vec::new(), code: code,
code_dir: HashMap::new(), code_dir: code_dir,
cached_query: None cached_query: None
} }
} }
@@ -103,8 +117,23 @@ impl Machine {
pub fn failed(&self) -> bool { pub fn failed(&self) -> bool {
self.ms.fail self.ms.fail
} }
pub fn add_fact(&mut self, fact: &Term, mut code: Code) { fn add_user_code<'a>(&mut self, name: Atom, arity: usize, offset: usize) -> EvalSession<'a>
{
match self.code_dir.get(&(name.clone(), arity)) {
Some(&(PredicateKeyType::BuiltIn, _)) =>
return EvalSession::EntryFailure(format!("error: cannot replace built-in predicate {}/{}",
name,
arity)),
_ => {}
};
self.code_dir.insert((name, arity), (PredicateKeyType::User, offset));
EvalSession::EntrySuccess
}
pub fn add_fact<'a>(&mut self, fact: &Term, mut code: Code) -> EvalSession<'a>
{
if let Some(name) = fact.name() { if let Some(name) = fact.name() {
let p = self.code.len(); let p = self.code.len();
@@ -112,11 +141,14 @@ impl Machine {
let arity = fact.arity(); let arity = fact.arity();
self.code.append(&mut code); self.code.append(&mut code);
self.code_dir.insert((name, arity), p); self.add_user_code(name, arity, p)
} else {
EvalSession::EntryFailure(format!("error: the fact has no name."))
} }
} }
pub fn add_rule(&mut self, rule: &Rule, mut code: Code) { pub fn add_rule<'a>(&mut self, rule: &Rule, mut code: Code) -> EvalSession<'a>
{
if let Some(name) = rule.head.0.name() { if let Some(name) = rule.head.0.name() {
let p = self.code.len(); let p = self.code.len();
@@ -124,11 +156,14 @@ impl Machine {
let arity = rule.head.0.arity(); let arity = rule.head.0.arity();
self.code.append(&mut code); self.code.append(&mut code);
self.code_dir.insert((name, arity), p); self.add_user_code(name, arity, p)
} else {
EvalSession::EntryFailure(format!("error: the rule has no name."))
} }
} }
pub fn add_predicate(&mut self, clauses: &Vec<PredicateClause>, mut code: Code) pub fn add_predicate<'a>(&mut self, clauses: &Vec<PredicateClause>, mut code: Code)
-> EvalSession<'a>
{ {
let p = self.code.len(); let p = self.code.len();
@@ -136,7 +171,7 @@ impl Machine {
let name = clauses.first().unwrap().name().clone(); let name = clauses.first().unwrap().name().clone();
self.code.append(&mut code); self.code.append(&mut code);
self.code_dir.insert((name, arity), p); self.add_user_code(name, arity, p)
} }
fn cached_query_size(&self) -> usize { fn cached_query_size(&self) -> usize {
@@ -162,6 +197,8 @@ impl Machine {
}; };
match instr { match instr {
&Line::BuiltIn(ref builtin_instr) =>
self.ms.execute_builtin_instr(&self.code_dir, builtin_instr),
&Line::Choice(ref choice_instr) => &Line::Choice(ref choice_instr) =>
self.ms.execute_choice_instr(choice_instr), self.ms.execute_choice_instr(choice_instr),
&Line::Cut(ref cut_instr) => &Line::Cut(ref cut_instr) =>
@@ -412,7 +449,8 @@ impl MachineState {
registers: vec![Addr::HeapCell(0); 64], registers: vec![Addr::HeapCell(0); 64],
trail: Vec::new(), trail: Vec::new(),
tr: 0, tr: 0,
hb: 0 hb: 0,
lco: false
} }
} }
@@ -955,12 +993,21 @@ impl MachineState {
} }
} }
fn try_call_predicate(&mut self, code_dir: &CodeDir, name: Atom, arity: usize) fn try_call_predicate(&mut self, code_dir: &CodeDir, name: Atom, arity: usize, lco: bool)
{ {
let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| *index); let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1);
match compiled_tl_index { match compiled_tl_index {
Some(compiled_tl_index) if lco => {
self.lco = true;
self.num_of_args = arity;
self.b0 = self.b;
self.p = CodePtr::DirEntry(compiled_tl_index);
},
Some(compiled_tl_index) => { Some(compiled_tl_index) => {
self.lco = false;
self.cp = self.p + 1; self.cp = self.p + 1;
self.num_of_args = arity; self.num_of_args = arity;
self.b0 = self.b; self.b0 = self.b;
@@ -970,89 +1017,57 @@ impl MachineState {
}; };
} }
fn try_execute_predicate(&mut self, code_dir: &CodeDir, name: Atom, arity: usize) fn handle_internal_call_n(&mut self, code_dir: &CodeDir)
{ {
let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| *index); let arity = self.num_of_args + 1;
let lco = self.lco;
let pred = self.registers[1].clone();
match compiled_tl_index { for i in 2 .. arity {
Some(compiled_tl_index) => { self.registers[i-1] = self.registers[i].clone();
self.num_of_args = arity;
self.b0 = self.b;
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 { if arity > 1 {
self.try_call_predicate(code_dir, name, *arity + narity - 1); self.registers[arity - 1] = pred;
self.execute_call_n(code_dir, arity - 1, lco);
} else { } else {
self.try_execute_predicate(code_dir, name, *arity + narity - 1); self.fail = true;
} }
false
} }
fn execute_call_n(&mut self, code_dir: &CodeDir, is_call: bool, mut arity: usize) fn execute_call_n(&mut self, code_dir: &CodeDir, arity: usize, lco: bool)
{ {
loop { let addr = self.deref(self.registers[arity].clone());
let addr = self.deref(self.registers[arity].clone());
match self.store(addr) { let (name, narity) = match self.store(addr) {
Addr::Str(a) => { Addr::Str(a) => {
let result = self.heap[a].clone(); let result = self.heap[a].clone();
if let HeapCellValue::NamedStr(narity, name) = result { if let HeapCellValue::NamedStr(narity, name) = result {
for i in (1 .. arity).rev() { for i in (1 .. arity).rev() {
self.registers[i + narity] = self.registers[i].clone(); 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)) => for i in 1 .. narity + 1 {
if self.dispatch_call_n(code_dir, name, is_call, &mut arity, 0) { self.registers[i] = self.heap[a + i].as_addr(a + i);
continue; }
},
_ => self.fail = true (name, narity)
}; } else {
self.fail = true;
break; return;
} }
},
Addr::Con(Constant::Atom(name)) => (name, 0),
_ => {
self.fail = true;
return;
}
};
self.try_call_predicate(code_dir, name, arity + narity - 1, lco);
} }
fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction) fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
{ {
match instr { match instr {
@@ -1065,9 +1080,9 @@ impl MachineState {
self.p += 1; self.p += 1;
}, },
&ControlInstruction::Call(ref name, arity, _) => &ControlInstruction::Call(ref name, arity, _) =>
self.try_call_predicate(code_dir, name.clone(), arity), self.try_call_predicate(code_dir, name.clone(), arity, false),
&ControlInstruction::CallN(arity) => &ControlInstruction::CallN(arity) =>
self.execute_call_n(code_dir, true, arity), self.execute_call_n(code_dir, arity, false),
&ControlInstruction::Deallocate => { &ControlInstruction::Deallocate => {
let e = self.e; let e = self.e;
@@ -1076,10 +1091,10 @@ impl MachineState {
self.p += 1; self.p += 1;
}, },
&ControlInstruction::Execute(ref name, arity) => &ControlInstruction::Execute(ref name, arity) =>
self.try_execute_predicate(code_dir, name.clone(), arity), self.try_call_predicate(code_dir, name.clone(), arity, true),
&ControlInstruction::ExecuteN(arity) => &ControlInstruction::ExecuteN(arity) =>
self.execute_call_n(code_dir, false, arity), self.execute_call_n(code_dir, arity, true),
&ControlInstruction::Proceed => &ControlInstruction::Proceed =>
self.p = self.cp, self.p = self.cp,
}; };
@@ -1171,6 +1186,13 @@ impl MachineState {
}; };
} }
fn execute_builtin_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction)
{
match instr {
&BuiltInInstruction::InternalCallN => self.handle_internal_call_n(code_dir)
}
}
fn execute_choice_instr(&mut self, instr: &ChoiceInstruction) fn execute_choice_instr(&mut self, instr: &ChoiceInstruction)
{ {
match instr { match instr {