properly handle cyclic terms in the printer.
This commit is contained in:
@@ -4,6 +4,14 @@ use prolog::ast::*;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::IndexMut;
|
||||
|
||||
pub type Trail = Vec<(Ref, HeapCellValue)>;
|
||||
|
||||
pub struct RedirectInfo {
|
||||
trail: Trail,
|
||||
list_redirect: HashMap<usize, usize>,
|
||||
offset: usize
|
||||
}
|
||||
|
||||
// allows us to reconstruct a HeapVarDict by relating variables
|
||||
// in an existing HeapVarDict to the ones created in fn duplicate_term.
|
||||
// It is used to create meaningful error reports at the toplevel.
|
||||
@@ -13,9 +21,11 @@ impl CellRedirect {
|
||||
pub fn new() -> Self {
|
||||
CellRedirect(HashMap::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub type Trail = Vec<(Ref, HeapCellValue)>;
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CopierTarget
|
||||
{
|
||||
@@ -26,49 +36,60 @@ pub trait CopierTarget
|
||||
fn deref(&self, Addr) -> Addr;
|
||||
fn stack(&mut self) -> &mut AndStack;
|
||||
|
||||
fn compile_redirect(&mut self, trail: Trail, list_redirect: HashMap<usize, usize>, old_h: usize)
|
||||
-> CellRedirect
|
||||
where Self: IndexMut<usize, Output=HeapCellValue>
|
||||
// unwind the trail *and* compute a cell redirection table.
|
||||
fn unwind_and_redirect(&mut self, redirect: RedirectInfo) -> CellRedirect
|
||||
where Self: IndexMut<usize, Output=HeapCellValue>
|
||||
{
|
||||
let mut cell_redirect: HashMap<Addr, Addr> = HashMap::new();
|
||||
|
||||
for (r, hcv) in trail {
|
||||
|
||||
for (r, hcv) in redirect.trail {
|
||||
let addr = Addr::from(r);
|
||||
|
||||
|
||||
match r {
|
||||
Ref::HeapCell(hc) => {
|
||||
cell_redirect.insert(addr, self[hc].as_addr(hc) - old_h);
|
||||
self[hc] = hcv;
|
||||
Ref::HeapCell(h) => {
|
||||
cell_redirect.insert(addr, self[h].as_addr(h) - redirect.offset);
|
||||
self[h] = hcv;
|
||||
},
|
||||
Ref::StackCell(fr, sc) => {
|
||||
cell_redirect.insert(addr, self.stack()[fr][sc].clone() - old_h);
|
||||
cell_redirect.insert(addr, self.stack()[fr][sc].clone() - redirect.offset);
|
||||
self.stack()[fr][sc] = hcv.as_addr(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for (l, h) in list_redirect {
|
||||
cell_redirect.insert(Addr::Lis(l), Addr::Lis(h - old_h));
|
||||
for (l, h) in redirect.list_redirect {
|
||||
cell_redirect.insert(Addr::Lis(l), Addr::Lis(h - redirect.offset));
|
||||
}
|
||||
|
||||
CellRedirect(cell_redirect)
|
||||
}
|
||||
|
||||
// duplicate_term(L1, L2) uses Cheney's algorithm to copy the term
|
||||
|
||||
fn unwind_trail(&mut self, redirect: RedirectInfo)
|
||||
where Self: IndexMut<usize, Output=HeapCellValue>
|
||||
{
|
||||
for (r, hcv) in redirect.trail {
|
||||
match r {
|
||||
Ref::HeapCell(hc) => self[hc] = hcv.clone(),
|
||||
Ref::StackCell(fr, sc) => self.stack()[fr][sc] = hcv.as_addr(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// duplicate_term_impl(L1, L2) uses Cheney's algorithm to copy the term
|
||||
// at L1 to L2. trail is kept to restore the innards of L1 after
|
||||
// it's been copied to L2.
|
||||
fn duplicate_term(&mut self, addr: Addr) -> CellRedirect
|
||||
fn duplicate_term_impl(&mut self, addr: Addr) -> RedirectInfo
|
||||
where Self: IndexMut<usize, Output=HeapCellValue>
|
||||
{
|
||||
let mut trail = Trail::new();
|
||||
let mut scan = self.source();
|
||||
let old_h = self.threshold();
|
||||
let mut scan = self.source();
|
||||
let old_h = self.threshold();
|
||||
|
||||
// Lists have a compressed representation as structures,
|
||||
// removing the need for NamedStr, so we use a redirection
|
||||
// table for copying lists.
|
||||
let mut list_redirect = HashMap::new();
|
||||
|
||||
|
||||
self.push(HeapCellValue::Addr(addr));
|
||||
|
||||
while scan < self.threshold() {
|
||||
@@ -84,15 +105,15 @@ pub trait CopierTarget
|
||||
continue;
|
||||
}
|
||||
|
||||
list_redirect.insert(a, self.threshold());
|
||||
list_redirect.insert(a, self.threshold());
|
||||
self[scan] = HeapCellValue::Addr(Addr::Lis(self.threshold()));
|
||||
|
||||
|
||||
let hcv = self[a].clone();
|
||||
self.push(hcv);
|
||||
|
||||
|
||||
let hcv = self[a+1].clone();
|
||||
self.push(hcv);
|
||||
|
||||
|
||||
scan += 1;
|
||||
},
|
||||
Addr::HeapCell(_) | Addr::StackCell(_, _) => {
|
||||
@@ -152,6 +173,20 @@ pub trait CopierTarget
|
||||
}
|
||||
}
|
||||
|
||||
self.compile_redirect(trail, list_redirect, old_h)
|
||||
RedirectInfo { trail, list_redirect, offset: old_h }
|
||||
}
|
||||
|
||||
fn duplicate_term_and_redirect(&mut self, addr: Addr) -> CellRedirect
|
||||
where Self: IndexMut<usize, Output=HeapCellValue>
|
||||
{
|
||||
let redirect = self.duplicate_term_impl(addr);
|
||||
self.unwind_and_redirect(redirect)
|
||||
}
|
||||
|
||||
fn duplicate_term(&mut self, addr: Addr)
|
||||
where Self: IndexMut<usize, Output=HeapCellValue>
|
||||
{
|
||||
let redirect = self.duplicate_term_impl(addr);
|
||||
self.unwind_trail(redirect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ impl<'a, Formatter: HCValueFormatter, Outputter: HCValueOutputter>
|
||||
-> Self
|
||||
{
|
||||
let mut printer = Self::new(machine_st, fmt, output);
|
||||
|
||||
|
||||
printer.heap_locs = Cow::Borrowed(heap_locs);
|
||||
printer
|
||||
}
|
||||
@@ -184,10 +184,20 @@ impl<'a, Formatter: HCValueFormatter, Outputter: HCValueOutputter>
|
||||
let addr = machine_st.store(machine_st.deref(addr.clone()));
|
||||
printer.printed_vars.insert(addr.clone());
|
||||
}
|
||||
|
||||
|
||||
printer
|
||||
}
|
||||
|
||||
|
||||
fn print_offset(&mut self, addr: Addr) {
|
||||
match addr {
|
||||
Addr::HeapCell(h) | Addr::Lis(h) =>
|
||||
self.outputter.append(format!("_{}", h).as_str()),
|
||||
Addr::StackCell(fr, sc) =>
|
||||
self.outputter.append(format!("s_{}_{}", fr, sc).as_str()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// returns a HeapCellValue iff the next element to come hasn't been seen previously.
|
||||
fn check_for_seen(&mut self, iter: &mut HCPreOrderIterator) -> Option<HeapCellValue> {
|
||||
iter.stack().last().cloned().and_then(|addr| {
|
||||
@@ -207,13 +217,16 @@ impl<'a, Formatter: HCValueFormatter, Outputter: HCValueOutputter>
|
||||
}
|
||||
}
|
||||
|
||||
// addr is not the address of a named variable. If it is a variable,
|
||||
// it must be anonymous.
|
||||
if addr.is_ref() {
|
||||
self.outputter.append("_");
|
||||
iter.stack().pop();
|
||||
|
||||
None
|
||||
if self.machine_st.is_cyclic_term(addr.clone()) {
|
||||
if self.printed_vars.contains(&addr) {
|
||||
iter.stack().pop();
|
||||
self.print_offset(addr);
|
||||
|
||||
None
|
||||
} else {
|
||||
self.printed_vars.insert(addr);
|
||||
iter.next()
|
||||
}
|
||||
} else {
|
||||
iter.next()
|
||||
}
|
||||
@@ -226,7 +239,7 @@ impl<'a, Formatter: HCValueFormatter, Outputter: HCValueOutputter>
|
||||
Some(heap_val) => heap_val,
|
||||
_ => return
|
||||
};
|
||||
|
||||
|
||||
match heap_val {
|
||||
HeapCellValue::NamedStr(arity, name, fixity) => {
|
||||
let ct = ClauseType::from(name, arity, fixity);
|
||||
@@ -249,11 +262,7 @@ impl<'a, Formatter: HCValueFormatter, Outputter: HCValueOutputter>
|
||||
|
||||
self.state_stack.push(TokenOrRedirect::OpenList(cell));
|
||||
},
|
||||
HeapCellValue::Addr(Addr::HeapCell(h)) =>
|
||||
self.outputter.append(format!("_{}", h).as_str()),
|
||||
HeapCellValue::Addr(Addr::StackCell(fr, sc)) =>
|
||||
self.outputter.append(format!("s_{}_{}", fr, sc).as_str()),
|
||||
HeapCellValue::Addr(Addr::Str(_)) => {}
|
||||
HeapCellValue::Addr(addr) => self.print_offset(addr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1121,10 +1121,9 @@ impl MachineState {
|
||||
diff
|
||||
}
|
||||
|
||||
pub(super) fn is_cyclic_term(&self, addr: Addr) -> bool {
|
||||
pub(crate) fn is_cyclic_term(&self, addr: Addr) -> bool {
|
||||
let mut seen = HashSet::new();
|
||||
let mut fail = false;
|
||||
|
||||
let mut iter = self.pre_order_iter(addr);
|
||||
|
||||
loop {
|
||||
@@ -1188,26 +1187,7 @@ impl MachineState {
|
||||
|
||||
self.p += 1;
|
||||
}
|
||||
|
||||
// everything in self.redirect is potentially offset on the heap by 'offset',
|
||||
// so correct for that when building the HeapVarDict.
|
||||
pub(super) fn reconstruct_dict(&self, heap_locs: &HeapVarDict, offset: usize) -> HeapVarDict
|
||||
{
|
||||
let mut dest_heap_locs = HeapVarDict::new();
|
||||
|
||||
'outer:
|
||||
for (orig_addr, addr) in self.redirect.0.iter() {
|
||||
for (var, var_addr) in heap_locs.iter() {
|
||||
if orig_addr == &self.store(self.deref(var_addr.clone())) {
|
||||
dest_heap_locs.insert(var.clone(), addr.clone() + offset);
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dest_heap_locs
|
||||
}
|
||||
|
||||
|
||||
pub(super) fn compare_term(&mut self, qt: CompareTermQT) {
|
||||
let a1 = self[temp_v!(1)].clone();
|
||||
let a2 = self[temp_v!(2)].clone();
|
||||
@@ -1638,12 +1618,11 @@ impl MachineState {
|
||||
let addr = self[temp_v!(1)].clone();
|
||||
self.ball.boundary = self.heap.h;
|
||||
|
||||
let cell_redirect = {
|
||||
self.redirect = {
|
||||
let mut duplicator = DuplicateBallTerm::new(self);
|
||||
duplicator.duplicate_term(addr)
|
||||
duplicator.duplicate_term_and_redirect(addr)
|
||||
};
|
||||
|
||||
self.redirect.0.extend(cell_redirect.0.into_iter());
|
||||
|
||||
self.p += 1;
|
||||
},
|
||||
&BuiltInInstruction::SetCutPoint(r) =>
|
||||
@@ -2354,6 +2333,6 @@ impl MachineState {
|
||||
self.block = 0;
|
||||
|
||||
self.ball.reset();
|
||||
self.redirect.0.clear();
|
||||
self.redirect.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,8 +375,7 @@ impl Machine {
|
||||
if self.ms.ball.stub.len() > 0 {
|
||||
let h = self.ms.heap.h;
|
||||
self.ms.copy_and_align_ball_to_heap();
|
||||
|
||||
let heap_locs = self.ms.reconstruct_dict(heap_locs, h);
|
||||
|
||||
let error_str = self.ms.print_exception(Addr::HeapCell(h),
|
||||
&heap_locs,
|
||||
TermFormatter {},
|
||||
|
||||
Reference in New Issue
Block a user