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.

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