PageRenderTime 49ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/prolog/interpreter/heap.py

https://bitbucket.org/cfbolz/pyrolog/
Python | 239 lines | 181 code | 36 blank | 22 comment | 39 complexity | 9824d79a265bf3768ca900cc8caaf853 MD5 | raw file
  1. from rpython.rlib import debug
  2. from prolog.interpreter.term import BindingVar, AttVar
  3. from rpython.rlib import jit
  4. INIT_TRAIL_VAR = []
  5. INIT_TRAIL_BINDING = []
  6. UNROLL_SIZE = 6
  7. class Heap(object):
  8. def __init__(self, prev=None):
  9. self.trail_var = INIT_TRAIL_VAR
  10. debug.make_sure_not_resized(self.trail_var)
  11. self.trail_binding = INIT_TRAIL_BINDING
  12. debug.make_sure_not_resized(self.trail_binding)
  13. self.i = 0
  14. self.trail_attrs = None
  15. self.prev = prev
  16. self.discarded = False
  17. self.hook = None
  18. # _____________________________________________________
  19. # interface that term.py uses
  20. def _find_not_discarded(self):
  21. while self is not None and self.discarded:
  22. self = self.prev
  23. return self
  24. def add_trail_atts(self, attvar, attr_name):
  25. if self._is_created_in_self(attvar):
  26. return
  27. value, index = attvar.get_attribute(attr_name)
  28. self._add_entry_trail_attrs(attvar, index, value)
  29. def trail_new_attr(self, attvar, index, value):
  30. if self._is_created_in_self(attvar):
  31. return
  32. self._add_entry_trail_attrs(attvar, index, value)
  33. def _add_entry_trail_attrs(self, attvar, index, value):
  34. entry = (attvar, index, value)
  35. if self.trail_attrs is None:
  36. self.trail_attrs = [entry]
  37. else:
  38. self.trail_attrs.append(entry)
  39. def add_trail(self, var):
  40. """ Remember the current state of a variable to be able to backtrack it
  41. to that state. Usually called just before a variable changes. """
  42. # if the variable doesn't exist before the last choice point, don't
  43. # trail it (variable shunting)
  44. if self._is_created_in_self(var):
  45. return
  46. i = self.i
  47. if i >= len(self.trail_var):
  48. assert i == len(self.trail_var)
  49. self._double_size()
  50. self.trail_var[i] = var
  51. self.trail_binding[i] = var.binding
  52. self.i = i + 1
  53. def _is_created_in_self(self, var):
  54. created_in = var.created_after_choice_point
  55. if self is created_in: # fast path
  56. return True
  57. if created_in is not None and created_in.discarded:
  58. # unroll _find_not_discarded once for better jittability
  59. created_in = created_in.prev
  60. if created_in is not None and created_in.discarded:
  61. created_in = created_in._find_not_discarded()
  62. var.created_after_choice_point = created_in
  63. return self is created_in
  64. def _double_size(self):
  65. l = len(self.trail_var)
  66. if l == 0:
  67. self.trail_var = [None, None]
  68. self.trail_binding = [None, None]
  69. elif l == 1:
  70. assert 0, "cannot happen"
  71. else:
  72. self.trail_var = self.trail_var + [None] * l
  73. self.trail_binding = self.trail_binding + [None] * l
  74. def newvar(self):
  75. """ Make a new variable. Should return a Var instance, possibly with
  76. interesting attributes set that e.g. add_trail can inspect."""
  77. result = BindingVar()
  78. result.created_after_choice_point = self
  79. return result
  80. def new_attvar(self):
  81. result = AttVar()
  82. result.created_after_choice_point = self
  83. return result
  84. # _____________________________________________________
  85. def branch(self):
  86. """ Branch of a heap for a choice point. The return value is the new
  87. heap that should be used from now on, self is the heap that can be
  88. backtracked to."""
  89. res = Heap(self)
  90. return res
  91. @jit.unroll_safe
  92. def revert_upto(self, heap, discard_choicepoint=False):
  93. """ Revert to the heap corresponding to a choice point. The return
  94. value is the new heap that should be used."""
  95. previous = self
  96. while self is not heap:
  97. if self is None:
  98. break
  99. self._revert()
  100. previous = self
  101. self = self.prev
  102. if discard_choicepoint:
  103. return heap
  104. return previous
  105. @jit.look_inside_iff(lambda self: self.i < UNROLL_SIZE)
  106. def _revert(self):
  107. i = jit.promote(self.i) - 1
  108. while i >= 0:
  109. v = self.trail_var[i]
  110. assert v is not None
  111. v.binding = self.trail_binding[i]
  112. self.trail_var[i] = None
  113. self.trail_binding[i] = None
  114. i -= 1
  115. self.i = 0
  116. if self.trail_attrs is not None:
  117. for i in range(len(self.trail_attrs) - 1, -1, -1):
  118. attvar, index, value = self.trail_attrs[i]
  119. attvar.reset_field(index, value)
  120. self.trail_attrs = None
  121. self.hook = None
  122. def discard(self, current_heap):
  123. """ Remove a heap that is no longer needed (usually due to a cut) from
  124. a chain of frames. """
  125. self.discarded = True
  126. if current_heap.prev is self:
  127. current_heap._discard_try_remove_current_trail(self)
  128. if current_heap.trail_attrs is not None:
  129. current_heap._discard_try_remove_current_trail_attvars(self)
  130. # move the variable bindings from the discarded heap to the current
  131. # heap
  132. self._discard_move_bindings_to_current(current_heap)
  133. if self.trail_attrs is not None:
  134. if current_heap.trail_attrs is not None:
  135. current_heap.trail_attrs.extend(self.trail_attrs)
  136. else:
  137. current_heap.trail_attrs = self.trail_attrs
  138. current_heap.prev = self.prev
  139. self.trail_var = None
  140. self.trail_binding = None
  141. self.trail_attrs = None
  142. self.i = -1
  143. self.prev = current_heap
  144. else:
  145. return self
  146. return current_heap
  147. @jit.look_inside_iff(lambda self, discarded_heap:
  148. self.i < UNROLL_SIZE)
  149. def _discard_try_remove_current_trail(self, discarded_heap):
  150. targetpos = 0
  151. # check whether variables in the current heap no longer need to be
  152. # traced, because they originate in the discarded heap
  153. for i in range(jit.promote(self.i)):
  154. var = self.trail_var[i]
  155. binding = self.trail_binding[i]
  156. if var.created_after_choice_point is discarded_heap:
  157. var.created_after_choice_point = discarded_heap.prev
  158. self.trail_var[i] = None
  159. self.trail_binding[i] = None
  160. else:
  161. self.trail_var[targetpos] = var
  162. self.trail_binding[targetpos] = binding
  163. targetpos += 1
  164. self.i = targetpos
  165. def _discard_try_remove_current_trail_attvars(self, discarded_heap):
  166. trail_attrs = []
  167. targetpos = 0
  168. for var, attr, value in self.trail_attrs:
  169. if var.created_after_choice_point is discarded_heap:
  170. var.created_after_choice_point = discarded_heap.prev
  171. else:
  172. trail_attrs[targetpos] = (var, attr, value)
  173. if not trail_attrs:
  174. trail_attrs = None
  175. self.trail_attrs = trail_attrs
  176. @jit.look_inside_iff(lambda self, current_heap:
  177. self.i < UNROLL_SIZE)
  178. def _discard_move_bindings_to_current(self, current_heap):
  179. for i in range(jit.promote(self.i)):
  180. var = self.trail_var[i]
  181. currbinding = var.binding
  182. binding = self.trail_binding[i]
  183. var.binding = binding
  184. current_heap.add_trail(var)
  185. var.binding = currbinding
  186. def __repr__(self):
  187. return "<Heap %r trailed vars>" % (self.i, )
  188. def _dot(self, seen):
  189. if self in seen:
  190. return
  191. seen.add(self)
  192. yield '%s [label="%r", shape=octagon]' % (id(self), self)
  193. if self.prev:
  194. yield "%s -> %s [label=prev]" % (id(self), id(self.prev))
  195. for line in self.prev._dot(seen):
  196. yield line
  197. # methods to for the hook chain
  198. def add_hook(self, attvar):
  199. self.hook = HookCell(attvar, self.hook)
  200. class HookCell(object):
  201. def __init__(self, attvar, next=None):
  202. self.attvar = attvar
  203. self.next = next