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