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.

You are not allowed to attach a file to this page.