531 lines
18 KiB
Python
Executable File
531 lines
18 KiB
Python
Executable File
#!/usr/bin/env python
|
|
""" genpyx.py - parse c declarations
|
|
|
|
(c) 2002, 2003, 2004, 2005 Simon Burton <simon@arrowtheory.com>
|
|
Released under GNU LGPL license.
|
|
|
|
version 0.xx
|
|
|
|
This is a module of mixin classes for ir.py .
|
|
|
|
Towards the end of ir.py our global class definitions
|
|
are remapped to point to the class definitions in ir.py .
|
|
So, for example, when we refer to Node we get ir.Node .
|
|
|
|
"""
|
|
|
|
import sys
|
|
from datetime import datetime
|
|
|
|
# XX use this Context class instead of all those kw dicts !! XX
|
|
class Context(object):
|
|
" just a record (struct) "
|
|
def __init__( self, **kw ):
|
|
for key, value in kw.items():
|
|
setattr( self, key, value )
|
|
def __getattr__( self, name ):
|
|
return None # ?
|
|
def __getitem__( self, name ):
|
|
return getattr(self, name)
|
|
|
|
class OStream(object):
|
|
def __init__( self, filename=None ):
|
|
self.filename = filename
|
|
self.tokens = []
|
|
self._indent = 0
|
|
def put( self, token="" ):
|
|
assert type(token) is str
|
|
self.tokens.append( token )
|
|
def startln( self, token="" ):
|
|
assert type(token) is str
|
|
self.tokens.append( ' '*self._indent + token )
|
|
def putln( self, ln="" ):
|
|
assert type(ln) is str
|
|
self.tokens.append( ' '*self._indent + ln + '\n')
|
|
def endln( self, token="" ):
|
|
assert type(token) is str
|
|
self.tokens.append( token + '\n')
|
|
def indent( self ):
|
|
self._indent += 1
|
|
def dedent( self ):
|
|
self._indent -= 1
|
|
assert self._indent >= 0, self._indent
|
|
def join( self ):
|
|
return ''.join( self.tokens )
|
|
def close( self ):
|
|
s = ''.join( self.tokens )
|
|
f = open( self.filename, 'w' )
|
|
f.write(s)
|
|
|
|
#
|
|
###############################################################################
|
|
#
|
|
|
|
class Node(object):
|
|
"""
|
|
tree structure
|
|
"""
|
|
_unique_id = 0
|
|
def get_unique_id(cls):
|
|
Node._unique_id += 1
|
|
return Node._unique_id
|
|
get_unique_id = classmethod(get_unique_id)
|
|
|
|
# XX toks: use a tree of tokens: a list that can be push'ed and pop'ed XX
|
|
def pyxstr(self,toks=None,indent=0,**kw):
|
|
"""
|
|
Build a list of tokens; return the joined tokens string
|
|
"""
|
|
if toks is None:
|
|
toks = []
|
|
for x in self:
|
|
if isinstance(x,Node):
|
|
x.pyxstr(toks, indent, **kw)
|
|
else:
|
|
toks.insert(0,str(x)+' ')
|
|
s = ''.join(toks)
|
|
return s
|
|
|
|
#
|
|
#################################################
|
|
|
|
class Named(object):
|
|
"has a .name property"
|
|
pass
|
|
|
|
class BasicType(object):
|
|
"float double void char int"
|
|
pass
|
|
|
|
class Qualifier(object):
|
|
"register signed unsigned short long const volatile inline"
|
|
def pyxstr(self,toks=None,indent=0,**kw):
|
|
if toks is None:
|
|
toks = []
|
|
x = self[0]
|
|
if x not in ( 'const','volatile','inline','register'): # ignore these
|
|
toks.insert(0,str(x)+' ')
|
|
s = ''.join(toks)
|
|
return s
|
|
|
|
class StorageClass(object):
|
|
"extern static auto"
|
|
def pyxstr(self,toks=None,indent=0,**kw):
|
|
return ""
|
|
|
|
class Ellipses(object):
|
|
"..."
|
|
pass
|
|
|
|
class GCCBuiltin(BasicType):
|
|
"things with __builtin prefix"
|
|
pass
|
|
|
|
class Identifier(object):
|
|
"""
|
|
"""
|
|
def pyxstr(self,toks=None,indent=0,**kw):
|
|
if toks is None:
|
|
toks=[]
|
|
if self.name:
|
|
toks.append( self.name )
|
|
return " ".join(toks)
|
|
|
|
class TypeAlias(object):
|
|
"""
|
|
typedefed things, eg. size_t
|
|
"""
|
|
def pyxstr(self,toks=None,indent=0,cprefix="",**kw):
|
|
if toks is None:
|
|
toks = []
|
|
for x in self:
|
|
if isinstance(x,Node):
|
|
x.pyxstr(toks, indent, cprefix=cprefix, **kw)
|
|
else:
|
|
s = str(x)+' '
|
|
if cprefix:
|
|
s = cprefix+s
|
|
toks.insert(0,s)
|
|
s = ''.join(toks)
|
|
return s
|
|
|
|
class Function(object):
|
|
"""
|
|
"""
|
|
def pyxstr(self,toks,indent=0,**kw):
|
|
#print '%s.pyxstr(%s)'%(self,toks)
|
|
_toks=[]
|
|
assert len(self)
|
|
i=0
|
|
while isinstance(self[i],Declarator):
|
|
if not self[i].is_void():
|
|
_toks.append( self[i].pyxstr(indent=indent, **kw) )
|
|
i=i+1
|
|
toks.append( '(%s)'% ', '.join(_toks) )
|
|
while i<len(self):
|
|
self[i].pyxstr(toks, indent=indent, **kw)
|
|
i=i+1
|
|
return " ".join(toks)
|
|
|
|
class Pointer(object):
|
|
"""
|
|
"""
|
|
def pyxstr(self,toks,indent=0,**kw):
|
|
assert len(self)
|
|
node=self[0]
|
|
toks.insert(0,'*')
|
|
if isinstance(node,Function):
|
|
toks.insert(0,'(')
|
|
toks.append(')')
|
|
elif isinstance(node,Array):
|
|
toks.insert(0,'(')
|
|
toks.append(')')
|
|
return Node.pyxstr(self,toks,indent, **kw)
|
|
|
|
class Array(object):
|
|
"""
|
|
"""
|
|
def pyxstr(self,toks,indent=0,**kw):
|
|
if self.size is None:
|
|
toks.append('[]')
|
|
else:
|
|
try:
|
|
int(self.size)
|
|
toks.append('[%s]'%self.size)
|
|
except:
|
|
toks.append('[]')
|
|
return Node( *self[:-1] ).pyxstr( toks,indent, **kw )
|
|
|
|
class Tag(object):
|
|
" the tag of a Struct, Union or Enum "
|
|
pass
|
|
|
|
class Taged(object):
|
|
"Struct, Union or Enum "
|
|
pass
|
|
|
|
class Compound(Taged):
|
|
"Struct or Union"
|
|
def pyxstr(self,_toks=None,indent=0,cprefix="",shadow_name=True,**kw):
|
|
if _toks is None:
|
|
_toks=[]
|
|
names = kw.get('names',{})
|
|
kw['names'] = names
|
|
tag_lookup = kw.get('tag_lookup')
|
|
if self.tag:
|
|
tag=self.tag.name
|
|
else:
|
|
tag = ''
|
|
if isinstance(self,Struct):
|
|
descr = 'struct'
|
|
elif isinstance(self,Union):
|
|
descr = 'union'
|
|
_node = names.get(self.tag.name,None)
|
|
if ( _node is not None and _node.has_members() ) or \
|
|
( _node is not None and not self.has_members() ):
|
|
descr = '' # i am not defining myself here
|
|
#print "Compound.pyxstr", tag
|
|
#print self.deepstr()
|
|
if descr:
|
|
if cprefix and shadow_name:
|
|
tag = '%s%s "%s"'%(cprefix,tag,tag)
|
|
elif cprefix:
|
|
tag = cprefix+tag
|
|
toks = [ descr+' '+tag ] # struct foo
|
|
if self.has_members():
|
|
toks.append(':\n')
|
|
for decl in self[1:]: # XX self.members
|
|
toks.append( decl.pyxstr(indent=indent+1, cprefix=cprefix, shadow_name=shadow_name, **kw)+"\n" ) # shadow_name = False ?
|
|
#elif not tag_lookup.get( self.tag.name, self ).has_members():
|
|
# define empty struct here, it's the best we're gonna get
|
|
#pass
|
|
else:
|
|
if cprefix: # and shadow_name:
|
|
tag = cprefix+tag
|
|
toks = [ ' '+tag+' ' ] # foo
|
|
while toks:
|
|
_toks.insert( 0, toks.pop() )
|
|
return "".join( _toks )
|
|
|
|
class Struct(Compound):
|
|
"""
|
|
"""
|
|
pass
|
|
|
|
class Union(Compound):
|
|
"""
|
|
"""
|
|
pass
|
|
|
|
|
|
class Enum(Taged):
|
|
"""
|
|
"""
|
|
def pyxstr(self,_toks=None,indent=0,cprefix="",shadow_name=True,**kw):
|
|
if _toks is None:
|
|
_toks=[]
|
|
names = kw.get('names',{})
|
|
kw['names'] = names
|
|
if self.tag:
|
|
tag=self.tag.name
|
|
else:
|
|
tag = ''
|
|
_node = names.get(self.tag.name,None)
|
|
if ( _node is not None and _node.has_members() ) or \
|
|
( _node is not None and not self.has_members() ):
|
|
descr = '' # i am not defining myself here
|
|
else:
|
|
descr = 'enum'
|
|
if descr:
|
|
#if not names.has_key(self.tag.name):
|
|
toks = [ descr+' '+tag ] # enum foo
|
|
toks.append(':\n')
|
|
idents = [ ident for ident in self.members if ident.name not in names ]
|
|
for ident in idents:
|
|
if cprefix and shadow_name:
|
|
ident = ident.clone()
|
|
ident.name = '%s%s "%s"' % ( cprefix, ident.name, ident.name )
|
|
#else: assert 0
|
|
toks.append( ' '+' '*indent + ident.pyxstr(**kw)+"\n" )
|
|
names[ ident.name ] = ident
|
|
if not idents:
|
|
# empty enum def'n !
|
|
#assert 0 # should be handled by parents...
|
|
toks.append( ' '+' '*indent + "pass\n" )
|
|
else:
|
|
toks = [ ' '+tag+' ' ] # foo
|
|
while toks:
|
|
_toks.insert( 0, toks.pop() )
|
|
return "".join( _toks )
|
|
|
|
class Declarator(object):
|
|
def is_pyxnative( self ):
|
|
# pyrex handles char* too
|
|
# but i don't know if we should make this the default
|
|
# sometimes we want to send a NULL, so ... XX
|
|
self = self.cbasetype() # WARNING: cbasetype may be cached
|
|
if self.is_void():
|
|
return False
|
|
if self.is_primative():
|
|
return True
|
|
if self.enum:
|
|
return True
|
|
#pointer = None
|
|
#if self.pointer:
|
|
#pointer = self.pointer
|
|
#elif self.array:
|
|
#pointer = self.array
|
|
#if pointer and pointer.spec:
|
|
#spec = pointer.spec
|
|
#if BasicType("char") in spec and not Qualifier("unsigned") in spec:
|
|
# char*, const char*
|
|
##print self.deepstr()
|
|
#return True
|
|
return False
|
|
|
|
def _pyxstr( self, toks, indent, cprefix, use_cdef, shadow_name, **kw ):
|
|
" this is the common part of pyxstr that gets called from both Declarator and Typedef "
|
|
names = kw.get('names',{}) # what names have been defined ?
|
|
kw['names']=names
|
|
for node in self.nodes(): # depth-first
|
|
if isinstance(node,Taged):
|
|
#print "Declarator.pyxstr", node.cstr()
|
|
if not node.tag.name:
|
|
node.tag.name = "_anon_%s" % Node.get_unique_id()
|
|
_node = names.get(node.tag.name,None)
|
|
#tag_lookup = kw.get('tag_lookup')
|
|
#other = tag_lookup.get(node.tag.name, node)
|
|
#if ((_node is None and (not isinstance(other,Compound) or not other.has_members()))
|
|
# or node.has_members()):
|
|
if _node is None or node.has_members():
|
|
# either i am not defined at all, or this is my _real_ definition
|
|
# emit def'n of this node
|
|
#if isinstance(self,Typedef):
|
|
#toks.append( ' '*indent + 'ctypedef ' + node.pyxstr(indent=indent, cprefix=cprefix, shadow_name=shadow_name, **kw).strip() )
|
|
#else:
|
|
toks.append( ' '*indent + 'cdef ' + node.pyxstr(indent=indent, cprefix=cprefix, shadow_name=shadow_name, **kw).strip() )
|
|
names[ node.tag.name ] = node
|
|
elif isinstance(node,GCCBuiltin) and node[0] not in names:
|
|
#toks.append( ' '*indent + 'ctypedef long ' + node.pyxstr(indent=indent, **kw).strip() + ' # XX ??' ) # XX ??
|
|
toks.append( ' '*indent + 'struct __unknown_builtin ' )
|
|
toks.append( ' '*indent + 'ctypedef __unknown_builtin ' + node.pyxstr(indent=indent, **kw).strip() )
|
|
names[ node[0] ] = node
|
|
for idx, child in enumerate(node):
|
|
if type(child)==Array and not child.has_size():
|
|
# mutate this mystery array into a pointer XX method: Array.to_pointer()
|
|
node[idx] = Pointer()
|
|
node[idx].init_from( child ) # warning: shallow init
|
|
node[idx].pop() # pop the size element
|
|
|
|
def pyxstr(self,toks=None,indent=0,cprefix="",use_cdef=True,shadow_name=True,**kw):
|
|
" note: i do not check if my name is already in 'names' "
|
|
self = self.clone() # <----- NOTE
|
|
toks=[]
|
|
names = kw.get('names',{}) # what names have been defined ?
|
|
kw['names']=names
|
|
|
|
self._pyxstr( toks, indent, cprefix, use_cdef, shadow_name, **kw )
|
|
|
|
if self.name and not names.has_key( self.name ):
|
|
names[ self.name ] = self
|
|
if self.identifier is not None:
|
|
comment = ""
|
|
if self.name in python_kws:
|
|
comment = "#"
|
|
if cprefix and use_cdef and shadow_name:
|
|
# When we are defining this guy, we refer to it using the pyrex shadow syntax.
|
|
self.name = '%s%s "%s" ' % ( cprefix, self.name, self.name )
|
|
cdef = 'cdef '
|
|
if not use_cdef: cdef = '' # sometimes we don't want the cdef (eg. in a cast)
|
|
# this may need shadow_name=False:
|
|
toks.append( ' '*indent + comment + cdef + Node.pyxstr(self,indent=indent, cprefix=cprefix, **kw).strip() ) # + "(cprefix=%s)"%cprefix)
|
|
#else: i am just a struct def (so i already did that) # huh ?? XX bad comment
|
|
return ' \n'.join(toks)
|
|
|
|
def pyxsym(self, ostream, names=None, tag_lookup=None, cprefix="", modname=None, cobjects=None):
|
|
assert self.name is not None, self.deepstr()
|
|
ostream.putln( '# ' + self.cstr() )
|
|
# This cdef is no good: it does not expose a python object
|
|
# and we can't reliably set a global var
|
|
#ostream.putln( 'cdef %s %s' % ( self.pyx_adaptor_decl(cobjects), self.name ) ) # _CObject
|
|
#ostream.putln( '%s = %s()' % (self.name, self.pyx_adaptor_name(cobjects)) )
|
|
#ostream.putln( '%s.p = <void*>&%s' % (self.name, cprefix+self.name) )
|
|
## expose a python object:
|
|
#ostream.putln( '%s.%s = %s' % (modname,self.name, self.name) )
|
|
ostream.putln( '%s = %s( addr = <long>&%s )' % (self.name, self.pyx_adaptor_name(cobjects), cprefix+self.name) )
|
|
return ostream
|
|
|
|
|
|
class Typedef(Declarator):
|
|
def pyxstr(self,toks=None,indent=0,cprefix="",use_cdef=True,shadow_name=True,**kw): # shadow_name=True
|
|
" warning: i do not check if my name is already in 'names' "
|
|
assert shadow_name == True
|
|
self = self.clone() # <----- NOTE
|
|
toks=[]
|
|
names = kw.get('names',{}) # what names have been defined ?
|
|
kw['names']=names
|
|
|
|
#if self.tagged and not self.tagged.tag.name:
|
|
## "typedef struct {...} foo;" => "typedef struct foo {...} foo;"
|
|
## (to be emitted in the node loop below, and suppressed in the final toks.append)
|
|
#self.tagged.tag = Tag( self.name ) # this is how pyrex does it: tag.name == self.name
|
|
# XX that doesn't work (the resulting c fails to compile) XX
|
|
|
|
self._pyxstr( toks, indent, cprefix, use_cdef, shadow_name, **kw )
|
|
|
|
#print self.deepstr()
|
|
if self.name and not names.has_key( self.name ):
|
|
names[ self.name ] = self
|
|
if not (self.tagged and self.name == self.tagged.tag.name):
|
|
comment = ""
|
|
if self.name in python_kws:
|
|
comment = "#"
|
|
#if cprefix:
|
|
# self.name = '%s%s "%s" ' % ( cprefix, self.name, self.name ) # XX pyrex can't do this
|
|
if cprefix: # shadow_name=True
|
|
# My c-name gets this prefix. See also TypeAlias.pyxstr(): it also prepends the cprefix.
|
|
self.name = '%s%s "%s" ' % ( cprefix, self.name, self.name )
|
|
toks.append( ' '*indent + comment + 'ctypedef ' + Node.pyxstr(self,indent=indent, cprefix=cprefix, **kw).strip() )
|
|
return ' \n'.join(toks)
|
|
|
|
|
|
class AbstractDeclarator(Declarator):
|
|
""" used in Function; may lack an identifier """
|
|
def pyxstr(self,toks=None,indent=0,**kw):
|
|
if self.name in python_kws:
|
|
# Would be better to do this in __init__, but our subclass doesn't call our __init__.
|
|
self.name = '_' + self.name
|
|
#return ' '*indent + Node.pyxstr(self,toks,indent, **kw).strip()
|
|
return Node.pyxstr(self,toks,indent, **kw).strip()
|
|
|
|
|
|
class FieldLength(object):
|
|
"""
|
|
"""
|
|
def pyxstr(self,toks,indent,**kw):
|
|
pass
|
|
|
|
|
|
class StructDeclarator(Declarator): # also used in Union
|
|
"""
|
|
"""
|
|
def pyxstr(self,toks=None,indent=0,**kw):
|
|
comment = ""
|
|
if self.name in python_kws:
|
|
comment = "#"
|
|
return ' '*indent + comment + Node.pyxstr(self,toks,indent, **kw).strip()
|
|
|
|
class DeclarationSpecifiers(object):
|
|
"""
|
|
"""
|
|
pass
|
|
|
|
class TypeSpecifiers(DeclarationSpecifiers):
|
|
"""
|
|
"""
|
|
pass
|
|
|
|
class Initializer(object):
|
|
"""
|
|
"""
|
|
pass
|
|
|
|
class Declaration(object):
|
|
"""
|
|
"""
|
|
pass
|
|
|
|
class ParameterDeclaration(Declaration):
|
|
"""
|
|
"""
|
|
pass
|
|
|
|
class StructDeclaration(Declaration):
|
|
"""
|
|
"""
|
|
pass
|
|
|
|
class TransUnit(object):
|
|
"""
|
|
Top level node.
|
|
"""
|
|
def pyx_decls(self, filenames, modname, macros = {}, names = {}, func_cb=None, cprefix="", **kw):
|
|
# PART 1: emit extern declarations
|
|
ostream = OStream()
|
|
now = datetime.today()
|
|
ostream.putln( now.strftime('# Code generated by pyxelator on %x at %X') + '\n' )
|
|
ostream.putln("# PART 1: extern declarations")
|
|
for filename in filenames:
|
|
ostream.putln( 'cdef extern from "%s":\n pass\n' % filename )
|
|
ostream.putln( 'cdef extern from *:' )
|
|
file = None # current file
|
|
for node in self:
|
|
ostream.putln('')
|
|
ostream.putln(' # ' + node.cstr() )
|
|
assert node.marked
|
|
comment = False
|
|
if node.name and node.name in names:
|
|
comment = True # redeclaration
|
|
#ostream.putln( node.deepstr( comment=True ) )
|
|
s = node.pyxstr(indent=1, names=names, tag_lookup = self.tag_lookup, cprefix=cprefix, **kw)
|
|
if s.split():
|
|
if comment:
|
|
s = "#"+s.replace( '\n', '\n#' ) + " # redeclaration "
|
|
if node.file != file:
|
|
file = node.file
|
|
#ostream.putln( 'cdef extern from "%s":' % file )
|
|
ostream.putln( ' # "%s"' % file )
|
|
ostream.putln( s )
|
|
ostream.putln('\n')
|
|
#s = '\n'.join(toks)
|
|
return ostream.join()
|
|
|
|
# XX warn when we find a python keyword XX
|
|
python_kws = """
|
|
break continue del def except exec finally pass print raise
|
|
return try global assert lambda yield
|
|
for while if elif else and in is not or import from """.split()
|
|
python_kws = dict( zip( python_kws, (None,)*len(python_kws) ) )
|
|
|
|
|