Carl Friedrich Bolz avatar Carl Friedrich Bolz committed 6b4f4ff

switch from storing an index in VarInTerms to storing an "indicator". The
reason is that a bound VarInTerm must remain valid even when the parent changes
its shape.

Comments (0)

Files changed (5)

prolog/interpreter/shape.py

         for i in range(index):
             newstorage[i] = self.storage[i]
         for i in range(obj.size_storage()):
-            newstorage[i + index] = obj.get_storage(i)
+            child = newstorage[i + index] = obj.get_storage(i)
+            if isinstance(child, term.VarInTerm):
+                indicator = child.indicator
+                deref = child.getbinding()
+                self = self._make_mutable()
+                if deref is None:
+                    child.parent = self
+                    child.indicator = term.VarInTermIndex(i + index)
+                else:
+                    newstorage[i + index] = deref
+
+        offset = obj.size_storage() - 1
         for i in range(index + 1, self.size_storage()):
-            newstorage[i + obj.size_storage() - 1] = self.storage[i]
+            child = newstorage[i + offset] = self.storage[i]
+            if isinstance(child, term.VarInTerm) and child.parent is self:
+                assert isinstance(self, ShapedCallableMutable)
+                indicator = child.indicator
+                if (isinstance(indicator, term.VarInTermIndex) and
+                        indicator.index == i):
+                    child.indicator = term.VarInTermIndex(i + offset)
         self.storage = newstorage
         self.shape = new_shape
+        return self
 
     def replace_child(self, index, obj):
         if isinstance(obj, ShapedCallableBase):
             new_shape = self.get_shape().get_transition(index, obj.get_shape())
             if new_shape is not None:
-                self._replace_child(index, obj, new_shape)
-                if isinstance(obj, ShapedCallableMutable):
-                    self = self._fixup_var_in_term(obj, index)
-                return self
+                return self._replace_child(index, obj, new_shape)
         return None
 
     @jit.unroll_safe
                 if deref is None:
                     self = self._make_mutable()
                     old_child.parent = self
-                    old_child.index = newi
+                    old_child.indicator = term.VarInTermIndex(newi)
                 else:
                     self.set_storage(newi, deref)
             newi += 1

prolog/interpreter/term.py

                 self.setvalue(other, heap)
             next._unify_derefed(other, heap, occurs_check)
 
+# _____________________________________________________________________
 
 class VarInTerm(Var):
     def __init__(self, parent, index):
         from prolog.interpreter.shape import ShapedCallableMutable
         assert isinstance(parent, ShapedCallableMutable)
         self.parent = parent
-        self.index = index
+        self.indicator = VarInTermIndex(index)
 
     def getbinding(self):
-        val = self.parent.get_storage(self.index)
+        val = self.indicator.get(self.parent)
         if val is self:
             return None
         return val
         from prolog.interpreter.shape import ShapedCallableMutable
         obj = self.parent
         assert isinstance(obj, ShapedCallableMutable)
-        index = jit.promote(self.index)
+        indicator = self.indicator
+        assert isinstance(indicator, VarInTermIndex)
+        index = jit.promote(indicator.index)
+        path = obj.get_shape().get_path(index)
         newobj = obj.replace_child(index, value)
         if newobj is None:
             obj.storage[index] = value
         else:
             assert newobj is obj
+        self.indicator = VarInTermPath(path)
 
     def __repr__(self):
         if self.getbinding():
             return "%s(%s)" % (self.__class__.__name__, self.getbinding())
-        return "%s(%s, %s)" % (self.__class__.__name__, self.parent.signature(), self.index)
+        return "%s(%s, %s)" % (self.__class__.__name__, self.parent.signature(), self.indicator.index)
 
+class VarInTermIndicator(object):
+    pass
 
+class VarInTermIndex(VarInTermIndicator): # XXX cache
+    _immutable_fields_ = ["index"]
+
+    def __init__(self, index):
+        self.index = index
+
+    def get(self, obj):
+        return obj.get_storage(self.index)
+
+class VarInTermPath(VarInTermIndicator): # XXX cache
+    _immutable_fields_ = ["path[*]"]
+
+    def __init__(self, path):
+        self.path = path
+
+    def get(self, obj):
+        for i in self.path:
+            obj = obj.argument_at(i)
+        return obj
+
+# _____________________________________________________________________
 
 class AttMap(object):
     def __init__(self):

prolog/interpreter/test/test_shape.py

     self._fixup_var_in_term(obj, 1)
     assert self.storage[2] is var
     assert var.parent is self
-    assert var.index == 2
+    assert var.indicator.index == 2
 
 
     # an bound VarInTerm is shunted
     self._fixup_var_in_term(obj, 1)
     assert self.storage[2] is b
     assert var.parent is obj
-    assert var.index == 0
+    assert var.indicator.index == 0
 
-def test_replace_child_fixup_varinterm():
+def test_replace_child_fixup_varinterm_at_end():
+    from prolog.interpreter.heap import Heap
+    h = Heap()
+    sig = signature.Signature.getsignature(".", 3)
+    build = shape.SharingShape
+    X = shape.InStorageShape.build()
+    s1 = build(sig, [X, X, X])
+    a = term.Callable.build("a")
+    b = term.Callable.build("b")
+    c = term.Callable.build("c")
+    nil = term.Callable.build("[]")
+    c1 = shape.ShapedCallableMutable(s1, [a, None, None])
+
+    c2 = shape.ShapedCallableMutable(s1, [b, c, c])
+    var1 = h.newvar_in_term(c1, 2)
+    c1.storage[2] = var1
+
+    s1.get_transition(1, s1)
+    s2 = s1.get_transition(1, s1)
+    c1._replace_child(1, c2, s2)
+    assert c1.storage[4].parent is c1
+    assert c1.storage[4].indicator.index == 4
+
+
+def test_replace_child_fixup_varinterm_from_replacement():
     from prolog.interpreter.heap import Heap
     h = Heap()
     sig = signature.Signature.getsignature(".", 3)
     res = c1.replace_child(1, c2)
     assert res is c1
     assert c1.storage[2].parent is c1
-    assert c1.storage[2].index == 2
+    assert c1.storage[2].indicator.index == 2
 
     c1 = shape.ShapedCallable(s1, [a, None, c])
     c2 = shape.ShapedCallableMutable(s1, [b, None, c])
     res = c1.replace_child(1, c2)
     assert isinstance(res, shape.ShapedCallableMutable)
     assert res.storage[2].parent is res
-    assert res.storage[2].index == 2
+    assert res.storage[2].indicator.index == 2
 
 def test_depth():
     sig = signature.Signature.getsignature(".", 2)

prolog/interpreter/test/test_specialized_terms.py

-from prolog.interpreter.term import Atom, Number, Term, Callable, specialized_term_classes
+from prolog.interpreter.term import Atom, Number, Term, Callable
 from prolog.interpreter.test.tool import parse
 import py
 

prolog/interpreter/test/test_varinterm.py

     t = Callable.build("a", [Callable.build("b"), NumberedVar(0)])
     res = t.copy_standardize_apart(h, [None])
     assert res.argument_at(1).parent is res
-    assert res.argument_at(1).index == 1
+    assert res.argument_at(1).indicator.index == 1
 
 def test_varinterm_bind():
     h = Heap()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.