    PYPARSING_PACKAGE = "cPyparsing"
    PYPARSING_INFO = "Cython cPyparsing v" + __version__
        PYPARSING_PACKAGE = "pyparsing"
        PYPARSING_INFO = "Python pyparsing v" + __version__
        PYPARSING_PACKAGE = "cPyparsing"
        PYPARSING_INFO = None
    MODERN_PYPARSING = True
    MODERN_PYPARSING = False
USE_COMPUTATION_GRAPH = (
    not MODERN_PYPARSING  # not yet supported
    and not PYPY  # experimentally determined
)
    def fast_repr(cls):
def set_fast_pyparsing_reprs():
def unset_fast_pyparsing_reprs():
def add_timing_to_method(cls, method_name, method):
def collect_timing_info():
def print_timing_info():
class _timing_sentinel(object): def fixpath(path):
def str_to_bool(boolstr, default=False):
WINDOWS = os.name == "nt"
PYPY = platform.python_implementation() == "PyPy"
CPYTHON = platform.python_implementation() == "CPython"
PY32 = sys.version_info >= (3, 2)
PY33 = sys.version_info >= (3, 3)
PY34 = sys.version_info >= (3, 4)
PY35 = sys.version_info >= (3, 5)
PY36 = sys.version_info >= (3, 6)
PY38 = sys.version_info >= (3, 8)
PY310 = sys.version_info >= (3, 10)
IPY = (
    ((PY2 and not PY26) or PY35)
    and not (PYPY and WINDOWS)
    # necessary until jupyter-console fixes https://github.com/jupyter/jupyter_console/issues/245
    and not PY310
)
MYPY = (
    PY34
    and not WINDOWS
    and not PYPY
)
PURE_PYTHON = str_to_bool(os.environ.get(pure_python_env_var, "")) def checksum(data):
    """Compute a checksum of the given data.
    Used for computing __coconut_hash__."""
    return crc32(data) & 0xffffffff  # necessary for cross-compatibility class CoconutException(Exception):
    """Base Coconut exception."""

    def __init__(self, message, item=None, extra=None):
        """Creates the Coconut exception."""
        self.args = (message, item, extra)

    def message(self, message, item, extra):
        """Uses arguments to create the message."""
        if item is not None:
            message += ": " + ascii(item)
        if extra is not None:
            message += " (" + str(extra) + ")"
        return message

    def syntax_err(self):
        """Converts to a SyntaxError."""
        return SyntaxError(str(self))

    def __str__(self):
        """Get the exception message."""
        return self.message(*self.args)

    def __reduce__(self):
        """Get pickling information."""
        return (self.__class__, self.args)

    def __repr__(self):
        """Get a representation of the exception."""
        return self.__class__.__name__ + "(" + ", ".join(
            repr(arg) for arg in self.args if arg is not None
        ) + ")" class CoconutSyntaxError(CoconutException):
    """Coconut SyntaxError."""

    def __init__(self, message, source=None, point=None, ln=None, extra=None):
        """Creates the Coconut SyntaxError."""
        self.args = (message, source, point, ln, extra)

    def message(self, message, source, point, ln, extra=None):
        """Creates a SyntaxError-like message."""
        if message is None:
            message = "parsing failed"
        if extra is not None:
            message += " (" + str(extra) + ")"
        if ln is not None:
            message += " (line " + str(ln) + ")"
        if source:
            if point is None:
                message += "\n" + " " * taberrfmt + clean(source)
            else:
                part = clean(source.splitlines()[lineno(point, source) - 1], False).lstrip()
                point -= len(source) - len(part)  # adjust all points based on lstrip
                part = part.rstrip()  # adjust only points that are too large based on rstrip
                message += "\n" + " " * taberrfmt + part
                if point > 0:
                    if point >= len(part):
                        point = len(part) - 1
                    message += "\n" + " " * (taberrfmt + point) + "^"
        return message

    def syntax_err(self):
        """Creates a SyntaxError."""
        args = self.args[:2] + (None, None) + self.args[4:]
        err = SyntaxError(self.message(*args))
        err.offset = args[2]
        err.lineno = args[3]
        return err class CoconutParseError(CoconutSyntaxError):
    """Coconut ParseError."""

    def __init__(self, message=None, source=None, point=None, ln=None, extra=None):
        """Creates the ParseError."""
        self.args = (message, source, point, ln, extra) class CoconutStyleError(CoconutSyntaxError):
    """Coconut --strict error."""

    def __init__(self, message, source=None, point=None, ln=None):
        """Creates the --strict Coconut error."""
        self.args = (message, source, point, ln)

    def message(self, message, source, point, ln):
        """Creates the --strict Coconut error message."""
        return super(CoconutStyleError, self).message(
            message,
            source,
            point,
            ln,
            extra="remove --strict to dismiss",
        ) class CoconutTargetError(CoconutSyntaxError):
    """Coconut --target error."""

    def __init__(self, message, source=None, point=None, ln=None, target=None):
        """Creates the --target Coconut error."""
        self.args = (message, source, point, ln, target)

    def message(self, message, source, point, ln, target):
        """Creates the --target Coconut error message."""
        if target is None:
            extra = None
        else:
            extra = "pass --target " + target + " to fix"
        return super(CoconutTargetError, self).message(message, source, point, ln, extra) class CoconutInternalException(CoconutException):
    """Internal Coconut exception."""

    def message(self, message, item, extra):
        """Creates the Coconut internal exception message."""
        return (
            super(CoconutInternalException, self).message(message, item, extra)
            + " " + report_this_text
        ) class CoconutSyntaxWarning(CoconutSyntaxError, CoconutWarning):
    """CoconutWarning with CoconutSyntaxError semantics.""" class CoconutDeferredSyntaxError(CoconutException):
    """Deferred Coconut SyntaxError."""

    def __init__(self, message, loc):
        """Creates the Coconut exception."""
        self.args = (message, loc)

    def message(self, message, loc):
        """Uses arguments to create the message."""
        return message + " (loc " + str(loc) + ")" def clean(inputline, strip=True, rem_indents=True, encoding_errors="replace"):
    """Clean and strip a line."""
    stdout_encoding = get_encoding(sys.stdout)
    inputline = str(inputline)
    if rem_indents:
        inputline = inputline.replace(openindent, "").replace(closeindent, "")
    if strip:
        inputline = inputline.strip()
    return inputline.encode(stdout_encoding, encoding_errors).decode(stdout_encoding) def format_error(err_type, err_value, err_trace=None):
def complain(error):
def internal_assert(condition, message=None, item=None, extra=None):
def get_name(expr):
    def __init__(self, log_to=None, prefix=""):
    def write(self, s):
    def writelines(self, lines):
    def log(self, *args):
    def logging(self):
    def __init__(self, other=None):
    def copy_from(self, other):
    def reset(self):
    def copy(self):
    def display(self, messages, sig="", debug=False, **kwargs):
    def show(self, *messages):
    def show_sig(self, *messages):
    def show_error(self, *messages):
    def log(self, *messages):
    def log_func(self, func):
    def log_prefix(self, prefix, *messages):
    def log_sig(self, *messages):
    def log_vars(self, message, variables, rem_vars=("self",)):
    def get_error(self, err=None, show_tb=None):
    def in_path(self, new_path, old_path=None):
    def warn(self, *args, **kwargs):
    def warn_err(self, warning, force=False):
    def print_exc(self, err=None, show_tb=None):
    def log_exc(self, err=None):
    def log_cmd(self, args):
    def show_tabulated(self, begin, middle, end):
    def indent_tracing(self):
    def print_trace(self, *args):
    def log_tag(self, tag, code, multiline=False):
    def log_trace(self, expr, original, loc, item=None, extra=None):
    def _trace_success_action(self, original, start_loc, end_loc, expr, tokens):
    def _trace_exc_action(self, original, loc, expr, exc):
    def trace(self, item):
    def gather_parsing_stats(self):
    def patch_logging(self):
            def getLogger(name=None):
    def pylog(self, *args, **kwargs):
class LoggingStringIO(StringIO):
class Logger(object): class Matcher(object):
    """Pattern-matching processor."""
    __slots__ = (
        "comp",
        "original",
        "loc",
        "check_var",
        "style",
        "position",
        "checkdefs",
        "names",
        "var_index_obj",
        "name_list",
        "child_groups",
        "guards",
        "parent_names",
    )
    matchers = {
        "dict": lambda self: self.match_dict,
        "iter": lambda self: self.match_iterator,
        "series": lambda self: self.match_sequence,
        "rseries": lambda self: self.match_rsequence,
        "mseries": lambda self: self.match_msequence,
        "string": lambda self: self.match_string,
        "rstring": lambda self: self.match_rstring,
        "mstring": lambda self: self.match_mstring,
        "const": lambda self: self.match_const,
        "var": lambda self: self.match_var,
        "set": lambda self: self.match_set,
        "data": lambda self: self.match_data,
        "class": lambda self: self.match_class,
        "data_or_class": lambda self: self.match_data_or_class,
        "paren": lambda self: self.match_paren,
        "trailer": lambda self: self.match_trailer,
        "and": lambda self: self.match_and,
        "or": lambda self: self.match_or,
        "star": lambda self: self.match_star,
        "implicit_tuple": lambda self: self.match_implicit_tuple,
        "view": lambda self: self.match_view,
        "infix": lambda self: self.match_infix,
    }
    valid_styles = (
        "coconut",
        "python",
        "coconut warn",
        "python warn",
        "coconut strict",
        "python strict",
    )

    def __init__(self, comp, original, loc, check_var, style="coconut", name_list=None, checkdefs=None, parent_names={}, var_index_obj=None):
        """Creates the matcher."""
        self.comp = comp
        self.original = original
        self.loc = loc
        self.check_var = check_var
        internal_assert(style in self.valid_styles, "invalid Matcher style", style)
        self.style = style
        self.name_list = name_list
        self.position = 0
        self.checkdefs = []
        if checkdefs is None:
            self.increment()
        else:
            for checks, defs in checkdefs:
                self.checkdefs.append((checks[:], defs[:]))
            self.set_position(-1)
        self.parent_names = parent_names
        self.names = OrderedDict()  # ensures deterministic ordering of name setting code
        self.var_index_obj = [0] if var_index_obj is None else var_index_obj
        self.guards = []
        self.child_groups = []

    def branches(self, num_branches):
        """Create num_branches child matchers, one of which must match for the parent match to succeed."""
        child_group = []
        for _ in range(num_branches):
            new_matcher = Matcher(self.comp, self.original, self.loc, self.check_var, self.style, self.name_list, self.checkdefs, self.names, self.var_index_obj)
            new_matcher.insert_check(0, "not " + self.check_var)
            child_group.append(new_matcher)

        self.child_groups.append(child_group)
        return child_group

    def get_checks(self, position=None):
        """Gets the checks at the position."""
        if position is None:
            position = self.position
        return self.checkdefs[position][0]

    def set_checks(self, checks, position=None):
        """Sets the checks at the position."""
        if position is None:
            position = self.position
        self.checkdefs[position][0] = checks

    checks = property(get_checks, set_checks)

    def get_defs(self, position=None):
        """Gets the defs at the position."""
        if position is None:
            position = self.position
        return self.checkdefs[position][1]

    def set_defs(self, defs, position=None):
        """Sets the defs at the position."""
        if position is None:
            position = self.position
        self.checkdefs[position][1] = defs

    defs = property(get_defs, set_defs)

    def add_check(self, check_item):
        """Adds a check universally."""
        self.checks.append(check_item)

    def add_def(self, def_item):
        """Adds a def universally."""
        self.defs.append(def_item)

    def insert_check(self, index, check_item):
        """Inserts a check universally."""
        self.checks.insert(index, check_item)

    def insert_def(self, index, def_item):
        """Inserts a def universally."""
        self.defs.insert(index, def_item)

    @property
    def using_python_rules(self):
        """Whether the current style uses PEP 622 rules."""
        return self.style.startswith("python")

    def rule_conflict_warn(self, message, if_coconut=None, if_python=None, extra=None):
        """Warns on conflicting style rules if callback was given."""
        if self.style.endswith("warn") or self.style.endswith("strict") and self.comp.strict:
            full_msg = message
            if if_python or if_coconut:
                full_msg += " (" + (if_python if self.using_python_rules else if_coconut) + ")"
            if extra:
                full_msg += " (" + extra + ")"
            if self.style.endswith("strict"):
                full_msg += " (remove --strict to dismiss)"
            logger.warn_err(self.comp.make_err(CoconutSyntaxWarning, full_msg, self.original, self.loc))

    def add_guard(self, cond):
        """Adds cond as a guard."""
        self.guards.append(cond)

    def set_position(self, position):
        """Sets the if-statement position."""
        if position < 0:
            position += len(self.checkdefs)
        while position >= len(self.checkdefs):
            self.checkdefs.append(([], []))
        self.position = position

    def increment(self, by=1):
        """Advances the if-statement position."""
        new_pos = self.position + by
        internal_assert(new_pos > 0, "invalid increment/decrement call to set pos to", new_pos)
        self.set_position(new_pos)

    def decrement(self, by=1):
        """Decrements the if-statement position."""
        self.increment(-by)

    @contextmanager
    def down_a_level(self, by=1):
        """Increment then decrement."""
        self.increment(by)
        try:
            yield
        finally:
            self.decrement(by)

    def get_temp_var(self):
        """Gets the next match_temp_var."""
        tempvar = match_temp_var + "_" + str(self.var_index_obj[0])
        self.var_index_obj[0] += 1
        return tempvar

    def get_set_name_var(self, name):
        """Gets the var for checking whether a name should be set."""
        return match_set_name_var + "_" + name

    def register_name(self, name, value):
        """Register a new name and return its name set var."""
        self.names[name] = value
        if self.name_list is not None and name not in self.name_list:
            self.name_list.append(name)
        return self.get_set_name_var(name)

    def match_var(self, tokens, item, bind_wildcard=False):
        """Matches a variable."""
        varname, = tokens
        if varname == wildcard and not bind_wildcard:
            return
        if varname in self.parent_names:
            self.add_check(self.parent_names[varname] + " == " + item)
        elif varname in self.names:
            self.add_check(self.names[varname] + " == " + item)
        else:
            set_name_var = self.register_name(varname, item)
            self.add_def(set_name_var + " = " + item)

    def match_all_in(self, matches, item):
        """Matches all matches to elements of item."""
        for i, match in enumerate(matches):
            self.match(match, item + "[" + str(i) + "]")

    def check_len_in(self, min_len, max_len, item):
        """Checks that the length of item is in range(min_len, max_len+1)."""
        if max_len is None:
            if min_len:
                self.add_check("_coconut.len(" + item + ") >= " + str(min_len))
        elif min_len == max_len:
            self.add_check("_coconut.len(" + item + ") == " + str(min_len))
        elif not min_len:
            self.add_check("_coconut.len(" + item + ") <= " + str(max_len))
        else:
            self.add_check(str(min_len) + " <= _coconut.len(" + item + ") <= " + str(max_len))

    def match_function(self, args, kwargs, pos_only_match_args=(), match_args=(), star_arg=None, kwd_only_match_args=(), dubstar_arg=None):
        """Matches a pattern-matching function."""
        # before everything, pop the FunctionMatchError from context
        self.add_def(function_match_error_var + " = _coconut_get_function_match_error()")
        with self.down_a_level():

            self.match_in_args_kwargs(pos_only_match_args, match_args, args, kwargs, allow_star_args=star_arg is not None)

            if star_arg is not None:
                self.match(star_arg, args + "[" + str(len(match_args)) + ":]")

            self.match_in_kwargs(kwd_only_match_args, kwargs)

            with self.down_a_level():
                if dubstar_arg is None:
                    self.add_check("not " + kwargs)
                else:
                    self.match(dubstar_arg, kwargs)

    def match_in_args_kwargs(self, pos_only_match_args, match_args, args, kwargs, allow_star_args=False):
        """Matches against args or kwargs."""
        req_len = 0
        arg_checks = {}
        to_match = []  # [(move_down, match, against)]
        for i, arg in enumerate(pos_only_match_args + match_args):
            if isinstance(arg, tuple):
                (match, default) = arg
            else:
                match, default = arg, None
            if i < len(pos_only_match_args):  # faster if arg in pos_only_match_args
                names = None
            else:
                names = get_match_names(match)
            if default is None:
                if not names:
                    req_len = i + 1
                    to_match.append((False, match, args + "[" + str(i) + "]"))
                else:
                    arg_checks[i] = (
                        # if i < req_len
                        " and ".join('"' + name + '" not in ' + kwargs for name in names),
                        # if i >= req_len
                        "_coconut.sum((_coconut.len(" + args + ") > " + str(i) + ", "
                        + ", ".join('"' + name + '" in ' + kwargs for name in names)
                        + ")) == 1",
                    )
                    tempvar = self.get_temp_var()
                    self.add_def(
                        tempvar + " = "
                        + args + "[" + str(i) + "] if _coconut.len(" + args + ") > " + str(i) + " else "
                        + "".join(
                            kwargs + '.pop("' + name + '") if "' + name + '" in ' + kwargs + " else "
                            for name in names[:-1]
                        )
                        + kwargs + '.pop("' + names[-1] + '")',
                    )
                    to_match.append((True, match, tempvar))
            else:
                if not names:
                    tempvar = self.get_temp_var()
                    self.add_def(tempvar + " = " + args + "[" + str(i) + "] if _coconut.len(" + args + ") > " + str(i) + " else " + default)
                    to_match.append((True, match, tempvar))
                else:
                    arg_checks[i] = (
                        # if i < req_len
                        None,
                        # if i >= req_len
                        "_coconut.sum((_coconut.len(" + args + ") > " + str(i) + ", "
                        + ", ".join('"' + name + '" in ' + kwargs for name in names)
                        + ")) <= 1",
                    )
                    tempvar = self.get_temp_var()
                    self.add_def(
                        tempvar + " = "
                        + args + "[" + str(i) + "] if _coconut.len(" + args + ") > " + str(i) + " else "
                        + "".join(
                            kwargs + '.pop("' + name + '") if "' + name + '" in ' + kwargs + " else "
                            for name in names
                        )
                        + default,
                    )
                    to_match.append((True, match, tempvar))

        max_len = None if allow_star_args else len(pos_only_match_args) + len(match_args)
        self.check_len_in(req_len, max_len, args)
        for i in sorted(arg_checks):
            lt_check, ge_check = arg_checks[i]
            if i < req_len:
                if lt_check is not None:
                    self.add_check(lt_check)
            else:
                if ge_check is not None:
                    self.add_check(ge_check)

        for move_down, match, against in to_match:
            if move_down:
                with self.down_a_level():
                    self.match(match, against)
            else:
                self.match(match, against)

    def match_in_kwargs(self, match_args, kwargs):
        """Matches against kwargs."""
        for match, default in match_args:
            names = get_match_names(match)
            if not names:
                raise CoconutDeferredSyntaxError("keyword-only pattern-matching function arguments must be named", self.loc)
            tempvar = self.get_temp_var()
            self.add_def(
                tempvar + " = "
                + "".join(
                    kwargs + '.pop("' + name + '") if "' + name + '" in ' + kwargs + " else "
                    for name in names
                )
                + (default if default is not None else "_coconut_sentinel"),
            )
            with self.down_a_level():
                if default is None:
                    self.add_check(tempvar + " is not _coconut_sentinel")
                self.match(match, tempvar)

    def match_dict(self, tokens, item):
        """Matches a dictionary."""
        internal_assert(1 <= len(tokens) <= 2, "invalid dict match tokens", tokens)
        if len(tokens) == 1:
            matches, rest = tokens[0], None
        else:
            matches, rest = tokens

        self.add_check("_coconut.isinstance(" + item + ", _coconut.abc.Mapping)")

        if rest is None:
            self.rule_conflict_warn(
                "ambiguous pattern; could be Coconut-style len-checking dict match or Python-style len-ignoring dict match",
                if_coconut='resolving to Coconut-style len-checking dict match by default',
                if_python='resolving to Python-style len-ignoring dict match due to Python-style "match: case" block',
                extra="use explicit '{..., **_}' or '{..., **{}}' syntax to dismiss",
            )
            check_len = not self.using_python_rules
        elif rest == "{}":
            check_len = True
            rest = None
        else:
            check_len = False

        if check_len:
            self.add_check("_coconut.len(" + item + ") == " + str(len(matches)))

        seen_keys = set()
        for k, v in matches:
            if k in seen_keys:
                raise CoconutDeferredSyntaxError("duplicate key {k!r} in dictionary pattern".format(k=k), self.loc)
            seen_keys.add(k)
            key_var = self.get_temp_var()
            self.add_def(key_var + " = " + item + ".get(" + k + ", _coconut_sentinel)")
            with self.down_a_level():
                self.add_check(key_var + " is not _coconut_sentinel")
                self.match(v, key_var)

        if rest is not None and rest != wildcard:
            match_keys = [k for k, v in matches]
            with self.down_a_level():
                self.add_def(
                    rest + " = dict((k, v) for k, v in "
                    + item + ".items() if k not in set(("
                    + ", ".join(match_keys) + ("," if len(match_keys) == 1 else "")
                    + ")))",
                )

    def assign_to_series(self, name, series_type, item):
        """Assign name to item converted to the given series_type."""
        if self.using_python_rules or series_type == "[":
            self.add_def(name + " = _coconut.list(" + item + ")")
        elif series_type == "(":
            self.add_def(name + " = _coconut.tuple(" + item + ")")
        else:
            raise CoconutInternalException("invalid series match type", series_type)

    def match_implicit_tuple(self, tokens, item):
        """Matches an implicit tuple."""
        return self.match_sequence(["(", tokens], item)

    def match_sequence(self, tokens, item):
        """Matches a sequence."""
        internal_assert(2 <= len(tokens) <= 3, "invalid sequence match tokens", tokens)
        tail = None
        if len(tokens) == 2:
            series_type, matches = tokens
        else:
            series_type, matches, tail = tokens
        self.add_check("_coconut.isinstance(" + item + ", _coconut.abc.Sequence)")
        if tail is None:
            self.add_check("_coconut.len(" + item + ") == " + str(len(matches)))
        else:
            self.add_check("_coconut.len(" + item + ") >= " + str(len(matches)))
            if tail != wildcard:
                if len(matches) > 0:
                    splice = "[" + str(len(matches)) + ":]"
                else:
                    splice = ""
                self.assign_to_series(tail, series_type, item + splice)
        self.match_all_in(matches, item)

    def match_iterator(self, tokens, item):
        """Matches a lazy list or a chain."""
        internal_assert(2 <= len(tokens) <= 3, "invalid iterator match tokens", tokens)
        tail = None
        if len(tokens) == 2:
            _, matches = tokens
        else:
            _, matches, tail = tokens
        self.add_check("_coconut.isinstance(" + item + ", _coconut.abc.Iterable)")
        if tail is None:
            itervar = self.get_temp_var()
            self.add_def(itervar + " = _coconut.tuple(" + item + ")")
        elif matches:
            itervar = self.get_temp_var()
            if tail == wildcard:
                tail = item
            else:
                self.add_def(tail + " = _coconut.iter(" + item + ")")
            self.add_def(itervar + " = _coconut.tuple(_coconut_igetitem(" + tail + ", _coconut.slice(None, " + str(len(matches)) + ")))")
        else:
            itervar = None
            if tail != wildcard:
                self.add_def(tail + " = " + item)
        if itervar is not None:
            with self.down_a_level():
                self.add_check("_coconut.len(" + itervar + ") == " + str(len(matches)))
                self.match_all_in(matches, itervar)

    def match_star(self, tokens, item):
        """Matches starred assignment."""
        internal_assert(1 <= len(tokens) <= 3, "invalid star match tokens", tokens)
        head_matches, last_matches = None, None
        if len(tokens) == 1:
            middle = tokens[0]
        elif len(tokens) == 2:
            if isinstance(tokens[0], str):
                middle, last_matches = tokens
            else:
                head_matches, middle = tokens
        else:
            head_matches, middle, last_matches = tokens
        self.add_check("_coconut.isinstance(" + item + ", _coconut.abc.Iterable)")
        if head_matches is None and last_matches is None:
            if middle != wildcard:
                self.add_def(middle + " = _coconut.list(" + item + ")")
        else:
            itervar = self.get_temp_var()
            self.add_def(itervar + " = _coconut.list(" + item + ")")
            with self.down_a_level():
                req_length = (len(head_matches) if head_matches is not None else 0) + (len(last_matches) if last_matches is not None else 0)
                self.add_check("_coconut.len(" + itervar + ") >= " + str(req_length))
                if middle != wildcard:
                    head_splice = str(len(head_matches)) if head_matches is not None else ""
                    last_splice = "-" + str(len(last_matches)) if last_matches is not None else ""
                    self.add_def(middle + " = " + itervar + "[" + head_splice + ":" + last_splice + "]")
                if head_matches is not None:
                    self.match_all_in(head_matches, itervar)
                if last_matches is not None:
                    for x in range(1, len(last_matches) + 1):
                        self.match(last_matches[-x], itervar + "[-" + str(x) + "]")

    def match_rsequence(self, tokens, item):
        """Matches a reverse sequence."""
        front, series_type, matches = tokens
        self.add_check("_coconut.isinstance(" + item + ", _coconut.abc.Sequence)")
        self.add_check("_coconut.len(" + item + ") >= " + str(len(matches)))
        if front != wildcard:
            if len(matches):
                splice = "[:" + str(-len(matches)) + "]"
            else:
                splice = ""
            self.assign_to_series(front, series_type, item + splice)
        for i, match in enumerate(matches):
            self.match(match, item + "[" + str(i - len(matches)) + "]")

    def match_msequence(self, tokens, item):
        """Matches a middle sequence."""
        series_type, head_matches, middle, _, last_matches = tokens
        self.add_check("_coconut.isinstance(" + item + ", _coconut.abc.Sequence)")
        self.add_check("_coconut.len(" + item + ") >= " + str(len(head_matches) + len(last_matches)))
        if middle != wildcard:
            if len(head_matches) and len(last_matches):
                splice = "[" + str(len(head_matches)) + ":" + str(-len(last_matches)) + "]"
            elif len(head_matches):
                splice = "[" + str(len(head_matches)) + ":]"
            elif len(last_matches):
                splice = "[:" + str(-len(last_matches)) + "]"
            else:
                splice = ""
            self.assign_to_series(middle, series_type, item + splice)
        self.match_all_in(head_matches, item)
        for i, match in enumerate(last_matches):
            self.match(match, item + "[" + str(i - len(last_matches)) + "]")

    def match_string(self, tokens, item):
        """Match prefix string."""
        prefix, name = tokens
        return self.match_mstring((prefix, name, None), item)

    def match_rstring(self, tokens, item):
        """Match suffix string."""
        name, suffix = tokens
        return self.match_mstring((None, name, suffix), item)

    def match_mstring(self, tokens, item):
        """Match prefix and suffix string."""
        prefix, name, suffix = tokens
        if prefix is None:
            use_bytes = suffix.startswith("b")
        elif suffix is None:
            use_bytes = prefix.startswith("b")
        elif prefix.startswith("b") and suffix.startswith("b"):
            use_bytes = True
        elif prefix.startswith("b") or suffix.startswith("b"):
            raise CoconutDeferredSyntaxError("string literals and byte literals cannot be added in patterns", self.loc)
        else:
            use_bytes = False
        if use_bytes:
            self.add_check("_coconut.isinstance(" + item + ", _coconut.bytes)")
        else:
            self.add_check("_coconut.isinstance(" + item + ", _coconut.str)")
        if prefix is not None:
            self.add_check(item + ".startswith(" + prefix + ")")
        if suffix is not None:
            self.add_check(item + ".endswith(" + suffix + ")")
        if name != wildcard:
            self.add_def(
                name + " = " + item + "["
                + ("" if prefix is None else self.comp.eval_now("len(" + prefix + ")")) + ":"
                + ("" if suffix is None else self.comp.eval_now("-len(" + suffix + ")")) + "]",
            )

    def match_const(self, tokens, item):
        """Matches a constant."""
        match, = tokens
        if match in const_vars:
            self.add_check(item + " is " + match)
        else:
            self.add_check(item + " == " + match)

    def match_set(self, tokens, item):
        """Matches a set."""
        match, = tokens
        self.add_check("_coconut.isinstance(" + item + ", _coconut.abc.Set)")
        self.add_check("_coconut.len(" + item + ") == " + str(len(match)))
        for const in match:
            self.add_check(const + " in " + item)

    def split_data_or_class_match(self, tokens):
        """Split data/class match tokens into cls_name, pos_matches, name_matches, star_match."""
        cls_name, matches = tokens

        pos_matches = []
        name_matches = {}
        star_match = None
        for match_arg in matches:
            if len(match_arg) == 1:
                match, = match_arg
                if star_match is not None:
                    raise CoconutDeferredSyntaxError("positional arg after starred arg in data/class match", self.loc)
                if name_matches:
                    raise CoconutDeferredSyntaxError("positional arg after named arg in data/class match", self.loc)
                pos_matches.append(match)
            elif len(match_arg) == 2:
                internal_assert(match_arg[0] == "*", "invalid starred data/class match arg tokens", match_arg)
                _, match = match_arg
                if star_match is not None:
                    raise CoconutDeferredSyntaxError("duplicate starred arg in data/class match", self.loc)
                if name_matches:
                    raise CoconutDeferredSyntaxError("both starred arg and named arg in data/class match", self.loc)
                star_match = match
            elif len(match_arg) == 3:
                internal_assert(match_arg[1] == "=", "invalid named data/class match arg tokens", match_arg)
                name, _, match = match_arg
                if star_match is not None:
                    raise CoconutDeferredSyntaxError("both named arg and starred arg in data/class match", self.loc)
                if name in name_matches:
                    raise CoconutDeferredSyntaxError("duplicate named arg {name!r} in data/class match".format(name=name), self.loc)
                name_matches[name] = match
            else:
                raise CoconutInternalException("invalid data/class match arg", match_arg)

        return cls_name, pos_matches, name_matches, star_match

    def match_class(self, tokens, item):
        """Matches a class PEP-622-style."""
        cls_name, pos_matches, name_matches, star_match = self.split_data_or_class_match(tokens)

        self.add_check("_coconut.isinstance(" + item + ", " + cls_name + ")")

        self_match_matcher, other_cls_matcher = self.branches(2)

        # handle instances of _coconut_self_match_types
        self_match_matcher.add_check("_coconut.isinstance(" + item + ", _coconut_self_match_types)")
        if pos_matches:
            if len(pos_matches) > 1:
                self_match_matcher.add_def('raise _coconut.TypeError("too many positional args in class match (got ' + str(len(pos_matches)) + '; type supports 1)")')
            else:
                self_match_matcher.match(pos_matches[0], item)

        # handle all other classes
        other_cls_matcher.add_check("not _coconut.isinstance(" + item + ", _coconut_self_match_types)")
        for i, match in enumerate(pos_matches):
            other_cls_matcher.match(match, "_coconut.getattr(" + item + ", " + item + ".__match_args__[" + str(i) + "])")

        # handle starred arg
        if star_match is not None:
            temp_var = self.get_temp_var()
            self.add_def(
                "{temp_var} = _coconut.tuple(_coconut.getattr({item}, {item}.__match_args__[i]) for i in _coconut.range({min_ind}, _coconut.len({item}.__match_args__)))".format(
                    temp_var=temp_var,
                    item=item,
                    min_ind=len(pos_matches),
                ),
            )
            with self.down_a_level():
                self.match(star_match, temp_var)

        # handle keyword args
        for name, match in name_matches.items():
            self.match(match, item + "." + name)

    def match_data(self, tokens, item):
        """Matches a data type."""
        cls_name, pos_matches, name_matches, star_match = self.split_data_or_class_match(tokens)

        self.add_check("_coconut.isinstance(" + item + ", " + cls_name + ")")

        if star_match is None:
            self.add_check(
                '_coconut.len({item}) == {total_len}'.format(
                    item=item,
                    total_len=len(pos_matches) + len(name_matches),
                ),
            )
        # avoid checking >= 0
        elif len(pos_matches):
            self.add_check(
                "_coconut.len({item}) >= {min_len}".format(
                    item=item,
                    min_len=len(pos_matches),
                ),
            )

        self.match_all_in(pos_matches, item)

        if star_match is not None:
            self.match(star_match, item + "[" + str(len(pos_matches)) + ":]")

        for name, match in name_matches.items():
            self.match(match, item + "." + name)

    def match_data_or_class(self, tokens, item):
        """Matches an ambiguous data or class match."""
        self.rule_conflict_warn(
            "ambiguous pattern; could be class match or data match",
            if_coconut='resolving to Coconut data match by default',
            if_python='resolving to Python-style class match due to Python-style "match: case" block',
            extra="use explicit 'data data_name(patterns)' or 'class cls_name(patterns)' syntax to dismiss",
        )
        if self.using_python_rules:
            return self.match_class(tokens, item)
        else:
            return self.match_data(tokens, item)

    def match_paren(self, tokens, item):
        """Matches a paren."""
        match, = tokens
        return self.match(match, item)

    def match_trailer(self, tokens, item):
        """Matches typedefs and as patterns."""
        internal_assert(len(tokens) > 1 and len(tokens) % 2 == 1, "invalid trailer match tokens", tokens)
        match, trailers = tokens[0], tokens[1:]
        for i in range(0, len(trailers), 2):
            op, arg = trailers[i], trailers[i + 1]
            if op == "as":
                self.match_var([arg], item, bind_wildcard=True)
            elif op == "is":
                self.add_check("_coconut.isinstance(" + item + ", " + arg + ")")
            else:
                raise CoconutInternalException("invalid trailer match operation", op)
        self.match(match, item)

    def match_and(self, tokens, item):
        """Matches and."""
        for match in tokens:
            self.match(match, item)

    def match_or(self, tokens, item):
        """Matches or."""
        new_matchers = self.branches(len(tokens))
        for m, tok in zip(new_matchers, tokens):
            m.match(tok, item)

    def match_view(self, tokens, item):
        """Matches view patterns"""
        view_func, view_pattern = tokens

        func_result_var = self.get_temp_var()
        self.add_def(
            handle_indentation(
                """
try:
    {func_result_var} = ({view_func})({item})
except _coconut.Exception as _coconut_view_func_exc:
    if _coconut.getattr(_coconut_view_func_exc.__class__, "__name__", None) == "MatchError":
        {func_result_var} = _coconut_sentinel
    else:
        raise
            """,
            ).format(
                func_result_var=func_result_var,
                view_func=view_func,
                item=item,
            ),
        )

        with self.down_a_level():
            self.add_check(func_result_var + " is not _coconut_sentinel")
            self.match(view_pattern, func_result_var)

    def match_infix(self, tokens, item):
        """Matches infix patterns."""
        internal_assert(len(tokens) > 1 and len(tokens) % 2 == 1, "invalid infix match tokens", tokens)
        match = tokens[0]
        for i in range(1, len(tokens), 2):
            op, arg = tokens[i], tokens[i + 1]
            self.add_check("(" + op + ")(" + item + ", " + arg + ")")
        self.match(match, item)

    def match(self, tokens, item):
        """Performs pattern-matching processing."""
        for flag, get_handler in self.matchers.items():
            if flag in tokens:
                return get_handler(self)(tokens, item)
        raise CoconutInternalException("invalid pattern-matching tokens", tokens)

    def out(self):
        """Return pattern-matching code assuming check_var starts False."""
        out = []

        # set match_set_name_vars to sentinels
        for name in self.names:
            out.append(self.get_set_name_var(name) + " = _coconut_sentinel\n")

        # match checkdefs setting check_var
        closes = 0
        for checks, defs in self.checkdefs:
            if checks:
                out.append("if " + paren_join(checks, "and") + ":\n" + openindent)
                closes += 1
            if defs:
                out.append("\n".join(defs) + "\n")
        out.append(self.check_var + " = True\n" + closeindent * closes)

        # handle children
        for children in self.child_groups:
            out.append(
                handle_indentation(
                    """
if {check_var}:
    {check_var} = False
    {children}
                """,
                    add_newline=True,
                ).format(
                    check_var=self.check_var,
                    children="".join(child.out() for child in children),
                ),
            )

        # commit variable definitions
        name_set_code = []
        for name, val in self.names.items():
            name_set_code.append(
                handle_indentation(
                    """
if {set_name_var} is not _coconut_sentinel:
    {name} = {val}
                """,
                    add_newline=True,
                ).format(
                    set_name_var=self.get_set_name_var(name),
                    name=name,
                    val=val,
                ),
            )
        if name_set_code:
            out.append(
                handle_indentation(
                    """
if {check_var}:
    {name_set_code}
                """,
                ).format(
                    check_var=self.check_var,
                    name_set_code="".join(name_set_code),
                ),
            )

        # handle guards
        if self.guards:
            out.append(
                handle_indentation(
                    """
if {check_var} and not ({guards}):
    {check_var} = False
                """,
                    add_newline=True,
                ).format(
                    check_var=self.check_var,
                    guards=paren_join(self.guards, "and"),
                ),
            )

        return "".join(out)

    def build(self, stmts=None, set_check_var=True, invert=False):
        """Construct code for performing the match then executing stmts."""
        out = []
        if set_check_var:
            out.append(self.check_var + " = False\n")
        out.append(self.out())
        if stmts is not None:
            out.append("if " + ("not " if invert else "") + self.check_var + ":" + "\n" + openindent + "".join(stmts) + closeindent)
        return "".join(out) class Grammar(object):
    """Coconut grammar specification."""
    timing_info = None

    comma = Literal(",")
    dubstar = Literal("**")
    star = ~dubstar + Literal("*")
    at = Literal("@")
    arrow = Literal("->") | fixto(Literal("\u2192"), "->")
    colon_eq = Literal(":=")
    unsafe_dubcolon = Literal("::")
    unsafe_colon = Literal(":")
    colon = ~unsafe_dubcolon + ~colon_eq + unsafe_colon
    semicolon = Literal(";") | invalid_syntax("\u037e", "invalid Greek question mark instead of semicolon", greedy=True)
    eq = Literal("==")
    equals = ~eq + Literal("=")
    lbrack = Literal("[")
    rbrack = Literal("]")
    lbrace = Literal("{")
    rbrace = Literal("}")
    lbanana = ~Literal("(|)") + ~Literal("(|>") + ~Literal("(|*") + ~Literal("(|?") + Literal("(|")
    rbanana = Literal("|)")
    lparen = ~lbanana + Literal("(")
    rparen = Literal(")")
    unsafe_dot = Literal(".")
    dot = ~Literal("..") + unsafe_dot
    plus = Literal("+")
    minus = ~Literal("->") + Literal("-")
    dubslash = Literal("//")
    slash = ~dubslash + Literal("/")
    pipe = Literal("|>") | fixto(Literal("\u21a6"), "|>")
    star_pipe = Literal("|*>") | fixto(Literal("*\u21a6"), "|*>")
    dubstar_pipe = Literal("|**>") | fixto(Literal("**\u21a6"), "|**>")
    back_pipe = Literal("<|") | fixto(Literal("\u21a4"), "<|")
    back_star_pipe = Literal("<*|") | ~Literal("\u21a4**") + fixto(Literal("\u21a4*"), "<*|")
    back_dubstar_pipe = Literal("<**|") | fixto(Literal("\u21a4**"), "<**|")
    none_pipe = Literal("|?>") | fixto(Literal("?\u21a6"), "|?>")
    none_star_pipe = Literal("|?*>") | fixto(Literal("?*\u21a6"), "|?*>")
    none_dubstar_pipe = Literal("|?**>") | fixto(Literal("?**\u21a6"), "|?**>")
    dotdot = (
        ~Literal("...") + ~Literal("..>") + ~Literal("..*") + Literal("..")
        | ~Literal("\u2218>") + ~Literal("\u2218*>") + fixto(Literal("\u2218"), "..")
    )
    comp_pipe = Literal("..>") | fixto(Literal("\u2218>"), "..>")
    comp_back_pipe = Literal("<..") | fixto(Literal("<\u2218"), "<..")
    comp_star_pipe = Literal("..*>") | fixto(Literal("\u2218*>"), "..*>")
    comp_back_star_pipe = Literal("<*..") | fixto(Literal("<*\u2218"), "<*..")
    comp_dubstar_pipe = Literal("..**>") | fixto(Literal("\u2218**>"), "..**>")
    comp_back_dubstar_pipe = Literal("<**..") | fixto(Literal("<**\u2218"), "<**..")
    amp = Literal("&") | fixto(Literal("\u2227") | Literal("\u2229"), "&")
    caret = Literal("^") | fixto(Literal("\u22bb") | Literal("\u2295"), "^")
    unsafe_bar = ~Literal("|>") + ~Literal("|*") + Literal("|") | fixto(Literal("\u2228") | Literal("\u222a"), "|")
    bar = ~rbanana + unsafe_bar
    percent = Literal("%")
    dollar = Literal("$")
    lshift = Literal("<<") | fixto(Literal("\xab"), "<<")
    rshift = Literal(">>") | fixto(Literal("\xbb"), ">>")
    tilde = Literal("~") | fixto(~Literal("\xac=") + Literal("\xac"), "~")
    underscore = Literal("_")
    pound = Literal("#")
    unsafe_backtick = Literal("`")
    dubbackslash = Literal("\\\\")
    backslash = ~dubbackslash + Literal("\\")
    dubquestion = Literal("??")
    questionmark = ~dubquestion + Literal("?")

    except_star_kwd = combine(keyword("except") + star)
    except_kwd = ~except_star_kwd + keyword("except")
    lambda_kwd = keyword("lambda") | fixto(keyword("\u03bb", explicit_prefix=colon), "lambda")
    async_kwd = keyword("async", explicit_prefix=colon)
    await_kwd = keyword("await", explicit_prefix=colon)
    data_kwd = keyword("data", explicit_prefix=colon)
    match_kwd = keyword("match", explicit_prefix=colon)
    case_kwd = keyword("case", explicit_prefix=colon)
    cases_kwd = keyword("cases", explicit_prefix=colon)
    where_kwd = keyword("where", explicit_prefix=colon)
    addpattern_kwd = keyword("addpattern", explicit_prefix=colon)
    then_kwd = keyword("then", explicit_prefix=colon)

    ellipsis = Forward()
    ellipsis_ref = Literal("...") | Literal("\u2026")

    lt = ~Literal("<<") + ~Literal("<=") + ~Literal("<|") + ~Literal("<..") + ~Literal("<*") + Literal("<")
    gt = ~Literal(">>") + ~Literal(">=") + Literal(">")
    le = Literal("<=") | fixto(Literal("\u2264"), "<=")
    ge = Literal(">=") | fixto(Literal("\u2265"), ">=")
    ne = Literal("!=") | fixto(Literal("\xac=") | Literal("\u2260"), "!=")

    mul_star = star | fixto(Literal("\xd7"), "*")
    exp_dubstar = dubstar | fixto(Literal("\u2191"), "**")
    neg_minus = (
        minus
        | fixto(Literal("\u207b"), "-")
        | invalid_syntax("\u2212", "U+2212 is only for negation, not subtraction")
    )
    sub_minus = (
        minus
        | fixto(Literal("\u2212"), "-")
        | invalid_syntax("\u207b", "U+207b is only for subtraction, not negation")
    )
    div_slash = slash | fixto(Literal("\xf7") + ~slash, "/")
    div_dubslash = dubslash | fixto(combine(Literal("\xf7") + slash), "//")
    matrix_at_ref = at | fixto(Literal("\u22c5"), "@")
    matrix_at = Forward()

    test = Forward()
    test_no_chain, dubcolon = disable_inside(test, unsafe_dubcolon)
    test_no_infix, backtick = disable_inside(test, unsafe_backtick)

    base_name_regex = r""
    for no_kwd in keyword_vars + const_vars:
        base_name_regex += r"(?!" + no_kwd + r"\b)"
    base_name_regex += r"(?![0-9])\w+\b"
    base_name = (
        regex_item(base_name_regex)
        | backslash.suppress() + any_keyword_in(reserved_vars)
    )

    name = Forward()
    dotted_name = condense(name + ZeroOrMore(dot + name))
    must_be_dotted_name = condense(name + OneOrMore(dot + name))

    integer = combine(Word(nums) + ZeroOrMore(underscore.suppress() + Word(nums)))
    binint = combine(Word("01") + ZeroOrMore(underscore.suppress() + Word("01")))
    octint = combine(Word("01234567") + ZeroOrMore(underscore.suppress() + Word("01234567")))
    hexint = combine(Word(hexnums) + ZeroOrMore(underscore.suppress() + Word(hexnums)))

    imag_j = CaselessLiteral("j") | fixto(CaselessLiteral("i"), "j")
    basenum = combine(
        integer + dot + Optional(integer)
        | Optional(integer) + dot + integer,
    ) | integer
    sci_e = combine(CaselessLiteral("e") + Optional(plus | neg_minus))
    numitem = ~(Literal("0") + Word(nums + "_", exact=1)) + combine(basenum + Optional(sci_e + integer))
    imag_num = combine(numitem + imag_j)
    bin_num = combine(CaselessLiteral("0b") + Optional(underscore.suppress()) + binint)
    oct_num = combine(CaselessLiteral("0o") + Optional(underscore.suppress()) + octint)
    hex_num = combine(CaselessLiteral("0x") + Optional(underscore.suppress()) + hexint)
    number = addspace(
        (
            bin_num
            | oct_num
            | hex_num
            | imag_num
            | numitem
        )
        + Optional(condense(dot + name)),
    )

    moduledoc_item = Forward()
    unwrap = Literal(unwrapper)
    comment = Forward()
    comment_ref = combine(pound + integer + unwrap)
    string_item = (
        combine(Literal(strwrapper) + integer + unwrap)
        | invalid_syntax(("\u201c", "\u201d", "\u2018", "\u2019"), "invalid unicode quotation mark; strings must use \" or '", greedy=True)
    )
    passthrough = combine(backslash + integer + unwrap)
    passthrough_block = combine(fixto(dubbackslash, "\\") + integer + unwrap)

    endline = Forward()
    endline_ref = condense(OneOrMore(Literal("\n")))
    lineitem = combine(Optional(comment) + endline)
    newline = condense(OneOrMore(lineitem))
    end_simple_stmt_item = FollowedBy(semicolon | newline)

    start_marker = StringStart()
    moduledoc_marker = condense(ZeroOrMore(lineitem) - Optional(moduledoc_item))
    end_marker = StringEnd()
    indent = Literal(openindent)
    dedent = Literal(closeindent)

    u_string = Forward()
    f_string = Forward()
    bit_b = Optional(CaselessLiteral("b"))
    raw_r = Optional(CaselessLiteral("r"))
    b_string = combine((bit_b + raw_r | raw_r + bit_b) + string_item)
    unicode_u = CaselessLiteral("u").suppress()
    u_string_ref = combine((unicode_u + raw_r | raw_r + unicode_u) + string_item)
    format_f = CaselessLiteral("f").suppress()
    f_string_ref = combine((format_f + raw_r | raw_r + format_f) + string_item)
    string = trace(b_string | u_string | f_string)
    moduledoc = string + newline
    docstring = condense(moduledoc)

    pipe_augassign = (
        combine(pipe + equals)
        | combine(star_pipe + equals)
        | combine(dubstar_pipe + equals)
        | combine(back_pipe + equals)
        | combine(back_star_pipe + equals)
        | combine(back_dubstar_pipe + equals)
        | combine(none_pipe + equals)
        | combine(none_star_pipe + equals)
        | combine(none_dubstar_pipe + equals)
    )
    augassign = (
        pipe_augassign
        | combine(comp_pipe + equals)
        | combine(dotdot + equals)
        | combine(comp_back_pipe + equals)
        | combine(comp_star_pipe + equals)
        | combine(comp_back_star_pipe + equals)
        | combine(comp_dubstar_pipe + equals)
        | combine(comp_back_dubstar_pipe + equals)
        | combine(unsafe_dubcolon + equals)
        | combine(div_dubslash + equals)
        | combine(div_slash + equals)
        | combine(exp_dubstar + equals)
        | combine(mul_star + equals)
        | combine(plus + equals)
        | combine(sub_minus + equals)
        | combine(percent + equals)
        | combine(amp + equals)
        | combine(bar + equals)
        | combine(caret + equals)
        | combine(lshift + equals)
        | combine(rshift + equals)
        | combine(matrix_at + equals)
        | combine(dubquestion + equals)
    )

    comp_op = (
        le | ge | ne | lt | gt | eq
        | addspace(keyword("not") + keyword("in"))
        | keyword("in")
        | addspace(keyword("is") + keyword("not"))
        | keyword("is")
    )

    expr = Forward()
    star_expr = Forward()
    dubstar_expr = Forward()
    comp_for = Forward()
    test_no_cond = Forward()
    namedexpr_test = Forward()
    # for namedexpr locations only supported in Python 3.10
    new_namedexpr_test = Forward()

    testlist = trace(itemlist(test, comma, suppress_trailing=False))
    testlist_has_comma = trace(addspace(OneOrMore(condense(test + comma)) + Optional(test)))
    new_namedexpr_testlist_has_comma = trace(addspace(OneOrMore(condense(new_namedexpr_test + comma)) + Optional(test)))

    testlist_star_expr = Forward()
    testlist_star_expr_ref = tokenlist(Group(test) | star_expr, comma, suppress=False)
    testlist_star_namedexpr = Forward()
    testlist_star_namedexpr_tokens = tokenlist(Group(namedexpr_test) | star_expr, comma, suppress=False)

    yield_from = Forward()
    dict_comp = Forward()
    dict_literal = Forward()
    yield_classic = addspace(keyword("yield") + Optional(testlist))
    yield_from_ref = keyword("yield").suppress() + keyword("from").suppress() + test
    yield_expr = yield_from | yield_classic
    dict_comp_ref = lbrace.suppress() + (
        test + colon.suppress() + test + comp_for
        | invalid_syntax(dubstar_expr + comp_for, "dict unpacking cannot be used in dict comprehension")
    ) + rbrace.suppress()
    dict_literal_ref = (
        lbrace.suppress()
        + Optional(
            tokenlist(
                Group(addspace(condense(test + colon) + test)) | dubstar_expr,
                comma,
            ),
        )
        + rbrace.suppress()
    )
    test_expr = yield_expr | testlist_star_expr

    op_item = (
        # must go dubstar then star then no star
        fixto(dubstar_pipe, "_coconut_dubstar_pipe")
        | fixto(back_dubstar_pipe, "_coconut_back_dubstar_pipe")
        | fixto(none_dubstar_pipe, "_coconut_none_dubstar_pipe")
        | fixto(star_pipe, "_coconut_star_pipe")
        | fixto(back_star_pipe, "_coconut_back_star_pipe")
        | fixto(none_star_pipe, "_coconut_none_star_pipe")
        | fixto(pipe, "_coconut_pipe")
        | fixto(back_pipe, "_coconut_back_pipe")
        | fixto(none_pipe, "_coconut_none_pipe")

        # must go dubstar then star then no star
        | fixto(comp_dubstar_pipe, "_coconut_forward_dubstar_compose")
        | fixto(comp_back_dubstar_pipe, "_coconut_back_dubstar_compose")
        | fixto(comp_star_pipe, "_coconut_forward_star_compose")
        | fixto(comp_back_star_pipe, "_coconut_back_star_compose")
        | fixto(comp_pipe, "_coconut_forward_compose")
        | fixto(dotdot | comp_back_pipe, "_coconut_back_compose")

        | fixto(keyword("assert"), "_coconut_assert")
        | fixto(keyword("and"), "_coconut_bool_and")
        | fixto(keyword("or"), "_coconut_bool_or")
        | fixto(dubquestion, "_coconut_none_coalesce")
        | fixto(minus, "_coconut_minus")
        | fixto(dot, "_coconut.getattr")
        | fixto(unsafe_dubcolon, "_coconut.itertools.chain")
        | fixto(dollar + lbrack + rbrack, "_coconut_igetitem")
        | fixto(dollar, "_coconut.functools.partial")
        | fixto(exp_dubstar, "_coconut.operator.pow")
        | fixto(mul_star, "_coconut.operator.mul")
        | fixto(div_dubslash, "_coconut.operator.floordiv")
        | fixto(div_slash, "_coconut.operator.truediv")
        | fixto(percent, "_coconut.operator.mod")
        | fixto(plus, "_coconut.operator.add")
        | fixto(amp, "_coconut.operator.and_")
        | fixto(caret, "_coconut.operator.xor")
        | fixto(unsafe_bar, "_coconut.operator.or_")
        | fixto(lshift, "_coconut.operator.lshift")
        | fixto(rshift, "_coconut.operator.rshift")
        | fixto(lt, "_coconut.operator.lt")
        | fixto(gt, "_coconut.operator.gt")
        | fixto(eq, "_coconut.operator.eq")
        | fixto(le, "_coconut.operator.le")
        | fixto(ge, "_coconut.operator.ge")
        | fixto(ne, "_coconut.operator.ne")
        | fixto(tilde, "_coconut.operator.inv")
        | fixto(matrix_at, "_coconut.operator.matmul")
        | fixto(keyword("not"), "_coconut.operator.not_")
        | fixto(keyword("is"), "_coconut.operator.is_")
        | fixto(keyword("in"), "_coconut.operator.contains")
    )

    typedef = Forward()
    typedef_default = Forward()
    unsafe_typedef_default = Forward()
    typedef_test = Forward()

    # we include (var)arg_comma to ensure the pattern matches the whole arg
    arg_comma = comma | fixto(FollowedBy(rparen), "")
    vararg_comma = arg_comma | fixto(FollowedBy(colon), "")
    typedef_ref = name + colon.suppress() + typedef_test + arg_comma
    default = condense(equals + test)
    unsafe_typedef_default_ref = name + colon.suppress() + typedef_test + Optional(default)
    typedef_default_ref = unsafe_typedef_default_ref + arg_comma
    tfpdef = typedef | condense(name + arg_comma)
    tfpdef_default = typedef_default | condense(name + Optional(default) + arg_comma)

    star_sep_arg = Forward()
    star_sep_arg_ref = condense(star + arg_comma)
    star_sep_vararg = Forward()
    star_sep_vararg_ref = condense(star + vararg_comma)

    slash_sep_arg = Forward()
    slash_sep_arg_ref = condense(slash + arg_comma)
    slash_sep_vararg = Forward()
    slash_sep_vararg_ref = condense(slash + vararg_comma)

    just_star = star + rparen
    just_slash = slash + rparen
    just_op = just_star | just_slash

    match = Forward()
    args_list = trace(
        ~just_op
        + addspace(
            ZeroOrMore(
                condense(
                    # everything here must end with arg_comma
                    (star | dubstar) + tfpdef
                    | star_sep_arg
                    | slash_sep_arg
                    | tfpdef_default,
                ),
            ),
        ),
    )
    parameters = condense(lparen + args_list + rparen)
    var_args_list = trace(
        ~just_op
        + addspace(
            ZeroOrMore(
                condense(
                    # everything here must end with vararg_comma
                    (star | dubstar) + name + vararg_comma
                    | star_sep_vararg
                    | slash_sep_vararg
                    | name + Optional(default) + vararg_comma,
                ),
            ),
        ),
    )
    match_args_list = trace(
        Group(
            Optional(
                tokenlist(
                    Group(
                        (star | dubstar) + match
                        | star  # not star_sep because pattern-matching can handle star separators on any Python version
                        | slash  # not slash_sep as above
                        | match + Optional(equals.suppress() + test),
                    ),
                    comma,
                ),
            ),
        ),
    )

    call_item = (
        dubstar + test
        | star + test
        | name + default
        | namedexpr_test
    )
    function_call_tokens = lparen.suppress() + (
        # everything here must end with rparen
        rparen.suppress()
        | tokenlist(Group(call_item), comma) + rparen.suppress()
        | Group(attach(addspace(test + comp_for), add_paren_handle)) + rparen.suppress()
        | Group(op_item) + rparen.suppress()
    )
    function_call = Forward()
    questionmark_call_tokens = Group(
        tokenlist(
            Group(
                questionmark
                | call_item,
            ),
            comma,
        ),
    )
    methodcaller_args = (
        itemlist(condense(call_item), comma)
        | op_item
    )

    slicetest = Optional(test_no_chain)
    sliceop = condense(unsafe_colon + slicetest)
    subscript = condense(slicetest + sliceop + Optional(sliceop)) | test
    subscriptlist = itemlist(subscript, comma, suppress_trailing=False) | new_namedexpr_test

    slicetestgroup = Optional(test_no_chain, default="")
    sliceopgroup = unsafe_colon.suppress() + slicetestgroup
    subscriptgroup = attach(slicetestgroup + sliceopgroup + Optional(sliceopgroup) | test, subscriptgroup_handle)
    subscriptgrouplist = itemlist(subscriptgroup, comma)

    comprehension_expr = addspace(
        namedexpr_test + comp_for
        | invalid_syntax(star_expr + comp_for, "iterable unpacking cannot be used in comprehension"),
    )
    paren_atom = condense(
        lparen + Optional(
            yield_expr
            | comprehension_expr
            | testlist_star_namedexpr,
        ) + rparen,
    )

    list_literal = Forward()
    list_literal_ref = lbrack.suppress() + testlist_star_namedexpr_tokens + rbrack.suppress()
    list_item = (
        condense(lbrack + Optional(comprehension_expr) + rbrack)
        | list_literal
    )

    op_atom = lparen.suppress() + op_item + rparen.suppress()
    keyword_atom = any_keyword_in(const_vars)
    string_atom = attach(OneOrMore(string), string_atom_handle)
    passthrough_atom = trace(addspace(OneOrMore(passthrough)))
    set_literal = Forward()
    set_letter_literal = Forward()
    set_s = fixto(CaselessLiteral("s"), "s")
    set_f = fixto(CaselessLiteral("f"), "f")
    set_letter = set_s | set_f
    setmaker = Group(
        addspace(new_namedexpr_test + comp_for)("comp")
        | new_namedexpr_testlist_has_comma("list")
        | new_namedexpr_test("test"),
    )
    set_literal_ref = lbrace.suppress() + setmaker + rbrace.suppress()
    set_letter_literal_ref = set_letter + lbrace.suppress() + Optional(setmaker) + rbrace.suppress()
    lazy_items = Optional(tokenlist(test, comma))
    lazy_list = attach(lbanana.suppress() + lazy_items + rbanana.suppress(), lazy_list_handle)

    const_atom = (
        keyword_atom
        | number
        | string_atom
    )
    known_atom = trace(
        const_atom
        | list_item
        | dict_comp
        | dict_literal
        | set_literal
        | set_letter_literal
        | lazy_list
        | ellipsis,
    )
    atom = (
        # known_atom must come before name to properly parse string prefixes
        known_atom
        | name
        | paren_atom
        | op_atom
        | passthrough_atom
    )

    typedef_atom = Forward()
    typedef_or_expr = Forward()

    simple_trailer = (
        condense(dot + name)
        | condense(lbrack + subscriptlist + rbrack)
    )
    call_trailer = (
        function_call
        | invalid_syntax(dollar + questionmark, "'?' must come before '$' in None-coalescing partial application")
        | Group(dollar + ~lparen + ~lbrack)  # keep $ for item_handle
    )
    known_trailer = typedef_atom | (
        Group(condense(dollar + lbrack) + subscriptgroup + rbrack.suppress())  # $[
        | Group(condense(dollar + lbrack + rbrack))  # $[]
        | Group(condense(lbrack + rbrack))  # []
        | Group(dot + ~name + ~lbrack)  # .
        | Group(questionmark)  # ?
    ) + ~questionmark
    partial_trailer = (
        Group(fixto(dollar, "$(") + function_call)  # $(
        | Group(fixto(dollar + lparen, "$(?") + questionmark_call_tokens) + rparen.suppress()  # $(?
    ) + ~questionmark
    partial_trailer_tokens = Group(dollar.suppress() + function_call_tokens)

    no_call_trailer = simple_trailer | partial_trailer | known_trailer

    no_partial_complex_trailer = call_trailer | known_trailer
    no_partial_trailer = simple_trailer | no_partial_complex_trailer

    complex_trailer = no_partial_complex_trailer | partial_trailer
    trailer = simple_trailer | complex_trailer

    attrgetter_atom_tokens = dot.suppress() + dotted_name + Optional(
        lparen + Optional(methodcaller_args) + rparen.suppress(),
    )
    attrgetter_atom = attach(attrgetter_atom_tokens, attrgetter_atom_handle)
    itemgetter_atom_tokens = dot.suppress() + OneOrMore(condense(Optional(dollar) + lbrack) + subscriptgrouplist + rbrack.suppress())
    itemgetter_atom = attach(itemgetter_atom_tokens, itemgetter_handle)
    implicit_partial_atom = attrgetter_atom | itemgetter_atom

    trailer_atom = Forward()
    trailer_atom_ref = atom + ZeroOrMore(trailer)
    atom_item = (
        trailer_atom
        | implicit_partial_atom
    )

    no_partial_trailer_atom = Forward()
    no_partial_trailer_atom_ref = atom + ZeroOrMore(no_partial_trailer)
    partial_atom_tokens = no_partial_trailer_atom + partial_trailer_tokens

    simple_assign = Forward()
    simple_assign_ref = maybeparens(
        lparen,
        (name | passthrough_atom)
        + ZeroOrMore(ZeroOrMore(complex_trailer) + OneOrMore(simple_trailer)),
        rparen,
    )
    simple_assignlist = maybeparens(lparen, itemlist(simple_assign, comma, suppress_trailing=False), rparen)

    assignlist = Forward()
    star_assign_item = Forward()
    base_assign_item = condense(simple_assign | lparen + assignlist + rparen | lbrack + assignlist + rbrack)
    star_assign_item_ref = condense(star + base_assign_item)
    assign_item = star_assign_item | base_assign_item
    assignlist <<= itemlist(assign_item, comma, suppress_trailing=False)

    typed_assign_stmt = Forward()
    typed_assign_stmt_ref = simple_assign + colon.suppress() + typedef_test + Optional(equals.suppress() + test_expr)
    basic_stmt = trace(addspace(ZeroOrMore(assignlist + equals) + test_expr))

    compose_item = attach(tokenlist(atom_item, dotdot, allow_trailing=False), compose_item_handle)

    impl_call_arg = disallow_keywords(reserved_vars) + (
        keyword_atom
        | number
        | dotted_name
    )
    impl_call = attach(
        disallow_keywords(reserved_vars)
        + compose_item
        + OneOrMore(impl_call_arg),
        impl_call_item_handle,
    )
    impl_call_item = (
        compose_item + ~impl_call_arg
        | impl_call
    )

    await_item = Forward()
    await_item_ref = await_kwd.suppress() + impl_call_item
    power_item = await_item | impl_call_item

    factor = Forward()
    unary = plus | neg_minus | tilde
    power = trace(condense(power_item + Optional(exp_dubstar + factor)))
    factor <<= condense(ZeroOrMore(unary) + power)

    mulop = mul_star | div_dubslash | div_slash | percent | matrix_at
    addop = plus | sub_minus
    shift = lshift | rshift

    term = exprlist(factor, mulop)
    arith_expr = exprlist(term, addop)
    shift_expr = exprlist(arith_expr, shift)
    and_expr = exprlist(shift_expr, amp)
    xor_expr = exprlist(and_expr, caret)
    or_expr = typedef_or_expr | exprlist(xor_expr, bar)

    chain_expr = attach(tokenlist(or_expr, dubcolon, allow_trailing=False), chain_handle)

    lambdef = Forward()

    infix_op = condense(backtick.suppress() + test_no_infix + backtick.suppress())

    infix_expr = Forward()
    infix_item = attach(
        Group(Optional(chain_expr))
        + OneOrMore(
            infix_op + Group(Optional(lambdef | chain_expr)),
        ),
        infix_handle,
    )
    infix_expr <<= (
        chain_expr + ~backtick
        | infix_item
    )

    none_coalesce_expr = attach(tokenlist(infix_expr, dubquestion, allow_trailing=False), none_coalesce_handle)

    comp_pipe_op = (
        comp_pipe
        | comp_star_pipe
        | comp_back_pipe
        | comp_back_star_pipe
        | comp_dubstar_pipe
        | comp_back_dubstar_pipe
    )
    comp_pipe_item = attach(
        OneOrMore(none_coalesce_expr + comp_pipe_op) + (lambdef | none_coalesce_expr),
        comp_pipe_handle,
    )
    comp_pipe_expr = (
        comp_pipe_item
        | none_coalesce_expr
    )

    pipe_op = (
        pipe
        | star_pipe
        | dubstar_pipe
        | back_pipe
        | back_star_pipe
        | back_dubstar_pipe
        | none_pipe
        | none_star_pipe
        | none_dubstar_pipe
    )
    pipe_item = (
        # we need the pipe_op since any of the atoms could otherwise be the start of an expression
        Group(attrgetter_atom_tokens("attrgetter")) + pipe_op
        | Group(itemgetter_atom_tokens("itemgetter")) + pipe_op
        | Group(partial_atom_tokens("partial")) + pipe_op
        | Group(comp_pipe_expr("expr")) + pipe_op
    )
    pipe_augassign_item = trace(
        # should match pipe_item but with pipe_op -> end_simple_stmt_item and no expr
        Group(attrgetter_atom_tokens("attrgetter")) + end_simple_stmt_item
        | Group(itemgetter_atom_tokens("itemgetter")) + end_simple_stmt_item
        | Group(partial_atom_tokens("partial")) + end_simple_stmt_item,
    )
    last_pipe_item = Group(
        lambdef("expr")
        | longest(
            # we need longest here because there's no following pipe_op we can use as above
            attrgetter_atom_tokens("attrgetter"),
            itemgetter_atom_tokens("itemgetter"),
            partial_atom_tokens("partial"),
            comp_pipe_expr("expr"),
        ),
    )
    normal_pipe_expr = Forward()
    normal_pipe_expr_tokens = OneOrMore(pipe_item) + last_pipe_item

    pipe_expr = (
        comp_pipe_expr + ~pipe_op
        | normal_pipe_expr
    )

    expr <<= pipe_expr

    star_expr <<= Group(star + expr)
    dubstar_expr <<= Group(dubstar + expr)

    comparison = exprlist(expr, comp_op)
    not_test = addspace(ZeroOrMore(keyword("not")) + comparison)
    and_test = exprlist(not_test, keyword("and"))
    test_item = trace(exprlist(and_test, keyword("or")))

    simple_stmt_item = Forward()
    unsafe_simple_stmt_item = Forward()
    simple_stmt = Forward()
    stmt = Forward()
    suite = Forward()
    nocolon_suite = Forward()
    base_suite = Forward()

    classic_lambdef = Forward()
    classic_lambdef_params = maybeparens(lparen, var_args_list, rparen)
    new_lambdef_params = lparen.suppress() + var_args_list + rparen.suppress() | name
    classic_lambdef_ref = addspace(lambda_kwd + condense(classic_lambdef_params + colon))
    new_lambdef = attach(new_lambdef_params + arrow.suppress(), lambdef_handle)
    implicit_lambdef = fixto(arrow, "lambda _=None:")
    lambdef_base = classic_lambdef | new_lambdef | implicit_lambdef

    stmt_lambdef = Forward()
    match_guard = Optional(keyword("if").suppress() + namedexpr_test)
    closing_stmt = longest(testlist("tests"), unsafe_simple_stmt_item)
    stmt_lambdef_match_params = Group(lparen.suppress() + match_args_list + match_guard + rparen.suppress())
    stmt_lambdef_params = Optional(
        attach(name, add_paren_handle)
        | parameters
        | stmt_lambdef_match_params,
        default="(_=None)",
    )
    stmt_lambdef_body = (
        Group(OneOrMore(simple_stmt_item + semicolon.suppress())) + Optional(closing_stmt)
        | Group(ZeroOrMore(simple_stmt_item + semicolon.suppress())) + closing_stmt
    )
    general_stmt_lambdef = (
        keyword("def").suppress()
        + stmt_lambdef_params
        + arrow.suppress()
        + stmt_lambdef_body
    )
    match_stmt_lambdef = (
        match_kwd.suppress()
        + keyword("def").suppress()
        + stmt_lambdef_match_params
        + arrow.suppress()
        + stmt_lambdef_body
    )
    stmt_lambdef_ref = general_stmt_lambdef | match_stmt_lambdef

    lambdef <<= addspace(lambdef_base + test) | stmt_lambdef
    lambdef_no_cond = trace(addspace(lambdef_base + test_no_cond))

    typedef_callable_params = (
        lparen.suppress() + Optional(testlist, default="") + rparen.suppress()
        | Optional(atom_item)
    )
    unsafe_typedef_callable = attach(typedef_callable_params + arrow.suppress() + typedef_test, typedef_callable_handle)
    unsafe_typedef_atom = (  # use special type signifier for item_handle
        Group(fixto(lbrack + rbrack, "type:[]"))
        | Group(fixto(dollar + lbrack + rbrack, "type:$[]"))
        | Group(fixto(questionmark + ~questionmark, "type:?"))
    )
    unsafe_typedef_or_expr = Forward()
    unsafe_typedef_or_expr_ref = tokenlist(xor_expr, bar, allow_trailing=False, at_least_two=True)

    _typedef_test, typedef_callable, _typedef_atom, _typedef_or_expr = disable_outside(
        test,
        unsafe_typedef_callable,
        unsafe_typedef_atom,
        unsafe_typedef_or_expr,
    )
    typedef_test <<= _typedef_test
    typedef_atom <<= _typedef_atom
    typedef_or_expr <<= _typedef_or_expr

    alt_ternary_expr = attach(keyword("if").suppress() + test_item + then_kwd.suppress() + test_item + keyword("else").suppress() + test, alt_ternary_handle)
    test <<= (
        typedef_callable
        | lambdef
        | alt_ternary_expr
        | addspace(test_item + Optional(keyword("if") + test_item + keyword("else") + test))  # must come last since it includes plain test_item
    )
    test_no_cond <<= lambdef_no_cond | test_item

    namedexpr = Forward()
    namedexpr_ref = addspace(name + colon_eq + test)
    namedexpr_test <<= (
        test + ~colon_eq
        | namedexpr
    )

    new_namedexpr = Forward()
    new_namedexpr_ref = namedexpr_ref
    new_namedexpr_test <<= (
        test + ~colon_eq
        | new_namedexpr
    )

    async_comp_for = Forward()
    classdef = Forward()
    classlist = Group(
        Optional(function_call_tokens)
        + ~equals,  # don't match class destructuring assignment
    )
    class_suite = suite | attach(newline, class_suite_handle)
    classdef_ref = keyword("class").suppress() + name + classlist + class_suite
    comp_iter = Forward()
    comp_it_item = (
        invalid_syntax(maybeparens(lparen, namedexpr, rparen), "PEP 572 disallows assignment expressions in comprehension iterable expressions")
        | test_item
    )
    base_comp_for = addspace(keyword("for") + assignlist + keyword("in") + comp_it_item + Optional(comp_iter))
    async_comp_for_ref = addspace(async_kwd + base_comp_for)
    comp_for <<= async_comp_for | base_comp_for
    comp_if = addspace(keyword("if") + test_no_cond + Optional(comp_iter))
    comp_iter <<= comp_for | comp_if

    # add parens to support return x, *y on 3.5 - 3.7, which supports return (x, *y) but not return x, *y
    return_testlist = attach(testlist_star_expr, add_paren_handle)
    return_stmt = addspace(keyword("return") - Optional(return_testlist))

    complex_raise_stmt = Forward()
    pass_stmt = keyword("pass")
    break_stmt = keyword("break")
    continue_stmt = keyword("continue")
    simple_raise_stmt = addspace(keyword("raise") + Optional(test))
    complex_raise_stmt_ref = keyword("raise").suppress() + test + keyword("from").suppress() - test
    raise_stmt = complex_raise_stmt | simple_raise_stmt
    flow_stmt = (
        return_stmt
        | raise_stmt
        | break_stmt
        | yield_expr
        | continue_stmt
    )

    dotted_as_name = Group(dotted_name - Optional(keyword("as").suppress() - name))
    import_as_name = Group(name - Optional(keyword("as").suppress() - name))
    import_names = Group(maybeparens(lparen, tokenlist(dotted_as_name, comma), rparen))
    from_import_names = Group(maybeparens(lparen, tokenlist(import_as_name, comma), rparen))
    basic_import = keyword("import").suppress() - (import_names | Group(star))
    from_import = (
        keyword("from").suppress()
        - condense(ZeroOrMore(unsafe_dot) + dotted_name | OneOrMore(unsafe_dot) | star)
        - keyword("import").suppress() - (from_import_names | Group(star))
    )
    import_stmt = Forward()
    import_stmt_ref = from_import | basic_import

    augassign_stmt = Forward()
    augassign_rhs = (
        labeled_group(pipe_augassign + pipe_augassign_item, "pipe")
        | labeled_group(augassign + test_expr, "simple")
    )
    augassign_stmt_ref = simple_assign + augassign_rhs

    simple_kwd_assign = attach(
        maybeparens(lparen, itemlist(name, comma), rparen) + Optional(equals.suppress() - test_expr),
        simple_kwd_assign_handle,
    )
    kwd_augassign = Forward()
    kwd_augassign_ref = name + augassign_rhs
    kwd_assign = (
        kwd_augassign
        | simple_kwd_assign
    )
    global_stmt = addspace(keyword("global") - kwd_assign)
    nonlocal_stmt = Forward()
    nonlocal_stmt_ref = addspace(keyword("nonlocal") - kwd_assign)

    del_stmt = addspace(keyword("del") - simple_assignlist)

    matchlist_tuple_items = (
        match + OneOrMore(comma.suppress() + match) + Optional(comma.suppress())
        | match + comma.suppress()
    )
    matchlist_tuple = Group(Optional(matchlist_tuple_items))
    matchlist_list = Group(Optional(tokenlist(match, comma)))
    matchlist_star = (
        Optional(Group(OneOrMore(match + comma.suppress())))
        + star.suppress() + name
        + Optional(Group(OneOrMore(comma.suppress() + match)))
        + Optional(comma.suppress())
    )
    matchlist_data_item = Group(Optional(star | name + equals) + match)
    matchlist_data = Group(Optional(tokenlist(matchlist_data_item, comma)))

    match_check_equals = Forward()
    match_check_equals_ref = equals

    match_dotted_name_const = Forward()
    complex_number = condense(Optional(neg_minus) + number + (plus | sub_minus) + Optional(neg_minus) + imag_num)
    match_const = condense(
        (match_check_equals | eq).suppress() + atom_item
        | complex_number
        | Optional(neg_minus) + const_atom
        | match_dotted_name_const,
    )
    match_string = (
        (string + plus.suppress() + name + plus.suppress() + string)("mstring")
        | (string + plus.suppress() + name)("string")
        | (name + plus.suppress() + string)("rstring")
    )
    matchlist_set = Group(Optional(tokenlist(match_const, comma)))
    match_pair = Group(match_const + colon.suppress() + match)
    matchlist_dict = Group(Optional(tokenlist(match_pair, comma)))
    match_list = lbrack + matchlist_list + rbrack.suppress()
    match_tuple = lparen + matchlist_tuple + rparen.suppress()
    match_lazy = lbanana + matchlist_list + rbanana.suppress()
    series_match = (
        (match_list + plus.suppress() + name + plus.suppress() + match_list)("mseries")
        | (match_tuple + plus.suppress() + name + plus.suppress() + match_tuple)("mseries")
        | ((match_list | match_tuple) + Optional(plus.suppress() + name))("series")
        | (name + plus.suppress() + (match_list | match_tuple))("rseries")
    )
    iter_match = (
        ((match_list | match_tuple | match_lazy) + unsafe_dubcolon.suppress() + name)
        | match_lazy
    )("iter")
    star_match = (
        lbrack.suppress() + matchlist_star + rbrack.suppress()
        | lparen.suppress() + matchlist_star + rparen.suppress()
    )("star")
    base_match = trace(
        Group(
            (atom_item + arrow.suppress() + match)("view")
            | match_string
            | match_const("const")
            | (lbrace.suppress() + matchlist_dict + Optional(dubstar.suppress() + (name | condense(lbrace + rbrace))) + rbrace.suppress())("dict")
            | (Optional(set_s.suppress()) + lbrace.suppress() + matchlist_set + rbrace.suppress())("set")
            | iter_match
            | series_match
            | star_match
            | (lparen.suppress() + match + rparen.suppress())("paren")
            | (data_kwd.suppress() + name + lparen.suppress() + matchlist_data + rparen.suppress())("data")
            | (keyword("class").suppress() + name + lparen.suppress() + matchlist_data + rparen.suppress())("class")
            | (name + lparen.suppress() + matchlist_data + rparen.suppress())("data_or_class")
            | Optional(keyword("as").suppress()) + name("var"),
        ),
    )

    matchlist_isinstance = base_match + OneOrMore(keyword("is") + atom_item)  # match_trailer expects unsuppressed isinstance
    isinstance_match = labeled_group(matchlist_isinstance, "trailer") | base_match

    matchlist_bar_or = isinstance_match + OneOrMore(bar.suppress() + isinstance_match)
    bar_or_match = labeled_group(matchlist_bar_or, "or") | isinstance_match

    matchlist_infix = bar_or_match + OneOrMore(infix_op + atom_item)
    infix_match = labeled_group(matchlist_infix, "infix") | bar_or_match

    matchlist_as = infix_match + OneOrMore(keyword("as") + name)  # match_trailer expects unsuppressed as
    as_match = labeled_group(matchlist_as, "trailer") | infix_match

    matchlist_and = as_match + OneOrMore(keyword("and").suppress() + as_match)
    and_match = labeled_group(matchlist_and, "and") | as_match

    matchlist_kwd_or = and_match + OneOrMore(keyword("or").suppress() + and_match)
    kwd_or_match = labeled_group(matchlist_kwd_or, "or") | and_match

    match <<= trace(kwd_or_match)

    many_match = (
        labeled_group(matchlist_star, "star")
        | labeled_group(matchlist_tuple_items, "implicit_tuple")
        | match
    )

    else_stmt = condense(keyword("else") - suite)
    full_suite = colon.suppress() - Group((newline.suppress() - indent.suppress() - OneOrMore(stmt) - dedent.suppress()) | simple_stmt)
    full_match = Forward()
    full_match_ref = (
        match_kwd.suppress()
        + many_match
        + addspace(Optional(keyword("not")) + keyword("in"))
        - testlist_star_namedexpr
        - match_guard
        - full_suite
    )
    match_stmt = trace(condense(full_match - Optional(else_stmt)))

    destructuring_stmt = Forward()
    base_destructuring_stmt = Optional(match_kwd.suppress()) + many_match + equals.suppress() + test_expr
    destructuring_stmt_ref, match_dotted_name_const_ref = disable_inside(base_destructuring_stmt, must_be_dotted_name)

    case_stmt = Forward()
    # both syntaxes here must be kept matching except for the keywords
    cases_kwd = fixto(case_kwd, "cases") | cases_kwd
    case_match_co_syntax = trace(
        Group(
            match_kwd.suppress()
            + stores_loc_item
            + many_match
            + Optional(keyword("if").suppress() + namedexpr_test)
            - full_suite,
        ),
    )
    case_stmt_co_syntax = (
        cases_kwd + testlist_star_namedexpr + colon.suppress() + newline.suppress()
        + indent.suppress() + Group(OneOrMore(case_match_co_syntax))
        + dedent.suppress() + Optional(keyword("else").suppress() + suite)
    )
    case_match_py_syntax = trace(
        Group(
            case_kwd.suppress()
            + stores_loc_item
            + many_match
            + Optional(keyword("if").suppress() + namedexpr_test)
            - full_suite,
        ),
    )
    case_stmt_py_syntax = (
        match_kwd + testlist_star_namedexpr + colon.suppress() + newline.suppress()
        + indent.suppress() + Group(OneOrMore(case_match_py_syntax))
        + dedent.suppress() + Optional(keyword("else").suppress() - suite)
    )
    case_stmt_ref = case_stmt_co_syntax | case_stmt_py_syntax

    assert_stmt = addspace(keyword("assert") - testlist)
    if_stmt = condense(
        addspace(keyword("if") + condense(namedexpr_test + suite))
        - ZeroOrMore(addspace(keyword("elif") - condense(namedexpr_test - suite)))
        - Optional(else_stmt),
    )
    while_stmt = addspace(keyword("while") - condense(namedexpr_test - suite - Optional(else_stmt)))
    for_stmt = addspace(keyword("for") - assignlist - keyword("in") - condense(testlist - suite - Optional(else_stmt)))

    exec_stmt = Forward()
    exec_stmt_ref = keyword("exec").suppress() + lparen.suppress() + test + Optional(
        comma.suppress() + test + Optional(
            comma.suppress() + test + Optional(
                comma.suppress(),
            ),
        ),
    ) + rparen.suppress()

    except_item = (
        testlist_has_comma("list")
        | test("test")
    ) - Optional(
        keyword("as").suppress() - name,
    )
    except_clause = attach(except_kwd + except_item, except_handle)
    except_star_clause = Forward()
    except_star_clause_ref = attach(except_star_kwd + except_item, except_handle)
    try_stmt = condense(
        keyword("try") - suite + (
            keyword("finally") - suite
            | (
                OneOrMore(except_clause - suite) - Optional(except_kwd - suite)
                | except_kwd - suite
                | OneOrMore(except_star_clause - suite)
            ) - Optional(else_stmt) - Optional(keyword("finally") - suite)
        ),
    )

    with_item = addspace(test + Optional(keyword("as") + base_assign_item))
    with_item_list = Group(maybeparens(lparen, tokenlist(with_item, comma), rparen))
    with_stmt_ref = keyword("with").suppress() - with_item_list - suite
    with_stmt = Forward()

    return_typedef = Forward()
    name_funcdef = trace(condense(dotted_name + parameters))
    op_tfpdef = unsafe_typedef_default | condense(name + Optional(default))
    op_funcdef_arg = name | condense(lparen.suppress() + op_tfpdef + rparen.suppress())
    op_funcdef_name = unsafe_backtick.suppress() + dotted_name + unsafe_backtick.suppress()
    op_funcdef = trace(
        attach(
            Group(Optional(op_funcdef_arg))
            + op_funcdef_name
            + Group(Optional(op_funcdef_arg)),
            op_funcdef_handle,
        ),
    )
    return_typedef_ref = arrow.suppress() + typedef_test
    end_func_colon = return_typedef + colon.suppress() | colon
    base_funcdef = op_funcdef | name_funcdef
    funcdef = trace(addspace(keyword("def") + condense(base_funcdef + end_func_colon + nocolon_suite)))

    name_match_funcdef = Forward()
    op_match_funcdef = Forward()
    op_match_funcdef_arg = Group(
        Optional(
            lparen.suppress()
            + Group(match + Optional(equals.suppress() + test))
            + rparen.suppress(),
        ),
    )
    name_match_funcdef_ref = keyword("def").suppress() + dotted_name + lparen.suppress() + match_args_list + match_guard + rparen.suppress()
    op_match_funcdef_ref = keyword("def").suppress() + op_match_funcdef_arg + op_funcdef_name + op_match_funcdef_arg + match_guard
    base_match_funcdef = trace(op_match_funcdef | name_match_funcdef)
    def_match_funcdef = trace(
        attach(
            base_match_funcdef
            + end_func_colon
            - (
                attach(simple_stmt, make_suite_handle)
                | (
                    newline.suppress()
                    - indent.suppress()
                    - Optional(docstring)
                    - attach(condense(OneOrMore(stmt)), make_suite_handle)
                    - dedent.suppress()
                )
            ),
            join_match_funcdef,
        ),
    )
    match_def_modifiers = trace(
        Optional(
            # we don't suppress addpattern so its presence can be detected later
            match_kwd.suppress() + Optional(addpattern_kwd)
            | addpattern_kwd + Optional(match_kwd.suppress()),
        ),
    )
    match_funcdef = addspace(match_def_modifiers + def_match_funcdef)

    where_stmt = attach(
        unsafe_simple_stmt_item
        + where_kwd.suppress()
        - full_suite,
        where_handle,
    )

    implicit_return = (
        invalid_syntax(return_stmt, "expected expression but got return statement")
        | attach(return_testlist, implicit_return_handle)
    )
    implicit_return_where = attach(
        implicit_return
        + where_kwd.suppress()
        - full_suite,
        where_handle,
    )
    implicit_return_stmt = (
        condense(implicit_return + newline)
        | implicit_return_where
    )
    math_funcdef_body = condense(ZeroOrMore(~(implicit_return_stmt + dedent) + stmt) - implicit_return_stmt)
    math_funcdef_suite = (
        attach(implicit_return_stmt, make_suite_handle)
        | condense(newline - indent - math_funcdef_body - dedent)
    )
    end_func_equals = return_typedef + equals.suppress() | fixto(equals, ":")
    math_funcdef = trace(
        attach(
            condense(addspace(keyword("def") + base_funcdef) + end_func_equals) - math_funcdef_suite,
            math_funcdef_handle,
        ),
    )
    math_match_funcdef = trace(
        addspace(
            match_def_modifiers
            + attach(
                base_match_funcdef
                + end_func_equals
                + (
                    attach(implicit_return_stmt, make_suite_handle)
                    | (
                        newline.suppress() - indent.suppress()
                        + Optional(docstring)
                        + attach(math_funcdef_body, make_suite_handle)
                        + dedent.suppress()
                    )
                ),
                join_match_funcdef,
            ),
        ),
    )

    async_stmt = Forward()
    async_stmt_ref = addspace(async_kwd + (with_stmt | for_stmt))

    async_funcdef = async_kwd.suppress() + (funcdef | math_funcdef)
    async_match_funcdef = trace(
        addspace(
            (
                # we don't suppress addpattern so its presence can be detected later
                match_kwd.suppress() + addpattern_kwd + async_kwd.suppress()
                | addpattern_kwd + match_kwd.suppress() + async_kwd.suppress()
                | match_kwd.suppress() + async_kwd.suppress() + Optional(addpattern_kwd)
                | addpattern_kwd + async_kwd.suppress() + Optional(match_kwd.suppress())
                | async_kwd.suppress() + match_def_modifiers
            ) + (def_match_funcdef | math_match_funcdef),
        ),
    )

    yield_normal_funcdef = keyword("yield").suppress() + funcdef
    yield_match_funcdef = trace(
        addspace(
            (
                # must match async_match_funcdef above with async_kwd -> keyword("yield")
                match_kwd.suppress() + addpattern_kwd + keyword("yield").suppress()
                | addpattern_kwd + match_kwd.suppress() + keyword("yield").suppress()
                | match_kwd.suppress() + keyword("yield").suppress() + Optional(addpattern_kwd)
                | addpattern_kwd + keyword("yield").suppress() + Optional(match_kwd.suppress())
                | keyword("yield").suppress() + match_def_modifiers
            ) + def_match_funcdef,
        ),
    )
    yield_funcdef = attach(yield_normal_funcdef | yield_match_funcdef, yield_funcdef_handle)

    datadef = Forward()
    data_args = Group(
        Optional(
            lparen.suppress() + ZeroOrMore(
                Group(
                    # everything here must end with arg_comma
                    (name + arg_comma.suppress())("name")
                    | (name + equals.suppress() + test + arg_comma.suppress())("default")
                    | (star.suppress() + name + arg_comma.suppress())("star")
                    | (name + colon.suppress() + typedef_test + equals.suppress() + test + arg_comma.suppress())("type default")
                    | (name + colon.suppress() + typedef_test + arg_comma.suppress())("type"),
                ),
            ) + rparen.suppress(),
        ),
    ) + Optional(keyword("from").suppress() + testlist)
    data_suite = Group(
        colon.suppress() - (
            (newline.suppress() + indent.suppress() + Optional(docstring) + Group(OneOrMore(stmt)) - dedent.suppress())("complex")
            | (newline.suppress() + indent.suppress() + docstring - dedent.suppress() | docstring)("docstring")
            | simple_stmt("simple")
        ) | newline("empty"),
    )
    datadef_ref = data_kwd.suppress() + name + data_args + data_suite

    match_datadef = Forward()
    match_data_args = lparen.suppress() + Group(
        match_args_list + match_guard,
    ) + rparen.suppress() + Optional(keyword("from").suppress() + testlist)
    match_datadef_ref = Optional(match_kwd.suppress()) + data_kwd.suppress() + name + match_data_args + data_suite

    simple_decorator = condense(dotted_name + Optional(function_call))("simple")
    complex_decorator = namedexpr_test("complex")
    decorators_ref = OneOrMore(at.suppress() - Group(longest(simple_decorator, complex_decorator)) - newline.suppress())
    decorators = Forward()

    decoratable_normal_funcdef_stmt = Forward()
    normal_funcdef_stmt = (
        funcdef
        | math_funcdef
        | math_match_funcdef
        | match_funcdef
        | yield_funcdef
    )
    decoratable_normal_funcdef_stmt_ref = Optional(decorators) + normal_funcdef_stmt

    decoratable_async_funcdef_stmt = Forward()
    async_funcdef_stmt = async_funcdef | async_match_funcdef
    decoratable_async_funcdef_stmt_ref = Optional(decorators) + async_funcdef_stmt

    decoratable_func_stmt = decoratable_normal_funcdef_stmt | decoratable_async_funcdef_stmt

    class_stmt = classdef | datadef | match_datadef
    decoratable_class_stmt = trace(condense(Optional(decorators) + class_stmt))

    passthrough_stmt = condense(passthrough_block - (base_suite | newline))

    simple_compound_stmt = trace(
        if_stmt
        | try_stmt
        | match_stmt
        | passthrough_stmt,
    )
    compound_stmt = trace(
        decoratable_class_stmt
        | decoratable_func_stmt
        | for_stmt
        | while_stmt
        | with_stmt
        | async_stmt
        | simple_compound_stmt
        | where_stmt,
    )
    endline_semicolon = Forward()
    endline_semicolon_ref = semicolon.suppress() + newline
    keyword_stmt = trace(
        flow_stmt
        | import_stmt
        | assert_stmt
        | pass_stmt
        | del_stmt
        | global_stmt
        | nonlocal_stmt
        | exec_stmt,
    )
    special_stmt = (
        keyword_stmt
        | augassign_stmt
        | typed_assign_stmt
    )
    unsafe_simple_stmt_item <<= special_stmt | longest(basic_stmt, destructuring_stmt)
    simple_stmt_item <<= (
        special_stmt
        | basic_stmt + end_simple_stmt_item
        | destructuring_stmt + end_simple_stmt_item
    )
    simple_stmt <<= condense(
        simple_stmt_item
        + ZeroOrMore(fixto(semicolon, "\n") + simple_stmt_item)
        + (newline | endline_semicolon),
    )
    stmt <<= final(
        compound_stmt
        | simple_stmt
        # must come at end due to ambiguity with destructuring
        | case_stmt,
    )
    base_suite <<= condense(newline + indent - OneOrMore(stmt) - dedent)
    simple_suite = attach(stmt, make_suite_handle)
    nocolon_suite <<= base_suite | simple_suite
    suite <<= condense(colon + nocolon_suite)
    line = trace(newline | stmt)

    single_input = trace(condense(Optional(line) - ZeroOrMore(newline)))
    file_input = trace(condense(moduledoc_marker - ZeroOrMore(line)))
    eval_input = trace(condense(testlist - ZeroOrMore(newline)))

    single_parser = start_marker - single_input - end_marker
    file_parser = start_marker - file_input - end_marker
    eval_parser = start_marker - eval_input - end_marker

# end: MAIN GRAMMAR
# -----------------------------------------------------------------------------------------------------------------------
# EXTRA GRAMMAR:
# -----------------------------------------------------------------------------------------------------------------------

    just_non_none_atom = start_marker + ~keyword("None") + known_atom + end_marker

    parens = originalTextFor(nestedExpr("(", ")"))
    brackets = originalTextFor(nestedExpr("[", "]"))
    braces = originalTextFor(nestedExpr("{", "}"))
    any_char = regex_item(r".", re.DOTALL)

    original_function_call_tokens = lparen.suppress() + (
        rparen.suppress()
        # we need to add parens here, since f(x for x in y) is fine but tail_call(f, x for x in y) is not
        | attach(originalTextFor(test + comp_for), add_paren_handle) + rparen.suppress()
        | originalTextFor(tokenlist(call_item, comma)) + rparen.suppress()
    )

    def get_tre_return_grammar(self, func_name):
        return (
            self.start_marker
            + keyword("return").suppress()
            + maybeparens(
                self.lparen,
                keyword(func_name, explicit_prefix=False).suppress()
                + self.original_function_call_tokens,
                self.rparen,
            ) + self.end_marker
        )

    tco_return = attach(
        start_marker
        + keyword("return").suppress()
        + maybeparens(
            lparen,
            disallow_keywords(untcoable_funcs, with_suffix=lparen)
            + condense(
                (base_name | parens | brackets | braces | string)
                + ZeroOrMore(
                    dot + base_name
                    | brackets
                    # don't match the last set of parentheses
                    | parens + ~end_marker + ~rparen,
                ),
            )
            + original_function_call_tokens,
            rparen,
        ) + end_marker,
        tco_return_handle,
        # this is the root in what it's used for, so might as well evaluate greedily
        greedy=True,
    )

    rest_of_arg = ZeroOrMore(parens | brackets | braces | ~comma + ~rparen + any_char)
    tfpdef_tokens = base_name - Optional(originalTextFor(colon - rest_of_arg))
    tfpdef_default_tokens = base_name - Optional(originalTextFor((equals | colon) - rest_of_arg))
    parameters_tokens = Group(
        Optional(
            tokenlist(
                Group(
                    dubstar - tfpdef_tokens
                    | star - Optional(tfpdef_tokens)
                    | slash
                    | tfpdef_default_tokens,
                ) + Optional(passthrough.suppress()),
                comma + Optional(passthrough),  # implicitly suppressed
            ),
        ),
    )

    dotted_base_name = condense(base_name + ZeroOrMore(dot + base_name))
    split_func = (
        start_marker
        - keyword("def").suppress()
        - dotted_base_name
        - lparen.suppress() - parameters_tokens - rparen.suppress()
    )

    stores_scope = (
        lambda_kwd
        # match comprehensions but not for loops
        | ~indent + ~dedent + any_char + keyword("for") + base_name + keyword("in")
    )

    just_a_string = start_marker + string + end_marker

    end_of_line = end_marker | Literal("\n") | pound

    unsafe_equals = Literal("=")

    kwd_err_msg = attach(any_keyword_in(keyword_vars), kwd_err_msg_handle)
    parse_err_msg = start_marker + (
        fixto(end_marker, "misplaced newline (maybe missing ':')")
        | fixto(Optional(keyword("if") + skip_to_in_line(unsafe_equals)) + equals, "misplaced assignment (maybe should be '==')")
        | kwd_err_msg
    ) def lazy_list_handle(loc, tokens):
    """Process lazy lists."""
    if len(tokens) == 0:
        return "_coconut_reiterable(())"
    else:
        lambda_exprs = "lambda: " + ", lambda: ".join(tokens)
        # := changes meaning inside lambdas, so we must disallow it when wrapping
        #  user expressions in lambdas (and naive string analysis is safe here)
        if ":=" in lambda_exprs:
            raise CoconutDeferredSyntaxError("illegal assignment expression in lazy list or chain expression", loc)
        return "_coconut_reiterable({func_var}() for {func_var} in ({lambdas}{tuple_comma}))".format(
            func_var=func_var,
            lambdas=lambda_exprs,
            tuple_comma="," if len(tokens) == 1 else "",
        ) def get_infix_items(tokens, callback=infix_error):
    """Perform infix token processing.

    Takes a callback that (takes infix tokens and returns a string) to handle inner infix calls.
    """
    internal_assert(len(tokens) >= 3, "invalid infix tokens", tokens)
    (arg1, func, arg2), tokens = tokens[:3], tokens[3:]
    args = list(arg1) + list(arg2)
    while tokens:
        args = [callback([args, func, []])]
        (func, newarg), tokens = tokens[:2], tokens[2:]
        args += list(newarg)
    return func, args def pipe_info(op):
    """Returns (direction, stars, None-aware) where direction is 'forwards' or 'backwards'.
    Works with normal pipe operators and composition pipe operators."""
    none_aware = "?" in op
    stars = op.count("*")
    if not 0 <= stars <= 2:
        raise CoconutInternalException("invalid stars in pipe operator", op)
    if ">" in op:
        direction = "forwards"
    elif "<" in op:
        direction = "backwards"
    else:
        raise CoconutInternalException("invalid direction in pipe operator", op)
    return direction, stars, none_aware def attrgetter_atom_split(tokens):
    """Split attrgetter_atom_tokens into (attr_or_method_name, method_args_or_none_if_attr)."""
    if len(tokens) == 1:  # .attr
        return tokens[0], None
    elif len(tokens) >= 2 and tokens[1] == "(":  # .method(...
        if len(tokens) == 2:  # .method()
            return tokens[0], ""
        elif len(tokens) == 3:  # .method(args)
            return tokens[0], tokens[2]
        else:
            raise CoconutInternalException("invalid methodcaller literal tokens", tokens)
    else:
        raise CoconutInternalException("invalid attrgetter literal tokens", tokens) def attrgetter_atom_handle(loc, tokens):
    """Process attrgetter literals."""
    name, args = attrgetter_atom_split(tokens)
    if args is None:
        return '_coconut.operator.attrgetter("' + name + '")'
    elif "." in name:
        attr, method = name.rsplit(".", 1)
        return '_coconut_forward_compose(_coconut.operator.attrgetter("{attr}"), {methodcaller})'.format(
            attr=attr,
            methodcaller=attrgetter_atom_handle(loc, [method, "(", args]),
        )
    elif args == "":
        return '_coconut.operator.methodcaller("' + name + '")'
    else:
        return '_coconut.operator.methodcaller("' + name + '", ' + args + ")" def itemgetter_handle(tokens):
    """Process implicit itemgetter partials."""
    if len(tokens) == 2:
        op, args = tokens
        if op == "[":
            return "_coconut.operator.itemgetter((" + args + "))"
        elif op == "$[":
            return "_coconut.functools.partial(_coconut_igetitem, index=(" + args + "))"
        else:
            raise CoconutInternalException("invalid implicit itemgetter type", op)
    elif len(tokens) > 2:
        internal_assert(len(tokens) % 2 == 0, "invalid itemgetter composition tokens", tokens)
        itemgetters = []
        for i in range(0, len(tokens), 2):
            itemgetters.append(itemgetter_handle(tokens[i:i + 2]))
        return "_coconut_forward_compose(" + ", ".join(itemgetters) + ")"
    else:
        raise CoconutInternalException("invalid implicit itemgetter tokens", tokens) def evaluate_tokens(tokens, **kwargs):
    def __new__(cls, action, original, loc, tokens, ignore_no_tokens=False, ignore_one_token=False, greedy=False):
    def name(self):
    def evaluate(self):
    def __repr__(self):
    def _combine(self, original, loc, tokens):
    def postParse(self, original, loc, tokens):
def add_action(item, action):
def attach(item, action, ignore_no_tokens=None, ignore_one_token=None, ignore_tokens=None, **kwargs):
def final_evaluate_tokens(tokens):
def final(item):
def unpack(tokens):
def parse_context(inner_parse):
def parse(grammar, text, inner=False):
def try_parse(grammar, text, inner=False):
def all_matches(grammar, text, inner=False):
def match_in(grammar, text, inner=False):
def get_target_info(target):
def get_vers_for_target(target):
def get_target_info_smart(target, mode="lowest"):
    def __init__(self, item, wrapper):
    def _wrapper_name(self):
    def parseImpl(self, instring, loc, *args, **kwargs):
def disable_inside(item, *elems, **kwargs):
    def manage_item(self, instring, loc):
    def manage_elem(self, instring, loc):
def disable_outside(item, *elems):
def labeled_group(item, label):
def invalid_syntax(item, msg, **kwargs):
    def invalid_syntax_handle(loc, tokens):
def multi_index_lookup(iterable, item, indexable_types, default=None):
def append_it(iterator, last_val):
def join_args(*arglists):
def paren_join(items, sep):
def skip_to_in_line(item):
def longest(*args):
def addskip(skips, skip):
def count_end(teststr, testchar):
def paren_change(inputstring, opens=opens, closes=closes):
def ind_change(inputstring):
def compile_regex(regex, options=None):
def regex_item(regex, options=None):
def fixto(item, output):
def addspace(item):
def condense(item):
def maybeparens(lparen, item, rparen, prefer_parens=False):
def tokenlist(item, sep, suppress=True, allow_trailing=True, at_least_two=False):
def add_list_spacing(tokens):
def itemlist(item, sep, suppress_trailing=True):
def exprlist(expr, op):
def stores_loc_action(loc, tokens):
def disallow_keywords(kwds, with_suffix=None):
def any_keyword_in(kwds):
def keyword(name, explicit_prefix=None):
def tuple_str_of(items, add_quotes=False, add_parens=True):
def rem_comment(line):
def should_indent(code):
def split_comment(line):
def split_leading_comment(inputstring):
def split_leading_indent(line, max_indents=None):
def split_trailing_indent(line, max_indents=None):
def split_leading_trailing_indent(line, max_indents=None):
def collapse_indents(indentation):
def final_indentation_level(code):
def transform(grammar, text):
def interleaved_join(first_list, second_list):
def handle_indentation(inputstr, add_newline=False):
class ComputationNode(object):
class CombineNode(Combine):
class Wrap(ParseElementEnhance): def minify(compiled):
    """Perform basic minification of the header.

    Fails on non-tabideal indentation, strings with #s, or multi-line strings.
    (So don't do those things in the header.)
    """
    compiled = compiled.strip()
    if compiled:
        out = []
        for line in compiled.splitlines():
            new_line, comment = split_comment(line)
            new_line = new_line.rstrip()
            if new_line:
                ind = 0
                while new_line.startswith(" "):
                    new_line = new_line[1:]
                    ind += 1
                internal_assert(ind % tabideal == 0, "invalid indentation in", line)
                new_line = " " * (ind // tabideal) + new_line
            comment = comment.strip()
            if comment:
                new_line += "#" + comment
            if new_line:
                out.append(new_line)
        compiled = "\n".join(out) + "\n"
    return compiled def getheader(which, target, use_hash, no_tco, strict, no_wrap):
    """Generate the specified header."""
    internal_assert(
        which.startswith("package") or which in (
            "none", "initial", "__coconut__", "sys", "code", "file",
        ),
        "invalid header type",
        which,
    )

    if which == "none":
        return ""

    target_startswith = one_num_ver(target)
    target_info = get_target_info(target)

    # initial, __coconut__, package:n, sys, code, file

    format_dict = process_header_args(which, target, use_hash, no_tco, strict, no_wrap)

    if which == "initial" or which == "__coconut__":
        header = '''#!/usr/bin/env python{target_startswith}
# -*- coding: {default_encoding} -*-
{hash_line}{typing_line}
# Compiled with Coconut version {VERSION_STR}

{module_docstring}'''.format(**format_dict)
    elif use_hash is not None:
        raise CoconutInternalException("can only add a hash to an initial or __coconut__ header, not", which)
    else:
        header = ""

    if which == "initial":
        return header

    # __coconut__, package:n, sys, code, file

    header += section("Coconut Header", newline_before=False)

    if target_startswith != "3":
        header += "from __future__ import print_function, absolute_import, unicode_literals, division\n"
    elif target_info >= (3, 7):
        if no_wrap:
            header += "from __future__ import generator_stop\n"
        else:
            header += "from __future__ import generator_stop, annotations\n"
    elif target_info >= (3, 5):
        header += "from __future__ import generator_stop\n"

    if which.startswith("package"):
        levels_up = int(which[len("package:"):])
        coconut_file_dir = "_coconut_os.path.dirname(_coconut_os.path.abspath(__file__))"
        for _ in range(levels_up):
            coconut_file_dir = "_coconut_os.path.dirname(" + coconut_file_dir + ")"
        return header + '''import sys as _coconut_sys, os as _coconut_os
_coconut_file_dir = {coconut_file_dir}
_coconut_cached_module = _coconut_sys.modules.get({__coconut__})
if _coconut_cached_module is not None and _coconut_os.path.dirname(_coconut_cached_module.__file__) != _coconut_file_dir:
    del _coconut_sys.modules[{__coconut__}]
_coconut_sys.path.insert(0, _coconut_file_dir)
_coconut_module_name = _coconut_os.path.splitext(_coconut_os.path.basename(_coconut_file_dir))[0]
if _coconut_module_name and _coconut_module_name[0].isalpha() and all(c.isalpha() or c.isdigit() for c in _coconut_module_name) and "__init__.py" in _coconut_os.listdir(_coconut_file_dir):
    _coconut_full_module_name = str(_coconut_module_name + ".__coconut__")
    import __coconut__ as _coconut__coconut__
    _coconut__coconut__.__name__ = _coconut_full_module_name
    for _coconut_v in vars(_coconut__coconut__).values():
        if getattr(_coconut_v, "__module__", None) == {__coconut__}:
            try:
                _coconut_v.__module__ = _coconut_full_module_name
            except AttributeError:
                _coconut_v_type = type(_coconut_v)
                if getattr(_coconut_v_type, "__module__", None) == {__coconut__}:
                    _coconut_v_type.__module__ = _coconut_full_module_name
    _coconut_sys.modules[_coconut_full_module_name] = _coconut__coconut__
from __coconut__ import *
from __coconut__ import {underscore_imports}
_coconut_sys.path.pop(0)
'''.format(
            coconut_file_dir=coconut_file_dir,
            __coconut__=(
                '"__coconut__"' if target_startswith == "3"
                else 'b"__coconut__"' if target_startswith == "2"
                else 'str("__coconut__")'
            ),
            **format_dict
        ) + section("Compiled Coconut")

    if which == "sys":
        return header + '''import sys as _coconut_sys
from coconut.__coconut__ import *
from coconut.__coconut__ import {underscore_imports}
'''.format(**format_dict)

    # __coconut__, code, file

    header += "import sys as _coconut_sys\n"

    if target_info >= (3, 7):
        header += PY37_HEADER
    elif target_startswith == "3":
        header += PY3_HEADER
    elif target_info >= (2, 7):
        header += PY27_HEADER
    elif target_startswith == "2":
        header += PY2_HEADER
    else:
        header += PYCHECK_HEADER

    header += get_template("header").format(**format_dict)

    if which == "file":
        header += section("Compiled Coconut")

    return header
from coconut.root import *  # NOQA
from contextlib import contextmanager
from functools import partial
from collections import defaultdict
from threading import Lock
from coconut._pyparsing import (
    ParseBaseException,
    ParseResults,
    col as getcol,
    line as getline,
    lineno,
    nums,
)
from coconut.constants import (
    specific_targets,
    targets,
    pseudo_targets,
    default_encoding,
    hash_sep,
    openindent,
    closeindent,
    strwrapper,
    lnwrapper,
    unwrapper,
    holds,
    tabideal,
    match_to_args_var,
    match_to_kwargs_var,
    py3_to_py2_stdlib,
    reserved_prefix,
    function_match_error_var,
    legal_indent_chars,
    format_var,
    replwrapper,
    none_coalesce_var,
)
from coconut.util import checksum
from coconut.exceptions import (
    CoconutException,
    CoconutSyntaxError,
    CoconutParseError,
    CoconutStyleError,
    CoconutTargetError,
    CoconutInternalException,
    CoconutSyntaxWarning,
    CoconutDeferredSyntaxError,
    clean,
)
from coconut.terminal import (
    logger,
    complain,
    internal_assert,
)
from coconut.compiler.matching import Matcher
from coconut.compiler.grammar import (
    Grammar,
    lazy_list_handle,
    get_infix_items,
    pipe_info,
    attrgetter_atom_split,
    attrgetter_atom_handle,
    itemgetter_handle,
)
from coconut.compiler.util import (
    get_target_info,
    sys_target,
    addskip,
    count_end,
    paren_change,
    ind_change,
    rem_comment,
    split_comment,
    attach,
    split_leading_indent,
    split_trailing_indent,
    split_leading_trailing_indent,
    match_in,
    transform,
    parse,
    all_matches,
    get_target_info_smart,
    split_leading_comment,
    compile_regex,
    append_it,
    interleaved_join,
    handle_indentation,
    Wrap,
    tuple_str_of,
    join_args,
)
from coconut.compiler.header import (
    minify,
    getheader,
)
import sys
        """Handle Type | Type typedefs."""
        internal_assert(len(tokens) >= 2, "invalid union typedef tokens", tokens)
        if self.target_info >= (3, 10):
            return " | ".join(tokens)
        else:
            return "_coconut.typing.Union[" + ", ".join(tokens) + "]"

    def split_star_expr_tokens(self, tokens):
        """Split testlist_star_expr or dict_literal tokens."""
        groups = [[]]
        has_star = False
        has_comma = False
        for tok_grp in tokens:
            if tok_grp == ",":
                has_comma = True
            elif len(tok_grp) == 1:
                groups[-1].append(tok_grp[0])
            elif len(tok_grp) == 2:
                internal_assert(not tok_grp[0].lstrip("*"), "invalid star expr item signifier", tok_grp[0])
                has_star = True
                groups.append(tok_grp[1])
                groups.append([])
            else:
                raise CoconutInternalException("invalid testlist_star_expr tokens", tokens)
        if not groups[-1]:
            groups.pop()
        return groups, has_star, has_comma

    def testlist_star_expr_handle(self, original, loc, tokens, list_literal=False):
        """Handle naked a, *b."""
        groups, has_star, has_comma = self.split_star_expr_tokens(tokens)
        is_sequence = has_comma or list_literal

        if not is_sequence:
            if has_star:
                raise CoconutDeferredSyntaxError("can't use starred expression here", loc)
            internal_assert(len(groups) == 1 and len(groups[0]) == 1, "invalid single-item testlist_star_expr tokens", tokens)
            out = groups[0][0]

        elif not has_star:
            internal_assert(len(groups) == 1, "testlist_star_expr group splitting failed on", tokens)
            out = tuple_str_of(groups[0], add_parens=False)

        # naturally supported on 3.5+
        elif self.target_info >= (3, 5):
            to_literal = []
            for g in groups:
                if isinstance(g, list):
                    to_literal.extend(g)
                else:
                    to_literal.append("*" + g)
            out = tuple_str_of(to_literal, add_parens=False)

        # otherwise universalize
        else:
            to_chain = []
            for g in groups:
                if isinstance(g, list):
                    to_chain.append(tuple_str_of(g))
                else:
                    to_chain.append(g)

            # return immediately, since we handle list_literal here
            if list_literal:
                return "_coconut.list(_coconut.itertools.chain(" + ", ".join(to_chain) + "))"
            else:
                return "_coconut.tuple(_coconut.itertools.chain(" + ", ".join(to_chain) + "))"

        if list_literal:
            return "[" + out + "]"
        else:
            return out  # the grammar wraps this in parens as needed

    def list_literal_handle(self, original, loc, tokens):
        """Handle non-comprehension list literals."""
        return self.testlist_star_expr_handle(original, loc, tokens, list_literal=True)

    def dict_literal_handle(self, original, loc, tokens):
        """Handle {**d1, **d2}."""
        if not tokens:
            return "{}"

        groups, has_star, _ = self.split_star_expr_tokens(tokens)

        if not has_star:
            internal_assert(len(groups) == 1, "dict_literal group splitting failed on", tokens)
            return "{" + ", ".join(groups[0]) + "}"

        # naturally supported on 3.5+
        elif self.target_info >= (3, 5):
            to_literal = []
            for g in groups:
                if isinstance(g, list):
                    to_literal.extend(g)
                else:
                    to_literal.append("**" + g)
            return "{" + ", ".join(to_literal) + "}"

        # otherwise universalize
        else:
            to_merge = []
            for g in groups:
                if isinstance(g, list):
                    to_merge.append("{" + ", ".join(g) + "}")
                else:
                    to_merge.append(g)
            return "_coconut_dict_merge(" + ", ".join(to_merge) + ")"

# end: COMPILER HANDLERS
# -----------------------------------------------------------------------------------------------------------------------
# CHECKING HANDLERS:
# -----------------------------------------------------------------------------------------------------------------------

    def check_strict(self, name, original, loc, tokens, only_warn=False):
        """Check that syntax meets --strict requirements."""
        internal_assert(len(tokens) == 1, "invalid " + name + " tokens", tokens)
        if self.strict:
            if only_warn:
                logger.warn_err(self.make_err(CoconutSyntaxWarning, "found " + name, original, loc, extra="remove --strict to dismiss"))
            else:

                raise self.make_err(CoconutStyleError, "found " + name, original, loc)