_DEFAULT_MARKER_: Any = Marker("_DEFAULT_MARKER_") class ValueKind(Enum):
    VALUE = 0
    MANDATORY_MISSING = 1
    INTERPOLATION = 2 def _get_value(value: Any) -> Any:
    from .base import Container
    from .nodes import ValueNode

    if isinstance(value, ValueNode):
        return value._value()
    elif isinstance(value, Container):
        boxed = value._value()
        if boxed is None or _is_missing_literal(boxed) or _is_interpolation(boxed):
            return boxed

    # return primitives and regular OmegaConf Containers as is
    return value def _is_missing_value(value: Any) -> bool:
    from omegaconf import Node

    if isinstance(value, Node):
        value = value._value()
    return _is_missing_literal(value) def format_and_raise(
    node: Any,
    key: Any,
    value: Any,
    msg: str,
    cause: Exception,
    type_override: Any = None,
) -> None:
    from omegaconf import OmegaConf
    from omegaconf.base import Node

    if isinstance(cause, AssertionError):
        raise

    if isinstance(cause, OmegaConfBaseException) and cause._initialized:
        ex = cause
        if type_override is not None:
            ex = type_override(str(cause))
            ex.__dict__ = copy.deepcopy(cause.__dict__)
        _raise(ex, cause)

    object_type: Optional[Type[Any]]
    object_type_str: Optional[str] = None
    ref_type: Optional[Type[Any]]
    ref_type_str: Optional[str]

    child_node: Optional[Node] = None
    if node is None:
        full_key = key if key is not None else ""
        object_type = None
        ref_type = None
        ref_type_str = None
    else:
        if key is not None and not node._is_none():
            child_node = node._get_node(key, validate_access=False)

        try:
            full_key = node._get_full_key(key=key)
        except Exception as exc:
            # Since we are handling an exception, raising a different one here would
            # be misleading. Instead, we display it in the key.
            full_key = f"<unresolvable due to {type(exc).__name__}: {exc}>"

        object_type = OmegaConf.get_type(node)
        object_type_str = type_str(object_type)

        ref_type = get_ref_type(node)
        ref_type_str = type_str(ref_type)

    msg = string.Template(msg).safe_substitute(
        REF_TYPE=ref_type_str,
        OBJECT_TYPE=object_type_str,
        KEY=key,
        FULL_KEY=full_key,
        VALUE=value,
        VALUE_TYPE=type_str(type(value), include_module_name=True),
        KEY_TYPE=f"{type(key).__name__}",
    )

    if ref_type not in (None, Any):
        template = dedent(
            """\
            $MSG
                full_key: $FULL_KEY
                reference_type=$REF_TYPE
                object_type=$OBJECT_TYPE"""
        )
    else:
        template = dedent(
            """\
            $MSG
                full_key: $FULL_KEY
                object_type=$OBJECT_TYPE"""
        )
    s = string.Template(template=template)

    message = s.substitute(
        REF_TYPE=ref_type_str, OBJECT_TYPE=object_type_str, MSG=msg, FULL_KEY=full_key
    )
    exception_type = type(cause) if type_override is None else type_override
    if exception_type == TypeError:
        exception_type = ConfigTypeError
    elif exception_type == IndexError:
        exception_type = ConfigIndexError

    ex = exception_type(f"{message}")
    if issubclass(exception_type, OmegaConfBaseException):
        ex._initialized = True
        ex.msg = message
        ex.parent_node = node
        ex.child_node = child_node
        ex.key = key
        ex.full_key = full_key
        ex.value = value
        ex.object_type = object_type
        ex.object_type_str = object_type_str
        ex.ref_type = ref_type
        ex.ref_type_str = ref_type_str

    _raise(ex, cause) def get_value_kind(
    value: Any, strict_interpolation_validation: bool = False
) -> ValueKind:
    """
    Determine the kind of a value
    Examples:
    VALUE: "10", "20", True
    MANDATORY_MISSING: "???"
    INTERPOLATION: "${foo.bar}", "${foo.${bar}}", "${foo:bar}", "[${foo}, ${bar}]",
                   "ftp://${host}/path", "${foo:${bar}, [true], {'baz': ${baz}}}"

    :param value: Input to classify.
    :param strict_interpolation_validation: If `True`, then when `value` is a string
        containing "${", it is parsed to validate the interpolation syntax. If `False`,
        this parsing step is skipped: this is more efficient, but will not detect errors.
    """

    if _is_missing_value(value):
        return ValueKind.MANDATORY_MISSING

    value = _get_value(value)

    # We identify potential interpolations by the presence of "${" in the string.
    # Note that escaped interpolations (ex: "esc: \${bar}") are identified as
    # interpolations: this is intended, since they must be processed as interpolations
    # for the string to be properly un-escaped.
    # Keep in mind that invalid interpolations will only be detected when
    # `strict_interpolation_validation` is True.
    if isinstance(value, str) and "${" in value:
        if strict_interpolation_validation:
            # First try the cheap regex matching that detects common interpolations.
            if SIMPLE_INTERPOLATION_PATTERN.match(value) is None:
                # If no match, do the more expensive grammar parsing to detect errors.
                parse(value)
        return ValueKind.INTERPOLATION
    else:
        return ValueKind.VALUE def split_key(key: str) -> List[str]:
    """
    Split a full key path into its individual components.

    This is similar to `key.split(".")` but also works with the getitem syntax:
        "a.b"       -> ["a", "b"]
        "a[b]"      -> ["a, "b"]
        ".a.b[c].d" -> ["", "a", "b", "c", "d"]
        "[a].b"     -> ["a", "b"]
    """
    # Obtain the first part of the key (in docstring examples: a, a, .a, '')
    first = KEY_PATH_HEAD.match(key)
    assert first is not None
    first_stop = first.span()[1]

    # `tokens` will contain all elements composing the key.
    tokens = key[0:first_stop].split(".")

    # Optimization in case `key` has no other component: we are done.
    if first_stop == len(key):
        return tokens

    if key[first_stop] == "[" and not tokens[-1]:
        # This is a special case where the first key starts with brackets, e.g.
        # [a] or ..[a]. In that case there is an extra "" in `tokens` that we
        # need to get rid of:
        #   [a]   -> tokens = [""] but we would like []
        #   ..[a] -> tokens = ["", "", ""] but we would like ["", ""]
        tokens.pop()

    # Identify other key elements (in docstring examples: b, b, b/c/d, b)
    others = KEY_PATH_OTHER.findall(key[first_stop:])

    # There are two groups in the `KEY_PATH_OTHER` regex: one for keys starting
    # with a dot (.b, .d) and one for keys starting with a bracket ([b], [c]).
    # Only one group can be non-empty.
    tokens += [dot_key if dot_key else bracket_key for dot_key, bracket_key in others]

    return tokens def type_str(t: Any, include_module_name: bool = False) -> str:
    is_optional, t = _resolve_optional(t)
    if t is None:
        return type(t).__name__
    if t is Any:
        return "Any"
    if t is ...:
        return "..."

    if sys.version_info < (3, 7, 0):  # pragma: no cover
        # Python 3.6
        if hasattr(t, "__name__"):
            name = str(t.__name__)
        else:
            if t.__origin__ is not None:
                name = type_str(t.__origin__)
            else:
                name = str(t)
                if name.startswith("typing."):
                    name = name[len("typing.") :]
    else:  # pragma: no cover
        # Python >= 3.7
        if hasattr(t, "__name__"):
            name = str(t.__name__)
        else:
            if t._name is None:
                if t.__origin__ is not None:
                    name = type_str(
                        t.__origin__, include_module_name=include_module_name
                    )
            else:
                name = str(t._name)

    args = getattr(t, "__args__", None)
    if args is not None:
        args = ", ".join(
            [type_str(t, include_module_name=include_module_name) for t in t.__args__]
        )
        ret = f"{name}[{args}]"
    else:
        ret = name
    if include_module_name:
        if (
            hasattr(t, "__module__")
            and t.__module__ != "builtins"
            and t.__module__ != "typing"
            and not t.__module__.startswith("omegaconf.")
        ):
            module_prefix = t.__module__ + "."
        else:
            module_prefix = ""
        ret = module_prefix + ret
    if is_optional:
        return f"Optional[{ret}]"
    else:
        return ret def valid_value_annotation_type(type_: Any) -> bool:
    _, type_ = _resolve_optional(type_)
    return type_ is Any or is_primitive_type(type_) or is_structured_config(type_) class ConfigKeyError(OmegaConfBaseException, KeyError):
    """
    Thrown from DictConfig when a regular dict access would have caused a KeyError.
    """

    msg: str

    def __init__(self, msg: str) -> None:
        super().__init__(msg)
        self.msg = msg

    def __str__(self) -> str:
        """
        Workaround to nasty KeyError quirk: https://bugs.python.org/issue2651
        """
        return self.msg class ConfigTypeError(OmegaConfBaseException, TypeError):
    """
    Thrown from a config object when a regular access would have caused a TypeError.
    """ class InterpolationKeyError(InterpolationResolutionError):
    """
    Thrown when a node does not exist when resolving an interpolation.
    """ class InterpolationResolutionError(OmegaConfBaseException, ValueError):
    """
    Base class for exceptions raised when resolving an interpolation.
    """ class InterpolationToMissingValueError(InterpolationResolutionError):
    """
    Thrown when a node interpolation points to a node that is set to ???.
    """ class InterpolationValidationError(InterpolationResolutionError, ValidationError):
    """
    Thrown when the result of an interpolation fails the validation step.
    """ class MissingMandatoryValue(OmegaConfBaseException):
    """Thrown when a variable flagged with '???' value is accessed to
    indicate that the value was not set""" class UnsupportedInterpolationType(InterpolationResolutionError):
    """
    Thrown when an attempt to use an unregistered interpolation is made
    """ class ValidationError(OmegaConfBaseException, ValueError):
    """
    Thrown when a value fails validation
    """ def parse(
    value: str, parser_rule: str = "configValue", lexer_mode: str = "DEFAULT_MODE"
) -> ParserRuleContext:
    """
    Parse interpolated string `value` (and return the parse tree).
    """
    l_mode = getattr(OmegaConfGrammarLexer, lexer_mode)
    istream = InputStream(value)

    cached = getattr(_grammar_cache, "data", None)
    if cached is None:
        error_listener = OmegaConfErrorListener()
        lexer = OmegaConfGrammarLexer(istream)
        lexer.removeErrorListeners()
        lexer.addErrorListener(error_listener)
        lexer.mode(l_mode)
        token_stream = CommonTokenStream(lexer)
        parser = OmegaConfGrammarParser(token_stream)
        parser.removeErrorListeners()
        parser.addErrorListener(error_listener)

        # The two lines below could be enabled in the future if we decide to switch
        # to SLL prediction mode. Warning though, it has not been fully tested yet!
        # from antlr4 import PredictionMode
        # parser._interp.predictionMode = PredictionMode.SLL

        # Note that although the input stream `istream` is implicitly cached within
        # the lexer, it will be replaced by a new input next time the lexer is re-used.
        _grammar_cache.data = lexer, token_stream, parser

    else:
        lexer, token_stream, parser = cached
        # Replace the old input stream with the new one.
        lexer.inputStream = istream
        # Initialize the lexer / token stream / parser to process the new input.
        lexer.mode(l_mode)
        token_stream.setTokenSource(lexer)
        parser.reset()

    try:
        return getattr(parser, parser_rule)()
    except Exception as exc:
        if type(exc) is Exception and str(exc) == "Empty Stack":
            # This exception is raised by antlr when trying to pop a mode while
            # no mode has been pushed. We convert it into an `GrammarParseError`
            # to facilitate exception handling from the caller.
            raise GrammarParseError("Empty Stack")
        else:
            raise class GrammarVisitor(OmegaConfGrammarParserVisitor):
    def __init__(
        self,
        node_interpolation_callback: Callable[
            [str, Optional[Set[int]]],
            Optional["Node"],
        ],
        resolver_interpolation_callback: Callable[..., Any],
        memo: Optional[Set[int]],
        **kw: Dict[Any, Any],
    ):
        """
        Constructor.

        :param node_interpolation_callback: Callback function that is called when
            needing to resolve a node interpolation. This function should take a single
            string input which is the key's dot path (ex: `"foo.bar"`).

        :param resolver_interpolation_callback: Callback function that is called when
            needing to resolve a resolver interpolation. This function should accept
            three keyword arguments: `name` (str, the name of the resolver),
            `args` (tuple, the inputs to the resolver), and `args_str` (tuple,
            the string representation of the inputs to the resolver).

        :param kw: Additional keyword arguments to be forwarded to parent class.
        """
        super().__init__(**kw)
        self.node_interpolation_callback = node_interpolation_callback
        self.resolver_interpolation_callback = resolver_interpolation_callback
        self.memo = memo

    def aggregateResult(self, aggregate: List[Any], nextResult: Any) -> List[Any]:
        raise NotImplementedError

    def defaultResult(self) -> List[Any]:
        # Raising an exception because not currently used (like `aggregateResult()`).
        raise NotImplementedError

    def visitConfigKey(self, ctx: OmegaConfGrammarParser.ConfigKeyContext) -> str:
        from ._utils import _get_value

        # interpolation | ID | INTER_KEY
        assert ctx.getChildCount() == 1
        child = ctx.getChild(0)
        if isinstance(child, OmegaConfGrammarParser.InterpolationContext):
            res = _get_value(self.visitInterpolation(child))
            if not isinstance(res, str):
                raise InterpolationResolutionError(
                    f"The following interpolation is used to denote a config key and "
                    f"thus should return a string, but instead returned `{res}` of "
                    f"type `{type(res)}`: {ctx.getChild(0).getText()}"
                )
            return res
        else:
            assert isinstance(child, TerminalNode) and isinstance(
                child.symbol.text, str
            )
            return child.symbol.text

    def visitConfigValue(self, ctx: OmegaConfGrammarParser.ConfigValueContext) -> Any:
        # text EOF
        assert ctx.getChildCount() == 2
        return self.visit(ctx.getChild(0))

    def visitDictKey(self, ctx: OmegaConfGrammarParser.DictKeyContext) -> Any:
        return self._createPrimitive(ctx)

    def visitDictContainer(
        self, ctx: OmegaConfGrammarParser.DictContainerContext
    ) -> Dict[Any, Any]:
        # BRACE_OPEN (dictKeyValuePair (COMMA dictKeyValuePair)*)? BRACE_CLOSE
        assert ctx.getChildCount() >= 2
        return dict(
            self.visitDictKeyValuePair(ctx.getChild(i))
            for i in range(1, ctx.getChildCount() - 1, 2)
        )

    def visitElement(self, ctx: OmegaConfGrammarParser.ElementContext) -> Any:
        # primitive | quotedValue | listContainer | dictContainer
        assert ctx.getChildCount() == 1
        return self.visit(ctx.getChild(0))

    def visitInterpolation(
        self, ctx: OmegaConfGrammarParser.InterpolationContext
    ) -> Any:
        assert ctx.getChildCount() == 1  # interpolationNode | interpolationResolver
        return self.visit(ctx.getChild(0))

    def visitInterpolationNode(
        self, ctx: OmegaConfGrammarParser.InterpolationNodeContext
    ) -> Optional["Node"]:
        # INTER_OPEN
        # DOT*                                                     // relative interpolation?
        # (configKey | BRACKET_OPEN configKey BRACKET_CLOSE)       // foo, [foo]
        # (DOT configKey | BRACKET_OPEN configKey BRACKET_CLOSE)*  // .foo, [foo], .foo[bar], [foo].bar[baz]
        # INTER_CLOSE;

        assert ctx.getChildCount() >= 3

        inter_key_tokens = []  # parsed elements of the dot path
        for child in ctx.getChildren():
            if isinstance(child, TerminalNode):
                s = child.symbol
                if s.type in [
                    OmegaConfGrammarLexer.DOT,
                    OmegaConfGrammarLexer.BRACKET_OPEN,
                    OmegaConfGrammarLexer.BRACKET_CLOSE,
                ]:
                    inter_key_tokens.append(s.text)
                else:
                    assert s.type in (
                        OmegaConfGrammarLexer.INTER_OPEN,
                        OmegaConfGrammarLexer.INTER_CLOSE,
                    )
            else:
                assert isinstance(child, OmegaConfGrammarParser.ConfigKeyContext)
                inter_key_tokens.append(self.visitConfigKey(child))

        inter_key = "".join(inter_key_tokens)
        return self.node_interpolation_callback(inter_key, self.memo)

    def visitInterpolationResolver(
        self, ctx: OmegaConfGrammarParser.InterpolationResolverContext
    ) -> Any:

        # INTER_OPEN resolverName COLON sequence? BRACE_CLOSE
        assert 4 <= ctx.getChildCount() <= 5

        resolver_name = self.visit(ctx.getChild(1))
        maybe_seq = ctx.getChild(3)
        args = []
        args_str = []
        if isinstance(maybe_seq, TerminalNode):  # means there are no args
            assert maybe_seq.symbol.type == OmegaConfGrammarLexer.BRACE_CLOSE
        else:
            assert isinstance(maybe_seq, OmegaConfGrammarParser.SequenceContext)
            for val, txt in self.visitSequence(maybe_seq):
                args.append(val)
                args_str.append(txt)

        return self.resolver_interpolation_callback(
            name=resolver_name,
            args=tuple(args),
            args_str=tuple(args_str),
        )

    def visitDictKeyValuePair(
        self, ctx: OmegaConfGrammarParser.DictKeyValuePairContext
    ) -> Tuple[Any, Any]:
        from ._utils import _get_value

        assert ctx.getChildCount() == 3  # dictKey COLON element
        key = self.visit(ctx.getChild(0))
        colon = ctx.getChild(1)
        assert (
            isinstance(colon, TerminalNode)
            and colon.symbol.type == OmegaConfGrammarLexer.COLON
        )
        value = _get_value(self.visitElement(ctx.getChild(2)))
        return key, value

    def visitListContainer(
        self, ctx: OmegaConfGrammarParser.ListContainerContext
    ) -> List[Any]:
        # BRACKET_OPEN sequence? BRACKET_CLOSE;
        assert ctx.getChildCount() in (2, 3)
        if ctx.getChildCount() == 2:
            return []
        sequence = ctx.getChild(1)
        assert isinstance(sequence, OmegaConfGrammarParser.SequenceContext)
        return list(val for val, _ in self.visitSequence(sequence))  # ignore raw text

    def visitPrimitive(self, ctx: OmegaConfGrammarParser.PrimitiveContext) -> Any:
        return self._createPrimitive(ctx)

    def visitQuotedValue(self, ctx: OmegaConfGrammarParser.QuotedValueContext) -> str:
        # (QUOTE_OPEN_SINGLE | QUOTE_OPEN_DOUBLE) text? MATCHING_QUOTE_CLOSE
        n = ctx.getChildCount()
        assert n in [2, 3]
        return str(self.visit(ctx.getChild(1))) if n == 3 else ""

    def visitResolverName(self, ctx: OmegaConfGrammarParser.ResolverNameContext) -> str:
        from ._utils import _get_value

        # (interpolation | ID) (DOT (interpolation | ID))*
        assert ctx.getChildCount() >= 1
        items = []
        for child in list(ctx.getChildren())[::2]:
            if isinstance(child, TerminalNode):
                assert child.symbol.type == OmegaConfGrammarLexer.ID
                items.append(child.symbol.text)
            else:
                assert isinstance(child, OmegaConfGrammarParser.InterpolationContext)
                item = _get_value(self.visitInterpolation(child))
                if not isinstance(item, str):
                    raise InterpolationResolutionError(
                        f"The name of a resolver must be a string, but the interpolation "
                        f"{child.getText()} resolved to `{item}` which is of type "
                        f"{type(item)}"
                    )
                items.append(item)
        return ".".join(items)

    def visitSequence(
        self, ctx: OmegaConfGrammarParser.SequenceContext
    ) -> Generator[Any, None, None]:
        from ._utils import _get_value

        # (element (COMMA element?)*) | (COMMA element?)+
        assert ctx.getChildCount() >= 1

        # DEPRECATED: remove in 2.2 (revert #571)
        def empty_str_warning() -> None:
            txt = ctx.getText()
            warnings.warn(
                f"In the sequence `{txt}` some elements are missing: please replace "
                f"them with empty quoted strings. "
                f"See https://github.com/omry/omegaconf/issues/572 for details.",
                category=UserWarning,
            )

        is_previous_comma = True  # whether previous child was a comma (init to True)
        for child in ctx.getChildren():
            if isinstance(child, OmegaConfGrammarParser.ElementContext):
                # Also preserve the original text representation of `child` so
                # as to allow backward compatibility with old resolvers (registered
                # with `legacy_register_resolver()`). Note that we cannot just cast
                # the value to string later as for instance `null` would become "None".
                yield _get_value(self.visitElement(child)), child.getText()
                is_previous_comma = False
            else:
                assert (
                    isinstance(child, TerminalNode)
                    and child.symbol.type == OmegaConfGrammarLexer.COMMA
                )
                if is_previous_comma:
                    empty_str_warning()
                    yield "", ""
                else:
                    is_previous_comma = True
        if is_previous_comma:
            # Trailing comma.
            empty_str_warning()
            yield "", ""

    def visitSingleElement(
        self, ctx: OmegaConfGrammarParser.SingleElementContext
    ) -> Any:
        # element EOF
        assert ctx.getChildCount() == 2
        return self.visit(ctx.getChild(0))

    def visitText(self, ctx: OmegaConfGrammarParser.TextContext) -> Any:
        # (interpolation | ANY_STR | ESC | ESC_INTER | TOP_ESC | QUOTED_ESC)+

        # Single interpolation? If yes, return its resolved value "as is".
        if ctx.getChildCount() == 1:
            c = ctx.getChild(0)
            if isinstance(c, OmegaConfGrammarParser.InterpolationContext):
                return self.visitInterpolation(c)

        # Otherwise, concatenate string representations together.
        return self._unescape(list(ctx.getChildren()))

    def _createPrimitive(
        self,
        ctx: Union[
            OmegaConfGrammarParser.PrimitiveContext,
            OmegaConfGrammarParser.DictKeyContext,
        ],
    ) -> Any:
        # (ID | NULL | INT | FLOAT | BOOL | UNQUOTED_CHAR | COLON | ESC | WS | interpolation)+
        if ctx.getChildCount() == 1:
            child = ctx.getChild(0)
            if isinstance(child, OmegaConfGrammarParser.InterpolationContext):
                return self.visitInterpolation(child)
            assert isinstance(child, TerminalNode)
            symbol = child.symbol
            # Parse primitive types.
            if symbol.type in (
                OmegaConfGrammarLexer.ID,
                OmegaConfGrammarLexer.UNQUOTED_CHAR,
                OmegaConfGrammarLexer.COLON,
            ):
                return symbol.text
            elif symbol.type == OmegaConfGrammarLexer.NULL:
                return None
            elif symbol.type == OmegaConfGrammarLexer.INT:
                return int(symbol.text)
            elif symbol.type == OmegaConfGrammarLexer.FLOAT:
                return float(symbol.text)
            elif symbol.type == OmegaConfGrammarLexer.BOOL:
                return symbol.text.lower() == "true"
            elif symbol.type == OmegaConfGrammarLexer.ESC:
                return self._unescape([child])
            elif symbol.type == OmegaConfGrammarLexer.WS:  # pragma: no cover
                # A single WS should have been "consumed" by another token.
                raise AssertionError("WS should never be reached")
            assert False, symbol.type
        # Concatenation of multiple items ==> un-escape the concatenation.
        return self._unescape(list(ctx.getChildren()))

    def _unescape(
        self,
        seq: List[Union[TerminalNode, OmegaConfGrammarParser.InterpolationContext]],
    ) -> str:
        """
        Concatenate all symbols / interpolations in `seq`, unescaping symbols as needed.

        Interpolations are resolved and cast to string *WITHOUT* escaping their result
        (it is assumed that whatever escaping is required was already handled during the
        resolving of the interpolation).
        """
        chrs = []
        for node, next_node in zip_longest(seq, seq[1:]):
            if isinstance(node, TerminalNode):
                s = node.symbol
                if s.type == OmegaConfGrammarLexer.ESC_INTER:
                    # `ESC_INTER` is of the form `\\...\${`: the formula below computes
                    # the number of characters to keep at the end of the string to remove
                    # the correct number of backslashes.
                    text = s.text[-(len(s.text) // 2 + 1) :]
                elif (
                    # Character sequence identified as requiring un-escaping.
                    s.type == OmegaConfGrammarLexer.ESC
                    or (
                        # At top level, we need to un-escape backslashes that precede
                        # an interpolation.
                        s.type == OmegaConfGrammarLexer.TOP_ESC
                        and isinstance(
                            next_node, OmegaConfGrammarParser.InterpolationContext
                        )
                    )
                    or (
                        # In a quoted sring, we need to un-escape backslashes that
                        # either end the string, or are followed by an interpolation.
                        s.type == OmegaConfGrammarLexer.QUOTED_ESC
                        and (
                            next_node is None
                            or isinstance(
                                next_node, OmegaConfGrammarParser.InterpolationContext
                            )
                        )
                    )
                ):
                    text = s.text[1::2]  # un-escape the sequence
                else:
                    text = s.text  # keep the original text
            else:
                assert isinstance(node, OmegaConfGrammarParser.InterpolationContext)
                text = str(self.visitInterpolation(node))
            chrs.append(text)

        return "".join(chrs)
import copy
import sys
from abc import ABC, abstractmethod
from collections import defaultdict
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type, Union
from antlr4 import ParserRuleContext
from ._utils import (
    _DEFAULT_MARKER_,
    ValueKind,
    _get_value,
    _is_missing_value,
    format_and_raise,
    get_value_kind,
    split_key,
    type_str,
    valid_value_annotation_type,
)
from .errors import (
    ConfigKeyError,
    ConfigTypeError,
    InterpolationKeyError,
    InterpolationResolutionError,
    InterpolationToMissingValueError,
    InterpolationValidationError,
    MissingMandatoryValue,
    UnsupportedInterpolationType,
    ValidationError,
)
from .grammar.gen.OmegaConfGrammarParser import OmegaConfGrammarParser
from .grammar_parser import parse
from .grammar_visitor import GrammarVisitor
        from .omegaconf import _select_one
        from .nodes import AnyNode, InterpolationResultNode, ValueNode
        from omegaconf import OmegaConf
        from .dictconfig import DictConfig
        from .listconfig import ListConfig
        from .dictconfig import DictConfig
        from .listconfig import ListConfig
        return self._validate_and_convert_interpolation_result(
            parent=parent,
            value=value,
            key=key,
            resolved=resolved,
            throw_on_resolution_failure=throw_on_resolution_failure,
        )

    def _validate_and_convert_interpolation_result(
        self,
        parent: Optional["Container"],
        value: "Node",
        key: Any,
        resolved: Any,
        throw_on_resolution_failure: bool,
    ) -> Optional["Node"]:

        # If the output is not a Node already (e.g., because it is the output of a
        # custom resolver), then we will need to wrap it within a Node.
        must_wrap = not isinstance(resolved, Node)

        # If the node is typed, validate (and possibly convert) the result.
        if isinstance(value, ValueNode) and not isinstance(value, AnyNode):
            res_value = _get_value(resolved)
            try:
                conv_value = value.validate_and_convert(res_value)
            except ValidationError as e:
                if throw_on_resolution_failure:
                    self._format_and_raise(
                        key=key,
                        value=res_value,
                        cause=e,
                        msg=f"While dereferencing interpolation '{value}': {e}",
                        type_override=InterpolationValidationError,
                    )
                return None

            # If the converted value is of the same type, it means that no conversion
            # was actually needed. As a result, we can keep the original `resolved`
            # (and otherwise, the converted value must be wrapped into a new node).
            if type(conv_value) != type(res_value):
                must_wrap = True
                resolved = conv_value

        if must_wrap:
            return InterpolationResultNode(value=resolved, key=key, parent=parent)
        else:
            assert isinstance(resolved, Node)
            return resolved

    def _validate_not_dereferencing_to_parent(self, node: Node, target: Node) -> None:
        parent: Optional[Node] = node
        while parent is not None:
            if parent is target:
                raise InterpolationResolutionError(
                    "Interpolation to parent node detected"
                )
            parent = parent._get_parent()

    def _resolve_node_interpolation(
        self, inter_key: str, memo: Optional[Set[int]]
    ) -> "Node":
        """A node interpolation is of the form `${foo.bar}`"""
        try:
            root_node, inter_key = self._resolve_key_and_root(inter_key)
        except ConfigKeyError as exc:
            raise InterpolationKeyError(
                f"ConfigKeyError while resolving interpolation: {exc}"
            ).with_traceback(sys.exc_info()[2])

        try:
            parent, last_key, value = root_node._select_impl(
                inter_key,
                throw_on_missing=True,
                throw_on_resolution_failure=True,
                memo=memo,
            )
        except MissingMandatoryValue as exc:
            raise InterpolationToMissingValueError(
                f"MissingMandatoryValue while resolving interpolation: {exc}"
            ).with_traceback(sys.exc_info()[2])

        if parent is None or value is None:
            raise InterpolationKeyError(f"Interpolation key '{inter_key}' not found")
        else:
            self._validate_not_dereferencing_to_parent(node=self, target=value)
            return value

    def _evaluate_custom_resolver(
        self,
        key: Any,
        node: Node,
        inter_type: str,
        inter_args: Tuple[Any, ...],
        inter_args_str: Tuple[str, ...],
    ) -> Any:

        resolver = OmegaConf._get_resolver(inter_type)
        if resolver is not None:
            root_node = self._get_root()
            return resolver(
                root_node,
                self,
                node,
                inter_args,
                inter_args_str,
            )
        else:
            raise UnsupportedInterpolationType(
                f"Unsupported interpolation type {inter_type}"
            )

    def _maybe_resolve_interpolation(
        self,
        parent: Optional["Container"],
        key: Any,
        value: Node,
        throw_on_resolution_failure: bool,
        memo: Optional[Set[int]] = None,
    ) -> Optional[Node]:

        value_kind = get_value_kind(value)