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
29 ELEMENT_START
, ELEMENT_END
, CHARACTER_DATA
, EOF
= range(4)
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
)
37 self
.name_or_data
= name_or_data
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
55 """Expat based XML tokenizer."""
57 def __init__(self
, fp
, skip_ws
= True):
62 self
.skip_ws
= skip_ws
64 self
.character_pos
= 0, 0
65 self
.character_data
= ''
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
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
)
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
)
84 def handle_character_data(self
, data
):
85 if not self
.character_data
:
86 self
.character_pos
= self
.pos()
87 self
.character_data
+= data
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
= ''
99 while self
.index
>= len(self
.tokens
) and not self
.final
:
102 data
= self
.fp
.read(size
)
103 self
.final
= len(data
) < size
104 data
= data
.rstrip('\0')
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:
113 if self
.index
>= len(self
.tokens
):
114 line
, column
= self
.pos()
115 token
= XmlToken(EOF
, None, None, line
, column
)
117 token
= self
.tokens
[self
.index
]
122 return self
.parser
.CurrentLineNumber
, self
.parser
.CurrentColumnNumber
125 class TokenMismatch(Exception):
127 def __init__(self
, expected
, found
):
128 self
.expected
= expected
132 return '%u:%u: %s expected, %s found' % (self
.found
.line
, self
.found
.column
, str(self
.expected
), str(self
.found
))
137 """Base XML document parser."""
139 def __init__(self
, fp
):
140 self
.tokenizer
= XmlTokenizer(fp
)
144 self
.token
= self
.tokenizer
.next()
146 def match_element_start(self
, name
):
147 return self
.token
.type == ELEMENT_START
and self
.token
.name_or_data
== name
149 def match_element_end(self
, name
):
150 return self
.token
.type == ELEMENT_END
and self
.token
.name_or_data
== name
152 def element_start(self
, name
):
153 while self
.token
.type == CHARACTER_DATA
:
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
163 def element_end(self
, name
):
164 while self
.token
.type == CHARACTER_DATA
:
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
)
172 def character_data(self
, strip
= True):
174 while self
.token
.type == CHARACTER_DATA
:
175 data
+= self
.token
.name_or_data
182 class TraceParser(XmlParser
):
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')
192 def parse_call(self
):
193 attrs
= self
.element_start('call')
194 klass
= attrs
['class']
195 method
= attrs
['method']
198 while self
.token
.type == ELEMENT_START
:
199 if self
.token
.name_or_data
== 'arg':
200 arg
= self
.parse_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
208 raise TokenMismatch("<arg ...> or <ret ...>", self
.token
)
209 self
.element_end('call')
211 return Call(klass
, method
, args
, ret
)
214 attrs
= self
.element_start('arg')
216 value
= self
.parse_value()
217 self
.element_end('arg')
222 attrs
= self
.element_start('ret')
223 value
= self
.parse_value()
224 self
.element_end('ret')
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
)
234 raise TokenMismatch(" or " .join(expected_tokens
), self
.token
)
236 def parse_null(self
):
237 self
.element_start('null')
238 self
.element_end('null')
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
)
248 self
.element_start('int')
249 value
= int(self
.character_data())
250 self
.element_end('int')
251 return Literal(value
)
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
)
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
)
265 def parse_enum(self
):
266 self
.element_start('enum')
267 name
= self
.character_data()
268 self
.element_end('enum')
269 return NamedConstant(name
)
271 def parse_string(self
):
272 self
.element_start('string')
273 value
= self
.character_data()
274 self
.element_end('string')
275 return Literal(value
)
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
)
283 def parse_array(self
):
284 self
.element_start('array')
286 while self
.token
.type != ELEMENT_END
:
287 elems
.append(self
.parse_elem())
288 self
.element_end('array')
291 def parse_elem(self
):
292 self
.element_start('elem')
293 value
= self
.parse_value()
294 self
.element_end('elem')
297 def parse_struct(self
):
298 attrs
= self
.element_start('struct')
301 while self
.token
.type != ELEMENT_END
:
302 members
.append(self
.parse_member())
303 self
.element_end('struct')
304 return Struct(name
, members
)
306 def parse_member(self
):
307 attrs
= self
.element_start('member')
309 value
= self
.parse_value()
310 self
.element_end('member')
315 self
.element_start('ptr')
316 address
= self
.character_data()
317 self
.element_end('ptr')
319 return Pointer(address
)
321 def handle_call(self
, call
):
326 class TraceDumper(TraceParser
):
329 def handle_call(self
, call
):
333 def main(ParserFactory
):
334 for arg
in sys
.argv
[1:]:
335 if arg
.endswith('.gz'):
337 stream
= gzip
.GzipFile(arg
, 'rt')
339 stream
= open(arg
, 'rt')
340 parser
= ParserFactory(stream
)
344 if __name__
== '__main__':