<commit_before>from . import r
from . import tree
from .. import const

# CONST
# {
globals().update({
    n: r(v)[0] for n, v in
    dict(
        ST_GROUP = '(_)'

      , ST_TUPLE_S = '_,'
      , ST_TUPLE   = '_, _'
      , ST_CALL    = '_ _'

      , ST_ARG_KW       = '_: _'
      , ST_ARG_VAR      = '*_'
      , ST_ARG_VAR_KW   = '**_'
      , ST_ARG_VAR_C    = '(*)'
      , ST_ARG_VAR_KW_C = '(**)'

      , ST_IMPORT     = 'import'
      , ST_IMPORT_REL = '_ _'
      , ST_IMPORT_SEP = '_._'

      , ST_ASSIGN_ATTR = '_._'
      , ST_ASSIGN_ITEM = '_ !! _'
    ).items()
})
# }

# MISC
# {
# Drop outermost parentheses from a syntactic construct `f`.
unwrap  = lambda f:    tree.matchR(f, ST_GROUP, lambda f, q: q.pop(-1))[-1]

# Recursively match `f` with a binary operator `p`, returning all the operands.
uncurry = lambda f, p: tree.matchR(f, p,        lambda f, q: q.pop(-2))[::-1]

# Same as `assert not ...`, but without `AssertionError`.
def ERROR(pred, msg):

    if pred:

        raise Exception(msg)
# }


def assignment(var, expr):

    if tree.matchQ(expr, ST_IMPORT):

        var    = tree.matchA(var, ST_IMPORT_REL) or [var]
        parent = var[0].count('.') if len(var) > 1 else 0
      # ERROR(parent != (len(var[0]) if len(var) > 1 else 0), ...)

        var = var[len(var) > 1]
        var, *args = uncurry(unwrap(var), ST_IMPORT_SEP)

        ERROR(not isinstance(var, tree.Link), const.ERR.NONCONST_IMPORT)
        return '.'.join([var] + args), const.AT.IMPORT, var, parent

    # Other assignment types do not depend on right-hand statement value.
    return (expr,) + assignment_target(var)


def assignment_target(var):

    # Attempt to do iterable unpacking first.
    var  = unwrap(var)
    pack = uncurry(var, ST_TUPLE)
    pack = pack if len(pack) > 1 else tree.matchA(var, ST_TUPLE_S)

    if pack:

        # Allow one starred argument that is similar to `varargs`.
        star = [i for i, q in enumerate(pack) if tree.matchQ(q, ST_ARG_VAR)] or [-1]
        ERROR(len(star) < 2, const.ERR.MULTIPLE_VARARGS)
        ERROR(star[0] > 255, const.ERR.TOO_MANY_ITEMS_BEFORE_STAR)

        if star[0] >= 0:<commit_msg>Fix tuple unpacking.<commit_after>from . import r
from . import tree
from .. import const

# CONST
# {
globals().update({
    n: r(v)[0] for n, v in
    dict(
        ST_GROUP = '(_)'

      , ST_TUPLE_S = '_,'
      , ST_TUPLE   = '_, _'
      , ST_CALL    = '_ _'

      , ST_ARG_KW       = '_: _'
      , ST_ARG_VAR      = '*_'
      , ST_ARG_VAR_KW   = '**_'
      , ST_ARG_VAR_C    = '(*)'
      , ST_ARG_VAR_KW_C = '(**)'

      , ST_IMPORT     = 'import'
      , ST_IMPORT_REL = '_ _'
      , ST_IMPORT_SEP = '_._'

      , ST_ASSIGN_ATTR = '_._'
      , ST_ASSIGN_ITEM = '_ !! _'
    ).items()
})
# }

# MISC
# {
# Drop outermost parentheses from a syntactic construct `f`.
unwrap  = lambda f:    tree.matchR(f, ST_GROUP, lambda f, q: q.pop(-1))[-1]

# Recursively match `f` with a binary operator `p`, returning all the operands.
uncurry = lambda f, p: tree.matchR(f, p,        lambda f, q: q.pop(-2))[::-1]

# Same as `assert not ...`, but without `AssertionError`.
def ERROR(pred, msg):

    if pred:

        raise Exception(msg)
# }


def assignment(var, expr):

    if tree.matchQ(expr, ST_IMPORT):

        var    = tree.matchA(var, ST_IMPORT_REL) or [var]
        parent = var[0].count('.') if len(var) > 1 else 0
      # ERROR(parent != (len(var[0]) if len(var) > 1 else 0), ...)

        var = var[len(var) > 1]
        var, *args = uncurry(unwrap(var), ST_IMPORT_SEP)

        ERROR(not isinstance(var, tree.Link), const.ERR.NONCONST_IMPORT)
        return '.'.join([var] + args), const.AT.IMPORT, var, parent

    # Other assignment types do not depend on right-hand statement value.
    return (expr,) + assignment_target(var)


def assignment_target(var):

    # Attempt to do iterable unpacking first.
    var  = unwrap(var)
    pack = uncurry(var, ST_TUPLE)
    pack = pack if len(pack) > 1 else tree.matchA(var, ST_TUPLE_S)

    if pack:

        # Allow one starred argument that is similar to `varargs`.
        star = [i for i, q in enumerate(pack) if tree.matchQ(q, ST_ARG_VAR)] or [-1]
        ERROR(len(star) > 1, const.ERR.MULTIPLE_VARARGS)
        ERROR(star[0] > 255, const.ERR.TOO_MANY_ITEMS_BEFORE_STAR)

        if star[0] >= 0:
{'fraction_non_alphanumeric': 0.1496645847790886, 'fraction_numerical': 0.009715475364330326, 'mean_word_length': 2.420886075949367, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2078017', 'n_tokens_mistral': 1675, 'n_tokens_neox': 1613, 'n_words': 393}