/basil/utils/Visitor.py

https://github.com/tylergreen/mython
Python | 296 lines | 207 code | 16 blank | 73 comment | 2 complexity | 02fb9e123815a17e679ee6468017fda9 MD5 | raw file
  1. #! /usr/bin/env python
  2. # ______________________________________________________________________
  3. """Module Visitor
  4. Implements the AbstractVisitor and TreeVisitor classes.
  5. Jonathan Riehl
  6. $Id: Visitor.py 12580 2005-05-19 20:10:21Z jriehl $
  7. """
  8. # ______________________________________________________________________
  9. # Module imports
  10. # ______________________________________________________________________
  11. # Module data
  12. __DEBUG__ = False
  13. flags = []
  14. # ______________________________________________________________________
  15. # Class definitions
  16. class ENTER (object):
  17. """The ENTER class is used to send the signal that the element is being
  18. seen for the first time (acting as a cue for prefix.
  19. """
  20. pass
  21. flags.append(ENTER)
  22. # ______________________________________________________________________
  23. class EXIT (object):
  24. """The EXIT class is used to signal that the element is being seen for
  25. the last time (acting as a cue for postfix actions.)
  26. """
  27. pass
  28. flags.append(EXIT)
  29. # ______________________________________________________________________
  30. class DUPLICATE (object):
  31. """The DUPLICATE class is used to signal that the element is being seen
  32. again (and the elements being visited are either a DAG or a graph.)
  33. """
  34. pass
  35. flags.append(DUPLICATE)
  36. # ______________________________________________________________________
  37. def only (*keepFlags):
  38. """only()
  39. Generate a flag mask that will only permit the passed flags through.
  40. """
  41. global flags
  42. retVal = []
  43. for flag in flags:
  44. if flag not in keepFlags:
  45. retVal.append(flag)
  46. return set(retVal)
  47. # ______________________________________________________________________
  48. class AbstractVisitor (object):
  49. """Class AbstractVisitor
  50. """
  51. # ____________________________________________________________
  52. def __init__ (self, target, mask = None):
  53. """AbstractVisitor.__init__()
  54. Constructor for the AbstractVisitor class. The mask argument should
  55. either be a set or a sequence that will be turned into a set.
  56. By default no visitation 'signals' are masked.
  57. """
  58. self.target = target
  59. if mask == None:
  60. mask = set()
  61. elif type(mask) in (list, tuple):
  62. mask = set(mask)
  63. self.mask = mask
  64. self.reset()
  65. # ____________________________________________________________
  66. def __iter__ (self):
  67. """AbstractVisitor.__iter__()
  68. Creates a generator that calls self.next() until the StopIteration
  69. exception is raised (which must be done by child classes.)
  70. """
  71. while True:
  72. yield self.next()
  73. # ____________________________________________________________
  74. def next (self):
  75. """AbstractVisitor.next()
  76. """
  77. raise NotImplementedError("Child must override next().")
  78. # ____________________________________________________________
  79. def up (self):
  80. """AbstractVisitor.up()
  81. """
  82. raise NotImplementedError("Child must override up().")
  83. # ____________________________________________________________
  84. def reset (self):
  85. """AbstractVisitor.reset()
  86. """
  87. raise NotImplementedError("Child must override reset().")
  88. # ______________________________________________________________________
  89. class IterVisitor (AbstractVisitor):
  90. """Class IterVisitor
  91. """
  92. # ____________________________________________________________
  93. def reset (self):
  94. """IterVisitor.reset()
  95. """
  96. self.visited = set()
  97. self.cursor = [(None, self.target)]
  98. # ____________________________________________________________
  99. def getIterForElem (self, elem):
  100. """IterVisitor.getIterForElem()
  101. By default, just calls iter() on the passed element. Can be
  102. overridden by subclasses to handle specific kinds of elements,
  103. or ignore elements that aren't iterable.
  104. """
  105. return iter(elem)
  106. # ____________________________________________________________
  107. def _next (self):
  108. """IterVisitor._next()
  109. """
  110. if __DEBUG__:
  111. print "IterVisitor._next():", self.cursor
  112. retVal = None
  113. if len(self.cursor) > 0:
  114. cIter, cTarget = self.cursor[-1]
  115. if cIter == None:
  116. # This should only happen just after a reset.
  117. cIter = self.getIterForElem(cTarget)
  118. self.cursor[-1] = (cIter, cTarget)
  119. retVal = ([ENTER], cTarget)
  120. else:
  121. try:
  122. nextTarget = cIter.next()
  123. retVal = ([ENTER], nextTarget)
  124. self.cursor.append((iter(nextTarget), nextTarget))
  125. if nextTarget in self.visited:
  126. retVal[0].append(DUPLICATE)
  127. else:
  128. self.visited.add(nextTarget)
  129. except StopIteration:
  130. if __DEBUG__:
  131. print "IterVisitor._next(): StopIteration", cIter
  132. del self.cursor[-1]
  133. retVal = ([EXIT], cTarget)
  134. else:
  135. raise StopIteration
  136. if __DEBUG__:
  137. print "IterVisitor._next() -> ", retVal
  138. return retVal
  139. # ____________________________________________________________
  140. def next (self):
  141. """IterVisitor.next()
  142. Implements a next() method similar to the next() method of an iter
  143. object. This in turn calls the _next() method until an element is
  144. visited that is not masked.
  145. """
  146. if __DEBUG__:
  147. print "IterVisitor.next():", self.cursor
  148. while True:
  149. retVal = self._next()
  150. flags, target = retVal
  151. maskedFlags = self.mask.intersection(flags)
  152. if len(maskedFlags) == 0:
  153. break
  154. elif DUPLICATE in maskedFlags:
  155. # Silently back out of duplicated visitations.
  156. self.up()
  157. return retVal
  158. # ____________________________________________________________
  159. def up (self):
  160. """IterVisitor.up()
  161. """
  162. if len(self.cursor) > 0:
  163. del self.cursor[-1]
  164. else:
  165. raise StopIteration
  166. # ______________________________________________________________________
  167. class TreeVisitor (IterVisitor):
  168. """Class TreeVisitor
  169. Subclass of the IterVisitor class. Overrides getIterForElem() to handle
  170. tree representation:
  171. tree := (payload, [tree...])
  172. """
  173. # ____________________________________________________________
  174. def getIterForElem (self, elem):
  175. """TreeVisitor.getIterForElem()
  176. """
  177. return iter(elem[1])
  178. # ______________________________________________________________________
  179. class SafeIterVisitor (IterVisitor):
  180. """Class SafeIterVisitor
  181. Subclass of the IterVisitor class. Overrides getIterForElem() to handle
  182. elements that are not iterable.
  183. """
  184. # ____________________________________________________________
  185. def getIterForElem (self, elem):
  186. """SafeIterVisitor.getIterForElem()
  187. """
  188. try:
  189. return iter(elem)
  190. except:
  191. # Assume that the exception is:
  192. # TypeError("iteration over non-sequence")
  193. pass
  194. return iter([])
  195. # ______________________________________________________________________
  196. # Main (test) routine
  197. def main ():
  198. # ____________________________________________________________
  199. class BasilList (list):
  200. def __repr__ (self):
  201. return "0x%x %s" % (id(self), list.__repr__(self))
  202. def __hash__ (self):
  203. return hash(id(self))
  204. # ____________________________________________________________
  205. def doVariousTests (target, *baseMask):
  206. print "_" * 70
  207. print "NORMAL"
  208. iv = IterVisitor(target, baseMask)
  209. for f, n in iv:
  210. print f, n
  211. print "_" * 70
  212. print "PREFIX"
  213. iv1 = IterVisitor(target, only(ENTER))
  214. for f, n in iv1:
  215. print f, n
  216. print "_" * 70
  217. print "POSTFIX"
  218. iv2 = IterVisitor(target, only(EXIT))
  219. for f, n in iv2:
  220. print f, n
  221. print "_" * 70
  222. # ____________________________________________________________
  223. # Tree test
  224. t0 = BasilList([])
  225. t1 = BasilList([])
  226. t2 = BasilList([])
  227. t1.append(t2)
  228. t3 = BasilList([])
  229. t1.append(t3)
  230. t0.append(t1)
  231. t4 = BasilList([])
  232. t0.append(t4)
  233. doVariousTests(t0)
  234. # ____________________________________________________________
  235. # DAG
  236. d0 = BasilList([])
  237. d1 = BasilList([])
  238. d0.append(d1)
  239. d2 = BasilList([])
  240. d0.append(d2)
  241. d3 = BasilList([])
  242. d1.append(d3)
  243. d2.append(d3)
  244. doVariousTests(d0)
  245. # ____________________________________________________________
  246. # Cycle
  247. c0 = BasilList([])
  248. c1 = BasilList([])
  249. c0.append(c1)
  250. c2 = BasilList([])
  251. c1.append(c2)
  252. c2.append(c1)
  253. doVariousTests(c0, DUPLICATE)
  254. # ______________________________________________________________________
  255. if __name__ == "__main__":
  256. main()
  257. # ______________________________________________________________________
  258. # End of Visitor.py