1######################################################################## 2# Copyright (c) 2000, BeOpen.com. 3# Copyright (c) 1995-2000, Corporation for National Research Initiatives. 4# Copyright (c) 1990-1995, Stichting Mathematisch Centrum. 5# All rights reserved. 6# 7# See the file "Misc/COPYRIGHT" for information on usage and 8# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. 9######################################################################## 10 11# Python script to parse cstubs file for gl and generate C stubs. 12# usage: python cgen.py <cstubs >glmodule.c 13# 14# NOTE: You must first make a python binary without the "GL" option 15# before you can run this, when building Python for the first time. 16# See comments in the Makefile. 17# 18# XXX BUG return arrays generate wrong code 19# XXX need to change error returns into gotos to free mallocked arrays 20from warnings import warnpy3k 21warnpy3k("the cgen module has been removed in Python 3.0", stacklevel=2) 22del warnpy3k 23 24 25import string 26import sys 27 28 29# Function to print to stderr 30# 31def err(*args): 32 savestdout = sys.stdout 33 try: 34 sys.stdout = sys.stderr 35 for i in args: 36 print i, 37 print 38 finally: 39 sys.stdout = savestdout 40 41 42# The set of digits that form a number 43# 44digits = '0123456789' 45 46 47# Function to extract a string of digits from the front of the string. 48# Returns the leading string of digits and the remaining string. 49# If no number is found, returns '' and the original string. 50# 51def getnum(s): 52 n = '' 53 while s and s[0] in digits: 54 n = n + s[0] 55 s = s[1:] 56 return n, s 57 58 59# Function to check if a string is a number 60# 61def isnum(s): 62 if not s: return False 63 for c in s: 64 if not c in digits: return False 65 return True 66 67 68# Allowed function return types 69# 70return_types = ['void', 'short', 'long'] 71 72 73# Allowed function argument types 74# 75arg_types = ['char', 'string', 'short', 'u_short', 'float', 'long', 'double'] 76 77 78# Need to classify arguments as follows 79# simple input variable 80# simple output variable 81# input array 82# output array 83# input giving size of some array 84# 85# Array dimensions can be specified as follows 86# constant 87# argN 88# constant * argN 89# retval 90# constant * retval 91# 92# The dimensions given as constants * something are really 93# arrays of points where points are 2- 3- or 4-tuples 94# 95# We have to consider three lists: 96# python input arguments 97# C stub arguments (in & out) 98# python output arguments (really return values) 99# 100# There is a mapping from python input arguments to the input arguments 101# of the C stub, and a further mapping from C stub arguments to the 102# python return values 103 104 105# Exception raised by checkarg() and generate() 106# 107arg_error = 'bad arg' 108 109 110# Function to check one argument. 111# Arguments: the type and the arg "name" (really mode plus subscript). 112# Raises arg_error if something's wrong. 113# Return type, mode, factor, rest of subscript; factor and rest may be empty. 114# 115def checkarg(type, arg): 116 # 117 # Turn "char *x" into "string x". 118 # 119 if type == 'char' and arg[0] == '*': 120 type = 'string' 121 arg = arg[1:] 122 # 123 # Check that the type is supported. 124 # 125 if type not in arg_types: 126 raise arg_error, ('bad type', type) 127 if type[:2] == 'u_': 128 type = 'unsigned ' + type[2:] 129 # 130 # Split it in the mode (first character) and the rest. 131 # 132 mode, rest = arg[:1], arg[1:] 133 # 134 # The mode must be 's' for send (= input) or 'r' for return argument. 135 # 136 if mode not in ('r', 's'): 137 raise arg_error, ('bad arg mode', mode) 138 # 139 # Is it a simple argument: if so, we are done. 140 # 141 if not rest: 142 return type, mode, '', '' 143 # 144 # Not a simple argument; must be an array. 145 # The 'rest' must be a subscript enclosed in [ and ]. 146 # The subscript must be one of the following forms, 147 # otherwise we don't handle it (where N is a number): 148 # N 149 # argN 150 # retval 151 # N*argN 152 # N*retval 153 # 154 if rest[:1] <> '[' or rest[-1:] <> ']': 155 raise arg_error, ('subscript expected', rest) 156 sub = rest[1:-1] 157 # 158 # Is there a leading number? 159 # 160 num, sub = getnum(sub) 161 if num: 162 # There is a leading number 163 if not sub: 164 # The subscript is just a number 165 return type, mode, num, '' 166 if sub[:1] == '*': 167 # There is a factor prefix 168 sub = sub[1:] 169 else: 170 raise arg_error, ('\'*\' expected', sub) 171 if sub == 'retval': 172 # size is retval -- must be a reply argument 173 if mode <> 'r': 174 raise arg_error, ('non-r mode with [retval]', mode) 175 elif not isnum(sub) and (sub[:3] <> 'arg' or not isnum(sub[3:])): 176 raise arg_error, ('bad subscript', sub) 177 # 178 return type, mode, num, sub 179 180 181# List of functions for which we have generated stubs 182# 183functions = [] 184 185 186# Generate the stub for the given function, using the database of argument 187# information build by successive calls to checkarg() 188# 189def generate(type, func, database): 190 # 191 # Check that we can handle this case: 192 # no variable size reply arrays yet 193 # 194 n_in_args = 0 195 n_out_args = 0 196 # 197 for a_type, a_mode, a_factor, a_sub in database: 198 if a_mode == 's': 199 n_in_args = n_in_args + 1 200 elif a_mode == 'r': 201 n_out_args = n_out_args + 1 202 else: 203 # Can't happen 204 raise arg_error, ('bad a_mode', a_mode) 205 if (a_mode == 'r' and a_sub) or a_sub == 'retval': 206 err('Function', func, 'too complicated:', 207 a_type, a_mode, a_factor, a_sub) 208 print '/* XXX Too complicated to generate code for */' 209 return 210 # 211 functions.append(func) 212 # 213 # Stub header 214 # 215 print 216 print 'static PyObject *' 217 print 'gl_' + func + '(self, args)' 218 print '\tPyObject *self;' 219 print '\tPyObject *args;' 220 print '{' 221 # 222 # Declare return value if any 223 # 224 if type <> 'void': 225 print '\t' + type, 'retval;' 226 # 227 # Declare arguments 228 # 229 for i in range(len(database)): 230 a_type, a_mode, a_factor, a_sub = database[i] 231 print '\t' + a_type, 232 brac = ket = '' 233 if a_sub and not isnum(a_sub): 234 if a_factor: 235 brac = '(' 236 ket = ')' 237 print brac + '*', 238 print 'arg' + repr(i+1) + ket, 239 if a_sub and isnum(a_sub): 240 print '[', a_sub, ']', 241 if a_factor: 242 print '[', a_factor, ']', 243 print ';' 244 # 245 # Find input arguments derived from array sizes 246 # 247 for i in range(len(database)): 248 a_type, a_mode, a_factor, a_sub = database[i] 249 if a_mode == 's' and a_sub[:3] == 'arg' and isnum(a_sub[3:]): 250 # Sending a variable-length array 251 n = eval(a_sub[3:]) 252 if 1 <= n <= len(database): 253 b_type, b_mode, b_factor, b_sub = database[n-1] 254 if b_mode == 's': 255 database[n-1] = b_type, 'i', a_factor, repr(i) 256 n_in_args = n_in_args - 1 257 # 258 # Assign argument positions in the Python argument list 259 # 260 in_pos = [] 261 i_in = 0 262 for i in range(len(database)): 263 a_type, a_mode, a_factor, a_sub = database[i] 264 if a_mode == 's': 265 in_pos.append(i_in) 266 i_in = i_in + 1 267 else: 268 in_pos.append(-1) 269 # 270 # Get input arguments 271 # 272 for i in range(len(database)): 273 a_type, a_mode, a_factor, a_sub = database[i] 274 if a_type[:9] == 'unsigned ': 275 xtype = a_type[9:] 276 else: 277 xtype = a_type 278 if a_mode == 'i': 279 # 280 # Implicit argument; 281 # a_factor is divisor if present, 282 # a_sub indicates which arg (`database index`) 283 # 284 j = eval(a_sub) 285 print '\tif', 286 print '(!geti' + xtype + 'arraysize(args,', 287 print repr(n_in_args) + ',', 288 print repr(in_pos[j]) + ',', 289 if xtype <> a_type: 290 print '('+xtype+' *)', 291 print '&arg' + repr(i+1) + '))' 292 print '\t\treturn NULL;' 293 if a_factor: 294 print '\targ' + repr(i+1), 295 print '= arg' + repr(i+1), 296 print '/', a_factor + ';' 297 elif a_mode == 's': 298 if a_sub and not isnum(a_sub): 299 # Allocate memory for varsize array 300 print '\tif ((arg' + repr(i+1), '=', 301 if a_factor: 302 print '('+a_type+'(*)['+a_factor+'])', 303 print 'PyMem_NEW(' + a_type, ',', 304 if a_factor: 305 print a_factor, '*', 306 print a_sub, ')) == NULL)' 307 print '\t\treturn PyErr_NoMemory();' 308 print '\tif', 309 if a_factor or a_sub: # Get a fixed-size array array 310 print '(!geti' + xtype + 'array(args,', 311 print repr(n_in_args) + ',', 312 print repr(in_pos[i]) + ',', 313 if a_factor: print a_factor, 314 if a_factor and a_sub: print '*', 315 if a_sub: print a_sub, 316 print ',', 317 if (a_sub and a_factor) or xtype <> a_type: 318 print '('+xtype+' *)', 319 print 'arg' + repr(i+1) + '))' 320 else: # Get a simple variable 321 print '(!geti' + xtype + 'arg(args,', 322 print repr(n_in_args) + ',', 323 print repr(in_pos[i]) + ',', 324 if xtype <> a_type: 325 print '('+xtype+' *)', 326 print '&arg' + repr(i+1) + '))' 327 print '\t\treturn NULL;' 328 # 329 # Begin of function call 330 # 331 if type <> 'void': 332 print '\tretval =', func + '(', 333 else: 334 print '\t' + func + '(', 335 # 336 # Argument list 337 # 338 for i in range(len(database)): 339 if i > 0: print ',', 340 a_type, a_mode, a_factor, a_sub = database[i] 341 if a_mode == 'r' and not a_factor: 342 print '&', 343 print 'arg' + repr(i+1), 344 # 345 # End of function call 346 # 347 print ');' 348 # 349 # Free varsize arrays 350 # 351 for i in range(len(database)): 352 a_type, a_mode, a_factor, a_sub = database[i] 353 if a_mode == 's' and a_sub and not isnum(a_sub): 354 print '\tPyMem_DEL(arg' + repr(i+1) + ');' 355 # 356 # Return 357 # 358 if n_out_args: 359 # 360 # Multiple return values -- construct a tuple 361 # 362 if type <> 'void': 363 n_out_args = n_out_args + 1 364 if n_out_args == 1: 365 for i in range(len(database)): 366 a_type, a_mode, a_factor, a_sub = database[i] 367 if a_mode == 'r': 368 break 369 else: 370 raise arg_error, 'expected r arg not found' 371 print '\treturn', 372 print mkobject(a_type, 'arg' + repr(i+1)) + ';' 373 else: 374 print '\t{ PyObject *v = PyTuple_New(', 375 print n_out_args, ');' 376 print '\t if (v == NULL) return NULL;' 377 i_out = 0 378 if type <> 'void': 379 print '\t PyTuple_SetItem(v,', 380 print repr(i_out) + ',', 381 print mkobject(type, 'retval') + ');' 382 i_out = i_out + 1 383 for i in range(len(database)): 384 a_type, a_mode, a_factor, a_sub = database[i] 385 if a_mode == 'r': 386 print '\t PyTuple_SetItem(v,', 387 print repr(i_out) + ',', 388 s = mkobject(a_type, 'arg' + repr(i+1)) 389 print s + ');' 390 i_out = i_out + 1 391 print '\t return v;' 392 print '\t}' 393 else: 394 # 395 # Simple function return 396 # Return None or return value 397 # 398 if type == 'void': 399 print '\tPy_INCREF(Py_None);' 400 print '\treturn Py_None;' 401 else: 402 print '\treturn', mkobject(type, 'retval') + ';' 403 # 404 # Stub body closing brace 405 # 406 print '}' 407 408 409# Subroutine to return a function call to mknew<type>object(<arg>) 410# 411def mkobject(type, arg): 412 if type[:9] == 'unsigned ': 413 type = type[9:] 414 return 'mknew' + type + 'object((' + type + ') ' + arg + ')' 415 return 'mknew' + type + 'object(' + arg + ')' 416 417 418defined_archs = [] 419 420# usage: cgen [ -Dmach ... ] [ file ] 421for arg in sys.argv[1:]: 422 if arg[:2] == '-D': 423 defined_archs.append(arg[2:]) 424 else: 425 # Open optional file argument 426 sys.stdin = open(arg, 'r') 427 428 429# Input line number 430lno = 0 431 432 433# Input is divided in two parts, separated by a line containing '%%'. 434# <part1> -- literally copied to stdout 435# <part2> -- stub definitions 436 437# Variable indicating the current input part. 438# 439part = 1 440 441# Main loop over the input 442# 443while 1: 444 try: 445 line = raw_input() 446 except EOFError: 447 break 448 # 449 lno = lno+1 450 words = string.split(line) 451 # 452 if part == 1: 453 # 454 # In part 1, copy everything literally 455 # except look for a line of just '%%' 456 # 457 if words == ['%%']: 458 part = part + 1 459 else: 460 # 461 # Look for names of manually written 462 # stubs: a single percent followed by the name 463 # of the function in Python. 464 # The stub name is derived by prefixing 'gl_'. 465 # 466 if words and words[0][0] == '%': 467 func = words[0][1:] 468 if (not func) and words[1:]: 469 func = words[1] 470 if func: 471 functions.append(func) 472 else: 473 print line 474 continue 475 if not words: 476 continue # skip empty line 477 elif words[0] == 'if': 478 # if XXX rest 479 # if !XXX rest 480 if words[1][0] == '!': 481 if words[1][1:] in defined_archs: 482 continue 483 elif words[1] not in defined_archs: 484 continue 485 words = words[2:] 486 if words[0] == '#include': 487 print line 488 elif words[0][:1] == '#': 489 pass # ignore comment 490 elif words[0] not in return_types: 491 err('Line', lno, ': bad return type :', words[0]) 492 elif len(words) < 2: 493 err('Line', lno, ': no funcname :', line) 494 else: 495 if len(words) % 2 <> 0: 496 err('Line', lno, ': odd argument list :', words[2:]) 497 else: 498 database = [] 499 try: 500 for i in range(2, len(words), 2): 501 x = checkarg(words[i], words[i+1]) 502 database.append(x) 503 print 504 print '/*', 505 for w in words: print w, 506 print '*/' 507 generate(words[0], words[1], database) 508 except arg_error, msg: 509 err('Line', lno, ':', msg) 510 511 512print 513print 'static struct PyMethodDef gl_methods[] = {' 514for func in functions: 515 print '\t{"' + func + '", gl_' + func + '},' 516print '\t{NULL, NULL} /* Sentinel */' 517print '};' 518print 519print 'void' 520print 'initgl()' 521print '{' 522print '\t(void) Py_InitModule("gl", gl_methods);' 523print '}' 524