1 ########################################################################
2 # Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
7 # Permission to use, copy, modify, and distribute this software and its
8 # documentation for any purpose and without fee is hereby granted,
9 # provided that the above copyright notice appear in all copies and that
10 # both that copyright notice and this permission notice appear in
11 # supporting documentation, and that the names of Stichting Mathematisch
12 # Centrum or CWI or Corporation for National Research Initiatives or
13 # CNRI not be used in advertising or publicity pertaining to
14 # distribution of the software without specific, written prior
17 # While CWI is the initial source for this software, a modified version
18 # is made available by the Corporation for National Research Initiatives
19 # (CNRI) at the Internet address ftp://ftp.python.org.
21 # STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH
22 # REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
23 # MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH
24 # CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
25 # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
26 # PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
27 # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
28 # PERFORMANCE OF THIS SOFTWARE.
29 ########################################################################
31 # Python script to parse cstubs file for gl and generate C stubs.
32 # usage: python cgen.py <cstubs >glmodule.c
34 # NOTE: You must first make a python binary without the "GL" option
35 # before you can run this, when building Python for the first time.
36 # See comments in the Makefile.
38 # XXX BUG return arrays generate wrong code
39 # XXX need to change error returns into gotos to free mallocked arrays
46 # Function to print to stderr
49 savestdout
= sys
.stdout
51 sys
.stdout
= sys
.stderr
56 sys
.stdout
= savestdout
59 # The set of digits that form a number
64 # Function to extract a string of digits from the front of the string.
65 # Returns the leading string of digits and the remaining string.
66 # If no number is found, returns '' and the original string.
70 while s
and s
[0] in digits
:
76 # Function to check if a string is a number
81 if not c
in digits
: return 0
85 # Allowed function return types
87 return_types
= ['void', 'short', 'long']
90 # Allowed function argument types
92 arg_types
= ['char', 'string', 'short', 'u_short', 'float', 'long', 'double']
95 # Need to classify arguments as follows
96 # simple input variable
97 # simple output variable
100 # input giving size of some array
102 # Array dimensions can be specified as follows
109 # The dimensions given as constants * something are really
110 # arrays of points where points are 2- 3- or 4-tuples
112 # We have to consider three lists:
113 # python input arguments
114 # C stub arguments (in & out)
115 # python output arguments (really return values)
117 # There is a mapping from python input arguments to the input arguments
118 # of the C stub, and a further mapping from C stub arguments to the
119 # python return values
122 # Exception raised by checkarg() and generate()
124 arg_error
= 'bad arg'
127 # Function to check one argument.
128 # Arguments: the type and the arg "name" (really mode plus subscript).
129 # Raises arg_error if something's wrong.
130 # Return type, mode, factor, rest of subscript; factor and rest may be empty.
132 def checkarg(type, arg
):
134 # Turn "char *x" into "string x".
136 if type == 'char' and arg
[0] == '*':
140 # Check that the type is supported.
142 if type not in arg_types
:
143 raise arg_error
, ('bad type', type)
145 type = 'unsigned ' + type[2:]
147 # Split it in the mode (first character) and the rest.
149 mode
, rest
= arg
[:1], arg
[1:]
151 # The mode must be 's' for send (= input) or 'r' for return argument.
153 if mode
not in ('r', 's'):
154 raise arg_error
, ('bad arg mode', mode
)
156 # Is it a simple argument: if so, we are done.
159 return type, mode
, '', ''
161 # Not a simple argument; must be an array.
162 # The 'rest' must be a subscript enclosed in [ and ].
163 # The subscript must be one of the following forms,
164 # otherwise we don't handle it (where N is a number):
171 if rest
[:1] <> '[' or rest
[-1:] <> ']':
172 raise arg_error
, ('subscript expected', rest
)
175 # Is there a leading number?
177 num
, sub
= getnum(sub
)
179 # There is a leading number
181 # The subscript is just a number
182 return type, mode
, num
, ''
184 # There is a factor prefix
187 raise arg_error
, ('\'*\' expected', sub
)
189 # size is retval -- must be a reply argument
191 raise arg_error
, ('non-r mode with [retval]', mode
)
192 elif not isnum(sub
) and (sub
[:3] <> 'arg' or not isnum(sub
[3:])):
193 raise arg_error
, ('bad subscript', sub
)
195 return type, mode
, num
, sub
198 # List of functions for which we have generated stubs
203 # Generate the stub for the given function, using the database of argument
204 # information build by successive calls to checkarg()
206 def generate(type, func
, database
):
208 # Check that we can handle this case:
209 # no variable size reply arrays yet
214 for a_type
, a_mode
, a_factor
, a_sub
in database
:
216 n_in_args
= n_in_args
+ 1
218 n_out_args
= n_out_args
+ 1
221 raise arg_error
, ('bad a_mode', a_mode
)
222 if (a_mode
== 'r' and a_sub
) or a_sub
== 'retval':
223 err('Function', func
, 'too complicated:',
224 a_type
, a_mode
, a_factor
, a_sub
)
225 print '/* XXX Too complicated to generate code for */'
228 functions
.append(func
)
233 print 'static PyObject *'
234 print 'gl_' + func
+ '(self, args)'
235 print '\tPyObject *self;'
236 print '\tPyObject *args;'
239 # Declare return value if any
242 print '\t' + type, 'retval;'
246 for i
in range(len(database
)):
247 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
250 if a_sub
and not isnum(a_sub
):
255 print 'arg' + `i
+1`
+ ket
,
256 if a_sub
and isnum(a_sub
):
257 print '[', a_sub
, ']',
259 print '[', a_factor
, ']',
262 # Find input arguments derived from array sizes
264 for i
in range(len(database
)):
265 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
266 if a_mode
== 's' and a_sub
[:3] == 'arg' and isnum(a_sub
[3:]):
267 # Sending a variable-length array
269 if 1 <= n
<= len(database
):
270 b_type
, b_mode
, b_factor
, b_sub
= database
[n
-1]
272 database
[n
-1] = b_type
, 'i', a_factor
, `i`
273 n_in_args
= n_in_args
- 1
275 # Assign argument positions in the Python argument list
279 for i
in range(len(database
)):
280 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
287 # Get input arguments
289 for i
in range(len(database
)):
290 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
291 if a_type
[:9] == 'unsigned ':
298 # a_factor is divisor if present,
299 # a_sub indicates which arg (`database index`)
303 print '(!geti' + xtype
+ 'arraysize(args,',
304 print `n_in_args`
+ ',',
305 print `in_pos
[j
]`
+ ',',
307 print '('+xtype
+' *)',
308 print '&arg' + `i
+1`
+ '))'
309 print '\t\treturn NULL;'
311 print '\targ' + `i
+1`
,
312 print '= arg' + `i
+1`
,
313 print '/', a_factor
+ ';'
315 if a_sub
and not isnum(a_sub
):
316 # Allocate memory for varsize array
317 print '\tif ((arg' + `i
+1`
, '=',
319 print '('+a_type
+'(*)['+a_factor
+'])',
320 print 'PyMem_NEW(' + a_type
, ',',
323 print a_sub
, ')) == NULL)'
324 print '\t\treturn PyErr_NoMemory();'
326 if a_factor
or a_sub
: # Get a fixed-size array array
327 print '(!geti' + xtype
+ 'array(args,',
328 print `n_in_args`
+ ',',
329 print `in_pos
[i
]`
+ ',',
330 if a_factor
: print a_factor
,
331 if a_factor
and a_sub
: print '*',
332 if a_sub
: print a_sub
,
334 if (a_sub
and a_factor
) or xtype
<> a_type
:
335 print '('+xtype
+' *)',
336 print 'arg' + `i
+1`
+ '))'
337 else: # Get a simple variable
338 print '(!geti' + xtype
+ 'arg(args,',
339 print `n_in_args`
+ ',',
340 print `in_pos
[i
]`
+ ',',
342 print '('+xtype
+' *)',
343 print '&arg' + `i
+1`
+ '))'
344 print '\t\treturn NULL;'
346 # Begin of function call
349 print '\tretval =', func
+ '(',
351 print '\t' + func
+ '(',
355 for i
in range(len(database
)):
357 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
358 if a_mode
== 'r' and not a_factor
:
362 # End of function call
366 # Free varsize arrays
368 for i
in range(len(database
)):
369 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
370 if a_mode
== 's' and a_sub
and not isnum(a_sub
):
371 print '\tPyMem_DEL(arg' + `i
+1`
+ ');'
377 # Multiple return values -- construct a tuple
380 n_out_args
= n_out_args
+ 1
382 for i
in range(len(database
)):
383 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
387 raise arg_error
, 'expected r arg not found'
389 print mkobject(a_type
, 'arg' + `i
+1`
) + ';'
391 print '\t{ PyObject *v = PyTuple_New(',
392 print n_out_args
, ');'
393 print '\t if (v == NULL) return NULL;'
396 print '\t PyTuple_SetItem(v,',
398 print mkobject(type, 'retval') + ');'
400 for i
in range(len(database
)):
401 a_type
, a_mode
, a_factor
, a_sub
= database
[i
]
403 print '\t PyTuple_SetItem(v,',
405 s
= mkobject(a_type
, 'arg' + `i
+1`
)
412 # Simple function return
413 # Return None or return value
416 print '\tPy_INCREF(Py_None);'
417 print '\treturn Py_None;'
419 print '\treturn', mkobject(type, 'retval') + ';'
421 # Stub body closing brace
426 # Subroutine to return a function call to mknew<type>object(<arg>)
428 def mkobject(type, arg
):
429 if type[:9] == 'unsigned ':
431 return 'mknew' + type + 'object((' + type + ') ' + arg
+ ')'
432 return 'mknew' + type + 'object(' + arg
+ ')'
437 # usage: cgen [ -Dmach ... ] [ file ]
438 for arg
in sys
.argv
[1:]:
440 defined_archs
.append(arg
[2:])
442 # Open optional file argument
443 sys
.stdin
= open(arg
, 'r')
450 # Input is divided in two parts, separated by a line containing '%%'.
451 # <part1> -- literally copied to stdout
452 # <part2> -- stub definitions
454 # Variable indicating the current input part.
458 # Main loop over the input
467 words
= string
.split(line
)
471 # In part 1, copy everything literally
472 # except look for a line of just '%%'
478 # Look for names of manually written
479 # stubs: a single percent followed by the name
480 # of the function in Python.
481 # The stub name is derived by prefixing 'gl_'.
483 if words
and words
[0][0] == '%':
485 if (not func
) and words
[1:]:
488 functions
.append(func
)
493 continue # skip empty line
494 elif words
[0] == 'if':
497 if words
[1][0] == '!':
498 if words
[1][1:] in defined_archs
:
500 elif words
[1] not in defined_archs
:
503 if words
[0] == '#include':
505 elif words
[0][:1] == '#':
506 pass # ignore comment
507 elif words
[0] not in return_types
:
508 err('Line', lno
, ': bad return type :', words
[0])
510 err('Line', lno
, ': no funcname :', line
)
512 if len(words
) % 2 <> 0:
513 err('Line', lno
, ': odd argument list :', words
[2:])
517 for i
in range(2, len(words
), 2):
518 x
= checkarg(words
[i
], words
[i
+1])
522 for w
in words
: print w
,
524 generate(words
[0], words
[1], database
)
525 except arg_error
, msg
:
526 err('Line', lno
, ':', msg
)
530 print 'static struct PyMethodDef gl_methods[] = {'
531 for func
in functions
:
532 print '\t{"' + func
+ '", gl_' + func
+ '},'
533 print '\t{NULL, NULL} /* Sentinel */'
539 print '\t(void) Py_InitModule("gl", gl_methods);'