520 lines · 14.9 KB · Python
RawDownload
######################################################################### 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 stringimport 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' + repr(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, repr(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 repr(n_in_args) + ',',            print repr(in_pos[j]) + ',',            if xtype <> a_type:                print '('+xtype+' *)',            print '&arg' + repr(i+1) + '))'            print '\t\treturn NULL;'            if a_factor:                print '\targ' + repr(i+1),                print '= arg' + repr(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' + repr(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 repr(n_in_args) + ',',                print repr(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' + repr(i+1) + '))'            else: # Get a simple variable                print '(!geti' + xtype + 'arg(args,',                print repr(n_in_args) + ',',                print repr(in_pos[i]) + ',',                if xtype <> a_type:                    print '('+xtype+' *)',                print '&arg' + repr(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' + repr(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' + repr(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' + repr(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 repr(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 repr(i_out) + ',',                    s = mkobject(a_type, 'arg' + repr(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 numberlno = 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

Lines 1–500 of 520.