This repo contains code to mirror other repos. It also contains the code that is getting mirrored.
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 

162 líneas
4.9 KiB

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