normalizer.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. from contextlib import contextmanager
  2. from typing import Dict, List, Any
  3. class _NormalizerMeta(type):
  4. rule_value_classes: Any
  5. rule_type_classes: Any
  6. def __new__(cls, name, bases, dct):
  7. new_cls = type.__new__(cls, name, bases, dct)
  8. new_cls.rule_value_classes = {}
  9. new_cls.rule_type_classes = {}
  10. return new_cls
  11. class Normalizer(metaclass=_NormalizerMeta):
  12. _rule_type_instances: Dict[str, List[type]] = {}
  13. _rule_value_instances: Dict[str, List[type]] = {}
  14. def __init__(self, grammar, config):
  15. self.grammar = grammar
  16. self._config = config
  17. self.issues = []
  18. self._rule_type_instances = self._instantiate_rules('rule_type_classes')
  19. self._rule_value_instances = self._instantiate_rules('rule_value_classes')
  20. def _instantiate_rules(self, attr):
  21. dct = {}
  22. for base in type(self).mro():
  23. rules_map = getattr(base, attr, {})
  24. for type_, rule_classes in rules_map.items():
  25. new = [rule_cls(self) for rule_cls in rule_classes]
  26. dct.setdefault(type_, []).extend(new)
  27. return dct
  28. def walk(self, node):
  29. self.initialize(node)
  30. value = self.visit(node)
  31. self.finalize()
  32. return value
  33. def visit(self, node):
  34. try:
  35. children = node.children
  36. except AttributeError:
  37. return self.visit_leaf(node)
  38. else:
  39. with self.visit_node(node):
  40. return ''.join(self.visit(child) for child in children)
  41. @contextmanager
  42. def visit_node(self, node):
  43. self._check_type_rules(node)
  44. yield
  45. def _check_type_rules(self, node):
  46. for rule in self._rule_type_instances.get(node.type, []):
  47. rule.feed_node(node)
  48. def visit_leaf(self, leaf):
  49. self._check_type_rules(leaf)
  50. for rule in self._rule_value_instances.get(leaf.value, []):
  51. rule.feed_node(leaf)
  52. return leaf.prefix + leaf.value
  53. def initialize(self, node):
  54. pass
  55. def finalize(self):
  56. pass
  57. def add_issue(self, node, code, message):
  58. issue = Issue(node, code, message)
  59. if issue not in self.issues:
  60. self.issues.append(issue)
  61. return True
  62. @classmethod
  63. def register_rule(cls, *, value=None, values=(), type=None, types=()):
  64. """
  65. Use it as a class decorator::
  66. normalizer = Normalizer('grammar', 'config')
  67. @normalizer.register_rule(value='foo')
  68. class MyRule(Rule):
  69. error_code = 42
  70. """
  71. values = list(values)
  72. types = list(types)
  73. if value is not None:
  74. values.append(value)
  75. if type is not None:
  76. types.append(type)
  77. if not values and not types:
  78. raise ValueError("You must register at least something.")
  79. def decorator(rule_cls):
  80. for v in values:
  81. cls.rule_value_classes.setdefault(v, []).append(rule_cls)
  82. for t in types:
  83. cls.rule_type_classes.setdefault(t, []).append(rule_cls)
  84. return rule_cls
  85. return decorator
  86. class NormalizerConfig:
  87. normalizer_class = Normalizer
  88. def create_normalizer(self, grammar):
  89. return self.normalizer_class(grammar, self)
  90. class Issue:
  91. def __init__(self, node, code, message):
  92. self.code = code
  93. """
  94. An integer code that stands for the type of error.
  95. """
  96. self.message = message
  97. """
  98. A message (string) for the issue.
  99. """
  100. self.start_pos = node.start_pos
  101. """
  102. The start position position of the error as a tuple (line, column). As
  103. always in |parso| the first line is 1 and the first column 0.
  104. """
  105. self.end_pos = node.end_pos
  106. def __eq__(self, other):
  107. return self.start_pos == other.start_pos and self.code == other.code
  108. def __ne__(self, other):
  109. return not self.__eq__(other)
  110. def __hash__(self):
  111. return hash((self.code, self.start_pos))
  112. def __repr__(self):
  113. return '<%s: %s>' % (self.__class__.__name__, self.code)
  114. class Rule:
  115. code: int
  116. message: str
  117. def __init__(self, normalizer):
  118. self._normalizer = normalizer
  119. def is_issue(self, node):
  120. raise NotImplementedError()
  121. def get_node(self, node):
  122. return node
  123. def _get_message(self, message, node):
  124. if message is None:
  125. message = self.message
  126. if message is None:
  127. raise ValueError("The message on the class is not set.")
  128. return message
  129. def add_issue(self, node, code=None, message=None):
  130. if code is None:
  131. code = self.code
  132. if code is None:
  133. raise ValueError("The error code on the class is not set.")
  134. message = self._get_message(message, node)
  135. self._normalizer.add_issue(node, code, message)
  136. def feed_node(self, node):
  137. if self.is_issue(node):
  138. issue_node = self.get_node(node)
  139. self.add_issue(issue_node)
  140. class RefactoringNormalizer(Normalizer):
  141. def __init__(self, node_to_str_map):
  142. self._node_to_str_map = node_to_str_map
  143. def visit(self, node):
  144. try:
  145. return self._node_to_str_map[node]
  146. except KeyError:
  147. return super().visit(node)
  148. def visit_leaf(self, leaf):
  149. try:
  150. return self._node_to_str_map[leaf]
  151. except KeyError:
  152. return super().visit_leaf(leaf)