This repo contains code to mirror other repos. It also contains the code that is getting mirrored.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

158 Zeilen
5.1 KiB

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