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.

224 lines
7.7 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. class ExpandSingleChild:
  8. def __init__(self, node_builder):
  9. self.node_builder = node_builder
  10. def __call__(self, children):
  11. if len(children) == 1:
  12. return children[0]
  13. else:
  14. return self.node_builder(children)
  15. class PropagatePositions:
  16. def __init__(self, node_builder):
  17. self.node_builder = node_builder
  18. def __call__(self, children):
  19. res = self.node_builder(children)
  20. if isinstance(res, Tree) and getattr(res.meta, 'empty', True):
  21. res.meta.empty = True
  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 Callback(object):
  125. pass
  126. def ptb_inline_args(func):
  127. @wraps(func)
  128. def f(children):
  129. return func(*children)
  130. return f
  131. class ParseTreeBuilder:
  132. def __init__(self, rules, tree_class, propagate_positions=False, keep_all_tokens=False, ambiguous=False, maybe_placeholders=False):
  133. self.tree_class = tree_class
  134. self.propagate_positions = propagate_positions
  135. self.always_keep_all_tokens = keep_all_tokens
  136. self.ambiguous = ambiguous
  137. self.maybe_placeholders = maybe_placeholders
  138. self.rule_builders = list(self._init_builders(rules))
  139. self.user_aliases = {}
  140. def _init_builders(self, rules):
  141. for rule in rules:
  142. options = rule.options
  143. keep_all_tokens = self.always_keep_all_tokens or (options.keep_all_tokens if options else False)
  144. expand_single_child = options.expand1 if options else False
  145. wrapper_chain = filter(None, [
  146. (expand_single_child and not rule.alias) and ExpandSingleChild,
  147. maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous, options.empty_indices if self.maybe_placeholders and options else None),
  148. self.propagate_positions and PropagatePositions,
  149. ])
  150. yield rule, wrapper_chain
  151. def create_callback(self, transformer=None):
  152. callback = Callback()
  153. i = 0
  154. for rule, wrapper_chain in self.rule_builders:
  155. internal_callback_name = '_cb%d_%s' % (i, rule.origin)
  156. i += 1
  157. user_callback_name = rule.alias or rule.origin.name
  158. try:
  159. f = getattr(transformer, user_callback_name)
  160. assert not getattr(f, 'meta', False), "Meta args not supported for internal transformer"
  161. # XXX InlineTransformer is deprecated!
  162. if getattr(f, 'inline', False) or isinstance(transformer, InlineTransformer):
  163. f = ptb_inline_args(f)
  164. except AttributeError:
  165. f = partial(self.tree_class, user_callback_name)
  166. self.user_aliases[rule] = rule.alias
  167. rule.alias = internal_callback_name
  168. for w in wrapper_chain:
  169. f = w(f)
  170. if hasattr(callback, internal_callback_name):
  171. raise GrammarError("Rule '%s' already exists" % (rule,))
  172. setattr(callback, internal_callback_name, f)
  173. return callback
  174. ###}