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.

149 lines
4.7 KiB

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