From: Mark Thom Date: Sat, 5 May 2018 18:33:29 +0000 (-0600) Subject: properly handle cyclic terms in the printer. X-Git-Tag: v0.8.110~469 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=81a199836dd268ee9398e434da4c5cb0b7e11639;p=scryer-prolog.git properly handle cyclic terms in the printer. --- diff --git a/src/prolog/copier.rs b/src/prolog/copier.rs index e79d9e46..7a083656 100644 --- a/src/prolog/copier.rs +++ b/src/prolog/copier.rs @@ -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, + 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, old_h: usize) - -> CellRedirect - where Self: IndexMut + // unwind the trail *and* compute a cell redirection table. + fn unwind_and_redirect(&mut self, redirect: RedirectInfo) -> CellRedirect + where Self: IndexMut { let mut cell_redirect: HashMap = 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 + { + 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 { 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 + { + let redirect = self.duplicate_term_impl(addr); + self.unwind_and_redirect(redirect) + } + + fn duplicate_term(&mut self, addr: Addr) + where Self: IndexMut + { + let redirect = self.duplicate_term_impl(addr); + self.unwind_trail(redirect); } } diff --git a/src/prolog/heap_print.rs b/src/prolog/heap_print.rs index 765415da..11a23ae1 100644 --- a/src/prolog/heap_print.rs +++ b/src/prolog/heap_print.rs @@ -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 { 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) } } diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs index 6592fc20..8f1fd505 100644 --- a/src/prolog/machine/machine_state_impl.rs +++ b/src/prolog/machine/machine_state_impl.rs @@ -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(); } } diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index 9cf732f4..f2a4ad81 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -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 {}, diff --git a/src/tests.rs b/src/tests.rs index d33a1039..9477caf2 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -271,7 +271,7 @@ fn test_queries_on_rules() { assert_prolog_success!(&mut wam, "?- p(f(X, g(Y), Z), g(Z), h).", [["Z = b", "Y = b", "X = f(a)"]]); assert_prolog_success!(&mut wam, "?- p(Z, Y, X).", - [["X = h", "Y = g(b)", "Z = f(f(a), g(b), _)"]]); + [["X = h", "Y = g(b)", "Z = f(f(a), g(b), _7)"]]); assert_prolog_success!(&mut wam, "?- p(f(X, Y, Z), Y, h).", [["Y = g(b)", "Z = _3", "X = f(a)"]]); @@ -539,7 +539,7 @@ fn test_queries_on_lists() assert_prolog_success!(&mut wam, "?- p([Z, Z]).", [["Z = _0"]]); assert_prolog_failure!(&mut wam, "?- p([Z, W, Y])."); - assert_prolog_success!(&mut wam, "?- p([Z | W]).", [["Z = _0", "W = [_]"]]); + assert_prolog_success!(&mut wam, "?- p([Z | W]).", [["Z = _0", "W = [_3]"]]); assert_prolog_success!(&mut wam, "?- p([Z | [Z]]).", [["Z = _0"]]); assert_prolog_success!(&mut wam, "?- p([Z | [W]]).", [["Z = _2", "W = _0"]]); assert_prolog_failure!(&mut wam, "?- p([Z | []])."); @@ -600,7 +600,7 @@ fn test_queries_on_conjuctive_queries() { submit(&mut wam, "p(a, [f(g(X))])."); submit(&mut wam, "q(Y, c)."); - assert_prolog_success!(&mut wam, "?- p(X, Y), q(Y, Z).", [["Y = [f(g(_))]", "X = a", "Z = c"]]); + assert_prolog_success!(&mut wam, "?- p(X, Y), q(Y, Z).", [["Y = [f(g(_9))]", "X = a", "Z = c"]]); assert_prolog_failure!(&mut wam, "?- p(X, Y), q(Y, X)."); submit(&mut wam, "member(X, [X|_]). @@ -632,10 +632,10 @@ fn test_queries_on_conjuctive_queries() { submit(&mut wam, "q(Y, c)."); assert_prolog_success!(&mut wam, "?- p(X, Y), q(Y, Z).", - [["X = a", "Z = c", "Y = [f(g(_))]"], + [["X = a", "Z = c", "Y = [f(g(_9))]"], ["X = _0", "Z = c", "Y = c"]]); assert_prolog_success!(&mut wam, "?- p(X, Y), !, q(Y, Z).", - [["Z = c", "Y = [f(g(_))]", "X = a"]]); + [["Z = c", "Y = [f(g(_9))]", "X = a"]]); submit(&mut wam, "q([f(g(x))], Z). q([f(g(y))], Y). q([f(g(z))], a)."); @@ -1260,8 +1260,8 @@ fn test_queries_on_builtins() assert_prolog_success!(&mut wam, "?- functor(F, f, 0).", [["F = f"]]); - assert_prolog_success!(&mut wam, "?- functor(Func, f, 3).", [["Func = f(_, _, _)"]]); - assert_prolog_success!(&mut wam, "?- functor(Func, f, 4).", [["Func = f(_, _, _, _)"]]); + assert_prolog_success!(&mut wam, "?- functor(Func, f, 3).", [["Func = f(_2, _3, _4)"]]); + assert_prolog_success!(&mut wam, "?- functor(Func, f, 4).", [["Func = f(_2, _3, _4, _5)"]]); assert_prolog_success!(&mut wam, "?- catch(functor(F, \"sdf\", 3), error(E, _), true).", [["E = instantiation_error", "F = _1"]]); @@ -1289,14 +1289,14 @@ fn test_queries_on_builtins() assert_prolog_success_with_limit!(&mut wam, "?- length(Xs, N).", [["N = 0", "Xs = []"], - ["N = 1", "Xs = [_]"], - ["N = 2", "Xs = [_, _]"], - ["N = 3", "Xs = [_, _, _]"], - ["N = 4", "Xs = [_, _, _, _]"], - ["N = 5", "Xs = [_, _, _, _, _]"]], + ["N = 1", "Xs = [_3]"], + ["N = 2", "Xs = [_3, _6]"], + ["N = 3", "Xs = [_3, _6, _9]"], + ["N = 4", "Xs = [_3, _6, _9, _12]"], + ["N = 5", "Xs = [_3, _6, _9, _12, _15]"]], 6); - assert_prolog_success!(&mut wam, "?- length(Xs, 3).", [["Xs = [_, _, _]"]]); + assert_prolog_success!(&mut wam, "?- length(Xs, 3).", [["Xs = [_2, _5, _8]"]]); assert_prolog_success!(&mut wam, "?- length([a,b,c], N).", [["N = 3"]]); assert_prolog_success!(&mut wam, "?- length([], N).", [["N = 0"]]); assert_prolog_success!(&mut wam, "?- length(Xs, 0).", [["Xs = []"]]); @@ -1413,13 +1413,13 @@ fn test_queries_on_builtins() assert_prolog_success!(&mut wam, "?- keysort([1-1, 1-1], Sorted).", [["Sorted = [1 - 1, 1 - 1]"]]); assert_prolog_success!(&mut wam, "?- keysort([2-99, 1-a, 3-f(_), 1-z, 1-a, 2-44], Sorted).", - [["Sorted = [1 - a, 1 - z, 1 - a, 2 - 99, 2 - 44, 3 - f(_)]"]]); + [["Sorted = [1 - a, 1 - z, 1 - a, 2 - 99, 2 - 44, 3 - f(_7)]"]]); assert_prolog_success!(&mut wam, "?- keysort([X-1,1-1],[2-1,1-1]).", [["X = 2"]]); - + assert_prolog_failure!(&mut wam, "?- Pairs = [a-a|Pairs], keysort(Pairs, _)."); -// assert_prolog_success!(&mut wam, "?- Pairs = [a-a|Pairs], catch(keysort(Pairs, _), error(E, _), true).", - // [["E = type_error(list, Pairs)", "Pairs = [a - a | Pairs]"]]); + assert_prolog_success!(&mut wam, "?- Pairs = [a-a|Pairs], catch(keysort(Pairs, _), error(E, _), true).", + [["E = type_error(list, [a - a | _21])", "Pairs = [a - a | Pairs]"]]); assert_prolog_success!(&mut wam, "?- keysort([], L).", [["L = []"]]); @@ -1428,9 +1428,9 @@ fn test_queries_on_builtins() assert_prolog_success!(&mut wam, "?- catch(keysort([],[a|a]),error(Pat, _),true).", [["Pat = type_error(list, [a | a])"]]); assert_prolog_success!(&mut wam, "?- catch(keysort(_, _), error(E, _), true).", - [["E = type_error(list, _)"]]); + [["E = type_error(list, _12)"]]); assert_prolog_success!(&mut wam, "?- catch(keysort([a-1], [_|b]), error(E, _), true).", - [["E = type_error(list, [_ | b])"]]); + [["E = type_error(list, [_23 | b])"]]); assert_prolog_success!(&mut wam, "?- catch(keysort([a-1], [a-b,c-d,a]), error(E, _), true).", [["E = type_error(pair, a)"]]); assert_prolog_success!(&mut wam, "?- catch(keysort([a], [a-b]), error(E, _), true).", @@ -1443,7 +1443,7 @@ fn test_queries_on_builtins() assert_prolog_success!(&mut wam, "?- sort([], L).", [["L = []"]]); assert_prolog_success!(&mut wam, "?- catch(sort(_, []), error(E, _), true).", - [["E = type_error(list, _)"]]); + [["E = type_error(list, _12)"]]); assert_prolog_success!(&mut wam, "?- catch(sort([a,b,c], not_a_list), error(E, _), true).", [["E = type_error(list, not_a_list)"]]);