#!/usr/bin/env python """ ir.py - parse c declarations (c) 2002, 2003, 2004, 2005 Simon Burton Released under GNU LGPL license. version 0.xx """ import sys #import cPickle as pickle import pickle #from lexer import Lexer from parse_core import Symbols #, Parser import node as node_module import cparse import genpyx class Node(genpyx.Node, node_module.Node): """ tree structure """ def __init__( self, *args, **kw ): node_module.Node.__init__( self, *args, **kw ) self._marked = False def get_marked( self ): return self._marked def set_marked( self, marked ): # if marked: # print "MARK", self self._marked = marked marked = property( get_marked, set_marked ) # def __getstate__( self ): # return self.__class__, tuple( [ item.__getstate__() for item in self ] ) # def __setstate__( self, state ): # cls, states = state # states = list(states) # for idx, state in enumerate(states): # items[idx] = items[idx].__setstate__( def __getstate__(self): return str(self) def __setstate__(self, state): Node.__init__(self) self[:] = eval(state) # _unique_id = 0 # def get_unique_id(cls): # Node._unique_id += 1 # return Node._unique_id # get_unique_id = classmethod(get_unique_id) def __hash__( self ): return hash( tuple([hash(type(self))]+[hash(item) for item in self]) ) def clone(self): l = [] for item in self: if isinstance(item,Node): item = item.clone() l.append(item) return self.__class__(*l, **self.__dict__) def init_from( self, other ): # class method ? # Warning: shallow init self[:] = other self.__dict__.update( other.__dict__ ) return self # def is_struct(self): # for x in self: # if isinstance(x,Node): # if x.is_struct(): # return 1 # return 0 #def explain(self): #l = [] #for x in self: #if isinstance(x,Node): #l.append(x.explain()) #else: #l.append(str(x)) #return string.join(l," ") ##(self.__class__.__name__,string.join(l) ) def psource(self): if hasattr(self,'lines'): # print "# "+string.join(self.lines,"\n# ")+"\n" print "# "+"\n# ".join(self.lines)+"\n" def cstr(self,l=None): """ Build a list of tokens; return the joined tokens string """ if l is None: l = [] for x in self: if isinstance(x,Node): x.cstr(l) else: l.insert(0,str(x)+' ') s = ''.join(l) return s def ctype(self): # anon_clone " return clone of self without identifiers " #print "%s.ctype()"%self l=[] for x in self: if isinstance(x,Node): l.append(x.ctype()) else: l.append(x) #print "%s.__class__(*%s)"%(self,l) return self.__class__(*l, **self.__dict__) # XX **self.__dict__ ? def cbasetype(self): " return ctype with all TypeAlias's replaced " # WARNING: we cache results (so do not mutate self!!) l=[] for x in self: if isinstance(x,Node): l.append(x.cbasetype()) else: l.append(x) #print "%s.__class__(*%s)"%(self,l) return self.__class__(*l, **self.__dict__) # XX **self.__dict__ ? def signature( self, tank=None ): if tank is None: tank = {} for node in self.nodes(): if not tank.has_key( type(node) ): tank[ type(node) ] = {} type(node).tank = tank[type(node)] shape = tuple( [ type(_node).__name__ for _node in node ] ) if not tank[type(node)].has_key(shape): tank[type(node)][shape] = [] tank[type(node)][shape].append( node ) return tank def psig( self, tank=None ): if tank is None: tank = {} tank = self.signature(tank) for key in tank.keys(): print key.__name__ for shape in tank[key].keys(): print " ", shape # ################################################# class Named(genpyx.Named, Node): " has a .name property " def get_name(self): if self: assert type(self[0])==str return self[0] return None def set_name(self, name): if self: self[0] = name else: self.append(name) name = property(get_name,set_name) class BasicType(genpyx.BasicType, Named): "float double void char int" pass class Qualifier(genpyx.Qualifier, Named): "register signed unsigned short long const volatile inline" pass class StorageClass(genpyx.StorageClass, Named): "extern static auto" pass class Ellipses(genpyx.Ellipses, Named): "..." pass class GCCBuiltin(genpyx.GCCBuiltin, BasicType): "things with __builtin prefix" pass class Identifier(genpyx.Identifier, Named): """ shape = +( str, +ConstExpr ) """ #def explain(self): #if len(self)==1: #return "%s"%self.name #else: #return "%s initialized to %s"%(self.name, #Node(self[1]).explain()) # will handle Initializer # def ctype(self): # return self.__class__(*self[1:]) #.clone() ? # def get_name(self): # if self: # return self[0] # def set_name(self, name): # if self: # self[0] = name # else: # self.append(name) # name = property(get_name,set_name) def cstr(self,l=None): if l is None: l=[] if len(self)>1: assert len(self)==2 l.append( '%s = %s'%(self[0],self[1]) ) elif len(self)==1: l.append( str(self[0]) ) return " ".join(l) class TypeAlias(genpyx.TypeAlias, Named): """ typedefed things, eg. size_t """ def cbasetype( self ): node = self.typedef.cbasetype().get_rest() return node class Function(genpyx.Function, Node): """ """ #def explain(self): #if len(self): #return "function (%s), returning"%\ #", ".join( map(lambda x:x.explain(),self) ) #else: #return "function returning" def cstr(self,l): #print '%s.cstr(%s)'%(self,l) _l=[] assert len(self) i=0 while isinstance(self[i],Declarator): _l.append( self[i].cstr() ) i=i+1 l.append( '(%s)'% ', '.join(_l) ) while i1 # more than just a tag def get_members(self): return self[1:] members = property(get_members) # fields ? def ctype(self): if not self.tag.name: #print "# WARNING : anonymous struct " # OK i think return self.clone() # self = self.clone() # return self[:1] # just the tag return self.__class__( self.tag, **self.__dict__ ) # just the Tag # return self.__class__( *self, **self.__dict__ ) def cbasetype(self): return self.ctype() # is this enough ??? # return Node.cbasetype(self) # XX lookup my tag if i am empty ..? class Compound(genpyx.Compound, Taged): "Struct or Union" def cstr(self,_l=None): assert isinstance( self[0], Tag ) tag='' if len(self[0]): tag=' '+self[0][0] if isinstance(self,Struct): l=[ 'struct%s '%tag ] elif isinstance(self,Union): l=[ 'union%s '%tag ] if len(self)>1: l.append(' { ') for decl in self[1:]: l.append( decl.cstr()+"; " ) l.append('} ') if _l is None: _l=[] while l: _l.insert( 0, l.pop() ) # XX empty struct with no tag -> "struct" XX return "".join( _l ) def ctype(self): tp = Taged.ctype(self) for i in range(1,len(tp)): tp[i] = StructDeclarator().init_from( tp[i] ) return tp class Struct(genpyx.Struct, Compound): """ """ pass class Union(genpyx.Union, Compound): """ """ pass class Enum(genpyx.Enum, Taged): """ """ def cstr(self,_l=None): assert isinstance( self[0], Tag ) tag='' if len(self[0]): tag=' '+self[0][0] l=[ 'enum%s '%tag ] if len(self)>1: l.append(' { ') for node in self[1:]: l.append( node.cstr()+', ' ) l.append('} ') if _l is None: _l=[] while l: _l.insert( 0, l.pop() ) return ''.join( _l ) class Declarator(genpyx.Declarator, Node): """ """ def __eq__(self,other): " unordered equality " # ordering sometimes gets lost when we do a cbasetype if not isinstance(other,Node): return False a, b = self[:], other[:] a.sort() b.sort() return a == b def __hash__( self ): hs = [hash(item) for item in self] hs.sort() return hash( tuple([hash(type(self))]+hs) ) def transform(self): return def get_identifier(self): if len(self)>1: return self[0] def set_identifier(self, identifier): if len(self)>1: self[0] = identifier else: self.insert(0,identifier) identifier = property(get_identifier,set_identifier) def get_spec(self): spec = self[-1] if type(spec)==TypeSpecifiers: # isinstance ?? return spec spec = property(get_spec) def get_type_alias(self): if self.spec: if isinstance(self.spec[0], TypeAlias): return self.spec[0] type_alias = property(get_type_alias) def get_tagged(self): if self.spec: return self.spec.tagged # i am a tagged tagged = property(get_tagged) def get_compound(self): if self.spec: return self.spec.compound # i am a compound compound = property(get_compound) def get_struct(self): if self.spec: return self.spec.struct # i am a struct struct = property(get_struct) def get_union(self): if self.spec: return self.spec.union # i am a union union = property(get_union) def get_enum(self): if self.spec: return self.spec.enum # i am an enum enum = property(get_enum) def get_function(self): if len(self)>1 and type(self[1])==Function: # isinstance ?? return self[1] function = property(get_function) def get_pointer(self): if len(self)>1 and type(self[1])==Pointer: # isinstance ?? return self[1] pointer = property(get_pointer) def get_array(self): if len(self)>1 and type(self[1])==Array: # isinstance ?? return self[1] array = property(get_array) def get_name(self): if self.identifier: return self.identifier.name def set_name(self, name): assert self.identifier is not None self.identifier.name = name name = property(get_name, set_name) def get_rest(self): # XX needs a better name if len(self)>1: return self[1] return self[0] def pointer_to( self ): " return Declarator pointing to self's type " decl = Declarator(Identifier(), Pointer(self.get_rest().clone())) return decl def deref( self ): " return (clone of) Declarator that self is pointing to " node = self.ctype() # clone pointer = node.pointer or node.array assert pointer, "cannot dereference non-pointer" node[1:2] = pointer return node def is_void(self): return self.spec and BasicType('void') in self.spec def is_pointer_to_fn(self): return self.pointer and self.deref().function def is_pointer_to_char(self): # return self.ctype() == TransUnit("char *a;").transform()[0].ctype() node = self.pointer or self.array if node: spec = node.spec if spec and BasicType('char') in spec and not BasicType('unsigned') in spec: return True return False def is_callback(self): " i am a pointer to a function whose last arg is void* " if self.is_pointer_to_fn(): fn = self.deref().function if fn.args: arg = fn.args[-1] if arg.pointer and arg.deref().is_void(): return True def is_complete( self, tag_lookup ): if self.tagged and self.tagged.tag.name in tag_lookup and not tag_lookup[self.tagged.tag.name].has_members(): return False return True def is_primative( self ): "i am a char,short,int,float,double... " spec = self.cbasetype().spec return spec and spec.find(BasicType) 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 ... XXX self = self.cbasetype() 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 cstr(self,l=None): return Node.cstr(self,l).strip() def ctype(self): decl=Declarator() decl.init_from( self.clone() ) decl.identifier = Identifier() for i in range(1,len(decl)): decl[i]=decl[i].ctype() return decl def cbasetype(self): # WARNING: we cache results (so do not mutate self!!) try: # this cache improves performance by 50% return self.__cbasetype.clone() except AttributeError: pass decl = self.ctype() # gets rid of Identifier names for i, node in enumerate(decl): decl[i] = decl[i].cbasetype() # return decl.get_rest() done = False while not done: done = True nodes = decl.deepfilter( TypeSpecifiers ) for node in nodes: if node.deepfind( TypeSpecifiers ) != node: # this node has another TypeSpecifier; decl.expose_node( node ) done = False break # start again... # each TypeSpecifier needs to absorb primitive siblings (StorageClass, BasicType etc.) nodes = decl.deepfilter( TypeSpecifiers ) for node in nodes: parent = decl.get_parent(node) i = 0 while i < len(parent): assert not type(parent[i]) in (TypeAlias, Enum, Struct, Union) if type(parent[i]) in (StorageClass, BasicType, Qualifier): node.append( parent.pop(i) ) else: i = i + 1 self.__cbasetype = decl.clone() return decl def invalidate(self): # flush cache, etc. try: del self.__cbasetype except AttributeError: pass def declare_str(self,name): " return c string declaring name with same type as self " tp = self.ctype() tp.name = name return tp.cstr()+";" class Typedef(genpyx.Typedef, Declarator): def cstr(self,l=None): return 'typedef ' + Declarator.cstr(self,l) #.strip() class AbstractDeclarator(genpyx.AbstractDeclarator, Declarator): """ used in Function; may lack an identifier """ #def cstr(self,l=None): #return Node.cstr(self,l) # def ctype(self): # # _type_ ignores the name of our identifier # return Node.ctype(self) class FieldLength(genpyx.FieldLength, Node): """ """ #def explain(self): #return "" def cstr(self,l): l.append(':%s'%self[0]) class StructDeclarator(genpyx.StructDeclarator, Declarator): # also used in Union """ """ #def explain(self): #flen = self.find(FieldLength) #if flen is not None: #i = self.index(flen) #self.pop(i) #s = Declarator.explain(self) #self.insert(i,flen) #width = flen[0] #if width > 0: #return s+" bitfield %s wide"%width #else: #return s+" alignment bitfield" #else: #return Declarator.explain(self) # def ctype(self): # return self def get_field_length(self): if len(self)>1 and isinstance( self[1], FieldLength ): return self[1] field_length = property(get_field_length) class DeclarationSpecifiers(genpyx.DeclarationSpecifiers, Node): #class TypeSpecifiers(Node): """ """ def __eq__(self,other): " unordered equality " if not isinstance(other,Node): return False a, b = self[:], other[:] a.sort() b.sort() return a == b def __hash__( self ): hs = [hash(item) for item in self] hs.sort() return hash( tuple([hash(type(self))]+hs) ) # def is_struct(self): # return self.find(Struct) is not None class TypeSpecifiers(genpyx.TypeSpecifiers, DeclarationSpecifiers): """ """ def get_tagged(self): if self and isinstance(self[0],Taged): return self[0] tagged = property(get_tagged) def get_compound(self): if self and isinstance(self[0],Compound): return self[0] compound = property(get_compound) def get_struct(self): if self and isinstance(self[0],Struct): return self[0] struct = property(get_struct) def get_union(self): if self and isinstance(self[0],Union): return self[0] union = property(get_union) def get_enum(self): if self and isinstance(self[0],Enum): return self[0] enum = property(get_enum) def cbasetype(self): node = Node.cbasetype(self) # node.expose( TypeSpecifiers ) # if node.deepfind(TypeSpecifiers) != node: return node class Initializer(genpyx.Initializer, Node): """ """ pass class Declaration(genpyx.Declaration, Node): """ """ def do_spec(self): " distribute DeclarationSpecifiers over each Declarator " spec=self[0] assert isinstance(spec,DeclarationSpecifiers), spec.deepstr() self.pop(0) for declarator in self: assert isinstance(declarator,Declarator) #if isinstance(declarator,DeclarationSpecifiers #huh? ##for node in spec: ##declarator.append(node.clone()) declarator.append(spec) def transform(self): # children go first for node in self.nodes(): if isinstance(node,Declaration): node.do_spec() node.file = self.file # overkill ? self.expose(Declaration) #def explain(self): #return string.join([x.explain() for x in self],", ") #return string.join(map(lambda x:x.explain(),self),", ") class ParameterDeclaration(genpyx.ParameterDeclaration, Declaration): """ """ pass class StructDeclaration(genpyx.StructDeclaration, Declaration): """ """ pass class TransUnit(genpyx.TransUnit, Node): """ Top level node. """ def __init__( self, item ): # XX __init__ uses different signature ! XX if type(item)==str: node = cparse.TransUnit() node.parse(item) else: node = item assert isinstance( node, cparse.TransUnit ), str(node) Node.__init__(self) self[:] = [ self.convert(child) for child in node ] self.__dict__.update( node.__dict__ ) assert "name" not in node.__dict__ self.syms = {} # map identifier names to their Declarator's self.typedefs = {} # map names to Typedef's self.tag_lookup = {} # map struct, union, enum tags to Taged's # XX should call transform here XX # print self.deepstr() def __getstate__( self ): nodes = tuple( [ repr(node) for node in self ] ) typedefs = tuple( [ (key,repr(val)) for key,val in self.typedefs.items() ] ) return nodes, typedefs def __setstate__( self, state ): Node.__init__(self) nodes, typedefs = state nodes = [ eval(node) for node in nodes ] self[:] = nodes typedefs = [ (key,eval(val)) for key,val in typedefs ] self.typedefs = dict(typedefs) def convert( self, node ): # name = node.__class__.__name__ # cls = globals()[ name ] cls = cls_lookup[ type(node) ] _node = cls() for child in node: if isinstance(child, node_module.Node): child = self.convert( child ) else: assert child is None or type(child) in (str, int), type(child) _node.append( child ) _node.__dict__.update( node.__dict__ ) return _node def strip(self,files): " leave only the declarations from " i=0 while i