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