diff options
Diffstat (limited to 'tools/python-yasm/pyxelator/genpyx.py')
-rwxr-xr-x | tools/python-yasm/pyxelator/genpyx.py | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/tools/python-yasm/pyxelator/genpyx.py b/tools/python-yasm/pyxelator/genpyx.py new file mode 100755 index 0000000..3f2a4cc --- /dev/null +++ b/tools/python-yasm/pyxelator/genpyx.py @@ -0,0 +1,530 @@ +#!/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) ) ) + + |