# Source code for parso.normalizer

    from contextlib import contextmanager
    from typing import Dict, List

    class _NormalizerMeta(type):
        def __new__(cls, name, bases, dct):
            new_cls = type.__new__(cls, name, bases, dct)
            new_cls.rule_value_classes = {}
            new_cls.rule_type_classes = {}
            return new_cls

    class Normalizer(metaclass=_NormalizerMeta):
        _rule_type_instances: Dict[str, List[type]] = {}
        _rule_value_instances: Dict[str, List[type]] = {}

        def __init__(self, grammar, config):
            self.grammar = grammar
            self._config = config
            self.issues = []

            self._rule_type_instances = self._instantiate_rules('rule_type_classes')
            self._rule_value_instances = self._instantiate_rules('rule_value_classes')

        def _instantiate_rules(self, attr):
            dct = {}
            for base in type(self).mro():
                rules_map = getattr(base, attr, {})
                for type_, rule_classes in rules_map.items():
                    new = [rule_cls(self) for rule_cls in rule_classes]
                    dct.setdefault(type_, []).extend(new)
            return dct

        def walk(self, node):
            self.initialize(node)
            value = self.visit(node)
            self.finalize()
            return value

        def visit(self, node):
            try:
                children = node.children
            except AttributeError:
                return self.visit_leaf(node)
            else:
                with self.visit_node(node):
                    return ''.join(self.visit(child) for child in children)

        @contextmanager
        def visit_node(self, node):
            self._check_type_rules(node)
            yield

        def _check_type_rules(self, node):
            for rule in self._rule_type_instances.get(node.type, []):
                rule.feed_node(node)

        def visit_leaf(self, leaf):
            self._check_type_rules(leaf)

            for rule in self._rule_value_instances.get(leaf.value, []):
                rule.feed_node(leaf)

            return leaf.prefix + leaf.value

        def initialize(self, node):
            pass

        def finalize(self):
            pass

        def add_issue(self, node, code, message):
            issue = Issue(node, code, message)
            if issue not in self.issues:
                self.issues.append(issue)
            return True

        @classmethod
        def register_rule(cls, *, value=None, values=(), type=None, types=()):
            """
            Use it as a class decorator::

                normalizer = Normalizer('grammar', 'config')
                @normalizer.register_rule(value='foo')
                class MyRule(Rule):
                    error_code = 42
            """
            values = list(values)
            types = list(types)
            if value is not None:
                values.append(value)
            if type is not None:
                types.append(type)

            if not values and not types:
                raise ValueError("You must register at least something.")

            def decorator(rule_cls):
                for v in values:
                    cls.rule_value_classes.setdefault(v, []).append(rule_cls)
                for t in types:
                    cls.rule_type_classes.setdefault(t, []).append(rule_cls)
                return rule_cls

            return decorator

    class NormalizerConfig:
        normalizer_class = Normalizer

        def create_normalizer(self, grammar):
            if self.normalizer_class is None:
                return None

            return self.normalizer_class(grammar, self)

    class Issue:
        def __init__(self, node, code, message):
            self.code = code
            """
            An integer code that stands for the type of error.
            """
            self.message = message
            """
            A message (string) for the issue.
            """
            self.start_pos = node.start_pos
            """
            The start position position of the error as a tuple (line, column). As
            always in |parso| the first line is 1 and the first column 0.
            """
            self.end_pos = node.end_pos

        def __eq__(self, other):
            return self.start_pos == other.start_pos and self.code == other.code

        def __ne__(self, other):
            return not self.__eq__(other)

        def __hash__(self):
            return hash((self.code, self.start_pos))

        def __repr__(self):
            return '<%s: %s>' % (self.__class__.__name__, self.code)

    class Rule:
        code: int
        message: str

        def __init__(self, normalizer):
            self._normalizer = normalizer

        def is_issue(self, node):
            raise NotImplementedError()

        def get_node(self, node):
            return node

        def _get_message(self, message, node):
            if message is None:
                message = self.message
                if message is None:
                    raise ValueError("The message on the class is not set.")
            return message

        def add_issue(self, node, code=None, message=None):
            if code is None:
                code = self.code
                if code is None:
                    raise ValueError("The error code on the class is not set.")

            message = self._get_message(message, node)

            self._normalizer.add_issue(node, code, message)

        def feed_node(self, node):
            if self.is_issue(node):
                issue_node = self.get_node(node)
                self.add_issue(issue_node)

    class RefactoringNormalizer(Normalizer):
        def __init__(self, node_to_str_map):
            self._node_to_str_map = node_to_str_map

        def visit(self, node):
            try:
                return self._node_to_str_map[node]
            except KeyError:
                return super().visit(node)

        def visit_leaf(self, leaf):
            try:
                return self._node_to_str_map[leaf]
            except KeyError:
                return super().visit_leaf(leaf)