bug fixes.
This commit is contained in:
14
README.md
14
README.md
@@ -16,7 +16,7 @@ argument indexing, and conjunctive queries.
|
|||||||
Extend rusty-wam to include the following, among other features:
|
Extend rusty-wam to include the following, among other features:
|
||||||
|
|
||||||
* call/N as a built-in meta-predicate (_done_).
|
* call/N as a built-in meta-predicate (_done_).
|
||||||
* ISO Prolog compliant throw/catch (_in progress_).
|
* ISO Prolog compliant throw/catch (_done_).
|
||||||
* Built-in and user-defined operators of all fixities,
|
* Built-in and user-defined operators of all fixities,
|
||||||
with custom associativity and precedence.
|
with custom associativity and precedence.
|
||||||
* Bignum and floating point arithmetic.
|
* Bignum and floating point arithmetic.
|
||||||
@@ -42,6 +42,18 @@ IR to get JIT-compiled and -executed Prolog programs.
|
|||||||
It's my hope to use rusty-wam as the logic engine of a low level (and
|
It's my hope to use rusty-wam as the logic engine of a low level (and
|
||||||
ideally, very fast) [Shen](http://shenlanguage.org) implementation.
|
ideally, very fast) [Shen](http://shenlanguage.org) implementation.
|
||||||
|
|
||||||
|
## Built-in predicates
|
||||||
|
|
||||||
|
The following predicates are built-in to rusty-wam.
|
||||||
|
|
||||||
|
* atomic
|
||||||
|
* call/N (0 <= N <= 62)
|
||||||
|
* catch/3
|
||||||
|
* duplicate_term/2
|
||||||
|
* false/0
|
||||||
|
* not/1
|
||||||
|
* var/1
|
||||||
|
|
||||||
## Tutorial
|
## Tutorial
|
||||||
To enter a multi-clause predicate, the brackets ":{" and "}:" are used
|
To enter a multi-clause predicate, the brackets ":{" and "}:" are used
|
||||||
as delimiters. They must be contained entirely within their own lines.
|
as delimiters. They must be contained entirely within their own lines.
|
||||||
|
|||||||
11
src/main.rs
11
src/main.rs
@@ -721,9 +721,16 @@ mod tests {
|
|||||||
submit(&mut wam, "g(g_success). g(g_success_2). g(X) :- throw(X).");
|
submit(&mut wam, "g(g_success). g(g_success_2). g(X) :- throw(X).");
|
||||||
submit(&mut wam, "handle(x). handle(y). handle(z). handle(v) :- throw(X).");
|
submit(&mut wam, "handle(x). handle(y). handle(z). handle(v) :- throw(X).");
|
||||||
|
|
||||||
//TODO: fix this test. record the ball properly. currently it
|
|
||||||
// is unwound when the heap is truncated.
|
|
||||||
assert_eq!(submit(&mut wam, "?- catch(f(X), E, E)."), true);
|
assert_eq!(submit(&mut wam, "?- catch(f(X), E, E)."), true);
|
||||||
|
|
||||||
|
submit(&mut wam, "handle(x). handle(y). handle(z). handle(v) :- throw(handle_top(X)).");
|
||||||
|
submit(&mut wam, "handle_top(an_error_1). handle_top(an_error_2).");
|
||||||
|
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(X), E, E)."), true);
|
||||||
|
|
||||||
|
submit(&mut wam, "handle(x). handle(y). handle(z). handle(v) :- throw(X).");
|
||||||
|
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(X), E, handle_top(E))."), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -297,7 +297,6 @@ pub enum BuiltInInstruction {
|
|||||||
Fail,
|
Fail,
|
||||||
GetBall,
|
GetBall,
|
||||||
GetCurrentBlock,
|
GetCurrentBlock,
|
||||||
Goto(usize, usize),
|
|
||||||
InstallNewBlock,
|
InstallNewBlock,
|
||||||
InternalCallN,
|
InternalCallN,
|
||||||
IsAtomic,
|
IsAtomic,
|
||||||
@@ -317,6 +316,7 @@ pub enum ControlInstruction {
|
|||||||
Deallocate,
|
Deallocate,
|
||||||
Execute(Atom, usize),
|
Execute(Atom, usize),
|
||||||
ExecuteN(usize),
|
ExecuteN(usize),
|
||||||
|
Goto(usize, usize),
|
||||||
Proceed,
|
Proceed,
|
||||||
ThrowCall,
|
ThrowCall,
|
||||||
ThrowExecute
|
ThrowExecute
|
||||||
@@ -333,6 +333,7 @@ impl ControlInstruction {
|
|||||||
&ControlInstruction::ExecuteN(_) => true,
|
&ControlInstruction::ExecuteN(_) => true,
|
||||||
&ControlInstruction::ThrowCall => true,
|
&ControlInstruction::ThrowCall => true,
|
||||||
&ControlInstruction::ThrowExecute => true,
|
&ControlInstruction::ThrowExecute => true,
|
||||||
|
&ControlInstruction::Goto(_, _) => true,
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ pub trait CopierTarget
|
|||||||
fn deref(&self, Addr) -> Addr;
|
fn deref(&self, Addr) -> Addr;
|
||||||
fn stack(&mut self) -> &mut AndStack;
|
fn stack(&mut self) -> &mut AndStack;
|
||||||
|
|
||||||
|
//TODO: extend this so include a boundary() function which is constant!!
|
||||||
|
// this will return self.h. the threshold() will be used to set the addresses
|
||||||
|
// contained within the terms so that the offsets match those of the heap
|
||||||
|
// to be truncated by BLOCK, ie. starting at the BLOCK.H value. This should
|
||||||
|
// allow us to get around the latest bug.
|
||||||
|
|
||||||
// duplicate_term(L1, L2) uses Cheney's algorithm to copy the term at
|
// duplicate_term(L1, L2) uses Cheney's algorithm to copy the term at
|
||||||
// L1 to L2. forwarding_terms is kept to restore the innards of L1
|
// L1 to L2. forwarding_terms is kept to restore the innards of L1
|
||||||
// after it's been copied to L2.
|
// after it's been copied to L2.
|
||||||
@@ -67,7 +73,7 @@ pub trait CopierTarget
|
|||||||
match self[s].clone() {
|
match self[s].clone() {
|
||||||
HeapCellValue::NamedStr(arity, name) => {
|
HeapCellValue::NamedStr(arity, name) => {
|
||||||
let threshold = self.threshold();
|
let threshold = self.threshold();
|
||||||
|
|
||||||
self[scan] = HeapCellValue::Str(threshold);
|
self[scan] = HeapCellValue::Str(threshold);
|
||||||
self[s] = HeapCellValue::Str(threshold);
|
self[s] = HeapCellValue::Str(threshold);
|
||||||
|
|
||||||
@@ -89,8 +95,8 @@ pub trait CopierTarget
|
|||||||
scan += 1;
|
scan += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (r, hcv) in forward_trail {
|
for (r, hcv) in forward_trail {
|
||||||
match r {
|
match r {
|
||||||
Ref::HeapCell(hc) => self[hc] = hcv,
|
Ref::HeapCell(hc) => self[hc] = hcv,
|
||||||
@@ -99,4 +105,3 @@ pub trait CopierTarget
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ impl fmt::Display for ControlInstruction {
|
|||||||
write!(f, "deallocate"),
|
write!(f, "deallocate"),
|
||||||
&ControlInstruction::Execute(ref name, arity) =>
|
&ControlInstruction::Execute(ref name, arity) =>
|
||||||
write!(f, "execute {}/{}", name, arity),
|
write!(f, "execute {}/{}", name, arity),
|
||||||
|
&ControlInstruction::Goto(p, arity) =>
|
||||||
|
write!(f, "goto {}/{}", p, arity),
|
||||||
&ControlInstruction::Proceed =>
|
&ControlInstruction::Proceed =>
|
||||||
write!(f, "proceed"),
|
write!(f, "proceed"),
|
||||||
&ControlInstruction::ThrowCall =>
|
&ControlInstruction::ThrowCall =>
|
||||||
@@ -372,7 +374,6 @@ 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);
|
||||||
print_code(&compiled_pred);
|
|
||||||
wam.add_predicate(clauses, compiled_pred)
|
wam.add_predicate(clauses, compiled_pred)
|
||||||
} else {
|
} else {
|
||||||
let msg = r"Error: predicate is inconsistent.
|
let msg = r"Error: predicate is inconsistent.
|
||||||
@@ -391,14 +392,12 @@ Each predicate must have the same name and arity.";
|
|||||||
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);
|
||||||
print_code(&compiled_rule);
|
|
||||||
wam.add_rule(rule, compiled_rule)
|
wam.add_rule(rule, compiled_rule)
|
||||||
},
|
},
|
||||||
&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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ struct MachineState {
|
|||||||
tr: usize,
|
tr: usize,
|
||||||
hb: usize,
|
hb: usize,
|
||||||
block: usize, // an offset into the OR stack.
|
block: usize, // an offset into the OR stack.
|
||||||
ball: Heap
|
ball: (usize, Heap) // heap boundary, and a term copy
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DuplicateTerm<'a> {
|
struct DuplicateTerm<'a> {
|
||||||
@@ -111,7 +111,7 @@ impl<'a> Index<usize> for DuplicateBallTerm<'a> {
|
|||||||
&self.state.heap[index]
|
&self.state.heap[index]
|
||||||
} else {
|
} else {
|
||||||
let index = index - self.heap_boundary;
|
let index = index - self.heap_boundary;
|
||||||
&self.state.ball[index]
|
&self.state.ball.1[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ impl<'a> IndexMut<usize> for DuplicateBallTerm<'a> {
|
|||||||
&mut self.state.heap[index]
|
&mut self.state.heap[index]
|
||||||
} else {
|
} else {
|
||||||
let index = index - self.heap_boundary;
|
let index = index - self.heap_boundary;
|
||||||
&mut self.state.ball[index]
|
&mut self.state.ball.1[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,11 +134,11 @@ impl<'a> CopierTarget for DuplicateBallTerm<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn threshold(&self) -> usize {
|
fn threshold(&self) -> usize {
|
||||||
self.heap_boundary + self.state.ball.len()
|
self.heap_boundary + self.state.ball.1.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push(&mut self, hcv: HeapCellValue) {
|
fn push(&mut self, hcv: HeapCellValue) {
|
||||||
self.state.ball.push(hcv);
|
self.state.ball.1.push(hcv);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store(&self, a: Addr) -> Addr {
|
fn store(&self, a: Addr) -> Addr {
|
||||||
@@ -377,7 +377,7 @@ impl Machine {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn record_var_places<'a>(&self,
|
fn record_var_places<'a>(&self,
|
||||||
chunk_num: usize,
|
chunk_num: usize,
|
||||||
alloc_locs: &AllocVarDict<'a>,
|
alloc_locs: &AllocVarDict<'a>,
|
||||||
@@ -409,6 +409,10 @@ impl Machine {
|
|||||||
|
|
||||||
while self.ms.p < end_ptr {
|
while self.ms.p < end_ptr {
|
||||||
if let CodePtr::TopLevel(mut cn, p) = self.ms.p {
|
if let CodePtr::TopLevel(mut cn, p) = self.ms.p {
|
||||||
|
//TODO: Shouldn't have to work nearly this hard!! Why
|
||||||
|
// are we only recording addresses, for instance? Why
|
||||||
|
// not just offsets into the heap? Not like they
|
||||||
|
// change.
|
||||||
if let &Line::Control(ref ctrl_instr) = &self[CodePtr::TopLevel(cn, p)] {
|
if let &Line::Control(ref ctrl_instr) = &self[CodePtr::TopLevel(cn, p)] {
|
||||||
if ctrl_instr.is_jump_instr() {
|
if ctrl_instr.is_jump_instr() {
|
||||||
self.record_var_places(cn, alloc_locs, heap_locs);
|
self.record_var_places(cn, alloc_locs, heap_locs);
|
||||||
@@ -558,7 +562,7 @@ impl MachineState {
|
|||||||
tr: 0,
|
tr: 0,
|
||||||
hb: 0,
|
hb: 0,
|
||||||
block: 0,
|
block: 0,
|
||||||
ball: Vec::new()
|
ball: (0, Vec::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1207,17 +1211,29 @@ impl MachineState {
|
|||||||
self.p += 1;
|
self.p += 1;
|
||||||
},
|
},
|
||||||
&BuiltInInstruction::EraseBall => {
|
&BuiltInInstruction::EraseBall => {
|
||||||
self.ball.truncate(0);
|
self.ball.0 = 0;
|
||||||
|
self.ball.1.truncate(0);
|
||||||
self.p += 1;
|
self.p += 1;
|
||||||
},
|
},
|
||||||
&BuiltInInstruction::GetBall => {
|
&BuiltInInstruction::GetBall => {
|
||||||
let addr = self.store(self.deref(self[temp_v!(1)].clone()));
|
let addr = self.store(self.deref(self[temp_v!(1)].clone()));
|
||||||
let h = self.h;
|
let h = self.h;
|
||||||
|
|
||||||
if self.ball.len() > 0 {
|
if self.ball.1.len() > 0 {
|
||||||
let copied_ball_iter = self.ball.iter().cloned();
|
let diff = self.ball.0 - h;
|
||||||
self.heap.extend(copied_ball_iter);
|
|
||||||
self.h += self.ball.len();
|
for heap_value in self.ball.1.iter().cloned() {
|
||||||
|
self.heap.push(match heap_value {
|
||||||
|
HeapCellValue::Con(c) => HeapCellValue::Con(c),
|
||||||
|
HeapCellValue::Lis(a) => HeapCellValue::Lis(a - diff),
|
||||||
|
HeapCellValue::Ref(Ref::HeapCell(hc)) =>
|
||||||
|
HeapCellValue::Ref(Ref::HeapCell(hc - diff)),
|
||||||
|
HeapCellValue::Str(s) => HeapCellValue::Str(s - diff),
|
||||||
|
_ => heap_value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.h += self.ball.1.len();
|
||||||
} else {
|
} else {
|
||||||
self.fail = true;
|
self.fail = true;
|
||||||
return;
|
return;
|
||||||
@@ -1237,6 +1253,8 @@ impl MachineState {
|
|||||||
let addr = self[temp_v!(1)].clone();
|
let addr = self[temp_v!(1)].clone();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
self.ball.0 = self.h;
|
||||||
|
|
||||||
let mut duplicator = DuplicateBallTerm::new(self);
|
let mut duplicator = DuplicateBallTerm::new(self);
|
||||||
duplicator.duplicate_term(addr);
|
duplicator.duplicate_term(addr);
|
||||||
}
|
}
|
||||||
@@ -1304,12 +1322,7 @@ impl MachineState {
|
|||||||
&BuiltInInstruction::Fail => {
|
&BuiltInInstruction::Fail => {
|
||||||
self.fail = true;
|
self.fail = true;
|
||||||
self.p += 1;
|
self.p += 1;
|
||||||
},
|
}
|
||||||
&BuiltInInstruction::Goto(p, arity) => {
|
|
||||||
self.num_of_args = arity;
|
|
||||||
self.b0 = self.b;
|
|
||||||
self.p = CodePtr::DirEntry(p);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1355,6 +1368,11 @@ impl MachineState {
|
|||||||
if let Some((name, arity)) = self.setup_call_n(arity) {
|
if let Some((name, arity)) = self.setup_call_n(arity) {
|
||||||
self.try_execute_predicate(code_dir, name, arity);
|
self.try_execute_predicate(code_dir, name, arity);
|
||||||
},
|
},
|
||||||
|
&ControlInstruction::Goto(p, arity) => {
|
||||||
|
self.num_of_args = arity;
|
||||||
|
self.b0 = self.b;
|
||||||
|
self.p = CodePtr::DirEntry(p);
|
||||||
|
},
|
||||||
&ControlInstruction::Proceed =>
|
&ControlInstruction::Proceed =>
|
||||||
self.p = self.cp,
|
self.p = self.cp,
|
||||||
&ControlInstruction::ThrowCall => {
|
&ControlInstruction::ThrowCall => {
|
||||||
@@ -1605,5 +1623,7 @@ impl MachineState {
|
|||||||
self.and_stack.clear();
|
self.and_stack.clear();
|
||||||
self.or_stack.clear();
|
self.or_stack.clear();
|
||||||
self.registers = vec![Addr::HeapCell(0); 64];
|
self.registers = vec![Addr::HeapCell(0); 64];
|
||||||
|
self.block = 0;
|
||||||
|
self.ball = (0, Vec::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ macro_rules! install_new_block {
|
|||||||
|
|
||||||
macro_rules! goto {
|
macro_rules! goto {
|
||||||
($line:expr, $arity:expr) => (
|
($line:expr, $arity:expr) => (
|
||||||
Line::BuiltIn(BuiltInInstruction::Goto($line, $arity))
|
Line::Control(ControlInstruction::Goto($line, $arity))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user