add top level exception reporting, exceptions for call/N.

This commit is contained in:
Mark Thom
2017-08-17 18:20:58 -06:00
parent 1a7edc2d13
commit 02c0f1cf56
5 changed files with 165 additions and 95 deletions

View File

@@ -47,7 +47,7 @@ ideally, very fast) [Shen](http://shenlanguage.org) implementation.
The following predicates are built-in to rusty-wam. The following predicates are built-in to rusty-wam.
* atomic/1 * atomic/1
* call/N (0 <= N <= 62) * call/N (1 <= N <= 63)
* catch/3 * catch/3
* duplicate_term/2 * duplicate_term/2
* false/0 * false/0

View File

@@ -18,6 +18,7 @@ pub enum EvalSession<'a> {
EntrySuccess, EntrySuccess,
InitialQuerySuccess(AllocVarDict<'a>, HeapVarDict<'a>), InitialQuerySuccess(AllocVarDict<'a>, HeapVarDict<'a>),
QueryFailure, QueryFailure,
QueryFailureWithException(String),
SubsequentQuerySuccess, SubsequentQuerySuccess,
} }

View File

@@ -403,6 +403,10 @@ Each predicate must have the same name and arity.";
} }
} }
fn error_string(e: &String) -> String {
format!("error: {}", e)
}
pub fn print(wam: &mut Machine, result: EvalSession) { pub fn print(wam: &mut Machine, result: EvalSession) {
match result { match result {
EvalSession::InitialQuerySuccess(alloc_locs, mut heap_locs) => { EvalSession::InitialQuerySuccess(alloc_locs, mut heap_locs) => {
@@ -451,6 +455,12 @@ pub fn print(wam: &mut Machine, result: EvalSession) {
stdout.flush().unwrap(); stdout.flush().unwrap();
return; return;
} }
if let &EvalSession::QueryFailureWithException(ref e) = &result {
write!(stdout, "{}\n\r", error_string(e)).unwrap();
stdout.flush().unwrap();
return;
}
} else { } else {
break; break;
} }
@@ -459,6 +469,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::QueryFailureWithException(e) => println!("{}", error_string(&e)),
EvalSession::EntryFailure(msg) => println!("{}", msg), EvalSession::EntryFailure(msg) => println!("{}", msg),
_ => {} _ => {}
}; };

View File

@@ -284,9 +284,6 @@ impl Machine {
fn execute_instr(&mut self) fn execute_instr(&mut self)
{ {
// can't use self[ptr] or self.index(ptr) to set the value of
// instr! instr is then typed as Line, not &Line. WHY????
// This is a compiler bug. Has to be.
let instr = match self.ms.p { let instr = match self.ms.p {
CodePtr::TopLevel(_, p) => { CodePtr::TopLevel(_, p) => {
match &self.cached_query { match &self.cached_query {
@@ -409,10 +406,6 @@ 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);
@@ -432,6 +425,18 @@ impl Machine {
} }
} }
fn fail<'a>(&mut self) -> EvalSession<'a>
{
if self.ms.ball.1.len() > 0 {
let h = self.ms.h;
self.ms.copy_and_align_ball_to_heap();
EvalSession::QueryFailureWithException(self.print_term(&Addr::HeapCell(h)))
} else {
EvalSession::QueryFailure
}
}
pub fn submit_query<'a>(&mut self, code: Code, alloc_locs: AllocVarDict<'a>) -> EvalSession<'a> pub fn submit_query<'a>(&mut self, code: Code, alloc_locs: AllocVarDict<'a>) -> EvalSession<'a>
{ {
let mut heap_locs = HashMap::new(); let mut heap_locs = HashMap::new();
@@ -440,16 +445,14 @@ impl Machine {
self.run_query(&alloc_locs, &mut heap_locs); self.run_query(&alloc_locs, &mut heap_locs);
if self.failed() { if self.failed() {
EvalSession::QueryFailure self.fail()
} else { } else {
EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) EvalSession::InitialQuerySuccess(alloc_locs, heap_locs)
} }
} }
pub fn continue_query<'a>(&mut self, pub fn continue_query<'a>(&mut self, alloc_locs: &AllocVarDict<'a>, heap_locs: &mut HeapVarDict<'a>)
alloc_locs: &AllocVarDict<'a>, -> EvalSession<'a>
heap_locs: &mut HeapVarDict<'a>)
-> EvalSession
{ {
if !self.or_stack_is_empty() { if !self.or_stack_is_empty() {
let b = self.ms.b - 1; let b = self.ms.b - 1;
@@ -462,7 +465,7 @@ impl Machine {
self.run_query(alloc_locs, heap_locs); self.run_query(alloc_locs, heap_locs);
if self.failed() { if self.failed() {
EvalSession::QueryFailure self.fail()
} else { } else {
EvalSession::SubsequentQuerySuccess EvalSession::SubsequentQuerySuccess
} }
@@ -471,20 +474,13 @@ impl Machine {
} }
} }
pub fn heap_view(&self, var_dir: &HeapVarDict) -> String { fn print_term(&self, addr: &Addr) -> String
let mut result = String::new(); {
for (var, addr) in var_dir {
let mut viewer = HeapCellViewer::new(&self.ms.heap, let mut viewer = HeapCellViewer::new(&self.ms.heap,
&self.ms.and_stack, &self.ms.and_stack,
addr); addr);
if result != "" { let mut result = String::new();
result += "\n\r";
}
result += var.as_str();
result += " = ";
while let Some(view) = viewer.next() { while let Some(view) = viewer.next() {
match view { match view {
@@ -522,6 +518,22 @@ impl Machine {
result += token.as_str() result += token.as_str()
}; };
} }
result
}
pub fn heap_view(&self, var_dir: &HeapVarDict) -> String {
let mut result = String::new();
for (var, addr) in var_dir {
if result != "" {
result += "\n\r";
}
result += var.as_str();
result += " = ";
result += self.print_term(addr).as_str();
} }
result result
@@ -1143,15 +1155,38 @@ impl MachineState {
} }
} }
fn goto_throw(&mut self) {
self.num_of_args = 1;
self.b0 = self.b;
self.p = CodePtr::DirEntry(59);
}
fn throw_exception(&mut self, mut hcv: Vec<HeapCellValue>) {
let h = self.h;
self.registers[1] = Addr::HeapCell(h);
self.h += hcv.len();
self.heap.append(&mut hcv);
self.goto_throw();
}
fn setup_call_n(&mut self, arity: usize) -> Option<PredicateKey> fn setup_call_n(&mut self, arity: usize) -> Option<PredicateKey>
{ {
let addr = self.deref(self.registers[arity].clone()); let addr = self.store(self.deref(self.registers[arity].clone()));
let (name, narity) = match self.store(addr) { let (name, narity) = match 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 {
if narity + arity > 63 {
self.throw_exception(functor!("representation_error",
1,
[atom!("exceeds_max_arity")]));
return None;
}
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();
} }
@@ -1167,8 +1202,15 @@ impl MachineState {
} }
}, },
Addr::Con(Constant::Atom(name)) => (name, 0), Addr::Con(Constant::Atom(name)) => (name, 0),
Addr::HeapCell(_) | Addr::StackCell(_, _) => {
self.throw_exception(functor!("instantiation_error", 0, []));
return None;
},
_ => { _ => {
self.fail = true; self.throw_exception(functor!("type_error",
2,
[atom!("callable"),
HeapCellValue::from(addr)]));
return None; return None;
} }
}; };
@@ -1176,6 +1218,23 @@ impl MachineState {
Some((name, arity + narity - 1)) Some((name, arity + narity - 1))
} }
fn copy_and_align_ball_to_heap(&mut self) {
let diff = self.ball.0 - self.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();
}
fn execute_built_in_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction) fn execute_built_in_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction)
{ {
match instr { match instr {
@@ -1220,20 +1279,7 @@ impl MachineState {
let h = self.h; let h = self.h;
if self.ball.1.len() > 0 { if self.ball.1.len() > 0 {
let diff = self.ball.0 - h; self.copy_and_align_ball_to_heap();
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;
@@ -1377,14 +1423,10 @@ impl MachineState {
self.p = self.cp, self.p = self.cp,
&ControlInstruction::ThrowCall => { &ControlInstruction::ThrowCall => {
self.cp = self.p + 1; self.cp = self.p + 1;
self.num_of_args = 1; self.goto_throw();
self.b0 = self.b;
self.p = CodePtr::DirEntry(59);
}, },
&ControlInstruction::ThrowExecute => { &ControlInstruction::ThrowExecute => {
self.num_of_args = 1; self.goto_throw();
self.b0 = self.b;
self.p = CodePtr::DirEntry(59);
} }
}; };
} }

View File

@@ -22,6 +22,22 @@ macro_rules! query {
) )
} }
macro_rules! functor {
($name:expr, $len:expr, [$($args:expr),*]) => {{
if $len > 0 {
vec![ HeapCellValue::NamedStr($len, String::from($name)), $($args),* ]
} else {
vec![ atom!($name) ]
}
}}
}
macro_rules! atom {
($name:expr) => (
HeapCellValue::Con(Constant::Atom(String::from($name)))
)
}
macro_rules! fact { macro_rules! fact {
[$($x:expr),+] => ( [$($x:expr),+] => (
Line::Fact(vec![$($x),+]) Line::Fact(vec![$($x),+])