1#!/usr/bin/env python 2""" genpyx.py - parse c declarations 3 4(c) 2002, 2003, 2004, 2005 Simon Burton <simon@arrowtheory.com> 5Released under GNU LGPL license. 6 7version 0.xx 8 9This is a module of mixin classes for ir.py . 10 11Towards the end of ir.py our global class definitions 12are remapped to point to the class definitions in ir.py . 13So, for example, when we refer to Node we get ir.Node . 14 15""" 16 17import sys 18from datetime import datetime 19 20# XX use this Context class instead of all those kw dicts !! XX 21class 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 31class 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 64class 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 92class Named(object): 93 "has a .name property" 94 pass 95 96class BasicType(object): 97 "float double void char int" 98 pass 99 100class 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 111class StorageClass(object): 112 "extern static auto" 113 def pyxstr(self,toks=None,indent=0,**kw): 114 return "" 115 116class Ellipses(object): 117 "..." 118 pass 119 120class GCCBuiltin(BasicType): 121 "things with __builtin prefix" 122 pass 123 124class 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 134class 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 152class 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 170class 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 185class 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 199class Tag(object): 200 " the tag of a Struct, Union or Enum " 201 pass 202 203class Taged(object): 204 "Struct, Union or Enum " 205 pass 206 207class 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 250class Struct(Compound): 251 """ 252 """ 253 pass 254 255class Union(Compound): 256 """ 257 """ 258 pass 259 260 261class 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 301class 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 399class 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 432class 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 442class FieldLength(object): 443 """ 444 """ 445 def pyxstr(self,toks,indent,**kw): 446 pass 447 448 449class 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 458class DeclarationSpecifiers(object): 459 """ 460 """ 461 pass 462 463class TypeSpecifiers(DeclarationSpecifiers): 464 """ 465 """ 466 pass 467 468class Initializer(object): 469 """ 470 """ 471 pass 472 473class Declaration(object): 474 """ 475 """ 476 pass 477 478class ParameterDeclaration(Declaration): 479 """ 480 """ 481 pass 482 483class StructDeclaration(Declaration): 484 """ 485 """ 486 pass 487 488class 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 524python_kws = """ 525break continue del def except exec finally pass print raise 526return try global assert lambda yield 527for while if elif else and in is not or import from """.split() 528python_kws = dict( zip( python_kws, (None,)*len(python_kws) ) ) 529 530 531