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.
7 # See the file "Misc/COPYRIGHT" for information on usage and
8 # redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
9 ########################################################################
11 # Python script to parse cstubs file for gl and generate C stubs.
12 # usage: python cgen.py <cstubs >glmodule.c
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.
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)
29 # Function to print to stderr
32 savestdout
= sys
.stdout
34 sys
.stdout
= sys
.stderr
39 sys
.stdout
= savestdout
42 # The set of digits that form a number
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.
53 while s
and s
[0] in digits
:
59 # Function to check if a string is a number
62 if not s
: return False
64 if not c
in digits
: return False
68 # Allowed function return types
70 return_types
= ['void', 'short', 'long']
73 # Allowed function argument types
75 arg_types
= ['char', 'string', 'short', 'u_short', 'float', 'long', 'double']
78 # Need to classify arguments as follows
79 # simple input variable
80 # simple output variable
83 # input giving size of some array
85 # Array dimensions can be specified as follows
92 # The dimensions given as constants * something are really
93 # arrays of points where points are 2- 3- or 4-tuples
95 # We have to consider three lists:
96 # python input arguments
97 # C stub arguments (in & out)
98 # python output arguments (really return values)
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
105 # Exception raised by checkarg() and generate()
107 arg_error
= 'bad arg'
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.
115 def checkarg(type, arg
):
117 # Turn "char *x" into "string x".
119 if type == 'char' and arg
[0] == '*':
123 # Check that the type is supported.
125 if type not in arg_types
:
126 raise arg_error
, ('bad type', type)
128 type = 'unsigned ' + type[2:]
130 # Split it in the mode (first character) and the rest.
132 mode
, rest
= arg
[:1], arg
[1:]
134 # The mode must be 's' for send (= input) or 'r' for return argument.
136 if mode
not in ('r', 's'):
137 raise arg_error
, ('bad arg mode', mode
)
139 # Is it a simple argument: if so, we are done.
142 return type, mode
, '', ''
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):
154 if rest
[:1] <> '[' or rest
[-1:] <> ']':
155 raise arg_error
, ('subscript expected', rest
)
158 # Is there a leading number?
160 num
, sub
= getnum(sub
)
162 # There is a leading number
164 # The subscript is just a number
165 return type, mode
, num
, ''
167 # There is a factor prefix
170 raise arg_error
, ('\'*\' expected', sub
)
172 # size is retval -- must be a reply argument
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
)
178 return type, mode
, num
, sub
181 # List of functions for which we have generated stubs
186 # Generate the stub for the given function, using the database of argument
187 # information build by successive calls to checkarg()
189 def generate(type, func
, database
):
191 # Check that we can handle this case:
192 # no variable size reply arrays yet
197 for a_type
, a_mode
, a_factor
, a_sub
in database
:
199 n_in_args
= n_in_args
+ 1
201 n_out_args
= n_out_args
+ 1
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 */'
211 functions
.append(func
)
216 print 'static PyObject *'
217 print 'gl_' + func
+ '(self, args)'
218 print '\tPyObject *self;'
219 print '\tPyObject *args;'
222 # Declare return value if any
225 print '\t' + type, 'retval;'
229 for i
in range(len(database
)):
230 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
233 if a_sub
and not isnum(a_sub
):
238 print 'arg' + repr(i
+1) + ket
,
239 if a_sub
and isnum(a_sub
):
240 print '[', a_sub
, ']',
242 print '[', a_factor
, ']',
245 # Find input arguments derived from array sizes
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
252 if 1 <= n
<= len(database
):
253 b_type
, b_mode
, b_factor
, b_sub
= database
[n
-1]
255 database
[n
-1] = b_type
, 'i', a_factor
, repr(i
)
256 n_in_args
= n_in_args
- 1
258 # Assign argument positions in the Python argument list
262 for i
in range(len(database
)):
263 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
270 # Get input arguments
272 for i
in range(len(database
)):
273 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
274 if a_type
[:9] == 'unsigned ':
281 # a_factor is divisor if present,
282 # a_sub indicates which arg (`database index`)
286 print '(!geti' + xtype
+ 'arraysize(args,',
287 print repr(n_in_args
) + ',',
288 print repr(in_pos
[j
]) + ',',
290 print '('+xtype
+' *)',
291 print '&arg' + repr(i
+1) + '))'
292 print '\t\treturn NULL;'
294 print '\targ' + repr(i
+1),
295 print '= arg' + repr(i
+1),
296 print '/', a_factor
+ ';'
298 if a_sub
and not isnum(a_sub
):
299 # Allocate memory for varsize array
300 print '\tif ((arg' + repr(i
+1), '=',
302 print '('+a_type
+'(*)['+a_factor
+'])',
303 print 'PyMem_NEW(' + a_type
, ',',
306 print a_sub
, ')) == NULL)'
307 print '\t\treturn PyErr_NoMemory();'
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
,
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
]) + ',',
325 print '('+xtype
+' *)',
326 print '&arg' + repr(i
+1) + '))'
327 print '\t\treturn NULL;'
329 # Begin of function call
332 print '\tretval =', func
+ '(',
334 print '\t' + func
+ '(',
338 for i
in range(len(database
)):
340 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
341 if a_mode
== 'r' and not a_factor
:
343 print 'arg' + repr(i
+1),
345 # End of function call
349 # Free varsize arrays
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) + ');'
360 # Multiple return values -- construct a tuple
363 n_out_args
= n_out_args
+ 1
365 for i
in range(len(database
)):
366 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
370 raise arg_error
, 'expected r arg not found'
372 print mkobject(a_type
, 'arg' + repr(i
+1)) + ';'
374 print '\t{ PyObject *v = PyTuple_New(',
375 print n_out_args
, ');'
376 print '\t if (v == NULL) return NULL;'
379 print '\t PyTuple_SetItem(v,',
380 print repr(i_out
) + ',',
381 print mkobject(type, 'retval') + ');'
383 for i
in range(len(database
)):
384 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
386 print '\t PyTuple_SetItem(v,',
387 print repr(i_out
) + ',',
388 s
= mkobject(a_type
, 'arg' + repr(i
+1))
395 # Simple function return
396 # Return None or return value
399 print '\tPy_INCREF(Py_None);'
400 print '\treturn Py_None;'
402 print '\treturn', mkobject(type, 'retval') + ';'
404 # Stub body closing brace
409 # Subroutine to return a function call to mknew<type>object(<arg>)
411 def mkobject(type, arg
):
412 if type[:9] == 'unsigned ':
414 return 'mknew' + type + 'object((' + type + ') ' + arg
+ ')'
415 return 'mknew' + type + 'object(' + arg
+ ')'
420 # usage: cgen [ -Dmach ... ] [ file ]
421 for arg
in sys
.argv
[1:]:
423 defined_archs
.append(arg
[2:])
425 # Open optional file argument
426 sys
.stdin
= open(arg
, 'r')
433 # Input is divided in two parts, separated by a line containing '%%'.
434 # <part1> -- literally copied to stdout
435 # <part2> -- stub definitions
437 # Variable indicating the current input part.
441 # Main loop over the input
450 words
= string
.split(line
)
454 # In part 1, copy everything literally
455 # except look for a line of just '%%'
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_'.
466 if words
and words
[0][0] == '%':
468 if (not func
) and words
[1:]:
471 functions
.append(func
)
476 continue # skip empty line
477 elif words
[0] == 'if':
480 if words
[1][0] == '!':
481 if words
[1][1:] in defined_archs
:
483 elif words
[1] not in defined_archs
:
486 if words
[0] == '#include':
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])
493 err('Line', lno
, ': no funcname :', line
)
495 if len(words
) % 2 <> 0:
496 err('Line', lno
, ': odd argument list :', words
[2:])
500 for i
in range(2, len(words
), 2):
501 x
= checkarg(words
[i
], words
[i
+1])
505 for w
in words
: print w
,
507 generate(words
[0], words
[1], database
)
508 except arg_error
, msg
:
509 err('Line', lno
, ':', msg
)
513 print 'static struct PyMethodDef gl_methods[] = {'
514 for func
in functions
:
515 print '\t{"' + func
+ '", gl_' + func
+ '},'
516 print '\t{NULL, NULL} /* Sentinel */'
522 print '\t(void) Py_InitModule("gl", gl_methods);'