1#!/usr/bin/env python
2""" cdecl.py - parse c declarations
3
4(c) 2002, 2003, 2004, 2005 Simon Burton <simon@arrowtheory.com>
5Released under GNU LGPL license.
6
7version 0.xx
8
9"""
10
11import string
12
13
14class Node(list):
15    " A node in a parse tree "
16
17    def __init__(self,*items,**kw):
18        list.__init__( self, items )
19        self.lock1 = 0 # these two should be properties (simplifies serializing)
20        self.lock2 = 0
21        self.verbose = 0
22        for key in kw.keys():
23            self.__dict__[key] = kw[key]
24
25    def __str__(self):
26        attrs = []
27        for item in self:
28            if isinstance(item,Node):
29                attrs.append( str(item) )
30            else:
31                attrs.append( repr(item) )
32        attrs = ','.join(attrs)
33        return "%s(%s)"%(self.__class__.__name__,attrs)
34
35    def safe_repr( self, tank ):
36        tank[ str(self) ] = None
37        attrs = []
38        for item in self:
39            if isinstance(item,Node):
40                attrs.append( item.safe_repr(tank) ) # can we use repr here ?
41            else:
42                attrs.append( repr(item) )
43        # this is the dangerous bit:
44        for key, val in self.__dict__.items():
45            if isinstance(val,Node):
46                if str(val) not in tank:
47                    attrs.append( '%s=%s'%(key,val.safe_repr(tank)) )
48            else:
49                attrs.append( '%s=%s'%(key,repr(val)) )
50        attrs = ','.join(attrs)
51        return "%s(%s)"%(self.__class__.__name__,attrs)
52
53    def __repr__(self):
54        #attrs = ','.join( [repr(item) for item in self] + \
55        # [ '%s=%s'%(key,repr(val)) for key,val in self.__dict__.items() ] )
56        #return "%s%s"%(self.__class__.__name__,tuple(attrs))
57        return self.safe_repr({})
58
59    def __eq__(self,other):
60        if not isinstance(other,Node):
61            return 0
62        if len(self)!=len(other):
63            return 0
64        for i in range(len(self)):
65            if not self[i]==other[i]:
66                return 0
67        return 1
68
69    def __ne__(self,other):
70        return not self==other
71
72    def filter(self,cls):
73        return [x for x in self if isinstance(x,cls)]
74        #return filter( lambda x:isinstance(x,cls), self )
75
76    def deepfilter(self,cls):
77        " bottom-up "
78        return [x for x in self.nodes() if isinstance(x,cls)]
79
80    def find(self,cls):
81        for x in self:
82            if isinstance(x,cls):
83                return x
84        return None
85
86    def deepfind(self,cls):
87        " bottom-up isinstance search "
88        for x in self:
89            if isinstance(x,Node):
90                if isinstance(x,cls):
91                    return x
92                node = x.deepfind(cls)
93                if node is not None:
94                    return node
95        if isinstance(self,cls):
96            return self
97        return None
98
99    def leaves(self):
100        for i in self:
101            if isinstance( i, Node ):
102                for j in i.leaves():
103                    yield j
104            else:
105                yield i
106
107    def nodes(self):
108        " bottom-up iteration "
109        for i in self:
110            if isinstance( i, Node ):
111                for j in i.nodes():
112                    yield j
113        yield self
114
115    def deeplen(self):
116        i=0
117        if not self.lock2:
118            self.lock2=1
119            for item in self:
120                i+=1
121                if isinstance(item,Node):
122                    i+=item.deeplen()
123            self.lock2=0
124        else:
125            i+=1
126        return i
127
128    def deepstr(self,level=0,comment=False,nl='\n',indent='    '):
129        if self.deeplen() < 4:
130            nl = ""; indent = ""
131        #else:
132            #nl="\n"; indent = "    "
133        s = []
134        if not self.lock1:
135            self.lock1=1
136            for item in self:
137                if isinstance(item,Node):
138                    s.append( indent*(level+1)+item.deepstr(level+1,False,nl,indent) )
139                else:
140                    s.append( indent*(level+1)+repr(item) )
141            self.lock1=0
142        else:
143            for item in self:
144                if isinstance(item,Node):
145                    s.append( indent*(level+1)+"<recursion...>" )
146                else:
147                    s.append( indent*(level+1)+"%s"%repr(item) )
148        s = "%s(%s)"%(self.__class__.__name__,nl+string.join(s,","+nl))
149        if comment:
150            s = '#' + s.replace('\n','\n#')
151        return s
152
153    def clone(self):
154        items = []
155        for item in self:
156            if isinstance(item,Node):
157                item = item.clone()
158            items.append(item)
159        # we skip any attributes...
160        return self.__class__(*items)
161
162    def fastclone(self):
163        # XX is it faster ???
164        #print "clone"
165        nodes = [self]
166        idxs = [0]
167        itemss = [ [] ]
168        while nodes:
169            assert len(nodes)==len(idxs)==len(itemss)
170            node = nodes[-1]
171            items = itemss[-1]
172            assert idxs[-1] == len(items)
173            while idxs[-1]==len(node):
174                # pop
175                _node = node.__class__( *items )
176                _node.__dict__.update( node.__dict__ )
177                nodes.pop(-1)
178                idxs.pop(-1)
179                itemss.pop(-1)
180                if not nodes:
181                    #for node0 in self.nodes():
182                        #for node1 in _node.nodes():
183                            #assert node0 is not node1
184                    #assert _node == self
185                    return _node # Done !!
186                node = nodes[-1]
187                items = itemss[-1]
188                items.append(_node) # set
189                idxs[-1] += 1
190                assert idxs[-1] == len(items)
191                #assert idxs[-1] < len(node), str( (node,nodes,idxs,itemss) )
192
193            _node = node[ idxs[-1] ]
194            # while idxs[-1]<len(node):
195            if isinstance(_node,Node):
196                # push
197                nodes.append( _node )
198                idxs.append( 0 )
199                itemss.append( [] )
200            else:
201                # next
202                items.append(_node)
203                idxs[-1] += 1
204                assert idxs[-1] == len(items)
205
206    def expose(self,cls):
207        ' expose children of any <cls> instance '
208        # children first
209        for x in self:
210            if isinstance(x,Node):
211                x.expose(cls)
212        # now the tricky bit
213        i=0
214        while i < len(self):
215            if isinstance(self[i],cls):
216                node=self.pop(i)
217                for x in node:
218                    assert not isinstance(x,cls)
219                    # pass on some attributes
220                    if hasattr(node,'lines') and not hasattr(x,'lines'):
221                        x.lines=node.lines
222                    if hasattr(node,'file') and not hasattr(x,'file'):
223                        x.file=node.file
224                    self.insert(i,x) # expose
225                    i=i+1
226                    assert i<=len(self)
227            else:
228                i=i+1
229
230    def get_parent( self, item ): # XX 25% CPU time here XX
231        assert self != item
232        if item in self:
233            return self
234        for child in self:
235            if isinstance(child, Node):
236                parent = child.get_parent(item)
237                if parent is not None:
238                    return parent
239        return None
240
241    def expose_node( self, item ):
242        assert self != item
243        parent = self.get_parent(item)
244        idx = parent.index( item )
245        parent[idx:idx+1] = item[:]
246
247    def delete(self,cls):
248        ' delete any <cls> subtree '
249        for x in self:
250            if isinstance(x,Node):
251                x.delete(cls)
252        # now the tricky bit
253        i=0
254        while i < len(self):
255            if isinstance(self[i],cls):
256                self.pop(i)
257            else:
258                i=i+1
259
260    def deeprm(self,item):
261        ' remove any items matching <item> '
262        for x in self:
263            if isinstance(x,Node):
264                x.deeprm(item)
265        # now the tricky bit
266        i=0
267        while i < len(self):
268            if self[i] == item:
269                self.pop(i)
270            else:
271                i=i+1
272
273    def idem(self,cls):
274        " <cls> is made idempotent "
275        # children first
276        for x in self:
277            if isinstance(x,Node):
278                x.idem(cls)
279        if isinstance(self,cls):
280            # now the tricky bit
281            i=0
282            while i < len(self):
283                if isinstance(self[i],cls):
284                    node = self.pop(i)
285                    for x in node:
286                        assert not isinstance(x,cls)
287                        self.insert(i,x) # idempotent
288                        i=i+1
289                        assert i<=len(self)
290                else:
291                    i=i+1
292
293if __name__=="__main__":
294    node = Node( 'a', Node(1,2), Node(Node(Node(),1)) )
295
296    print node
297    print node.clone()
298
299
300
301
302