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.

157 rivejä
5.1 KiB

  1. from .common import is_terminal, GrammarError
  2. from .utils import suppress
  3. from .lexer import Token
  4. class NodeBuilder:
  5. def __init__(self, tree_class, name):
  6. self.tree_class = tree_class
  7. self.name = name
  8. def __call__(self, children):
  9. return self.tree_class(self.name, children)
  10. class Expand1:
  11. def __init__(self, node_builder):
  12. self.node_builder = node_builder
  13. def __call__(self, children):
  14. if len(children) == 1:
  15. return children[0]
  16. else:
  17. return self.node_builder(children)
  18. class Factory:
  19. def __init__(self, cls, *args):
  20. self.cls = cls
  21. self.args = args
  22. def __call__(self, node_builder):
  23. return self.cls(node_builder, *self.args)
  24. class TokenWrapper:
  25. "Used for fixing the results of scanless parsing"
  26. def __init__(self, node_builder, token_name):
  27. self.node_builder = node_builder
  28. self.token_name = token_name
  29. def __call__(self, children):
  30. return self.node_builder( [Token(self.token_name, ''.join(children))] )
  31. def identity(node_builder):
  32. return node_builder
  33. class ChildFilter:
  34. def __init__(self, node_builder, to_include):
  35. self.node_builder = node_builder
  36. self.to_include = to_include
  37. def __call__(self, children):
  38. filtered = []
  39. for i, to_expand in self.to_include:
  40. if to_expand:
  41. filtered += children[i].children
  42. else:
  43. filtered.append(children[i])
  44. return self.node_builder(filtered)
  45. def create_rule_handler(expansion, keep_all_tokens, filter_out):
  46. # if not keep_all_tokens:
  47. to_include = [(i, not is_terminal(sym) and sym.startswith('_'))
  48. for i, sym in enumerate(expansion)
  49. if keep_all_tokens
  50. or not ((is_terminal(sym) and sym.startswith('_')) or sym in filter_out)
  51. ]
  52. if len(to_include) < len(expansion) or any(to_expand for i, to_expand in to_include):
  53. return Factory(ChildFilter, to_include)
  54. # else, if no filtering required..
  55. return identity
  56. class PropagatePositions:
  57. def __init__(self, node_builder):
  58. self.node_builder = node_builder
  59. def __call__(self, children):
  60. res = self.node_builder(children)
  61. if children:
  62. for a in children:
  63. with suppress(AttributeError):
  64. res.line = a.line
  65. res.column = a.column
  66. break
  67. for a in reversed(children):
  68. with suppress(AttributeError):
  69. res.end_line = a.end_line
  70. res.end_col = a.end_col
  71. break
  72. return res
  73. class Callback(object):
  74. pass
  75. class ParseTreeBuilder:
  76. def __init__(self, rules, tree_class, propagate_positions=False, keep_all_tokens=False):
  77. self.tree_class = tree_class
  78. self.propagate_positions = propagate_positions
  79. self.always_keep_all_tokens = keep_all_tokens
  80. self.rule_builders = list(self._init_builders(rules))
  81. def _init_builders(self, rules):
  82. filter_out = set()
  83. for origin, (expansions, options) in rules.items():
  84. if options and options.filter_out:
  85. assert origin.startswith('_') # Just to make sure
  86. filter_out.add(origin)
  87. for origin, (expansions, options) in rules.items():
  88. keep_all_tokens = self.always_keep_all_tokens or (options.keep_all_tokens if options else False)
  89. expand1 = options.expand1 if options else False
  90. create_token = options.create_token if options else False
  91. for expansion, alias in expansions:
  92. if alias and origin.startswith('_'):
  93. raise Exception("Rule %s is marked for expansion (it starts with an underscore) and isn't allowed to have aliases (alias=%s)" % (origin, alias))
  94. wrapper_chain = filter(None, [
  95. (expand1 and not alias) and Expand1,
  96. create_token and Factory(TokenWrapper, create_token),
  97. create_rule_handler(expansion, keep_all_tokens, filter_out),
  98. self.propagate_positions and PropagatePositions,
  99. ])
  100. yield origin, expansion, options, alias or origin, wrapper_chain
  101. def apply(self, transformer=None):
  102. callback = Callback()
  103. new_rules = []
  104. for origin, expansion, options, alias, wrapper_chain in self.rule_builders:
  105. callback_name = '_callback_%s_%s' % (origin, '_'.join(expansion))
  106. try:
  107. f = transformer._get_func(alias)
  108. except AttributeError:
  109. f = NodeBuilder(self.tree_class, alias)
  110. for w in wrapper_chain:
  111. f = w(f)
  112. if hasattr(callback, callback_name):
  113. raise GrammarError("Rule expansion '%s' already exists in rule %s" % (' '.join(expansion), origin))
  114. setattr(callback, callback_name, f)
  115. new_rules.append(( origin, expansion, callback_name, options ))
  116. return new_rules, callback