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.

259 lines
9.9 KiB

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