2 #############################################################################
4 # Copyright 2008 Tungsten Graphics, Inc.
6 # This program is free software: you can redistribute it and/or modify it
7 # under the terms of the GNU Lesser General Public License as published
8 # by the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #############################################################################
23 import xml.parsers.expat
26 ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF = range(4)
31 def __init__(self, type, name_or_data, attrs = None, line = None, column = None):
32 assert type in (ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF)
34 self.name_or_data = name_or_data
40 if self.type == ELEMENT_START:
41 return '<' + self.name_or_data + ' ...>'
42 if self.type == ELEMENT_END:
43 return '</' + self.name_or_data + '>'
44 if self.type == CHARACTER_DATA:
45 return self.name_or_data
52 """Expat based XML tokenizer."""
54 def __init__(self, fp, skip_ws = True):
59 self.skip_ws = skip_ws
61 self.character_pos = 0, 0
62 self.character_data = ''
64 self.parser = xml.parsers.expat.ParserCreate()
65 self.parser.StartElementHandler = self.handle_element_start
66 self.parser.EndElementHandler = self.handle_element_end
67 self.parser.CharacterDataHandler = self.handle_character_data
69 def handle_element_start(self, name, attributes):
70 self.finish_character_data()
71 line, column = self.pos()
72 token = XmlToken(ELEMENT_START, name, attributes, line, column)
73 self.tokens.append(token)
75 def handle_element_end(self, name):
76 self.finish_character_data()
77 line, column = self.pos()
78 token = XmlToken(ELEMENT_END, name, None, line, column)
79 self.tokens.append(token)
81 def handle_character_data(self, data):
82 if not self.character_data:
83 self.character_pos = self.pos()
84 self.character_data += data
86 def finish_character_data(self):
87 if self.character_data:
88 if not self.skip_ws or not self.character_data.isspace():
89 line, column = self.character_pos
90 token = XmlToken(CHARACTER_DATA, self.character_data, None, line, column)
91 self.tokens.append(token)
92 self.character_data = ''
96 while self.index >= len(self.tokens) and not self.final:
99 data = self.fp.read(size)
100 self.final = len(data) < size
102 self.parser.Parse(data, self.final)
103 except xml.parsers.expat.ExpatError, e:
104 #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS:
109 if self.index >= len(self.tokens):
110 line, column = self.pos()
111 token = XmlToken(EOF, None, None, line, column)
113 token = self.tokens[self.index]
118 return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber
121 class TokenMismatch(Exception):
123 def __init__(self, expected, found):
124 self.expected = expected
128 return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found))
133 """Base XML document parser."""
135 def __init__(self, fp):
136 self.tokenizer = XmlTokenizer(fp)
140 self.token = self.tokenizer.next()
142 def match_element_start(self, name):
143 return self.token.type == ELEMENT_START and self.token.name_or_data == name
145 def match_element_end(self, name):
146 return self.token.type == ELEMENT_END and self.token.name_or_data == name
148 def element_start(self, name):
149 while self.token.type == CHARACTER_DATA:
151 if self.token.type != ELEMENT_START:
152 raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
153 if self.token.name_or_data != name:
154 raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
155 attrs = self.token.attrs
159 def element_end(self, name):
160 while self.token.type == CHARACTER_DATA:
162 if self.token.type != ELEMENT_END:
163 raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
164 if self.token.name_or_data != name:
165 raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
168 def character_data(self, strip = True):
170 while self.token.type == CHARACTER_DATA:
171 data += self.token.name_or_data
180 def function(self, name):
183 def variable(self, name):
186 def literal(self, value):
189 def address(self, addr):
193 class AnsiFormatter(Formatter):
194 '''Formatter for plain-text files which outputs ANSI escape codes. See
195 http://en.wikipedia.org/wiki/ANSI_escape_code for more information
196 concerning ANSI escape codes.
208 def _escape(self, code, text):
209 return self._csi + code + text + self._csi + self._normal
211 def function(self, name):
212 text = Formatter.function(self, name)
213 return self._escape(self._bold, text)
215 def variable(self, name):
216 text = Formatter.variable(self, name)
217 return self._escape(self._italic, text)
219 def literal(self, value):
220 text = Formatter.literal(self, value)
221 return self._escape(self._blue, text)
223 def address(self, value):
224 text = Formatter.address(self, value)
225 return self._escape(self._green, text)
228 class TraceParser(XmlParser):
230 def __init__(self, stream, formatter):
231 XmlParser.__init__(self, stream)
232 self.formatter = formatter
235 self.element_start('trace')
236 while self.token.type != ELEMENT_END:
238 self.element_end('trace')
240 def parse_call(self):
241 attrs = self.element_start('call')
245 while self.token.type == ELEMENT_START:
246 if self.token.name_or_data == 'arg':
247 arg = self.parse_arg()
249 elif self.token.name_or_data == 'ret':
250 ret = self.parse_ret()
251 elif self.token.name_or_data == 'call':
254 raise TokenMismatch("<arg ...> or <ret ...>", self.token)
255 self.element_end('call')
257 call = self.formatter.function(name)
258 call += '(' + ', '.join([self.formatter.variable(name) + ' = ' + value for name, value in args]) + ')'
264 sys.stdout.write(call)
270 attrs = self.element_start('arg')
272 value = self.parse_value()
273 self.element_end('arg')
278 attrs = self.element_start('ret')
279 value = self.parse_value()
280 self.element_end('ret')
284 def parse_value(self):
285 if self.token.type == CHARACTER_DATA:
286 return self.formatter.literal(self.character_data())
287 if self.token.type == ELEMENT_START:
288 if self.token.name_or_data == 'elem':
289 return self.parse_elems()
290 if self.token.name_or_data == 'ref':
291 return self.parse_ref()
292 raise TokenMismatch("<elem ...>, <ref ...>, or text", self.token)
294 def parse_elems(self):
295 elems = [self.parse_elem()]
296 while self.token.type != ELEMENT_END:
297 elems.append(self.parse_elem())
298 return '{' + ', '.join([name + ' = ' + value for name, value in elems]) + '}'
300 def parse_elem(self):
301 attrs = self.element_start('elem')
303 value = self.parse_value()
304 self.element_end('elem')
309 attrs = self.element_start('ref')
310 if self.token.type != ELEMENT_END:
311 value = '&' + self.parse_value()
313 value = self.formatter.address(attrs['addr'])
314 self.element_end('ref')
320 formatter = AnsiFormatter()
325 if arg.endswith('.gz'):
326 from gzip import GzipFile
327 stream = GzipFile(arg, 'rt')
328 elif arg.endswith('.bz2'):
329 from bz2 import BZ2File
330 stream = BZ2File(arg, 'rt')
332 stream = open(arg, 'rt')
333 parser = TraceParser(stream, formatter)
336 parser = TraceParser(sys.stdin, formatter)
340 if __name__ == '__main__':