]> Repositorios git - scryer-prolog.git/commitdiff
fix is_cyclic_term (#864)
authorMark Thom <[email protected]>
Wed, 10 Mar 2021 23:11:43 +0000 (16:11 -0700)
committerMark Thom <[email protected]>
Wed, 10 Mar 2021 23:11:48 +0000 (16:11 -0700)
src/machine/machine_state_impl.rs

index ca80d23e8bb53d1e52e64fb01fafadb94ee6f766..71852bfb533f0aa96696dac158ca1b0ba8cf1c63 100644 (file)
@@ -1750,28 +1750,57 @@ impl MachineState {
         let mut seen = IndexSet::new();
         let mut fail = false;
         let mut iter = self.pre_order_iter(addr);
+        let mut parent_stack = vec![];
 
-        let is_composite = |addr: &Addr| match *addr {
+        let is_composite = |addr: Addr| match addr {
             Addr::Str(_) | Addr::Lis(_) | Addr::PStrLocation(..) => true,
             _ => false,
         };
 
-        loop {
-            if let Some(addr) = iter.stack().last() {
+        'outer: loop {
+            if let Some(addr) = iter.stack().last().cloned() {
+                let addr = self.store(self.deref(addr));
+
                 if is_composite(addr) {
-                    if !seen.contains(addr) {
-                        seen.insert(*addr);
+                    if !seen.contains(&addr) {
+                        seen.insert(addr);
                     } else {
-                        fail = true;
-                        break;
+                        // when we again encounter a seen composite
+                        // term, check that it precedes itself as a
+                        // parent in the post-order traversal. in the
+                        // future, when value cells have mark bits,
+                        // use them to designate parenthood instead of
+                        // this linear search.
+
+                        for (_, prec_addr) in parent_stack.iter().rev().cloned() {
+                            if prec_addr == addr {
+                                fail = true;
+                                break 'outer;
+                            }
+                        }
                     }
-                } else {
-                    iter.stack().pop();
+
+                    let arity = match addr {
+                        Addr::Str(h) => match &self.heap[h] {
+                            &HeapCellValue::NamedStr(arity, ..) => arity,
+                            _ => unreachable!(),
+                        },
+                        _ => 2,
+                    };
+
+                    parent_stack.push((arity, addr));
                 }
             }
 
             if iter.next().is_none() {
                 break;
+            } else {
+                while let Some((rem_children, addr)) = parent_stack.pop() {
+                    if rem_children > 0 {
+                        parent_stack.push((rem_children - 1, addr));
+                        break;
+                    }
+                }
             }
         }