# Copyright Bruno da Silva de Oliveira 2003. Use, modification and # distribution is subject to the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) from declarations import * try: # try to use internal elementtree from xml.etree.cElementTree import ElementTree except ImportError: # try to use cElementTree if avaiable try: from cElementTree import ElementTree except ImportError: # fall back to the normal elementtree from elementtree.ElementTree import ElementTree from xml.parsers.expat import ExpatError from copy import deepcopy from utils import enumerate #============================================================================== # Exceptions #============================================================================== class InvalidXMLError(Exception): pass class ParserError(Exception): pass class InvalidContextError(ParserError): pass #============================================================================== # GCCXMLParser #============================================================================== class GCCXMLParser(object): 'Parse a GCC_XML file and extract the top-level declarations.' interested_tags = {'Class':0, 'Function':0, 'Variable':0, 'Enumeration':0} def Parse(self, filename): self.elements = self.GetElementsFromXML(filename) # high level declarations self.declarations = [] self._names = {} # parse the elements for id in self.elements: element, decl = self.elements[id] if decl is None: try: self.ParseElement(id, element) except InvalidContextError: pass # ignore those nodes with invalid context # (workaround gccxml bug) def Declarations(self): return self.declarations def AddDecl(self, decl): if decl.FullName() in self._names: decl.is_unique= False for d in self.declarations: if d.FullName() == decl.FullName(): d.is_unique = False self._names[decl.FullName()] = 0 self.declarations.append(decl) def ParseElement(self, id, element): method = 'Parse' + element.tag if hasattr(self, method): func = getattr(self, method) func(id, element) else: self.ParseUnknown(id, element) def GetElementsFromXML(self,filename): 'Extracts a dictionary of elements from the gcc_xml file.' tree = ElementTree() try: tree.parse(filename) except ExpatError: raise InvalidXMLError, 'Not a XML file: %s' % filename root = tree.getroot() if root.tag != 'GCC_XML': raise InvalidXMLError, 'Not a valid GCC_XML file' # build a dictionary of id -> element, None elementlist = root.getchildren() elements = {} for element in elementlist: id = element.get('id') if id: elements[id] = element, None return elements def GetDecl(self, id): if id not in self.elements: if id == '_0': raise InvalidContextError, 'Invalid context found in the xml file.' else: msg = 'ID not found in elements: %s' % id raise ParserError, msg elem, decl = self.elements[id] if decl is None: self.ParseElement(id, elem) elem, decl = self.elements[id] if decl is None: raise ParserError, 'Could not parse element: %s' % elem.tag return decl def GetType(self, id): def Check(id, feature): pos = id.find(feature) if pos != -1: id = id[:pos] + id[pos+1:] return True, id else: return False, id const, id = Check(id, 'c') volatile, id = Check(id, 'v') restricted, id = Check(id, 'r') decl = self.GetDecl(id) if isinstance(decl, Type): res = deepcopy(decl) if const: res.const = const if volatile: res.volatile = volatile if restricted: res.restricted = restricted else: res = Type(decl.FullName(), const) res.volatile = volatile res.restricted = restricted return res def GetLocation(self, location): file, line = location.split(':') file = self.GetDecl(file) return file, int(line) def Update(self, id, decl): element, _ = self.elements[id] self.elements[id] = element, decl def ParseUnknown(self, id, element): name = '__Unknown_Element_%s' % id decl = Unknown(name) self.Update(id, decl) def ParseNamespace(self, id, element): namespace = element.get('name') context = element.get('context') if context: outer = self.GetDecl(context) if not outer.endswith('::'): outer += '::' namespace = outer + namespace if namespace.startswith('::'): namespace = namespace[2:] self.Update(id, namespace) def ParseFile(self, id, element): filename = element.get('name') self.Update(id, filename) def ParseVariable(self, id, element): # in gcc_xml, a static Field is declared as a Variable, so we check # this and call the Field parser. context = self.GetDecl(element.get('context')) if isinstance(context, Class): self.ParseField(id, element) elem, decl = self.elements[id] decl.static = True else: namespace = context name = element.get('name') type_ = self.GetType(element.get('type')) location = self.GetLocation(element.get('location')) variable = Variable(type_, name, namespace) variable.location = location self.AddDecl(variable) self.Update(id, variable) def GetArguments(self, element): args = [] for child in element: if child.tag == 'Argument': type = self.GetType(child.get('type')) type.default = child.get('default') args.append(type) return args def GetExceptions(self, exception_list): if exception_list is None: return None exceptions = [] for t in exception_list.split(): exceptions.append(self.GetType(t)) return exceptions def ParseFunction(self, id, element, functionType=Function): '''functionType is used because a Operator is identical to a normal function, only the type of the function changes.''' name = element.get('name') returns = self.GetType(element.get('returns')) namespace = self.GetDecl(element.get('context')) location = self.GetLocation(element.get('location')) params = self.GetArguments(element) incomplete = bool(int(element.get('incomplete', 0))) throws = self.GetExceptions(element.get('throw', None)) function = functionType(name, namespace, returns, params, throws) function.location = location self.AddDecl(function) self.Update(id, function) def ParseOperatorFunction(self, id, element): self.ParseFunction(id, element, Operator) def GetHierarchy(self, bases): '''Parses the string "bases" from the xml into a list of tuples of Base instances. The first tuple is the most direct inheritance, and then it goes up in the hierarchy. ''' if bases is None: return [] base_names = bases.split() this_level = [] next_levels = [] for base in base_names: # get the visibility split = base.split(':') if len(split) == 2: visib = split[0] base = split[1] else: visib = Scope.public decl = self.GetDecl(base) if not isinstance(decl, Class): # on windows, there are some classes which "bases" points to an # "Unimplemented" tag, but we are not interested in this classes # anyway continue base = Base(decl.FullName(), visib) this_level.append(base) # normalize with the other levels for index, level in enumerate(decl.hierarchy): if index < len(next_levels): next_levels[index] = next_levels[index] + level else: next_levels.append(level) hierarchy = [] if this_level: hierarchy.append(tuple(this_level)) if next_levels: hierarchy.extend(next_levels) return hierarchy def GetMembers(self, member_list): # members must be a string with the ids of the members if member_list is None: return [] members = [] for member in member_list.split(): decl = self.GetDecl(member) if type(decl) in Class.ValidMemberTypes(): members.append(decl) return members def ParseClass(self, id, element): name = element.get('name') abstract = bool(int(element.get('abstract', '0'))) location = self.GetLocation(element.get('location')) context = self.GetDecl(element.get('context')) incomplete = bool(int(element.get('incomplete', 0))) if isinstance(context, str): class_ = Class(name, context, [], abstract) else: # a nested class visib = element.get('access', Scope.public) class_ = NestedClass( name, context.FullName(), visib, [], abstract) class_.incomplete = incomplete # we have to add the declaration of the class before trying # to parse its members and bases, to avoid recursion. self.AddDecl(class_) class_.location = location self.Update(id, class_) # now we can get the members and the bases class_.hierarchy = self.GetHierarchy(element.get('bases')) if class_.hierarchy: class_.bases = class_.hierarchy[0] members = self.GetMembers(element.get('members')) for member in members: class_.AddMember(member) def ParseStruct(self, id, element): self.ParseClass(id, element) FUNDAMENTAL_RENAME = { 'long long int' : 'boost::int64_t', 'long long unsigned int' : 'boost::uint64_t', } def ParseFundamentalType(self, id, element): name = element.get('name') name = self.FUNDAMENTAL_RENAME.get(name, name) type_ = FundamentalType(name) self.Update(id, type_) def ParseArrayType(self, id, element): type = self.GetType(element.get('type')) min = element.get('min') max = element.get('max') array = ArrayType(type.name, type.const, min, max) self.Update(id, array) def ParseReferenceType(self, id, element): type = self.GetType(element.get('type')) expand = not isinstance(type, FunctionType) ref = ReferenceType(type.name, type.const, None, expand, type.suffix) self.Update(id, ref) def ParsePointerType(self, id, element): type = self.GetType(element.get('type')) expand = not isinstance(type, FunctionType) ref = PointerType(type.name, type.const, None, expand, type.suffix) self.Update(id, ref) def ParseFunctionType(self, id, element): result = self.GetType(element.get('returns')) args = self.GetArguments(element) func = FunctionType(result, args) self.Update(id, func) def ParseMethodType(self, id, element): class_ = self.GetDecl(element.get('basetype')).FullName() result = self.GetType(element.get('returns')) args = self.GetArguments(element) method = MethodType(result, args, class_) self.Update(id, method) def ParseField(self, id, element): name = element.get('name') visib = element.get('access', Scope.public) classname = self.GetDecl(element.get('context')).FullName() type_ = self.GetType(element.get('type')) static = bool(int(element.get('extern', '0'))) location = self.GetLocation(element.get('location')) var = ClassVariable(type_, name, classname, visib, static) var.location = location self.Update(id, var) def ParseMethod(self, id, element, methodType=Method): name = element.get('name') result = self.GetType(element.get('returns')) classname = self.GetDecl(element.get('context')).FullName() visib = element.get('access', Scope.public) static = bool(int(element.get('static', '0'))) virtual = bool(int(element.get('virtual', '0'))) abstract = bool(int(element.get('pure_virtual', '0'))) const = bool(int(element.get('const', '0'))) location = self.GetLocation(element.get('location')) throws = self.GetExceptions(element.get('throw', None)) params = self.GetArguments(element) method = methodType( name, classname, result, params, visib, virtual, abstract, static, const, throws) method.location = location self.Update(id, method) def ParseOperatorMethod(self, id, element): self.ParseMethod(id, element, ClassOperator) def ParseConstructor(self, id, element): name = element.get('name') visib = element.get('access', Scope.public) classname = self.GetDecl(element.get('context')).FullName() location = self.GetLocation(element.get('location')) params = self.GetArguments(element) artificial = element.get('artificial', False) ctor = Constructor(name, classname, params, visib) ctor.location = location self.Update(id, ctor) def ParseDestructor(self, id, element): name = element.get('name') visib = element.get('access', Scope.public) classname = self.GetDecl(element.get('context')).FullName() virtual = bool(int(element.get('virtual', '0'))) location = self.GetLocation(element.get('location')) des = Destructor(name, classname, visib, virtual) des.location = location self.Update(id, des) def ParseConverter(self, id, element): self.ParseMethod(id, element, ConverterOperator) def ParseTypedef(self, id, element): name = element.get('name') type = self.GetType(element.get('type')) context = self.GetDecl(element.get('context')) if isinstance(context, Class): context = context.FullName() typedef = Typedef(type, name, context) self.Update(id, typedef) self.AddDecl(typedef) def ParseEnumeration(self, id, element): name = element.get('name') location = self.GetLocation(element.get('location')) context = self.GetDecl(element.get('context')) incomplete = bool(int(element.get('incomplete', 0))) if isinstance(context, str): enum = Enumeration(name, context) else: visib = element.get('access', Scope.public) enum = ClassEnumeration(name, context.FullName(), visib) self.AddDecl(enum) enum.location = location for child in element: if child.tag == 'EnumValue': name = child.get('name') value = int(child.get('init')) enum.values[name] = value enum.incomplete = incomplete self.Update(id, enum) def ParseDeclarations(filename): 'Returns a list of the top declarations found in the gcc_xml file.' parser = GCCXMLParser() parser.Parse(filename) return parser.Declarations() if __name__ == '__main__': ParseDeclarations(r'D:\Programming\Libraries\boost-cvs\boost\libs\python\pyste\example\test.xml')