This repo contains code to mirror other repos. It also contains the code that is getting mirrored.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

158 行
5.1 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 origin, (expansions, options) in rules.items():
  85. if options and options.filter_out:
  86. assert origin.startswith('_') # Just to make sure
  87. filter_out.add(origin)
  88. for origin, (expansions, options) in rules.items():
  89. keep_all_tokens = self.always_keep_all_tokens or (options.keep_all_tokens if options else False)
  90. expand1 = options.expand1 if options else False
  91. create_token = options.create_token if options else False
  92. for expansion, alias in expansions:
  93. if alias and origin.startswith('_'):
  94. raise Exception("Rule %s is marked for expansion (it starts with an underscore) and isn't allowed to have aliases (alias=%s)" % (origin, alias))
  95. wrapper_chain = filter(None, [
  96. (expand1 and not alias) and Expand1,
  97. create_token and Factory(TokenWrapper, create_token),
  98. create_rule_handler(expansion, keep_all_tokens, filter_out),
  99. self.propagate_positions and PropagatePositions,
  100. ])
  101. yield origin, expansion, options, alias or origin, wrapper_chain
  102. def apply(self, transformer=None):
  103. callback = Callback()
  104. new_rules = []
  105. for origin, expansion, options, alias, wrapper_chain in self.rule_builders:
  106. callback_name = '_callback_%s_%s' % (origin, '_'.join(expansion))
  107. try:
  108. f = transformer._get_func(alias)
  109. except AttributeError:
  110. f = NodeBuilder(self.tree_class, alias)
  111. for w in wrapper_chain:
  112. f = w(f)
  113. if hasattr(callback, callback_name):
  114. raise GrammarError("Rule expansion '%s' already exists in rule %s" % (' '.join(expansion), origin))
  115. setattr(callback, callback_name, f)
  116. new_rules.append( Rule( origin, expansion, callback_name, options ))
  117. return new_rules, callback