You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
520 lines
13 KiB
520 lines
13 KiB
######################################################################## |
|
# Copyright (c) 2000, BeOpen.com. |
|
# Copyright (c) 1995-2000, Corporation for National Research Initiatives. |
|
# Copyright (c) 1990-1995, Stichting Mathematisch Centrum. |
|
# All rights reserved. |
|
# |
|
# See the file "Misc/COPYRIGHT" for information on usage and |
|
# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
|
######################################################################## |
|
|
|
# Python script to parse cstubs file for gl and generate C stubs. |
|
# usage: python cgen.py <cstubs >glmodule.c |
|
# |
|
# NOTE: You must first make a python binary without the "GL" option |
|
# before you can run this, when building Python for the first time. |
|
# See comments in the Makefile. |
|
# |
|
# 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 and s[0] in digits: |
|
n = n + s[0] |
|
s = s[1:] |
|
return n, s |
|
|
|
|
|
# Function to check if a string is a number |
|
# |
|
def isnum(s): |
|
if not s: return False |
|
for c in s: |
|
if not c in digits: return False |
|
return True |
|
|
|
|
|
# Allowed function return types |
|
# |
|
return_types = ['void', 'short', 'long'] |
|
|
|
|
|
# Allowed function argument types |
|
# |
|
arg_types = ['char', 'string', 'short', 'u_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) |
|
if type[:2] == 'u_': |
|
type = 'unsigned ' + type[2:] |
|
# |
|
# 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 not isnum(sub) and (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': |
|
err('Function', func, 'too complicated:', |
|
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 PyObject *' |
|
print 'gl_' + func + '(self, args)' |
|
print '\tPyObject *self;' |
|
print '\tPyObject *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, |
|
brac = ket = '' |
|
if a_sub and not isnum(a_sub): |
|
if a_factor: |
|
brac = '(' |
|
ket = ')' |
|
print brac + '*', |
|
print 'arg' + `i+1` + ket, |
|
if a_sub and isnum(a_sub): |
|
print '[', a_sub, ']', |
|
if a_factor: |
|
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_type[:9] == 'unsigned ': |
|
xtype = a_type[9:] |
|
else: |
|
xtype = a_type |
|
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' + xtype + 'arraysize(args,', |
|
print `n_in_args` + ',', |
|
print `in_pos[j]` + ',', |
|
if xtype <> a_type: |
|
print '('+xtype+' *)', |
|
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 and not isnum(a_sub): |
|
# Allocate memory for varsize array |
|
print '\tif ((arg' + `i+1`, '=', |
|
if a_factor: |
|
print '('+a_type+'(*)['+a_factor+'])', |
|
print 'PyMem_NEW(' + a_type, ',', |
|
if a_factor: |
|
print a_factor, '*', |
|
print a_sub, ')) == NULL)' |
|
print '\t\treturn PyErr_NoMemory();' |
|
print '\tif', |
|
if a_factor or a_sub: # Get a fixed-size array array |
|
print '(!geti' + xtype + '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 ',', |
|
if (a_sub and a_factor) or xtype <> a_type: |
|
print '('+xtype+' *)', |
|
print 'arg' + `i+1` + '))' |
|
else: # Get a simple variable |
|
print '(!geti' + xtype + 'arg(args,', |
|
print `n_in_args` + ',', |
|
print `in_pos[i]` + ',', |
|
if xtype <> a_type: |
|
print '('+xtype+' *)', |
|
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 and not isnum(a_sub): |
|
print '\tPyMem_DEL(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{ PyObject *v = PyTuple_New(', |
|
print n_out_args, ');' |
|
print '\t if (v == NULL) return NULL;' |
|
i_out = 0 |
|
if type <> 'void': |
|
print '\t PyTuple_SetItem(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 PyTuple_SetItem(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 '\tPy_INCREF(Py_None);' |
|
print '\treturn Py_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): |
|
if type[:9] == 'unsigned ': |
|
type = type[9:] |
|
return 'mknew' + type + 'object((' + type + ') ' + arg + ')' |
|
return 'mknew' + type + 'object(' + arg + ')' |
|
|
|
|
|
defined_archs = [] |
|
|
|
# usage: cgen [ -Dmach ... ] [ file ] |
|
for arg in sys.argv[1:]: |
|
if arg[:2] == '-D': |
|
defined_archs.append(arg[2:]) |
|
else: |
|
# Open optional file argument |
|
sys.stdin = open(arg, 'r') |
|
|
|
|
|
# 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 |
|
continue |
|
if not words: |
|
continue # skip empty line |
|
elif words[0] == 'if': |
|
# if XXX rest |
|
# if !XXX rest |
|
if words[1][0] == '!': |
|
if words[1][1:] in defined_archs: |
|
continue |
|
elif words[1] not in defined_archs: |
|
continue |
|
words = words[2:] |
|
if 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 PyMethodDef gl_methods[] = {' |
|
for func in functions: |
|
print '\t{"' + func + '", gl_' + func + '},' |
|
print '\t{NULL, NULL} /* Sentinel */' |
|
print '};' |
|
print |
|
print 'void' |
|
print 'initgl()' |
|
print '{' |
|
print '\t(void) Py_InitModule("gl", gl_methods);' |
|
print '}'
|
|
|