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.

192 lines
6.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. 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 AddMaybePlaceholder:
  16. def __init__(self, empty_indices, node_builder):
  17. self.node_builder = node_builder
  18. self.empty_indices = empty_indices
  19. def __call__(self, children):
  20. t = self.node_builder(children)
  21. if self.empty_indices:
  22. exp_len, empty_indices = self.empty_indices
  23. # Calculate offset to handle repetition correctly
  24. # e.g. ("a" "b"?)+
  25. # For non-repetitive rules, offset should be 0
  26. offset = len(t.children) - (exp_len - len(empty_indices))
  27. for i in empty_indices:
  28. t.children.insert(i + offset, None)
  29. return t
  30. class PropagatePositions:
  31. def __init__(self, node_builder):
  32. self.node_builder = node_builder
  33. def __call__(self, children):
  34. res = self.node_builder(children)
  35. if isinstance(res, Tree) and getattr(res.meta, 'empty', True):
  36. res.meta.empty = True
  37. for c in children:
  38. if isinstance(c, Tree) and c.children and not c.meta.empty:
  39. res.meta.line = c.meta.line
  40. res.meta.column = c.meta.column
  41. res.meta.start_pos = c.meta.start_pos
  42. res.meta.empty = False
  43. break
  44. elif isinstance(c, Token):
  45. res.meta.line = c.line
  46. res.meta.column = c.column
  47. res.meta.start_pos = c.pos_in_stream
  48. res.meta.empty = False
  49. break
  50. for c in reversed(children):
  51. if isinstance(c, Tree) and c.children and not c.meta.empty:
  52. res.meta.end_line = c.meta.end_line
  53. res.meta.end_column = c.meta.end_column
  54. res.meta.end_pos = c.meta.end_pos
  55. res.meta.empty = False
  56. break
  57. elif isinstance(c, Token):
  58. res.meta.end_line = c.end_line
  59. res.meta.end_column = c.end_column
  60. res.meta.end_pos = c.pos_in_stream + len(c.value)
  61. res.meta.empty = False
  62. break
  63. return res
  64. class ChildFilter:
  65. def __init__(self, to_include, node_builder):
  66. self.node_builder = node_builder
  67. self.to_include = to_include
  68. def __call__(self, children):
  69. filtered = []
  70. for i, to_expand in self.to_include:
  71. if to_expand:
  72. filtered += children[i].children
  73. else:
  74. filtered.append(children[i])
  75. return self.node_builder(filtered)
  76. class ChildFilterLALR(ChildFilter):
  77. "Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)"
  78. def __call__(self, children):
  79. filtered = []
  80. for i, to_expand in self.to_include:
  81. if to_expand:
  82. if filtered:
  83. filtered += children[i].children
  84. else: # Optimize for left-recursion
  85. filtered = children[i].children
  86. else:
  87. filtered.append(children[i])
  88. return self.node_builder(filtered)
  89. def _should_expand(sym):
  90. return not sym.is_term and sym.name.startswith('_')
  91. def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous):
  92. to_include = [(i, _should_expand(sym)) for i, sym in enumerate(expansion)
  93. if keep_all_tokens or not (sym.is_term and sym.filter_out)]
  94. if len(to_include) < len(expansion) or any(to_expand for i, to_expand in to_include):
  95. return partial(ChildFilter if ambiguous else ChildFilterLALR, to_include)
  96. class Callback(object):
  97. pass
  98. def ptb_inline_args(func):
  99. @wraps(func)
  100. def f(children):
  101. return func(*children)
  102. return f
  103. class ParseTreeBuilder:
  104. def __init__(self, rules, tree_class, propagate_positions=False, keep_all_tokens=False, ambiguous=False, maybe_placeholders=False):
  105. self.tree_class = tree_class
  106. self.propagate_positions = propagate_positions
  107. self.always_keep_all_tokens = keep_all_tokens
  108. self.ambiguous = ambiguous
  109. self.maybe_placeholders = maybe_placeholders
  110. self.rule_builders = list(self._init_builders(rules))
  111. self.user_aliases = {}
  112. def _init_builders(self, rules):
  113. for rule in rules:
  114. options = rule.options
  115. keep_all_tokens = self.always_keep_all_tokens or (options.keep_all_tokens if options else False)
  116. expand_single_child = options.expand1 if options else False
  117. wrapper_chain = filter(None, [
  118. (expand_single_child and not rule.alias) and ExpandSingleChild,
  119. maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous),
  120. self.maybe_placeholders and partial(AddMaybePlaceholder, options.empty_indices),
  121. self.propagate_positions and PropagatePositions,
  122. ])
  123. yield rule, wrapper_chain
  124. def create_callback(self, transformer=None):
  125. callback = Callback()
  126. i = 0
  127. for rule, wrapper_chain in self.rule_builders:
  128. internal_callback_name = '_cb%d_%s' % (i, rule.origin)
  129. i += 1
  130. user_callback_name = rule.alias or rule.origin.name
  131. try:
  132. f = getattr(transformer, user_callback_name)
  133. assert not getattr(f, 'meta', False), "Meta args not supported for internal transformer"
  134. # XXX InlineTransformer is deprecated!
  135. if getattr(f, 'inline', False) or isinstance(transformer, InlineTransformer):
  136. f = ptb_inline_args(f)
  137. except AttributeError:
  138. f = partial(self.tree_class, user_callback_name)
  139. self.user_aliases[rule] = rule.alias
  140. rule.alias = internal_callback_name
  141. for w in wrapper_chain:
  142. f = w(f)
  143. if hasattr(callback, internal_callback_name):
  144. raise GrammarError("Rule '%s' already exists" % (rule,))
  145. setattr(callback, internal_callback_name, f)
  146. return callback
  147. ###}