~terceiro/lava-dispatcher/filter-ansi-color-codes-mwhudson-state-machine

« back to all changes in this revision

Viewing changes to lava_dispatcher/incremental_pattern_remover.py

  • Committer: Antonio Terceiro
  • Date: 2013-08-08 22:23:31 UTC
  • Revision ID: antonio.terceiro@linaro.org-20130808222331-l2xqo15bzmooivbb
An solution based on Michael Hudson's state machine

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2013 Linaro Limited
 
2
#
 
3
# Author: Michael Hudson-Doyle <michael.hudson@linaro.org>
 
4
#
 
5
# This file is part of LAVA Dispatcher.
 
6
#
 
7
# LAVA Dispatcher is free software; you can redistribute it and/or modify
 
8
# it under the terms of the GNU General Public License as published by
 
9
# the Free Software Foundation; either version 2 of the License, or
 
10
# (at your option) any later version.
 
11
#
 
12
# LAVA Dispatcher is distributed in the hope that it will be useful,
 
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
# GNU General Public License for more details.
 
16
#
 
17
# You should have received a copy of the GNU General Public License
 
18
# along
 
19
# with this program; if not, see <http://www.gnu.org/licenses>.
 
20
 
 
21
class Pattern(object):
 
22
    def compile(self):
 
23
        r = {}
 
24
        self._compile(r, None)
 
25
        return r
 
26
 
 
27
class Literal(Pattern):
 
28
    def __init__(self, value):
 
29
        self.value = value
 
30
 
 
31
    def _compile(self, r, d):
 
32
        c = r
 
33
        for char in self.value[:-1]:
 
34
            c[char] = {}
 
35
            c = c[char]
 
36
        c[self.value[-1]] = d
 
37
 
 
38
class Sequence(Pattern):
 
39
    def __init__(self, *patterns):
 
40
        self.patterns = patterns
 
41
 
 
42
    def _compile(self, r, d):
 
43
        c = r
 
44
        for p in self.patterns[:-1]:
 
45
            n = {}
 
46
            p._compile(c, n)
 
47
            c = n
 
48
        self.patterns[-1]._compile(c, d)
 
49
 
 
50
class Bracket(Pattern):
 
51
    def __init__(self, contents):
 
52
        self.contents = ''
 
53
        if contents[0] == '-':
 
54
            self.contents += '-'
 
55
            contents = contents[1:]
 
56
        while '-' in contents:
 
57
            before, after = contents.split('-', 1)
 
58
            self.contents += before[:-1]
 
59
            contents = after[1:]
 
60
            from_char = before[-1]
 
61
            to_char = after[0]
 
62
            for o in range(ord(from_char), ord(to_char)+1):
 
63
                self.contents += chr(o)
 
64
        self.contents += contents
 
65
 
 
66
    def _compile(self, r, d):
 
67
        for option in self.contents:
 
68
            r[option] = d
 
69
 
 
70
def _merge(d1, d2):
 
71
    for k, v in d2.items():
 
72
        if k not in d1:
 
73
            d1[k] = v
 
74
        else:
 
75
            if v is not None and d1[k] is not None:
 
76
                _merge(d1[k], v)
 
77
            else:
 
78
                raise ValueError('overlapping patterns')
 
79
 
 
80
class Alternates(Pattern):
 
81
    def __init__(self, *patterns):
 
82
        self.patterns = patterns
 
83
 
 
84
    def _compile(self, r, d):
 
85
        for p in self.patterns:
 
86
            x = {}
 
87
            p._compile(x, d)
 
88
            _merge(r, x)
 
89
 
 
90
class OneOrMore(Pattern):
 
91
    def __init__(self, pattern):
 
92
        self.pattern = pattern
 
93
 
 
94
    def _compile(self, r, d):
 
95
        self.pattern._compile(r, d)
 
96
        _merge(d, r)
 
97
 
 
98
class ZeroOrMore(Pattern):
 
99
    def __init__(self, pattern):
 
100
        self.pattern = pattern
 
101
 
 
102
    def _compile(self, r, d):
 
103
        Optional(OneOrMore(self.pattern))._compile(r, d)
 
104
 
 
105
class Optional(Pattern):
 
106
    def __init__(self, pattern):
 
107
        self.pattern = pattern
 
108
 
 
109
    def _compile(self, r, d):
 
110
        self.pattern._compile(r, d)
 
111
        r[''] = d
 
112
 
 
113
ansi_escape_pattern = Sequence(
 
114
    OneOrMore(
 
115
        Sequence(
 
116
            Literal('\x1b['),
 
117
            ZeroOrMore(Bracket('0123456789;=')),
 
118
        )
 
119
    ),
 
120
    Bracket('A-Za-z')
 
121
)
 
122
 
 
123
class PatternRemover(object):
 
124
    def __init__(self, pattern, sink):
 
125
        self._base = pattern.compile()
 
126
        self._cur = self._base
 
127
        self._buf = ''
 
128
        self.sink = sink
 
129
    def feed(self, chars):
 
130
        for char in chars:
 
131
            self._feed1(char)
 
132
    def _feed1(self, char):
 
133
        if char in self._cur:
 
134
            next = self._cur[char]
 
135
            if next is None:
 
136
                self._buf = ''
 
137
                self._cur = self._base
 
138
            else:
 
139
                self._buf += char
 
140
                self._cur = next
 
141
        else:
 
142
            if '' in self._cur:
 
143
                # An "epsilon edge"
 
144
                self._cur = self._cur['']
 
145
                self._feed1(char)
 
146
                return
 
147
            if char in self._base:
 
148
                self.sink(self._buf)
 
149
                self._cur = self._base[char]
 
150
                self._buf = char
 
151
            else:
 
152
                self._cur = self._base
 
153
                self._buf += char
 
154
                self.sink(self._buf)
 
155
                self._buf = ''
 
156
                #print (char, self._buf)
 
157
    def flush(self):
 
158
        self.sink(self._buf)
 
159
 
 
160
if __name__ == '__main__':
 
161
    output = []
 
162
    d = PatternRemover(ansi_escape_pattern, output.append)
 
163
    for c in '\033[23;01H2.0 \033[1m\033[33m\033[40mShell> \033[0m\033[37m\033[40m':
 
164
        d.feed(c)
 
165
    d.flush()
 
166
    o = ''.join(output)
 
167
    print repr(o)