Package pygccxml :: Package declarations :: Module algorithm |

# Source Code for Module pygccxml.declarations.algorithm

      1  # Copyright 2004-2008 Roman Yakovenko.
      2  # Distributed under the Boost Software License, Version 1.0. (See
      3  # accompanying file LICENSE_1_0.txt or copy at
      4  # http://www.boost.org/LICENSE_1_0.txt)
      5
      6  """defines few unrelated algorithms, that works on declarations"""
      7
      8  import types
      9
     10 -def declaration_path( decl, with_defaults=True ):
     11      """
     12      returns a list of parent declarations names
     13
     14      @param decl: declaration for which declaration path should be calculated
     15      @type decl: L{declaration_t}
     16
     17      @return: [names], where first item contains top parent name and last item
     18               contains decl name
     19      """
     20      if not decl:
     21          return []
     22      if not decl.cache.declaration_path:
     23          result = [ decl.name ]
     24          parent = decl.parent
     25          while parent:
     26              if parent.cache.declaration_path:
     27                  result.reverse()
     28                  decl.cache.declaration_path = parent.cache.declaration_path + result
     29                  return decl.cache.declaration_path
     30              else:
     31                  result.append( parent.name )
     32                  parent = parent.parent
     33          result.reverse()
     34          decl.cache.declaration_path = result
     35          return result
     36      else:
     37          return decl.cache.declaration_path
     38
     39 -def partial_declaration_path( decl ):
     40      """
     41      returns a list of parent declarations names without template arguments that
     42      have default value
     43
     44      @param decl: declaration for which declaration path should be calculated
     45      @type decl: L{declaration_t}
     46
     47      @return: [names], where first item contains top parent name and last item
     48               contains decl name
     49      """
     50      #TODO:
     51      #If parent declaration cache already has declaration_path, reuse it for
     52      #calculation.
     53      if not decl:
     54          return []
     55      if not decl.cache.partial_declaration_path:
     56          result = [ decl.partial_name ]
     57          parent = decl.parent
     58          while parent:
     59              if parent.cache.partial_declaration_path:
     60                  result.reverse()
     61                  decl.cache.partial_declaration_path \
     62                      = parent.cache.partial_declaration_path + result
     63                  return decl.cache.partial_declaration_path
     64              else:
     65                  result.append( parent.partial_name )
     66                  parent = parent.parent
     67          result.reverse()
     68          decl.cache.partial_declaration_path = result
     69          return result
     70      else:
     71          return decl.cache.partial_declaration_path
     72
     73 -def get_named_parent( decl ):
     74      """
     75      returns a reference to a named parent declaration
     76
     77      @param decl: the child declaration
     78      @type decl: L{declaration_t}
     79
     80      @return: reference to L{declaration_t} or None if not found
     81      """
     82      if not decl:
     83          return None
     84
     85      parent = decl.parent
     86      while parent and ( not parent.name or parent.name == '::' ):
     87          parent = parent.parent
     88      return parent
     89
     90
     91 -def full_name_from_declaration_path( dpath ):
     92      ##Here I have lack of knowledge:
     93      ##TODO: "What is the full name of declaration declared in unnamed namespace?"
     94      result = filter( None, dpath )
     95      result = result[0] + '::'.join( result[1:] )
     96      return result
     97
     98 -def full_name( decl, with_defaults=True ):
     99      """
    100      returns full name of the declaration
    101      @param decl: declaration for which full name should be calculated. If decl
    102      belongs to unnamed namespace, then L{full_name} is not valid C++ full name.
    103
    104      @type decl: L{declaration_t}
    105
    106      @return: full name of declarations.
    107      """
    108      if None is decl:
    109          raise RuntimeError( "Unable to generate full name for None object!" )
    110      if with_defaults:
    111          if not decl.cache.full_name:
    112              decl.cache.full_name = full_name_from_declaration_path( declaration_path( decl ) )
    113          return decl.cache.full_name
    114      else:
    115          if not decl.cache.full_partial_name:
    116              decl.cache.full_partial_name \
    117                  = full_name_from_declaration_path( partial_declaration_path( decl ) )
    118          return decl.cache.full_partial_name
    119
    120 -def make_flatten( decl_or_decls ):
    121      """
    122      converts tree representation of declarations to flatten one.
    123
    124      @param decl_or_decls: reference to list of declaration's or single declaration
    125      @type decl_or_decls: L{declaration_t} or [ L{declaration_t} ]
    126
    127      @return: [ all internal declarations ]
    128      """
    129      import pygccxml.declarations #prevent cyclic import
    130      def proceed_single( decl ):
    131          answer = [ decl ]
    132          if not isinstance( decl, pygccxml.declarations.scopedef_t ):
    133              return answer
    134          for elem in decl.declarations:
    135              if isinstance( elem, pygccxml.declarations.scopedef_t ):
    136                  answer.extend( proceed_single( elem ) )
    137              else:
    138                  answer.append( elem )
    139          return answer
    140
    141      decls = []
    142      if isinstance( decl_or_decls, types.ListType ):
    143          decls.extend( decl_or_decls )
    144      else:
    145          decls.append( decl_or_decls )
    146      answer = []
    147      for decl in decls:
    148          answer.extend( proceed_single( decl ) )
    149      return answer
    150
    151 -def __make_flatten_generator( decl_or_decls ):
    152      """
    153      converts tree representation of declarations to flatten one.
    154
    155      @param decl_or_decls: reference to list of declaration's or single declaration
    156      @type decl_or_decls: L{declaration_t} or [ L{declaration_t} ]
    157
    158      @return: [ all internal declarations ]
    159      """
    160
    161      import pygccxml.declarations
    162      def proceed_single( decl ):
    163          yield decl
    164          if not isinstance( decl, pygccxml.declarations.scopedef_t):
    165              return
    166          for internal in decl.declarations:
    167              if isinstance( internal, pygccxml.declarations.scopedef_t):
    168                  for internal_internal in proceed_single( internal ):
    169                      yield internal_internal
    170              else:
    171                  yield internal
    172
    173      if isinstance( decl_or_decls, types.ListType ):
    174          for creator in decl_or_decls:
    175              for internal in proceed_single( creator ):
    176                  yield internal
    177      else:
    178          for internal in proceed_single( decl_or_decls ):
    179              yield internal
    180
    181 -def get_global_namespace(decls):
    182      import pygccxml.declarations
    183      found = filter( lambda decl: decl.name == '::'
    184                                   and isinstance( decl, pygccxml.declarations.namespace_t )
    185                      , make_flatten( decls ) )
    186      if len( found ) == 1:
    187          return found[0]
    188      raise RuntimeError( "Unable to find global namespace." )
    189
    190 -class match_declaration_t:
    191      """
    192      helper class for different search algorithms.
    193
    194      This class will help developer to match declaration by:
    195          - declaration type, for example L{class_t} or L{operator_t}.
    196          - declaration name
    197          - declaration full name
    198          - reference to parent declaration
    199      """
    200
    201 -    def __init__( self, type=None, name=None, fullname=None, parent=None ):
    202          self.type = type
    203          self.name = name
    204          self.fullname = fullname
    205          self.parent = parent
    206
    207 -    def does_match_exist(self, inst):
    208          """
    209          returns True if inst do match one of specified criteria
    210
    211          @param inst: declaration instance
    212          @type inst: L{declaration_t}
    213
    214          @return: bool
    215          """
    216          answer = True
    217          if None != self.type:
    218              answer &= isinstance( inst, self.type)
    219          if None != self.name:
    220              answer &= inst.name == self.name
    221          if None != self.parent:
    222              answer &= self.parent is inst.parent
    223          if None != self.fullname:
    224              if inst.name:
    225                  answer &= self.fullname == full_name( inst )
    226              else:
    227                  answer = False
    228          return answer
    229
    230 -    def __call__(self, inst):
    231          """C{return self.does_match_exist(inst)}"""
    232          return self.does_match_exist(inst)
    233
    234 -def find_all_declarations( declarations
    235                             , type=None
    236                             , name=None
    237                             , parent=None
    238                             , recursive=True
    239                             , fullname=None ):
    240      """
    241      returns a list of all declarations that match criteria, defined by developer
    242
    243      For more information about arguments see L{match_declaration_t} class.
    244
    245      @return: [ matched declarations ]
    246      """
    247      decls = []
    248      if recursive:
    249          decls = make_flatten( declarations )
    250      else:
    251          decls = declarations
    252
    253      return filter( match_declaration_t(type, name, fullname, parent), decls )
    254
    255 -def find_declaration( declarations
    256                        , type=None
    257                        , name=None
    258                        , parent=None
    259                        , recursive=True
    260                        , fullname=None ):
    261      """
    262      returns single declaration that match criteria, defined by developer.
    263      If more the one declaration was found None will be returned.
    264
    265      For more information about arguments see L{match_declaration_t} class.
    266
    267      @return: matched declaration L{declaration_t} or None
    268      """
    269      decl = find_all_declarations( declarations, type=type, name=name, parent=parent, recursive=recursive, fullname=fullname )
    270      if len( decl ) == 1:
    271          return decl[0]
    272
    273 -def find_first_declaration( declarations, type=None, name=None, parent=None, recursive=True, fullname=None ):
    274      """
    275      returns first declaration that match criteria, defined by developer
    276
    277      For more information about arguments see L{match_declaration_t} class.
    278
    279      @return: matched declaration L{declaration_t} or None
    280      """
    281      matcher = match_declaration_t(type, name, fullname, parent)
    282      if recursive:
    283          decls = make_flatten( declarations )
    284      else:
    285          decls = declarations
    286      for decl in decls:
    287          if matcher( decl ):
    288              return decl
    289      return None
    290
    291 -def declaration_files(decl_or_decls):
    292      """
    293      returns set of files
    294
    295      Every declaration is declared in some file. This function returns set, that
    296      contains all file names of declarations.
    297
    298      @param decl_or_decls: reference to list of declaration's or single declaration
    299      @type decl_or_decls: L{declaration_t} or [ L{declaration_t} ]
    300
    301      @return: set( declaration file names )
    302      """
    303      files = set()
    304      decls = make_flatten( decl_or_decls )
    305      for decl in decls:
    306          if decl.location:
    307              files.add( decl.location.file_name )
    308      return files
    309
    310 -class visit_function_has_not_been_found_t( RuntimeError ):
    311      """
    312      exception that is raised, from L{apply_visitor}, when a visitor could not be
    313      applied.
    314
    315      """
    316 -    def __init__( self, visitor, decl_inst ):
    317          RuntimeError.__init__( self )
    318          self.__msg = \
    319              "Unable to find visit function. Visitor class: %s. Declaration instance class: %s'" \
    320              % ( visitor.__class__.__name__, decl_inst.__class__.__name__ )
    321 -    def __str__(self):
    322          return self.__msg
    323
    324 -def apply_visitor( visitor, decl_inst):
    325      """
    326      applies a visitor on declaration instance
    327
    328      @param visitor: instance
    329      @type visitor: L{type_visitor_t} or L{decl_visitor_t}
    330      """
    331      fname = 'visit_' + decl_inst.__class__.__name__[:-2] #removing '_t' from class name
    332      if not hasattr(visitor, fname ):
    333          raise visit_function_has_not_been_found_t( visitor, decl_inst )
    334      getattr( visitor, fname )()
    335