Home | History | Annotate | Download | only in Modules
      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
     20 from warnings import warnpy3k
     21 warnpy3k("the cgen module has been removed in Python 3.0", stacklevel=2)
     22 del warnpy3k
     23 
     24 
     25 import string
     26 import sys
     27 
     28 
     29 # Function to print to stderr
     30 #
     31 def 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 #
     44 digits = '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 #
     51 def 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 #
     61 def 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 #
     70 return_types = ['void', 'short', 'long']
     71 
     72 
     73 # Allowed function argument types
     74 #
     75 arg_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 #
    107 arg_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 #
    115 def 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 #
    183 functions = []
    184 
    185 
    186 # Generate the stub for the given function, using the database of argument
    187 # information build by successive calls to checkarg()
    188 #
    189 def 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 #
    411 def 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 
    418 defined_archs = []
    419 
    420 # usage: cgen [ -Dmach ... ] [ file ]
    421 for 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
    430 lno = 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 #
    439 part = 1
    440 
    441 # Main loop over the input
    442 #
    443 while 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 
    512 print
    513 print 'static struct PyMethodDef gl_methods[] = {'
    514 for func in functions:
    515     print '\t{"' + func + '", gl_' + func + '},'
    516 print '\t{NULL, NULL} /* Sentinel */'
    517 print '};'
    518 print
    519 print 'void'
    520 print 'initgl()'
    521 print '{'
    522 print '\t(void) Py_InitModule("gl", gl_methods);'
    523 print '}'
    524