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.

143 lines
4.6 KiB

  1. from .common import GrammarError
  2. from .utils import suppress
  3. from .lexer import Token
  4. from .grammar import Rule
  5. ###{standalone
  6. from functools import partial
  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 children:
  21. for a in children:
  22. with suppress(AttributeError):
  23. res.line = a.line
  24. res.column = a.column
  25. break
  26. for a in reversed(children):
  27. with suppress(AttributeError):
  28. res.end_line = a.end_line
  29. res.end_column = a.end_column
  30. break
  31. return res
  32. class ChildFilter:
  33. def __init__(self, to_include, node_builder):
  34. self.node_builder = node_builder
  35. self.to_include = to_include
  36. def __call__(self, children):
  37. filtered = []
  38. for i, to_expand in self.to_include:
  39. if to_expand:
  40. filtered += children[i].children
  41. else:
  42. filtered.append(children[i])
  43. return self.node_builder(filtered)
  44. class ChildFilterLALR(ChildFilter):
  45. "Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)"
  46. def __call__(self, children):
  47. filtered = []
  48. for i, to_expand in self.to_include:
  49. if to_expand:
  50. if filtered:
  51. filtered += children[i].children
  52. else: # Optimize for left-recursion
  53. filtered = children[i].children
  54. else:
  55. filtered.append(children[i])
  56. return self.node_builder(filtered)
  57. def _should_expand(sym):
  58. return not sym.is_term and sym.name.startswith('_')
  59. def maybe_create_child_filter(expansion, filter_out, ambiguous):
  60. to_include = [(i, _should_expand(sym)) for i, sym in enumerate(expansion) if sym not in filter_out]
  61. if len(to_include) < len(expansion) or any(to_expand for i, to_expand in to_include):
  62. return partial(ChildFilter if ambiguous else ChildFilterLALR, to_include)
  63. class Callback(object):
  64. pass
  65. class ParseTreeBuilder:
  66. def __init__(self, rules, tree_class, propagate_positions=False, keep_all_tokens=False, ambiguous=False):
  67. self.tree_class = tree_class
  68. self.propagate_positions = propagate_positions
  69. self.always_keep_all_tokens = keep_all_tokens
  70. self.ambiguous = ambiguous
  71. self.rule_builders = list(self._init_builders(rules))
  72. self.user_aliases = {}
  73. def _init_builders(self, rules):
  74. filter_out = {rule.origin for rule in rules if rule.options and rule.options.filter_out}
  75. filter_out |= {sym for rule in rules for sym in rule.expansion if sym.is_term and sym.filter_out}
  76. assert all(t.name.startswith('_') for t in filter_out)
  77. for rule in rules:
  78. options = rule.options
  79. keep_all_tokens = self.always_keep_all_tokens or (options.keep_all_tokens if options else False)
  80. expand_single_child = options.expand1 if options else False
  81. wrapper_chain = filter(None, [
  82. (expand_single_child and not rule.alias) and ExpandSingleChild,
  83. maybe_create_child_filter(rule.expansion, () if keep_all_tokens else filter_out, self.ambiguous),
  84. self.propagate_positions and PropagatePositions,
  85. ])
  86. yield rule, wrapper_chain
  87. def create_callback(self, transformer=None):
  88. callback = Callback()
  89. for rule, wrapper_chain in self.rule_builders:
  90. internal_callback_name = '_callback_%s_%s' % (rule.origin, '_'.join(x.name for x in rule.expansion))
  91. user_callback_name = rule.alias or rule.origin.name
  92. try:
  93. f = transformer._get_func(user_callback_name)
  94. except AttributeError:
  95. f = partial(self.tree_class, user_callback_name)
  96. self.user_aliases[rule] = rule.alias
  97. rule.alias = internal_callback_name
  98. for w in wrapper_chain:
  99. f = w(f)
  100. if hasattr(callback, internal_callback_name):
  101. raise GrammarError("Rule '%s' already exists" % (rule,))
  102. setattr(callback, internal_callback_name, f)
  103. return callback
  104. ###}