PredictionContext.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. #
  2. # Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
  3. # Use of this file is governed by the BSD 3-clause license that
  4. # can be found in the LICENSE.txt file in the project root.
  5. #/
  6. from io import StringIO
  7. from antlr4.error.Errors import IllegalStateException
  8. from antlr4.RuleContext import RuleContext
  9. from antlr4.atn.ATN import ATN
  10. from antlr4.atn.ATNState import ATNState
  11. class PredictionContext(object):
  12. # Represents {@code $} in local context prediction, which means wildcard.
  13. # {@code#+x =#}.
  14. #/
  15. EMPTY = None
  16. # Represents {@code $} in an array in full context mode, when {@code $}
  17. # doesn't mean wildcard: {@code $ + x = [$,x]}. Here,
  18. # {@code $} = {@link #EMPTY_RETURN_STATE}.
  19. #/
  20. EMPTY_RETURN_STATE = 0x7FFFFFFF
  21. globalNodeCount = 1
  22. id = globalNodeCount
  23. # Stores the computed hash code of this {@link PredictionContext}. The hash
  24. # code is computed in parts to match the following reference algorithm.
  25. #
  26. # <pre>
  27. # private int referenceHashCode() {
  28. # int hash = {@link MurmurHash#initialize MurmurHash.initialize}({@link #INITIAL_HASH});
  29. #
  30. # for (int i = 0; i &lt; {@link #size()}; i++) {
  31. # hash = {@link MurmurHash#update MurmurHash.update}(hash, {@link #getParent getParent}(i));
  32. # }
  33. #
  34. # for (int i = 0; i &lt; {@link #size()}; i++) {
  35. # hash = {@link MurmurHash#update MurmurHash.update}(hash, {@link #getReturnState getReturnState}(i));
  36. # }
  37. #
  38. # hash = {@link MurmurHash#finish MurmurHash.finish}(hash, 2# {@link #size()});
  39. # return hash;
  40. # }
  41. # </pre>
  42. #/
  43. def __init__(self, cachedHashCode:int):
  44. self.cachedHashCode = cachedHashCode
  45. def __len__(self):
  46. return 0
  47. # This means only the {@link #EMPTY} context is in set.
  48. def isEmpty(self):
  49. return self is self.EMPTY
  50. def hasEmptyPath(self):
  51. return self.getReturnState(len(self) - 1) == self.EMPTY_RETURN_STATE
  52. def getReturnState(self, index:int):
  53. raise IllegalStateException("illegal!")
  54. def __hash__(self):
  55. return self.cachedHashCode
  56. def calculateHashCode(parent:PredictionContext, returnState:int):
  57. return hash("") if parent is None else hash((hash(parent), returnState))
  58. def calculateListsHashCode(parents:[], returnStates:[] ):
  59. h = 0
  60. for parent, returnState in zip(parents, returnStates):
  61. h = hash((h, calculateHashCode(parent, returnState)))
  62. return h
  63. # Used to cache {@link PredictionContext} objects. Its used for the shared
  64. # context cash associated with contexts in DFA states. This cache
  65. # can be used for both lexers and parsers.
  66. class PredictionContextCache(object):
  67. def __init__(self):
  68. self.cache = dict()
  69. # Add a context to the cache and return it. If the context already exists,
  70. # return that one instead and do not add a new context to the cache.
  71. # Protect shared cache from unsafe thread access.
  72. #
  73. def add(self, ctx:PredictionContext):
  74. if ctx==PredictionContext.EMPTY:
  75. return PredictionContext.EMPTY
  76. existing = self.cache.get(ctx, None)
  77. if existing is not None:
  78. return existing
  79. self.cache[ctx] = ctx
  80. return ctx
  81. def get(self, ctx:PredictionContext):
  82. return self.cache.get(ctx, None)
  83. def __len__(self):
  84. return len(self.cache)
  85. class SingletonPredictionContext(PredictionContext):
  86. @staticmethod
  87. def create(parent:PredictionContext , returnState:int ):
  88. if returnState == PredictionContext.EMPTY_RETURN_STATE and parent is None:
  89. # someone can pass in the bits of an array ctx that mean $
  90. return SingletonPredictionContext.EMPTY
  91. else:
  92. return SingletonPredictionContext(parent, returnState)
  93. def __init__(self, parent:PredictionContext, returnState:int):
  94. hashCode = calculateHashCode(parent, returnState)
  95. super().__init__(hashCode)
  96. self.parentCtx = parent
  97. self.returnState = returnState
  98. def __len__(self):
  99. return 1
  100. def getParent(self, index:int):
  101. return self.parentCtx
  102. def getReturnState(self, index:int):
  103. return self.returnState
  104. def __eq__(self, other):
  105. if self is other:
  106. return True
  107. elif other is None:
  108. return False
  109. elif not isinstance(other, SingletonPredictionContext):
  110. return False
  111. else:
  112. return self.returnState == other.returnState and self.parentCtx == other.parentCtx
  113. def __hash__(self):
  114. return self.cachedHashCode
  115. def __str__(self):
  116. up = "" if self.parentCtx is None else str(self.parentCtx)
  117. if len(up)==0:
  118. if self.returnState == self.EMPTY_RETURN_STATE:
  119. return "$"
  120. else:
  121. return str(self.returnState)
  122. else:
  123. return str(self.returnState) + " " + up
  124. class EmptyPredictionContext(SingletonPredictionContext):
  125. def __init__(self):
  126. super().__init__(None, PredictionContext.EMPTY_RETURN_STATE)
  127. def isEmpty(self):
  128. return True
  129. def __eq__(self, other):
  130. return self is other
  131. def __hash__(self):
  132. return self.cachedHashCode
  133. def __str__(self):
  134. return "$"
  135. PredictionContext.EMPTY = EmptyPredictionContext()
  136. class ArrayPredictionContext(PredictionContext):
  137. # Parent can be null only if full ctx mode and we make an array
  138. # from {@link #EMPTY} and non-empty. We merge {@link #EMPTY} by using null parent and
  139. # returnState == {@link #EMPTY_RETURN_STATE}.
  140. def __init__(self, parents:list, returnStates:list):
  141. super().__init__(calculateListsHashCode(parents, returnStates))
  142. self.parents = parents
  143. self.returnStates = returnStates
  144. def isEmpty(self):
  145. # since EMPTY_RETURN_STATE can only appear in the last position, we
  146. # don't need to verify that size==1
  147. return self.returnStates[0]==PredictionContext.EMPTY_RETURN_STATE
  148. def __len__(self):
  149. return len(self.returnStates)
  150. def getParent(self, index:int):
  151. return self.parents[index]
  152. def getReturnState(self, index:int):
  153. return self.returnStates[index]
  154. def __eq__(self, other):
  155. if self is other:
  156. return True
  157. elif not isinstance(other, ArrayPredictionContext):
  158. return False
  159. elif hash(self) != hash(other):
  160. return False # can't be same if hash is different
  161. else:
  162. return self.returnStates==other.returnStates and self.parents==other.parents
  163. def __str__(self):
  164. if self.isEmpty():
  165. return "[]"
  166. with StringIO() as buf:
  167. buf.write("[")
  168. for i in range(0,len(self.returnStates)):
  169. if i>0:
  170. buf.write(", ")
  171. if self.returnStates[i]==PredictionContext.EMPTY_RETURN_STATE:
  172. buf.write("$")
  173. continue
  174. buf.write(str(self.returnStates[i]))
  175. if self.parents[i] is not None:
  176. buf.write(' ')
  177. buf.write(str(self.parents[i]))
  178. else:
  179. buf.write("null")
  180. buf.write("]")
  181. return buf.getvalue()
  182. def __hash__(self):
  183. return self.cachedHashCode
  184. # Convert a {@link RuleContext} tree to a {@link PredictionContext} graph.
  185. # Return {@link #EMPTY} if {@code outerContext} is empty or null.
  186. #/
  187. def PredictionContextFromRuleContext(atn:ATN, outerContext:RuleContext=None):
  188. if outerContext is None:
  189. outerContext = RuleContext.EMPTY
  190. # if we are in RuleContext of start rule, s, then PredictionContext
  191. # is EMPTY. Nobody called us. (if we are empty, return empty)
  192. if outerContext.parentCtx is None or outerContext is RuleContext.EMPTY:
  193. return PredictionContext.EMPTY
  194. # If we have a parent, convert it to a PredictionContext graph
  195. parent = PredictionContextFromRuleContext(atn, outerContext.parentCtx)
  196. state = atn.states[outerContext.invokingState]
  197. transition = state.transitions[0]
  198. return SingletonPredictionContext.create(parent, transition.followState.stateNumber)
  199. def merge(a:PredictionContext, b:PredictionContext, rootIsWildcard:bool, mergeCache:dict):
  200. # share same graph if both same
  201. if a==b:
  202. return a
  203. if isinstance(a, SingletonPredictionContext) and isinstance(b, SingletonPredictionContext):
  204. return mergeSingletons(a, b, rootIsWildcard, mergeCache)
  205. # At least one of a or b is array
  206. # If one is $ and rootIsWildcard, return $ as# wildcard
  207. if rootIsWildcard:
  208. if isinstance( a, EmptyPredictionContext ):
  209. return a
  210. if isinstance( b, EmptyPredictionContext ):
  211. return b
  212. # convert singleton so both are arrays to normalize
  213. if isinstance( a, SingletonPredictionContext ):
  214. a = ArrayPredictionContext([a.parentCtx], [a.returnState])
  215. if isinstance( b, SingletonPredictionContext):
  216. b = ArrayPredictionContext([b.parentCtx], [b.returnState])
  217. return mergeArrays(a, b, rootIsWildcard, mergeCache)
  218. #
  219. # Merge two {@link SingletonPredictionContext} instances.
  220. #
  221. # <p>Stack tops equal, parents merge is same; return left graph.<br>
  222. # <embed src="images/SingletonMerge_SameRootSamePar.svg" type="image/svg+xml"/></p>
  223. #
  224. # <p>Same stack top, parents differ; merge parents giving array node, then
  225. # remainders of those graphs. A new root node is created to point to the
  226. # merged parents.<br>
  227. # <embed src="images/SingletonMerge_SameRootDiffPar.svg" type="image/svg+xml"/></p>
  228. #
  229. # <p>Different stack tops pointing to same parent. Make array node for the
  230. # root where both element in the root point to the same (original)
  231. # parent.<br>
  232. # <embed src="images/SingletonMerge_DiffRootSamePar.svg" type="image/svg+xml"/></p>
  233. #
  234. # <p>Different stack tops pointing to different parents. Make array node for
  235. # the root where each element points to the corresponding original
  236. # parent.<br>
  237. # <embed src="images/SingletonMerge_DiffRootDiffPar.svg" type="image/svg+xml"/></p>
  238. #
  239. # @param a the first {@link SingletonPredictionContext}
  240. # @param b the second {@link SingletonPredictionContext}
  241. # @param rootIsWildcard {@code true} if this is a local-context merge,
  242. # otherwise false to indicate a full-context merge
  243. # @param mergeCache
  244. #/
  245. def mergeSingletons(a:SingletonPredictionContext, b:SingletonPredictionContext, rootIsWildcard:bool, mergeCache:dict):
  246. if mergeCache is not None:
  247. previous = mergeCache.get((a,b), None)
  248. if previous is not None:
  249. return previous
  250. previous = mergeCache.get((b,a), None)
  251. if previous is not None:
  252. return previous
  253. merged = mergeRoot(a, b, rootIsWildcard)
  254. if merged is not None:
  255. if mergeCache is not None:
  256. mergeCache[(a, b)] = merged
  257. return merged
  258. if a.returnState==b.returnState:
  259. parent = merge(a.parentCtx, b.parentCtx, rootIsWildcard, mergeCache)
  260. # if parent is same as existing a or b parent or reduced to a parent, return it
  261. if parent == a.parentCtx:
  262. return a # ax + bx = ax, if a=b
  263. if parent == b.parentCtx:
  264. return b # ax + bx = bx, if a=b
  265. # else: ax + ay = a'[x,y]
  266. # merge parents x and y, giving array node with x,y then remainders
  267. # of those graphs. dup a, a' points at merged array
  268. # new joined parent so create new singleton pointing to it, a'
  269. merged = SingletonPredictionContext.create(parent, a.returnState)
  270. if mergeCache is not None:
  271. mergeCache[(a, b)] = merged
  272. return merged
  273. else: # a != b payloads differ
  274. # see if we can collapse parents due to $+x parents if local ctx
  275. singleParent = None
  276. if a is b or (a.parentCtx is not None and a.parentCtx==b.parentCtx): # ax + bx = [a,b]x
  277. singleParent = a.parentCtx
  278. if singleParent is not None: # parents are same
  279. # sort payloads and use same parent
  280. payloads = [ a.returnState, b.returnState ]
  281. if a.returnState > b.returnState:
  282. payloads = [ b.returnState, a.returnState ]
  283. parents = [singleParent, singleParent]
  284. merged = ArrayPredictionContext(parents, payloads)
  285. if mergeCache is not None:
  286. mergeCache[(a, b)] = merged
  287. return merged
  288. # parents differ and can't merge them. Just pack together
  289. # into array; can't merge.
  290. # ax + by = [ax,by]
  291. payloads = [ a.returnState, b.returnState ]
  292. parents = [ a.parentCtx, b.parentCtx ]
  293. if a.returnState > b.returnState: # sort by payload
  294. payloads = [ b.returnState, a.returnState ]
  295. parents = [ b.parentCtx, a.parentCtx ]
  296. merged = ArrayPredictionContext(parents, payloads)
  297. if mergeCache is not None:
  298. mergeCache[(a, b)] = merged
  299. return merged
  300. #
  301. # Handle case where at least one of {@code a} or {@code b} is
  302. # {@link #EMPTY}. In the following diagrams, the symbol {@code $} is used
  303. # to represent {@link #EMPTY}.
  304. #
  305. # <h2>Local-Context Merges</h2>
  306. #
  307. # <p>These local-context merge operations are used when {@code rootIsWildcard}
  308. # is true.</p>
  309. #
  310. # <p>{@link #EMPTY} is superset of any graph; return {@link #EMPTY}.<br>
  311. # <embed src="images/LocalMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
  312. #
  313. # <p>{@link #EMPTY} and anything is {@code #EMPTY}, so merged parent is
  314. # {@code #EMPTY}; return left graph.<br>
  315. # <embed src="images/LocalMerge_EmptyParent.svg" type="image/svg+xml"/></p>
  316. #
  317. # <p>Special case of last merge if local context.<br>
  318. # <embed src="images/LocalMerge_DiffRoots.svg" type="image/svg+xml"/></p>
  319. #
  320. # <h2>Full-Context Merges</h2>
  321. #
  322. # <p>These full-context merge operations are used when {@code rootIsWildcard}
  323. # is false.</p>
  324. #
  325. # <p><embed src="images/FullMerge_EmptyRoots.svg" type="image/svg+xml"/></p>
  326. #
  327. # <p>Must keep all contexts; {@link #EMPTY} in array is a special value (and
  328. # null parent).<br>
  329. # <embed src="images/FullMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
  330. #
  331. # <p><embed src="images/FullMerge_SameRoot.svg" type="image/svg+xml"/></p>
  332. #
  333. # @param a the first {@link SingletonPredictionContext}
  334. # @param b the second {@link SingletonPredictionContext}
  335. # @param rootIsWildcard {@code true} if this is a local-context merge,
  336. # otherwise false to indicate a full-context merge
  337. #/
  338. def mergeRoot(a:SingletonPredictionContext, b:SingletonPredictionContext, rootIsWildcard:bool):
  339. if rootIsWildcard:
  340. if a == PredictionContext.EMPTY:
  341. return PredictionContext.EMPTY ## + b =#
  342. if b == PredictionContext.EMPTY:
  343. return PredictionContext.EMPTY # a +# =#
  344. else:
  345. if a == PredictionContext.EMPTY and b == PredictionContext.EMPTY:
  346. return PredictionContext.EMPTY # $ + $ = $
  347. elif a == PredictionContext.EMPTY: # $ + x = [$,x]
  348. payloads = [ b.returnState, PredictionContext.EMPTY_RETURN_STATE ]
  349. parents = [ b.parentCtx, None ]
  350. return ArrayPredictionContext(parents, payloads)
  351. elif b == PredictionContext.EMPTY: # x + $ = [$,x] ($ is always first if present)
  352. payloads = [ a.returnState, PredictionContext.EMPTY_RETURN_STATE ]
  353. parents = [ a.parentCtx, None ]
  354. return ArrayPredictionContext(parents, payloads)
  355. return None
  356. #
  357. # Merge two {@link ArrayPredictionContext} instances.
  358. #
  359. # <p>Different tops, different parents.<br>
  360. # <embed src="images/ArrayMerge_DiffTopDiffPar.svg" type="image/svg+xml"/></p>
  361. #
  362. # <p>Shared top, same parents.<br>
  363. # <embed src="images/ArrayMerge_ShareTopSamePar.svg" type="image/svg+xml"/></p>
  364. #
  365. # <p>Shared top, different parents.<br>
  366. # <embed src="images/ArrayMerge_ShareTopDiffPar.svg" type="image/svg+xml"/></p>
  367. #
  368. # <p>Shared top, all shared parents.<br>
  369. # <embed src="images/ArrayMerge_ShareTopSharePar.svg" type="image/svg+xml"/></p>
  370. #
  371. # <p>Equal tops, merge parents and reduce top to
  372. # {@link SingletonPredictionContext}.<br>
  373. # <embed src="images/ArrayMerge_EqualTop.svg" type="image/svg+xml"/></p>
  374. #/
  375. def mergeArrays(a:ArrayPredictionContext, b:ArrayPredictionContext, rootIsWildcard:bool, mergeCache:dict):
  376. if mergeCache is not None:
  377. previous = mergeCache.get((a,b), None)
  378. if previous is not None:
  379. return previous
  380. previous = mergeCache.get((b,a), None)
  381. if previous is not None:
  382. return previous
  383. # merge sorted payloads a + b => M
  384. i = 0 # walks a
  385. j = 0 # walks b
  386. k = 0 # walks target M array
  387. mergedReturnStates = [None] * (len(a.returnStates) + len( b.returnStates))
  388. mergedParents = [None] * len(mergedReturnStates)
  389. # walk and merge to yield mergedParents, mergedReturnStates
  390. while i<len(a.returnStates) and j<len(b.returnStates):
  391. a_parent = a.parents[i]
  392. b_parent = b.parents[j]
  393. if a.returnStates[i]==b.returnStates[j]:
  394. # same payload (stack tops are equal), must yield merged singleton
  395. payload = a.returnStates[i]
  396. # $+$ = $
  397. bothDollars = payload == PredictionContext.EMPTY_RETURN_STATE and \
  398. a_parent is None and b_parent is None
  399. ax_ax = (a_parent is not None and b_parent is not None) and a_parent==b_parent # ax+ax -> ax
  400. if bothDollars or ax_ax:
  401. mergedParents[k] = a_parent # choose left
  402. mergedReturnStates[k] = payload
  403. else: # ax+ay -> a'[x,y]
  404. mergedParent = merge(a_parent, b_parent, rootIsWildcard, mergeCache)
  405. mergedParents[k] = mergedParent
  406. mergedReturnStates[k] = payload
  407. i += 1 # hop over left one as usual
  408. j += 1 # but also skip one in right side since we merge
  409. elif a.returnStates[i]<b.returnStates[j]: # copy a[i] to M
  410. mergedParents[k] = a_parent
  411. mergedReturnStates[k] = a.returnStates[i]
  412. i += 1
  413. else: # b > a, copy b[j] to M
  414. mergedParents[k] = b_parent
  415. mergedReturnStates[k] = b.returnStates[j]
  416. j += 1
  417. k += 1
  418. # copy over any payloads remaining in either array
  419. if i < len(a.returnStates):
  420. for p in range(i, len(a.returnStates)):
  421. mergedParents[k] = a.parents[p]
  422. mergedReturnStates[k] = a.returnStates[p]
  423. k += 1
  424. else:
  425. for p in range(j, len(b.returnStates)):
  426. mergedParents[k] = b.parents[p]
  427. mergedReturnStates[k] = b.returnStates[p]
  428. k += 1
  429. # trim merged if we combined a few that had same stack tops
  430. if k < len(mergedParents): # write index < last position; trim
  431. if k == 1: # for just one merged element, return singleton top
  432. merged = SingletonPredictionContext.create(mergedParents[0], mergedReturnStates[0])
  433. if mergeCache is not None:
  434. mergeCache[(a,b)] = merged
  435. return merged
  436. mergedParents = mergedParents[0:k]
  437. mergedReturnStates = mergedReturnStates[0:k]
  438. merged = ArrayPredictionContext(mergedParents, mergedReturnStates)
  439. # if we created same array as a or b, return that instead
  440. # TODO: track whether this is possible above during merge sort for speed
  441. if merged==a:
  442. if mergeCache is not None:
  443. mergeCache[(a,b)] = a
  444. return a
  445. if merged==b:
  446. if mergeCache is not None:
  447. mergeCache[(a,b)] = b
  448. return b
  449. combineCommonParents(mergedParents)
  450. if mergeCache is not None:
  451. mergeCache[(a,b)] = merged
  452. return merged
  453. #
  454. # Make pass over all <em>M</em> {@code parents}; merge any {@code equals()}
  455. # ones.
  456. #/
  457. def combineCommonParents(parents:list):
  458. uniqueParents = dict()
  459. for p in range(0, len(parents)):
  460. parent = parents[p]
  461. if uniqueParents.get(parent, None) is None:
  462. uniqueParents[parent] = parent
  463. for p in range(0, len(parents)):
  464. parents[p] = uniqueParents[parents[p]]
  465. def getCachedPredictionContext(context:PredictionContext, contextCache:PredictionContextCache, visited:dict):
  466. if context.isEmpty():
  467. return context
  468. existing = visited.get(context)
  469. if existing is not None:
  470. return existing
  471. existing = contextCache.get(context)
  472. if existing is not None:
  473. visited[context] = existing
  474. return existing
  475. changed = False
  476. parents = [None] * len(context)
  477. for i in range(0, len(parents)):
  478. parent = getCachedPredictionContext(context.getParent(i), contextCache, visited)
  479. if changed or parent is not context.getParent(i):
  480. if not changed:
  481. parents = [context.getParent(j) for j in range(len(context))]
  482. changed = True
  483. parents[i] = parent
  484. if not changed:
  485. contextCache.add(context)
  486. visited[context] = context
  487. return context
  488. updated = None
  489. if len(parents) == 0:
  490. updated = PredictionContext.EMPTY
  491. elif len(parents) == 1:
  492. updated = SingletonPredictionContext.create(parents[0], context.getReturnState(0))
  493. else:
  494. updated = ArrayPredictionContext(parents, context.returnStates)
  495. contextCache.add(updated)
  496. visited[updated] = updated
  497. visited[context] = updated
  498. return updated
  499. # # extra structures, but cut/paste/morphed works, so leave it.
  500. # # seems to do a breadth-first walk
  501. # public static List<PredictionContext> getAllNodes(PredictionContext context) {
  502. # Map<PredictionContext, PredictionContext> visited =
  503. # new IdentityHashMap<PredictionContext, PredictionContext>();
  504. # Deque<PredictionContext> workList = new ArrayDeque<PredictionContext>();
  505. # workList.add(context);
  506. # visited.put(context, context);
  507. # List<PredictionContext> nodes = new ArrayList<PredictionContext>();
  508. # while (!workList.isEmpty()) {
  509. # PredictionContext current = workList.pop();
  510. # nodes.add(current);
  511. # for (int i = 0; i < current.size(); i++) {
  512. # PredictionContext parent = current.getParent(i);
  513. # if ( parent!=null && visited.put(parent, parent) == null) {
  514. # workList.push(parent);
  515. # }
  516. # }
  517. # }
  518. # return nodes;
  519. # }
  520. # ter's recursive version of Sam's getAllNodes()
  521. def getAllContextNodes(context:PredictionContext, nodes:list=None, visited:dict=None):
  522. if nodes is None:
  523. nodes = list()
  524. return getAllContextNodes(context, nodes, visited)
  525. elif visited is None:
  526. visited = dict()
  527. return getAllContextNodes(context, nodes, visited)
  528. else:
  529. if context is None or visited.get(context, None) is not None:
  530. return nodes
  531. visited.put(context, context)
  532. nodes.add(context)
  533. for i in range(0, len(context)):
  534. getAllContextNodes(context.getParent(i), nodes, visited)
  535. return nodes