Extend rusty-wam to include the following, among other features:
* 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,
with custom associativity and precedence.
* Bignum and floating point arithmetic.
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.
+## 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
To enter a multi-clause predicate, the brackets ":{" and "}:" are used
as delimiters. They must be contained entirely within their own lines.
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).");
- //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);
+
+ 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);
}
}
Fail,
GetBall,
GetCurrentBlock,
- Goto(usize, usize),
InstallNewBlock,
InternalCallN,
IsAtomic,
Deallocate,
Execute(Atom, usize),
ExecuteN(usize),
+ Goto(usize, usize),
Proceed,
ThrowCall,
ThrowExecute
&ControlInstruction::ExecuteN(_) => true,
&ControlInstruction::ThrowCall => true,
&ControlInstruction::ThrowExecute => true,
+ &ControlInstruction::Goto(_, _) => true,
_ => false
}
}
fn deref(&self, Addr) -> Addr;
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
// L1 to L2. forwarding_terms is kept to restore the innards of L1
// after it's been copied to L2.
match self[s].clone() {
HeapCellValue::NamedStr(arity, name) => {
let threshold = self.threshold();
-
+
self[scan] = HeapCellValue::Str(threshold);
self[s] = HeapCellValue::Str(threshold);
scan += 1;
}
};
- }
-
+ }
+
for (r, hcv) in forward_trail {
match r {
Ref::HeapCell(hc) => self[hc] = hcv,
}
}
}
-
write!(f, "deallocate"),
&ControlInstruction::Execute(ref name, arity) =>
write!(f, "execute {}/{}", name, arity),
+ &ControlInstruction::Goto(p, arity) =>
+ write!(f, "goto {}/{}", p, arity),
&ControlInstruction::Proceed =>
write!(f, "proceed"),
&ControlInstruction::ThrowCall =>
if is_consistent(clauses) {
let compiled_pred = cg.compile_predicate(clauses);
- print_code(&compiled_pred);
wam.add_predicate(clauses, compiled_pred)
} else {
let msg = r"Error: predicate is inconsistent.
let mut cg = CodeGenerator::<DebrayAllocator>::new();
let compiled_rule = cg.compile_rule(rule);
- print_code(&compiled_rule);
wam.add_rule(rule, compiled_rule)
},
&TopLevel::Query(ref query) => {
let mut cg = CodeGenerator::<DebrayAllocator>::new();
let compiled_query = cg.compile_query(query);
- print_code(&compiled_query);
wam.submit_query(compiled_query, cg.take_vars())
}
}
tr: usize,
hb: usize,
block: usize, // an offset into the OR stack.
- ball: Heap
+ ball: (usize, Heap) // heap boundary, and a term copy
}
struct DuplicateTerm<'a> {
&self.state.heap[index]
} else {
let index = index - self.heap_boundary;
- &self.state.ball[index]
+ &self.state.ball.1[index]
}
}
}
&mut self.state.heap[index]
} else {
let index = index - self.heap_boundary;
- &mut self.state.ball[index]
+ &mut self.state.ball.1[index]
}
}
}
}
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) {
- self.state.ball.push(hcv);
+ self.state.ball.1.push(hcv);
}
fn store(&self, a: Addr) -> Addr {
};
}
}
-
+
fn record_var_places<'a>(&self,
chunk_num: usize,
alloc_locs: &AllocVarDict<'a>,
while self.ms.p < end_ptr {
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 ctrl_instr.is_jump_instr() {
self.record_var_places(cn, alloc_locs, heap_locs);
tr: 0,
hb: 0,
block: 0,
- ball: Vec::new()
+ ball: (0, Vec::new())
}
}
self.p += 1;
},
&BuiltInInstruction::EraseBall => {
- self.ball.truncate(0);
+ self.ball.0 = 0;
+ self.ball.1.truncate(0);
self.p += 1;
},
&BuiltInInstruction::GetBall => {
let addr = self.store(self.deref(self[temp_v!(1)].clone()));
let h = self.h;
- if self.ball.len() > 0 {
- let copied_ball_iter = self.ball.iter().cloned();
- self.heap.extend(copied_ball_iter);
- self.h += self.ball.len();
+ if self.ball.1.len() > 0 {
+ let diff = self.ball.0 - h;
+
+ 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 {
self.fail = true;
return;
let addr = self[temp_v!(1)].clone();
{
+ self.ball.0 = self.h;
+
let mut duplicator = DuplicateBallTerm::new(self);
duplicator.duplicate_term(addr);
}
&BuiltInInstruction::Fail => {
self.fail = true;
self.p += 1;
- },
- &BuiltInInstruction::Goto(p, arity) => {
- self.num_of_args = arity;
- self.b0 = self.b;
- self.p = CodePtr::DirEntry(p);
- }
+ }
};
}
if let Some((name, arity)) = self.setup_call_n(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 =>
self.p = self.cp,
&ControlInstruction::ThrowCall => {
self.and_stack.clear();
self.or_stack.clear();
self.registers = vec![Addr::HeapCell(0); 64];
+ self.block = 0;
+ self.ball = (0, Vec::new());
}
}
macro_rules! goto {
($line:expr, $arity:expr) => (
- Line::BuiltIn(BuiltInInstruction::Goto($line, $arity))
+ Line::Control(ControlInstruction::Goto($line, $arity))
)
}