Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/python/pyste/src/Pyste/GCCXMLParser.py @ 29

Last change on this file since 29 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

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