Merge commit 'origin/master' into HEAD
[mesa.git] / src / gallium / state_trackers / python / retrace / parser.py
1 #!/usr/bin/env python
2 #############################################################################
3 #
4 # Copyright 2008 Tungsten Graphics, Inc.
5 #
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.
10 #
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.
15 #
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/>.
18 #
19 #############################################################################
20
21
22 import sys
23 import xml.parsers.expat
24 import binascii
25
26 from model import *
27
28
29 ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF = range(4)
30
31
32 class XmlToken:
33
34 def __init__(self, type, name_or_data, attrs = None, line = None, column = None):
35 assert type in (ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF)
36 self.type = type
37 self.name_or_data = name_or_data
38 self.attrs = attrs
39 self.line = line
40 self.column = column
41
42 def __str__(self):
43 if self.type == ELEMENT_START:
44 return '<' + self.name_or_data + ' ...>'
45 if self.type == ELEMENT_END:
46 return '</' + self.name_or_data + '>'
47 if self.type == CHARACTER_DATA:
48 return self.name_or_data
49 if self.type == EOF:
50 return 'end of file'
51 assert 0
52
53
54 class XmlTokenizer:
55 """Expat based XML tokenizer."""
56
57 def __init__(self, fp, skip_ws = True):
58 self.fp = fp
59 self.tokens = []
60 self.index = 0
61 self.final = False
62 self.skip_ws = skip_ws
63
64 self.character_pos = 0, 0
65 self.character_data = ''
66
67 self.parser = xml.parsers.expat.ParserCreate()
68 self.parser.StartElementHandler = self.handle_element_start
69 self.parser.EndElementHandler = self.handle_element_end
70 self.parser.CharacterDataHandler = self.handle_character_data
71
72 def handle_element_start(self, name, attributes):
73 self.finish_character_data()
74 line, column = self.pos()
75 token = XmlToken(ELEMENT_START, name, attributes, line, column)
76 self.tokens.append(token)
77
78 def handle_element_end(self, name):
79 self.finish_character_data()
80 line, column = self.pos()
81 token = XmlToken(ELEMENT_END, name, None, line, column)
82 self.tokens.append(token)
83
84 def handle_character_data(self, data):
85 if not self.character_data:
86 self.character_pos = self.pos()
87 self.character_data += data
88
89 def finish_character_data(self):
90 if self.character_data:
91 if not self.skip_ws or not self.character_data.isspace():
92 line, column = self.character_pos
93 token = XmlToken(CHARACTER_DATA, self.character_data, None, line, column)
94 self.tokens.append(token)
95 self.character_data = ''
96
97 def next(self):
98 size = 16*1024
99 while self.index >= len(self.tokens) and not self.final:
100 self.tokens = []
101 self.index = 0
102 data = self.fp.read(size)
103 self.final = len(data) < size
104 data = data.rstrip('\0')
105 try:
106 self.parser.Parse(data, self.final)
107 except xml.parsers.expat.ExpatError, e:
108 #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS:
109 if e.code == 3:
110 pass
111 else:
112 raise e
113 if self.index >= len(self.tokens):
114 line, column = self.pos()
115 token = XmlToken(EOF, None, None, line, column)
116 else:
117 token = self.tokens[self.index]
118 self.index += 1
119 return token
120
121 def pos(self):
122 return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber
123
124
125 class TokenMismatch(Exception):
126
127 def __init__(self, expected, found):
128 self.expected = expected
129 self.found = found
130
131 def __str__(self):
132 return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found))
133
134
135
136 class XmlParser:
137 """Base XML document parser."""
138
139 def __init__(self, fp):
140 self.tokenizer = XmlTokenizer(fp)
141 self.consume()
142
143 def consume(self):
144 self.token = self.tokenizer.next()
145
146 def match_element_start(self, name):
147 return self.token.type == ELEMENT_START and self.token.name_or_data == name
148
149 def match_element_end(self, name):
150 return self.token.type == ELEMENT_END and self.token.name_or_data == name
151
152 def element_start(self, name):
153 while self.token.type == CHARACTER_DATA:
154 self.consume()
155 if self.token.type != ELEMENT_START:
156 raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
157 if self.token.name_or_data != name:
158 raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
159 attrs = self.token.attrs
160 self.consume()
161 return attrs
162
163 def element_end(self, name):
164 while self.token.type == CHARACTER_DATA:
165 self.consume()
166 if self.token.type != ELEMENT_END:
167 raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
168 if self.token.name_or_data != name:
169 raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
170 self.consume()
171
172 def character_data(self, strip = True):
173 data = ''
174 while self.token.type == CHARACTER_DATA:
175 data += self.token.name_or_data
176 self.consume()
177 if strip:
178 data = data.strip()
179 return data
180
181
182 class TraceParser(XmlParser):
183
184 def parse(self):
185 self.element_start('trace')
186 while self.token.type not in (ELEMENT_END, EOF):
187 call = self.parse_call()
188 self.handle_call(call)
189 if self.token.type != EOF:
190 self.element_end('trace')
191
192 def parse_call(self):
193 attrs = self.element_start('call')
194 klass = attrs['class']
195 method = attrs['method']
196 args = []
197 ret = None
198 while self.token.type == ELEMENT_START:
199 if self.token.name_or_data == 'arg':
200 arg = self.parse_arg()
201 args.append(arg)
202 elif self.token.name_or_data == 'ret':
203 ret = self.parse_ret()
204 elif self.token.name_or_data == 'call':
205 # ignore nested function calls
206 self.parse_call()
207 else:
208 raise TokenMismatch("<arg ...> or <ret ...>", self.token)
209 self.element_end('call')
210
211 return Call(klass, method, args, ret)
212
213 def parse_arg(self):
214 attrs = self.element_start('arg')
215 name = attrs['name']
216 value = self.parse_value()
217 self.element_end('arg')
218
219 return name, value
220
221 def parse_ret(self):
222 attrs = self.element_start('ret')
223 value = self.parse_value()
224 self.element_end('ret')
225
226 return value
227
228 def parse_value(self):
229 expected_tokens = ('null', 'bool', 'int', 'uint', 'float', 'string', 'enum', 'array', 'struct', 'ptr', 'bytes')
230 if self.token.type == ELEMENT_START:
231 if self.token.name_or_data in expected_tokens:
232 method = getattr(self, 'parse_' + self.token.name_or_data)
233 return method()
234 raise TokenMismatch(" or " .join(expected_tokens), self.token)
235
236 def parse_null(self):
237 self.element_start('null')
238 self.element_end('null')
239 return Literal(None)
240
241 def parse_bool(self):
242 self.element_start('bool')
243 value = int(self.character_data())
244 self.element_end('bool')
245 return Literal(value)
246
247 def parse_int(self):
248 self.element_start('int')
249 value = int(self.character_data())
250 self.element_end('int')
251 return Literal(value)
252
253 def parse_uint(self):
254 self.element_start('uint')
255 value = int(self.character_data())
256 self.element_end('uint')
257 return Literal(value)
258
259 def parse_float(self):
260 self.element_start('float')
261 value = float(self.character_data())
262 self.element_end('float')
263 return Literal(value)
264
265 def parse_enum(self):
266 self.element_start('enum')
267 name = self.character_data()
268 self.element_end('enum')
269 return NamedConstant(name)
270
271 def parse_string(self):
272 self.element_start('string')
273 value = self.character_data()
274 self.element_end('string')
275 return Literal(value)
276
277 def parse_bytes(self):
278 self.element_start('bytes')
279 value = binascii.a2b_hex(self.character_data())
280 self.element_end('bytes')
281 return Literal(value)
282
283 def parse_array(self):
284 self.element_start('array')
285 elems = []
286 while self.token.type != ELEMENT_END:
287 elems.append(self.parse_elem())
288 self.element_end('array')
289 return Array(elems)
290
291 def parse_elem(self):
292 self.element_start('elem')
293 value = self.parse_value()
294 self.element_end('elem')
295 return value
296
297 def parse_struct(self):
298 attrs = self.element_start('struct')
299 name = attrs['name']
300 members = []
301 while self.token.type != ELEMENT_END:
302 members.append(self.parse_member())
303 self.element_end('struct')
304 return Struct(name, members)
305
306 def parse_member(self):
307 attrs = self.element_start('member')
308 name = attrs['name']
309 value = self.parse_value()
310 self.element_end('member')
311
312 return name, value
313
314 def parse_ptr(self):
315 self.element_start('ptr')
316 address = self.character_data()
317 self.element_end('ptr')
318
319 return Pointer(address)
320
321 def handle_call(self, call):
322
323 pass
324
325
326 class TraceDumper(TraceParser):
327
328
329 def handle_call(self, call):
330 print call
331
332
333 def main(ParserFactory):
334 for arg in sys.argv[1:]:
335 if arg.endswith('.gz'):
336 import gzip
337 stream = gzip.GzipFile(arg, 'rt')
338 else:
339 stream = open(arg, 'rt')
340 parser = ParserFactory(stream)
341 parser.parse()
342
343
344 if __name__ == '__main__':
345 main(TraceDumper)