This repo contains code to mirror other repos. It also contains the code that is getting mirrored.
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

157 righe
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