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.

102 lines
3.5 KiB

  1. from .common import is_terminal, GrammarError
  2. class Callback(object):
  3. pass
  4. def create_expand1_tree_builder_function(tree_builder):
  5. def expand1(children):
  6. if len(children) == 1:
  7. return children[0]
  8. else:
  9. return tree_builder(children)
  10. return expand1
  11. def create_join_children(tree_builder):
  12. def join_children(children):
  13. children = [''.join(children)]
  14. return tree_builder(children)
  15. return join_children
  16. def create_rule_handler(expansion, usermethod, keep_all_tokens, filter_out):
  17. # if not keep_all_tokens:
  18. to_include = [(i, not is_terminal(sym) and sym.startswith('_'))
  19. for i, sym in enumerate(expansion)
  20. if keep_all_tokens
  21. or not ((is_terminal(sym) and sym.startswith('_')) or sym in filter_out)
  22. ]
  23. if len(to_include) < len(expansion) or any(to_expand for i, to_expand in to_include):
  24. def _build_ast(match):
  25. children = []
  26. for i, to_expand in to_include:
  27. if to_expand:
  28. children += match[i].children
  29. else:
  30. children.append(match[i])
  31. return usermethod(children)
  32. return _build_ast
  33. # else, if no filtering required..
  34. return usermethod
  35. class ParseTreeBuilder:
  36. def __init__(self, tree_class):
  37. self.tree_class = tree_class
  38. def _create_tree_builder_function(self, name):
  39. tree_class = self.tree_class
  40. def tree_builder_f(children):
  41. return tree_class(name, children)
  42. return tree_builder_f
  43. def create_tree_builder(self, rules, transformer):
  44. callback = Callback()
  45. new_rules = []
  46. filter_out = set()
  47. for origin, (expansions, options) in rules.items():
  48. if options and options.filter_out:
  49. assert origin.startswith('_') # Just to make sure
  50. filter_out.add(origin)
  51. for origin, (expansions, options) in rules.items():
  52. keep_all_tokens = options.keep_all_tokens if options else False
  53. expand1 = options.expand1 if options else False
  54. join_children = options.join_children if options else False
  55. _origin = origin
  56. for expansion, alias in expansions:
  57. if alias and origin.startswith('_'):
  58. raise Exception("Rule %s is marked for expansion (it starts with an underscore) and isn't allowed to have aliases (alias=%s)" % (origin, alias))
  59. try:
  60. f = transformer._get_func(alias or _origin)
  61. except AttributeError:
  62. if alias:
  63. f = self._create_tree_builder_function(alias)
  64. else:
  65. f = self._create_tree_builder_function(_origin)
  66. if expand1:
  67. f = create_expand1_tree_builder_function(f)
  68. if join_children:
  69. f = create_join_children(f)
  70. alias_handler = create_rule_handler(expansion, f, keep_all_tokens, filter_out)
  71. callback_name = 'autoalias_%s_%s' % (_origin, '_'.join(expansion))
  72. if hasattr(callback, callback_name):
  73. raise GrammarError("Rule expansion '%s' already exists in rule %s" % (' '.join(expansion), origin))
  74. setattr(callback, callback_name, alias_handler)
  75. new_rules.append(( _origin, expansion, callback_name ))
  76. return new_rules, callback