#!/usr/bin/env python3
# --------------------( LICENSE                            )--------------------
# Copyright (c) 2014-2025 Beartype authors.
# See "LICENSE" for further details.

'''
Beartype **type-checking expression snippet classes** (i.e., low-level classes
dynamically and efficiently generating substrings intended to be interpolated
into boolean expressions type-checking arbitrary objects against various type
hints).

This private submodule is *not* intended for importation by downstream callers.
'''

# ....................{ IMPORTS                            }....................
from beartype._check.checkmagic import VAR_NAME_PITH_PREFIX

# ....................{ PRIVATE ~ subclasses               }....................
class _HintIndexToHintPlaceholder(dict):
    '''
    **Local type-checking placeholder cache** (i.e., dictionary mapping from the
    1-based index uniquely identifying each **pith** (i.e., current parameter or
    return value *or* item contained in the current parameter or return value
    type-checked by the current call in the body of a runtime type-checker
    dynamically generated by :mod:`beartype`) to the corresponding type-checking
    placeholder substring).

    Design
    ------
    Each value mapped by this cache is a **type-checking placeholder substring**
    to be globally replaced in the **type-checking wrapper function code
    snippet** (i.e., the ``func_wrapper_code`` local defined by the
    :func:`beartype._check.code.codemake.make_check_expr` factory) by a Python
    code snippet type-checking the **current pith expression** (i.e., the
    ``pith_var_name`` local) against the **currently visited type hint** (i.e.,
    the :attr:`hint` instance variable).

    Each such substring provides indirection enabling the currently visited
    parent hint to defer and delegate the generation of code type-checking each
    child type hint of this parent hint to the subsequent time at which that
    child type hint is visited by that BFS.

    Each such substring is intentionally prefixed and suffixed by characters
    that:

    * Are intentionally invalid as Python code, guaranteeing that the top-level
      call to the func:`exec` builtin subsequently performed by the
      :func:`beartype.beartype` decorator will raise a :exc:`SyntaxError`
      exception if the caller failed to replace *all* placeholder substrings.
    * Protect the :attr:`pith_var_name_index` integer embedded in this substring
      against ambiguous global replacements of longer such integers containing
      this integer (e.g., the integer 1, contained in the longer integers 11 and
      21). If this integer were *not* protected in this manner, then the first
      substring ``"0"`` would ambiguously overlap with the subsequent
      substring ``"10"``, which would then produce catastrophically erroneous
      and undebuggable Python code.

    Example
    -------
    For example, the :func:`beartype._check.code.codemake.make_check_expr`
    factory might generate intermediary code resembling the following on
    visiting the :obj:`typing.Union` parent type hint of a subscripted type hint
    ``typing.Union[int, str]`` *before* visiting either the :class:`int` or
    :class:`str` child type hints of that parent type hint:

    .. code-block:: python

       if not (
           @{0}! or
           @{1}!
       ):
           raise get_func_pith_violation(
               func=__beartype_func,
               pith_name=$%PITH_ROOT_NAME/~,
               pith_value=__beartype_pith_root,
           )

    Note the unique substrings ``"@{0}!"`` and ``"@{1}!"`` in that code, which
    that factory iteratively replaces with code type-checking each of the child
    type hints (e.g., :class:`int`, :class:`str`) subscripting that
    :obj:`typing.Union` parent type hint. The final code memoized by that
    factory might then resemble:

    .. code-block:: python

       if not (
           isinstance(__beartype_pith_root, int) or
           isinstance(__beartype_pith_root, str)
       ):
           raise get_func_pith_violation(
               func=__beartype_func,
               pith_name=$%PITH_ROOT_NAME/~,
               pith_value=__beartype_pith_root,
           )

    See Also
    --------
    :data:`.PITH_INDEX_TO_HINT_PLACEHOLDER`
        Singleton instance of this dictionary subclass.
    '''

    # ....................{ DUNDERS                        }....................
    def __missing__(self, pith_index: int) -> str:
        '''
        Dunder method explicitly called by the superclass
        :meth:`dict.__getitem__` method implicitly called on the first ``[``-
        and ``]``-delimited attempt to access a local type-checking placeholder
        uniquely identified by the passed 1-based index.

        Parameters
        ----------
        pith_index : int
            1-based index suffixing the local type-checking placeholder to be
            created, cached, and returned.

        Returns
        -------
        str
            Prospective type-checking placeholder of this local pith variable.

        Raises
        ------
        AssertionError
            If either:

            * ``pith_index`` is *not* an integer.
            * ``pith_index`` is a **negative integer** (i.e., less than 0).
        '''
        assert isinstance(pith_index, int), f'{repr(pith_index)} not integer.'
        assert pith_index >= 0, f'{pith_index} < 0.'
        # print(f'Generating indentation level {indent_level}...')

        # Placeholder substring to be globally replaced by code type-checking
        # the current pith against this hint.
        hint_placeholder = (
            f'{_HINT_PLACEHOLDER_PREFIX}{pith_index}'
            f'{_HINT_PLACEHOLDER_SUFFIX}'
        )

        # Cache this placeholder substring.
        self[pith_index] = hint_placeholder

        # Return this placeholder substring.
        return hint_placeholder


class _PithIndexToVarName(dict):
    '''
    **Local pith variable name cache** (i.e., dictionary mapping from the
    1-based index uniquely identifying each **pith** (i.e., current parameter or
    return value *or* item contained in the current parameter or return value
    type-checked by the current call in the body of a runtime type-checker
    dynamically generated by :mod:`beartype`) to the corresponding name of a
    prospective local variable assigned that value in that body).

    See Also
    --------
    :data:`.PITH_INDEX_TO_VAR_NAME`
        Singleton instance of this dictionary subclass.
    '''

    # ....................{ DUNDERS                        }....................
    def __missing__(self, pith_index: int) -> str:
        '''
        Dunder method explicitly called by the superclass
        :meth:`dict.__getitem__` method implicitly called on the first ``[``-
        and ``]``-delimited attempt to access a local pith variable name
        uniquely identified by the passed 1-based index.

        Parameters
        ----------
        pith_index : int
            1-based index suffixing the local pith variable name to be created,
            cached, and returned.

        Returns
        -------
        str
            Prospective name of this local pith variable.

        Raises
        ------
        AssertionError
            If either:

            * ``pith_index`` is *not* an integer.
            * ``pith_index`` is a **negative integer** (i.e., less than 0).
        '''
        assert isinstance(pith_index, int), f'{repr(pith_index)} not integer.'
        assert pith_index >= 0, f'{pith_index} < 0.'
        # print(f'Generating indentation level {indent_level}...')

        # Prospective name of this local pith variable.
        pith_var_name = f'{VAR_NAME_PITH_PREFIX}{pith_index}'

        # Cache this name.
        self[pith_index] = pith_var_name

        # Return this name.
        return pith_var_name

# ....................{ MAPPINGS                           }....................
HINT_INDEX_TO_HINT_PLACEHOLDER = _HintIndexToHintPlaceholder()
'''
**Local type-checking placeholder cache singleton** (i.e., global dictionary
efficiently mapping from the 1-based index uniquely identifying each pith to the
corresponding type-checking placeholder substring).

Examples
--------
.. code-block:: pycon

   >>> from beartype._check.code.snip.codesnipcls import (
   ...     HINT_INDEX_TO_HINT_PLACEHOLDER)
   >>> HINT_INDEX_TO_HINT_PLACEHOLDER[1]
   '@[1)!'
   >>> HINT_INDEX_TO_HINT_PLACEHOLDER[2]
   '@[2)!'
'''


PITH_INDEX_TO_VAR_NAME = _PithIndexToVarName()
'''
**Local pith variable name cache singleton** (i.e., global dictionary
efficiently mapping from the 1-based index uniquely identifying each pith to the
name of a prospective local variable assigned that value in that body).

Examples
--------
.. code-block:: pycon

   >>> from beartype._check.code.snip.codesnipcls import PITH_INDEX_TO_VAR_NAME
   >>> PITH_INDEX_TO_VAR_NAME[1]
   '__beartype_pith_1'
   >>> PITH_INDEX_TO_VAR_NAME[2]
   '__beartype_pith_2'
'''

# ....................{ PRIVATE ~ constants                }....................
_HINT_PLACEHOLDER_PREFIX = '@['
'''
Prefix of each **placeholder hint child type-checking substring** (i.e.,
placeholder to be globally replaced by a Python code snippet type-checking the
current pith expression against the currently iterated child hint of the
currently visited parent hint).
'''


_HINT_PLACEHOLDER_SUFFIX = ')!'
'''
Suffix of each **placeholder hint child type-checking substring** (i.e.,
placeholder to be globally replaced by a Python code snippet type-checking the
current pith expression against the currently iterated child hint of the
currently visited parent hint).
'''
