diff --git a/src/heap_iter.rs b/src/heap_iter.rs index 0bfbd465..9dd30b7e 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -1,5 +1,6 @@ #[cfg(test)] -pub(crate) use crate::machine::gc::{IteratorUMP, StacklessPreOrderHeapIter}; +pub(crate) use crate::machine::gc::{IteratorUMP}; +pub(crate) use crate::machine::gc::{CycleDetectorUMP, StacklessPreOrderHeapIter}; use crate::atom_table::*; use crate::machine::heap::*; @@ -12,6 +13,145 @@ use modular_bitfield::prelude::*; use std::ops::Deref; use std::vec::Vec; +#[inline(always)] +pub fn eager_stackful_preorder_iter( + heap: &mut Heap, + value: HeapCellValue, +) -> EagerStackfulPreOrderHeapIter { + EagerStackfulPreOrderHeapIter::new(heap, value) +} + +/* + * Unlike StackfulPreOrderHeapIter, this iterator not only marks + * cyclic terms for the sake of skipping them at the second visit but + * leaves them marked until it is dropped. This makes for, e.g., more + * efficient ground/1 and term_variables/2 definitions. + */ + +pub struct EagerStackfulPreOrderHeapIter<'a> { + start_value: HeapCellValue, + iter_stack: Vec, + mark_phase: bool, + heap: &'a mut Heap, +} + +impl<'a> Drop for EagerStackfulPreOrderHeapIter<'a> { + fn drop(&mut self) { + self.mark_phase = false; + + self.iter_stack.clear(); + self.start_value.set_mark_bit(true); + self.iter_stack.push(self.start_value); + + while let Some(_) = self.follow() {} + } +} + +impl<'a> EagerStackfulPreOrderHeapIter<'a> { + pub fn new(heap: &'a mut Heap, value: HeapCellValue) -> Self { + Self { + start_value: value, + iter_stack: vec![value], + mark_phase: true, + heap, + } + } + + #[inline] + fn is_self_ref_var(&self, value: HeapCellValue) -> bool { + if value.is_var() { + let h = value.get_value() as usize; + + if self.heap[h].is_var() && self.heap[h].get_value() as usize == h { + return true; + } + } + + false + } + + fn follow(&mut self) -> Option { + while let Some(value) = self.iter_stack.pop() { + if value.get_mark_bit() == self.mark_phase { + // follow marked variables to their end. only marked + // non-variables are ignored. + if self.is_self_ref_var(value) { + return Some(unmark_cell_bits!(value)); + } else if !value.is_var() { + continue; + } + } + + read_heap_cell!(value, + (HeapCellValueTag::Str, s) => { + let arity = cell_as_atom_cell!(self.heap[s]).get_arity(); + + for idx in (s + 1 .. s + arity + 1).rev() { + if self.heap[idx].get_mark_bit() != self.mark_phase { + self.iter_stack.push(self.heap[idx]); + self.heap[idx].set_mark_bit(self.mark_phase); + } + } + } + (HeapCellValueTag::Lis, l) => { + if self.heap[l+1].get_mark_bit() != self.mark_phase { + self.iter_stack.push(self.heap[l+1]); + self.heap[l+1].set_mark_bit(self.mark_phase); + } + + if self.heap[l].get_mark_bit() != self.mark_phase { + self.iter_stack.push(self.heap[l]); + self.heap[l].set_mark_bit(self.mark_phase); + } + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { + let var_value = self.heap[h]; + self.heap[h].set_mark_bit(self.mark_phase); + + if !(self.heap[h].is_var() && self.heap[h].get_value() as usize == h) { + self.iter_stack.push(var_value); + continue; + } + } + (HeapCellValueTag::PStrLoc, h) => { + let h = if self.heap[h].get_tag() == HeapCellValueTag::PStr { + h + } else { + debug_assert_eq!(self.heap[h].get_tag(), HeapCellValueTag::PStrOffset); + self.heap[h].get_value() as usize + }; + + if self.heap[h].get_mark_bit() == self.mark_phase { + continue; + } + + let value = self.heap[h+1]; + + self.heap[h].set_mark_bit(self.mark_phase); + self.heap[h+1].set_mark_bit(self.mark_phase); + + self.iter_stack.push(value); + } + _ => { + } + ); + + return Some(value); + } + + None + } +} + +impl<'a> Iterator for EagerStackfulPreOrderHeapIter<'a> { + type Item = HeapCellValue; + + #[inline] + fn next(&mut self) -> Option { + self.follow() + } +} + #[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)] #[bits = 2] enum IterStackLocTag { @@ -198,11 +338,6 @@ impl<'a, ElideLists> StackfulPreOrderHeapIter<'a, ElideLists> { None } - #[inline] - pub fn stack_len(&self) -> usize { - self.stack.len() - } - fn push_if_unmarked(&mut self, loc: IterStackLoc) { let cell = self.read_cell_mut(loc); @@ -236,17 +371,17 @@ impl<'a, ElideLists: ListElisionPolicy> StackfulPreOrderHeapIter<'a, ElideLists> let cell = self.read_cell(loc); read_heap_cell!(cell, - (HeapCellValueTag::Lis | - HeapCellValueTag::Str | - HeapCellValueTag::PStrLoc, vh) => { + (HeapCellValueTag::Lis, vh) => { let forward = if ElideLists::elide_lists() { true } else { cell.get_mark_bit() }; if forward && self.heap[vh].get_mark_bit() { self.read_cell_mut(loc).set_forwarding_bit(true); } } - (HeapCellValueTag::AttrVar | - HeapCellValueTag::Var, vh) => { + (HeapCellValueTag::Str | + HeapCellValueTag::AttrVar | + HeapCellValueTag::Var | + HeapCellValueTag::PStrLoc, vh) => { if self.heap[vh].get_mark_bit() { self.read_cell_mut(loc).set_forwarding_bit(true); } @@ -374,11 +509,20 @@ impl<'a, ElideLists: ListElisionPolicy> Iterator for StackfulPreOrderHeapIter<'a #[inline(always)] pub(crate) fn stackless_preorder_iter( heap: &mut Vec, - cell: HeapCellValue, + start: usize, ) -> StacklessPreOrderHeapIter { - StacklessPreOrderHeapIter::::new(heap, cell) + StacklessPreOrderHeapIter::::new(heap, start) } + +pub(crate) fn cycle_detecting_stackless_preorder_iter( + heap: &mut Heap, + start: usize, +) -> StacklessPreOrderHeapIter { + StacklessPreOrderHeapIter::::new(heap, start) +} + + #[inline(always)] pub(crate) fn stackful_preorder_iter<'a, ElideLists: ListElisionPolicy>( heap: &'a mut Vec, @@ -529,9 +673,9 @@ pub(crate) type RightistPostOrderHeapIter<'a> = #[inline] pub(crate) fn stackless_post_order_iter<'a>( heap: &'a mut Heap, - cell: HeapCellValue, + start: usize, ) -> RightistPostOrderHeapIter<'a> { - PostOrderIterator::new(stackless_preorder_iter(heap, cell)) + PostOrderIterator::new(stackless_preorder_iter(heap, start)) } #[cfg(test)] @@ -551,8 +695,10 @@ mod tests { .heap .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + wam.machine_st.heap.push(str_loc_as_cell!(0)); + { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 3); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -584,8 +730,10 @@ mod tests { ] )); + wam.machine_st.heap.push(str_loc_as_cell!(0)); + for _ in 0..20 { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 5); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -628,7 +776,7 @@ mod tests { )); for _ in 0..200000 { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -659,7 +807,7 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -680,7 +828,7 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -715,7 +863,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -749,8 +897,10 @@ mod tests { put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 2); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!( @@ -764,8 +914,7 @@ mod tests { assert_eq!(wam.machine_st.heap[0], pstr_cell); assert_eq!(wam.machine_st.heap[1], heap_loc_as_cell!(1)); - wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); + wam.machine_st.heap[1] = pstr_loc_as_cell!(3); let pstr_second_var_cell = put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); @@ -773,36 +922,40 @@ mod tests { let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 2); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - heap_loc_as_cell!(3), + heap_loc_as_cell!(4), ); assert!(iter.next().is_none()); } assert_eq!(wam.machine_st.heap[0], pstr_cell); - assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(2)); - assert_eq!(wam.machine_st.heap[2], pstr_second_cell); - assert_eq!(wam.machine_st.heap[3], heap_loc_as_cell!(3)); + assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(3)); + assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(0)); + assert_eq!(wam.machine_st.heap[3], pstr_second_cell); + assert_eq!(wam.machine_st.heap[4], heap_loc_as_cell!(4)); wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(4)); + wam.machine_st.heap.push(pstr_loc_as_cell!(5)); wam.machine_st.heap.push(pstr_offset_as_cell!(0)); wam.machine_st .heap .push(fixnum_as_cell!(Fixnum::build_with(2))); + wam.machine_st.heap[2] = heap_loc_as_cell!(4); + { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(4)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 2); let pstr_offset_cell = pstr_offset_as_cell!(0); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); + assert_eq!(unmark_cell_bits!(iter.next().unwrap()), fixnum_as_cell!(Fixnum::build_with(2))); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); @@ -812,19 +965,19 @@ mod tests { assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(5) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), + pstr_offset_as_cell!(0) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); @@ -840,31 +993,31 @@ mod tests { .heap .push(fixnum_as_cell!(Fixnum::build_with(0i64))); + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 6); let pstr_offset_cell = pstr_offset_as_cell!(0); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); + assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_loc_as_cell!(4)); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); + assert_eq!(unmark_cell_bits!(iter.next().unwrap()), fixnum_as_cell!(Fixnum::build_with(0))); assert_eq!(iter.next(), None); } all_cells_unmarked(&wam.machine_st.heap); - wam.machine_st.heap.pop(); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(1i64))); + wam.machine_st.heap[5] = fixnum_as_cell!(Fixnum::build_with(1i64)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 6); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); + assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_loc_as_cell!(4)); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -874,13 +1027,17 @@ mod tests { unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0) ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + fixnum_as_cell!(Fixnum::build_with(1)) + ); assert_eq!(iter.next(), None); - - assert_eq!(iter.heap[4], pstr_offset_as_cell!(0)); - assert_eq!(iter.heap[5], fixnum_as_cell!(Fixnum::build_with(1i64))); } + assert_eq!(wam.machine_st.heap[4], pstr_offset_as_cell!(0)); + assert_eq!(wam.machine_st.heap[5], fixnum_as_cell!(Fixnum::build_with(1i64))); + all_cells_unmarked(&wam.machine_st.heap); wam.machine_st.heap.clear(); @@ -896,7 +1053,7 @@ mod tests { wam.machine_st.heap.extend(functor); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -933,26 +1090,13 @@ mod tests { atom_as_cell!(f_atom, 3) ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(a_atom) - ); - assert_eq!(iter.next(), None); } all_cells_unmarked(&wam.machine_st.heap); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -999,7 +1143,7 @@ mod tests { assert_eq!(wam.machine_st.heap[4], empty_list_as_cell!()); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1031,7 +1175,7 @@ mod tests { wam.machine_st.heap[4] = list_loc_as_cell!(1); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1069,19 +1213,6 @@ mod tests { atom_as_cell!(f_atom, 3) ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(a_atom) - ); - assert_eq!(iter.next(), None); } @@ -1093,12 +1224,12 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(2)); wam.machine_st.heap.push(heap_loc_as_cell!(3)); wam.machine_st.heap.push(heap_loc_as_cell!(3)); + wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 4); assert_eq!(iter.next().unwrap(), heap_loc_as_cell!(3)); - assert_eq!(iter.next(), None); } @@ -1129,7 +1260,7 @@ mod tests { wam.machine_st.heap.push(list_loc_as_cell!(1)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); assert_eq!( @@ -1137,10 +1268,6 @@ mod tests { list_loc_as_cell!(1) ); - assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); - // this is what happens! this next line! We would like it not to happen though. - assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); - assert_eq!(iter.next(), None); } @@ -1176,9 +1303,10 @@ mod tests { wam.machine_st.heap.push(attr_var_as_cell!(11)); // linked from 7. wam.machine_st.heap.push(heap_loc_as_cell!(12)); + wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 13); assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); @@ -1217,6 +1345,7 @@ mod tests { let clpz_atom = atom!("clpz"); let p_atom = atom!("p"); + wam.machine_st.heap.pop(); wam.machine_st.heap.pop(); wam.machine_st.heap.push(heap_loc_as_cell!(13)); // 12 @@ -1231,9 +1360,10 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); // 21 wam.machine_st.heap.push(atom_as_cell!(p_atom, 1)); // 22 wam.machine_st.heap.push(heap_loc_as_cell!(23)); // 23 + wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 24); assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); @@ -1368,10 +1498,9 @@ mod tests { wam.machine_st.heap.clear(); { - let mut iter = stackless_preorder_iter( - &mut wam.machine_st.heap, - fixnum_as_cell!(Fixnum::build_with(0)), - ); + wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0))); + + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1383,8 +1512,6 @@ mod tests { all_cells_unmarked(&wam.machine_st.heap); - assert_eq!(wam.machine_st.heap.len(), 0); - wam.machine_st.heap.clear(); wam.machine_st.heap.push(str_loc_as_cell!(1)); @@ -1394,7 +1521,7 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(atom!("y"))); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(1)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1406,7 +1533,7 @@ mod tests { atom_as_cell!(atom!("y")) ); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(1)); + assert_eq!(unmark_cell_bits!(iter.next().unwrap()), heap_loc_as_cell!(0)); assert!(iter.next().is_none()); } @@ -1418,9 +1545,10 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(atom!("g"), 2)); wam.machine_st.heap.push(str_loc_as_cell!(0)); wam.machine_st.heap.push(atom_as_cell!(atom!("y"))); + wam.machine_st.heap.push(str_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 3); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1453,7 +1581,7 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(7)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 7); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1511,9 +1639,10 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(atom!("f"), 2)); wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(1)); + wam.machine_st.heap.push(str_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 3); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1540,38 +1669,68 @@ mod tests { wam.machine_st.heap.clear(); // representation of one of the heap terms as in issue #1384. - /* - wam.machine_st.heap.push(list_loc_as_cell!(7)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(list_loc_as_cell!(5)); - wam.machine_st.heap.push(empty_list_as_cell!()); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); - wam.machine_st.heap.push(empty_list_as_cell!()); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); + wam.machine_st.heap.push(list_loc_as_cell!(7)); // 0 + wam.machine_st.heap.push(heap_loc_as_cell!(0)); // 1 + wam.machine_st.heap.push(list_loc_as_cell!(3)); // 2 + wam.machine_st.heap.push(list_loc_as_cell!(5)); // 3 + wam.machine_st.heap.push(empty_list_as_cell!()); // 4 + wam.machine_st.heap.push(heap_loc_as_cell!(2)); // 5 + wam.machine_st.heap.push(heap_loc_as_cell!(2)); // 6 + wam.machine_st.heap.push(empty_list_as_cell!()); // 7 + wam.machine_st.heap.push(heap_loc_as_cell!(3)); // 8 - { - let mut iter = stackless_preorder_iter( - &mut wam.machine_st.heap, - heap_loc_as_cell!(0), - ); + wam.machine_st.heap.push(heap_loc_as_cell!(0)); - while let Some(_) = iter.next() { - print_heap_terms(iter.heap.iter(), 0); - println!(""); - } + { + let mut iter = stackless_preorder_iter( + &mut wam.machine_st.heap, + 9, + ); - /* - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(atom!("f"), 2) - ); + /* + while let Some(_) = iter.next() { + print_heap_terms(iter.heap.iter(), 0); + println!(""); + } + */ - assert!(iter.next().is_none()); - */ - } - */ + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + list_loc_as_cell!(7) + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + list_loc_as_cell!(5) + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + heap_loc_as_cell!(2) + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + list_loc_as_cell!(3) + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + empty_list_as_cell!() + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + heap_loc_as_cell!(2) + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + empty_list_as_cell!() + ); + + assert_eq!(iter.next(), None); + } } #[test] @@ -2663,8 +2822,10 @@ mod tests { .heap .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + wam.machine_st.heap.push(str_loc_as_cell!(0)); + { - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 3); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2694,8 +2855,10 @@ mod tests { ] )); + wam.machine_st.heap.push(str_loc_as_cell!(0)); + for _ in 0..20 { - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 5); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0)); @@ -2726,7 +2889,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + stackless_post_order_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2742,8 +2905,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2769,7 +2931,7 @@ mod tests { { let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + stackless_post_order_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2802,7 +2964,7 @@ mod tests { { let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + stackless_post_order_iter(&mut wam.machine_st.heap, 0); // the cycle will be iterated twice before being detected. assert_eq!( @@ -2831,7 +2993,7 @@ mod tests { { let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + stackless_post_order_iter(&mut wam.machine_st.heap, 0); // cut the iteration short to check that all cells are // unmarked and unforwarded by the Drop instance of @@ -2865,9 +3027,11 @@ mod tests { put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + stackless_post_order_iter(&mut wam.machine_st.heap, 2); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2879,6 +3043,7 @@ mod tests { assert_eq!(iter.next(), None); } + wam.machine_st.heap.pop(); wam.machine_st.heap.pop(); wam.machine_st.heap.push(pstr_loc_as_cell!(2)); @@ -2887,9 +3052,10 @@ mod tests { let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 4); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2914,9 +3080,10 @@ mod tests { .heap .push(fixnum_as_cell!(Fixnum::build_with(0))); + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 7); let mut pstr_loc_cell = pstr_loc_as_cell!(0); pstr_loc_cell.set_forwarding_bit(true); @@ -2924,11 +3091,7 @@ mod tests { // assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(0i64))); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) + heap_loc_as_cell!(3) ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); @@ -2939,28 +3102,27 @@ mod tests { all_cells_unmarked(&wam.machine_st.heap); + wam.machine_st.heap.pop(); wam.machine_st.heap.pop(); wam.machine_st .heap .push(fixnum_as_cell!(Fixnum::build_with(1))); + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 7); //assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(1))); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) + heap_loc_as_cell!(3) ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) + pstr_second_cell ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(iter.next(), None); } @@ -2976,9 +3138,10 @@ mod tests { wam.machine_st.heap.extend(functor); + wam.machine_st.heap.push(heap_loc_as_cell!(0)); + { - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 9); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -3008,18 +3171,6 @@ mod tests { list_loc_as_cell!(3) ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(a_atom) - ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), atom_as_cell!(f_atom, 3) @@ -3038,8 +3189,7 @@ mod tests { wam.machine_st.heap[4] = list_loc_as_cell!(1); { - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -3059,25 +3209,15 @@ mod tests { atom_as_cell!(f_atom, 3) ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(a_atom) - ); - assert_eq!( unmark_cell_bits!(iter.next().unwrap()), atom_as_cell!(f_atom, 3) ); - assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + list_loc_as_cell!(1) + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index b7b60df9..34f607f6 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -328,6 +328,9 @@ staggered_sc(_, G) :- call(G). % to reason about the programs. Also restricts the ability to run the program with alternative execution strategies !. +:- non_counted_backtracking get_cp/1. +get_cp(B) :- '$get_cp'(B). + :- non_counted_backtracking set_cp/1. set_cp(B) :- '$set_cp'(B). @@ -359,24 +362,21 @@ cont_list_goal(Conts, '$call'(builtins:dispatch_call_list(Conts))). :- non_counted_backtracking dispatch_prep/3. -dispatch_prep(Gs, B, [Cont|Conts]) :- +dispatch_prep(Gs, B, Conts) :- ( callable(Gs) -> strip_module(Gs, M, Gs0), ( nonvar(Gs0), - dispatch_prep_(Gs0, B, [Cont|Conts]) -> + dispatch_prep_(Gs0, B, Conts) -> true ; Gs0 == ! -> - Cont = '$call'(builtins:set_cp(B)), - Conts = [] + Conts = ['$call'(builtins:set_cp(B))] ; nonvar(Gs0), \+ callable(Gs0) -> throw(dispatch_prep_error) - ; Cont = Gs, - Conts = [] + ; Conts = [Gs] ) ; var(Gs) -> - Cont = Gs, - Conts = [] + Conts = [Gs] ; throw(dispatch_prep_error) ). @@ -387,20 +387,32 @@ dispatch_prep_((G1, G2), B, [Cont|Conts]) :- dispatch_prep(G1, B, IConts1), cont_list_goal(IConts1, Cont), dispatch_prep(G2, B, Conts). -dispatch_prep_((G1 ; G2), B, [Cont|Conts]) :- - dispatch_prep(G1, B, IConts0), +dispatch_prep_((G1 ; G2), B, Conts) :- + ( nonvar(G1) -> + ( G1 = (G11 -> G12) -> + dispatch_prep(G11, B, IConts2), + dispatch_prep(G12, B, IConts3), + cont_list_goal(IConts2, Cont2), + cont_list_goal(IConts3, Cont3), + Cont0 = '$call'(builtins:staggered_if_then(Cont2, Cont3)) + ; dispatch_prep(G1, B, IConts0), + dispatch_prep(G2, B, IConts1), + cont_list_goal(IConts0, Cont0) + ) + ; dispatch_prep(G1, B1, IConts0), + cont_list_goal(IConts0, Cont0) + ), dispatch_prep(G2, B, IConts1), cont_list_goal(IConts0, Cont0), cont_list_goal(IConts1, Cont1), - Cont = '$call'(builtins:staggered_sc(Cont0, Cont1)), - Conts = []. -dispatch_prep_((G1 -> G2), B, [Cont|Conts]) :- - dispatch_prep(G1, B, IConts1), + Conts = ['$call'(builtins:staggered_sc(Cont0, Cont1))]. +dispatch_prep_((G1 -> G2), B, Conts) :- + dispatch_prep(G1, B1, IConts1), dispatch_prep(G2, B, IConts2), cont_list_goal(IConts1, Cont1), cont_list_goal(IConts2, Cont2), - Cont = '$call'(builtins:staggered_if_then(Cont1, Cont2)), - Conts = []. + Conts = ['$call'(builtins:get_cp(B1)), + '$call'(builtins:staggered_if_then(Cont1, Cont2))]. :- non_counted_backtracking dispatch_call_list/1. diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index f438043d..f766771a 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -1030,8 +1030,8 @@ term_expansion(Term0, Term) :- once(duodcg_body(Body0, Body, As0, As, Bs0, Bs)). duodcg_body([], (As0=As,Bs0=Bs), As0, As, Bs0, Bs). -duodcg_body(Xs+Ys, (phrase(list(Xs), As0, As), - phrase(list(Ys), Bs0, Bs)), As0, As, Bs0, Bs). +duodcg_body(Xs+Ys, (phrase(seq(Xs), As0, As), + phrase(seq(Ys), Bs0, Bs)), As0, As, Bs0, Bs). duodcg_body({Goal}, call(Goal), As, As, Bs, Bs). duodcg_body((A0,B0), (A,B), As0, As, Bs0, Bs) :- duodcg_body(A0, A, As0, As1, Bs0, Bs1), @@ -1957,7 +1957,6 @@ choice_order_variable(step, Order, Var, Vars, Vars0, Selection, Consistency) :- ( Var = Next, label(Vars, Selection, Order, step, Consistency) ; neq_num(Var, Next), - do_queue, label(Vars0, Selection, Order, step, Consistency) ). choice_order_variable(enum, Order, Var, Vars, _, Selection, Consistency) :- @@ -2566,13 +2565,13 @@ parse_clpz(E, R, g(power_var_num(E, V, N)) => [p(pexp(V, N, R))], m(A*B) => [p(ptimes(A, B, R))], m(A-B) => [p(pplus(R,B,A))], - m(-A) => [p(ptimes(-1,A,R))], + m(-A) => [p(pplus(A,R,0))], m(max(A,B)) => [g(A #=< #R), g(B #=< R), p(pmax(A, B, R))], m(min(A,B)) => [g(A #>= #R), g(B #>= R), p(pmin(A, B, R))], m(A mod B) => [g(B #\= 0), p(pmod(A, B, R))], m(A rem B) => [g(B #\= 0), p(prem(A, B, R))], m(abs(A)) => [g(#R #>= 0), p(pabs(A, R))], - m(A/B) => [g(B #\= 0), p(prdiv(A, B, R))], + m(A/B) => [g(B #\= 0), p(ptimes(R, B, A))], m(A//B) => [g(B #\= 0), p(ptzdiv(A, B, R))], m(A div B) => [g(#R #= (A - (A mod B)) // B)], m(A^B) => [p(pexp(A, B, R))], @@ -2636,14 +2635,37 @@ parse_goals([]) --> []. parse_goals([G|Gs]) --> parse_goal(G), parse_goals(Gs). parse_goal(g(Goal)) --> [Goal]. -parse_goal(p(Prop)) --> - { term_variables(Prop, Vs) }, +parse_goal(p(Prop0)) --> + { term_variables(Prop0, Vs), + morphing_propagator(Prop0, Prop, _) }, [make_propagator(Prop, P), new_queue(Q0), phrase(init_propagator_(Vs, P), [Q0], [Q]), variables_same_queue(Vs), trigger_once_(P, Q)]. +morphing(pplus). +morphing(ptimes). +morphing(pexp). +morphing(ptzdiv). + +morphing_propagator(P0, P, Target) :- + P0 =.. [F|Args0], + ( morphing(F) -> + append(Args0, [Last], Args), + Target = p(Last) + ; Args = Args0, + Target = none + ), + P =.. [F|Args]. + +morph_into_propagator(MState, Vs, P0, Morph) --> + kill(MState), + { morphing_propagator(P0, P, _), + make_propagator(P, Morph) }, + init_propagator_(Vs, Morph), + trigger_prop(Morph). + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ?- use_module(library(lists)), use_module(library(format)), @@ -2734,14 +2756,12 @@ geq(A, B) :- ) ; ( AI cis_geq n(B) -> true ; domain_remove_smaller_than(AD, B, AD1), - fd_put(A, AD1, APs), - do_queue + fd_put(A, AD1, APs) ) ) ; fd_get(B, BD, BPs) -> domain_remove_greater_than(BD, A, BD1), - fd_put(B, BD1, BPs), - do_queue + fd_put(B, BD1, BPs) ; A >= B ). @@ -2811,7 +2831,7 @@ matches([ m(var(X) #= var(Y)+var(Z)) => [p(pplus(Y,Z,X))], m(var(X) #= var(Y)-var(Z)) => [p(pplus(X,Z,Y))], m(var(X) #= var(Y)*var(Z)) => [p(ptimes(Y,Z,X))], - m(var(X) #= -var(Z)) => [p(ptimes(-1, Z, X))], + m(var(X) #= -var(Y)) => [p(pplus(X,Y,0))], m_c(any(X) #= any(Y), left_right_linsum_const(X, Y, Cs, Vs, S)) => [g(scalar_product_(#=, Cs, Vs, S))], m_c(var(X) #= abs(var(Y)) + any(V0), X == Y) => [d(V0,V),p(x_eq_abs_plus_v(X,V))], @@ -2907,8 +2927,9 @@ match_goals([G|Gs], F) --> match_goal(G, F), match_goals(Gs, F). match_goal(r(X,Y), F) --> { G =.. [F,X,Y] }, [G]. match_goal(d(X,Y), _) --> [parse_clpz(X, Y)]. match_goal(g(Goal), _) --> [Goal]. -match_goal(p(Prop), _) --> - { term_variables(Prop, Vs) }, +match_goal(p(Prop0), _) --> + { term_variables(Prop0, Vs), + morphing_propagator(Prop0, Prop, _) }, [make_propagator(Prop, P), new_queue(Q0), phrase(init_propagator_(Vs, P), [Q0], [Q]), @@ -3296,7 +3317,7 @@ integer_kroot_leq(L, U, N, K, R) :- % When reasoning over integers, replace (=\=)/2 by (#\=)/2 to obtain more % general relations. -X #\= Y :- clpz_neq(X, Y), do_queue. +X #\= Y :- clpz_neq(X, Y). % X #\= Y + Z @@ -3359,7 +3380,7 @@ X #< Y :- Y #> X. % X in inf.. -4\/1..9\/81..sup. % ``` -#\ Q :- reify(Q, 0), do_queue. +#\ Q :- reify(Q, 0). %% #<==>(?P, ?Q) % @@ -3397,7 +3418,7 @@ X #< Y :- Y #> X. % Z = 2. % ``` -L #<==> R :- reify(L, B), reify(R, B), do_queue. +L #<==> R :- reify(L, B), reify(R, B). %% #==>(?P, ?Q) % @@ -3432,7 +3453,7 @@ L #<== R :- R #==> L. % % P and Q hold. -L #/\ R :- reify(L, 1), reify(R, 1), do_queue. +L #/\ R :- reify(L, 1), reify(R, 1). conjunctive_neqs_var_drep(Eqs, Var, Drep) :- conjunctive_neqs_var(Eqs, Var), @@ -3520,16 +3541,17 @@ L #\ R :- (L #\/ R) #/\ #\ (L #/\ R). d(D) that states D is 1 iff all subexpressions are defined. a(V) means that V is an auxiliary variable that was introduced while parsing a compound expression. a(X,V) means V is auxiliary unless - it is ==/2 X, and a(X,Y,V) means V is auxiliary unless it is ==/2 X - or Y. l(L) means the literal L occurs in the described list. + it is (==)/2 X, and a(X,Y,V) means V is auxiliary unless it is + (==)/2 X or Y. l(L) means the literal L occurs in the described + list, and ls(Ls) means the literals Ls occur in the described list. When a constraint becomes entailed or subexpressions become undefined, created auxiliary constraints are killed, and the "clpz" attribute is removed from auxiliary variables. - For mod/2, div/2, rem/2 etc. we create a skeleton propagator and - remember it as an auxiliary constraint. The pskeleton propagator - can use the skeleton when the constraint is defined. + For (//)/2, (mod)/2 and (rem)/2, we create a skeleton propagator + and remember it as an auxiliary constraint. The pskeleton + propagator can use the skeleton when the constraint is defined. We cannot use a skeleton propagator for (/)/2, since (/)/2 can fail in cases such as 0 #==> X #= 1/2, where we expect success. @@ -3545,27 +3567,32 @@ parse_reified(E, R, D, m(A+B) => [d(D), p(pplus(A,B,R)), a(A,B,R)], m(A*B) => [d(D), p(ptimes(A,B,R)), a(A,B,R)], m(A-B) => [d(D), p(pplus(R,B,A)), a(A,B,R)], - m(-A) => [d(D), p(ptimes(-1,A,R)), a(R)], + m(-A) => [d(D), p(pplus(A,R,0)), a(R)], m(max(A,B)) => [d(D), p(pgeq(R, A)), p(pgeq(R, B)), p(pmax(A,B,R)), a(A,B,R)], m(min(A,B)) => [d(D), p(pgeq(A, R)), p(pgeq(B, R)), p(pmin(A,B,R)), a(A,B,R)], - m(abs(A)) => [g(#R#>=0), d(D), p(pabs(A, R)), a(A,R)], - m(A/B) => [p(preified_slash(A,B,D,R)), a(A,B,R)], + m(abs(A)) => [d(D), g(#R#>=0), p(pabs(A, R)), a(A,R)], + m(A^B) => [d(D1), p(preified_exp(A,B,D2,R)), + p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], + m(A/B) => [d(D1), p(preified_slash(A,B,D2,R)), + p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], + m(A div B) => [d(D1), + g(phrase(parse_reified_clpz(((A-(A mod B)) // B), R, D2), Ps)), + ls(Ps), + p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], m(A//B) => [skeleton(A,B,D,R,ptzdiv)], - m(A div B) => [skeleton(A,B,D,R,pdiv)], m(A mod B) => [skeleton(A,B,D,R,pmod)], m(A rem B) => [skeleton(A,B,D,R,prem)], - m(A^B) => [d(D), p(pexp(A,B,R)), a(A,B,R)], % bitwise operations m(\A) => [function(D,\,A,R)], m(msb(A)) => [g(#A#>0) ,function(D,msb,A,R)], m(lsb(A)) => [g(#A#>0), function(D,lsb,A,R)], - m(popcount(A)) => [function(D,popcount,A,R)], - m(sign(A)) => [function(D,sign,A,R)], + m(popcount(A)) => [d(D), p(ppopcount(A, R)), a(A,R)], + m(sign(A)) => [d(D), p(psign(A, R)), a(A,R)], m(A< [function(D,<<,A,B,R)], m(A>>B) => [function(D,>>,A,B,R)], m(A/\B) => [function(D,/\,A,B,R)], m(A\/B) => [function(D,\/,A,B,R)], - m(xor(A, B)) => [function(D,xor,A,B,R)], + m(xor(A, B)) => [skeleton(A,B,D,R,pxor)], g(true) => [g(domain_error(clpz_expression, E))]] ). @@ -3621,10 +3648,13 @@ reified_goal(d(D), Ds) --> ; { domain_error(one_or_two_element_list, Ds) } ). reified_goal(g(Goal), _) --> [{Goal}]. -reified_goal(p(Vs, Prop), _) --> +reified_goal(p(Vs, Prop0), _) --> + { morphing_propagator(Prop0, Prop, Target) }, [{make_propagator(Prop, P)}], + target_propagator(Target), parse_init_dcg(Vs, P), - [{trigger_once(P)}], + [{variables_same_queue(Vs), + trigger_once(P)}], [( { propagator_state(P, S), S == dead } -> [] ; [p(P)])]. reified_goal(p(Prop), Ds) --> { term_variables(Prop, Vs) }, @@ -3634,7 +3664,9 @@ reified_goal(function(D,Op,A,B,R), Ds) --> reified_goal(function(D,Op,A,R), Ds) --> reified_goals([d(D),p(pfunction(Op,A,R)),a(A,R)], Ds). reified_goal(skeleton(A,B,D,R,F), Ds) --> - { Prop =.. [F,X,Y,Z] }, + { Prop0 =.. [F,X,Y,Z], + morphing_propagator(Prop0, Prop, Target) }, + target_propagator(Target), reified_goals([d(D1),l(p(P)),g(make_propagator(Prop, P)), p([A,B,D2,R], pskeleton(A,B,D2,[X,Y,Z]-P,R,F)), p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], Ds). @@ -3642,12 +3674,20 @@ reified_goal(a(V), _) --> [a(V)]. reified_goal(a(X,V), _) --> [a(X,V)]. reified_goal(a(X,Y,V), _) --> [a(X,Y,V)]. reified_goal(l(L), _) --> [[L]]. +reified_goal(ls(Ls), _) --> [Ls]. + +target_propagator(p(Prop)) --> [[p(Prop)]]. +target_propagator(none) --> []. parse_init_dcg([], _) --> []. parse_init_dcg([V|Vs], P) --> [{init_propagator(V, P)}], parse_init_dcg(Vs, P). -%?- set_prolog_flag(answer_write_options, [portray(true)]), -% clpz:parse_reified_clauses(Cs), maplist(portray_clause, Cs). +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +?- use_module(library(lists)), + use_module(library(format)), + clpz:parse_reified_clauses(Cs), + maplist(portray_clause, Cs). +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ reify(E, B) :- reify(E, B, _). @@ -3692,7 +3732,7 @@ reify_(tuples_in(Tuples, Relation), B) --> #B #<==> And }, propagator_init_trigger([B], tuples_not_in(Tuples, Relation, B)), kill_reified_tuples(Bs, Ps, Bs), - list(Ps), + seq(Ps), as([B|Bs]). reify_(finite_domain(V), B) --> propagator_init_trigger(reified_fd(V,B)), @@ -3724,20 +3764,17 @@ arithmetic(L, R, B, Functor) --> { phrase((parse_reified_clpz(L, LR, LD), parse_reified_clpz(R, RR, RD)), Ps), Prop =.. [Functor,LD,LR,RD,RR,Ps,B] }, - list(Ps), + seq(Ps), propagator_init_trigger([LD,LR,RD,RR,B], Prop), a(B). boolean(L, R, B, Functor) --> { reify(L, LR, Ps1), reify(R, RR, Ps2), Prop =.. [Functor,LR,Ps1,RR,Ps2,B] }, - list(Ps1), list(Ps2), + seq(Ps1), seq(Ps2), propagator_init_trigger([LR,RR,B], Prop), a(LR, RR, B). -list([]) --> []. -list([L|Ls]) --> [L], list(Ls). - a(X,Y,B) --> ( nonvar(X) -> a(Y, B) ; nonvar(Y) -> a(X, B) @@ -3872,7 +3909,6 @@ domain(V, Dom) :- domains_intersection(Dom, Dom0, Dom1), %format("intersected\n: ~w\n ~w\n==> ~w\n\n", [Dom,Dom0,Dom1]), fd_put(V, Dom1, VPs), - do_queue, reinforce(V) ; domain_contains(Dom, V) ). @@ -4187,7 +4223,6 @@ activate_propagator(propagator(P,State)) --> enable_queue :- true. % NOP disable_queue :- true. % NOP -do_queue. % NOP %do_queue --> print_queue, { false }. do_queue --> @@ -4769,7 +4804,7 @@ run_propagator(scalar_product_eq(Cs0,Vs0,P0), MState) --> ) }. % X + Y = Z -run_propagator(pplus(X,Y,Z), MState) --> +run_propagator(pplus(X,Y,Z,Morph), MState) --> ( nonvar(X) -> ( X =:= 0 -> kill(MState), Y = Z ; Y == Z -> kill(MState), X =:= 0 @@ -4788,7 +4823,7 @@ run_propagator(pplus(X,Y,Z), MState) --> ; [] ) ) - ; nonvar(Y) -> run_propagator(pplus(Y,X,Z), MState) + ; nonvar(Y) -> run_propagator(pplus(Y,X,Z,Morph), MState) ; nonvar(Z) -> ( X == Y -> kill(MState), { even(Z), X is Z // 2 } ; { fd_get(X, XD, _), @@ -4805,7 +4840,8 @@ run_propagator(pplus(X,Y,Z), MState) --> ; [] ) ) - ; ( X == Y -> { kill(MState), 2*X #= Z } + ; ( X == Y -> + morph_into_propagator(MState, [X,Z], ptimes(2,X,Z), Morph) ; X == Z -> kill(MState), Y = 0 ; Y == Z -> kill(MState), X = 0 ; { fd_get(X, XD, XL, XU, XPs), @@ -4831,7 +4867,7 @@ run_propagator(pplus(X,Y,Z), MState) --> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -run_propagator(ptimes(X,Y,Z), MState) --> +run_propagator(ptimes(X,Y,Z,Morph), MState) --> ( nonvar(X) -> ( nonvar(Y) -> kill(MState), Z is X * Y ; X =:= 0 -> kill(MState), Z = 0 @@ -4851,7 +4887,7 @@ run_propagator(ptimes(X,Y,Z), MState) --> ) ) ) - ; nonvar(Y) -> run_propagator(ptimes(Y,X,Z), MState) + ; nonvar(Y) -> run_propagator(ptimes(Y,X,Z,Morph), MState) ; nonvar(Z) -> ( X == Y -> kill(MState), @@ -4877,7 +4913,8 @@ run_propagator(ptimes(X,Y,Z), MState) --> ; neq_num(X, 0), neq_num(Y, 0) ) ) - ; ( X == Y -> kill(MState), { X^2 #= Z } + ; ( X == Y -> + morph_into_propagator(MState, [X,Z], pexp(X,2,Z), Morph) ; { fd_get(X, XD, XL, XU, XPs), fd_get(Y, _, YL, YU, _), fd_get(Z, ZD, ZL, ZU, _) }, @@ -4908,17 +4945,8 @@ run_propagator(ptimes(X,Y,Z), MState) --> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% X div Y = Z -run_propagator(pdiv(X,Y,Z), MState) --> - { kill(MState), Z #= (X-(X mod Y)) // Y }. - -% X rdiv Y = Z -run_propagator(prdiv(X,Y,Z), MState) --> - { kill(MState), Z*Y #= X }. - - % X // Y = Z (round towards zero) -run_propagator(ptzdiv(X,Y,Z), MState) --> +run_propagator(ptzdiv(X,Y,Z,Morph), MState) --> ( nonvar(X) -> ( nonvar(Y) -> kill(MState), Y =\= 0, Z is X // Y ; { fd_get(Y, YD, YL, YU, YPs) }, @@ -4962,7 +4990,8 @@ run_propagator(ptzdiv(X,Y,Z), MState) --> ; nonvar(Y) -> Y =\= 0, ( Y =:= 1 -> kill(MState), X = Z - ; Y =:= -1 -> kill(MState), { Z #= -X } + ; Y =:= -1 -> + morph_into_propagator(MState, [X,Z], pplus(X,Z,0), Morph) ; { fd_get(X, XD, XL, XU, XPs) }, ( nonvar(Z) -> kill(MState), @@ -5412,9 +5441,11 @@ run_propagator(pmin(X,Y,Z), MState) --> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% % Z = X ^ Y -run_propagator(pexp(X,Y,Z), MState) --> +run_propagator(pexp(X,Y,Z,Morph), MState) --> ( X == 1 -> kill(MState), Z = 1 - ; X == 0 -> kill(MState), queue_goal((Z in 0..1, Y #>= 0, Z #<==> Y #= 0)) + ; X == 0 -> + queue_goal((Z in 0..1, Y #>= 0)), + morph_into_propagator(MState, [Y,Z], reified_eq(1,Y,1,0,[],Z), Morph) ; Y == 0 -> kill(MState), Z = 1 ; Y == 1 -> kill(MState), Z = X ; nonvar(X) -> @@ -5474,7 +5505,9 @@ run_propagator(pexp(X,Y,Z), MState) --> ) ; nonvar(Y), Y > 0 -> ( { even(Y) } -> - { geq(Z, 0) } + { fd_get(Z, ZD0, ZPs0), + domain_remove_smaller_than(ZD0, 0, ZDG0) }, + fd_put(Z, ZDG0, ZPs0) ; true ), ( { fd_get(X, XD, XL, XU, _), fd_get(Z, ZD, ZL, ZU, ZPs) } -> @@ -5907,6 +5940,35 @@ run_propagator(preified_slash(X, Y, D, R), MState) --> ; [] ). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +run_propagator(preified_exp(X, Y, D, R), MState) --> + ( X == 1 -> + kill(MState), + D = 1, + R = 1 + ; Y == 0 -> + kill(MState), + D = 1, + R = 1 + ; Y == 1 -> + kill(MState), + D = 1, + R = X + ; nonvar(X), + nonvar(Y) -> + kill(MState), + ( ( abs(X) =:= 1 ; Y >= 0 ) -> + D = 1, + R is X^Y + ; D = 0 + ) + ; D == 1 -> + kill(MState), + queue_goal(X^Y #= R) + ; [] + ). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -6055,7 +6117,7 @@ domain_to_list(Domain, List) :- phrase(domain_to_list(Domain), List). domain_to_list(split(_, Left, Right)) --> domain_to_list(Left), domain_to_list(Right). domain_to_list(empty) --> []. -domain_to_list(from_to(n(F),n(T))) --> { numlist(F, T, Ns) }, list(Ns). +domain_to_list(from_to(n(F),n(T))) --> { numlist(F, T, Ns) }, seq(Ns). difference_arcs([], []) --> []. difference_arcs([V|Vs], FL0) --> @@ -6415,8 +6477,7 @@ num_infinite(Var, N0, N) :- weak_arc_all_distinct(Ls) :- must_be(list, Ls), Orig = original_goal(_, weak_arc_all_distinct(Ls)), - all_distinct(Ls, [], Orig), - do_queue. + all_distinct(Ls, [], Orig). all_distinct([], _, _). all_distinct([X|Right], Left, Orig) :- @@ -6729,8 +6790,10 @@ gcc_pairs([Key-Num0|KNs], Vs, [Key-Num|Rest]) :- gcc_global(Vs, KNs) :- gcc_check(KNs), - % reach fix-point: all elements of clpz_gcc_vs must be variables - do_queue, + % previously: call do_queue/0 (now a NOP) here to reach a + % fix-point: all elements of clpz_gcc_vs must be variables. We + % must ensure this holds if gcc_check/1 is later rewritten to + % actually disable the queue. with_local_attributes(Vs, (gcc_arcs(KNs, S, Vals), variables_with_num_occurrences(Vs, VNs), @@ -7753,7 +7816,7 @@ attributes_goals([propagator(P, State)|As]) --> ; maplist(unwrap_with(=), Gs, Gs1) ), maplist(with_clpz, Gs1, Gs2) }, - list(Gs2) + seq(Gs2) ; [P] % possibly user-defined constraint ), attributes_goals(As). @@ -7771,17 +7834,15 @@ bare_integer(V0, V) :- ( integer(V0) -> V = V0 ; V = #V0 ). attribute_goal_(presidual(Goal)) --> [Goal]. attribute_goal_(pgeq(A,B)) --> [#A #>= #B]. -attribute_goal_(pplus(X,Y,Z)) --> [#X + #Y #= #Z]. +attribute_goal_(pplus(X,Y,Z,_)) --> [#X + #Y #= #Z]. attribute_goal_(pneq(A,B)) --> [#A #\= #B]. -attribute_goal_(ptimes(X,Y,Z)) --> [#X * #Y #= #Z]. +attribute_goal_(ptimes(X,Y,Z,_)) --> [#X * #Y #= #Z]. attribute_goal_(absdiff_neq(X,Y,C)) --> [abs(#X - #Y) #\= C]. attribute_goal_(x_eq_abs_plus_v(X,V)) --> [#X #= abs(#X) + #V]. attribute_goal_(x_neq_y_plus_z(X,Y,Z)) --> [#X #\= #Y + #Z]. attribute_goal_(x_leq_y_plus_c(X,Y,C)) --> [#X #=< #Y + C]. -attribute_goal_(ptzdiv(X,Y,Z)) --> [#X // #Y #= #Z]. -attribute_goal_(pdiv(X,Y,Z)) --> [#X div #Y #= #Z]. -attribute_goal_(prdiv(X,Y,Z)) --> [#X / #Y #= #Z]. -attribute_goal_(pexp(X,Y,Z)) --> [#X ^ #Y #= #Z]. +attribute_goal_(ptzdiv(X,Y,Z,_)) --> [#X // #Y #= #Z]. +attribute_goal_(pexp(X,Y,Z,_)) --> [#X ^ #Y #= #Z]. attribute_goal_(psign(X,Y)) --> [#Y #= sign(#X)]. attribute_goal_(pabs(X,Y)) --> [#Y #= abs(#X)]. attribute_goal_(pmod(X,M,K)) --> [#X mod #M #= #K]. @@ -7841,6 +7902,7 @@ attribute_goal_(reified_and(X,_,Y,_,B)) --> [#X #/\ #Y #<==> #B]. attribute_goal_(reified_or(X, _, Y, _, B)) --> [#X #\/ #Y #<==> #B]. attribute_goal_(reified_not(X, Y)) --> [#\ #X #<==> #Y]. attribute_goal_(preified_slash(X, Y, _, R)) --> [#X/ #Y #= R]. +attribute_goal_(preified_exp(X, Y, _, R)) --> [#X^ #Y #= R]. attribute_goal_(pimpl(X, Y, _)) --> [#X #==> #Y]. attribute_goal_(pfunction(Op, A, B, R)) --> { Expr =.. [Op,#A,#B] }, diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 149338b5..d1f764d6 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -1,5 +1,6 @@ -use dashu::base::Abs; -use dashu::base::Gcd; +use dashu::base::{Abs, Gcd, UnsignedAbs}; +use dashu::integer::IBig; +use dashu::integer::fast_div::ConstDivisor; use divrem::*; use num_order::NumOrd; @@ -854,7 +855,9 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result &Integer::ZERO { ((n1 + Integer::ONE) / n2) - Integer::ONE } else { - n1 / n2 + let ring = ConstDivisor::new(n2.unsigned_abs()); + let n1 = n1.clone(); + IBig::from(ring.reduce(n1).residue()) } } diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 5b4895bd..9b0f958f 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -5,44 +5,115 @@ use crate::types::*; #[cfg(test)] use crate::heap_iter::{FocusedHeapIter, HeapOrStackTag, IterStackLoc}; -use core::marker::PhantomData; - pub(crate) trait UnmarkPolicy { - fn unmark(heap: &mut [HeapCellValue], current: usize); - fn mark(heap: &mut [HeapCellValue], current: usize); fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option where Self: Sized; + fn invert_marker(iter: &mut StacklessPreOrderHeapIter) where Self: Sized; + fn cycle_detected(&mut self) where Self: Sized; + fn mark_phase(&self) -> bool; + fn var_rooted_cycle(_iter: &mut StacklessPreOrderHeapIter, _next: usize) + where + Self: Sized {} + fn detect_list_tail_cycle(_iter: &mut StacklessPreOrderHeapIter) where Self: Sized {} + fn list_head_cycle_detecting_backward( + iter: &mut StacklessPreOrderHeapIter, + ) -> bool where Self: Sized { + iter.backward() + } } -pub(crate) struct IteratorUMP; +pub(crate) struct IteratorUMP { + mark_phase: bool, +} + +fn invert_marker(iter: &mut StacklessPreOrderHeapIter) { + if iter.heap[iter.start].get_forwarding_bit() { + while !iter.backward() {} + } + + iter.heap[iter.start].set_forwarding_bit(true); + + iter.next = iter.heap[iter.start].get_value(); + iter.current = iter.start; + + while let Some(_) = iter.forward() {} +} impl UnmarkPolicy for IteratorUMP { #[inline(always)] - fn unmark(heap: &mut [HeapCellValue], current: usize) { - heap[current].set_mark_bit(false); + fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option { + iter.forward_var() + } + + #[inline] + fn invert_marker(iter: &mut StacklessPreOrderHeapIter) { + iter.iter_state.mark_phase = false; + invert_marker(iter); } #[inline(always)] - fn mark(_heap: &mut [HeapCellValue], _current: usize) {} + fn cycle_detected(&mut self) {} + #[inline] + fn mark_phase(&self) -> bool { + self.mark_phase + } +} + +pub(crate) struct CycleDetectorUMP { + mark_phase: bool, + cycle_detected: bool, +} + +impl UnmarkPolicy for CycleDetectorUMP { #[inline(always)] fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option { iter.forward_var() } + + #[inline] + fn invert_marker(iter: &mut StacklessPreOrderHeapIter) { + iter.iter_state.mark_phase = false; + invert_marker(iter); + } + + #[inline] + fn cycle_detected(&mut self) { + self.cycle_detected = true; + } + + #[inline(always)] + fn mark_phase(&self) -> bool { + self.mark_phase + } + + fn list_head_cycle_detecting_backward( + iter: &mut StacklessPreOrderHeapIter, + ) -> bool { + if !iter.iter_state.cycle_detected && iter.iter_state.mark_phase { + iter.iter_state.cycle_detected = iter.detect_list_cycle(iter.current); + } + + iter.backward() + } + + fn detect_list_tail_cycle(iter: &mut StacklessPreOrderHeapIter) { + if iter.iter_state.mark_phase && !iter.iter_state.cycle_detected { + iter.iter_state.cycle_detected = iter.detect_list_cycle(iter.current); + } + } + + fn var_rooted_cycle(iter: &mut StacklessPreOrderHeapIter, next: usize) { + if iter.current != next && iter.iter_state.mark_phase && !iter.iter_state.cycle_detected { + iter.iter_state.cycle_detected = iter.detect_list_cycle(next); + } + } } struct MarkerUMP {} impl UnmarkPolicy for MarkerUMP { - #[inline(always)] - fn unmark(_heap: &mut [HeapCellValue], _current: usize) {} - - #[inline(always)] - fn mark(heap: &mut [HeapCellValue], current: usize) { - heap[current].set_mark_bit(true); - } - #[inline(always)] fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option { if iter.heap[iter.current + 1].get_forwarding_bit() { @@ -60,16 +131,25 @@ impl UnmarkPolicy for MarkerUMP { iter.heap[iter.current].set_forwarding_bit(true); // forward the attr vars list. None } + + fn invert_marker(_iter: &mut StacklessPreOrderHeapIter) {} + + #[inline(always)] + fn mark_phase(&self) -> bool { + true + } + + #[inline(always)] + fn cycle_detected(&mut self) {} } #[derive(Debug)] pub(crate) struct StacklessPreOrderHeapIter<'a, UMP: UnmarkPolicy> { - pub(crate) heap: &'a mut Vec, - orig_heap_len: usize, + pub(crate) heap: &'a mut [HeapCellValue], start: usize, current: usize, next: u64, - _marker: PhantomData, + iter_state: UMP, } #[cfg(test)] @@ -82,79 +162,111 @@ impl<'a> FocusedHeapIter for StacklessPreOrderHeapIter<'a, IteratorUMP> { impl<'a, UMP: UnmarkPolicy> Drop for StacklessPreOrderHeapIter<'a, UMP> { fn drop(&mut self) { + UMP::invert_marker(self); + if self.current == self.start { - self.heap.truncate(self.orig_heap_len); return; } while !self.backward() {} - - self.heap.truncate(self.orig_heap_len); } } impl<'a> StacklessPreOrderHeapIter<'a, MarkerUMP> { - pub(crate) fn new(heap: &'a mut Vec, cell: HeapCellValue) -> Self { - let orig_heap_len = heap.len(); - let start = orig_heap_len; - - heap.push(cell); - + pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { heap[start].set_forwarding_bit(true); let next = heap[start].get_value(); Self { heap, - orig_heap_len, start, current: start, next, - _marker: PhantomData, + iter_state: MarkerUMP {}, } } } +impl<'a> StacklessPreOrderHeapIter<'a, CycleDetectorUMP> { + pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { + heap[start].set_forwarding_bit(true); + let next = heap[start].get_value(); + + Self { + heap, + start, + current: start, + next, + iter_state: CycleDetectorUMP { + mark_phase: true, + cycle_detected: false, + }, + } + } + + #[inline] + pub(crate) fn found_cycle(&self) -> bool { + self.iter_state.cycle_detected + } + + pub(crate) fn detect_list_cycle(&self, next: usize) -> bool { + use crate::machine::system_calls::BrentAlgState; + + let mut brent_alg_st = BrentAlgState::new(self.current); + + while self.heap[brent_alg_st.hare].get_mark_bit() { + let temp = self.heap[brent_alg_st.hare].get_value() as usize; + + if brent_alg_st.step(temp).is_some() || temp == next { + return true; + } + + if temp == self.start { + break; + } + } + + false + } +} + impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> { #[cfg(test)] - pub(crate) fn new(heap: &'a mut Vec, cell: HeapCellValue) -> Self { - let orig_heap_len = heap.len(); - let start = orig_heap_len + 1; - - heap.push(cell); - heap.push(heap_loc_as_cell!(orig_heap_len)); - + pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { heap[start].set_forwarding_bit(true); let next = heap[start].get_value(); Self { heap, - orig_heap_len, start, current: start, next, - _marker: PhantomData, + iter_state: IteratorUMP { + mark_phase: true, + }, } } } impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { - fn backward_and_return(&mut self) -> Option { - let current = self.current; + fn backward_and_return(&mut self) -> HeapCellValue { + let mut current = self.heap[self.current]; + current.set_value(self.next); if self.backward() { // set the f and m bits on the heap cell at start // so we invoke backward() and return None next call. self.heap[self.current].set_forwarding_bit(true); - self.heap[self.current].set_mark_bit(true); + self.heap[self.current].set_mark_bit(self.iter_state.mark_phase()); } - Some(self.heap[current]) + current } fn forward_var(&mut self) -> Option { if self.heap[self.next as usize].get_forwarding_bit() { - return self.backward_and_return(); + return Some(self.backward_and_return()); } let temp = self.heap[self.next as usize].get_value(); @@ -166,33 +278,79 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { None } + #[inline] + fn is_cyclic(&self, var_current: usize, var_next: usize, var_f: bool) -> bool { + if self.heap[var_next].is_var() { + // the third conjunct covers the case where var_current + // was just unforwarded by forward_var() and so + // self.current + 1 == var_current. see acyclic_term#2121 + // & acyclic_term_30 for examples of how this occurs. + self.heap[var_next].get_mark_bit() && var_current != var_next && !var_f + } else if self.heap[var_next].is_ref() { + self.heap[var_next].get_mark_bit() + } else { + false + } + } + fn forward(&mut self) -> Option { loop { - if !self.heap[self.current].get_mark_bit() { - self.heap[self.current].set_mark_bit(true); + if self.heap[self.current].get_mark_bit() != self.iter_state.mark_phase() { + self.heap[self.current].set_mark_bit(self.iter_state.mark_phase()); match self.heap[self.current].get_tag() { HeapCellValueTag::AttrVar => { + let next = self.next; + let current = self.current; + let f = self.heap[self.current].get_forwarding_bit(); + + if self.heap[next as usize].get_mark_bit() == self.iter_state.mark_phase() { + UMP::var_rooted_cycle(self, next as usize); + } + if let Some(cell) = UMP::forward_attr_var(self) { + if self.is_cyclic(current, next as usize, f) { + self.iter_state.cycle_detected(); + } + return Some(cell); } - if self.heap[self.next as usize].get_mark_bit() { - return Some(attr_var_as_cell!(self.current)); + if self.next < self.heap.len() as u64 { + if self.heap[self.next as usize].get_mark_bit() == self.iter_state.mark_phase() { + let tag = HeapCellValueTag::AttrVar; + return Some(HeapCellValue::build_with(tag, next)); + } } } HeapCellValueTag::Var => { + let next = self.next; + let current = self.current; + let f = self.heap[self.current].get_forwarding_bit(); + + if self.heap[next as usize].get_mark_bit() == self.iter_state.mark_phase() { + UMP::var_rooted_cycle(self, next as usize); + } + if let Some(cell) = self.forward_var() { + if self.is_cyclic(current, next as usize, f) { + self.iter_state.cycle_detected(); + } + return Some(cell); } - if self.heap[self.next as usize].get_mark_bit() { - return Some(heap_loc_as_cell!(self.current)); + if self.next < self.heap.len() as u64 { + if self.heap[self.next as usize].get_mark_bit() == self.iter_state.mark_phase() { + let tag = HeapCellValueTag::Var; + return Some(HeapCellValue::build_with(tag, next)); + } } } HeapCellValueTag::Str => { if self.heap[self.next as usize + 1].get_forwarding_bit() { - return self.backward_and_return(); + self.iter_state.cycle_detected(); + return Some(self.backward_and_return()); } let h = self.next as usize; @@ -216,43 +374,54 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let last_cell_loc = self.next as usize + 1; if self.heap[last_cell_loc].get_forwarding_bit() { - return self.backward_and_return(); + self.iter_state.cycle_detected(); + return Some(self.backward_and_return()); } - self.heap[last_cell_loc].set_forwarding_bit(true); - self.next = self.heap[last_cell_loc].get_value(); self.heap[last_cell_loc].set_value(self.current as u64); self.current = last_cell_loc; + self.heap[last_cell_loc].set_forwarding_bit(true); + + if self.heap[last_cell_loc].get_mark_bit() == self.iter_state.mark_phase() { + if self.heap[last_cell_loc-1].get_mark_bit() == self.iter_state.mark_phase() { + // the conjunction leading here is a necessary but not sufficient + // condition of the presence of a cycle at the list head. + + if last_cell_loc == self.current { + UMP::detect_list_tail_cycle(self); + } + + self.backward(); + + if UMP::list_head_cycle_detecting_backward(self) { + return None; + } + + continue; + } + } + return Some(list_loc_as_cell!(last_cell_loc - 1)); } HeapCellValueTag::PStrLoc => { let h = self.next as usize; - let cell = self.heap[h]; if self.heap[h + 1].get_forwarding_bit() { - return self.backward_and_return(); + self.iter_state.cycle_detected(); + return Some(self.backward_and_return()); } - if self.heap[h].get_tag() == HeapCellValueTag::PStr { - let last_cell_loc = h + 1; - self.heap[last_cell_loc].set_forwarding_bit(true); + let cell = self.heap[h]; - self.next = self.heap[last_cell_loc].get_value(); - self.heap[last_cell_loc].set_value(self.current as u64); - self.current = last_cell_loc; - } else { - debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::PStrOffset); + let last_cell_loc = h + 1; - self.next = self.heap[h].get_value(); - self.heap[h].set_value(self.current as u64); - self.current = h; + self.next = self.heap[last_cell_loc].get_value(); + self.heap[last_cell_loc].set_value(self.current as u64); + self.current = last_cell_loc; - if self.heap[h].get_mark_bit() { - continue; - } - } + self.heap[last_cell_loc].set_forwarding_bit(true); return Some(cell); } @@ -260,13 +429,11 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let h = self.next as usize; let cell = self.heap[h]; - // mark the Fixnum offset. - UMP::mark(self.heap, self.current + 1); - let last_cell_loc = h + 1; if self.heap[last_cell_loc].get_forwarding_bit() { - return self.backward_and_return(); + self.iter_state.cycle_detected(); + return Some(self.backward_and_return()); } if self.heap[h].get_tag() == HeapCellValueTag::PStr { @@ -290,7 +457,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let arity = AtomCell::from_bytes(cell.into_bytes()).get_arity(); if arity == 0 { - return self.backward_and_return(); + return Some(self.backward_and_return()); } else if self.backward() { return None; } @@ -301,7 +468,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { } } _ => { - return self.backward_and_return(); + return Some(self.backward_and_return()); } } } else { @@ -316,15 +483,13 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { while !self.heap[self.current].get_forwarding_bit() { let temp = self.heap[self.current].get_value(); - UMP::unmark(self.heap, self.current); - self.heap[self.current].set_value(self.next); + self.next = self.current as u64; self.current = temp as usize; } self.heap[self.current].set_forwarding_bit(false); - UMP::unmark(self.heap, self.current); if self.current == self.start { return true; @@ -351,8 +516,8 @@ impl<'a, UMP: UnmarkPolicy> Iterator for StacklessPreOrderHeapIter<'a, UMP> { } } -pub fn mark_cells(heap: &mut Heap, cell: HeapCellValue) { - let mut iter = StacklessPreOrderHeapIter::::new(heap, cell); +pub fn mark_cells(heap: &mut Heap, start: usize) { + let mut iter = StacklessPreOrderHeapIter::::new(heap, start); while let Some(_) = iter.forward() {} } @@ -369,62 +534,76 @@ mod tests { let a_atom = atom!("a"); let b_atom = atom!("b"); + wam.machine_st.heap.push(str_loc_as_cell!(1)); + wam.machine_st .heap .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); - mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), + str_loc_as_cell!(1) + ); + + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(f_atom, 2) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), + unmark_cell_bits!(wam.machine_st.heap[2]), atom_as_cell!(a_atom) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), + unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(b_atom) ); wam.machine_st.heap.clear(); + wam.machine_st.heap.push(str_loc_as_cell!(1)); + wam.machine_st.heap.extend(functor!( f_atom, [ atom(a_atom), atom(b_atom), atom(a_atom), - cell(str_loc_as_cell!(0)) + cell(str_loc_as_cell!(1)) ] )); - mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), + str_loc_as_cell!(1) + ); + + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(f_atom, 4) ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - atom_as_cell!(a_atom) - ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[2]), - atom_as_cell!(b_atom) + atom_as_cell!(a_atom) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[3]), - atom_as_cell!(a_atom) + atom_as_cell!(b_atom) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - str_loc_as_cell!(0) + atom_as_cell!(a_atom) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[5]), + str_loc_as_cell!(1) ); for cell in &mut wam.machine_st.heap { @@ -432,9 +611,9 @@ mod tests { } // make the structure doubly cyclic. - wam.machine_st.heap[2] = str_loc_as_cell!(0); + wam.machine_st.heap[2] = str_loc_as_cell!(1); - mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -452,7 +631,7 @@ mod tests { ] )); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -485,7 +664,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -503,7 +682,7 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(b_atom)); wam.machine_st.heap.push(empty_list_as_cell!()); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -537,7 +716,7 @@ mod tests { // now make the list cyclic. wam.machine_st.heap.push(heap_loc_as_cell!(0)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -569,7 +748,7 @@ mod tests { // make the list doubly cyclic. wam.machine_st.heap[3] = heap_loc_as_cell!(0); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -586,7 +765,7 @@ mod tests { wam.machine_st.heap.push(stream_cell); wam.machine_st.heap.push(empty_list_as_cell!()); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -617,7 +796,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(3)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -644,18 +823,20 @@ mod tests { // two-part complete string, then a three-part cyclic string // involving an uncompacted list of chars. - let pstr_var_cell = - put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); + wam.machine_st.heap.push(pstr_loc_as_cell!(1)); + + let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_loc_as_cell!(1)); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - heap_loc_as_cell!(1) + unmark_cell_bits!(wam.machine_st.heap[2]), + heap_loc_as_cell!(2) ); wam.machine_st.heap.pop(); @@ -664,25 +845,25 @@ mod tests { cell.set_mark_bit(false); } - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); + wam.machine_st.heap.push(pstr_loc_as_cell!(3)); let pstr_second_var_cell = put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - heap_loc_as_cell!(3) + unmark_cell_bits!(wam.machine_st.heap[4]), + heap_loc_as_cell!(4) ); for cell in &mut wam.machine_st.heap { @@ -690,141 +871,137 @@ mod tests { } wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(4)); - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); + wam.machine_st.heap.push(pstr_loc_as_cell!(5)); + wam.machine_st.heap.push(pstr_offset_as_cell!(1)); wam.machine_st .heap .push(fixnum_as_cell!(Fixnum::build_with(2))); + wam.machine_st.heap.push(pstr_loc_as_cell!(5)); - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(4)); + mark_cells(&mut wam.machine_st.heap, 7); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap[1 ..]); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(5) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), + pstr_offset_as_cell!(1) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[7]), + pstr_loc_as_cell!(5) + ); for cell in &mut wam.machine_st.heap { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(3)); + wam.machine_st.heap[7] = heap_loc_as_cell!(2); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + mark_cells(&mut wam.machine_st.heap, 7); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]); + + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(5) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), + pstr_offset_as_cell!(1) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[7]), + heap_loc_as_cell!(2) + ); for cell in &mut wam.machine_st.heap { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(2)); + wam.machine_st.heap[7] = pstr_loc_as_cell!(1); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + mark_cells(&mut wam.machine_st.heap, 7); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]); + + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(5) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), + pstr_offset_as_cell!(1) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[7]), + pstr_loc_as_cell!(1) + ); for cell in &mut wam.machine_st.heap { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(1)); + wam.machine_st.heap[7] = heap_loc_as_cell!(0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + mark_cells(&mut wam.machine_st.heap, 7); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]); + + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(5) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), + pstr_offset_as_cell!(1) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); - - all_cells_marked_and_unforwarded(&wam.machine_st.heap); - - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - fixnum_as_cell!(Fixnum::build_with(2)) + unmark_cell_bits!(wam.machine_st.heap[7]), + heap_loc_as_cell!(0) ); wam.machine_st.heap.truncate(4); @@ -836,45 +1013,46 @@ mod tests { wam.machine_st .heap .push(atom_as_cell!(atom!("irrelevant stuff"))); - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); + wam.machine_st.heap.push(pstr_offset_as_cell!(1)); wam.machine_st .heap .push(fixnum_as_cell!(Fixnum::build_with(2))); - wam.machine_st.heap[3] = pstr_loc_as_cell!(5); + // this is at index 7 + wam.machine_st.heap.push(pstr_loc_as_cell!(5)); - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(5)); + mark_cells(&mut wam.machine_st.heap, 7); - assert!(wam.machine_st.heap[0].get_mark_bit()); + assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); assert!(wam.machine_st.heap[2].get_mark_bit()); assert!(wam.machine_st.heap[3].get_mark_bit()); - assert!(!wam.machine_st.heap[4].get_mark_bit()); + assert!(wam.machine_st.heap[4].get_mark_bit()); assert!(wam.machine_st.heap[5].get_mark_bit()); assert!(wam.machine_st.heap[6].get_mark_bit()); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(5) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), atom_as_cell!(atom!("irrelevant stuff")) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_offset_as_cell!(0) + pstr_offset_as_cell!(1) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[7]), + pstr_loc_as_cell!(5) + ); wam.machine_st.heap.clear(); @@ -896,7 +1074,9 @@ mod tests { .heap .push(fixnum_as_cell!(Fixnum::build_with(2))); - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(7)); + wam.machine_st.heap.push(pstr_loc_as_cell!(7)); + + mark_cells(&mut wam.machine_st.heap, 9); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); @@ -943,7 +1123,9 @@ mod tests { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(5)); + wam.machine_st.heap[9] = heap_loc_as_cell!(5); + + mark_cells(&mut wam.machine_st.heap, 9); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); @@ -994,7 +1176,9 @@ mod tests { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(4)); + wam.machine_st.heap[9] = pstr_loc_as_cell!(4); + + mark_cells(&mut wam.machine_st.heap, 9); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); @@ -1045,7 +1229,9 @@ mod tests { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(2)); + mark_cells(&mut wam.machine_st.heap, 9); + + wam.machine_st.heap[9] = heap_loc_as_cell!(2); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); @@ -1096,7 +1282,9 @@ mod tests { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(1)); + wam.machine_st.heap[9] = pstr_loc_as_cell!(1); + + mark_cells(&mut wam.machine_st.heap, 9); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); @@ -1161,7 +1349,7 @@ mod tests { wam.machine_st.heap.push(pstr_loc_as_cell!(0)); wam.machine_st.heap.push(empty_list_as_cell!()); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(4)); + mark_cells(&mut wam.machine_st.heap, 4); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1207,7 +1395,7 @@ mod tests { wam.machine_st.heap.push(pstr_loc_as_cell!(0)); wam.machine_st.heap.push(heap_loc_as_cell!(4)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(4)); + mark_cells(&mut wam.machine_st.heap, 4); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1250,7 +1438,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(3)); wam.machine_st.heap.push(heap_loc_as_cell!(3)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1278,7 +1466,7 @@ mod tests { wam.machine_st.heap.push(list_loc_as_cell!(1)); wam.machine_st.heap.push(list_loc_as_cell!(1)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1313,7 +1501,7 @@ mod tests { wam.machine_st.heap.push(attr_var_as_cell!(11)); // linked from 7. wam.machine_st.heap.push(heap_loc_as_cell!(12)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1394,7 +1582,7 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(p_atom, 1)); // 22 wam.machine_st.heap.push(heap_loc_as_cell!(23)); // 23 - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1506,7 +1694,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(5)); wam.machine_st.heap.push(list_loc_as_cell!(5)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&mut wam.machine_st.heap[0..24]); @@ -1628,7 +1816,7 @@ mod tests { .heap .push(fixnum_as_cell!(Fixnum::build_with(0))); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); assert_eq!(wam.machine_st.heap.len(), 1); @@ -1647,7 +1835,7 @@ mod tests { wam.machine_st.heap.push(str_loc_as_cell!(4)); wam.machine_st.heap.push(empty_list_as_cell!()); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(7)); + mark_cells(&mut wam.machine_st.heap, 7); assert_eq!(wam.machine_st.heap.len(), 10); @@ -1699,8 +1887,9 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(atom!("f"), 2)); wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(1)); + wam.machine_st.heap.push(str_loc_as_cell!(0)); - mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 3); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1729,7 +1918,9 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); wam.machine_st.heap.push(heap_loc_as_cell!(2)); - mark_cells(&mut wam.machine_st.heap, list_loc_as_cell!(5)); + wam.machine_st.heap.push(list_loc_as_cell!(5)); + + mark_cells(&mut wam.machine_st.heap, 7); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1775,8 +1966,9 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(2)); wam.machine_st.heap.push(empty_list_as_cell!()); // C = [[]|B]. wam.machine_st.heap.push(heap_loc_as_cell!(3)); + wam.machine_st.heap.push(heap_loc_as_cell!(0)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 9); assert!(wam.machine_st.heap[0].get_mark_bit()); assert!(!wam.machine_st.heap[1].get_mark_bit()); @@ -1841,7 +2033,7 @@ mod tests { .heap .push(fixnum_as_cell!(Fixnum::build_with(4))); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 75c3deb2..eef5f18c 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -1131,27 +1131,29 @@ impl MachineState { #[inline] pub fn is_cyclic_term(&mut self, value: HeapCellValue) -> bool { - if value.is_constant() { + let value = self.store(self.deref(value)); + + if value.is_constant() || value.is_stack_var() { return false; } - let mut iter = stackful_preorder_iter:: - (&mut self.heap, &mut self.stack, value); + let h = self.heap.len(); + self.heap.push(value); - while let Some(value) = iter.next() { - if value.get_forwarding_bit() { - let value = unmark_cell_bits!(heap_bound_store( - iter.heap, - heap_bound_deref(iter.heap, value), - )); + let found_cycle = { + let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, h); - if value.is_compound(iter.heap) { - return true; + while let Some(_) = iter.next() { + if iter.found_cycle() { + break; } } - } - false + iter.found_cycle() + }; + + self.heap.pop(); + found_cycle } // arg(+N, +Term, ?Arg) @@ -1621,56 +1623,12 @@ impl MachineState { // returns true on failure. pub fn ground_test(&mut self) -> bool { - use fxhash::FxBuildHasher; + let iter = eager_stackful_preorder_iter(&mut self.heap, self.registers[1]); - if self.registers[1].is_constant() { - return false; - } - - let value = self.store(self.deref(self.registers[1])); - - if value.is_stack_var() { - return true; - } - - let mut visited = IndexSet::with_hasher(FxBuildHasher::default()); - let mut iter = stackful_preorder_iter::(&mut self.heap, &mut self.stack, value); - let mut stack_len = 0; - - let is_var = |heap: &Heap, value: HeapCellValue| -> bool { - let value = unmark_cell_bits!(value); - - if value.is_var() { - let value = heap_bound_store(heap, heap_bound_deref(heap, value)); - - if value.is_var() { - return true; - } - } - - false - }; - - while let Some(value) = iter.next() { - if is_var(iter.heap, value) { + for term in iter { + if term.is_var() { return true; } - - if value.is_ref() { - if visited.contains(&value) { - while iter.stack_len() > stack_len { - if let Some(value) = iter.pop_stack() { - if is_var(iter.heap, value) { - return true; - } - } - } - } else { - visited.insert(value); - } - } - - stack_len = iter.stack_len(); } false diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 5c482636..17a86e98 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -584,20 +584,11 @@ impl MachineState { seen_set: &mut IndexSet, value: HeapCellValue, ) { - let mut iter = stackful_preorder_iter::(&mut self.heap, &mut self.stack, value); + let iter = eager_stackful_preorder_iter(&mut self.heap, value); - while let Some(value) = iter.next() { - let value = unmark_cell_bits!(value); - - if value.is_var() { - let value = unmark_cell_bits!(heap_bound_store( - iter.heap, - heap_bound_deref(iter.heap, value) - )); - - if value.is_var() { - seen_set.insert(value); - } + for term in iter { + if term.is_var() { + seen_set.insert(term); } } } @@ -6891,6 +6882,17 @@ impl Machine { return; } + let stored_v = if stored_v.is_stack_var() { + let h = self.machine_st.heap.len(); + + self.machine_st.heap.push(heap_loc_as_cell!(h)); + self.machine_st.bind(Ref::heap_cell(h), stored_v); + + heap_loc_as_cell!(h) + } else { + stored_v + }; + let mut seen_set = IndexSet::with_hasher(FxBuildHasher::default()); self.machine_st.variable_set(&mut seen_set, stored_v); diff --git a/src/machine/unify.rs b/src/machine/unify.rs index f9c03189..104c5157 100644 --- a/src/machine/unify.rs +++ b/src/machine/unify.rs @@ -691,6 +691,9 @@ pub(crate) trait Unifier: DerefMut { (HeapCellValueTag::Cons, ptr_1) => { Self::unify_constant(self, ptr_1, d2); } + (HeapCellValueTag::CutPoint, n1) => { + Self::unify_fixnum(self, n1, d2); + } _ => { unreachable!(); } diff --git a/src/tests/acyclic_term.pl b/src/tests/acyclic_term.pl new file mode 100644 index 00000000..e340d09c --- /dev/null +++ b/src/tests/acyclic_term.pl @@ -0,0 +1,233 @@ +:- use_module(library(format)). + +term1(A) :- + B=[C|D], + A=[D|C], + B=[C|B]. + +term2(A) :- + A=[B|C], + D=[C|C], + D=[B|D]. + +term3(A) :- + A=[_B|C], + D=[C|_E], + A=[C|D]. + +term4(A) :- + A=[B|C], + C=[C|B]. + +term5(A) :- + A=[_B|C], + D=[_E|C], + A=[C|D]. + +term6(A) :- + A=[B|B], + B=[C|C]. + +test("acyclic_term_1", ( + L = [_Y,[M,B],B|M], acyclic_term(L) +)). + +test("acyclic_term_2", ( + L = [_Y,[M,_B,L]|M], \+ acyclic_term(L) +)). + +test("acyclic_term_3", ( + L = [_Y,[M,B,L,B]|M], \+ acyclic_term(L) +)). + +test("acyclic_term_4", ( + L = [_Y,[L,_A,_B]|_M], \+ acyclic_term(L) +)). + +test("acyclic_term_5", ( + L = [_Y,[M,_A,_B]|M], acyclic_term(L) +)). + +test("acyclic_term_6", ( + L = [_Y,[L,_A,_B]|_M], \+ acyclic_term(L) +)). + +test("acyclic_term_7", ( + L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(T) +)). + +test("acyclic_term_8", ( + L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(L) +)). + +test("acyclic_term_9", ( + L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(A) +)). + +test("acyclic_term_10", ( + L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(Y) +)). + +test("acyclic_term_11", ( + L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(X) +)). + +test("acyclic_term_12", ( + A = [1|2], X = A, T=a(X, A), acyclic_term(T) +)). + +test("acyclic_term_13", ( + A = [A|2], X = A, T=a(X, A), \+ acyclic_term(T) +)). + +test("acyclic_term_13", ( + A = [T|2], X = A, T=a(X, A), \+ acyclic_term(T) +)). + +test("acyclic_term_14", ( + T = [_A|T], \+ acyclic_term(T) +)). + +test("acyclic_term_15", ( + T = [T|_L], \+ acyclic_term(T) +)). + +test("acyclic_term_16", ( + A = [1|A], X = A, T=a(X, A), \+ acyclic_term(T) +)). + +test("acyclic_term_17", ( + T = [_A| [[[[L|T]|[]]]]], acyclic_term(L) +)). + +test("acyclic_term_18", ( + T = [A| [[[[_L|T]|[]]]]], acyclic_term(A) +)). + +test("acyclic_term_19", ( + T = [_A| [[[[_L|T]|[]]]]], \+ acyclic_term(T) +)). + +test("acyclic_term_20", ( + A = [_C|_B], X = A, T=a(t(X,A), A), acyclic_term(T) +)). + +test("acyclic_term_21", ( + X = [a | Rest], Rest = [_Y | Rest], \+ acyclic_term(X) +)). + +test("acyclic_term_22", ( + _X = [a | Rest], Rest = [_Y | Rest], \+ acyclic_term(Rest) +)). + +test("acyclic_term_23", ( + T = [[_A, T]], G = [1|T], \+ acyclic_term(G) +)). + +test("acyclic_term_24", ( + T = [[_A, T]], \+ acyclic_term(T) +)). + +test("acyclic_term_25", ( + T = [[_, _], T], \+ acyclic_term(T) +)). + +test("acyclic_term_26", ( + T = [[T, _], 1], \+ acyclic_term(T) +)). + +test("acyclic_term_27", ( + T = str(A,A), acyclic_term(T) +)). + +test("acyclic_term_28", ( + T = str(A,A,A), acyclic_term(T) +)). + +test("acyclic_term_29", ( + A = s(B, d(Y)), Y = B, acyclic_term(A), + acyclic_term(B), acyclic_term(Y) +)). + +test("acyclic_term_30", ( + A=str(B,B,B), C=str(A,_D,B), acyclic_term(C), + acyclic_term(A), acyclic_term(B) +)). + +test("acyclic_term#2111_1", ( + term1(A), \+ acyclic_term(A) +)). + +test("acyclic_term#2111_2", ( + term2(A), \+ acyclic_term(A) +)). + +test("acyclic_term#2111_3", ( + term3(A), \+ acyclic_term(A) +)). + +test("acyclic_term#2111_4", ( + term4(A), \+ acyclic_term(A) +)). + +test("acyclic_term#2111_5", ( + term5(A), \+ acyclic_term(A) +)). + +test("acyclic_term#2111_6", ( + term6(A), acyclic_term(A) +)). + +test("acyclic_term#2113", ( + A=[]*B,B=[]*B, \+ acyclic_term(A) +)). + +test("acyclic_term#2114", ( + A=B*B, acyclic_term(A) +)). + +test("acyclic_term#2116", ( + A=B*B,B=[]*[], acyclic_term(A) +)). + +test("acyclic_term#2117", ( + A=[]*A,B=[]*A, \+ acyclic_term(B) +)). + +test("acyclic_term#2121", ( + A=B*B, C=A*B, acyclic_term(C), + acyclic_term(A), acyclic_term(B) +)). + +main :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests(Tests, Failed), + show_failed(Failed), + halt. + +main_quiet :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests_quiet(Tests, Failed), + ( Failed = [] -> + format("All tests passed", []) + ; format("Some tests failed", []) + ), + halt. + +run_tests([], []). +run_tests([test(Name, Goal)|Tests], Failed) :- + format("Running test \"~s\"~n", [Name]), + ( call(Goal) -> + Failed = Failed1 + ; format("Failed test \"~s\"~n", [Name]), + Failed = [Name|Failed1] + ), + run_tests(Tests, Failed1). + +run_tests_quiet([], []). +run_tests_quiet([test(Name, Goal)|Tests], Failed) :- + ( call(Goal) -> + Failed = Failed1 + ; Failed = [Name|Failed1] + ), + run_tests_quiet(Tests, Failed1). diff --git a/src/tests/ground.pl b/src/tests/ground.pl new file mode 100644 index 00000000..09c398c0 --- /dev/null +++ b/src/tests/ground.pl @@ -0,0 +1,83 @@ +/**/ + +:- use_module(library(format)). +:- use_module(library(dcgs)). +:- use_module(library(lists)). +:- use_module(library(debug)). +:- use_module(library(atts)). + +:- attribute a/1. + +a(Var) :- put_atts(Var, +a(hello)). + +test("ground#239", ( + % double negate to avoid residual goal being printed + \+ \+ (a(X), var(X), \+ ground(X)) +)). + +test("ground#1411",( + G_0 = ( A=s(A) ), G_0, + ground(A), ground(G_0) +)). + +test("ground#2065",( + A = [B|_C], B = [A], \+ ground(B), \+ground([B]) +)). + +test("ground#2073",( + \+ ground(_-1+_-1), + \+ ground(1-1-_) +)). + +test("ground#2075",( + G_0 = (_,_,ground(_)), + G_0 = (D=[D|_],_=D*[],ground(D)), + \+ G_0, + _=_B*_,_D=_B*_A,_B=_B*_D,\+ ground(_B), + A=[A|B],B=A*B,ground(A) +)). + +main :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests(Tests, Failed), + show_failed(Failed), + halt. + +main_quiet :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests_quiet(Tests, Failed), + ( Failed = [] -> + format("All tests passed", []) + ; format("Some tests failed", []) + ), + halt. + +run_tests([], []). +run_tests([test(Name, Goal)|Tests], Failed) :- + format("Running test \"~s\"~n", [Name]), + ( call(Goal) -> + Failed = Failed1 + ; format("Failed test \"~s\"~n", [Name]), + Failed = [Name|Failed1] + ), + run_tests(Tests, Failed1). + +run_tests_quiet([], []). +run_tests_quiet([test(Name, Goal)|Tests], Failed) :- + ( call(Goal) -> + Failed = Failed1 + ; Failed = [Name|Failed1] + ), + run_tests_quiet(Tests, Failed1). + +portray_failed_([]) --> []. +portray_failed_([F|Fs]) --> + "\"", F, "\"", "\n", portray_failed_(Fs). + +portray_failed([]) --> []. +portray_failed([F|Fs]) --> + "\n", "Failed tests:", "\n", portray_failed_([F|Fs]). + +show_failed(Failed) :- + phrase(portray_failed(Failed), F), + format("~s", [F]). diff --git a/src/tests/term_variables.pl b/src/tests/term_variables.pl new file mode 100644 index 00000000..f3ef5b32 --- /dev/null +++ b/src/tests/term_variables.pl @@ -0,0 +1,117 @@ +/**/ + +:- use_module(library(format)). +:- use_module(library(dcgs)). +:- use_module(library(lists)). +:- use_module(library(debug)). + +test("term_variables#1400", ( + term_variables(A+B*C/B-D, Vars), + term_variables(t(A,B,C,D), Vars), + Vars = [A,B,C,D] +)). + +test("term_variables#1405", ( + \+ (B=[C|D],C=[_|D],C=[B|B], term_variables(B,_), false) +)). + +test("term_variables#1409", ( + G_0 = (A=[B|B],A=[C|C]), G_0, term_variables(G_0, Vars), Vars = [B] +)). + +test("term_variables#1410", ( + \+ \+ (G_0 = ( A=s(A) ), G_0, term_variables(G_0, Vars), Vars = []), + E_0 = (_=[B|B]), G_0 = (E_0,\_=B), G_0, term_variables(G_0, Vars) +)). + +test("term_variables#1412", ( + G_0 = =([A|B],[A|B]), G_0, term_variables(G_0, Vars), + Vars = [A,B] +)). + +test("term_variables#1414", ( + \+ (\B=A,C=[A|D],B=[a,b|E],C=[D|E], term_variables(\E,_), false) +)). + +test("term_variables#2063", ( + A=[B|C], B=[A], term_variables([B], Vars), + Vars = [C] +)). + +test("term_variables#2097", ( + termt(T), term_variables(T,Vs), + T = [[[A|B]|A]|A], Vs == [A,B] +)). + +test("term_variables#2100", ( + termt2(T), term_variables(T,Vs), + T = [[T|A]|B], Vs == [A,B] +)). + +test("term_variables#2101", ( + termt3(T), term_variables(T,Vs), + T = [[[[A|B]|A]|A]|A], Vs == [A, B] +)). + +termt(T) :- + T = [T1|T2], + T1 = [T3|A], + T3 = [A|_], + T2 = A. + +termt2(T) :- + T = [T1|_B], + T1 = [T|_A]. + +termt3(T) :- + T = [T1|T0], + T1 = [T2|T3], + T2 = [T4|A], + T4 = [A|_], + T3 = A, + T0 = A. + +main :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests(Tests, Failed), + show_failed(Failed), + halt. + +main_quiet :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests_quiet(Tests, Failed), + ( Failed = [] -> + format("All tests passed", []) + ; format("Some tests failed", []) + ), + halt. + +run_tests([], []). +run_tests([test(Name, Goal)|Tests], Failed) :- + format("Running test \"~s\"~n", [Name]), + ( call(Goal) -> + Failed = Failed1 + ; format("Failed test \"~s\"~n", [Name]), + Failed = [Name|Failed1] + ), + run_tests(Tests, Failed1). + +run_tests_quiet([], []). +run_tests_quiet([test(Name, Goal)|Tests], Failed) :- + ( call(Goal) -> + Failed = Failed1 + ; Failed = [Name|Failed1] + ), + run_tests_quiet(Tests, Failed1). + +portray_failed_([]) --> []. +portray_failed_([F|Fs]) --> + "\"", F, "\"", "\n", portray_failed_(Fs). + +portray_failed([]) --> []. +portray_failed([F|Fs]) --> + "\n", "Failed tests:", "\n", portray_failed_([F|Fs]). + +show_failed(Failed) :- + phrase(portray_failed(Failed), F), + format("~s", [F]). diff --git a/src/types.rs b/src/types.rs index cf2159ba..043bd221 100644 --- a/src/types.rs +++ b/src/types.rs @@ -430,6 +430,7 @@ impl HeapCellValue { HeapCellValueTag::Cons | HeapCellValueTag::F64 | HeapCellValueTag::Fixnum + | HeapCellValueTag::CutPoint | HeapCellValueTag::Char | HeapCellValueTag::CStr => true, HeapCellValueTag::Atom => cell_as_atom_cell!(self).get_arity() == 0, diff --git a/tests/scryer/src_tests.rs b/tests/scryer/src_tests.rs index 5572b75f..05d24c9e 100644 --- a/tests/scryer/src_tests.rs +++ b/tests/scryer/src_tests.rs @@ -84,3 +84,30 @@ fn dif_tests() { "All tests passed", ); } + +#[test] +fn ground_tests() { + run_top_level_test_with_args( + &["src/tests/ground.pl", "-f", "-g", "main_quiet"], + "", + "All tests passed", + ); +} + +#[test] +fn term_variables_tests() { + run_top_level_test_with_args( + &["src/tests/term_variables.pl", "-f", "-g", "main_quiet"], + "", + "All tests passed", + ); +} + +#[test] +fn acyclic_term_tests() { + run_top_level_test_with_args( + &["src/tests/acyclic_term.pl", "-f", "-g", "main_quiet"], + "", + "All tests passed", + ); +}