diff options
Diffstat (limited to 'Modules/cgen.py')
-rw-r--r-- | Modules/cgen.py | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/Modules/cgen.py b/Modules/cgen.py new file mode 100644 index 0000000..0cdeaa6 --- /dev/null +++ b/Modules/cgen.py @@ -0,0 +1,458 @@ +# Python script to parse cstubs file for gl and generate C stubs. +# usage: python cgen <cstubs >glmodule.c +# +# XXX BUG return arrays generate wrong code +# XXX need to change error returns into gotos to free mallocked arrays + + +import string +import sys + + +# Function to print to stderr +# +def err(args): + savestdout = sys.stdout + try: + sys.stdout = sys.stderr + for i in args: + print i, + print + finally: + sys.stdout = savestdout + + +# The set of digits that form a number +# +digits = '0123456789' + + +# Function to extract a string of digits from the front of the string. +# Returns the leading string of digits and the remaining string. +# If no number is found, returns '' and the original string. +# +def getnum(s): + n = '' + while s[:1] in digits: + n = n + s[:1] + s = s[1:] + return n, s + + +# Function to check if a string is a number +# +def isnum(s): + if not s: return 0 + for c in s: + if not c in digits: return 0 + return 1 + + +# Allowed function return types +# +return_types = ['void', 'short', 'long'] + + +# Allowed function argument types +# +arg_types = ['char', 'string', 'short', 'float', 'long', 'double'] + + +# Need to classify arguments as follows +# simple input variable +# simple output variable +# input array +# output array +# input giving size of some array +# +# Array dimensions can be specified as follows +# constant +# argN +# constant * argN +# retval +# constant * retval +# +# The dimensions given as constants * something are really +# arrays of points where points are 2- 3- or 4-tuples +# +# We have to consider three lists: +# python input arguments +# C stub arguments (in & out) +# python output arguments (really return values) +# +# There is a mapping from python input arguments to the input arguments +# of the C stub, and a further mapping from C stub arguments to the +# python return values + + +# Exception raised by checkarg() and generate() +# +arg_error = 'bad arg' + + +# Function to check one argument. +# Arguments: the type and the arg "name" (really mode plus subscript). +# Raises arg_error if something's wrong. +# Return type, mode, factor, rest of subscript; factor and rest may be empty. +# +def checkarg(type, arg): + # + # Turn "char *x" into "string x". + # + if type = 'char' and arg[0] = '*': + type = 'string' + arg = arg[1:] + # + # Check that the type is supported. + # + if type not in arg_types: + raise arg_error, ('bad type', type) + # + # Split it in the mode (first character) and the rest. + # + mode, rest = arg[:1], arg[1:] + # + # The mode must be 's' for send (= input) or 'r' for return argument. + # + if mode not in ('r', 's'): + raise arg_error, ('bad arg mode', mode) + # + # Is it a simple argument: if so, we are done. + # + if not rest: + return type, mode, '', '' + # + # Not a simple argument; must be an array. + # The 'rest' must be a subscript enclosed in [ and ]. + # The subscript must be one of the following forms, + # otherwise we don't handle it (where N is a number): + # N + # argN + # retval + # N*argN + # N*retval + # + if rest[:1] <> '[' or rest[-1:] <> ']': + raise arg_error, ('subscript expected', rest) + sub = rest[1:-1] + # + # Is there a leading number? + # + num, sub = getnum(sub) + if num: + # There is a leading number + if not sub: + # The subscript is just a number + return type, mode, num, '' + if sub[:1] = '*': + # There is a factor prefix + sub = sub[1:] + else: + raise arg_error, ('\'*\' expected', sub) + if sub = 'retval': + # size is retval -- must be a reply argument + if mode <> 'r': + raise arg_error, ('non-r mode with [retval]', mode) + elif sub[:3] <> 'arg' or not isnum(sub[3:]): + raise arg_error, ('bad subscript', sub) + # + return type, mode, num, sub + + +# List of functions for which we have generated stubs +# +functions = [] + + +# Generate the stub for the given function, using the database of argument +# information build by successive calls to checkarg() +# +def generate(type, func, database): + # + # Check that we can handle this case: + # no variable size reply arrays yet + # + n_in_args = 0 + n_out_args = 0 + # + for a_type, a_mode, a_factor, a_sub in database: + if a_mode = 's': + n_in_args = n_in_args + 1 + elif a_mode = 'r': + n_out_args = n_out_args + 1 + else: + # Can't happen + raise arg_error, ('bad a_mode', a_mode) + if (a_mode = 'r' and a_sub) or a_sub = 'retval': + e = 'Function', func, 'too complicated:' + err(e + (a_type, a_mode, a_factor, a_sub)) + print '/* XXX Too complicated to generate code for */' + return + # + functions.append(func) + # + # Stub header + # + print + print 'static object *' + print 'gl_' + func + '(self, args)' + print '\tobject *self;' + print '\tobject *args;' + print '{' + # + # Declare return value if any + # + if type <> 'void': + print '\t' + type, 'retval;' + # + # Declare arguments + # + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + print '\t' + a_type, + if a_sub: + print '*', + print 'arg' + `i+1`, + if a_factor and not a_sub: + print '[', a_factor, ']', + print ';' + # + # Find input arguments derived from array sizes + # + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode = 's' and a_sub[:3] = 'arg' and isnum(a_sub[3:]): + # Sending a variable-length array + n = eval(a_sub[3:]) + if 1 <= n <= len(database): + b_type, b_mode, b_factor, b_sub = database[n-1] + if b_mode = 's': + database[n-1] = b_type, 'i', a_factor, `i` + n_in_args = n_in_args - 1 + # + # Assign argument positions in the Python argument list + # + in_pos = [] + i_in = 0 + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode = 's': + in_pos.append(i_in) + i_in = i_in + 1 + else: + in_pos.append(-1) + # + # Get input arguments + # + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode = 'i': + # + # Implicit argument; + # a_factor is divisor if present, + # a_sub indicates which arg (`database index`) + # + j = eval(a_sub) + print '\tif', + print '(!geti' + a_type + 'arraysize(args,', + print `n_in_args` + ',', + print `in_pos[j]` + ',', + print '&arg' + `i+1` + '))' + print '\t\treturn NULL;' + if a_factor: + print '\targ' + `i+1`, + print '= arg' + `i+1`, + print '/', a_factor + ';' + elif a_mode = 's': + if a_sub: # Allocate memory for varsize array + print '\tif ((arg' + `i+1`, '=', + print 'NEW(' + a_type + ',', + if a_factor: print a_factor, '*', + print a_sub, ')) == NULL)' + print '\t\treturn err_nomem();' + print '\tif', + if a_factor or a_sub: # Get a fixed-size array array + print '(!geti' + a_type + 'array(args,', + print `n_in_args` + ',', + print `in_pos[i]` + ',', + if a_factor: print a_factor, + if a_factor and a_sub: print '*', + if a_sub: print a_sub, + print ', arg' + `i+1` + '))' + else: # Get a simple variable + print '(!geti' + a_type + 'arg(args,', + print `n_in_args` + ',', + print `in_pos[i]` + ',', + print '&arg' + `i+1` + '))' + print '\t\treturn NULL;' + # + # Begin of function call + # + if type <> 'void': + print '\tretval =', func + '(', + else: + print '\t' + func + '(', + # + # Argument list + # + for i in range(len(database)): + if i > 0: print ',', + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode = 'r' and not a_factor: + print '&', + print 'arg' + `i+1`, + # + # End of function call + # + print ');' + # + # Free varsize arrays + # + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode = 's' and a_sub: + print '\tDEL(arg' + `i+1` + ');' + # + # Return + # + if n_out_args: + # + # Multiple return values -- construct a tuple + # + if type <> 'void': + n_out_args = n_out_args + 1 + if n_out_args = 1: + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode = 'r': + break + else: + raise arg_error, 'expected r arg not found' + print '\treturn', + print mkobject(a_type, 'arg' + `i+1`) + ';' + else: + print '\t{ object *v = newtupleobject(', + print n_out_args, ');' + print '\t if (v == NULL) return NULL;' + i_out = 0 + if type <> 'void': + print '\t settupleitem(v,', + print `i_out` + ',', + print mkobject(type, 'retval') + ');' + i_out = i_out + 1 + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode = 'r': + print '\t settupleitem(v,', + print `i_out` + ',', + s = mkobject(a_type, 'arg' + `i+1`) + print s + ');' + i_out = i_out + 1 + print '\t return v;' + print '\t}' + else: + # + # Simple function return + # Return None or return value + # + if type = 'void': + print '\tINCREF(None);' + print '\treturn None;' + else: + print '\treturn', mkobject(type, 'retval') + ';' + # + # Stub body closing brace + # + print '}' + + +# Subroutine to return a function call to mknew<type>object(<arg>) +# +def mkobject(type, arg): + return 'mknew' + type + 'object(' + arg + ')' + + +# Input line number +lno = 0 + + +# Input is divided in two parts, separated by a line containing '%%'. +# <part1> -- literally copied to stdout +# <part2> -- stub definitions + +# Variable indicating the current input part. +# +part = 1 + +# Main loop over the input +# +while 1: + try: + line = raw_input() + except EOFError: + break + # + lno = lno+1 + words = string.split(line) + # + if part = 1: + # + # In part 1, copy everything literally + # except look for a line of just '%%' + # + if words = ['%%']: + part = part + 1 + else: + # + # Look for names of manually written + # stubs: a single percent followed by the name + # of the function in Python. + # The stub name is derived by prefixing 'gl_'. + # + if words and words[0][0] = '%': + func = words[0][1:] + if (not func) and words[1:]: + func = words[1] + if func: + functions.append(func) + else: + print line + elif not words: + pass # skip empty line + elif words[0] = '#include': + print line + elif words[0][:1] = '#': + pass # ignore comment + elif words[0] not in return_types: + err('Line', lno, ': bad return type :', words[0]) + elif len(words) < 2: + err('Line', lno, ': no funcname :', line) + else: + if len(words) % 2 <> 0: + err('Line', lno, ': odd argument list :', words[2:]) + else: + database = [] + try: + for i in range(2, len(words), 2): + x = checkarg(words[i], words[i+1]) + database.append(x) + print + print '/*', + for w in words: print w, + print '*/' + generate(words[0], words[1], database) + except arg_error, msg: + err('Line', lno, ':', msg) + + +print +print 'static struct methodlist gl_methods[] = {' +for func in functions: + print '\t{"' + func + '", gl_' + func + '},' +print '\t{NULL, NULL} /* Sentinel */' +print '};' +print +print 'initgl()' +print '{' +print '\tinitmodule("gl", gl_methods);' +print '}' |