# The file was automatically generated by Lark v1.1.2
__version__ = "1.1.2"

#
#
#   Lark Stand-alone Generator Tool
# ----------------------------------
# Generates a stand-alone LALR(1) parser
#
# Git:    https://github.com/erezsh/lark
# Author: Erez Shinan (erezshin@gmail.com)
#
#
#    >>> LICENSE
#
#    This tool and its generated code use a separate license from Lark,
#    and are subject to the terms of the Mozilla Public License, v. 2.0.
#    If a copy of the MPL was not distributed with this
#    file, You can obtain one at https://mozilla.org/MPL/2.0/.
#
#    If you wish to purchase a commercial license for this tool and its
#    generated code, you may contact me via email or otherwise.
#
#    If MPL2 is incompatible with your free or open-source project,
#    contact me and we'll work it out.
#
#

from abc import ABC, abstractmethod
from collections.abc import Sequence
from types import ModuleType
from typing import (
    IO,
    TYPE_CHECKING,
    Any,
    Callable,
    ClassVar,
    Collection,
    Dict,
    FrozenSet,
    Generic,
    Iterable,
    Iterator,
    List,
    Mapping,
    Optional,
)
from typing import Pattern as REPattern
from typing import Set, Tuple, Type, TypeVar, Union


class LarkError(Exception):
    pass


class ConfigurationError(LarkError, ValueError):
    pass


def assert_config(value, options: Collection, msg="Got %r, expected one of %s"):
    if value not in options:
        raise ConfigurationError(msg % (value, options))


class GrammarError(LarkError):
    pass


class ParseError(LarkError):
    pass


class LexError(LarkError):
    pass


T = TypeVar("T")


class UnexpectedInput(LarkError):
    # --
    line: int
    column: int
    pos_in_stream = None
    state: Any
    _terminals_by_name = None

    def get_context(self, text: str, span: int = 40) -> str:
        # --
        assert self.pos_in_stream is not None, self
        pos = self.pos_in_stream
        start = max(pos - span, 0)
        end = pos + span
        if not isinstance(text, bytes):
            before = text[start:pos].rsplit("\n", 1)[-1]
            after = text[pos:end].split("\n", 1)[0]
            return before + after + "\n" + " " * len(before.expandtabs()) + "^\n"
        else:
            before = text[start:pos].rsplit(b"\n", 1)[-1]
            after = text[pos:end].split(b"\n", 1)[0]
            return (
                before + after + b"\n" + b" " * len(before.expandtabs()) + b"^\n"
            ).decode("ascii", "backslashreplace")

    def match_examples(
        self,
        parse_fn: "Callable[[str], Tree]",
        examples: Union[Mapping[T, Iterable[str]], Iterable[Tuple[T, Iterable[str]]]],
        token_type_match_fallback: bool = False,
        use_accepts: bool = True,
    ) -> Optional[T]:
        # --
        assert self.state is not None, "Not supported for this exception"

        if isinstance(examples, Mapping):
            examples = examples.items()

        candidate = (None, False)
        for i, (label, example) in enumerate(examples):
            assert not isinstance(example, str), "Expecting a list"

            for j, malformed in enumerate(example):
                try:
                    parse_fn(malformed)
                except UnexpectedInput as ut:
                    if ut.state == self.state:
                        if (
                            use_accepts
                            and isinstance(self, UnexpectedToken)
                            and isinstance(ut, UnexpectedToken)
                            and ut.accepts != self.accepts
                        ):
                            logger.debug(
                                "Different accepts with same state[%d]: %s != %s at example [%s][%s]"
                                % (self.state, self.accepts, ut.accepts, i, j)
                            )
                            continue
                        if isinstance(
                            self, (UnexpectedToken, UnexpectedEOF)
                        ) and isinstance(ut, (UnexpectedToken, UnexpectedEOF)):
                            if ut.token == self.token:  ##

                                logger.debug("Exact Match at example [%s][%s]" % (i, j))
                                return label

                            if token_type_match_fallback:
                                ##

                                if (ut.token.type == self.token.type) and not candidate[
                                    -1
                                ]:
                                    logger.debug(
                                        "Token Type Fallback at example [%s][%s]"
                                        % (i, j)
                                    )
                                    candidate = label, True

                        if candidate[0] is None:
                            logger.debug(
                                "Same State match at example [%s][%s]" % (i, j)
                            )
                            candidate = label, False

        return candidate[0]

    def _format_expected(self, expected):
        if self._terminals_by_name:
            d = self._terminals_by_name
            expected = [
                d[t_name].user_repr() if t_name in d else t_name for t_name in expected
            ]
        return "Expected one of: \n\t* %s\n" % "\n\t* ".join(expected)


class UnexpectedEOF(ParseError, UnexpectedInput):
    # --
    expected: "List[Token]"

    def __init__(self, expected, state=None, terminals_by_name=None):
        super(UnexpectedEOF, self).__init__()

        self.expected = expected
        self.state = state
        from .lexer import Token

        self.token = Token("<EOF>", "")  ##

        self.pos_in_stream = -1
        self.line = -1
        self.column = -1
        self._terminals_by_name = terminals_by_name

    def __str__(self):
        message = "Unexpected end-of-input. "
        message += self._format_expected(self.expected)
        return message


class UnexpectedCharacters(LexError, UnexpectedInput):
    # --

    allowed: Set[str]
    considered_tokens: Set[Any]

    def __init__(
        self,
        seq,
        lex_pos,
        line,
        column,
        allowed=None,
        considered_tokens=None,
        state=None,
        token_history=None,
        terminals_by_name=None,
        considered_rules=None,
    ):
        super(UnexpectedCharacters, self).__init__()

        ##

        self.line = line
        self.column = column
        self.pos_in_stream = lex_pos
        self.state = state
        self._terminals_by_name = terminals_by_name

        self.allowed = allowed
        self.considered_tokens = considered_tokens
        self.considered_rules = considered_rules
        self.token_history = token_history

        if isinstance(seq, bytes):
            self.char = seq[lex_pos : lex_pos + 1].decode("ascii", "backslashreplace")
        else:
            self.char = seq[lex_pos]
        self._context = self.get_context(seq)

    def __str__(self):
        message = (
            "No terminal matches '%s' in the current parser context, at line %d col %d"
            % (self.char, self.line, self.column)
        )
        message += "\n\n" + self._context
        if self.allowed:
            message += self._format_expected(self.allowed)
        if self.token_history:
            message += "\nPrevious tokens: %s\n" % ", ".join(
                repr(t) for t in self.token_history
            )
        return message


class UnexpectedToken(ParseError, UnexpectedInput):
    # --

    expected: Set[str]
    considered_rules: Set[str]
    interactive_parser: "InteractiveParser"

    def __init__(
        self,
        token,
        expected,
        considered_rules=None,
        state=None,
        interactive_parser=None,
        terminals_by_name=None,
        token_history=None,
    ):
        super(UnexpectedToken, self).__init__()

        ##

        self.line = getattr(token, "line", "?")
        self.column = getattr(token, "column", "?")
        self.pos_in_stream = getattr(token, "start_pos", None)
        self.state = state

        self.token = token
        self.expected = expected  ##

        self._accepts = NO_VALUE
        self.considered_rules = considered_rules
        self.interactive_parser = interactive_parser
        self._terminals_by_name = terminals_by_name
        self.token_history = token_history

    @property
    def accepts(self) -> Set[str]:
        if self._accepts is NO_VALUE:
            self._accepts = (
                self.interactive_parser and self.interactive_parser.accepts()
            )
        return self._accepts

    def __str__(self):
        message = "Unexpected token %r at line %s, column %s.\n%s" % (
            self.token,
            self.line,
            self.column,
            self._format_expected(self.accepts or self.expected),
        )
        if self.token_history:
            message += "Previous tokens: %r\n" % self.token_history

        return message


class VisitError(LarkError):
    # --

    obj: "Union[Tree, Token]"
    orig_exc: Exception

    def __init__(self, rule, obj, orig_exc):
        message = 'Error trying to process rule "%s":\n\n%s' % (rule, orig_exc)
        super(VisitError, self).__init__(message)

        self.rule = rule
        self.obj = obj
        self.orig_exc = orig_exc


class MissingVariableError(LarkError):
    pass


import logging
import re
import sys

logger: logging.Logger = logging.getLogger("lark")
logger.addHandler(logging.StreamHandler())
##

##

logger.setLevel(logging.CRITICAL)


NO_VALUE = object()


def classify(seq, key=None, value=None):
    d = {}
    for item in seq:
        k = key(item) if (key is not None) else item
        v = value(item) if (value is not None) else item
        if k in d:
            d[k].append(v)
        else:
            d[k] = [v]
    return d


def _deserialize(data, namespace, memo):
    if isinstance(data, dict):
        if "__type__" in data:  ##

            class_ = namespace[data["__type__"]]
            return class_.deserialize(data, memo)
        elif "@" in data:
            return memo[data["@"]]
        return {
            key: _deserialize(value, namespace, memo) for key, value in data.items()
        }
    elif isinstance(data, list):
        return [_deserialize(value, namespace, memo) for value in data]
    return data


class Serialize:
    # --

    def memo_serialize(self, types_to_memoize):
        memo = SerializeMemoizer(types_to_memoize)
        return self.serialize(memo), memo.serialize()

    def serialize(self, memo=None):
        if memo and memo.in_types(self):
            return {"@": memo.memoized.get(self)}

        fields = getattr(self, "__serialize_fields__")
        res = {f: _serialize(getattr(self, f), memo) for f in fields}
        res["__type__"] = type(self).__name__
        if hasattr(self, "_serialize"):
            self._serialize(res, memo)
        return res

    @classmethod
    def deserialize(cls, data, memo):
        namespace = getattr(cls, "__serialize_namespace__", [])
        namespace = {c.__name__: c for c in namespace}

        fields = getattr(cls, "__serialize_fields__")

        if "@" in data:
            return memo[data["@"]]

        inst = cls.__new__(cls)
        for f in fields:
            try:
                setattr(inst, f, _deserialize(data[f], namespace, memo))
            except KeyError as e:
                raise KeyError("Cannot find key for class", cls, e)

        if hasattr(inst, "_deserialize"):
            inst._deserialize()

        return inst


class SerializeMemoizer(Serialize):
    # --

    __serialize_fields__ = ("memoized",)

    def __init__(self, types_to_memoize):
        self.types_to_memoize = tuple(types_to_memoize)
        self.memoized = Enumerator()

    def in_types(self, value):
        return isinstance(value, self.types_to_memoize)

    def serialize(self):
        return _serialize(self.memoized.reversed(), None)

    @classmethod
    def deserialize(cls, data, namespace, memo):
        return _deserialize(data, namespace, memo)


try:
    import regex  # #

except ImportError:
    regex = None

import sre_constants
import sre_parse

categ_pattern = re.compile(r"\\p{[A-Za-z_]+}")


def get_regexp_width(expr):
    if regex:
        ##

        ##

        ##

        regexp_final = re.sub(categ_pattern, "A", expr)
    else:
        if re.search(categ_pattern, expr):
            raise ImportError(
                "`regex` module must be installed in order to use Unicode categories.",
                expr,
            )
        regexp_final = expr
    try:
        return [int(x) for x in sre_parse.parse(regexp_final).getwidth()]
    except sre_constants.error:
        if not regex:
            raise ValueError(expr)
        else:
            ##

            ##

            c = regex.compile(regexp_final)
            if c.match("") is None:
                ##

                return 1, int(sre_constants.MAXREPEAT)
            else:
                return 0, int(sre_constants.MAXREPEAT)


from collections import OrderedDict


class Meta:

    empty: bool
    line: int
    column: int
    start_pos: int
    end_line: int
    end_column: int
    end_pos: int
    orig_expansion: "List[TerminalDef]"
    match_tree: bool

    def __init__(self):
        self.empty = True


_Leaf_T = TypeVar("_Leaf_T")
Branch = Union[_Leaf_T, "Tree[_Leaf_T]"]


class Tree(Generic[_Leaf_T]):
    # --

    data: str
    children: "List[Branch[_Leaf_T]]"

    def __init__(
        self, data: str, children: "List[Branch[_Leaf_T]]", meta: Optional[Meta] = None
    ) -> None:
        self.data = data
        self.children = children
        self._meta = meta

    @property
    def meta(self) -> Meta:
        if self._meta is None:
            self._meta = Meta()
        return self._meta

    def __repr__(self):
        return "Tree(%r, %r)" % (self.data, self.children)

    def _pretty_label(self):
        return self.data

    def _pretty(self, level, indent_str):
        if len(self.children) == 1 and not isinstance(self.children[0], Tree):
            return [
                indent_str * level,
                self._pretty_label(),
                "\t",
                "%s" % (self.children[0],),
                "\n",
            ]

        l = [indent_str * level, self._pretty_label(), "\n"]
        for n in self.children:
            if isinstance(n, Tree):
                l += n._pretty(level + 1, indent_str)
            else:
                l += [indent_str * (level + 1), "%s" % (n,), "\n"]

        return l

    def pretty(self, indent_str: str = "  ") -> str:
        # --
        return "".join(self._pretty(0, indent_str))

    def __rich__(self, parent: "rich.tree.Tree" = None) -> "rich.tree.Tree":
        # --
        return self._rich(parent)

    def _rich(self, parent):
        if parent:
            tree = parent.add(f"[bold]{self.data}[/bold]")
        else:
            import rich.tree

            tree = rich.tree.Tree(self.data)

        for c in self.children:
            if isinstance(c, Tree):
                c._rich(tree)
            else:
                tree.add(f"[green]{c}[/green]")

        return tree

    def __eq__(self, other):
        try:
            return self.data == other.data and self.children == other.children
        except AttributeError:
            return False

    def __ne__(self, other):
        return not (self == other)

    def __hash__(self) -> int:
        return hash((self.data, tuple(self.children)))

    def iter_subtrees(self) -> "Iterator[Tree[_Leaf_T]]":
        # --
        queue = [self]
        subtrees = OrderedDict()
        for subtree in queue:
            subtrees[id(subtree)] = subtree
            ##

            queue += [
                c
                for c in reversed(subtree.children)  ##
                if isinstance(c, Tree) and id(c) not in subtrees
            ]

        del queue
        return reversed(list(subtrees.values()))

    def find_pred(
        self, pred: "Callable[[Tree[_Leaf_T]], bool]"
    ) -> "Iterator[Tree[_Leaf_T]]":
        # --
        return filter(pred, self.iter_subtrees())

    def find_data(self, data: str) -> "Iterator[Tree[_Leaf_T]]":
        # --
        return self.find_pred(lambda t: t.data == data)


from functools import update_wrapper, wraps
from inspect import getmembers, getmro

_Return_T = TypeVar("_Return_T")
_Return_V = TypeVar("_Return_V")
_Leaf_T = TypeVar("_Leaf_T")
_Leaf_U = TypeVar("_Leaf_U")
_R = TypeVar("_R")
_FUNC = Callable[..., _Return_T]
_DECORATED = Union[_FUNC, type]


class _DiscardType:
    # --

    def __repr__(self):
        return "lark.visitors.Discard"


Discard = _DiscardType()

##


class _Decoratable:
    # --

    @classmethod
    def _apply_v_args(cls, visit_wrapper):
        mro = getmro(cls)
        assert mro[0] is cls
        libmembers = {name for _cls in mro[1:] for name, _ in getmembers(_cls)}
        for name, value in getmembers(cls):

            ##

            if name.startswith("_") or (
                name in libmembers and name not in cls.__dict__
            ):
                continue
            if not callable(value):
                continue

            ##

            if isinstance(cls.__dict__[name], _VArgsWrapper):
                continue

            setattr(cls, name, _VArgsWrapper(cls.__dict__[name], visit_wrapper))
        return cls

    def __class_getitem__(cls, _):
        return cls


class Transformer(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]):
    # --
    __visit_tokens__ = True  ##

    def __init__(self, visit_tokens: bool = True) -> None:
        self.__visit_tokens__ = visit_tokens

    def _call_userfunc(self, tree, new_children=None):
        ##

        children = new_children if new_children is not None else tree.children
        try:
            f = getattr(self, tree.data)
        except AttributeError:
            return self.__default__(tree.data, children, tree.meta)
        else:
            try:
                wrapper = getattr(f, "visit_wrapper", None)
                if wrapper is not None:
                    return f.visit_wrapper(f, tree.data, children, tree.meta)
                else:
                    return f(children)
            except GrammarError:
                raise
            except Exception as e:
                raise VisitError(tree.data, tree, e)

    def _call_userfunc_token(self, token):
        try:
            f = getattr(self, token.type)
        except AttributeError:
            return self.__default_token__(token)
        else:
            try:
                return f(token)
            except GrammarError:
                raise
            except Exception as e:
                raise VisitError(token.type, token, e)

    def _transform_children(self, children):
        for c in children:
            if isinstance(c, Tree):
                res = self._transform_tree(c)
            elif self.__visit_tokens__ and isinstance(c, Token):
                res = self._call_userfunc_token(c)
            else:
                res = c

            if res is not Discard:
                yield res

    def _transform_tree(self, tree):
        children = list(self._transform_children(tree.children))
        return self._call_userfunc(tree, children)

    def transform(self, tree: Tree[_Leaf_T]) -> _Return_T:
        # --
        return self._transform_tree(tree)

    def __mul__(
        self: "Transformer[_Leaf_T, Tree[_Leaf_U]]",
        other: "Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V,]]",
    ) -> "TransformerChain[_Leaf_T, _Return_V]":
        # --
        return TransformerChain(self, other)

    def __default__(self, data, children, meta):
        # --
        return Tree(data, children, meta)

    def __default_token__(self, token):
        # --
        return token


def merge_transformers(base_transformer=None, **transformers_to_merge):
    # --
    if base_transformer is None:
        base_transformer = Transformer()
    for prefix, transformer in transformers_to_merge.items():
        for method_name in dir(transformer):
            method = getattr(transformer, method_name)
            if not callable(method):
                continue
            if method_name.startswith("_") or method_name == "transform":
                continue
            prefixed_method = prefix + "__" + method_name
            if hasattr(base_transformer, prefixed_method):
                raise AttributeError(
                    "Cannot merge: method '%s' appears more than once" % prefixed_method
                )

            setattr(base_transformer, prefixed_method, method)

    return base_transformer


class InlineTransformer(Transformer):  ##
    def _call_userfunc(self, tree, new_children=None):
        ##

        children = new_children if new_children is not None else tree.children
        try:
            f = getattr(self, tree.data)
        except AttributeError:
            return self.__default__(tree.data, children, tree.meta)
        else:
            return f(*children)


class TransformerChain(Generic[_Leaf_T, _Return_T]):

    transformers: "Tuple[Union[Transformer, TransformerChain], ...]"

    def __init__(self, *transformers: "Union[Transformer, TransformerChain]") -> None:
        self.transformers = transformers

    def transform(self, tree: Tree[_Leaf_T]) -> _Return_T:
        for t in self.transformers:
            tree = t.transform(tree)
        return cast(_Return_T, tree)

    def __mul__(
        self: "TransformerChain[_Leaf_T, Tree[_Leaf_U]]",
        other: "Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V]]",
    ) -> "TransformerChain[_Leaf_T, _Return_V]":
        return TransformerChain(*self.transformers + (other,))


class Transformer_InPlace(Transformer):
    # --
    def _transform_tree(self, tree):  ##

        return self._call_userfunc(tree)

    def transform(self, tree: Tree[_Leaf_T]) -> _Return_T:
        for subtree in tree.iter_subtrees():
            subtree.children = list(self._transform_children(subtree.children))

        return self._transform_tree(tree)


class Transformer_NonRecursive(Transformer):
    # --

    def transform(self, tree: Tree[_Leaf_T]) -> _Return_T:
        ##

        rev_postfix = []
        q: List[Branch[_Leaf_T]] = [tree]
        while q:
            t = q.pop()
            rev_postfix.append(t)
            if isinstance(t, Tree):
                q += t.children

        ##

        stack: List = []
        for x in reversed(rev_postfix):
            if isinstance(x, Tree):
                size = len(x.children)
                if size:
                    args = stack[-size:]
                    del stack[-size:]
                else:
                    args = []

                res = self._call_userfunc(x, args)
                if res is not Discard:
                    stack.append(res)

            elif self.__visit_tokens__ and isinstance(x, Token):
                res = self._call_userfunc_token(x)
                if res is not Discard:
                    stack.append(res)
            else:
                stack.append(x)

        (result,) = stack  ##

        ##

        ##

        ##

        return cast(_Return_T, result)


class Transformer_InPlaceRecursive(Transformer):
    # --
    def _transform_tree(self, tree):
        tree.children = list(self._transform_children(tree.children))
        return self._call_userfunc(tree)


##


class VisitorBase:
    def _call_userfunc(self, tree):
        return getattr(self, tree.data, self.__default__)(tree)

    def __default__(self, tree):
        # --
        return tree

    def __class_getitem__(cls, _):
        return cls


class Visitor(VisitorBase, ABC, Generic[_Leaf_T]):
    # --

    def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]:
        # --
        for subtree in tree.iter_subtrees():
            self._call_userfunc(subtree)
        return tree

    def visit_topdown(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]:
        # --
        for subtree in tree.iter_subtrees_topdown():
            self._call_userfunc(subtree)
        return tree


class Visitor_Recursive(VisitorBase, Generic[_Leaf_T]):
    # --

    def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]:
        # --
        for child in tree.children:
            if isinstance(child, Tree):
                self.visit(child)

        self._call_userfunc(tree)
        return tree

    def visit_topdown(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]:
        # --
        self._call_userfunc(tree)

        for child in tree.children:
            if isinstance(child, Tree):
                self.visit_topdown(child)

        return tree


class Interpreter(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]):
    # --

    def visit(self, tree: Tree[_Leaf_T]) -> _Return_T:
        ##

        ##

        ##

        return self._visit_tree(tree)

    def _visit_tree(self, tree: Tree[_Leaf_T]):
        f = getattr(self, tree.data)
        wrapper = getattr(f, "visit_wrapper", None)
        if wrapper is not None:
            return f.visit_wrapper(f, tree.data, tree.children, tree.meta)
        else:
            return f(tree)

    def visit_children(self, tree: Tree[_Leaf_T]) -> List:
        return [
            self._visit_tree(child) if isinstance(child, Tree) else child
            for child in tree.children
        ]

    def __getattr__(self, name):
        return self.__default__

    def __default__(self, tree):
        return self.visit_children(tree)


_InterMethod = Callable[[Type[Interpreter], _Return_T], _R]


def visit_children_decor(func: _InterMethod) -> _InterMethod:
    # --
    @wraps(func)
    def inner(cls, tree):
        values = cls.visit_children(tree)
        return func(cls, values)

    return inner


##


def _apply_v_args(obj, visit_wrapper):
    try:
        _apply = obj._apply_v_args
    except AttributeError:
        return _VArgsWrapper(obj, visit_wrapper)
    else:
        return _apply(visit_wrapper)


class _VArgsWrapper:
    # --
    base_func: Callable

    def __init__(
        self, func: Callable, visit_wrapper: Callable[[Callable, str, list, Any], Any]
    ):
        if isinstance(func, _VArgsWrapper):
            func = func.base_func
        ##

        self.base_func = func  ##

        self.visit_wrapper = visit_wrapper
        update_wrapper(self, func)

    def __call__(self, *args, **kwargs):
        return self.base_func(*args, **kwargs)

    def __get__(self, instance, owner=None):
        try:
            g = self.base_func.__get__
        except AttributeError:
            return self
        else:
            return _VArgsWrapper(g(instance, owner), self.visit_wrapper)

    def __set_name__(self, owner, name):
        try:
            f = self.base_func.__set_name__
        except AttributeError:
            return
        else:
            f(owner, name)


def _vargs_inline(f, _data, children, _meta):
    return f(*children)


def _vargs_meta_inline(f, _data, children, meta):
    return f(meta, *children)


def _vargs_meta(f, _data, children, meta):
    return f(meta, children)


def _vargs_tree(f, data, children, meta):
    return f(Tree(data, children, meta))


def v_args(
    inline: bool = False,
    meta: bool = False,
    tree: bool = False,
    wrapper: Optional[Callable] = None,
) -> Callable[[_DECORATED], _DECORATED]:
    # --
    if tree and (meta or inline):
        raise ValueError(
            "Visitor functions cannot combine 'tree' with 'meta' or 'inline'."
        )

    func = None
    if meta:
        if inline:
            func = _vargs_meta_inline
        else:
            func = _vargs_meta
    elif inline:
        func = _vargs_inline
    elif tree:
        func = _vargs_tree

    if wrapper is not None:
        if func is not None:
            raise ValueError(
                "Cannot use 'wrapper' along with 'tree', 'meta' or 'inline'."
            )
        func = wrapper

    def _visitor_args_dec(obj):
        return _apply_v_args(obj, func)

    return _visitor_args_dec


TOKEN_DEFAULT_PRIORITY = 0


class Symbol(Serialize):
    __slots__ = ("name",)

    name: str
    is_term: ClassVar[bool] = NotImplemented

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

    def __eq__(self, other):
        assert isinstance(other, Symbol), other
        return self.is_term == other.is_term and self.name == other.name

    def __ne__(self, other):
        return not (self == other)

    def __hash__(self):
        return hash(self.name)

    def __repr__(self):
        return "%s(%r)" % (type(self).__name__, self.name)

    fullrepr = property(__repr__)

    def renamed(self, f):
        return type(self)(f(self.name))


class Terminal(Symbol):
    __serialize_fields__ = "name", "filter_out"

    is_term: ClassVar[bool] = True

    def __init__(self, name, filter_out=False):
        self.name = name
        self.filter_out = filter_out

    @property
    def fullrepr(self):
        return "%s(%r, %r)" % (type(self).__name__, self.name, self.filter_out)

    def renamed(self, f):
        return type(self)(f(self.name), self.filter_out)


class NonTerminal(Symbol):
    __serialize_fields__ = ("name",)

    is_term: ClassVar[bool] = False


class RuleOptions(Serialize):
    __serialize_fields__ = (
        "keep_all_tokens",
        "expand1",
        "priority",
        "template_source",
        "empty_indices",
    )

    keep_all_tokens: bool
    expand1: bool
    priority: Optional[int]
    template_source: Optional[str]
    empty_indices: Tuple[bool, ...]

    def __init__(
        self,
        keep_all_tokens: bool = False,
        expand1: bool = False,
        priority: Optional[int] = None,
        template_source: Optional[str] = None,
        empty_indices: Tuple[bool, ...] = (),
    ) -> None:
        self.keep_all_tokens = keep_all_tokens
        self.expand1 = expand1
        self.priority = priority
        self.template_source = template_source
        self.empty_indices = empty_indices

    def __repr__(self):
        return "RuleOptions(%r, %r, %r, %r)" % (
            self.keep_all_tokens,
            self.expand1,
            self.priority,
            self.template_source,
        )


class Rule(Serialize):
    # --
    __slots__ = ("origin", "expansion", "alias", "options", "order", "_hash")

    __serialize_fields__ = "origin", "expansion", "order", "alias", "options"
    __serialize_namespace__ = Terminal, NonTerminal, RuleOptions

    def __init__(self, origin, expansion, order=0, alias=None, options=None):
        self.origin = origin
        self.expansion = expansion
        self.alias = alias
        self.order = order
        self.options = options or RuleOptions()
        self._hash = hash((self.origin, tuple(self.expansion)))

    def _deserialize(self):
        self._hash = hash((self.origin, tuple(self.expansion)))

    def __str__(self):
        return "<%s : %s>" % (
            self.origin.name,
            " ".join(x.name for x in self.expansion),
        )

    def __repr__(self):
        return "Rule(%r, %r, %r, %r)" % (
            self.origin,
            self.expansion,
            self.alias,
            self.options,
        )

    def __hash__(self):
        return self._hash

    def __eq__(self, other):
        if not isinstance(other, Rule):
            return False
        return self.origin == other.origin and self.expansion == other.expansion


from copy import copy


class Pattern(Serialize, ABC):

    value: str
    flags: Collection[str]
    raw: Optional[str]
    type: ClassVar[str]

    def __init__(
        self, value: str, flags: Collection[str] = (), raw: Optional[str] = None
    ) -> None:
        self.value = value
        self.flags = frozenset(flags)
        self.raw = raw

    def __repr__(self):
        return repr(self.to_regexp())

    ##

    def __hash__(self):
        return hash((type(self), self.value, self.flags))

    def __eq__(self, other):
        return (
            type(self) == type(other)
            and self.value == other.value
            and self.flags == other.flags
        )

    @abstractmethod
    def to_regexp(self) -> str:
        raise NotImplementedError()

    @property
    @abstractmethod
    def min_width(self) -> int:
        raise NotImplementedError()

    @property
    @abstractmethod
    def max_width(self) -> int:
        raise NotImplementedError()

    def _get_flags(self, value):
        for f in self.flags:
            value = "(?%s:%s)" % (f, value)
        return value


class PatternStr(Pattern):
    __serialize_fields__ = "value", "flags"

    type: ClassVar[str] = "str"

    def to_regexp(self) -> str:
        return self._get_flags(re.escape(self.value))

    @property
    def min_width(self) -> int:
        return len(self.value)

    @property
    def max_width(self) -> int:
        return len(self.value)


class PatternRE(Pattern):
    __serialize_fields__ = "value", "flags", "_width"

    type: ClassVar[str] = "re"

    def to_regexp(self) -> str:
        return self._get_flags(self.value)

    _width = None

    def _get_width(self):
        if self._width is None:
            self._width = get_regexp_width(self.to_regexp())
        return self._width

    @property
    def min_width(self) -> int:
        return self._get_width()[0]

    @property
    def max_width(self) -> int:
        return self._get_width()[1]


class TerminalDef(Serialize):
    __serialize_fields__ = "name", "pattern", "priority"
    __serialize_namespace__ = PatternStr, PatternRE

    name: str
    pattern: Pattern
    priority: int

    def __init__(
        self, name: str, pattern: Pattern, priority: int = TOKEN_DEFAULT_PRIORITY
    ) -> None:
        assert isinstance(pattern, Pattern), pattern
        self.name = name
        self.pattern = pattern
        self.priority = priority

    def __repr__(self):
        return "%s(%r, %r)" % (type(self).__name__, self.name, self.pattern)

    def user_repr(self) -> str:
        if self.name.startswith("__"):  ##

            return self.pattern.raw or self.name
        else:
            return self.name


_T = TypeVar("_T", bound="Token")


class Token(str):
    # --
    __slots__ = (
        "type",
        "start_pos",
        "value",
        "line",
        "column",
        "end_line",
        "end_column",
        "end_pos",
    )

    type: str
    start_pos: int
    value: Any
    line: int
    column: int
    end_line: int
    end_column: int
    end_pos: int

    def __new__(
        cls,
        type_,
        value,
        start_pos=None,
        line=None,
        column=None,
        end_line=None,
        end_column=None,
        end_pos=None,
    ):
        inst = super(Token, cls).__new__(cls, value)
        inst.type = type_
        inst.start_pos = start_pos
        inst.value = value
        inst.line = line
        inst.column = column
        inst.end_line = end_line
        inst.end_column = end_column
        inst.end_pos = end_pos
        return inst

    def update(
        self, type_: Optional[str] = None, value: Optional[Any] = None
    ) -> "Token":
        return Token.new_borrow_pos(
            type_ if type_ is not None else self.type,
            value if value is not None else self.value,
            self,
        )

    @classmethod
    def new_borrow_pos(cls: Type[_T], type_: str, value: Any, borrow_t: "Token") -> _T:
        return cls(
            type_,
            value,
            borrow_t.start_pos,
            borrow_t.line,
            borrow_t.column,
            borrow_t.end_line,
            borrow_t.end_column,
            borrow_t.end_pos,
        )

    def __reduce__(self):
        return (
            self.__class__,
            (self.type, self.value, self.start_pos, self.line, self.column),
        )

    def __repr__(self):
        return "Token(%r, %r)" % (self.type, self.value)

    def __deepcopy__(self, memo):
        return Token(self.type, self.value, self.start_pos, self.line, self.column)

    def __eq__(self, other):
        if isinstance(other, Token) and self.type != other.type:
            return False

        return str.__eq__(self, other)

    __hash__ = str.__hash__


class LineCounter:
    __slots__ = "char_pos", "line", "column", "line_start_pos", "newline_char"

    def __init__(self, newline_char):
        self.newline_char = newline_char
        self.char_pos = 0
        self.line = 1
        self.column = 1
        self.line_start_pos = 0

    def __eq__(self, other):
        if not isinstance(other, LineCounter):
            return NotImplemented

        return (
            self.char_pos == other.char_pos and self.newline_char == other.newline_char
        )

    def feed(self, token: Token, test_newline=True):
        # --
        if test_newline:
            newlines = token.count(self.newline_char)
            if newlines:
                self.line += newlines
                self.line_start_pos = (
                    self.char_pos + token.rindex(self.newline_char) + 1
                )

        self.char_pos += len(token)
        self.column = self.char_pos - self.line_start_pos + 1


class UnlessCallback:
    def __init__(self, scanner):
        self.scanner = scanner

    def __call__(self, t):
        res = self.scanner.match(t.value, 0)
        if res:
            _value, t.type = res
        return t


class CallChain:
    def __init__(self, callback1, callback2, cond):
        self.callback1 = callback1
        self.callback2 = callback2
        self.cond = cond

    def __call__(self, t):
        t2 = self.callback1(t)
        return self.callback2(t) if self.cond(t2) else t2


def _get_match(re_, regexp, s, flags):
    m = re_.match(regexp, s, flags)
    if m:
        return m.group(0)


def _create_unless(terminals, g_regex_flags, re_, use_bytes):
    tokens_by_type = classify(terminals, lambda t: type(t.pattern))
    assert len(tokens_by_type) <= 2, tokens_by_type.keys()
    embedded_strs = set()
    callback = {}
    for retok in tokens_by_type.get(PatternRE, []):
        unless = []
        for strtok in tokens_by_type.get(PatternStr, []):
            if strtok.priority != retok.priority:
                continue
            s = strtok.pattern.value
            if s == _get_match(re_, retok.pattern.to_regexp(), s, g_regex_flags):
                unless.append(strtok)
                if strtok.pattern.flags <= retok.pattern.flags:
                    embedded_strs.add(strtok)
        if unless:
            callback[retok.name] = UnlessCallback(
                Scanner(
                    unless, g_regex_flags, re_, match_whole=True, use_bytes=use_bytes
                )
            )

    new_terminals = [t for t in terminals if t not in embedded_strs]
    return new_terminals, callback


class Scanner:
    def __init__(self, terminals, g_regex_flags, re_, use_bytes, match_whole=False):
        self.terminals = terminals
        self.g_regex_flags = g_regex_flags
        self.re_ = re_
        self.use_bytes = use_bytes
        self.match_whole = match_whole

        self.allowed_types = {t.name for t in self.terminals}

        self._mres = self._build_mres(terminals, len(terminals))

    def _build_mres(self, terminals, max_size):
        ##

        ##

        ##

        postfix = "$" if self.match_whole else ""
        mres = []
        while terminals:
            pattern = "|".join(
                "(?P<%s>%s)" % (t.name, t.pattern.to_regexp() + postfix)
                for t in terminals[:max_size]
            )
            if self.use_bytes:
                pattern = pattern.encode("latin-1")
            try:
                mre = self.re_.compile(pattern, self.g_regex_flags)
            except AssertionError:  ##

                return self._build_mres(terminals, max_size // 2)

            mres.append((mre, {i: n for n, i in mre.groupindex.items()}))
            terminals = terminals[max_size:]
        return mres

    def match(self, text, pos):
        for mre, type_from_index in self._mres:
            m = mre.match(text, pos)
            if m:
                return m.group(0), type_from_index[m.lastindex]


def _regexp_has_newline(r: str):
    # --
    return (
        "\n" in r or "\\n" in r or "\\s" in r or "[^" in r or ("(?s" in r and "." in r)
    )


class LexerState:
    # --

    __slots__ = "text", "line_ctr", "last_token"

    def __init__(self, text, line_ctr=None, last_token=None):
        self.text = text
        self.line_ctr = line_ctr or LineCounter(
            b"\n" if isinstance(text, bytes) else "\n"
        )
        self.last_token = last_token

    def __eq__(self, other):
        if not isinstance(other, LexerState):
            return NotImplemented

        return (
            self.text is other.text
            and self.line_ctr == other.line_ctr
            and self.last_token == other.last_token
        )

    def __copy__(self):
        return type(self)(self.text, copy(self.line_ctr), self.last_token)


class LexerThread:
    # --

    def __init__(self, lexer: "Lexer", lexer_state: LexerState):
        self.lexer = lexer
        self.state = lexer_state

    @classmethod
    def from_text(cls, lexer: "Lexer", text: str):
        return cls(lexer, LexerState(text))

    def lex(self, parser_state):
        return self.lexer.lex(self.state, parser_state)

    def __copy__(self):
        return type(self)(self.lexer, copy(self.state))

    _Token = Token


_Callback = Callable[[Token], Token]


class Lexer(ABC):
    # --
    @abstractmethod
    def lex(self, lexer_state: LexerState, parser_state: Any) -> Iterator[Token]:
        return NotImplemented

    def make_lexer_state(self, text):
        # --
        return LexerState(text)


class BasicLexer(Lexer):

    terminals: Collection[TerminalDef]
    ignore_types: FrozenSet[str]
    newline_types: FrozenSet[str]
    user_callbacks: Dict[str, _Callback]
    callback: Dict[str, _Callback]
    re: ModuleType

    def __init__(self, conf: "LexerConf") -> None:
        terminals = list(conf.terminals)
        assert all(isinstance(t, TerminalDef) for t in terminals), terminals

        self.re = conf.re_module

        if not conf.skip_validation:
            ##

            for t in terminals:
                try:
                    self.re.compile(t.pattern.to_regexp(), conf.g_regex_flags)
                except self.re.error:
                    raise LexError("Cannot compile token %s: %s" % (t.name, t.pattern))

                if t.pattern.min_width == 0:
                    raise LexError(
                        "Lexer does not allow zero-width terminals. (%s: %s)"
                        % (t.name, t.pattern)
                    )

            if not (set(conf.ignore) <= {t.name for t in terminals}):
                raise LexError(
                    "Ignore terminals are not defined: %s"
                    % (set(conf.ignore) - {t.name for t in terminals})
                )

        ##

        self.newline_types = frozenset(
            t.name for t in terminals if _regexp_has_newline(t.pattern.to_regexp())
        )
        self.ignore_types = frozenset(conf.ignore)

        terminals.sort(
            key=lambda x: (
                -x.priority,
                -x.pattern.max_width,
                -len(x.pattern.value),
                x.name,
            )
        )
        self.terminals = terminals
        self.user_callbacks = conf.callbacks
        self.g_regex_flags = conf.g_regex_flags
        self.use_bytes = conf.use_bytes
        self.terminals_by_name = conf.terminals_by_name

        self._scanner = None

    def _build_scanner(self):
        terminals, self.callback = _create_unless(
            self.terminals, self.g_regex_flags, self.re, self.use_bytes
        )
        assert all(self.callback.values())

        for type_, f in self.user_callbacks.items():
            if type_ in self.callback:
                ##

                self.callback[type_] = CallChain(
                    self.callback[type_], f, lambda t: t.type == type_
                )
            else:
                self.callback[type_] = f

        self._scanner = Scanner(terminals, self.g_regex_flags, self.re, self.use_bytes)

    @property
    def scanner(self):
        if self._scanner is None:
            self._build_scanner()
        return self._scanner

    def match(self, text, pos):
        return self.scanner.match(text, pos)

    def lex(self, state: LexerState, parser_state: Any) -> Iterator[Token]:
        with suppress(EOFError):
            while True:
                yield self.next_token(state, parser_state)

    def next_token(self, lex_state: LexerState, parser_state: Any = None) -> Token:
        line_ctr = lex_state.line_ctr
        while line_ctr.char_pos < len(lex_state.text):
            res = self.match(lex_state.text, line_ctr.char_pos)
            if not res:
                allowed = self.scanner.allowed_types - self.ignore_types
                if not allowed:
                    allowed = {"<END-OF-FILE>"}
                raise UnexpectedCharacters(
                    lex_state.text,
                    line_ctr.char_pos,
                    line_ctr.line,
                    line_ctr.column,
                    allowed=allowed,
                    token_history=lex_state.last_token and [lex_state.last_token],
                    state=parser_state,
                    terminals_by_name=self.terminals_by_name,
                )

            value, type_ = res

            if type_ not in self.ignore_types:
                t = Token(
                    type_, value, line_ctr.char_pos, line_ctr.line, line_ctr.column
                )
                line_ctr.feed(value, type_ in self.newline_types)
                t.end_line = line_ctr.line
                t.end_column = line_ctr.column
                t.end_pos = line_ctr.char_pos
                if t.type in self.callback:
                    t = self.callback[t.type](t)
                    if not isinstance(t, Token):
                        raise LexError(
                            "Callbacks must return a token (returned %r)" % t
                        )
                lex_state.last_token = t
                return t
            else:
                if type_ in self.callback:
                    t2 = Token(
                        type_, value, line_ctr.char_pos, line_ctr.line, line_ctr.column
                    )
                    self.callback[type_](t2)
                line_ctr.feed(value, type_ in self.newline_types)

        ##

        raise EOFError(self)


class ContextualLexer(Lexer):

    lexers: Dict[str, BasicLexer]
    root_lexer: BasicLexer

    def __init__(
        self,
        conf: "LexerConf",
        states: Dict[str, Collection[str]],
        always_accept: Collection[str] = (),
    ) -> None:
        terminals = list(conf.terminals)
        terminals_by_name = conf.terminals_by_name

        trad_conf = copy(conf)
        trad_conf.terminals = terminals

        lexer_by_tokens: Dict[FrozenSet[str], BasicLexer] = {}
        self.lexers = {}
        for state, accepts in states.items():
            key = frozenset(accepts)
            try:
                lexer = lexer_by_tokens[key]
            except KeyError:
                accepts = set(accepts) | set(conf.ignore) | set(always_accept)
                lexer_conf = copy(trad_conf)
                lexer_conf.terminals = [
                    terminals_by_name[n] for n in accepts if n in terminals_by_name
                ]
                lexer = BasicLexer(lexer_conf)
                lexer_by_tokens[key] = lexer

            self.lexers[state] = lexer

        assert trad_conf.terminals is terminals
        self.root_lexer = BasicLexer(trad_conf)

    def lex(self, lexer_state: LexerState, parser_state: Any) -> Iterator[Token]:
        try:
            while True:
                lexer = self.lexers[parser_state.position]
                yield lexer.next_token(lexer_state, parser_state)
        except EOFError:
            pass
        except UnexpectedCharacters as e:
            ##

            ##

            try:
                last_token = lexer_state.last_token  ##

                token = self.root_lexer.next_token(lexer_state, parser_state)
                raise UnexpectedToken(
                    token,
                    e.allowed,
                    state=parser_state,
                    token_history=[last_token],
                    terminals_by_name=self.root_lexer.terminals_by_name,
                )
            except UnexpectedCharacters:
                raise e  ##


_ParserArgType: "TypeAlias" = 'Literal["earley", "lalr", "cyk", "auto"]'
_LexerArgType: "TypeAlias" = 'Union[Literal["auto", "basic", "contextual", "dynamic", "dynamic_complete"], Type[Lexer]]'
_Callback = Callable[[Token], Token]


class LexerConf(Serialize):
    __serialize_fields__ = (
        "terminals",
        "ignore",
        "g_regex_flags",
        "use_bytes",
        "lexer_type",
    )
    __serialize_namespace__ = (TerminalDef,)

    terminals: Collection[TerminalDef]
    re_module: ModuleType
    ignore: Collection[str]
    postlex: "Optional[PostLex]"
    callbacks: Dict[str, _Callback]
    g_regex_flags: int
    skip_validation: bool
    use_bytes: bool
    lexer_type: Optional[_LexerArgType]

    def __init__(
        self,
        terminals: Collection[TerminalDef],
        re_module: ModuleType,
        ignore: Collection[str] = (),
        postlex: "Optional[PostLex]" = None,
        callbacks: Optional[Dict[str, _Callback]] = None,
        g_regex_flags: int = 0,
        skip_validation: bool = False,
        use_bytes: bool = False,
    ):
        self.terminals = terminals
        self.terminals_by_name = {t.name: t for t in self.terminals}
        assert len(self.terminals) == len(self.terminals_by_name)
        self.ignore = ignore
        self.postlex = postlex
        self.callbacks = callbacks or {}
        self.g_regex_flags = g_regex_flags
        self.re_module = re_module
        self.skip_validation = skip_validation
        self.use_bytes = use_bytes
        self.lexer_type = None

    def _deserialize(self):
        self.terminals_by_name = {t.name: t for t in self.terminals}

    def __deepcopy__(self, memo=None):
        return type(self)(
            deepcopy(self.terminals, memo),
            self.re_module,
            deepcopy(self.ignore, memo),
            deepcopy(self.postlex, memo),
            deepcopy(self.callbacks, memo),
            deepcopy(self.g_regex_flags, memo),
            deepcopy(self.skip_validation, memo),
            deepcopy(self.use_bytes, memo),
        )


class ParserConf(Serialize):
    __serialize_fields__ = "rules", "start", "parser_type"

    def __init__(self, rules, callbacks, start):
        assert isinstance(start, list)
        self.rules = rules
        self.callbacks = callbacks
        self.start = start

        self.parser_type = None


from functools import partial, wraps
from itertools import product, repeat


class ExpandSingleChild:
    def __init__(self, node_builder):
        self.node_builder = node_builder

    def __call__(self, children):
        if len(children) == 1:
            return children[0]
        else:
            return self.node_builder(children)


class PropagatePositions:
    def __init__(self, node_builder, node_filter=None):
        self.node_builder = node_builder
        self.node_filter = node_filter

    def __call__(self, children):
        res = self.node_builder(children)

        if isinstance(res, Tree):
            ##

            ##

            ##

            ##

            res_meta = res.meta

            first_meta = self._pp_get_meta(children)
            if first_meta is not None:
                if not hasattr(res_meta, "line"):
                    ##

                    res_meta.line = getattr(
                        first_meta, "container_line", first_meta.line
                    )
                    res_meta.column = getattr(
                        first_meta, "container_column", first_meta.column
                    )
                    res_meta.start_pos = getattr(
                        first_meta, "container_start_pos", first_meta.start_pos
                    )
                    res_meta.empty = False

                res_meta.container_line = getattr(
                    first_meta, "container_line", first_meta.line
                )
                res_meta.container_column = getattr(
                    first_meta, "container_column", first_meta.column
                )

            last_meta = self._pp_get_meta(reversed(children))
            if last_meta is not None:
                if not hasattr(res_meta, "end_line"):
                    res_meta.end_line = getattr(
                        last_meta, "container_end_line", last_meta.end_line
                    )
                    res_meta.end_column = getattr(
                        last_meta, "container_end_column", last_meta.end_column
                    )
                    res_meta.end_pos = getattr(
                        last_meta, "container_end_pos", last_meta.end_pos
                    )
                    res_meta.empty = False

                res_meta.container_end_line = getattr(
                    last_meta, "container_end_line", last_meta.end_line
                )
                res_meta.container_end_column = getattr(
                    last_meta, "container_end_column", last_meta.end_column
                )

        return res

    def _pp_get_meta(self, children):
        for c in children:
            if self.node_filter is not None and not self.node_filter(c):
                continue
            if isinstance(c, Tree):
                if not c.meta.empty:
                    return c.meta
            elif isinstance(c, Token):
                return c


def make_propagate_positions(option):
    if callable(option):
        return partial(PropagatePositions, node_filter=option)
    elif option is True:
        return PropagatePositions
    elif option is False:
        return None

    raise ConfigurationError("Invalid option for propagate_positions: %r" % option)


class ChildFilter:
    def __init__(self, to_include, append_none, node_builder):
        self.node_builder = node_builder
        self.to_include = to_include
        self.append_none = append_none

    def __call__(self, children):
        filtered = []

        for i, to_expand, add_none in self.to_include:
            if add_none:
                filtered += [None] * add_none
            if to_expand:
                filtered += children[i].children
            else:
                filtered.append(children[i])

        if self.append_none:
            filtered += [None] * self.append_none

        return self.node_builder(filtered)


class ChildFilterLALR(ChildFilter):
    # --

    def __call__(self, children):
        filtered = []
        for i, to_expand, add_none in self.to_include:
            if add_none:
                filtered += [None] * add_none
            if to_expand:
                if filtered:
                    filtered += children[i].children
                else:  ##

                    filtered = children[i].children
            else:
                filtered.append(children[i])

        if self.append_none:
            filtered += [None] * self.append_none

        return self.node_builder(filtered)


class ChildFilterLALR_NoPlaceholders(ChildFilter):
    # --
    def __init__(self, to_include, node_builder):
        self.node_builder = node_builder
        self.to_include = to_include

    def __call__(self, children):
        filtered = []
        for i, to_expand in self.to_include:
            if to_expand:
                if filtered:
                    filtered += children[i].children
                else:  ##

                    filtered = children[i].children
            else:
                filtered.append(children[i])
        return self.node_builder(filtered)


def _should_expand(sym):
    return not sym.is_term and sym.name.startswith("_")


def maybe_create_child_filter(
    expansion, keep_all_tokens, ambiguous, _empty_indices: List[bool]
):
    ##

    if _empty_indices:
        assert _empty_indices.count(False) == len(expansion)
        s = "".join(str(int(b)) for b in _empty_indices)
        empty_indices = [len(ones) for ones in s.split("0")]
        assert len(empty_indices) == len(expansion) + 1, (empty_indices, len(expansion))
    else:
        empty_indices = [0] * (len(expansion) + 1)

    to_include = []
    nones_to_add = 0
    for i, sym in enumerate(expansion):
        nones_to_add += empty_indices[i]
        if keep_all_tokens or not (sym.is_term and sym.filter_out):
            to_include.append((i, _should_expand(sym), nones_to_add))
            nones_to_add = 0

    nones_to_add += empty_indices[len(expansion)]

    if (
        _empty_indices
        or len(to_include) < len(expansion)
        or any(to_expand for i, to_expand, _ in to_include)
    ):
        if _empty_indices or ambiguous:
            return partial(
                ChildFilter if ambiguous else ChildFilterLALR, to_include, nones_to_add
            )
        else:
            ##

            return partial(
                ChildFilterLALR_NoPlaceholders, [(i, x) for i, x, _ in to_include]
            )


class AmbiguousExpander:
    # --
    def __init__(self, to_expand, tree_class, node_builder):
        self.node_builder = node_builder
        self.tree_class = tree_class
        self.to_expand = to_expand

    def __call__(self, children):
        def _is_ambig_tree(t):
            return hasattr(t, "data") and t.data == "_ambig"

        ##

        ##

        ##

        ##

        ambiguous = []
        for i, child in enumerate(children):
            if _is_ambig_tree(child):
                if i in self.to_expand:
                    ambiguous.append(i)

                child.expand_kids_by_data("_ambig")

        if not ambiguous:
            return self.node_builder(children)

        expand = [
            iter(child.children) if i in ambiguous else repeat(child)
            for i, child in enumerate(children)
        ]
        return self.tree_class(
            "_ambig", [self.node_builder(list(f[0])) for f in product(zip(*expand))]
        )


def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens):
    to_expand = [
        i
        for i, sym in enumerate(expansion)
        if keep_all_tokens
        or ((not (sym.is_term and sym.filter_out)) and _should_expand(sym))
    ]
    if to_expand:
        return partial(AmbiguousExpander, to_expand, tree_class)


class AmbiguousIntermediateExpander:
    # --

    def __init__(self, tree_class, node_builder):
        self.node_builder = node_builder
        self.tree_class = tree_class

    def __call__(self, children):
        def _is_iambig_tree(child):
            return hasattr(child, "data") and child.data == "_iambig"

        def _collapse_iambig(children):
            # --

            ##

            ##

            if children and _is_iambig_tree(children[0]):
                iambig_node = children[0]
                result = []
                for grandchild in iambig_node.children:
                    collapsed = _collapse_iambig(grandchild.children)
                    if collapsed:
                        for child in collapsed:
                            child.children += children[1:]
                        result += collapsed
                    else:
                        new_tree = self.tree_class(
                            "_inter", grandchild.children + children[1:]
                        )
                        result.append(new_tree)
                return result

        collapsed = _collapse_iambig(children)
        if collapsed:
            processed_nodes = [self.node_builder(c.children) for c in collapsed]
            return self.tree_class("_ambig", processed_nodes)

        return self.node_builder(children)


def inplace_transformer(func):
    @wraps(func)
    def f(children):
        ##

        tree = Tree(func.__name__, children)
        return func(tree)

    return f


def apply_visit_wrapper(func, name, wrapper):
    if wrapper is _vargs_meta or wrapper is _vargs_meta_inline:
        raise NotImplementedError("Meta args not supported for internal transformer")

    @wraps(func)
    def f(children):
        return wrapper(func, name, children, None)

    return f


class ParseTreeBuilder:
    def __init__(
        self,
        rules,
        tree_class,
        propagate_positions=False,
        ambiguous=False,
        maybe_placeholders=False,
    ):
        self.tree_class = tree_class
        self.propagate_positions = propagate_positions
        self.ambiguous = ambiguous
        self.maybe_placeholders = maybe_placeholders

        self.rule_builders = list(self._init_builders(rules))

    def _init_builders(self, rules):
        propagate_positions = make_propagate_positions(self.propagate_positions)

        for rule in rules:
            options = rule.options
            keep_all_tokens = options.keep_all_tokens
            expand_single_child = options.expand1

            wrapper_chain = list(
                filter(
                    None,
                    [
                        (expand_single_child and not rule.alias) and ExpandSingleChild,
                        maybe_create_child_filter(
                            rule.expansion,
                            keep_all_tokens,
                            self.ambiguous,
                            options.empty_indices if self.maybe_placeholders else None,
                        ),
                        propagate_positions,
                        self.ambiguous
                        and maybe_create_ambiguous_expander(
                            self.tree_class, rule.expansion, keep_all_tokens
                        ),
                        self.ambiguous
                        and partial(AmbiguousIntermediateExpander, self.tree_class),
                    ],
                )
            )

            yield rule, wrapper_chain

    def create_callback(self, transformer=None):
        callbacks = {}

        default_handler = getattr(transformer, "__default__", None)
        if default_handler:

            def default_callback(data, children):
                return default_handler(data, children, None)

        else:
            default_callback = self.tree_class

        for rule, wrapper_chain in self.rule_builders:

            user_callback_name = (
                rule.alias or rule.options.template_source or rule.origin.name
            )
            try:
                f = getattr(transformer, user_callback_name)
                wrapper = getattr(f, "visit_wrapper", None)
                if wrapper is not None:
                    f = apply_visit_wrapper(f, user_callback_name, wrapper)
                elif isinstance(transformer, Transformer_InPlace):
                    f = inplace_transformer(f)
            except AttributeError:
                f = partial(default_callback, user_callback_name)

            for w in wrapper_chain:
                f = w(f)

            if rule in callbacks:
                raise GrammarError("Rule '%s' already exists" % (rule,))

            callbacks[rule] = f

        return callbacks


class LALR_Parser(Serialize):
    def __init__(self, parser_conf, debug=False):
        analysis = LALR_Analyzer(parser_conf, debug=debug)
        analysis.compute_lalr()
        callbacks = parser_conf.callbacks

        self._parse_table = analysis.parse_table
        self.parser_conf = parser_conf
        self.parser = _Parser(analysis.parse_table, callbacks, debug)

    @classmethod
    def deserialize(cls, data, memo, callbacks, debug=False):
        inst = cls.__new__(cls)
        inst._parse_table = IntParseTable.deserialize(data, memo)
        inst.parser = _Parser(inst._parse_table, callbacks, debug)
        return inst

    def serialize(self, memo):
        return self._parse_table.serialize(memo)

    def parse_interactive(self, lexer, start):
        return self.parser.parse(lexer, start, start_interactive=True)

    def parse(self, lexer, start, on_error=None):
        try:
            return self.parser.parse(lexer, start)
        except UnexpectedInput as e:
            if on_error is None:
                raise

            while True:
                if isinstance(e, UnexpectedCharacters):
                    s = e.interactive_parser.lexer_thread.state
                    p = s.line_ctr.char_pos

                if not on_error(e):
                    raise e

                if isinstance(e, UnexpectedCharacters):
                    ##

                    if p == s.line_ctr.char_pos:
                        s.line_ctr.feed(s.text[p : p + 1])

                try:
                    return e.interactive_parser.resume_parse()
                except UnexpectedToken as e2:
                    if (
                        isinstance(e, UnexpectedToken)
                        and e.token.type == e2.token.type == "$END"
                        and e.interactive_parser == e2.interactive_parser
                    ):
                        ##

                        raise e2
                    e = e2
                except UnexpectedCharacters as e2:
                    e = e2


class ParseConf:
    __slots__ = (
        "parse_table",
        "callbacks",
        "start",
        "start_state",
        "end_state",
        "states",
    )

    def __init__(self, parse_table, callbacks, start):
        self.parse_table = parse_table

        self.start_state = self.parse_table.start_states[start]
        self.end_state = self.parse_table.end_states[start]
        self.states = self.parse_table.states

        self.callbacks = callbacks
        self.start = start


class ParserState:
    __slots__ = "parse_conf", "lexer", "state_stack", "value_stack"

    def __init__(self, parse_conf, lexer, state_stack=None, value_stack=None):
        self.parse_conf = parse_conf
        self.lexer = lexer
        self.state_stack = state_stack or [self.parse_conf.start_state]
        self.value_stack = value_stack or []

    @property
    def position(self):
        return self.state_stack[-1]

    ##

    def __eq__(self, other):
        if not isinstance(other, ParserState):
            return NotImplemented
        return (
            len(self.state_stack) == len(other.state_stack)
            and self.position == other.position
        )

    def __copy__(self):
        return type(self)(
            self.parse_conf,
            self.lexer,  ##
            copy(self.state_stack),
            deepcopy(self.value_stack),
        )

    def copy(self):
        return copy(self)

    def feed_token(self, token, is_end=False):
        state_stack = self.state_stack
        value_stack = self.value_stack
        states = self.parse_conf.states
        end_state = self.parse_conf.end_state
        callbacks = self.parse_conf.callbacks

        while True:
            state = state_stack[-1]
            try:
                action, arg = states[state][token.type]
            except KeyError:
                expected = {s for s in states[state].keys() if s.isupper()}
                raise UnexpectedToken(
                    token, expected, state=self, interactive_parser=None
                )

            assert arg != end_state

            if action is Shift:
                ##

                assert not is_end
                state_stack.append(arg)
                value_stack.append(
                    token
                    if token.type not in callbacks
                    else callbacks[token.type](token)
                )
                return
            else:
                ##

                rule = arg
                size = len(rule.expansion)
                if size:
                    s = value_stack[-size:]
                    del state_stack[-size:]
                    del value_stack[-size:]
                else:
                    s = []

                value = callbacks[rule](s)

                _action, new_state = states[state_stack[-1]][rule.origin.name]
                assert _action is Shift
                state_stack.append(new_state)
                value_stack.append(value)

                if is_end and state_stack[-1] == end_state:
                    return value_stack[-1]


class _Parser:
    def __init__(self, parse_table, callbacks, debug=False):
        self.parse_table = parse_table
        self.callbacks = callbacks
        self.debug = debug

    def parse(
        self, lexer, start, value_stack=None, state_stack=None, start_interactive=False
    ):
        parse_conf = ParseConf(self.parse_table, self.callbacks, start)
        parser_state = ParserState(parse_conf, lexer, state_stack, value_stack)
        if start_interactive:
            return InteractiveParser(self, parser_state, parser_state.lexer)
        return self.parse_from_state(parser_state)

    def parse_from_state(self, state):
        ##

        try:
            token = None
            for token in state.lexer.lex(state):
                state.feed_token(token)

            end_token = (
                Token.new_borrow_pos("$END", "", token)
                if token
                else Token("$END", "", 0, 1, 1)
            )
            return state.feed_token(end_token, True)
        except UnexpectedInput as e:
            try:
                e.interactive_parser = InteractiveParser(self, state, state.lexer)
            except NameError:
                pass
            raise e
        except Exception as e:
            if self.debug:
                print("")
                print("STATE STACK DUMP")
                print("----------------")
                for i, s in enumerate(state.state_stack):
                    print("%d)" % i, s)
                print("")

            raise


class Action:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def __repr__(self):
        return str(self)


Shift = Action("Shift")
Reduce = Action("Reduce")


class ParseTable:
    def __init__(self, states, start_states, end_states):
        self.states = states
        self.start_states = start_states
        self.end_states = end_states

    def serialize(self, memo):
        tokens = Enumerator()
        rules = Enumerator()

        states = {
            state: {
                tokens.get(token): (
                    (1, arg.serialize(memo)) if action is Reduce else (0, arg)
                )
                for token, (action, arg) in actions.items()
            }
            for state, actions in self.states.items()
        }

        return {
            "tokens": tokens.reversed(),
            "states": states,
            "start_states": self.start_states,
            "end_states": self.end_states,
        }

    @classmethod
    def deserialize(cls, data, memo):
        tokens = data["tokens"]
        states = {
            state: {
                tokens[token]: (
                    (Reduce, Rule.deserialize(arg, memo))
                    if action == 1
                    else (Shift, arg)
                )
                for token, (action, arg) in actions.items()
            }
            for state, actions in data["states"].items()
        }
        return cls(states, data["start_states"], data["end_states"])


class IntParseTable(ParseTable):
    @classmethod
    def from_ParseTable(cls, parse_table):
        enum = list(parse_table.states)
        state_to_idx = {s: i for i, s in enumerate(enum)}
        int_states = {}

        for s, la in parse_table.states.items():
            la = {
                k: (v[0], state_to_idx[v[1]]) if v[0] is Shift else v
                for k, v in la.items()
            }
            int_states[state_to_idx[s]] = la

        start_states = {
            start: state_to_idx[s] for start, s in parse_table.start_states.items()
        }
        end_states = {
            start: state_to_idx[s] for start, s in parse_table.end_states.items()
        }
        return cls(int_states, start_states, end_states)


def _wrap_lexer(lexer_class):
    future_interface = getattr(lexer_class, "__future_interface__", False)
    if future_interface:
        return lexer_class
    else:

        class CustomLexerWrapper(Lexer):
            def __init__(self, lexer_conf):
                self.lexer = lexer_class(lexer_conf)

            def lex(self, lexer_state, parser_state):
                return self.lexer.lex(lexer_state.text)

        return CustomLexerWrapper


def _deserialize_parsing_frontend(data, memo, lexer_conf, callbacks, options):
    parser_conf = ParserConf.deserialize(data["parser_conf"], memo)
    cls = (options and options._plugins.get("LALR_Parser")) or LALR_Parser
    parser = cls.deserialize(data["parser"], memo, callbacks, options.debug)
    parser_conf.callbacks = callbacks
    return ParsingFrontend(lexer_conf, parser_conf, options, parser=parser)


_parser_creators: "Dict[str, Callable[[LexerConf, Any, Any], Any]]" = {}


class ParsingFrontend(Serialize):
    __serialize_fields__ = "lexer_conf", "parser_conf", "parser"

    def __init__(self, lexer_conf, parser_conf, options, parser=None):
        self.parser_conf = parser_conf
        self.lexer_conf = lexer_conf
        self.options = options

        ##

        if parser:  ##

            self.parser = parser
        else:
            create_parser = _parser_creators.get(parser_conf.parser_type)
            assert (
                create_parser is not None
            ), "{} is not supported in standalone mode".format(parser_conf.parser_type)
            self.parser = create_parser(lexer_conf, parser_conf, options)

        ##

        lexer_type = lexer_conf.lexer_type
        self.skip_lexer = False
        if lexer_type in ("dynamic", "dynamic_complete"):
            assert lexer_conf.postlex is None
            self.skip_lexer = True
            return

        try:
            create_lexer = {
                "basic": create_basic_lexer,
                "contextual": create_contextual_lexer,
            }[lexer_type]
        except KeyError:
            assert issubclass(lexer_type, Lexer), lexer_type
            self.lexer = _wrap_lexer(lexer_type)(lexer_conf)
        else:
            self.lexer = create_lexer(
                lexer_conf, self.parser, lexer_conf.postlex, options
            )

        if lexer_conf.postlex:
            self.lexer = PostLexConnector(self.lexer, lexer_conf.postlex)

    def _verify_start(self, start=None):
        if start is None:
            start_decls = self.parser_conf.start
            if len(start_decls) > 1:
                raise ConfigurationError(
                    "Lark initialized with more than 1 possible start rule. Must specify which start rule to parse",
                    start_decls,
                )
            (start,) = start_decls
        elif start not in self.parser_conf.start:
            raise ConfigurationError(
                "Unknown start rule %s. Must be one of %r"
                % (start, self.parser_conf.start)
            )
        return start

    def _make_lexer_thread(self, text):
        cls = (self.options and self.options._plugins.get("LexerThread")) or LexerThread
        return text if self.skip_lexer else cls.from_text(self.lexer, text)

    def parse(self, text, start=None, on_error=None):
        chosen_start = self._verify_start(start)
        kw = {} if on_error is None else {"on_error": on_error}
        stream = self._make_lexer_thread(text)
        return self.parser.parse(stream, chosen_start, **kw)

    def parse_interactive(self, text=None, start=None):
        chosen_start = self._verify_start(start)
        if self.parser_conf.parser_type != "lalr":
            raise ConfigurationError(
                "parse_interactive() currently only works with parser='lalr' "
            )
        stream = self._make_lexer_thread(text)
        return self.parser.parse_interactive(stream, chosen_start)


def _validate_frontend_args(parser, lexer) -> None:
    assert_config(parser, ("lalr", "earley", "cyk"))
    if not isinstance(lexer, type):  ##

        expected = {
            "lalr": ("basic", "contextual"),
            "earley": ("basic", "dynamic", "dynamic_complete"),
            "cyk": ("basic",),
        }[parser]
        assert_config(
            lexer,
            expected,
            "Parser %r does not support lexer %%r, expected one of %%s" % parser,
        )


def _get_lexer_callbacks(transformer, terminals):
    result = {}
    for terminal in terminals:
        callback = getattr(transformer, terminal.name, None)
        if callback is not None:
            result[terminal.name] = callback
    return result


class PostLexConnector:
    def __init__(self, lexer, postlexer):
        self.lexer = lexer
        self.postlexer = postlexer

    def lex(self, lexer_state, parser_state):
        i = self.lexer.lex(lexer_state, parser_state)
        return self.postlexer.process(i)


def create_basic_lexer(lexer_conf, parser, postlex, options):
    cls = (options and options._plugins.get("BasicLexer")) or BasicLexer
    return cls(lexer_conf)


def create_contextual_lexer(lexer_conf, parser, postlex, options):
    cls = (options and options._plugins.get("ContextualLexer")) or ContextualLexer
    states = {idx: list(t.keys()) for idx, t in parser._parse_table.states.items()}
    always_accept = postlex.always_accept if postlex else ()
    return cls(lexer_conf, states, always_accept=always_accept)


def create_lalr_parser(lexer_conf, parser_conf, options=None):
    debug = options.debug if options else False
    cls = (options and options._plugins.get("LALR_Parser")) or LALR_Parser
    return cls(parser_conf, debug=debug)


_parser_creators["lalr"] = create_lalr_parser


class PostLex(ABC):
    @abstractmethod
    def process(self, stream: Iterator[Token]) -> Iterator[Token]:
        return stream

    always_accept: Iterable[str] = ()


class LarkOptions(Serialize):
    # --

    start: List[str]
    debug: bool
    transformer: "Optional[Transformer]"
    propagate_positions: Union[bool, str]
    maybe_placeholders: bool
    cache: Union[bool, str]
    regex: bool
    g_regex_flags: int
    keep_all_tokens: bool
    tree_class: Any
    parser: _ParserArgType
    lexer: _LexerArgType
    ambiguity: 'Literal["auto", "resolve", "explicit", "forest"]'
    postlex: Optional[PostLex]
    priority: 'Optional[Literal["auto", "normal", "invert"]]'
    lexer_callbacks: Dict[str, Callable[[Token], Token]]
    use_bytes: bool
    edit_terminals: Optional[Callable[[TerminalDef], TerminalDef]]
    import_paths: "List[Union[str, Callable[[Union[None, str, PackageResource], str], Tuple[str, str]]]]"
    source_path: Optional[str]

    OPTIONS_DOC = """
    **===  General Options  ===**

    start
            The start symbol. Either a string, or a list of strings for multiple possible starts (Default: "start")
    debug
            Display debug information and extra warnings. Use only when debugging (Default: ``False``)
            When used with Earley, it generates a forest graph as "sppf.png", if 'dot' is installed.
    transformer
            Applies the transformer to every parse tree (equivalent to applying it after the parse, but faster)
    propagate_positions
            Propagates (line, column, end_line, end_column) attributes into all tree branches.
            Accepts ``False``, ``True``, or a callable, which will filter which nodes to ignore when propagating.
    maybe_placeholders
            When ``True``, the ``[]`` operator returns ``None`` when not matched.
            When ``False``,  ``[]`` behaves like the ``?`` operator, and returns no value at all.
            (default= ``True``)
    cache
            Cache the results of the Lark grammar analysis, for x2 to x3 faster loading. LALR only for now.

            - When ``False``, does nothing (default)
            - When ``True``, caches to a temporary file in the local directory
            - When given a string, caches to the path pointed by the string
    regex
            When True, uses the ``regex`` module instead of the stdlib ``re``.
    g_regex_flags
            Flags that are applied to all terminals (both regex and strings)
    keep_all_tokens
            Prevent the tree builder from automagically removing "punctuation" tokens (Default: ``False``)
    tree_class
            Lark will produce trees comprised of instances of this class instead of the default ``lark.Tree``.

    **=== Algorithm Options ===**

    parser
            Decides which parser engine to use. Accepts "earley" or "lalr". (Default: "earley").
            (there is also a "cyk" option for legacy)
    lexer
            Decides whether or not to use a lexer stage

            - "auto" (default): Choose for me based on the parser
            - "basic": Use a basic lexer
            - "contextual": Stronger lexer (only works with parser="lalr")
            - "dynamic": Flexible and powerful (only with parser="earley")
            - "dynamic_complete": Same as dynamic, but tries *every* variation of tokenizing possible.
    ambiguity
            Decides how to handle ambiguity in the parse. Only relevant if parser="earley"

            - "resolve": The parser will automatically choose the simplest derivation
              (it chooses consistently: greedy for tokens, non-greedy for rules)
            - "explicit": The parser will return all derivations wrapped in "_ambig" tree nodes (i.e. a forest).
            - "forest": The parser will return the root of the shared packed parse forest.

    **=== Misc. / Domain Specific Options ===**

    postlex
            Lexer post-processing (Default: ``None``) Only works with the basic and contextual lexers.
    priority
            How priorities should be evaluated - "auto", ``None``, "normal", "invert" (Default: "auto")
    lexer_callbacks
            Dictionary of callbacks for the lexer. May alter tokens during lexing. Use with caution.
    use_bytes
            Accept an input of type ``bytes`` instead of ``str``.
    edit_terminals
            A callback for editing the terminals before parse.
    import_paths
            A List of either paths or loader functions to specify from where grammars are imported
    source_path
            Override the source of from where the grammar was loaded. Useful for relative imports and unconventional grammar loading
    **=== End of Options ===**
    """
    if __doc__:
        __doc__ += OPTIONS_DOC

    ##

    ##

    ##

    ##

    ##

    ##

    _defaults: Dict[str, Any] = {
        "debug": False,
        "keep_all_tokens": False,
        "tree_class": None,
        "cache": False,
        "postlex": None,
        "parser": "earley",
        "lexer": "auto",
        "transformer": None,
        "start": "start",
        "priority": "auto",
        "ambiguity": "auto",
        "regex": False,
        "propagate_positions": False,
        "lexer_callbacks": {},
        "maybe_placeholders": True,
        "edit_terminals": None,
        "g_regex_flags": 0,
        "use_bytes": False,
        "import_paths": [],
        "source_path": None,
        "_plugins": {},
    }

    def __init__(self, options_dict):
        o = dict(options_dict)

        options = {}
        for name, default in self._defaults.items():
            if name in o:
                value = o.pop(name)
                if isinstance(default, bool) and name not in (
                    "cache",
                    "use_bytes",
                    "propagate_positions",
                ):
                    value = bool(value)
            else:
                value = default

            options[name] = value

        if isinstance(options["start"], str):
            options["start"] = [options["start"]]

        self.__dict__["options"] = options

        assert_config(self.parser, ("earley", "lalr", "cyk", None))

        if self.parser == "earley" and self.transformer:
            raise ConfigurationError(
                "Cannot specify an embedded transformer when using the Earley algorithm. "
                "Please use your transformer on the resulting parse tree, or use a different algorithm (i.e. LALR)"
            )

        if o:
            raise ConfigurationError("Unknown options: %s" % o.keys())

    def __getattr__(self, name):
        try:
            return self.__dict__["options"][name]
        except KeyError as e:
            raise AttributeError(e)

    def __setattr__(self, name, value):
        assert_config(
            name, self.options.keys(), "%r isn't a valid option. Expected one of: %s"
        )
        self.options[name] = value

    def serialize(self, memo):
        return self.options

    @classmethod
    def deserialize(cls, data, memo):
        return cls(data)


##

##

_LOAD_ALLOWED_OPTIONS = {
    "postlex",
    "transformer",
    "lexer_callbacks",
    "use_bytes",
    "debug",
    "g_regex_flags",
    "regex",
    "propagate_positions",
    "tree_class",
    "_plugins",
}

_VALID_PRIORITY_OPTIONS = ("auto", "normal", "invert", None)
_VALID_AMBIGUITY_OPTIONS = ("auto", "resolve", "explicit", "forest")


_T = TypeVar("_T", bound="Lark")


class Lark(Serialize):
    # --

    source_path: str
    source_grammar: str
    grammar: "Grammar"
    options: LarkOptions
    lexer: Lexer
    terminals: List[TerminalDef]

    def __init__(self, grammar: "Union[Grammar, str, IO[str]]", **options) -> None:
        self.options = LarkOptions(options)

        ##

        use_regex = self.options.regex
        if use_regex:
            if regex:
                re_module = regex
            else:
                raise ImportError(
                    "`regex` module must be installed if calling `Lark(regex=True)`."
                )
        else:
            re_module = re

        ##

        if self.options.source_path is None:
            try:
                self.source_path = grammar.name
            except AttributeError:
                self.source_path = "<string>"
        else:
            self.source_path = self.options.source_path

        ##

        try:
            read = grammar.read
        except AttributeError:
            pass
        else:
            grammar = read()

        cache_fn = None
        cache_md5 = None
        if isinstance(grammar, str):
            self.source_grammar = grammar
            if self.options.use_bytes:
                if not isascii(grammar):
                    raise ConfigurationError(
                        "Grammar must be ascii only, when use_bytes=True"
                    )

            if self.options.cache:
                if self.options.parser != "lalr":
                    raise ConfigurationError(
                        "cache only works with parser='lalr' for now"
                    )

                unhashable = (
                    "transformer",
                    "postlex",
                    "lexer_callbacks",
                    "edit_terminals",
                    "_plugins",
                )
                options_str = "".join(
                    k + str(v) for k, v in options.items() if k not in unhashable
                )
                from . import __version__

                s = grammar + options_str + __version__ + str(sys.version_info[:2])
                cache_md5 = hashlib.md5(s.encode("utf8")).hexdigest()

                if isinstance(self.options.cache, str):
                    cache_fn = self.options.cache
                else:
                    if self.options.cache is not True:
                        raise ConfigurationError("cache argument must be bool or str")

                    cache_fn = tempfile.gettempdir() + "/.lark_cache_%s_%s_%s.tmp" % (
                        cache_md5,
                        *sys.version_info[:2],
                    )

                if FS.exists(cache_fn):
                    logger.debug("Loading grammar from cache: %s", cache_fn)
                    ##

                    for name in set(options) - _LOAD_ALLOWED_OPTIONS:
                        del options[name]
                    with FS.open(cache_fn, "rb") as f:
                        old_options = self.options
                        try:
                            file_md5 = f.readline().rstrip(b"\n")
                            cached_used_files = pickle.load(f)
                            if file_md5 == cache_md5.encode(
                                "utf8"
                            ) and verify_used_files(cached_used_files):
                                cached_parser_data = pickle.load(f)
                                self._load(cached_parser_data, **options)
                                return
                        except Exception:  ##

                            logger.exception(
                                "Failed to load Lark from cache: %r. We will try to carry on."
                                % cache_fn
                            )

                            ##

                            ##

                            self.options = old_options

            ##

            self.grammar, used_files = load_grammar(
                grammar,
                self.source_path,
                self.options.import_paths,
                self.options.keep_all_tokens,
            )
        else:
            assert isinstance(grammar, Grammar)
            self.grammar = grammar

        if self.options.lexer == "auto":
            if self.options.parser == "lalr":
                self.options.lexer = "contextual"
            elif self.options.parser == "earley":
                if self.options.postlex is not None:
                    logger.info(
                        "postlex can't be used with the dynamic lexer, so we use 'basic' instead. "
                        "Consider using lalr with contextual instead of earley"
                    )
                    self.options.lexer = "basic"
                else:
                    self.options.lexer = "dynamic"
            elif self.options.parser == "cyk":
                self.options.lexer = "basic"
            else:
                assert False, self.options.parser
        lexer = self.options.lexer
        if isinstance(lexer, type):
            assert issubclass(lexer, Lexer)  ##

        else:
            assert_config(lexer, ("basic", "contextual", "dynamic", "dynamic_complete"))
            if self.options.postlex is not None and "dynamic" in lexer:
                raise ConfigurationError(
                    "Can't use postlex with a dynamic lexer. Use basic or contextual instead"
                )

        if self.options.ambiguity == "auto":
            if self.options.parser == "earley":
                self.options.ambiguity = "resolve"
        else:
            assert_config(
                self.options.parser,
                ("earley", "cyk"),
                "%r doesn't support disambiguation. Use one of these parsers instead: %s",
            )

        if self.options.priority == "auto":
            self.options.priority = "normal"

        if self.options.priority not in _VALID_PRIORITY_OPTIONS:
            raise ConfigurationError(
                "invalid priority option: %r. Must be one of %r"
                % (self.options.priority, _VALID_PRIORITY_OPTIONS)
            )
        if self.options.ambiguity not in _VALID_AMBIGUITY_OPTIONS:
            raise ConfigurationError(
                "invalid ambiguity option: %r. Must be one of %r"
                % (self.options.ambiguity, _VALID_AMBIGUITY_OPTIONS)
            )

        if self.options.parser is None:
            terminals_to_keep = "*"
        elif self.options.postlex is not None:
            terminals_to_keep = set(self.options.postlex.always_accept)
        else:
            terminals_to_keep = set()

        ##

        self.terminals, self.rules, self.ignore_tokens = self.grammar.compile(
            self.options.start, terminals_to_keep
        )

        if self.options.edit_terminals:
            for t in self.terminals:
                self.options.edit_terminals(t)

        self._terminals_dict = {t.name: t for t in self.terminals}

        ##

        if self.options.priority == "invert":
            for rule in self.rules:
                if rule.options.priority is not None:
                    rule.options.priority = -rule.options.priority
            for term in self.terminals:
                term.priority = -term.priority
        ##

        ##

        ##

        elif self.options.priority is None:
            for rule in self.rules:
                if rule.options.priority is not None:
                    rule.options.priority = None
            for term in self.terminals:
                term.priority = 0

        ##

        self.lexer_conf = LexerConf(
            self.terminals,
            re_module,
            self.ignore_tokens,
            self.options.postlex,
            self.options.lexer_callbacks,
            self.options.g_regex_flags,
            use_bytes=self.options.use_bytes,
        )

        if self.options.parser:
            self.parser = self._build_parser()
        elif lexer:
            self.lexer = self._build_lexer()

        if cache_fn:
            logger.debug("Saving grammar to cache: %s", cache_fn)
            with FS.open(cache_fn, "wb") as f:
                assert cache_md5 is not None
                f.write(cache_md5.encode("utf8") + b"\n")
                pickle.dump(used_files, f)
                self.save(f, _LOAD_ALLOWED_OPTIONS)

    if __doc__:
        __doc__ += "\n\n" + LarkOptions.OPTIONS_DOC

    __serialize_fields__ = "parser", "rules", "options"

    def _build_lexer(self, dont_ignore=False):
        lexer_conf = self.lexer_conf
        if dont_ignore:
            from copy import copy

            lexer_conf = copy(lexer_conf)
            lexer_conf.ignore = ()
        return BasicLexer(lexer_conf)

    def _prepare_callbacks(self):
        self._callbacks = {}
        ##

        if self.options.ambiguity != "forest":
            self._parse_tree_builder = ParseTreeBuilder(
                self.rules,
                self.options.tree_class or Tree,
                self.options.propagate_positions,
                self.options.parser != "lalr" and self.options.ambiguity == "explicit",
                self.options.maybe_placeholders,
            )
            self._callbacks = self._parse_tree_builder.create_callback(
                self.options.transformer
            )
        self._callbacks.update(
            _get_lexer_callbacks(self.options.transformer, self.terminals)
        )

    def _build_parser(self):
        self._prepare_callbacks()
        _validate_frontend_args(self.options.parser, self.options.lexer)
        parser_conf = ParserConf(self.rules, self._callbacks, self.options.start)
        return _construct_parsing_frontend(
            self.options.parser,
            self.options.lexer,
            self.lexer_conf,
            parser_conf,
            options=self.options,
        )

    def save(self, f, exclude_options: Collection[str] = ()):
        # --
        data, m = self.memo_serialize([TerminalDef, Rule])
        if exclude_options:
            data["options"] = {
                n: v for n, v in data["options"].items() if n not in exclude_options
            }
        pickle.dump({"data": data, "memo": m}, f, protocol=pickle.HIGHEST_PROTOCOL)

    @classmethod
    def load(cls, f):
        # --
        inst = cls.__new__(cls)
        return inst._load(f)

    def _deserialize_lexer_conf(self, data, memo, options):
        lexer_conf = LexerConf.deserialize(data["lexer_conf"], memo)
        lexer_conf.callbacks = options.lexer_callbacks or {}
        lexer_conf.re_module = regex if options.regex else re
        lexer_conf.use_bytes = options.use_bytes
        lexer_conf.g_regex_flags = options.g_regex_flags
        lexer_conf.skip_validation = True
        lexer_conf.postlex = options.postlex
        return lexer_conf

    def _load(self, f, **kwargs):
        if isinstance(f, dict):
            d = f
        else:
            d = pickle.load(f)
        memo_json = d["memo"]
        data = d["data"]

        assert memo_json
        memo = SerializeMemoizer.deserialize(
            memo_json, {"Rule": Rule, "TerminalDef": TerminalDef}, {}
        )
        options = dict(data["options"])
        if (set(kwargs) - _LOAD_ALLOWED_OPTIONS) & set(LarkOptions._defaults):
            raise ConfigurationError(
                "Some options are not allowed when loading a Parser: {}".format(
                    set(kwargs) - _LOAD_ALLOWED_OPTIONS
                )
            )
        options.update(kwargs)
        self.options = LarkOptions.deserialize(options, memo)
        self.rules = [Rule.deserialize(r, memo) for r in data["rules"]]
        self.source_path = "<deserialized>"
        _validate_frontend_args(self.options.parser, self.options.lexer)
        self.lexer_conf = self._deserialize_lexer_conf(
            data["parser"], memo, self.options
        )
        self.terminals = self.lexer_conf.terminals
        self._prepare_callbacks()
        self._terminals_dict = {t.name: t for t in self.terminals}
        self.parser = _deserialize_parsing_frontend(
            data["parser"],
            memo,
            self.lexer_conf,
            self._callbacks,
            self.options,  ##
        )
        return self

    @classmethod
    def _load_from_dict(cls, data, memo, **kwargs):
        inst = cls.__new__(cls)
        return inst._load({"data": data, "memo": memo}, **kwargs)

    @classmethod
    def open(
        cls: Type[_T], grammar_filename: str, rel_to: Optional[str] = None, **options
    ) -> _T:
        # --
        if rel_to:
            basepath = os.path.dirname(rel_to)
            grammar_filename = os.path.join(basepath, grammar_filename)
        with open(grammar_filename, encoding="utf8") as f:
            return cls(f, **options)

    @classmethod
    def open_from_package(
        cls: Type[_T],
        package: str,
        grammar_path: str,
        search_paths: "Sequence[str]" = [""],
        **options,
    ) -> _T:
        # --
        package_loader = FromPackageLoader(package, search_paths)
        full_path, text = package_loader(None, grammar_path)
        options.setdefault("source_path", full_path)
        options.setdefault("import_paths", [])
        options["import_paths"].append(package_loader)
        return cls(text, **options)

    def __repr__(self):
        return "Lark(open(%r), parser=%r, lexer=%r, ...)" % (
            self.source_path,
            self.options.parser,
            self.options.lexer,
        )

    def lex(self, text: str, dont_ignore: bool = False) -> Iterator[Token]:
        # --
        if not hasattr(self, "lexer") or dont_ignore:
            lexer = self._build_lexer(dont_ignore)
        else:
            lexer = self.lexer
        lexer_thread = LexerThread.from_text(lexer, text)
        stream = lexer_thread.lex(None)
        if self.options.postlex:
            return self.options.postlex.process(stream)
        return stream

    def get_terminal(self, name: str) -> TerminalDef:
        # --
        return self._terminals_dict[name]

    def parse_interactive(
        self, text: Optional[str] = None, start: Optional[str] = None
    ) -> "InteractiveParser":
        # --
        return self.parser.parse_interactive(text, start=start)

    def parse(
        self,
        text: str,
        start: Optional[str] = None,
        on_error: "Optional[Callable[[UnexpectedInput], bool]]" = None,
    ) -> "ParseTree":
        # --
        return self.parser.parse(text, start=start, on_error=on_error)


class DedentError(LarkError):
    pass


class Indenter(PostLex, ABC):
    paren_level: int
    indent_level: List[int]

    def __init__(self) -> None:
        self.paren_level = 0
        self.indent_level = [0]
        assert self.tab_len > 0

    def handle_NL(self, token: Token) -> Iterator[Token]:
        if self.paren_level > 0:
            return

        yield token

        indent_str = token.rsplit("\n", 1)[1]  ##

        indent = indent_str.count(" ") + indent_str.count("\t") * self.tab_len

        if indent > self.indent_level[-1]:
            self.indent_level.append(indent)
            yield Token.new_borrow_pos(self.INDENT_type, indent_str, token)
        else:
            while indent < self.indent_level[-1]:
                self.indent_level.pop()
                yield Token.new_borrow_pos(self.DEDENT_type, indent_str, token)

            if indent != self.indent_level[-1]:
                raise DedentError(
                    "Unexpected dedent to column %s. Expected dedent to %s"
                    % (indent, self.indent_level[-1])
                )

    def _process(self, stream):
        for token in stream:
            if token.type == self.NL_type:
                yield from self.handle_NL(token)
            else:
                yield token

            if token.type in self.OPEN_PAREN_types:
                self.paren_level += 1
            elif token.type in self.CLOSE_PAREN_types:
                self.paren_level -= 1
                assert self.paren_level >= 0

        while len(self.indent_level) > 1:
            self.indent_level.pop()
            yield Token(self.DEDENT_type, "")

        assert self.indent_level == [0], self.indent_level

    def process(self, stream):
        self.paren_level = 0
        self.indent_level = [0]
        return self._process(stream)

    ##

    @property
    def always_accept(self):
        return (self.NL_type,)

    @property
    @abstractmethod
    def NL_type(self) -> str:
        raise NotImplementedError()

    @property
    @abstractmethod
    def OPEN_PAREN_types(self) -> List[str]:
        raise NotImplementedError()

    @property
    @abstractmethod
    def CLOSE_PAREN_types(self) -> List[str]:
        raise NotImplementedError()

    @property
    @abstractmethod
    def INDENT_type(self) -> str:
        raise NotImplementedError()

    @property
    @abstractmethod
    def DEDENT_type(self) -> str:
        raise NotImplementedError()

    @property
    @abstractmethod
    def tab_len(self) -> int:
        raise NotImplementedError()


class PythonIndenter(Indenter):
    NL_type = "_NEWLINE"
    OPEN_PAREN_types = ["LPAR", "LSQB", "LBRACE"]
    CLOSE_PAREN_types = ["RPAR", "RSQB", "RBRACE"]
    INDENT_type = "_INDENT"
    DEDENT_type = "_DEDENT"
    tab_len = 8


import base64
import pickle
import zlib

DATA = {
    "parser": {
        "lexer_conf": {
            "terminals": [
                {"@": 0},
                {"@": 1},
                {"@": 2},
                {"@": 3},
                {"@": 4},
                {"@": 5},
                {"@": 6},
                {"@": 7},
            ],
            "ignore": ["WS"],
            "g_regex_flags": 0,
            "use_bytes": False,
            "lexer_type": "contextual",
            "__type__": "LexerConf",
        },
        "parser_conf": {
            "rules": [
                {"@": 8},
                {"@": 9},
                {"@": 10},
                {"@": 11},
                {"@": 12},
                {"@": 13},
                {"@": 14},
                {"@": 15},
                {"@": 16},
                {"@": 17},
                {"@": 18},
                {"@": 19},
                {"@": 20},
                {"@": 21},
                {"@": 22},
            ],
            "start": ["unit", "quantity"],
            "parser_type": "lalr",
            "__type__": "ParserConf",
        },
        "parser": {
            "tokens": {
                0: "__unit_sequence_plus_1",
                1: "_MULTIPLY",
                2: "SYMBOL",
                3: "$END",
                4: "_DIVIDE",
                5: "term",
                6: "__unit_sequence_plus_0",
                7: "unit_sequence",
                8: "exponent",
                9: "CARAT_EXPONENT",
                10: "SUPERSCRIPT_EXPONENT",
                11: "magnitude",
                12: "SIGNED_FLOAT",
                13: "quantity",
                14: "SIGNED_INT",
                15: "unit",
            },
            "states": {
                0: {
                    0: (0, 22),
                    1: (0, 6),
                    2: (1, {"@": 19}),
                    3: (1, {"@": 19}),
                    4: (1, {"@": 19}),
                },
                1: {3: (1, {"@": 22}), 4: (1, {"@": 22}), 1: (1, {"@": 22})},
                2: {2: (0, 10), 5: (0, 0), 6: (0, 7), 7: (0, 13)},
                3: {3: (1, {"@": 21}), 4: (1, {"@": 21}), 1: (1, {"@": 21})},
                4: {},
                5: {4: (0, 2), 3: (1, {"@": 12})},
                6: {5: (0, 3), 2: (0, 10)},
                7: {2: (0, 10), 5: (0, 11), 3: (1, {"@": 13}), 4: (1, {"@": 13})},
                8: {3: (1, {"@": 8})},
                9: {2: (0, 10), 5: (0, 1)},
                10: {
                    8: (0, 12),
                    9: (0, 17),
                    10: (0, 16),
                    2: (1, {"@": 16}),
                    3: (1, {"@": 16}),
                    1: (1, {"@": 16}),
                    4: (1, {"@": 16}),
                },
                11: {2: (1, {"@": 20}), 3: (1, {"@": 20}), 4: (1, {"@": 20})},
                12: {
                    2: (1, {"@": 15}),
                    3: (1, {"@": 15}),
                    1: (1, {"@": 15}),
                    4: (1, {"@": 15}),
                },
                13: {3: (1, {"@": 11})},
                14: {},
                15: {11: (0, 18), 12: (0, 21), 13: (0, 14), 14: (0, 20)},
                16: {
                    2: (1, {"@": 18}),
                    3: (1, {"@": 18}),
                    4: (1, {"@": 18}),
                    1: (1, {"@": 18}),
                },
                17: {
                    2: (1, {"@": 17}),
                    3: (1, {"@": 17}),
                    4: (1, {"@": 17}),
                    1: (1, {"@": 17}),
                },
                18: {6: (0, 7), 15: (0, 8), 5: (0, 0), 7: (0, 5), 2: (0, 10)},
                19: {6: (0, 7), 15: (0, 4), 5: (0, 0), 7: (0, 5), 2: (0, 10)},
                20: {2: (1, {"@": 9})},
                21: {2: (1, {"@": 10})},
                22: {1: (0, 9), 3: (1, {"@": 14}), 4: (1, {"@": 14})},
            },
            "start_states": {"unit": 19, "quantity": 15},
            "end_states": {"unit": 4, "quantity": 14},
        },
        "__type__": "ParsingFrontend",
    },
    "rules": [
        {"@": 8},
        {"@": 9},
        {"@": 10},
        {"@": 11},
        {"@": 12},
        {"@": 13},
        {"@": 14},
        {"@": 15},
        {"@": 16},
        {"@": 17},
        {"@": 18},
        {"@": 19},
        {"@": 20},
        {"@": 21},
        {"@": 22},
    ],
    "options": {
        "debug": False,
        "keep_all_tokens": False,
        "tree_class": None,
        "cache": False,
        "postlex": None,
        "parser": "lalr",
        "lexer": "contextual",
        "transformer": None,
        "start": ["unit", "quantity"],
        "priority": "normal",
        "ambiguity": "auto",
        "regex": False,
        "propagate_positions": False,
        "lexer_callbacks": {},
        "maybe_placeholders": False,
        "edit_terminals": None,
        "g_regex_flags": 0,
        "use_bytes": False,
        "import_paths": [],
        "source_path": None,
        "_plugins": {},
    },
    "__type__": "Lark",
}
MEMO = {
    0: {
        "name": "SIGNED_INT",
        "pattern": {
            "value": "(?:(?:\\+|\\-))?(?:[0-9])+",
            "flags": [],
            "_width": [1, 4294967295],
            "__type__": "PatternRE",
        },
        "priority": 0,
        "__type__": "TerminalDef",
    },
    1: {
        "name": "SIGNED_FLOAT",
        "pattern": {
            "value": "(?:(?:\\+|\\-))?(?:(?:[0-9])+(?:e|E)(?:(?:\\+|\\-))?(?:[0-9])+|(?:(?:[0-9])+\\.(?:(?:[0-9])+)?|\\.(?:[0-9])+)(?:(?:e|E)(?:(?:\\+|\\-))?(?:[0-9])+)?)",
            "flags": [],
            "_width": [2, 4294967295],
            "__type__": "PatternRE",
        },
        "priority": 0,
        "__type__": "TerminalDef",
    },
    2: {
        "name": "WS",
        "pattern": {
            "value": "(?:[ \t\x0c\r\n])+",
            "flags": [],
            "_width": [1, 4294967295],
            "__type__": "PatternRE",
        },
        "priority": 0,
        "__type__": "TerminalDef",
    },
    3: {
        "name": "_MULTIPLY",
        "pattern": {
            "value": "(?:⋅|\\*)",
            "flags": [],
            "_width": [1, 1],
            "__type__": "PatternRE",
        },
        "priority": 0,
        "__type__": "TerminalDef",
    },
    4: {
        "name": "_DIVIDE",
        "pattern": {"value": "/", "flags": [], "__type__": "PatternStr"},
        "priority": 0,
        "__type__": "TerminalDef",
    },
    5: {
        "name": "CARAT_EXPONENT",
        "pattern": {
            "value": "\\^(?:(?:\\+|\\-))?(?:[0-9])+",
            "flags": [],
            "_width": [2, 4294967295],
            "__type__": "PatternRE",
        },
        "priority": 0,
        "__type__": "TerminalDef",
    },
    6: {
        "name": "SUPERSCRIPT_EXPONENT",
        "pattern": {
            "value": "(?:⁻)?(?:(?:⁰|¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹))+",
            "flags": [],
            "_width": [1, 4294967295],
            "__type__": "PatternRE",
        },
        "priority": 0,
        "__type__": "TerminalDef",
    },
    7: {
        "name": "SYMBOL",
        "pattern": {
            "value": "(?:(?:(?:\\.|°|\\-|\\(|\\))|(?:[A-Z]|Å)|[a-z]|[ₐ-ₜ]|[Α-ω]|1|☉))+",
            "flags": [],
            "_width": [1, 4294967295],
            "__type__": "PatternRE",
        },
        "priority": 0,
        "__type__": "TerminalDef",
    },
    8: {
        "origin": {"name": Token("RULE", "quantity"), "__type__": "NonTerminal"},
        "expansion": [
            {"name": "magnitude", "__type__": "NonTerminal"},
            {"name": "unit", "__type__": "NonTerminal"},
        ],
        "order": 0,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    9: {
        "origin": {"name": Token("RULE", "magnitude"), "__type__": "NonTerminal"},
        "expansion": [
            {"name": "SIGNED_INT", "filter_out": False, "__type__": "Terminal"}
        ],
        "order": 0,
        "alias": "int",
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    10: {
        "origin": {"name": Token("RULE", "magnitude"), "__type__": "NonTerminal"},
        "expansion": [
            {"name": "SIGNED_FLOAT", "filter_out": False, "__type__": "Terminal"}
        ],
        "order": 1,
        "alias": "float",
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    11: {
        "origin": {"name": Token("RULE", "unit"), "__type__": "NonTerminal"},
        "expansion": [
            {"name": "unit_sequence", "__type__": "NonTerminal"},
            {"name": "_DIVIDE", "filter_out": True, "__type__": "Terminal"},
            {"name": "unit_sequence", "__type__": "NonTerminal"},
        ],
        "order": 0,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    12: {
        "origin": {"name": Token("RULE", "unit"), "__type__": "NonTerminal"},
        "expansion": [{"name": "unit_sequence", "__type__": "NonTerminal"}],
        "order": 1,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    13: {
        "origin": {"name": Token("RULE", "unit_sequence"), "__type__": "NonTerminal"},
        "expansion": [{"name": "__unit_sequence_plus_0", "__type__": "NonTerminal"}],
        "order": 0,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    14: {
        "origin": {"name": Token("RULE", "unit_sequence"), "__type__": "NonTerminal"},
        "expansion": [
            {"name": "term", "__type__": "NonTerminal"},
            {"name": "__unit_sequence_plus_1", "__type__": "NonTerminal"},
        ],
        "order": 1,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    15: {
        "origin": {"name": Token("RULE", "term"), "__type__": "NonTerminal"},
        "expansion": [
            {"name": "SYMBOL", "filter_out": False, "__type__": "Terminal"},
            {"name": "exponent", "__type__": "NonTerminal"},
        ],
        "order": 0,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    16: {
        "origin": {"name": Token("RULE", "term"), "__type__": "NonTerminal"},
        "expansion": [{"name": "SYMBOL", "filter_out": False, "__type__": "Terminal"}],
        "order": 1,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    17: {
        "origin": {"name": Token("RULE", "exponent"), "__type__": "NonTerminal"},
        "expansion": [
            {"name": "CARAT_EXPONENT", "filter_out": False, "__type__": "Terminal"}
        ],
        "order": 0,
        "alias": "carat_exponent",
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    18: {
        "origin": {"name": Token("RULE", "exponent"), "__type__": "NonTerminal"},
        "expansion": [
            {
                "name": "SUPERSCRIPT_EXPONENT",
                "filter_out": False,
                "__type__": "Terminal",
            }
        ],
        "order": 1,
        "alias": "superscript_exponent",
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    19: {
        "origin": {"name": "__unit_sequence_plus_0", "__type__": "NonTerminal"},
        "expansion": [{"name": "term", "__type__": "NonTerminal"}],
        "order": 0,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    20: {
        "origin": {"name": "__unit_sequence_plus_0", "__type__": "NonTerminal"},
        "expansion": [
            {"name": "__unit_sequence_plus_0", "__type__": "NonTerminal"},
            {"name": "term", "__type__": "NonTerminal"},
        ],
        "order": 1,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    21: {
        "origin": {"name": "__unit_sequence_plus_1", "__type__": "NonTerminal"},
        "expansion": [
            {"name": "_MULTIPLY", "filter_out": True, "__type__": "Terminal"},
            {"name": "term", "__type__": "NonTerminal"},
        ],
        "order": 0,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
    22: {
        "origin": {"name": "__unit_sequence_plus_1", "__type__": "NonTerminal"},
        "expansion": [
            {"name": "__unit_sequence_plus_1", "__type__": "NonTerminal"},
            {"name": "_MULTIPLY", "filter_out": True, "__type__": "Terminal"},
            {"name": "term", "__type__": "NonTerminal"},
        ],
        "order": 1,
        "alias": None,
        "options": {
            "keep_all_tokens": False,
            "expand1": False,
            "priority": None,
            "template_source": None,
            "empty_indices": (),
            "__type__": "RuleOptions",
        },
        "__type__": "Rule",
    },
}
Shift = 0
Reduce = 1


def Parser(**kwargs):
    return Lark._load_from_dict(DATA, MEMO, **kwargs)
