Merge commit 'origin/gallium-master-merge'
[mesa.git] / src / gallium / state_trackers / python / retrace / parser.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
5 # All Rights Reserved.
6 #
7 # Permission is hereby granted, free of charge, to any person obtaining a
8 # copy of this software and associated documentation files (the
9 # "Software"), to deal in the Software without restriction, including
10 # without limitation the rights to use, copy, modify, merge, publish,
11 # distribute, sub license, and/or sell copies of the Software, and to
12 # permit persons to whom the Software is furnished to do so, subject to
13 # the following conditions:
14 #
15 # The above copyright notice and this permission notice (including the
16 # next paragraph) shall be included in all copies or substantial portions
17 # of the Software.
18 #
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 # IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
23 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 #
27 ##########################################################################
28
29
30 import sys
31 import xml.parsers.expat
32 import binascii
33
34 from model import *
35
36
37 ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF = range(4)
38
39
40 class XmlToken:
41
42 def __init__(self, type, name_or_data, attrs = None, line = None, column = None):
43 assert type in (ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF)
44 self.type = type
45 self.name_or_data = name_or_data
46 self.attrs = attrs
47 self.line = line
48 self.column = column
49
50 def __str__(self):
51 if self.type == ELEMENT_START:
52 return '<' + self.name_or_data + ' ...>'
53 if self.type == ELEMENT_END:
54 return '</' + self.name_or_data + '>'
55 if self.type == CHARACTER_DATA:
56 return self.name_or_data
57 if self.type == EOF:
58 return 'end of file'
59 assert 0
60
61
62 class XmlTokenizer:
63 """Expat based XML tokenizer."""
64
65 def __init__(self, fp, skip_ws = True):
66 self.fp = fp
67 self.tokens = []
68 self.index = 0
69 self.final = False
70 self.skip_ws = skip_ws
71
72 self.character_pos = 0, 0
73 self.character_data = ''
74
75 self.parser = xml.parsers.expat.ParserCreate()
76 self.parser.StartElementHandler = self.handle_element_start
77 self.parser.EndElementHandler = self.handle_element_end
78 self.parser.CharacterDataHandler = self.handle_character_data
79
80 def handle_element_start(self, name, attributes):
81 self.finish_character_data()
82 line, column = self.pos()
83 token = XmlToken(ELEMENT_START, name, attributes, line, column)
84 self.tokens.append(token)
85
86 def handle_element_end(self, name):
87 self.finish_character_data()
88 line, column = self.pos()
89 token = XmlToken(ELEMENT_END, name, None, line, column)
90 self.tokens.append(token)
91
92 def handle_character_data(self, data):
93 if not self.character_data:
94 self.character_pos = self.pos()
95 self.character_data += data
96
97 def finish_character_data(self):
98 if self.character_data:
99 if not self.skip_ws or not self.character_data.isspace():
100 line, column = self.character_pos
101 token = XmlToken(CHARACTER_DATA, self.character_data, None, line, column)
102 self.tokens.append(token)
103 self.character_data = ''
104
105 def next(self):
106 size = 16*1024
107 while self.index >= len(self.tokens) and not self.final:
108 self.tokens = []
109 self.index = 0
110 data = self.fp.read(size)
111 self.final = len(data) < size
112 data = data.rstrip('\0')
113 try:
114 self.parser.Parse(data, self.final)
115 except xml.parsers.expat.ExpatError, e:
116 #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS:
117 if e.code == 3:
118 pass
119 else:
120 raise e
121 if self.index >= len(self.tokens):
122 line, column = self.pos()
123 token = XmlToken(EOF, None, None, line, column)
124 else:
125 token = self.tokens[self.index]
126 self.index += 1
127 return token
128
129 def pos(self):
130 return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber
131
132
133 class TokenMismatch(Exception):
134
135 def __init__(self, expected, found):
136 self.expected = expected
137 self.found = found
138
139 def __str__(self):
140 return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found))
141
142
143
144 class XmlParser:
145 """Base XML document parser."""
146
147 def __init__(self, fp):
148 self.tokenizer = XmlTokenizer(fp)
149 self.consume()
150
151 def consume(self):
152 self.token = self.tokenizer.next()
153
154 def match_element_start(self, name):
155 return self.token.type == ELEMENT_START and self.token.name_or_data == name
156
157 def match_element_end(self, name):
158 return self.token.type == ELEMENT_END and self.token.name_or_data == name
159
160 def element_start(self, name):
161 while self.token.type == CHARACTER_DATA:
162 self.consume()
163 if self.token.type != ELEMENT_START:
164 raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
165 if self.token.name_or_data != name:
166 raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
167 attrs = self.token.attrs
168 self.consume()
169 return attrs
170
171 def element_end(self, name):
172 while self.token.type == CHARACTER_DATA:
173 self.consume()
174 if self.token.type != ELEMENT_END:
175 raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
176 if self.token.name_or_data != name:
177 raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
178 self.consume()
179
180 def character_data(self, strip = True):
181 data = ''
182 while self.token.type == CHARACTER_DATA:
183 data += self.token.name_or_data
184 self.consume()
185 if strip:
186 data = data.strip()
187 return data
188
189
190 class TraceParser(XmlParser):
191
192 def parse(self):
193 self.element_start('trace')
194 while self.token.type not in (ELEMENT_END, EOF):
195 call = self.parse_call()
196 self.handle_call(call)
197 if self.token.type != EOF:
198 self.element_end('trace')
199
200 def parse_call(self):
201 attrs = self.element_start('call')
202 klass = attrs['class']
203 method = attrs['method']
204 args = []
205 ret = None
206 while self.token.type == ELEMENT_START:
207 if self.token.name_or_data == 'arg':
208 arg = self.parse_arg()
209 args.append(arg)
210 elif self.token.name_or_data == 'ret':
211 ret = self.parse_ret()
212 elif self.token.name_or_data == 'call':
213 # ignore nested function calls
214 self.parse_call()
215 else:
216 raise TokenMismatch("<arg ...> or <ret ...>", self.token)
217 self.element_end('call')
218
219 return Call(klass, method, args, ret)
220
221 def parse_arg(self):
222 attrs = self.element_start('arg')
223 name = attrs['name']
224 value = self.parse_value()
225 self.element_end('arg')
226
227 return name, value
228
229 def parse_ret(self):
230 attrs = self.element_start('ret')
231 value = self.parse_value()
232 self.element_end('ret')
233
234 return value
235
236 def parse_value(self):
237 expected_tokens = ('null', 'bool', 'int', 'uint', 'float', 'string', 'enum', 'array', 'struct', 'ptr', 'bytes')
238 if self.token.type == ELEMENT_START:
239 if self.token.name_or_data in expected_tokens:
240 method = getattr(self, 'parse_' + self.token.name_or_data)
241 return method()
242 raise TokenMismatch(" or " .join(expected_tokens), self.token)
243
244 def parse_null(self):
245 self.element_start('null')
246 self.element_end('null')
247 return Literal(None)
248
249 def parse_bool(self):
250 self.element_start('bool')
251 value = int(self.character_data())
252 self.element_end('bool')
253 return Literal(value)
254
255 def parse_int(self):
256 self.element_start('int')
257 value = int(self.character_data())
258 self.element_end('int')
259 return Literal(value)
260
261 def parse_uint(self):
262 self.element_start('uint')
263 value = int(self.character_data())
264 self.element_end('uint')
265 return Literal(value)
266
267 def parse_float(self):
268 self.element_start('float')
269 value = float(self.character_data())
270 self.element_end('float')
271 return Literal(value)
272
273 def parse_enum(self):
274 self.element_start('enum')
275 name = self.character_data()
276 self.element_end('enum')
277 return NamedConstant(name)
278
279 def parse_string(self):
280 self.element_start('string')
281 value = self.character_data()
282 self.element_end('string')
283 return Literal(value)
284
285 def parse_bytes(self):
286 self.element_start('bytes')
287 value = binascii.a2b_hex(self.character_data())
288 self.element_end('bytes')
289 return Literal(value)
290
291 def parse_array(self):
292 self.element_start('array')
293 elems = []
294 while self.token.type != ELEMENT_END:
295 elems.append(self.parse_elem())
296 self.element_end('array')
297 return Array(elems)
298
299 def parse_elem(self):
300 self.element_start('elem')
301 value = self.parse_value()
302 self.element_end('elem')
303 return value
304
305 def parse_struct(self):
306 attrs = self.element_start('struct')
307 name = attrs['name']
308 members = []
309 while self.token.type != ELEMENT_END:
310 members.append(self.parse_member())
311 self.element_end('struct')
312 return Struct(name, members)
313
314 def parse_member(self):
315 attrs = self.element_start('member')
316 name = attrs['name']
317 value = self.parse_value()
318 self.element_end('member')
319
320 return name, value
321
322 def parse_ptr(self):
323 self.element_start('ptr')
324 address = self.character_data()
325 self.element_end('ptr')
326
327 return Pointer(address)
328
329 def handle_call(self, call):
330 pass
331
332
333 class TraceDumper(TraceParser):
334
335 def __init__(self, fp):
336 TraceParser.__init__(self, fp)
337 self.formatter = format.DefaultFormatter(sys.stdout)
338 self.pretty_printer = PrettyPrinter(self.formatter)
339
340 def handle_call(self, call):
341 call.visit(self.pretty_printer)
342 self.formatter.newline()
343
344
345 def main(ParserFactory):
346 for arg in sys.argv[1:]:
347 if arg.endswith('.gz'):
348 import gzip
349 stream = gzip.GzipFile(arg, 'rt')
350 else:
351 stream = open(arg, 'rt')
352 parser = ParserFactory(stream)
353 parser.parse()
354
355
356 if __name__ == '__main__':
357 main(TraceDumper)