summaryrefslogtreecommitdiff
path: root/libs/python/pyste/src/Pyste/GCCXMLParser.py
diff options
context:
space:
mode:
Diffstat (limited to 'libs/python/pyste/src/Pyste/GCCXMLParser.py')
-rw-r--r--libs/python/pyste/src/Pyste/GCCXMLParser.py478
1 files changed, 478 insertions, 0 deletions
diff --git a/libs/python/pyste/src/Pyste/GCCXMLParser.py b/libs/python/pyste/src/Pyste/GCCXMLParser.py
new file mode 100644
index 000000000..4a1017204
--- /dev/null
+++ b/libs/python/pyste/src/Pyste/GCCXMLParser.py
@@ -0,0 +1,478 @@
+# 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')