Completed
Push — 0.5.3 ( 6cbe30...080920 )
by Felipe A.
01:03
created

StateMachine.nearest()   B

Complexity

Conditions 6

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
rs 7.5384
cc 6
1
2
3
class StateMachine(object):
4
    '''
5
    Abstract simple character-driven state-machine for string transforms.
6
7
    Useful for implementig simple transpilations, compressors and so on.
8
9
    Important: when implementing this class, you must set the :attr:`current`
10
    attribute to a key defined in :attr:`jumps` dict.
11
    '''
12
    jumps = {}  # state machine jumps
13
    start = ''  # character which started current state
14
    current = ''  # initial and current state
15
    pending = ''  # buffer of current state data
16
    streaming = False  # stream mode toggle
17
18
    @property
19
    def nearest(self):
20
        try:
21
            options = self.jumps[self.current]
22
        except KeyError:
23
            raise KeyError(
24
                'Current state %r not defined in %s.jumps.'
25
                % (self.current, self.__class__)
26
                )
27
        offset = len(self.start)
28
        index = len(self.pending)
29
        if self.streaming:
30
            index -= max(map(len, options))
31
        key = (index, 1)
32
        result = (index, '', None)
33
        for amark, anext in options.items():
34
            asize = len(amark)
35
            aindex = self.pending.find(amark, offset, index + asize)
36
            if aindex > -1:
37
                index = aindex
38
                akey = (aindex, -asize)
39
                if akey < key:
40
                    key = akey
41
                    result = (aindex, amark, anext)
42
        return result
43
44
    def __init__(self, data=''):
45
        self.pending = data
46
47
    def __iter__(self):
48
        '''
49
        Iterate over tramsformation result chunks.
50
51
        On non-streaming mode, flush and yield it on completion.
52
53
        :yields: transformation result chunka
54
        :ytype: str
55
        '''
56
        index, mark, next = self.nearest
57
        while next is not None:
58
            data = self.transform(self.pending[:index], mark, next)
59
            self.start = mark
60
            self.current = next
61
            self.pending = self.pending[index:]
62
            if data:
63
                yield data
64
            index, mark, next = self.nearest
65
        if not self.streaming:
66
            data = self.transform(self.pending, mark, next)
67
            self.start = ''
68
            self.pending = ''
69
            if data:
70
                yield data
71
72
    def transform(self, data, mark, next):
73
        method = getattr(self, 'transform_%s' % self.current, None)
74
        return method(data, mark, next) if method else data
75
76
    def feed(self, data=''):
77
        self.streaming = True
78
        self.pending += data
79
        for i in self:
80
            yield i
81
82
    def finish(self, data=''):
83
        self.pending += data
84
        self.streaming = False
85
        for i in self:
86
            yield i
87