| # 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 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') |