Modules |
Files |
Inheritance Tree |
Inheritance Graph |
Name Index |
Config
File: Synopsis/Linker/Unduplicator.py
1| # $Id: Unduplicator.py,v 1.5 2002/12/10 07:28:49 chalky Exp $
2| #
3| # This file is a part of Synopsis.
4| # Copyright (C) 2000, 2001 Stefan Seefeld
5| # Copyright (C) 2000, 2001 Stephen Davies
6| #
7| # Synopsis is free software; you can redistribute it and/or modify it
8| # under the terms of the GNU General Public License as published by
9| # the Free Software Foundation; either version 2 of the License, or
10| # (at your option) any later version.
11| #
12| # This program is distributed in the hope that it will be useful,
13| # but WITHOUT ANY WARRANTY; without even the implied warranty of
14| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15| # General Public License for more details.
16| #
17| # You should have received a copy of the GNU General Public License
18| # along with this program; if not, write to the Free Software
19| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20| # 02111-1307, USA.
21| #
22| # $Log: Unduplicator.py,v $
23| # Revision 1.5 2002/12/10 07:28:49 chalky
24| # Unduplicate the list of declarations for each file
25| #
26| # Revision 1.4 2002/10/28 16:30:05 chalky
27| # Trying to fix some bugs in the unduplication/stripping stages. Needs more work
28| #
29| # Revision 1.3 2002/10/27 12:23:27 chalky
30| # Fix crash bug
31| #
32| # Revision 1.2 2002/09/20 10:35:12 chalky
33| # Better handling of namespaces with repeated comments
34| #
35| # Revision 1.1 2002/08/23 04:37:26 chalky
36| # Huge refactoring of Linker to make it modular, and use a config system similar
37| # to the HTML package
38| #
39|
40| import string
41|
42| from Synopsis.Core import AST, Type, Util
43|
44| from Linker import config, Operation
45|
46| class Unduplicator(AST.Visitor, Type.Visitor):
47| """Visitor that removes duplicate declarations"""
48| def __init__(self):
49| self.__global = AST.MetaModule("", "",[])
50| self.__scopes = [self.__global]
51| global_dict = {}
52| self.__dict_map = { id(self.__global) : global_dict }
53| self.__dicts = [ global_dict ]
54| def execute(self, ast):
55| declarations = ast.declarations()
56| for decl in declarations:
57| decl.accept(self)
58| declarations[:] = self.__global.declarations()
59| for file in ast.files().values():
60| self.visitSourceFile(file)
61| def lookup(self, name):
62| """look whether the current scope already contains a declaration with the given name"""
63| if self.__dicts[-1].has_key(name):
64| return self.__dicts[-1][name]
65| #for decl in self.__scopes[-1].declarations():
66| # if hasattr(decl, 'name') and decl.name() == name:
67| # retur
68| return None
69| def append(self, declaration):
70| """append declaration to the current scope"""
71| self.__scopes[-1].declarations().append(declaration)
72| self.__dicts[-1][declaration.name()] = declaration
73| def push(self, scope):
74| """push new scope on the stack"""
75| self.__scopes.append(scope)
76| dict = self.__dict_map.setdefault(id(scope), {})
77| self.__dicts.append(dict)
78| def pop(self):
79| """restore the previous scope"""
80| del self.__scopes[-1]
81| del self.__dicts[-1]
82| def top(self):
83| return self.__scopes[-1]
84| def top_dict(self):
85| return self.__dicts[-1]
86|
87| def linkType(self, type):
88| "Returns the same or new proxy type"
89| self.__type = type
90| if type is not None: type.accept(self)
91| return self.__type
92|
93| #################### Type Visitor ###########################################
94|
95| def visitBaseType(self, type):
96| if config.types.has_key(type.name()):
97| self.__type = config.types[type.name()]
98|
99| def visitUnknown(self, type):
100| if config.types.has_key(type.name()):
101| self.__type = config.types[type.name()]
102|
103| def visitDeclared(self, type):
104| if config.types.has_key(type.name()):
105| self.__type = config.types[type.name()]
106| else:
107| print "Couldn't find declared type:",type.name()
108|
109| def visitTemplate(self, type):
110| # Should be a Declared with the same name
111| if not config.types.has_key(type.name()):
112| return
113| declared = config.types[type.name()]
114| if not isinstance(declared, Type.Declared):
115| print "Warning: template declaration was not a declaration:",type.name(),declared.__class__.__name__
116| return
117| decl = declared.declaration()
118| if not hasattr(decl, 'template'):
119| #print "Warning: non-class/function template",type.name(), decl.__class__.__name__
120| return
121| if decl.template():
122| self.__type = decl.template()
123| else:
124| print "Warning: template type disappeared:",type.name()
125|
126| def visitModifier(self, type):
127| alias = self.linkType(type.alias())
128| if alias is not type.alias():
129| type.set_alias(alias)
130| self.__type = type
131|
132| def visitArray(self, type):
133| alias = self.linkType(type.alias())
134| if alias is not type.alias():
135| type.set_alias(alias)
136| self.__type = type
137|
138| def visitParametrized(self, type):
139| templ = self.linkType(type.template())
140| if templ is not type.template():
141| type.set_template(templ)
142| params = tuple(type.parameters())
143| del type.parameters()[:]
144| for param in params:
145| type.parameters().append(self.linkType(param))
146| self.__type = type
147|
148| def visitFunctionType(self, type):
149| ret = self.linkType(type.returnType())
150| if ret is not type.returnType():
151| type.set_returnType(ret)
152| params = tuple(type.parameters())
153| del type.parameters()[:]
154| for param in params:
155| type.parameters().append(self.linkType(param))
156| self.__type = type
157|
158| #################### AST Visitor ############################################
159|
160| def visitSourceFile(self, file):
161| """Resolves any duplicates in the list of declarations from this
162| file"""
163| types = config.types
164|
165| # Clear the list and refill it
166| old_decls = list(file.declarations())
167| new_decls = file.declarations()
168| new_decls[:] = []
169|
170| for decl in old_decls:
171| # Try to find a replacement declaration
172| if types.has_key(decl.name()):
173| declared = types[decl.name()]
174| if isinstance(type, Type.Declared):
175| decl = declared.declaration()
176| new_decls.append(decl)
177|
178| # TODO: includes.
179|
180| def visitModule(self, module):
181| #hmm, we assume that the parent node is a MetaModule. Can that ever fail ?
182| metamodule = self.lookup(module.name())
183| if metamodule is None:
184| metamodule = AST.MetaModule(module.language(), module.type(),module.name())
185| self.append(metamodule)
186| metamodule.module_declarations().append(module)
187| self.merge_comments(metamodule.comments(), module.comments())
188| self.push(metamodule)
189| decls = tuple(module.declarations())
190| del module.declarations()[:]
191| for decl in decls: decl.accept(self)
192| self.pop()
193|
194| def merge_comments(self, dest, src):
195| """Merges the src comments into dest. Merge is just an append, unless
196| src already exists inside dest!"""
197| texter = lambda x: x.text()
198| dest_str = map(texter, dest)
199| src_str = map(texter, src)
200| if dest_str[-len(src):] == src_str: return
201| dest.extend(src)
202|
203| def visitMetaModule(self, module):
204| #hmm, we assume that the parent node is a MetaModule. Can that ever fail ?
205| metamodule = self.lookup(module.name())
206| if metamodule is None or not isinstance(metamodule, AST.MetaModule):
207| metamodule = AST.MetaModule(module.language(), module.type(),module.name())
208| self.append(metamodule)
209| metamodule.module_declarations().extend(module.module_declarations())
210| metamodule.comments().extend(module.comments())
211| self.push(metamodule)
212| decls = tuple(module.declarations())
213| del module.declarations()[:]
214| for decl in decls: decl.accept(self)
215| self.pop()
216|
217| def addDeclaration(self, decl):
218| """Adds a declaration to the current (top) scope.
219| If there is already a Forward declaration, then this replaces it
220| unless this is also a Forward.
221| """
222| name = decl.name()
223| dict = self.__dicts[-1]
224| decls = self.top().declarations()
225| if dict.has_key(name):
226| prev = dict[name]
227| if not isinstance(prev, AST.Forward):
228| return
229| if not isinstance(decl, AST.Forward):
230| decls.remove(prev)
231| decls.append(decl)
232| dict[name] = decl # overwrite prev
233| return
234| decls.append(decl)
235| dict[name] = decl
236|
237| def visitNamed(self, decl):
238| name = decl.name()
239| if self.lookup(decl.name()): return
240| self.addDeclaration(decl)
241|
242| visitDeclaration = addDeclaration
243| visitForward = addDeclaration
244| visitEnum = addDeclaration
245|
246| def visitFunction(self, func):
247| if not isinstance(self.top(), AST.Class):
248| for decl in self.top().declarations():
249| if not isinstance(decl, AST.Function): continue
250| if func.name() == decl.name():
251| return
252| ret = self.linkType(func.returnType())
253| if ret is not func.returnType():
254| func.set_returnType(ret)
255| for param in func.parameters():
256| self.visitParameter(param)
257| self.top().declarations().append(func)
258|
259| visitOperation = visitFunction
260|
261| def visitVariable(self, var):
262| #if not scopedNameOkay(var.name()): return
263| vt = self.linkType(var.vtype())
264| if vt is not var.vtype():
265| var.set_vtype(vt)
266| self.addDeclaration(var)
267|
268| def visitTypedef(self, tdef):
269| alias = self.linkType(tdef.alias())
270| if alias is not tdef.alias():
271| tdef.set_alias(alias)
272| self.addDeclaration(tdef)
273|
274| def visitClass(self, clas):
275| name = clas.name()
276| prev = self.lookup(name)
277| if prev:
278| if isinstance(prev, AST.Forward):
279| # Forward declaration, replace it
280| self.top().declarations().remove(prev)
281| del self.top_dict()[name]
282| elif isinstance(prev, AST.Class):
283| # Previous class. Would ignore duplicate but clas may have
284| # class declarations that prev doesn't. (forward declared
285| # nested -- see ThreadData.hh for example)
286| self.push(prev)
287| for decl in clas.declarations():
288| if isinstance(decl, AST.Class):
289| decl.accept(self)
290| self.pop()
291| return
292| else:
293| print "Unduplicator.visitClass: clas=%s, prev=%s"%(clas.name(), prev)
294| if hasattr(prev, 'name'): print "prev.name=%s"%(prev.name())
295| raise TypeError, "Previous class declaration not a class"
296| self.addDeclaration(clas)
297| for parent in clas.parents():
298| parent.accept(self)
299| self.push(clas)
300| decls = tuple(clas.declarations())
301| del clas.declarations()[:]
302| for decl in decls: decl.accept(self)
303| self.pop()
304|
305| def visitInheritance(self, parent):
306| type = parent.parent()
307| if isinstance(type, Type.Declared) or isinstance(type, Type.Unknown):
308| ltype = self.linkType(type)
309| if ltype is not type:
310| parent.set_parent(ltype)
311| elif isinstance(type, Type.Parametrized):
312| ltype = self.linkType(type.template())
313| if ltype is not type.template():
314| # Must find a Type.Template from it
315| if not isinstance(ltype, Type.Declared):
316| # Error
317| return
318| decl = ltype.declaration()
319| if isinstance(decl, AST.Class):
320| type.set_template(decl.template())
321| else:
322| # Unknown type in class inheritance
323| pass
324|
325| def visitParameter(self, param):
326| type = self.linkType(param.type())
327| if type is not param.type():
328| param.set_type(type)
329|
330| def visitConst(self, const):
331| ct = self.linkType(const.ctype())
332| if ct is not const.ctype():
333| const.set_ctype(ct)
334| self.addDeclaration(const)
335|
336| linkerOperation = Unduplicator