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.

162 lines
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. ###}