This repo contains code to mirror other repos. It also contains the code that is getting mirrored.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 

155 řádky
4.7 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 rule in rules:
  85. if rule.options and rule.options.filter_out:
  86. assert rule.origin.startswith('_') # Just to make sure
  87. filter_out.add(rule.origin)
  88. for rule in rules:
  89. options = rule.options
  90. keep_all_tokens = self.always_keep_all_tokens or (options.keep_all_tokens if options else False)
  91. expand1 = options.expand1 if options else False
  92. create_token = options.create_token if options else False
  93. wrapper_chain = filter(None, [
  94. (expand1 and not rule.alias) and Expand1,
  95. create_token and Factory(TokenWrapper, create_token),
  96. create_rule_handler(rule.expansion, keep_all_tokens, filter_out),
  97. self.propagate_positions and PropagatePositions,
  98. ])
  99. yield rule, wrapper_chain
  100. def apply(self, transformer=None):
  101. callback = Callback()
  102. for rule, wrapper_chain in self.rule_builders:
  103. internal_callback_name = '_callback_%s_%s' % (rule.origin, '_'.join(rule.expansion))
  104. user_callback_name = rule.alias or rule.origin
  105. try:
  106. f = transformer._get_func(user_callback_name)
  107. except AttributeError:
  108. f = NodeBuilder(self.tree_class, user_callback_name)
  109. rule.alias = internal_callback_name
  110. for w in wrapper_chain:
  111. f = w(f)
  112. if hasattr(callback, internal_callback_name):
  113. raise GrammarError("Rule '%s' already exists" % (rule,))
  114. setattr(callback, internal_callback_name, f)
  115. return callback