This repo contains code to mirror other repos. It also contains the code that is getting mirrored.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 lines
8.5 KiB

  1. "This module implements an Earley Parser"
  2. # The parser uses a parse-forest to keep track of derivations and ambiguations.
  3. # When the parse ends successfully, a disambiguation stage resolves all ambiguity
  4. # (right now ambiguity resolution is not developed beyond the needs of lark)
  5. # Afterwards the parse tree is reduced (transformed) according to user callbacks.
  6. # I use the no-recursion version of Transformer and Visitor, because the tree might be
  7. # deeper than Python's recursion limit (a bit absurd, but that's life)
  8. #
  9. # The algorithm keeps track of each state set, using a corresponding Column instance.
  10. # Column keeps track of new items using NewsList instances.
  11. #
  12. # Author: Erez Shinan (2017)
  13. # Email : erezshin@gmail.com
  14. from ..common import ParseError, UnexpectedToken, is_terminal
  15. from ..tree import Tree, Visitor_NoRecurse, Transformer_NoRecurse
  16. from .grammar_analysis import GrammarAnalyzer
  17. class Derivation(Tree):
  18. _hash = None
  19. def __init__(self, rule, items=None):
  20. Tree.__init__(self, 'drv', items or [], rule=rule)
  21. def _pretty_label(self): # Nicer pretty for debugging the parser
  22. return self.rule.origin if self.rule else self.data
  23. def __hash__(self):
  24. if self._hash is None:
  25. self._hash = Tree.__hash__(self)
  26. return self._hash
  27. class Item(object):
  28. "An Earley Item, the atom of the algorithm."
  29. def __init__(self, rule, ptr, start, tree):
  30. self.rule = rule
  31. self.ptr = ptr
  32. self.start = start
  33. self.tree = tree if tree is not None else Derivation(self.rule)
  34. @property
  35. def expect(self):
  36. return self.rule.expansion[self.ptr]
  37. @property
  38. def is_complete(self):
  39. return self.ptr == len(self.rule.expansion)
  40. def advance(self, tree):
  41. assert self.tree.data == 'drv'
  42. new_tree = Derivation(self.rule, self.tree.children + [tree])
  43. return self.__class__(self.rule, self.ptr+1, self.start, new_tree)
  44. def __eq__(self, other):
  45. return self.start is other.start and self.ptr == other.ptr and self.rule == other.rule
  46. def __hash__(self):
  47. return hash((self.rule, self.ptr, id(self.start))) # Always runs Derivation.__hash__
  48. def __repr__(self):
  49. before = list(map(str, self.rule.expansion[:self.ptr]))
  50. after = list(map(str, self.rule.expansion[self.ptr:]))
  51. return '<(%d) %s : %s * %s>' % (id(self.start), self.rule.origin, ' '.join(before), ' '.join(after))
  52. class NewsList(list):
  53. "Keeps track of newly added items (append-only)"
  54. def __init__(self, initial=None):
  55. list.__init__(self, initial or [])
  56. self.last_iter = 0
  57. def get_news(self):
  58. i = self.last_iter
  59. self.last_iter = len(self)
  60. return self[i:]
  61. class Column:
  62. "An entry in the table, aka Earley Chart. Contains lists of items."
  63. def __init__(self, i, FIRST, predict_all=False):
  64. self.i = i
  65. self.to_reduce = NewsList()
  66. self.to_predict = NewsList()
  67. self.to_scan = []
  68. self.item_count = 0
  69. self.FIRST = FIRST
  70. self.predicted = set()
  71. self.completed = {}
  72. self.predict_all = predict_all
  73. def add(self, items):
  74. """Sort items into scan/predict/reduce newslists
  75. Makes sure only unique items are added.
  76. """
  77. for item in items:
  78. item_key = item, item.tree # Elsewhere, tree is not part of the comparison
  79. if item.is_complete:
  80. # XXX Potential bug: What happens if there's ambiguity in an empty rule?
  81. if item.rule.expansion and item_key in self.completed:
  82. old_tree = self.completed[item_key].tree
  83. if old_tree == item.tree:
  84. is_empty = not self.FIRST[item.rule.origin]
  85. if not is_empty:
  86. continue
  87. if old_tree.data != '_ambig':
  88. new_tree = old_tree.copy()
  89. new_tree.rule = old_tree.rule
  90. old_tree.set('_ambig', [new_tree])
  91. old_tree.rule = None # No longer a 'drv' node
  92. if item.tree.children[0] is old_tree: # XXX a little hacky!
  93. raise ParseError("Infinite recursion in grammar! (Rule %s)" % item.rule)
  94. if item.tree not in old_tree.children:
  95. old_tree.children.append(item.tree)
  96. # old_tree.children.append(item.tree)
  97. else:
  98. self.completed[item_key] = item
  99. self.to_reduce.append(item)
  100. else:
  101. if is_terminal(item.expect):
  102. self.to_scan.append(item)
  103. else:
  104. k = item_key if self.predict_all else item
  105. if k in self.predicted:
  106. continue
  107. self.predicted.add(k)
  108. self.to_predict.append(item)
  109. self.item_count += 1 # Only count if actually added
  110. def __bool__(self):
  111. return bool(self.item_count)
  112. __nonzero__ = __bool__ # Py2 backwards-compatibility
  113. class Parser:
  114. def __init__(self, parser_conf, term_matcher, resolve_ambiguity=None):
  115. self.analysis = GrammarAnalyzer(parser_conf)
  116. self.parser_conf = parser_conf
  117. self.resolve_ambiguity = resolve_ambiguity
  118. self.FIRST = self.analysis.FIRST
  119. self.postprocess = {}
  120. self.predictions = {}
  121. for rule in parser_conf.rules:
  122. self.postprocess[rule] = getattr(parser_conf.callback, rule.alias)
  123. self.predictions[rule.origin] = [x.rule for x in self.analysis.expand_rule(rule.origin)]
  124. self.term_matcher = term_matcher
  125. def parse(self, stream, start_symbol=None):
  126. # Define parser functions
  127. start_symbol = start_symbol or self.parser_conf.start
  128. _Item = Item
  129. match = self.term_matcher
  130. def predict(nonterm, column):
  131. assert not is_terminal(nonterm), nonterm
  132. return [_Item(rule, 0, column, None) for rule in self.predictions[nonterm]]
  133. def complete(item):
  134. name = item.rule.origin
  135. return [i.advance(item.tree) for i in item.start.to_predict if i.expect == name]
  136. def predict_and_complete(column):
  137. while True:
  138. to_predict = {x.expect for x in column.to_predict.get_news()
  139. if x.ptr} # if not part of an already predicted batch
  140. to_reduce = set(column.to_reduce.get_news())
  141. if not (to_predict or to_reduce):
  142. break
  143. for nonterm in to_predict:
  144. column.add( predict(nonterm, column) )
  145. for item in to_reduce:
  146. new_items = list(complete(item))
  147. if item in new_items:
  148. raise ParseError('Infinite recursion detected! (rule %s)' % item.rule)
  149. column.add(new_items)
  150. def scan(i, token, column):
  151. next_set = Column(i, self.FIRST)
  152. next_set.add(item.advance(token) for item in column.to_scan if match(item.expect, token))
  153. if not next_set:
  154. expect = {i.expect for i in column.to_scan}
  155. raise UnexpectedToken(token, expect, stream, i)
  156. return next_set
  157. # Main loop starts
  158. column0 = Column(0, self.FIRST)
  159. column0.add(predict(start_symbol, column0))
  160. column = column0
  161. for i, token in enumerate(stream):
  162. predict_and_complete(column)
  163. column = scan(i, token, column)
  164. predict_and_complete(column)
  165. # Parse ended. Now build a parse tree
  166. solutions = [n.tree for n in column.to_reduce
  167. if n.rule.origin==start_symbol and n.start is column0]
  168. if not solutions:
  169. raise ParseError('Incomplete parse: Could not find a solution to input')
  170. elif len(solutions) == 1:
  171. tree = solutions[0]
  172. else:
  173. tree = Tree('_ambig', solutions)
  174. if self.resolve_ambiguity:
  175. tree = self.resolve_ambiguity(tree)
  176. return ApplyCallbacks(self.postprocess).transform(tree)
  177. class ApplyCallbacks(Transformer_NoRecurse):
  178. def __init__(self, postprocess):
  179. self.postprocess = postprocess
  180. def drv(self, tree):
  181. children = tree.children
  182. callback = self.postprocess[tree.rule]
  183. if callback:
  184. return callback(children)
  185. else:
  186. return Tree(rule.origin, children)