trAvis - MANAGER
Edit File: genpyx.py
#!/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) ) )