Package pygccxml :: Package parser :: Module patcher |

# Source Code for Module pygccxml.parser.patcher

      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  from pygccxml import utils
      7  from pygccxml import declarations
      8
      9
     10 -class default_argument_patcher_t( object ):
     11 -    def __init__( self, enums ):
     12          object.__init__( self )
     13          self.__enums = enums
     14
     15 -    def __call__(self, decl):
     16          for arg in decl.arguments:
     17              if not arg.default_value:
     18                  continue
     19              fixer = self.__find_fixer( decl, arg )
     20              if fixer:
     21                  arg.default_value = fixer( decl, arg )
     22
     23 -    def __find_fixer(self, func, arg):
     24          if not arg.default_value:
     25              return False
     26          elif self.__is_unqualified_enum( func, arg ):
     27              return self.__fix_unqualified_enum
     28          elif self.__is_double_call( func, arg ):
     29              return self.__fix_double_call
     30          elif self.__is_invalid_integral( func, arg ):
     31              return self.__fix_invalid_integral
     32          elif self.__is_constructor_call( func, arg ):
     33              return self.__fix_constructor_call
     34          else:
     35              return None
     36
     37 -    def __join_names( self, prefix, suffix ):
     38          if prefix == '::':
     39              return '::' + suffix
     40          else:
     41              return prefix + '::' + suffix
     42
     43 -    def __is_unqualified_enum(self, func, arg):
     44          type_ = declarations.remove_reference( declarations.remove_cv( arg.type ) )
     45          if not declarations.is_enum( type_ ):
     46              return False
     47          enum_type = declarations.enum_declaration( type_ )
     48          return enum_type.has_value_name( arg.default_value )
     49
     50 -    def __fix_unqualified_enum( self, func, arg):
     51          type_ = declarations.remove_reference( declarations.remove_cv( arg.type ) )
     52          enum_type = declarations.enum_declaration( type_ )
     53          return self.__join_names( enum_type.parent.decl_string, arg.default_value )
     54
     55 -    def __is_invalid_integral(self, func, arg):
     56          type_ = declarations.remove_reference( declarations.remove_cv( arg.type ) )
     57          if not declarations.is_integral( type_ ):
     58              return False
     59          try:
     60              int( arg.default_value )
     61              return False
     62          except:
     63              return True
     64
     65 -    def __fix_invalid_integral(self, func, arg):
     66          try:
     67              int( arg.default_value )
     68              return arg.default_value
     69          except:
     70              pass
     71
     72          try:
     73              int( arg.default_value, 16 )
     74              if 64 == utils.get_architecture():
     75                  #on 64 bit architecture, gccxml reports 0fffff, which is valid number
     76                  #the problem is that in this case it is so buggy so pygccxml can not fix it
     77                  #users will have to fix the default value manually
     78                  return arg.default_value
     79              default_value = arg.default_value.lower()
     80              found_hex = filter( lambda ch: ch in 'abcdef', default_value )
     81              if found_hex and not default_value.startswith( '0x' ):
     82                  int( '0x' + default_value, 16 )
     83                  return '0x' + default_value
     84          except:
     85              pass
     86
     87          #may be we deal with enum
     88          parent = func.parent
     89          while parent:
     90              found = self.__find_enum( parent, arg.default_value )
     91              if found:
     92                  if declarations.is_fundamental( arg.type ) and ' ' in arg.type.decl_string:
     93                      template = '(%s)(%s)'
     94                  else:
     95                      template = '%s(%s)'
     96                  return template % ( arg.type.decl_string
     97                                      , self.__join_names( found.parent.decl_string, arg.default_value ) )
     98              else:
     99                  parent = parent.parent
    100          return arg.default_value
    101
    102 -    def __find_enum( self, scope, default_value ):
    103          #this algorithm could be improved: it could take into account
    104          #1. unnamed namespaced
    105          #2. location within files
    106
    107          for enum in self.__enums:
    108              if enum.parent is scope and enum.has_value_name( default_value ):
    109                  return enum
    110          return None
    111
    112 -    def __is_double_call( self, func, arg ):
    113          call_invocation = declarations.call_invocation
    114          dv = arg.default_value
    115          found1 = call_invocation.find_args( dv )
    116          if found1 == call_invocation.NOT_FOUND:
    117              return False
    118          found2 = call_invocation.find_args( dv, found1[1] + 1 )
    119          if found2 == call_invocation.NOT_FOUND:
    120              return False
    121          args1 = call_invocation.args( dv[ found1[0] : found1[1] + 1 ] )
    122          args2 = call_invocation.args( dv[ found2[0] : found2[1] + 1 ] )
    123          return len(args1) == len(args2)
    124
    125 -    def __fix_double_call( self, func, arg ):
    126          call_invocation = declarations.call_invocation
    127          dv = arg.default_value
    128          found1 = call_invocation.find_args( dv )
    129          found2 = call_invocation.find_args( dv, found1[1] + 1 )
    130          #args1 = call_invocation.args( dv[ found1[0] : found1[1] + 1 ] )
    131          args2 = call_invocation.args( dv[ found2[0] : found2[1] + 1 ] )
    132          return call_invocation.join( dv[:found1[0]], args2 )
    133
    134 -    def __is_constructor_call( self, func, arg ):
    135          #if '0.9' in func.compiler:
    136          #    return False
    137          call_invocation = declarations.call_invocation
    138          dv = arg.default_value
    139          if not call_invocation.is_call_invocation( dv ):
    140              return False
    141          name = call_invocation.name( dv )
    142          base_type = declarations.base_type( arg.type )
    143          if not isinstance( base_type, declarations.declarated_t ):
    144              return False
    145          decl = base_type.declaration
    146          return decl.name == name \
    147                 or ( isinstance( decl, declarations.class_t ) \
    148                      and name in map( lambda typedef: typedef.name, decl.aliases ) )
    149
    150 -    def __fix_constructor_call( self, func, arg ):
    151          call_invocation = declarations.call_invocation
    152          dv = arg.default_value
    153          if not call_invocation.is_call_invocation( dv ):
    154              return False
    155          base_type = declarations.base_type( arg.type )
    156          decl = base_type.declaration
    157          name, args = call_invocation.split( dv )
    158          if decl.name != name:
    159              #we have some alias to the class
    160              relevant_typedefs = filter( lambda typedef: typedef.name == name
    161                                          , decl.aliases )
    162              if 1 == len( relevant_typedefs ):
    163                  f_q_name = self.__join_names( declarations.full_name( relevant_typedefs[0].parent )
    164                                                , name )
    165              else:#in this case we can not say which typedef user uses:
    166                  f_q_name = self.__join_names( declarations.full_name( decl.parent )
    167                                                , decl.name )
    168          else:
    169              f_q_name = self.__join_names( declarations.full_name( decl.parent ), name )
    170
    171          return call_invocation.join( f_q_name, args )
    172
    173 -class casting_operator_patcher_t( object ):
    174 -    def __init__( self ):
    175          object.__init__( self )
    176
    177 -    def __call__(self, decl):
    178          decl.name = 'operator ' + decl.return_type.decl_string
    179
    180  _casting_oper_patcher_ = casting_operator_patcher_t()
    181
    182 -def fix_calldef_decls(decls, enums):
    183      default_arg_patcher = default_argument_patcher_t(enums)
    184      #decls should be flat list of all declarations, you want to apply patch on
    185      for decl in decls:
    186          default_arg_patcher( decl )
    187          if isinstance( decl, declarations.casting_operator_t):
    188              _casting_oper_patcher_( decl )
    189