use printer cycle detection when printing partial strings (#1263), refactor skip_max_list functions

This commit is contained in:
Mark Thom
2022-02-06 21:12:54 -07:00
parent f3f3dccf8e
commit 9ba503cd6b
3 changed files with 107 additions and 88 deletions

View File

@@ -13,6 +13,7 @@ use crate::forms::*;
use crate::heap_iter::*; use crate::heap_iter::*;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::machine_indices::*; use crate::machine::machine_indices::*;
use crate::machine::machine_state::pstr_loc_and_offset;
use crate::machine::partial_string::*; use crate::machine::partial_string::*;
use crate::machine::streams::*; use crate::machine::streams::*;
use crate::types::*; use crate::types::*;
@@ -203,6 +204,7 @@ impl NumberFocus {
enum TokenOrRedirect { enum TokenOrRedirect {
Atom(Atom), Atom(Atom),
BarAsOp, BarAsOp,
Char(char),
Op(Atom, OpDesc), Op(Atom, OpDesc),
NumberedVar(String), NumberedVar(String),
CompositeRedirect(usize, DirectedOp), CompositeRedirect(usize, DirectedOp),
@@ -1130,20 +1132,23 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
let end_h = heap_pstr_iter.focus(); let end_h = heap_pstr_iter.focus();
let end_cell = heap_pstr_iter.focus; let end_cell = heap_pstr_iter.focus;
self.remove_list_children(focus);
if self.check_max_depth(&mut max_depth) { if self.check_max_depth(&mut max_depth) {
self.remove_list_children(focus);
self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
return; return;
} }
let at_cdr = self.at_cdr(","); let at_cdr = self.outputter.ends_with("|");
if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) {
self.remove_list_children(focus);
return self.print_proper_string(focus, max_depth); return self.print_proper_string(focus, max_depth);
} }
if self.ignore_ops { if self.ignore_ops {
self.at_cdr(",");
self.remove_list_children(focus);
if !self.print_string_as_functor(focus, max_depth) { if !self.print_string_as_functor(focus, max_depth) {
if end_cell == empty_list_as_cell!() { if end_cell == empty_list_as_cell!() {
append_str!(self, "[]"); append_str!(self, "[]");
@@ -1153,49 +1158,52 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
} }
} }
} else { } else {
let switch = if !at_cdr { let value = heap_bound_store(
push_char!(self, '['); self.iter.heap,
true heap_bound_deref(self.iter.heap, self.iter.heap[focus]),
} else { );
false
};
let heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus); read_heap_cell!(value,
(HeapCellValueTag::Lis) => {
let mut iter = heap_pstr_iter.chars(); return self.push_list(max_depth);
let mut char_count = 0;
while let Some(c) = iter.next() {
print_char!(self, self.quoted, c);
push_char!(self, ',');
char_count += 1;
if max_depth > 0 && max_depth <= char_count {
break;
} }
_ => {
let switch = Rc::new(Cell::new((!at_cdr, 0)));
self.state_stack.push(TokenOrRedirect::CloseList(switch.clone()));
let (h, offset) = pstr_loc_and_offset(self.iter.heap, focus);
let pstr = cell_as_string!(self.iter.heap[h]);
let pstr = pstr.as_str_from(offset.get_num() as usize);
if max_depth > 0 && pstr.chars().count() + 1 >= max_depth {
if value.get_tag() != HeapCellValueTag::CStr {
self.iter.pop_stack();
} }
self.state_stack.push(TokenOrRedirect::CloseList(Rc::new(Cell::new((switch, 0)))));
if self.max_depth > 0 && iter.next().is_some() {
self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
self.state_stack.push(TokenOrRedirect::HeadTailSeparator); self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
} else { } else if end_cell != empty_list_as_cell!() {
if iter.cycle_detected() {
self.iter.heap[end_h].set_forwarding_bit(true);
}
if end_cell != empty_list_as_cell!() {
self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
self.state_stack.push(TokenOrRedirect::HeadTailSeparator); self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
self.iter.push_stack(end_h);
}
} }
if self.outputter.ends_with(",") { for (char_count, c) in pstr.chars().rev().enumerate() {
self.outputter.truncate(self.outputter.len() - ','.len_utf8()); if max_depth > 0 && char_count + 1 >= max_depth {
break;
} }
self.state_stack.push(TokenOrRedirect::Char(c));
self.state_stack.push(TokenOrRedirect::Comma);
}
if let Some(TokenOrRedirect::Comma) = self.state_stack.last() {
self.state_stack.pop();
}
self.state_stack.push(TokenOrRedirect::OpenList(switch));
}
);
} }
} }
@@ -1507,6 +1515,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
match loc_data { match loc_data {
TokenOrRedirect::Atom(atom) => self.print_atom(atom), TokenOrRedirect::Atom(atom) => self.print_atom(atom),
TokenOrRedirect::BarAsOp => append_str!(self, " | "), TokenOrRedirect::BarAsOp => append_str!(self, " | "),
TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c),
TokenOrRedirect::Op(atom, _) => self.print_op(atom.as_str()), TokenOrRedirect::Op(atom, _) => self.print_op(atom.as_str()),
TokenOrRedirect::NumberedVar(num_var) => append_str!(self, &num_var), TokenOrRedirect::NumberedVar(num_var) => append_str!(self, &num_var),
TokenOrRedirect::CompositeRedirect(max_depth, op) => { TokenOrRedirect::CompositeRedirect(max_depth, op) => {

View File

@@ -5,6 +5,7 @@ use crate::forms::*;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::loader::CompilationTarget; use crate::machine::loader::CompilationTarget;
use crate::machine::machine_state::*; use crate::machine::machine_state::*;
use crate::machine::system_calls::BrentAlgState;
use crate::types::*; use crate::types::*;
pub type MachineStub = Vec<HeapCellValue>; pub type MachineStub = Vec<HeapCellValue>;
@@ -783,10 +784,10 @@ impl MachineState {
pub(super) fn check_sort_errors(&mut self) -> CallResult { pub(super) fn check_sort_errors(&mut self) -> CallResult {
let stub_gen = || functor_stub(atom!("sort"), 2); let stub_gen = || functor_stub(atom!("sort"), 2);
let list = self.registers[1]; let list = self.store(self.deref(self.registers[1]));
let sorted = self.registers[2]; let sorted = self.registers[2];
match self.detect_cycles(list) { match BrentAlgState::detect_cycles(&self.heap, list) {
CycleSearchResult::PartialList(..) => { CycleSearchResult::PartialList(..) => {
let err = self.instantiation_error(); let err = self.instantiation_error();
return Err(self.error_form(err, stub_gen())) return Err(self.error_form(err, stub_gen()))
@@ -798,7 +799,7 @@ impl MachineState {
_ => {} _ => {}
}; };
match self.detect_cycles(sorted) { match BrentAlgState::detect_cycles(&self.heap, sorted) {
CycleSearchResult::NotList if !sorted.is_var() => { CycleSearchResult::NotList if !sorted.is_var() => {
let err = self.type_error(ValidType::List, sorted); let err = self.type_error(ValidType::List, sorted);
Err(self.error_form(err, stub_gen())) Err(self.error_form(err, stub_gen()))
@@ -810,7 +811,7 @@ impl MachineState {
fn check_for_list_pairs(&mut self, mut list: HeapCellValue) -> CallResult { fn check_for_list_pairs(&mut self, mut list: HeapCellValue) -> CallResult {
let stub_gen = || functor_stub(atom!("keysort"), 2); let stub_gen = || functor_stub(atom!("keysort"), 2);
match self.detect_cycles(list) { match BrentAlgState::detect_cycles(&self.heap, list) {
CycleSearchResult::NotList if !list.is_var() => { CycleSearchResult::NotList if !list.is_var() => {
let err = self.type_error(ValidType::List, list); let err = self.type_error(ValidType::List, list);
Err(self.error_form(err, stub_gen())) Err(self.error_form(err, stub_gen()))
@@ -872,7 +873,7 @@ impl MachineState {
let pairs = self.store(self.deref(self[temp_v!(1)])); let pairs = self.store(self.deref(self[temp_v!(1)]));
let sorted = self.store(self.deref(self[temp_v!(2)])); let sorted = self.store(self.deref(self[temp_v!(2)]));
match self.detect_cycles(pairs) { match BrentAlgState::detect_cycles(&self.heap, pairs) {
CycleSearchResult::PartialList(..) => { CycleSearchResult::PartialList(..) => {
let err = self.instantiation_error(); let err = self.instantiation_error();
Err(self.error_form(err, stub_gen())) Err(self.error_form(err, stub_gen()))

View File

@@ -209,29 +209,27 @@ impl BrentAlgState {
} }
) )
} }
}
impl MachineState {
#[inline(always)] #[inline(always)]
pub fn brents_alg_step(&self, brent_st: &mut BrentAlgState) -> Option<CycleSearchResult> { fn cycle_step(&mut self, heap: &[HeapCellValue]) -> Option<CycleSearchResult> {
loop { loop {
let store_v = self.heap[brent_st.hare]; let value = heap[self.hare];
read_heap_cell!(store_v, read_heap_cell!(value,
(HeapCellValueTag::PStrLoc, h) => { (HeapCellValueTag::PStrLoc, h) => {
return brent_st.add_pstr_chars_and_step(&self.heap, h); return self.add_pstr_chars_and_step(&heap, h);
} }
(HeapCellValueTag::CStr | HeapCellValueTag::PStrOffset) => { (HeapCellValueTag::CStr | HeapCellValueTag::PStrOffset) => {
return brent_st.add_pstr_chars_and_step(&self.heap, brent_st.hare); return self.add_pstr_chars_and_step(&heap, self.hare);
} }
(HeapCellValueTag::Lis, h) => { (HeapCellValueTag::Lis, h) => {
return brent_st.step(h+1); return self.step(h+1);
} }
(HeapCellValueTag::Str, s) => { (HeapCellValueTag::Str, s) => {
let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity(); let (name, arity) = cell_as_atom_cell!(heap[s]).get_name_and_arity();
return if name == atom!(".") && arity == 2 { return if name == atom!(".") && arity == 2 {
brent_st.step(s+2) self.step(s+2)
} else { } else {
Some(CycleSearchResult::NotList) Some(CycleSearchResult::NotList)
}; };
@@ -240,18 +238,18 @@ impl MachineState {
debug_assert!(arity == 0); debug_assert!(arity == 0);
return if name == atom!("[]") { return if name == atom!("[]") {
Some(CycleSearchResult::ProperList(brent_st.num_steps())) Some(CycleSearchResult::ProperList(self.num_steps()))
} else { } else {
Some(CycleSearchResult::NotList) Some(CycleSearchResult::NotList)
}; };
} }
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
if brent_st.hare == h { if self.hare == h {
let r = store_v.as_var().unwrap(); let r = value.as_var().unwrap();
return Some(CycleSearchResult::PartialList(brent_st.num_steps(), r)); return Some(CycleSearchResult::PartialList(self.num_steps(), r));
} }
brent_st.hare = h; self.hare = h;
} }
_ => { _ => {
return Some(CycleSearchResult::NotList); return Some(CycleSearchResult::NotList);
@@ -260,25 +258,24 @@ impl MachineState {
} }
} }
pub fn detect_cycles(&self, value: HeapCellValue) -> CycleSearchResult { pub fn detect_cycles(heap: &[HeapCellValue], value: HeapCellValue) -> CycleSearchResult {
let store_v = self.store(self.deref(value));
let mut pstr_chars = 0; let mut pstr_chars = 0;
let hare = read_heap_cell!(store_v, let hare = read_heap_cell!(value,
(HeapCellValueTag::Lis, offset) => { (HeapCellValueTag::Lis, offset) => {
offset+1 offset+1
} }
(HeapCellValueTag::PStrLoc, h) => { (HeapCellValueTag::PStrLoc, h) => {
let (h_offset, n) = pstr_loc_and_offset(&self.heap, h); let (h_offset, n) = pstr_loc_and_offset(&heap, h);
let n = n.get_num() as usize; let n = n.get_num() as usize;
let pstr = cell_as_string!(self.heap[h_offset]); let pstr = cell_as_string!(heap[h_offset]);
pstr_chars = pstr.as_str_from(n).chars().count() - 1; pstr_chars = pstr.as_str_from(n).chars().count() - 1;
if self.heap[h].get_tag() == HeapCellValueTag::PStrOffset { if heap[h].get_tag() == HeapCellValueTag::PStrOffset {
debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::PStrOffset); debug_assert!(heap[h].get_tag() == HeapCellValueTag::PStrOffset);
if self.heap[h_offset].get_tag() == HeapCellValueTag::CStr { if heap[h_offset].get_tag() == HeapCellValueTag::CStr {
return CycleSearchResult::ProperList(pstr_chars + 1); return CycleSearchResult::ProperList(pstr_chars + 1);
} }
} }
@@ -293,7 +290,7 @@ impl MachineState {
return CycleSearchResult::ProperList(cstr.as_str_from(0).chars().count()); return CycleSearchResult::ProperList(cstr.as_str_from(0).chars().count());
} }
(HeapCellValueTag::Str, s) => { (HeapCellValueTag::Str, s) => {
let (name, arity) = cell_as_atom_cell!(self.heap[s]) let (name, arity) = cell_as_atom_cell!(heap[s])
.get_name_and_arity(); .get_name_and_arity();
if name == atom!("[]") && arity == 0 { if name == atom!("[]") && arity == 0 {
@@ -312,7 +309,7 @@ impl MachineState {
}; };
} }
(HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => { (HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => {
return CycleSearchResult::PartialList(0, store_v.as_var().unwrap()); return CycleSearchResult::PartialList(0, value.as_var().unwrap());
} }
_ => { _ => {
return CycleSearchResult::NotList; return CycleSearchResult::NotList;
@@ -325,17 +322,20 @@ impl MachineState {
brent_st.pstr_chars = pstr_chars; brent_st.pstr_chars = pstr_chars;
loop { loop {
if let Some(result) = self.brents_alg_step(&mut brent_st) { if let Some(result) = brent_st.cycle_step(heap) {
return result; return result;
} }
} }
} }
pub fn detect_cycles_with_max(&self, max_steps: usize, value: HeapCellValue) -> CycleSearchResult { pub fn detect_cycles_with_max(
let store_v = self.store(self.deref(value)); heap: &[HeapCellValue],
max_steps: usize,
value: HeapCellValue,
) -> CycleSearchResult {
let mut pstr_chars = 0; let mut pstr_chars = 0;
let hare = read_heap_cell!(store_v, let hare = read_heap_cell!(value,
(HeapCellValueTag::Lis, offset) => { (HeapCellValueTag::Lis, offset) => {
if max_steps > 0 { if max_steps > 0 {
offset+1 offset+1
@@ -344,16 +344,16 @@ impl MachineState {
} }
} }
(HeapCellValueTag::PStrLoc, h) => { (HeapCellValueTag::PStrLoc, h) => {
let (h_offset, n) = pstr_loc_and_offset(&self.heap, h); let (h_offset, n) = pstr_loc_and_offset(&heap, h);
let n = n.get_num() as usize; let n = n.get_num() as usize;
let pstr = cell_as_string!(self.heap[h_offset]); let pstr = cell_as_string!(heap[h_offset]);
pstr_chars = pstr.as_str_from(n).chars().count() - 1; pstr_chars = pstr.as_str_from(n).chars().count() - 1;
if self.heap[h].get_tag() == HeapCellValueTag::PStrOffset { if heap[h].get_tag() == HeapCellValueTag::PStrOffset {
debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::PStrOffset); debug_assert!(heap[h].get_tag() == HeapCellValueTag::PStrOffset);
if self.heap[h_offset].get_tag() == HeapCellValueTag::CStr { if heap[h_offset].get_tag() == HeapCellValueTag::CStr {
return if pstr_chars + 1 <= max_steps { return if pstr_chars + 1 <= max_steps {
CycleSearchResult::ProperList(pstr_chars + 1) CycleSearchResult::ProperList(pstr_chars + 1)
} else { } else {
@@ -386,7 +386,7 @@ impl MachineState {
}; };
} }
(HeapCellValueTag::Str, s) => { (HeapCellValueTag::Str, s) => {
let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity(); let (name, arity) = cell_as_atom_cell!(heap[s]).get_name_and_arity();
if name == atom!("[]") && arity == 0 { if name == atom!("[]") && arity == 0 {
return CycleSearchResult::EmptyList; return CycleSearchResult::EmptyList;
@@ -408,7 +408,7 @@ impl MachineState {
}; };
} }
(HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => { (HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => {
return CycleSearchResult::PartialList(0, store_v.as_var().unwrap()); return CycleSearchResult::PartialList(0, value.as_var().unwrap());
} }
_ => { _ => {
return CycleSearchResult::NotList; return CycleSearchResult::NotList;
@@ -422,15 +422,17 @@ impl MachineState {
loop { loop {
if brent_st.num_steps() >= max_steps { if brent_st.num_steps() >= max_steps {
return brent_st.to_result(&self.heap); return brent_st.to_result(&heap);
} }
if let Some(result) = self.brents_alg_step(&mut brent_st) { if let Some(result) = brent_st.cycle_step(heap) {
return result; return result;
} }
} }
} }
}
impl MachineState {
fn skip_max_list_cycle(&mut self, lam: usize) { fn skip_max_list_cycle(&mut self, lam: usize) {
fn step(heap: &[HeapCellValue], mut value: HeapCellValue) -> usize { fn step(heap: &[HeapCellValue], mut value: HeapCellValue) -> usize {
loop { loop {
@@ -479,10 +481,10 @@ impl MachineState {
let mut brent_st = BrentAlgState::new(h); let mut brent_st = BrentAlgState::new(h);
self.brents_alg_step(&mut brent_st); brent_st.cycle_step(&self.heap);
while prev_hare != brent_st.hare { while prev_hare != brent_st.hare {
self.brents_alg_step(&mut brent_st); brent_st.cycle_step(&self.heap);
} }
self.heap.pop(); self.heap.pop();
@@ -507,9 +509,16 @@ impl MachineState {
fn skip_max_list_result(&mut self, max_steps: i64) { fn skip_max_list_result(&mut self, max_steps: i64) {
let search_result = if max_steps == -1 { let search_result = if max_steps == -1 {
self.detect_cycles(self.registers[3]) BrentAlgState::detect_cycles(
&self.heap,
self.store(self.deref(self.registers[3])),
)
} else { } else {
self.detect_cycles_with_max(max_steps as usize, self.registers[3]) BrentAlgState::detect_cycles_with_max(
&self.heap,
max_steps as usize,
self.store(self.deref(self.registers[3])),
)
}; };
match search_result { match search_result {