310 lines
9.8 KiB
Rust
310 lines
9.8 KiB
Rust
use l1::ast::{Addr, Atom, CompiledFact, CompiledQuery,
|
|
FactInstruction, Heap, HeapCellValue, QueryInstruction,
|
|
Registers};
|
|
|
|
use std::collections::HashMap;
|
|
use std::vec::Vec;
|
|
|
|
#[derive(Clone, Copy)]
|
|
enum MachineMode {
|
|
Read,
|
|
Write
|
|
}
|
|
|
|
pub struct Machine {
|
|
h : usize,
|
|
s : usize,
|
|
p : usize,
|
|
code : CompiledFact,
|
|
code_dir : HashMap<(Atom, usize), usize>,
|
|
fail : bool,
|
|
heap : Heap,
|
|
mode : MachineMode,
|
|
registers : Registers
|
|
}
|
|
|
|
impl Machine {
|
|
pub fn new() -> Machine {
|
|
Machine { h : 0,
|
|
s : 0,
|
|
p : 0,
|
|
code : Vec::new(),
|
|
code_dir : HashMap::new(),
|
|
fail : false,
|
|
heap : Vec::with_capacity(256),
|
|
mode : MachineMode::Write,
|
|
registers : vec![HeapCellValue::Ref(0); 32] }
|
|
}
|
|
|
|
pub fn add_fact(&mut self, mut fact: CompiledFact, name: Atom, arity: usize)
|
|
{
|
|
let index = self.code.len();
|
|
|
|
self.code.append(&mut fact);
|
|
self.code_dir.insert((name, arity), index);
|
|
}
|
|
|
|
pub fn failed(&self) -> bool {
|
|
self.fail
|
|
}
|
|
|
|
fn lookup(&self, a: Addr) -> &HeapCellValue {
|
|
match a {
|
|
Addr::HeapCell(hc) => &self.heap[hc],
|
|
Addr::RegNum(reg) => &self.registers[reg]
|
|
}
|
|
}
|
|
|
|
fn deref(&self, a: Addr) -> Addr {
|
|
let mut a = a;
|
|
|
|
loop {
|
|
if let &HeapCellValue::Ref(value) = self.lookup(a) {
|
|
if let Addr::HeapCell(av) = a {
|
|
if value != av {
|
|
a = Addr::HeapCell(value);
|
|
continue;
|
|
}
|
|
} else {
|
|
a = Addr::HeapCell(value);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return a;
|
|
};
|
|
}
|
|
|
|
fn is_unbound(hc: &HeapCellValue, index: usize) -> bool {
|
|
match hc {
|
|
&HeapCellValue::Ref(r) => r == index,
|
|
_ => false
|
|
}
|
|
}
|
|
|
|
//TODO: try to compress this function.
|
|
fn bind(&mut self, a: Addr, val: usize) {
|
|
let mut a = a;
|
|
|
|
loop {
|
|
match a {
|
|
Addr::RegNum(reg) => {
|
|
if let HeapCellValue::Ref(hc) = self.registers[reg] {
|
|
a = Addr::HeapCell(hc);
|
|
} else if Machine::is_unbound(&self.heap[val], val) {
|
|
self.heap[val] = self.registers[reg].clone();
|
|
break;
|
|
} else {
|
|
self.fail = true;
|
|
break;
|
|
}
|
|
},
|
|
Addr::HeapCell(hc) if Machine::is_unbound(&self.heap[hc], hc) => {
|
|
self.heap[hc] = HeapCellValue::Ref(val);
|
|
break;
|
|
},
|
|
Addr::HeapCell(hc) if Machine::is_unbound(&self.heap[val], val) => {
|
|
self.heap[val] = HeapCellValue::Ref(hc);
|
|
break;
|
|
},
|
|
_ => {
|
|
self.fail = true;
|
|
break;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
fn unify(&mut self, a1: Addr, a2: Addr) {
|
|
let mut pdl = vec![a1, a2];
|
|
|
|
self.fail = false;
|
|
|
|
while !(pdl.is_empty() || self.fail) {
|
|
let d1 = self.deref(pdl.pop().unwrap());
|
|
let d2 = self.deref(pdl.pop().unwrap());
|
|
|
|
if d1 != d2 {
|
|
match (self.lookup(d1), self.lookup(d2)) {
|
|
(&HeapCellValue::Ref(hc), _) =>
|
|
self.bind(d2, hc),
|
|
(_, &HeapCellValue::Ref(hc)) =>
|
|
self.bind(d1, hc),
|
|
(&HeapCellValue::Str(a1), &HeapCellValue::Str(a2)) => {
|
|
let r1 = &self.heap[a1];
|
|
let r2 = &self.heap[a2];
|
|
|
|
if let &HeapCellValue::NamedStr(n1, ref f1) = r1 {
|
|
if let &HeapCellValue::NamedStr(n2, ref f2) = r2 {
|
|
if n1 == n2 && *f1 == *f2 {
|
|
for i in 1 .. n1 {
|
|
pdl.push(Addr::HeapCell(a1 + i));
|
|
pdl.push(Addr::HeapCell(a2 + i));
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
self.fail = true;
|
|
},
|
|
_ => self.fail = true,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn execute_fact(&mut self) {
|
|
loop {
|
|
if let &FactInstruction::Proceed = &self.code[self.p] {
|
|
break;
|
|
} else if self.fail {
|
|
break;
|
|
}
|
|
|
|
let fact_instr = self.code[self.p].clone();
|
|
self.execute_fact_instr(fact_instr);
|
|
}
|
|
}
|
|
|
|
pub fn execute_query(&mut self, query: &CompiledQuery) {
|
|
for instr in query {
|
|
self.execute_query_instr(instr);
|
|
|
|
if self.fail {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn execute_query_instr<'a, 'b: 'a>(&'a mut self, instr: &'b QueryInstruction) {
|
|
match instr {
|
|
&QueryInstruction::Call(ref name, arity) => {
|
|
// why is Option<&T> not Deref?!?!?
|
|
// is it because if the value is None, there's nothing to
|
|
// dereference?
|
|
let compiled_fact_index =
|
|
self.code_dir.get(&(name.clone(), arity))
|
|
.map(|index| *index);
|
|
|
|
match compiled_fact_index {
|
|
Some(compiled_fact_index) => {
|
|
self.p = compiled_fact_index;
|
|
self.execute_fact();
|
|
},
|
|
None => self.fail = true,
|
|
};
|
|
}
|
|
&QueryInstruction::PutStructure(_, ref name, arity, reg) => {
|
|
self.heap.push(HeapCellValue::Str(self.h + 1));
|
|
self.heap.push(HeapCellValue::NamedStr(arity, name.clone()));
|
|
|
|
self.registers[reg] = self.heap[self.h].clone();
|
|
|
|
self.h += 2;
|
|
},
|
|
&QueryInstruction::PutValue(arg, norm) =>
|
|
self.registers[arg] = self.registers[norm].clone(),
|
|
&QueryInstruction::PutVariable(arg, norm) => {
|
|
self.heap.push(HeapCellValue::Ref(self.h));
|
|
|
|
self.registers[norm] = self.heap[self.h].clone();
|
|
self.registers[arg] = self.heap[self.h].clone();
|
|
|
|
self.h += 1;
|
|
},
|
|
&QueryInstruction::SetVariable(reg) => {
|
|
self.heap.push(HeapCellValue::Ref(self.h));
|
|
self.registers[reg] = self.heap[self.h].clone();
|
|
|
|
self.h += 1;
|
|
},
|
|
&QueryInstruction::SetValue(reg) => {
|
|
self.heap.push(self.registers[reg].clone());
|
|
self.h += 1;
|
|
},
|
|
}
|
|
}
|
|
|
|
fn execute_fact_instr(&mut self, instr: FactInstruction) {
|
|
match instr {
|
|
FactInstruction::Proceed => return,
|
|
FactInstruction::GetStructure(_, name, arity, reg) => {
|
|
let addr = self.deref(Addr::RegNum(reg));
|
|
|
|
match self.lookup(addr) {
|
|
&HeapCellValue::Str(a) => {
|
|
let result = &self.heap[a];
|
|
|
|
if let &HeapCellValue::NamedStr(named_arity, ref named_str) = result {
|
|
if arity == named_arity && *name == *named_str {
|
|
self.s = a + 1;
|
|
self.mode = MachineMode::Read;
|
|
} else {
|
|
self.fail = true;
|
|
}
|
|
}
|
|
},
|
|
&HeapCellValue::Ref(r) => {
|
|
self.heap.push(HeapCellValue::Str(self.h + 1));
|
|
self.heap.push(HeapCellValue::NamedStr(arity, name));
|
|
|
|
let h = self.h;
|
|
|
|
self.bind(Addr::HeapCell(r), h);
|
|
|
|
self.h += 2;
|
|
self.mode = MachineMode::Write;
|
|
},
|
|
_ => {
|
|
self.fail = true;
|
|
}
|
|
};
|
|
},
|
|
FactInstruction::GetVariable(arg, norm) =>
|
|
self.registers[norm] = self.registers[arg].clone(),
|
|
FactInstruction::GetValue(arg, norm) =>
|
|
self.unify(Addr::RegNum(norm), Addr::RegNum(arg)),
|
|
FactInstruction::UnifyVariable(reg) => {
|
|
match self.mode {
|
|
MachineMode::Read => self.registers[reg] = self.heap[self.s].clone(),
|
|
MachineMode::Write => {
|
|
self.heap.push(HeapCellValue::Ref(self.h));
|
|
self.registers[reg] = self.heap[self.h].clone();
|
|
self.h += 1;
|
|
}
|
|
};
|
|
|
|
self.s += 1;
|
|
},
|
|
FactInstruction::UnifyValue(reg) => {
|
|
let s = self.s;
|
|
|
|
match self.mode {
|
|
MachineMode::Read => self.unify(Addr::RegNum(reg), Addr::HeapCell(s)),
|
|
MachineMode::Write => {
|
|
self.heap.push(self.registers[reg].clone());
|
|
self.h += 1;
|
|
}
|
|
};
|
|
|
|
self.s += 1;
|
|
}
|
|
}
|
|
|
|
self.p += 1;
|
|
}
|
|
|
|
pub fn reset_machine_state(&mut self) {
|
|
self.h = 0;
|
|
self.s = 0;
|
|
self.p = 0;
|
|
|
|
self.fail = false;
|
|
self.heap = Vec::with_capacity(256);
|
|
self.mode = MachineMode::Write;
|
|
self.registers = vec![HeapCellValue::Ref(0); 32];
|
|
}
|
|
}
|