# Path: matchpy/utils.py
# class cached_property(property):
#     """Property with caching.
# 
#     An extension of the builtin `property`, that caches the value after the first access.
#     This is useful in case the computation of the property value is expensive.
# 
#     Use it just like a regular property decorator. Cached properties cannot have a setter.
# 
#     Example:
# 
#         First, create a class with a cached property:
# 
#         >>> class MyClass:
#         ...     @cached_property
#         ...     def my_property(self):
#         ...         print('my_property called')
#         ...         return 42
#         >>> instance = MyClass()
# 
#         Then, access the property and get the computed value:
# 
#         >>> instance.my_property
#         my_property called
#         42
# 
#         Now the result is cached and the original method will not be called again:
# 
#         >>> instance.my_property
#         42
#     """
# 
#     def __init__(self, getter, slot=None):
#         """
#         Use it as a decorator:
# 
#         >>> class MyClass:
#         ...     @cached_property
#         ...     def my_property(self):
#         ...         return 42
# 
#         The *slot* argument specifies a class slot to use for caching the property. You should use the
#         `slot_cached_property` decorator instead as that is more convenient.
# 
#         Args:
#             getter:
#                 The getter method for the property.
#             slot:
#                 Optional slot to use for the cached value. Only relevant in classes that use slots.
#                 Use `slot_cached_property` instead.
# 
#         Returns:
#             The wrapped `property` with caching.
#         """
#         super().__init__(getter)
#         self._name = getter.__name__
#         self._slot = slot
# 
#     def __get__(self, obj, cls):
#         if obj is None:
#             return self
#         if self._slot is not None:
#             attribute = cls.__dict__[self._slot]
#             try:
#                 return attribute.__get__(obj, cls)
#             except AttributeError:
#                 value = self.fget(obj)
#                 attribute.__set__(obj, value)
#                 return value
#         else:
#             if self._name not in obj.__dict__:
#                 obj.__dict__[self._name] = self.fget(obj)
#             return obj.__dict__[self._name]


from abc import ABCMeta
from enum import Enum, EnumMeta
from typing import Callable, Iterator, List, NamedTuple, Optional, Set, Tuple, Type, Union
from multiset import Multiset
from ..utils import cached_property
import keyword

    @cached_property
    def symbols(self) -> MultisetOfStr:
        """A multiset of the symbol names occurring in the expression."""
        symbols = Multiset()
        self.collect_symbols(symbols)
        return symbols

    def collect_symbols(self, symbols: MultisetOfStr) -> None:
        """Recursively adds all symbols occuring in the expression to the given multiset.

        This is used internally by `symbols`. Needs to be overwritten by inheriting expression classes that
        can contain symbols. This method can be used when gathering the `symbols` of multiple expressions, because only
        one multiset needs to be created and that is more efficient.

        Args:
            symbols:
                Multiset of symbols. All symbols contained in the expression are recursively added to this multiset.
        """
        pass

    @cached_property
    def is_constant(self) -> bool:
        """True, iff the expression does not contain any wildcards."""
        return self._is_constant()

    @staticmethod
    def _is_constant() -> bool:
        return True


# Path: matchpy/utils.py
# class cached_property(property):
#     """Property with caching.
# 
#     An extension of the builtin `property`, that caches the value after the first access.
#     This is useful in case the computation of the property value is expensive.
# 
#     Use it just like a regular property decorator. Cached properties cannot have a setter.
# 
#     Example:
# 
#         First, create a class with a cached property:
# 
#         >>> class MyClass:
#         ...     @cached_property
#         ...     def my_property(self):
#         ...         print('my_property called')
#         ...         return 42
#         >>> instance = MyClass()
# 
#         Then, access the property and get the computed value:
# 
#         >>> instance.my_property
#         my_property called
#         42
# 
#         Now the result is cached and the original method will not be called again:
# 
#         >>> instance.my_property
#         42
#     """
# 
#     def __init__(self, getter, slot=None):
#         """
#         Use it as a decorator:
# 
#         >>> class MyClass:
#         ...     @cached_property
#         ...     def my_property(self):
#         ...         return 42
# 
#         The *slot* argument specifies a class slot to use for caching the property. You should use the
#         `slot_cached_property` decorator instead as that is more convenient.
# 
#         Args:
#             getter:
#                 The getter method for the property.
#             slot:
#                 Optional slot to use for the cached value. Only relevant in classes that use slots.
#                 Use `slot_cached_property` instead.
# 
#         Returns:
#             The wrapped `property` with caching.
#         """
#         super().__init__(getter)
#         self._name = getter.__name__
#         self._slot = slot
# 
#     def __get__(self, obj, cls):
#         if obj is None:
#             return self
#         if self._slot is not None:
#             attribute = cls.__dict__[self._slot]
#             try:
#                 return attribute.__get__(obj, cls)
#             except AttributeError:
#                 value = self.fget(obj)
#                 attribute.__set__(obj, value)
#                 return value
#         else:
#             if self._name not in obj.__dict__:
#                 obj.__dict__[self._name] = self.fget(obj)
#             return obj.__dict__[self._name]


# Path: matchpy/expressions/expressions.py
from abc import ABCMeta
from enum import Enum, EnumMeta
from typing import Callable, Iterator, List, NamedTuple, Optional, Set, Tuple, Type, Union
from multiset import Multiset
from ..utils import cached_property
import keyword


    @cached_property
    def symbols(self) -> MultisetOfStr:
        """A multiset of the symbol names occurring in the expression."""
        symbols = Multiset()
        self.collect_symbols(symbols)
        return symbols

    def collect_symbols(self, symbols: MultisetOfStr) -> None:
        """Recursively adds all symbols occuring in the expression to the given multiset.

        This is used internally by `symbols`. Needs to be overwritten by inheriting expression classes that
        can contain symbols. This method can be used when gathering the `symbols` of multiple expressions, because only
        one multiset needs to be created and that is more efficient.

        Args:
            symbols:
                Multiset of symbols. All symbols contained in the expression are recursively added to this multiset.
        """
        pass

    @cached_property
    def is_constant(self) -> bool:
        """True, iff the expression does not contain any wildcards."""
        return self._is_constant()

    @staticmethod
    def _is_constant() -> bool:
        return True


    @cached_property