Attachment 'GCCXMLParser.py'
Download 1 # Copyright Bruno da Silva de Oliveira 2003. Use, modification and
2 # distribution is subject to the Boost Software License, Version 1.0.
3 # (See accompanying file LICENSE_1_0.txt or copy at
4 # http:#www.boost.org/LICENSE_1_0.txt)
5
6 from declarations import *
7 from elementtree.ElementTree import ElementTree
8 from xml.parsers.expat import ExpatError
9 from copy import deepcopy
10 from utils import enumerate
11
12
13 #==============================================================================
14 # Exceptions
15 #==============================================================================
16 class InvalidXMLError(Exception): pass
17
18 class ParserError(Exception): pass
19
20 class InvalidContextError(ParserError): pass
21
22
23 #==============================================================================
24 # GCCXMLParser
25 #==============================================================================
26 class GCCXMLParser(object):
27 'Parse a GCC_XML file and extract the top-level declarations.'
28
29 interested_tags = {'Class':0, 'Function':0, 'Variable':0, 'Enumeration':0}
30
31 def Parse(self, filename):
32 self.elements = self.GetElementsFromXML(filename)
33 # high level declarations
34 self.declarations = []
35 self._names = {}
36 # parse the elements
37 for id in self.elements:
38 element, decl = self.elements[id]
39 if decl is None:
40 try:
41 self.ParseElement(id, element)
42 except InvalidContextError:
43 pass # ignore those nodes with invalid context
44 # (workaround gccxml bug)
45
46
47 def Declarations(self):
48 return self.declarations
49
50
51 def AddDecl(self, decl):
52 if decl.FullName() in self._names:
53 decl.is_unique= False
54 for d in self.declarations:
55 if d.FullName() == decl.FullName():
56 d.is_unique = False
57 self._names[decl.FullName()] = 0
58 self.declarations.append(decl)
59
60
61 def ParseElement(self, id, element):
62 method = 'Parse' + element.tag
63 if hasattr(self, method):
64 func = getattr(self, method)
65 func(id, element)
66 else:
67 self.ParseUnknown(id, element)
68
69
70 def GetElementsFromXML(self,filename):
71 'Extracts a dictionary of elements from the gcc_xml file.'
72
73 tree = ElementTree()
74 try:
75 tree.parse(filename)
76 except ExpatError:
77 raise InvalidXMLError, 'Not a XML file: %s' % filename
78
79 root = tree.getroot()
80 if root.tag != 'GCC_XML':
81 raise InvalidXMLError, 'Not a valid GCC_XML file'
82
83 # build a dictionary of id -> element, None
84 elementlist = root.getchildren()
85 elements = {}
86 for element in elementlist:
87 id = element.get('id')
88 if id:
89 elements[id] = element, None
90 return elements
91
92
93 def GetDecl(self, id):
94 if id not in self.elements:
95 if id == '_0':
96 raise InvalidContextError, 'Invalid context found in the xml file.'
97 else:
98 msg = 'ID not found in elements: %s' % id
99 raise ParserError, msg
100
101 elem, decl = self.elements[id]
102 if decl is None:
103 self.ParseElement(id, elem)
104 elem, decl = self.elements[id]
105 if decl is None:
106 raise ParserError, 'Could not parse element: %s' % elem.tag
107 return decl
108
109
110 def GetType(self, id):
111 def Check(id, feature):
112 pos = id.find(feature)
113 if pos != -1:
114 id = id[:pos] + id[pos+1:]
115 return True, id
116 else:
117 return False, id
118 const, id = Check(id, 'c')
119 volatile, id = Check(id, 'v')
120 restricted, id = Check(id, 'r')
121 decl = self.GetDecl(id)
122 if isinstance(decl, Type):
123 res = deepcopy(decl)
124 if const:
125 res.const = const
126 if volatile:
127 res.volatile = volatile
128 if restricted:
129 res.restricted = restricted
130 else:
131 res = Type(decl.FullName(), const)
132 res.volatile = volatile
133 res.restricted = restricted
134 return res
135
136
137 def GetLocation(self, location):
138 file, line = location.split(':')
139 file = self.GetDecl(file)
140 return file, int(line)
141
142
143 def Update(self, id, decl):
144 element, _ = self.elements[id]
145 self.elements[id] = element, decl
146
147
148 def ParseUnknown(self, id, element):
149 name = '__Unknown_Element_%s' % id
150 decl = Unknown(name)
151 self.Update(id, decl)
152
153
154 def ParseNamespace(self, id, element):
155 namespace = element.get('name')
156 context = element.get('context')
157 if context:
158 outer = self.GetDecl(context)
159 if not outer.endswith('::'):
160 outer += '::'
161 namespace = outer + namespace
162 if namespace.startswith('::'):
163 namespace = namespace[2:]
164 self.Update(id, namespace)
165
166
167 def ParseFile(self, id, element):
168 filename = element.get('name')
169 self.Update(id, filename)
170
171
172 def ParseVariable(self, id, element):
173 # in gcc_xml, a static Field is declared as a Variable, so we check
174 # this and call the Field parser.
175 context = self.GetDecl(element.get('context'))
176 if isinstance(context, Class):
177 self.ParseField(id, element)
178 elem, decl = self.elements[id]
179 decl.static = True
180 else:
181 namespace = context
182 name = element.get('name')
183 type_ = self.GetType(element.get('type'))
184 location = self.GetLocation(element.get('location'))
185 variable = Variable(type_, name, namespace)
186 variable.location = location
187 self.AddDecl(variable)
188 self.Update(id, variable)
189
190
191 def GetArguments(self, element):
192 args = []
193 for child in element:
194 if child.tag == 'Argument':
195 type = self.GetType(child.get('type'))
196 type.default = child.get('default')
197 args.append(type)
198 return args
199
200
201 def GetExceptions(self, exception_list):
202 if exception_list is None:
203 return None
204
205 exceptions = []
206 for t in exception_list.split():
207 exceptions.append(self.GetType(t))
208
209 return exceptions
210
211
212 def ParseFunction(self, id, element, functionType=Function):
213 '''functionType is used because a Operator is identical to a normal
214 function, only the type of the function changes.'''
215 name = element.get('name')
216 returns = self.GetType(element.get('returns'))
217 namespace = self.GetDecl(element.get('context'))
218 location = self.GetLocation(element.get('location'))
219 params = self.GetArguments(element)
220 incomplete = bool(int(element.get('incomplete', 0)))
221 throws = self.GetExceptions(element.get('throw', None))
222 function = functionType(name, namespace, returns, params, throws)
223 function.location = location
224 self.AddDecl(function)
225 self.Update(id, function)
226
227
228 def ParseOperatorFunction(self, id, element):
229 self.ParseFunction(id, element, Operator)
230
231
232 def GetHierarchy(self, bases):
233 '''Parses the string "bases" from the xml into a list of tuples of Base
234 instances. The first tuple is the most direct inheritance, and then it
235 goes up in the hierarchy.
236 '''
237
238 if bases is None:
239 return []
240 base_names = bases.split()
241 this_level = []
242 next_levels = []
243 for base in base_names:
244 # get the visibility
245 split = base.split(':')
246 if len(split) == 2:
247 visib = split[0]
248 base = split[1]
249 else:
250 visib = Scope.public
251 decl = self.GetDecl(base)
252 if not isinstance(decl, Class):
253 # on windows, there are some classes which "bases" points to an
254 # "Unimplemented" tag, but we are not interested in this classes
255 # anyway
256 continue
257 base = Base(decl.FullName(), visib)
258 this_level.append(base)
259 # normalize with the other levels
260 for index, level in enumerate(decl.hierarchy):
261 if index < len(next_levels):
262 next_levels[index] = next_levels[index] + level
263 else:
264 next_levels.append(level)
265 hierarchy = []
266 if this_level:
267 hierarchy.append(tuple(this_level))
268 if next_levels:
269 hierarchy.extend(next_levels)
270 return hierarchy
271
272
273 def GetMembers(self, member_list):
274 # members must be a string with the ids of the members
275 if member_list is None:
276 return []
277 members = []
278 for member in member_list.split():
279 decl = self.GetDecl(member)
280 if type(decl) in Class.ValidMemberTypes():
281 if type(decl) is str:
282 print decl
283 members.append(decl)
284 return members
285
286
287 def ParseClass(self, id, element):
288 name = element.get('name')
289 abstract = bool(int(element.get('abstract', '0')))
290 location = self.GetLocation(element.get('location'))
291 context = self.GetDecl(element.get('context'))
292 incomplete = bool(int(element.get('incomplete', 0)))
293 if isinstance(context, str):
294 class_ = Class(name, context, [], abstract)
295 else:
296 # a nested class
297 visib = element.get('access', Scope.public)
298 class_ = NestedClass(
299 name, context.FullName(), visib, [], abstract)
300 class_.incomplete = incomplete
301 # we have to add the declaration of the class before trying
302 # to parse its members and bases, to avoid recursion.
303 self.AddDecl(class_)
304 class_.location = location
305 self.Update(id, class_)
306 # now we can get the members and the bases
307 class_.hierarchy = self.GetHierarchy(element.get('bases'))
308 if class_.hierarchy:
309 class_.bases = class_.hierarchy[0]
310 members = self.GetMembers(element.get('members'))
311 for member in members:
312 if type(member) is str:
313 print member
314 class_.AddMember(member)
315
316
317 def ParseStruct(self, id, element):
318 self.ParseClass(id, element)
319
320
321 FUNDAMENTAL_RENAME = {
322 'long long int' : 'boost::int64_t',
323 'long long unsigned int' : 'boost::uint64_t',
324 }
325
326 def ParseFundamentalType(self, id, element):
327 name = element.get('name')
328 name = self.FUNDAMENTAL_RENAME.get(name, name)
329 type_ = FundamentalType(name)
330 self.Update(id, type_)
331
332
333 def ParseArrayType(self, id, element):
334 type = self.GetType(element.get('type'))
335 min = element.get('min')
336 max = element.get('max')
337 array = ArrayType(type.name, type.const, min, max)
338 self.Update(id, array)
339
340
341 def ParseReferenceType(self, id, element):
342 type = self.GetType(element.get('type'))
343 expand = not isinstance(type, FunctionType)
344 ref = ReferenceType(type.name, type.const, None, expand, type.suffix)
345 self.Update(id, ref)
346
347
348 def ParsePointerType(self, id, element):
349 type = self.GetType(element.get('type'))
350 expand = not isinstance(type, FunctionType)
351 ref = PointerType(type.name, type.const, None, expand, type.suffix)
352 self.Update(id, ref)
353
354
355 def ParseFunctionType(self, id, element):
356 result = self.GetType(element.get('returns'))
357 args = self.GetArguments(element)
358 func = FunctionType(result, args)
359 self.Update(id, func)
360
361
362 def ParseMethodType(self, id, element):
363 class_ = self.GetDecl(element.get('basetype')).FullName()
364 result = self.GetType(element.get('returns'))
365 args = self.GetArguments(element)
366 method = MethodType(result, args, class_)
367 self.Update(id, method)
368
369
370 def ParseField(self, id, element):
371 name = element.get('name')
372 visib = element.get('access', Scope.public)
373 classname = self.GetDecl(element.get('context')).FullName()
374 type_ = self.GetType(element.get('type'))
375 static = bool(int(element.get('extern', '0')))
376 location = self.GetLocation(element.get('location'))
377 var = ClassVariable(type_, name, classname, visib, static)
378 var.location = location
379 self.Update(id, var)
380
381
382 def ParseMethod(self, id, element, methodType=Method):
383 name = element.get('name')
384 result = self.GetType(element.get('returns'))
385 classname = self.GetDecl(element.get('context')).FullName()
386 visib = element.get('access', Scope.public)
387 static = bool(int(element.get('static', '0')))
388 virtual = bool(int(element.get('virtual', '0')))
389 abstract = bool(int(element.get('pure_virtual', '0')))
390 const = bool(int(element.get('const', '0')))
391 location = self.GetLocation(element.get('location'))
392 throws = self.GetExceptions(element.get('throw', None))
393 params = self.GetArguments(element)
394 method = methodType(
395 name, classname, result, params, visib, virtual, abstract, static, const, throws)
396 method.location = location
397 self.Update(id, method)
398
399
400 def ParseOperatorMethod(self, id, element):
401 self.ParseMethod(id, element, ClassOperator)
402
403
404 def ParseConstructor(self, id, element):
405 name = element.get('name')
406 visib = element.get('access', Scope.public)
407 classname = self.GetDecl(element.get('context')).FullName()
408 location = self.GetLocation(element.get('location'))
409 params = self.GetArguments(element)
410 ctor = Constructor(name, classname, params, visib)
411 ctor.location = location
412 self.Update(id, ctor)
413
414
415 def ParseDestructor(self, id, element):
416 name = element.get('name')
417 visib = element.get('access', Scope.public)
418 classname = self.GetDecl(element.get('context')).FullName()
419 virtual = bool(int(element.get('virtual', '0')))
420 location = self.GetLocation(element.get('location'))
421 des = Destructor(name, classname, visib, virtual)
422 des.location = location
423 self.Update(id, des)
424
425
426 def ParseConverter(self, id, element):
427 self.ParseMethod(id, element, ConverterOperator)
428
429
430 def ParseTypedef(self, id, element):
431 name = element.get('name')
432 type = self.GetType(element.get('type'))
433 context = self.GetDecl(element.get('context'))
434 if isinstance(context, Class):
435 context = context.FullName()
436 typedef = Typedef(type, name, context)
437 self.Update(id, typedef)
438 self.AddDecl(typedef)
439
440
441 def ParseEnumeration(self, id, element):
442 name = element.get('name')
443 location = self.GetLocation(element.get('location'))
444 context = self.GetDecl(element.get('context'))
445 incomplete = bool(int(element.get('incomplete', 0)))
446 if isinstance(context, str):
447 enum = Enumeration(name, context)
448 else:
449 visib = element.get('access', Scope.public)
450 enum = ClassEnumeration(name, context.FullName(), visib)
451 self.AddDecl(enum)
452 enum.location = location
453 for child in element:
454 if child.tag == 'EnumValue':
455 name = child.get('name')
456 value = int(child.get('init'))
457 enum.values[name] = value
458 enum.incomplete = incomplete
459 self.Update(id, enum)
460
461
462
463 def ParseDeclarations(filename):
464 'Returns a list of the top declarations found in the gcc_xml file.'
465
466 parser = GCCXMLParser()
467 parser.Parse(filename)
468 return parser.Declarations()
469
470
471 if __name__ == '__main__':
472 ParseDeclarations(r'D:\Programming\Libraries\boost-cvs\boost\libs\python\pyste\example\test.xml')
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.- [get | view] (2011-10-26 21:54:24, 37.3 KB) [[attachment:ClassExporter.py]]
- [get | view] (2011-10-26 21:54:24, 16.1 KB) [[attachment:GCCXMLParser.py]]
- [get | view] (2011-10-26 21:54:24, 91.1 KB) [[attachment:sphere_cylinder_dist_grey_c.png]]
You are not allowed to attach a file to this page.