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