Passed
Push — master ( 8ea497...820800 )
by Max
01:11
created

DestructurerList.stack_iter()   A

Complexity

Conditions 3

Size

Total Lines 8
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nop 2
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
import typing
2
3
from ._adt_constructor import ADTConstructor
4
from ._match_failure import MatchFailure
5
from ._not_in import not_in
6
from ._patterns.basic_patterns import Pattern
7
from ._patterns.compound_match import CompoundMatch
8
from ._stack_iter import Action
9
from ._stack_iter import Extend
10
from ._stack_iter import Yield
11
from ._stack_iter import stack_iter
12
from ._unpack import unpack
13
14
_TYPE = type
15
16
17
class Destructurer:
18
    def __init_subclass__(cls, type, **kwargs):
19
        super().__init_subclass__(**kwargs)
20
        cls.type: _TYPE = type
21
22
    def __init__(self, target):
23
        self.target = target
24
25
    def __call__(self, value):
26
        return self.destructure(value)
27
28
    def destructure(self, value):
29
        raise NotImplementedError
30
31
32
class ADTDestructurer(Destructurer, type=ADTConstructor):
33
    def destructure(self, value):
34
        if value.__class__ is not self.target.__class__:
35
            raise MatchFailure
36
        return reversed(unpack(value))
37
38
39
class TupleDestructurer(Destructurer, type=tuple):
40
    def destructure(self, value):
41
        if isinstance(value, self.target.__class__) and len(self.target) == len(value):
42
            return reversed(value)
43
        raise MatchFailure
44
45
46
T = typing.TypeVar("T", bound="DestructurerList")
47
48
49
class DestructurerList(tuple):
50
51
    __slots__ = ()
52
53
    def __new__(cls, *destructurers):
54
        return super().__new__(cls, destructurers)
55
56
    def get_destructurer(
57
        self, item
58
    ) -> typing.Optional[typing.Callable[[typing.Any], typing.Sequence[typing.Any]]]:
59
        if isinstance(item, CompoundMatch):
60
            return item.destructure
61
        for destructurer in self:
62
            if isinstance(item, destructurer.type):
63
                return destructurer(item)
64
        return None
65
66
    @classmethod
67
    def custom(cls: typing.Type[T], *destructurers) -> T:
68
        return cls(*destructurers, ADTDestructurer, TupleDestructurer)
69
70
    def destructure(self, item) -> typing.Generator:
71
        destructurer = self.get_destructurer(item)
72
        if destructurer:
73
            yield from destructurer(item)
74
75
    def stack_iteration(self, item) -> Action:
76
        if isinstance(item, Pattern):
77
            return Yield(item)
78
        return Extend(self.destructure(item))
79
80
    def names(self, target) -> typing.List[str]:
81
        name_list: typing.List[str] = []
82
        for item in stack_iter(target, self.stack_iteration):
83
            not_in(name_list, item.name)
84
            name_list.append(item.name)
85
        return name_list
86
87
88
DESTRUCTURERS = DestructurerList.custom()
89