Package pygccxml :: Package declarations :: Module matchers |

# Source Code for Module pygccxml.declarations.matchers

      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  """
      7  defines all "built-in" classes that implement declarations compare functionality
      8  according to some criteria
      9  """
     10
     11  import os
     12  import re
     13  import types
     14  import algorithm
     15  import variable
     16  import namespace
     17  import calldef
     18  import cpptypes
     19  import templates
     20  import class_declaration
     21  from pygccxml import utils
     22
     23 -class matcher_base_t(object):
     24      """matcher_base_t class defines interface for classes that will implement
     25         compare functionality according to some criteria.
     26      """
     27 -    def __init__( self ):
     28          object.__init__( self )
     29
     30 -    def __call__(self, decl):
     31          raise NotImplementedError( "matcher must always implement the __call__() method." )
     32
     33 -    def __invert__(self):
     34          """not-operator (~)"""
     35          return not_matcher_t(self)
     36
     37 -    def __and__(self, other):
     38          """and-operator (&)"""
     39          return and_matcher_t([self, other])
     40
     41 -    def __or__(self, other):
     42          """or-operator (|)"""
     43          return or_matcher_t([self, other])
     44
     45 -    def __str__( self ):
     46          return "base class for all matchers"
     47
     48 -class and_matcher_t(matcher_base_t):
     49      """Combine several other matchers with "&".
     50
     51      For example: find all private functions with name XXX
     52
     53      C{ matcher = access_type_matcher_t( 'private' ) & calldef_matcher_t( name='XXX' ) }
     54      """
     55 -    def __init__(self, matchers):
     56          matcher_base_t.__init__(self)
     57          self.matchers = matchers
     58
     59 -    def __call__(self, decl):
     60          for matcher in self.matchers:
     61              if not matcher(decl):
     62                  return False
     63          return True
     64
     65 -    def __str__(self):
     66          return " & ".join( map( lambda x: "(%s)" % str( x ), self.matchers ) )
     67
     68
     69 -class or_matcher_t(matcher_base_t):
     70      """Combine several other matchers with "|".
     71
     72      For example: find all functions and variables with name 'XXX'
     73
     74      C{ matcher = variable_matcher_t( name='XXX' ) | calldef_matcher_t( name='XXX' ) }
     75
     76      """
     77 -    def __init__(self, matchers):
     78          matcher_base_t.__init__(self)
     79          self.matchers = matchers
     80
     81 -    def __call__(self, decl):
     82          for matcher in self.matchers:
     83              if matcher(decl):
     84                  return True
     85          return False
     86
     87 -    def __str__(self):
     88          return " | ".join( map( lambda x: "(%s)" % str( x ), self.matchers ) )
     89
     90
     91 -class not_matcher_t(matcher_base_t):
     92      """Return the inverse result of matcher, using "~"
     93
     94      For example: find all private and protected declarations
     95
     96      C{ matcher = ~access_type_matcher_t( 'private' ) }
     97
     98      """
     99 -    def __init__(self, matcher):
    100          matcher_base_t.__init__(self)
    101          self.matcher = matcher
    102
    103 -    def __call__(self, decl):
    104          return not self.matcher(decl)
    105
    106 -    def __str__(self):
    107          return "~(%s)"%str(self.matcher)
    108
    109 -class declaration_matcher_t( matcher_base_t ):
    110      """
    111      Instance of this class will match declarations by next criteria:
    112            - declaration name, also could be fully qualified name
    113              Example: wstring or ::std::wstring
    114            - declaration type
    115              Example: L{class_t}, L{namespace_t}, L{enumeration_t}
    116            - location within file system ( file or directory )
    117      """
    118 -    def __init__( self, name=None, decl_type=None, header_dir=None, header_file=None ):
    119          """
    120          @param decl_type: declaration type to match by. For example L{enumeration_t}.
    121          @type decl_type: any class that derives from L{declarations.declaration_t} class
    122
    123          @param name: declaration name, could be full name.
    124          @type name: str
    125
    126          @param header_dir: absolute directory path
    127          @type header_dir: str
    128
    129          @param header_file: absolute file path
    130          @type header_file: str
    131
    132          """
    133          #An other option is that pygccxml will create absolute path using
    134          #os.path.abspath function. But I think this is just wrong, because abspath
    135          #builds path using current working directory. This behavior is fragile
    136          #and very difficult to find a bug.
    137          matcher_base_t.__init__( self )
    138          self.decl_type = decl_type
    139          self.__name = None
    140          self.__opt_is_tmpl_inst = None
    141          self.__opt_tmpl_name = None
    142          self.__opt_is_full_name = None
    143          self.__decl_name_only = None
    144
    145          self._set_name( name )
    146
    147          self.header_dir = header_dir
    148          self.header_file = header_file
    149
    150          if self.header_dir:
    151              self.header_dir = utils.normalize_path( self.header_dir )
    152              if not os.path.isabs( self.header_dir ):
    153                  raise RuntimeError( "Path to header directory should be absolute!" )
    154
    155          if self.header_file:
    156              self.header_file = utils.normalize_path( self.header_file )
    157              if not os.path.isabs( self.header_file ):
    158                  raise RuntimeError( "Path to header file should be absolute!" )
    159
    160 -    def _get_name(self):
    161          return self.__name
    162
    163 -    def _set_name( self, name ):
    164          self.__name = name
    165          if not self.__name:
    166              self.__opt_is_tmpl_inst = None
    167              self.__opt_tmpl_name = None
    168              self.__opt_is_full_name = None
    169              self.__decl_name_only = None
    170          else:
    171              self.__opt_is_tmpl_inst = templates.is_instantiation( self.__name )
    172              self.__opt_tmpl_name = templates.name( self.__name )
    173              if self.__opt_is_tmpl_inst:
    174                  if '::' in self.__opt_tmpl_name:
    175                      self.__opt_is_full_name = True
    176                      self.__decl_name_only = self.__opt_tmpl_name.split('::')[-1]
    177                  else:
    178                      self.__opt_is_full_name = False
    179                      self.__decl_name_only = self.__opt_tmpl_name
    180                  self.__name = templates.normalize( name )
    181              else:
    182                  if '::' in self.__name:
    183                      self.__opt_is_full_name = True
    184                      self.__decl_name_only = self.__name.split('::')[-1]
    185                  else:
    186                      self.__opt_is_full_name = False
    187                      self.__decl_name_only = self.__name
    188
    189
    190      name = property( _get_name, _set_name )
    191
    192 -    def __str__( self ):
    193          msg = []
    194          if not None is self.decl_type:
    195              msg.append( '(decl type==%s)' % self.decl_type.__name__ )
    196          if not None is self.name:
    197              msg.append( '(name==%s)' % self.name )
    198          if not None is self.header_dir:
    199              msg.append( '(header dir==%s)' % self.header_dir )
    200          if not None is self.header_file:
    201              msg.append( '(header file==%s)' % self.header_file )
    202          if not msg:
    203              msg.append( 'any' )
    204          return ' and '.join( msg )
    205
    206 -    def __call__( self, decl ):
    207          if not None is self.decl_type:
    208              if not isinstance( decl, self.decl_type ):
    209                  return False
    210          if not None is self.name:
    211              if not self.check_name( decl ):
    212                  return False
    213          if not None is self.header_dir and decl.location:
    214              decl_dir = os.path.abspath( os.path.dirname( decl.location.file_name ) )
    215              decl_dir = utils.normalize_path( decl_dir )
    216              if decl_dir[:len(self.header_dir)] != self.header_dir:
    217                  return False
    218          if not None is self.header_file and decl.location:
    219              decl_file = os.path.abspath( decl.location.file_name )
    220              decl_file = utils.normalize_path( decl_file )
    221              if decl_file != self.header_file:
    222                  return False
    223          return True
    224
    225 -    def check_name( self, decl ):
    226          assert not None is self.name
    227          if self.__opt_is_tmpl_inst:
    228              if not self.__opt_is_full_name:
    229                  if self.name != templates.normalize( decl.name ) \
    230                     and self.name != templates.normalize( decl.partial_name ):
    231                      return False
    232              else:
    233                  if self.name != templates.normalize( algorithm.full_name( decl, with_defaults=True ) ) \
    234                     and self.name != templates.normalize( algorithm.full_name( decl, with_defaults=False ) ):
    235                      return False
    236          else:
    237              if not self.__opt_is_full_name:
    238                  if self.name != decl.name and self.name != decl.partial_name:
    239                      return False
    240              else:
    241                  if self.name != algorithm.full_name( decl, with_defaults=True ) \
    242                     and self.name != algorithm.full_name( decl, with_defaults=False ):
    243                      return False
    244          return True
    245
    246 -    def is_full_name(self):
    247          return self.__opt_is_full_name
    248
    249 -    def _get_decl_name_only(self):
    250          return self.__decl_name_only
    251      decl_name_only = property( _get_decl_name_only )
    252
    253 -class variable_matcher_t( declaration_matcher_t ):
    254      """
    255      Instance of this class will match variables by next criteria:
    256          - L{declaration_matcher_t} criteria
    257          - variable type. Example: L{int_t} or 'int'
    258      """
    259 -    def __init__( self, name=None, type=None, header_dir=None, header_file=None ):
    260          """
    261          @param type: variable type
    262          @type type: string or instance of L{type_t} derived class
    263          """
    264          declaration_matcher_t.__init__( self
    265                                          , name=name
    266                                          , decl_type=variable.variable_t
    267                                          , header_dir=header_dir
    268                                          , header_file=header_file )
    269          self.type = type
    270
    271 -    def __call__( self, decl ):
    272          if not super( variable_matcher_t, self ).__call__( decl ):
    273              return False
    274          if not None is self.type:
    275              if isinstance( self.type, cpptypes.type_t ):
    276                  if self.type != decl.type:
    277                      return False
    278              else:
    279                  if self.type != decl.type.decl_string:
    280                      return False
    281          return True
    282
    283 -    def __str__( self ):
    284          msg = [ super( variable_matcher_t, self ).__str__() ]
    285          if msg == [ 'any' ]:
    286              msg = []
    287          if not None is self.type:
    288              msg.append( '(value type==%s)' % str(self.type) )
    289          if not msg:
    290              msg.append( 'any' )
    291          return ' and '.join( msg )
    292
    293
    294 -class namespace_matcher_t( declaration_matcher_t ):
    295      """Instance of this class will match namespaces by name."""
    296
    297 -    def __init__( self, name=None ):
    298          declaration_matcher_t.__init__( self, name=name, decl_type=namespace.namespace_t)
    299
    300 -    def __call__( self, decl ):
    301          if self.name and decl.name == '':
    302              #unnamed namespace have same name as thier parent, we should prevent
    303              #this happens. The price is: user should search for unnamed namespace
    304              #directly.
    305              return False
    306          return super( namespace_matcher_t, self ).__call__( decl )
    307
    308
    309 -class calldef_matcher_t( declaration_matcher_t ):
    310      """
    311      Instance of this class will match callable by next criteria:
    312          - L{declaration_matcher_t} criteria
    313          - return type. Example: L{int_t} or 'int'
    314          - argument types
    315      """
    316
    317 -    def __init__( self, name=None, return_type=None, arg_types=None, decl_type=None, header_dir=None, header_file=None):
    318          """
    319          @param return_type: callable return type
    320          @type return_type: string or instance of L{type_t} derived class
    321
    322          @param arg_types: list of function argument types. arg_types can contain.
    323          Any item within the list could be string or instance of L{type_t} derived
    324          class. If you don't want some argument to participate in match you can
    325          put None. For example:
    326
    327          C{ calldef_matcher_t( arg_types=[ 'int &', None ] ) }
    328
    329          will match all functions that takes 2 arguments, where the first one is
    330          reference to integer and second any
    331          @type arg_types: list
    332          """
    333          if None is decl_type:
    334              decl_type = calldef.calldef_t
    335          declaration_matcher_t.__init__( self
    336                                          , name=name
    337                                          , decl_type=decl_type
    338                                          , header_dir=header_dir
    339                                          , header_file=header_file )
    340
    341          self.return_type = return_type
    342          self.arg_types = arg_types
    343
    344 -    def __call__( self, decl ):
    345          if not super( calldef_matcher_t, self ).__call__( decl ):
    346              return False
    347          if not None is self.return_type \
    348             and not self.__compare_types( self.return_type, decl.return_type ):
    349              return False
    350          if self.arg_types:
    351              if isinstance( self.arg_types, (types.ListType, types.TupleType)):
    352                  if len(self.arg_types) != len( decl.arguments ):
    353                      return False
    354                  for type_or_str, arg in zip( self.arg_types, decl.arguments ):
    355                      if None == type_or_str:
    356                          continue
    357                      else:
    358                          if not self.__compare_types( type_or_str, arg.type ):
    359                              return False
    360          return True
    361
    362 -    def __compare_types( self, type_or_str, type ):
    363          assert type_or_str
    364          if type is None:
    365              return False
    366          if isinstance( type_or_str, cpptypes.type_t ):
    367              if type_or_str != type:
    368                  return False
    369          else:
    370              if type_or_str != type.decl_string:
    371                  return False
    372          return True
    373
    374 -    def __str__( self ):
    375          msg = [ super( calldef_matcher_t, self ).__str__() ]
    376          if msg == [ 'any' ]:
    377              msg = []
    378          if not None is self.return_type:
    379              msg.append( '(return type==%s)' % str(self.return_type) )
    380          if self.arg_types:
    381              for i in range( len( self.arg_types ) ):
    382                  if self.arg_types[i] is None:
    383                      msg.append( '(arg %d type==any)' % i )
    384                  else:
    385                      msg.append( '(arg %d type==%s)' % ( i, str( self.arg_types[i] ) ) )
    386          if not msg:
    387              msg.append( 'any' )
    388          return ' and '.join( msg )
    389
    390
    391 -class operator_matcher_t( calldef_matcher_t ):
    392      """
    393      Instance of this class will match operators by next criteria:
    394          - L{calldef_matcher_t} criteria
    395          - operator symbol: =, !=, (), [] and etc
    396      """
    397 -    def __init__( self, name=None, symbol=None, return_type=None, arg_types=None, decl_type=None, header_dir=None, header_file=None):
    398          """
    399          @param symbol: operator symbol
    400          @type symbol: str
    401          """
    402          if None is decl_type:
    403              decl_type = calldef.operator_t
    404          calldef_matcher_t.__init__( self
    405                                      , name=name
    406                                      , return_type=return_type
    407                                      , arg_types=arg_types
    408                                      , decl_type=decl_type
    409                                      , header_dir=header_dir
    410                                      , header_file=header_file)
    411          self.symbol = symbol
    412
    413 -    def __call__( self, decl ):
    414          if not super( operator_matcher_t, self ).__call__( decl ):
    415              return False
    416          if not None is self.symbol:
    417              if self.symbol != decl.symbol:
    418                  return False
    419          return True
    420
    421 -    def __str__( self ):
    422          msg = [ super( operator_matcher_t, self ).__str__() ]
    423          if msg == [ 'any' ]:
    424              msg = []
    425          if not None is self.symbol:
    426              msg.append( '(symbol==%s)' % str(self.symbol) )
    427          if not msg:
    428              msg.append( 'any' )
    429          return ' and '.join( msg )
    430
    431 -class regex_matcher_t( matcher_base_t ):
    432      """
    433      Instance of this class will match declaration using regular expression.
    434      User should supply a function that will extract from declaration desired
    435      information as string. Later, this matcher will match that string using
    436      user regular expression.
    437      """
    438 -    def __init__( self, regex, function=None ):
    439          """
    440          @param regex: regular expression
    441          @type regex: string, an instance of this class will compile it for you
    442
    443          @param function: function that will be called to get an information from
    444          declaration as string. As input this function takes 1 argument: reference
    445          to declaration. Return value should be string. If function is None, then
    446          the matcher will use declaration name.
    447
    448          """
    449          matcher_base_t.__init__(self)
    450          self.regex = re.compile( regex )
    451          self.function = function
    452          if None is self.function:
    453              self.function = lambda decl: decl.name
    454
    455 -    def __call__( self, decl ):
    456          text = self.function( decl )
    457          return bool( self.regex.match( text ) )
    458
    459 -    def __str__( self ):
    460          return '(regex=%s)' % self.regex
    461
    462 -class access_type_matcher_t( matcher_base_t ):
    463      """
    464      Instance of this class will match declaration by its access type: public,
    465      private or protected. If declarations does not have access type, for example
    466      free function, then False will be returned.
    467      """
    468
    469 -    def __init__( self, access_type ):
    470          """
    471          @param access_type: declaration access type
    472          @type access_type: L{ACCESS_TYPES} defines few consts for your convinience.
    473          Any way you can pass public, private or protected as argument to this function
    474          """
    475          matcher_base_t.__init__( self )
    476          self.access_type = access_type
    477
    478 -    def __call__( self, decl ):
    479          if not isinstance( decl.parent, class_declaration.class_t ):
    480              return False
    481          return self.access_type == decl.parent.find_out_member_access_type( decl )
    482
    483 -    def __str__( self ):
    484          return '(access type=%s)' % self.access_type
    485
    486 -class virtuality_type_matcher_t( matcher_base_t ):
    487      """
    488      Instance of this class will match declaration by its virtuality type: not virtual,
    489      virtual or pure virtual. If declarations does not have virtuality type, for example
    490      free function, then False will be returned.
    491      """
    492
    493 -    def __init__( self, virtuality_type ):
    494          """
    495          @param access_type: declaration access type
    496          @type access_type: L{VIRTUALITY_TYPES} defines few consts for your convinience.
    497          """
    498          matcher_base_t.__init__( self )
    499          self.virtuality_type = virtuality_type
    500
    501 -    def __call__( self, decl ):
    502          if not isinstance( decl.parent, class_declaration.class_t ):
    503              return False
    504          return self.virtuality_type == decl.virtuality
    505
    506 -    def __str__( self ):
    507          return '(virtuality type=%s)' % self.virtuality_type
    508
    509
    510 -class custom_matcher_t( matcher_base_t ):
    511      """
    512      Instance of this class will match declaration by user custom criteria.
    513      """
    514
    515 -    def __init__( self, function ):
    516          """
    517          @param function: callable, that takes single argument - declaration instance
    518                           should return True or False
    519          """
    520          matcher_base_t.__init__( self )
    521          self.function = function
    522
    523 -    def __call__( self, decl ):
    524          return bool( self.function( decl ) )
    525
    526 -    def __str__( self ):
    527          return '(user criteria)'
    528