Home | History | Annotate | Download | only in pyxelator
      1 #!/usr/bin/env python
      2 """ genpyx.py - parse c declarations
      3 
      4 (c) 2002, 2003, 2004, 2005 Simon Burton <simon (at] arrowtheory.com>
      5 Released under GNU LGPL license.
      6 
      7 version 0.xx
      8 
      9 This is a module of mixin classes for ir.py .
     10 
     11 Towards the end of ir.py our global class definitions
     12 are remapped to point to the class definitions in ir.py .
     13 So, for example, when we refer to Node we get ir.Node .
     14 
     15 """
     16 
     17 import sys
     18 from datetime import datetime
     19 
     20 # XX use this Context class instead of all those kw dicts !! XX
     21 class Context(object):
     22     " just a record (struct) "
     23     def __init__( self, **kw ):
     24         for key, value in kw.items():
     25             setattr( self, key, value )
     26     def __getattr__( self, name ):
     27         return None # ?
     28     def __getitem__( self, name ):
     29         return getattr(self, name)
     30 
     31 class OStream(object):
     32     def __init__( self, filename=None ):
     33         self.filename = filename
     34         self.tokens = []
     35         self._indent = 0
     36     def put( self, token="" ):
     37         assert type(token) is str
     38         self.tokens.append( token )
     39     def startln( self, token="" ):
     40         assert type(token) is str
     41         self.tokens.append( '    '*self._indent + token )
     42     def putln( self, ln="" ):
     43         assert type(ln) is str
     44         self.tokens.append( '    '*self._indent + ln + '\n')
     45     def endln( self, token="" ):
     46         assert type(token) is str
     47         self.tokens.append( token + '\n')
     48     def indent( self ):
     49         self._indent += 1
     50     def dedent( self ):
     51         self._indent -= 1
     52         assert self._indent >= 0, self._indent
     53     def join( self ):
     54         return ''.join( self.tokens )
     55     def close( self ):
     56         s = ''.join( self.tokens )
     57         f = open( self.filename, 'w' )
     58         f.write(s)
     59 
     60 #
     61 ###############################################################################
     62 #
     63 
     64 class Node(object):
     65     """
     66         tree structure
     67     """
     68     _unique_id = 0
     69     def get_unique_id(cls):
     70         Node._unique_id += 1
     71         return Node._unique_id
     72     get_unique_id = classmethod(get_unique_id)
     73 
     74 # XX toks: use a tree of tokens: a list that can be push'ed and pop'ed XX
     75     def pyxstr(self,toks=None,indent=0,**kw):
     76         """
     77             Build a list of tokens; return the joined tokens string
     78         """
     79         if toks is None:
     80             toks = []
     81         for x in self:
     82             if isinstance(x,Node):
     83                 x.pyxstr(toks, indent, **kw)
     84             else:
     85                 toks.insert(0,str(x)+' ')
     86         s = ''.join(toks)
     87         return s
     88 
     89 #
     90 #################################################
     91 
     92 class Named(object):
     93     "has a .name property"
     94     pass
     95 
     96 class BasicType(object):
     97     "float double void char int"
     98     pass
     99 
    100 class Qualifier(object):
    101     "register signed unsigned short long const volatile inline"
    102     def pyxstr(self,toks=None,indent=0,**kw):
    103         if toks is None:
    104             toks = []
    105         x = self[0]
    106         if x not in ( 'const','volatile','inline','register'): # ignore these
    107             toks.insert(0,str(x)+' ')
    108         s = ''.join(toks)
    109         return s
    110 
    111 class StorageClass(object):
    112     "extern static auto"
    113     def pyxstr(self,toks=None,indent=0,**kw):
    114         return ""
    115 
    116 class Ellipses(object):
    117     "..."
    118     pass
    119 
    120 class GCCBuiltin(BasicType):
    121     "things with __builtin prefix"
    122     pass
    123 
    124 class Identifier(object):
    125     """
    126     """
    127     def pyxstr(self,toks=None,indent=0,**kw):
    128         if toks is None:
    129             toks=[]
    130         if self.name:
    131             toks.append( self.name )
    132         return " ".join(toks)
    133 
    134 class TypeAlias(object):
    135     """
    136      typedefed things, eg. size_t 
    137     """
    138     def pyxstr(self,toks=None,indent=0,cprefix="",**kw):
    139         if toks is None:
    140             toks = []
    141         for x in self:
    142             if isinstance(x,Node):
    143                 x.pyxstr(toks, indent, cprefix=cprefix, **kw)
    144             else:
    145                 s = str(x)+' '
    146                 if cprefix:
    147                     s = cprefix+s
    148                 toks.insert(0,s)
    149         s = ''.join(toks)
    150         return s
    151 
    152 class Function(object):
    153     """
    154     """
    155     def pyxstr(self,toks,indent=0,**kw):
    156         #print '%s.pyxstr(%s)'%(self,toks)
    157         _toks=[]
    158         assert len(self)
    159         i=0
    160         while isinstance(self[i],Declarator):
    161             if not self[i].is_void():
    162                 _toks.append( self[i].pyxstr(indent=indent, **kw) )
    163             i=i+1
    164         toks.append( '(%s)'% ', '.join(_toks) )
    165         while i<len(self):
    166             self[i].pyxstr(toks, indent=indent, **kw)
    167             i=i+1
    168         return " ".join(toks)
    169 
    170 class Pointer(object):
    171     """
    172     """
    173     def pyxstr(self,toks,indent=0,**kw):
    174         assert len(self)
    175         node=self[0]
    176         toks.insert(0,'*')
    177         if isinstance(node,Function):
    178             toks.insert(0,'(')
    179             toks.append(')')
    180         elif isinstance(node,Array):
    181             toks.insert(0,'(')
    182             toks.append(')')
    183         return Node.pyxstr(self,toks,indent, **kw)
    184 
    185 class Array(object):
    186     """
    187     """
    188     def pyxstr(self,toks,indent=0,**kw):
    189         if self.size is None:
    190             toks.append('[]')
    191         else:
    192             try:
    193                 int(self.size)
    194                 toks.append('[%s]'%self.size)
    195             except:
    196                 toks.append('[]')
    197         return Node( *self[:-1] ).pyxstr( toks,indent, **kw )
    198 
    199 class Tag(object):
    200     " the tag of a Struct, Union or Enum "
    201     pass
    202 
    203 class Taged(object):
    204     "Struct, Union or Enum "
    205     pass
    206 
    207 class Compound(Taged):
    208     "Struct or Union"
    209     def pyxstr(self,_toks=None,indent=0,cprefix="",shadow_name=True,**kw):
    210         if _toks is None:
    211             _toks=[]
    212         names = kw.get('names',{})
    213         kw['names'] = names
    214         tag_lookup = kw.get('tag_lookup')
    215         if self.tag:
    216             tag=self.tag.name
    217         else:
    218             tag = ''
    219         if isinstance(self,Struct):
    220             descr = 'struct'
    221         elif isinstance(self,Union):
    222             descr = 'union'
    223         _node = names.get(self.tag.name,None)
    224         if ( _node is not None and _node.has_members() ) or \
    225                 ( _node is not None and not self.has_members() ):
    226             descr = '' # i am not defining myself here
    227         #print "Compound.pyxstr", tag
    228         #print self.deepstr()
    229         if descr:
    230             if cprefix and shadow_name:
    231                 tag = '%s%s "%s"'%(cprefix,tag,tag)
    232             elif cprefix:
    233                 tag = cprefix+tag
    234             toks = [ descr+' '+tag ] # struct foo
    235             if self.has_members():
    236                 toks.append(':\n')
    237                 for decl in self[1:]: # XX self.members
    238                     toks.append( decl.pyxstr(indent=indent+1, cprefix=cprefix, shadow_name=shadow_name, **kw)+"\n" ) # shadow_name = False ?
    239             #elif not tag_lookup.get( self.tag.name, self ).has_members():
    240                 # define empty struct here, it's the best we're gonna get
    241                 #pass
    242         else:
    243             if cprefix: # and shadow_name:
    244                 tag = cprefix+tag
    245             toks = [ ' '+tag+' ' ] # foo
    246         while toks:
    247             _toks.insert( 0, toks.pop() )
    248         return "".join( _toks )
    249 
    250 class Struct(Compound):
    251     """
    252     """
    253     pass
    254 
    255 class Union(Compound):
    256     """
    257     """
    258     pass
    259 
    260 
    261 class Enum(Taged):
    262     """
    263     """
    264     def pyxstr(self,_toks=None,indent=0,cprefix="",shadow_name=True,**kw):
    265         if _toks is None:
    266             _toks=[]
    267         names = kw.get('names',{})
    268         kw['names'] = names
    269         if self.tag:
    270             tag=self.tag.name
    271         else:
    272             tag = ''
    273         _node = names.get(self.tag.name,None)
    274         if ( _node is not None and _node.has_members() ) or \
    275                 ( _node is not None and not self.has_members() ):
    276             descr = '' # i am not defining myself here
    277         else:
    278             descr = 'enum'
    279         if descr:
    280         #if not names.has_key(self.tag.name):
    281             toks = [ descr+' '+tag ] # enum foo
    282             toks.append(':\n')
    283             idents = [ ident for ident in self.members if ident.name not in names ]
    284             for ident in idents:
    285                 if cprefix and shadow_name:
    286                     ident = ident.clone()
    287                     ident.name = '%s%s "%s"' % ( cprefix, ident.name, ident.name )
    288                 #else: assert 0
    289                 toks.append( '    '+'    '*indent + ident.pyxstr(**kw)+"\n" )
    290                 names[ ident.name ] = ident
    291             if not idents:
    292                 # empty enum def'n !
    293                 #assert 0 # should be handled by parents...
    294                 toks.append( '    '+'    '*indent + "pass\n" )
    295         else:
    296             toks = [ ' '+tag+' ' ] # foo
    297         while toks:
    298             _toks.insert( 0, toks.pop() )
    299         return "".join( _toks )
    300 
    301 class Declarator(object):
    302     def is_pyxnative( self ):
    303         # pyrex handles char* too
    304         # but i don't know if we should make this the default
    305         # sometimes we want to send a NULL, so ... XX
    306         self = self.cbasetype() # WARNING: cbasetype may be cached
    307         if self.is_void():
    308             return False
    309         if self.is_primative():
    310             return True
    311         if self.enum:
    312             return True
    313         #pointer = None
    314         #if self.pointer:
    315             #pointer = self.pointer
    316         #elif self.array:
    317             #pointer = self.array
    318         #if pointer and pointer.spec:
    319             #spec = pointer.spec
    320             #if BasicType("char") in spec and not Qualifier("unsigned") in spec:
    321                 # char*, const char*
    322                 ##print self.deepstr()
    323                 #return True
    324         return False
    325 
    326     def _pyxstr( self, toks, indent, cprefix, use_cdef, shadow_name, **kw ):
    327         " this is the common part of pyxstr that gets called from both Declarator and Typedef "
    328         names = kw.get('names',{}) # what names have been defined ?
    329         kw['names']=names
    330         for node in self.nodes(): # depth-first
    331             if isinstance(node,Taged):
    332                 #print "Declarator.pyxstr", node.cstr()
    333                 if not node.tag.name:
    334                     node.tag.name = "_anon_%s" % Node.get_unique_id()
    335                 _node = names.get(node.tag.name,None)
    336                 #tag_lookup = kw.get('tag_lookup')
    337                 #other = tag_lookup.get(node.tag.name, node)
    338                 #if ((_node is None and (not isinstance(other,Compound) or not other.has_members()))
    339                 #    or node.has_members()):
    340                 if _node is None or node.has_members():
    341                     # either i am not defined at all, or this is my _real_ definition
    342                     # emit def'n of this node
    343                     #if isinstance(self,Typedef):
    344                         #toks.append( '    '*indent + 'ctypedef ' + node.pyxstr(indent=indent, cprefix=cprefix, shadow_name=shadow_name, **kw).strip() )
    345                     #else:
    346                     toks.append( '    '*indent + 'cdef ' + node.pyxstr(indent=indent, cprefix=cprefix, shadow_name=shadow_name, **kw).strip() )
    347                     names[ node.tag.name ] = node
    348             elif isinstance(node,GCCBuiltin) and node[0] not in names:
    349                 #toks.append( '    '*indent + 'ctypedef long ' + node.pyxstr(indent=indent, **kw).strip() + ' # XX ??'    ) # XX ??
    350                 toks.append( '    '*indent + 'struct __unknown_builtin ' )
    351                 toks.append( '    '*indent + 'ctypedef __unknown_builtin ' + node.pyxstr(indent=indent, **kw).strip() )
    352                 names[ node[0] ] = node
    353             for idx, child in enumerate(node):
    354                 if type(child)==Array and not child.has_size():
    355                     # mutate this mystery array into a pointer XX method: Array.to_pointer()
    356                     node[idx] = Pointer()
    357                     node[idx].init_from( child ) # warning: shallow init
    358                     node[idx].pop() # pop the size element
    359 
    360     def pyxstr(self,toks=None,indent=0,cprefix="",use_cdef=True,shadow_name=True,**kw):
    361         " note: i do not check if my name is already in 'names' "
    362         self = self.clone() # <----- NOTE
    363         toks=[]
    364         names = kw.get('names',{}) # what names have been defined ?
    365         kw['names']=names
    366 
    367         self._pyxstr( toks, indent, cprefix, use_cdef, shadow_name, **kw )
    368 
    369         if self.name and not names.has_key( self.name ):
    370             names[ self.name ] = self
    371         if self.identifier is not None:
    372             comment = ""
    373             if self.name in python_kws:
    374                 comment = "#"
    375             if cprefix and use_cdef and shadow_name:
    376                 # When we are defining this guy, we refer to it using the pyrex shadow syntax.
    377                 self.name = '%s%s "%s" ' % ( cprefix, self.name, self.name )
    378             cdef = 'cdef '
    379             if not use_cdef: cdef = '' # sometimes we don't want the cdef (eg. in a cast)
    380             # this may need shadow_name=False:
    381             toks.append( '    '*indent + comment + cdef + Node.pyxstr(self,indent=indent, cprefix=cprefix, **kw).strip() ) # + "(cprefix=%s)"%cprefix)
    382         #else: i am just a struct def (so i already did that) # huh ?? XX bad comment
    383         return ' \n'.join(toks)
    384 
    385     def pyxsym(self, ostream, names=None, tag_lookup=None, cprefix="", modname=None, cobjects=None):
    386         assert self.name is not None, self.deepstr()
    387         ostream.putln( '# ' + self.cstr() )
    388 # This cdef is no good: it does not expose a python object
    389 # and we can't reliably set a global var
    390         #ostream.putln( 'cdef %s %s' % ( self.pyx_adaptor_decl(cobjects), self.name ) ) # _CObject
    391         #ostream.putln( '%s = %s()' % (self.name, self.pyx_adaptor_name(cobjects)) )
    392         #ostream.putln( '%s.p = <void*>&%s' % (self.name, cprefix+self.name) )
    393         ## expose a python object:
    394         #ostream.putln( '%s.%s = %s' % (modname,self.name, self.name) )
    395         ostream.putln( '%s = %s( addr = <long>&%s )' % (self.name, self.pyx_adaptor_name(cobjects), cprefix+self.name) )
    396         return ostream
    397 
    398 
    399 class Typedef(Declarator):
    400     def pyxstr(self,toks=None,indent=0,cprefix="",use_cdef=True,shadow_name=True,**kw): # shadow_name=True
    401         " warning: i do not check if my name is already in 'names' "
    402         assert shadow_name == True
    403         self = self.clone() # <----- NOTE
    404         toks=[]
    405         names = kw.get('names',{}) # what names have been defined ?
    406         kw['names']=names
    407 
    408         #if self.tagged and not self.tagged.tag.name:
    409             ## "typedef struct {...} foo;" => "typedef struct foo {...} foo;"
    410             ## (to be emitted in the node loop below, and suppressed in the final toks.append)
    411             #self.tagged.tag = Tag( self.name ) # this is how pyrex does it: tag.name == self.name
    412         # XX that doesn't work (the resulting c fails to compile) XX
    413 
    414         self._pyxstr( toks, indent, cprefix, use_cdef, shadow_name, **kw )
    415 
    416         #print self.deepstr()
    417         if self.name and not names.has_key( self.name ):
    418             names[ self.name ] = self
    419         if not (self.tagged and self.name == self.tagged.tag.name):
    420             comment = ""
    421             if self.name in python_kws:
    422                 comment = "#"
    423                 #if cprefix:
    424                 #  self.name = '%s%s "%s" ' % ( cprefix, self.name, self.name ) # XX pyrex can't do this
    425             if cprefix: # shadow_name=True
    426                 # My c-name gets this prefix. See also TypeAlias.pyxstr(): it also prepends the cprefix.
    427                 self.name = '%s%s "%s" ' % ( cprefix, self.name, self.name )
    428             toks.append( '    '*indent + comment + 'ctypedef ' + Node.pyxstr(self,indent=indent, cprefix=cprefix, **kw).strip() )
    429         return ' \n'.join(toks)
    430 
    431 
    432 class AbstractDeclarator(Declarator):
    433     """ used in Function; may lack an identifier """
    434     def pyxstr(self,toks=None,indent=0,**kw):
    435         if self.name in python_kws:
    436             # Would be better to do this in __init__, but our subclass doesn't call our __init__.
    437             self.name = '_' + self.name
    438         #return '    '*indent + Node.pyxstr(self,toks,indent, **kw).strip()
    439         return Node.pyxstr(self,toks,indent, **kw).strip()
    440 
    441 
    442 class FieldLength(object):
    443     """
    444     """
    445     def pyxstr(self,toks,indent,**kw):
    446         pass
    447 
    448 
    449 class StructDeclarator(Declarator): # also used in Union
    450     """
    451     """
    452     def pyxstr(self,toks=None,indent=0,**kw):
    453         comment = ""
    454         if self.name in python_kws:
    455             comment = "#"
    456         return '    '*indent + comment + Node.pyxstr(self,toks,indent, **kw).strip()
    457 
    458 class DeclarationSpecifiers(object):
    459     """
    460     """
    461     pass
    462 
    463 class TypeSpecifiers(DeclarationSpecifiers):
    464     """
    465     """
    466     pass
    467 
    468 class Initializer(object):
    469     """
    470     """
    471     pass
    472 
    473 class Declaration(object):
    474     """
    475     """
    476     pass
    477 
    478 class ParameterDeclaration(Declaration):
    479     """
    480     """
    481     pass
    482 
    483 class StructDeclaration(Declaration):
    484     """
    485     """
    486     pass
    487 
    488 class TransUnit(object):
    489     """
    490         Top level node.
    491     """
    492     def pyx_decls(self, filenames, modname, macros = {}, names = {}, func_cb=None, cprefix="", **kw):
    493         # PART 1: emit extern declarations
    494         ostream = OStream()
    495         now = datetime.today()
    496         ostream.putln( now.strftime('# Code generated by pyxelator on %x at %X') + '\n' )
    497         ostream.putln("# PART 1: extern declarations")
    498         for filename in filenames:
    499             ostream.putln( 'cdef extern from "%s":\n    pass\n' % filename )
    500         ostream.putln( 'cdef extern from *:' )
    501         file = None # current file
    502         for node in self:
    503             ostream.putln('')
    504             ostream.putln('    # ' + node.cstr() )
    505             assert node.marked
    506             comment = False
    507             if node.name and node.name in names:
    508                 comment = True # redeclaration
    509             #ostream.putln( node.deepstr( comment=True ) )
    510             s = node.pyxstr(indent=1, names=names, tag_lookup = self.tag_lookup, cprefix=cprefix, **kw)
    511             if s.split():
    512                 if comment:
    513                     s = "#"+s.replace( '\n', '\n#' ) + " # redeclaration "
    514                 if node.file != file:
    515                     file = node.file
    516                     #ostream.putln( 'cdef extern from "%s":' % file )
    517                     ostream.putln( '    # "%s"' % file )
    518                 ostream.putln( s )
    519         ostream.putln('\n')
    520         #s = '\n'.join(toks)
    521         return ostream.join()
    522 
    523 # XX warn when we find a python keyword XX
    524 python_kws = """
    525 break continue del def except exec finally pass print raise
    526 return try global assert lambda yield
    527 for while if elif else and in is not or import from """.split()
    528 python_kws = dict( zip( python_kws, (None,)*len(python_kws) ) )
    529 
    530 
    531