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.
 
 

248 line
9.5 KiB

  1. from .exceptions import GrammarError
  2. from .lexer import Token
  3. from .tree import Tree
  4. from .visitors import InlineTransformer # XXX Deprecated
  5. ###{standalone
  6. from functools import partial, wraps
  7. from itertools import repeat, product
  8. class ExpandSingleChild:
  9. def __init__(self, node_builder):
  10. self.node_builder = node_builder
  11. def __call__(self, children):
  12. if len(children) == 1:
  13. return children[0]
  14. else:
  15. return self.node_builder(children)
  16. class PropagatePositions:
  17. def __init__(self, node_builder):
  18. self.node_builder = node_builder
  19. def __call__(self, children):
  20. res = self.node_builder(children)
  21. if isinstance(res, Tree):
  22. for c in children:
  23. if isinstance(c, Tree) and c.children and not c.meta.empty:
  24. res.meta.line = c.meta.line
  25. res.meta.column = c.meta.column
  26. res.meta.start_pos = c.meta.start_pos
  27. res.meta.empty = False
  28. break
  29. elif isinstance(c, Token):
  30. res.meta.line = c.line
  31. res.meta.column = c.column
  32. res.meta.start_pos = c.pos_in_stream
  33. res.meta.empty = False
  34. break
  35. for c in reversed(children):
  36. if isinstance(c, Tree) and c.children and not c.meta.empty:
  37. res.meta.end_line = c.meta.end_line
  38. res.meta.end_column = c.meta.end_column
  39. res.meta.end_pos = c.meta.end_pos
  40. res.meta.empty = False
  41. break
  42. elif isinstance(c, Token):
  43. res.meta.end_line = c.end_line
  44. res.meta.end_column = c.end_column
  45. res.meta.end_pos = c.pos_in_stream + len(c.value)
  46. res.meta.empty = False
  47. break
  48. return res
  49. class ChildFilter:
  50. def __init__(self, to_include, append_none, node_builder):
  51. self.node_builder = node_builder
  52. self.to_include = to_include
  53. self.append_none = append_none
  54. def __call__(self, children):
  55. filtered = []
  56. for i, to_expand, add_none in self.to_include:
  57. if add_none:
  58. filtered += [None] * add_none
  59. if to_expand:
  60. filtered += children[i].children
  61. else:
  62. filtered.append(children[i])
  63. if self.append_none:
  64. filtered += [None] * self.append_none
  65. return self.node_builder(filtered)
  66. class ChildFilterLALR(ChildFilter):
  67. "Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)"
  68. def __call__(self, children):
  69. filtered = []
  70. for i, to_expand, add_none in self.to_include:
  71. if add_none:
  72. filtered += [None] * add_none
  73. if to_expand:
  74. if filtered:
  75. filtered += children[i].children
  76. else: # Optimize for left-recursion
  77. filtered = children[i].children
  78. else:
  79. filtered.append(children[i])
  80. if self.append_none:
  81. filtered += [None] * self.append_none
  82. return self.node_builder(filtered)
  83. class ChildFilterLALR_NoPlaceholders(ChildFilter):
  84. "Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)"
  85. def __init__(self, to_include, node_builder):
  86. self.node_builder = node_builder
  87. self.to_include = to_include
  88. def __call__(self, children):
  89. filtered = []
  90. for i, to_expand in self.to_include:
  91. if to_expand:
  92. if filtered:
  93. filtered += children[i].children
  94. else: # Optimize for left-recursion
  95. filtered = children[i].children
  96. else:
  97. filtered.append(children[i])
  98. return self.node_builder(filtered)
  99. def _should_expand(sym):
  100. return not sym.is_term and sym.name.startswith('_')
  101. def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indices):
  102. # Prepare empty_indices as: How many Nones to insert at each index?
  103. if _empty_indices:
  104. assert _empty_indices.count(False) == len(expansion)
  105. s = ''.join(str(int(b)) for b in _empty_indices)
  106. empty_indices = [len(ones) for ones in s.split('0')]
  107. assert len(empty_indices) == len(expansion)+1, (empty_indices, len(expansion))
  108. else:
  109. empty_indices = [0] * (len(expansion)+1)
  110. to_include = []
  111. nones_to_add = 0
  112. for i, sym in enumerate(expansion):
  113. nones_to_add += empty_indices[i]
  114. if keep_all_tokens or not (sym.is_term and sym.filter_out):
  115. to_include.append((i, _should_expand(sym), nones_to_add))
  116. nones_to_add = 0
  117. nones_to_add += empty_indices[len(expansion)]
  118. if _empty_indices or len(to_include) < len(expansion) or any(to_expand for i, to_expand,_ in to_include):
  119. if _empty_indices or ambiguous:
  120. return partial(ChildFilter if ambiguous else ChildFilterLALR, to_include, nones_to_add)
  121. else:
  122. # LALR without placeholders
  123. return partial(ChildFilterLALR_NoPlaceholders, [(i, x) for i,x,_ in to_include])
  124. class AmbiguousExpander:
  125. """Deal with the case where we're expanding children ('_rule') into a parent but the children
  126. are ambiguous. i.e. (parent->_ambig->_expand_this_rule). In this case, make the parent itself
  127. ambiguous with as many copies as their are ambiguous children, and then copy the ambiguous children
  128. into the right parents in the right places, essentially shifting the ambiguiuty up the tree."""
  129. def __init__(self, to_expand, tree_class, node_builder):
  130. self.node_builder = node_builder
  131. self.tree_class = tree_class
  132. self.to_expand = to_expand
  133. def __call__(self, children):
  134. def _is_ambig_tree(child):
  135. return hasattr(child, 'data') and child.data == '_ambig'
  136. #### When we're repeatedly expanding ambiguities we can end up with nested ambiguities.
  137. # All children of an _ambig node should be a derivation of that ambig node, hence
  138. # it is safe to assume that if we see an _ambig node nested within an ambig node
  139. # it is safe to simply expand it into the parent _ambig node as an alternative derivation.
  140. ambiguous = []
  141. for i, child in enumerate(children):
  142. if _is_ambig_tree(child):
  143. if i in self.to_expand:
  144. ambiguous.append(i)
  145. to_expand = [j for j, grandchild in enumerate(child.children) if _is_ambig_tree(grandchild)]
  146. child.expand_kids_by_index(*to_expand)
  147. if not ambiguous:
  148. return self.node_builder(children)
  149. expand = [ iter(child.children) if i in ambiguous else repeat(child) for i, child in enumerate(children) ]
  150. return self.tree_class('_ambig', [self.node_builder(list(f[0])) for f in product(zip(*expand))])
  151. def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens):
  152. to_expand = [i for i, sym in enumerate(expansion)
  153. if keep_all_tokens or ((not (sym.is_term and sym.filter_out)) and _should_expand(sym))]
  154. if to_expand:
  155. return partial(AmbiguousExpander, to_expand, tree_class)
  156. def ptb_inline_args(func):
  157. @wraps(func)
  158. def f(children):
  159. return func(*children)
  160. return f
  161. class ParseTreeBuilder:
  162. def __init__(self, rules, tree_class, propagate_positions=False, keep_all_tokens=False, ambiguous=False, maybe_placeholders=False):
  163. self.tree_class = tree_class
  164. self.propagate_positions = propagate_positions
  165. self.always_keep_all_tokens = keep_all_tokens
  166. self.ambiguous = ambiguous
  167. self.maybe_placeholders = maybe_placeholders
  168. self.rule_builders = list(self._init_builders(rules))
  169. def _init_builders(self, rules):
  170. for rule in rules:
  171. options = rule.options
  172. keep_all_tokens = self.always_keep_all_tokens or (options.keep_all_tokens if options else False)
  173. expand_single_child = options.expand1 if options else False
  174. wrapper_chain = list(filter(None, [
  175. (expand_single_child and not rule.alias) and ExpandSingleChild,
  176. maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous, options.empty_indices if self.maybe_placeholders and options else None),
  177. self.propagate_positions and PropagatePositions,
  178. self.ambiguous and maybe_create_ambiguous_expander(self.tree_class, rule.expansion, keep_all_tokens),
  179. ]))
  180. yield rule, wrapper_chain
  181. def create_callback(self, transformer=None):
  182. callbacks = {}
  183. for rule, wrapper_chain in self.rule_builders:
  184. user_callback_name = rule.alias or rule.origin.name
  185. try:
  186. f = getattr(transformer, user_callback_name)
  187. assert not getattr(f, 'meta', False), "Meta args not supported for internal transformer"
  188. # XXX InlineTransformer is deprecated!
  189. if getattr(f, 'inline', False) or isinstance(transformer, InlineTransformer):
  190. f = ptb_inline_args(f)
  191. except AttributeError:
  192. f = partial(self.tree_class, user_callback_name)
  193. for w in wrapper_chain:
  194. f = w(f)
  195. if rule in callbacks:
  196. raise GrammarError("Rule '%s' already exists" % (rule,))
  197. callbacks[rule] = f
  198. return callbacks
  199. ###}