blob: f07d9843bd866a510ef7a48fc54006673d4ba5c8 [file] [log] [blame]
Guido van Rossumb6775db1994-08-01 11:34:53 +00001########################################################################
Guido van Rossumef4bb5f2000-07-01 00:16:13 +00002# 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.
Tim Peters182b5ac2004-07-18 06:16:08 +00006#
Guido van Rossumef4bb5f2000-07-01 00:16:13 +00007# See the file "Misc/COPYRIGHT" for information on usage and
8# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
Guido van Rossumb6775db1994-08-01 11:34:53 +00009########################################################################
10
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000011# Python script to parse cstubs file for gl and generate C stubs.
Guido van Rossum670690e1991-08-16 08:57:40 +000012# usage: python cgen.py <cstubs >glmodule.c
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000013#
Guido van Rossum1242f1d1991-10-20 20:28:02 +000014# NOTE: You must first make a python binary without the "GL" option
Tim Peters182b5ac2004-07-18 06:16:08 +000015# before you can run this, when building Python for the first time.
16# See comments in the Makefile.
Guido van Rossum1242f1d1991-10-20 20:28:02 +000017#
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000018# XXX BUG return arrays generate wrong code
19# XXX need to change error returns into gotos to free mallocked arrays
20
21
22import string
23import sys
24
25
26# Function to print to stderr
27#
Guido van Rossum064a62b1995-07-07 22:39:14 +000028def err(*args):
Tim Peters182b5ac2004-07-18 06:16:08 +000029 savestdout = sys.stdout
30 try:
31 sys.stdout = sys.stderr
32 for i in args:
33 print i,
34 print
35 finally:
36 sys.stdout = savestdout
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000037
38
39# The set of digits that form a number
40#
41digits = '0123456789'
42
43
44# Function to extract a string of digits from the front of the string.
45# Returns the leading string of digits and the remaining string.
46# If no number is found, returns '' and the original string.
47#
48def getnum(s):
Tim Peters182b5ac2004-07-18 06:16:08 +000049 n = ''
50 while s and s[0] in digits:
51 n = n + s[0]
52 s = s[1:]
53 return n, s
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000054
55
56# Function to check if a string is a number
57#
58def isnum(s):
Tim Peters182b5ac2004-07-18 06:16:08 +000059 if not s: return False
60 for c in s:
61 if not c in digits: return False
62 return True
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000063
64
65# Allowed function return types
66#
67return_types = ['void', 'short', 'long']
68
69
70# Allowed function argument types
71#
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +000072arg_types = ['char', 'string', 'short', 'u_short', 'float', 'long', 'double']
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000073
74
75# Need to classify arguments as follows
Tim Peters182b5ac2004-07-18 06:16:08 +000076# simple input variable
77# simple output variable
78# input array
79# output array
80# input giving size of some array
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000081#
82# Array dimensions can be specified as follows
Tim Peters182b5ac2004-07-18 06:16:08 +000083# constant
84# argN
85# constant * argN
86# retval
87# constant * retval
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000088#
89# The dimensions given as constants * something are really
90# arrays of points where points are 2- 3- or 4-tuples
91#
92# We have to consider three lists:
Tim Peters182b5ac2004-07-18 06:16:08 +000093# python input arguments
94# C stub arguments (in & out)
95# python output arguments (really return values)
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000096#
97# There is a mapping from python input arguments to the input arguments
98# of the C stub, and a further mapping from C stub arguments to the
99# python return values
100
101
102# Exception raised by checkarg() and generate()
103#
104arg_error = 'bad arg'
105
106
107# Function to check one argument.
108# Arguments: the type and the arg "name" (really mode plus subscript).
109# Raises arg_error if something's wrong.
110# Return type, mode, factor, rest of subscript; factor and rest may be empty.
111#
112def checkarg(type, arg):
Tim Peters182b5ac2004-07-18 06:16:08 +0000113 #
114 # Turn "char *x" into "string x".
115 #
116 if type == 'char' and arg[0] == '*':
117 type = 'string'
118 arg = arg[1:]
119 #
120 # Check that the type is supported.
121 #
122 if type not in arg_types:
123 raise arg_error, ('bad type', type)
124 if type[:2] == 'u_':
125 type = 'unsigned ' + type[2:]
126 #
127 # Split it in the mode (first character) and the rest.
128 #
129 mode, rest = arg[:1], arg[1:]
130 #
131 # The mode must be 's' for send (= input) or 'r' for return argument.
132 #
133 if mode not in ('r', 's'):
134 raise arg_error, ('bad arg mode', mode)
135 #
136 # Is it a simple argument: if so, we are done.
137 #
138 if not rest:
139 return type, mode, '', ''
140 #
141 # Not a simple argument; must be an array.
142 # The 'rest' must be a subscript enclosed in [ and ].
143 # The subscript must be one of the following forms,
144 # otherwise we don't handle it (where N is a number):
145 # N
146 # argN
147 # retval
148 # N*argN
149 # N*retval
150 #
151 if rest[:1] <> '[' or rest[-1:] <> ']':
152 raise arg_error, ('subscript expected', rest)
153 sub = rest[1:-1]
154 #
155 # Is there a leading number?
156 #
157 num, sub = getnum(sub)
158 if num:
159 # There is a leading number
160 if not sub:
161 # The subscript is just a number
162 return type, mode, num, ''
163 if sub[:1] == '*':
164 # There is a factor prefix
165 sub = sub[1:]
166 else:
167 raise arg_error, ('\'*\' expected', sub)
168 if sub == 'retval':
169 # size is retval -- must be a reply argument
170 if mode <> 'r':
171 raise arg_error, ('non-r mode with [retval]', mode)
172 elif not isnum(sub) and (sub[:3] <> 'arg' or not isnum(sub[3:])):
173 raise arg_error, ('bad subscript', sub)
174 #
175 return type, mode, num, sub
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000176
177
178# List of functions for which we have generated stubs
179#
180functions = []
181
182
183# Generate the stub for the given function, using the database of argument
184# information build by successive calls to checkarg()
185#
186def generate(type, func, database):
Tim Peters182b5ac2004-07-18 06:16:08 +0000187 #
188 # Check that we can handle this case:
189 # no variable size reply arrays yet
190 #
191 n_in_args = 0
192 n_out_args = 0
193 #
194 for a_type, a_mode, a_factor, a_sub in database:
195 if a_mode == 's':
196 n_in_args = n_in_args + 1
197 elif a_mode == 'r':
198 n_out_args = n_out_args + 1
199 else:
200 # Can't happen
201 raise arg_error, ('bad a_mode', a_mode)
202 if (a_mode == 'r' and a_sub) or a_sub == 'retval':
203 err('Function', func, 'too complicated:',
204 a_type, a_mode, a_factor, a_sub)
205 print '/* XXX Too complicated to generate code for */'
206 return
207 #
208 functions.append(func)
209 #
210 # Stub header
211 #
212 print
213 print 'static PyObject *'
214 print 'gl_' + func + '(self, args)'
215 print '\tPyObject *self;'
216 print '\tPyObject *args;'
217 print '{'
218 #
219 # Declare return value if any
220 #
221 if type <> 'void':
222 print '\t' + type, 'retval;'
223 #
224 # Declare arguments
225 #
226 for i in range(len(database)):
227 a_type, a_mode, a_factor, a_sub = database[i]
228 print '\t' + a_type,
229 brac = ket = ''
230 if a_sub and not isnum(a_sub):
231 if a_factor:
232 brac = '('
233 ket = ')'
234 print brac + '*',
235 print 'arg' + repr(i+1) + ket,
236 if a_sub and isnum(a_sub):
237 print '[', a_sub, ']',
238 if a_factor:
239 print '[', a_factor, ']',
240 print ';'
241 #
242 # Find input arguments derived from array sizes
243 #
244 for i in range(len(database)):
245 a_type, a_mode, a_factor, a_sub = database[i]
246 if a_mode == 's' and a_sub[:3] == 'arg' and isnum(a_sub[3:]):
247 # Sending a variable-length array
248 n = eval(a_sub[3:])
249 if 1 <= n <= len(database):
250 b_type, b_mode, b_factor, b_sub = database[n-1]
251 if b_mode == 's':
252 database[n-1] = b_type, 'i', a_factor, repr(i)
253 n_in_args = n_in_args - 1
254 #
255 # Assign argument positions in the Python argument list
256 #
257 in_pos = []
258 i_in = 0
259 for i in range(len(database)):
260 a_type, a_mode, a_factor, a_sub = database[i]
261 if a_mode == 's':
262 in_pos.append(i_in)
263 i_in = i_in + 1
264 else:
265 in_pos.append(-1)
266 #
267 # Get input arguments
268 #
269 for i in range(len(database)):
270 a_type, a_mode, a_factor, a_sub = database[i]
271 if a_type[:9] == 'unsigned ':
272 xtype = a_type[9:]
273 else:
274 xtype = a_type
275 if a_mode == 'i':
276 #
277 # Implicit argument;
278 # a_factor is divisor if present,
279 # a_sub indicates which arg (`database index`)
280 #
281 j = eval(a_sub)
282 print '\tif',
283 print '(!geti' + xtype + 'arraysize(args,',
284 print repr(n_in_args) + ',',
285 print repr(in_pos[j]) + ',',
286 if xtype <> a_type:
287 print '('+xtype+' *)',
288 print '&arg' + repr(i+1) + '))'
289 print '\t\treturn NULL;'
290 if a_factor:
291 print '\targ' + repr(i+1),
292 print '= arg' + repr(i+1),
293 print '/', a_factor + ';'
294 elif a_mode == 's':
295 if a_sub and not isnum(a_sub):
296 # Allocate memory for varsize array
297 print '\tif ((arg' + repr(i+1), '=',
298 if a_factor:
299 print '('+a_type+'(*)['+a_factor+'])',
300 print 'PyMem_NEW(' + a_type, ',',
301 if a_factor:
302 print a_factor, '*',
303 print a_sub, ')) == NULL)'
304 print '\t\treturn PyErr_NoMemory();'
305 print '\tif',
306 if a_factor or a_sub: # Get a fixed-size array array
307 print '(!geti' + xtype + 'array(args,',
308 print repr(n_in_args) + ',',
309 print repr(in_pos[i]) + ',',
310 if a_factor: print a_factor,
311 if a_factor and a_sub: print '*',
312 if a_sub: print a_sub,
313 print ',',
314 if (a_sub and a_factor) or xtype <> a_type:
315 print '('+xtype+' *)',
316 print 'arg' + repr(i+1) + '))'
317 else: # Get a simple variable
318 print '(!geti' + xtype + 'arg(args,',
319 print repr(n_in_args) + ',',
320 print repr(in_pos[i]) + ',',
321 if xtype <> a_type:
322 print '('+xtype+' *)',
323 print '&arg' + repr(i+1) + '))'
324 print '\t\treturn NULL;'
325 #
326 # Begin of function call
327 #
328 if type <> 'void':
329 print '\tretval =', func + '(',
330 else:
331 print '\t' + func + '(',
332 #
333 # Argument list
334 #
335 for i in range(len(database)):
336 if i > 0: print ',',
337 a_type, a_mode, a_factor, a_sub = database[i]
338 if a_mode == 'r' and not a_factor:
339 print '&',
340 print 'arg' + repr(i+1),
341 #
342 # End of function call
343 #
344 print ');'
345 #
346 # Free varsize arrays
347 #
348 for i in range(len(database)):
349 a_type, a_mode, a_factor, a_sub = database[i]
350 if a_mode == 's' and a_sub and not isnum(a_sub):
351 print '\tPyMem_DEL(arg' + repr(i+1) + ');'
352 #
353 # Return
354 #
355 if n_out_args:
356 #
357 # Multiple return values -- construct a tuple
358 #
359 if type <> 'void':
360 n_out_args = n_out_args + 1
361 if n_out_args == 1:
362 for i in range(len(database)):
363 a_type, a_mode, a_factor, a_sub = database[i]
364 if a_mode == 'r':
365 break
366 else:
367 raise arg_error, 'expected r arg not found'
368 print '\treturn',
369 print mkobject(a_type, 'arg' + repr(i+1)) + ';'
370 else:
371 print '\t{ PyObject *v = PyTuple_New(',
372 print n_out_args, ');'
373 print '\t if (v == NULL) return NULL;'
374 i_out = 0
375 if type <> 'void':
376 print '\t PyTuple_SetItem(v,',
377 print repr(i_out) + ',',
378 print mkobject(type, 'retval') + ');'
379 i_out = i_out + 1
380 for i in range(len(database)):
381 a_type, a_mode, a_factor, a_sub = database[i]
382 if a_mode == 'r':
383 print '\t PyTuple_SetItem(v,',
384 print repr(i_out) + ',',
385 s = mkobject(a_type, 'arg' + repr(i+1))
386 print s + ');'
387 i_out = i_out + 1
388 print '\t return v;'
389 print '\t}'
390 else:
391 #
392 # Simple function return
393 # Return None or return value
394 #
395 if type == 'void':
396 print '\tPy_INCREF(Py_None);'
397 print '\treturn Py_None;'
398 else:
399 print '\treturn', mkobject(type, 'retval') + ';'
400 #
401 # Stub body closing brace
402 #
403 print '}'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000404
405
406# Subroutine to return a function call to mknew<type>object(<arg>)
407#
408def mkobject(type, arg):
Tim Peters182b5ac2004-07-18 06:16:08 +0000409 if type[:9] == 'unsigned ':
410 type = type[9:]
411 return 'mknew' + type + 'object((' + type + ') ' + arg + ')'
412 return 'mknew' + type + 'object(' + arg + ')'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000413
414
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000415defined_archs = []
416
417# usage: cgen [ -Dmach ... ] [ file ]
418for arg in sys.argv[1:]:
Tim Peters182b5ac2004-07-18 06:16:08 +0000419 if arg[:2] == '-D':
420 defined_archs.append(arg[2:])
421 else:
422 # Open optional file argument
423 sys.stdin = open(arg, 'r')
Guido van Rossum5c850621992-09-11 23:55:51 +0000424
425
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000426# Input line number
427lno = 0
428
429
430# Input is divided in two parts, separated by a line containing '%%'.
Tim Peters182b5ac2004-07-18 06:16:08 +0000431# <part1> -- literally copied to stdout
432# <part2> -- stub definitions
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000433
434# Variable indicating the current input part.
435#
436part = 1
437
438# Main loop over the input
439#
440while 1:
Tim Peters182b5ac2004-07-18 06:16:08 +0000441 try:
442 line = raw_input()
443 except EOFError:
444 break
445 #
446 lno = lno+1
447 words = string.split(line)
448 #
449 if part == 1:
450 #
451 # In part 1, copy everything literally
452 # except look for a line of just '%%'
453 #
454 if words == ['%%']:
455 part = part + 1
456 else:
457 #
458 # Look for names of manually written
459 # stubs: a single percent followed by the name
460 # of the function in Python.
461 # The stub name is derived by prefixing 'gl_'.
462 #
463 if words and words[0][0] == '%':
464 func = words[0][1:]
465 if (not func) and words[1:]:
466 func = words[1]
467 if func:
468 functions.append(func)
469 else:
470 print line
471 continue
472 if not words:
473 continue # skip empty line
474 elif words[0] == 'if':
475 # if XXX rest
476 # if !XXX rest
477 if words[1][0] == '!':
478 if words[1][1:] in defined_archs:
479 continue
480 elif words[1] not in defined_archs:
481 continue
482 words = words[2:]
483 if words[0] == '#include':
484 print line
485 elif words[0][:1] == '#':
486 pass # ignore comment
487 elif words[0] not in return_types:
488 err('Line', lno, ': bad return type :', words[0])
489 elif len(words) < 2:
490 err('Line', lno, ': no funcname :', line)
491 else:
492 if len(words) % 2 <> 0:
493 err('Line', lno, ': odd argument list :', words[2:])
494 else:
495 database = []
496 try:
497 for i in range(2, len(words), 2):
498 x = checkarg(words[i], words[i+1])
499 database.append(x)
500 print
501 print '/*',
502 for w in words: print w,
503 print '*/'
504 generate(words[0], words[1], database)
505 except arg_error, msg:
506 err('Line', lno, ':', msg)
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000507
508
509print
Guido van Rossum0a3eaf01997-04-29 15:39:28 +0000510print 'static struct PyMethodDef gl_methods[] = {'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000511for func in functions:
Tim Peters182b5ac2004-07-18 06:16:08 +0000512 print '\t{"' + func + '", gl_' + func + '},'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000513print '\t{NULL, NULL} /* Sentinel */'
514print '};'
515print
Guido van Rossum7ce52be1996-12-09 18:51:51 +0000516print 'void'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000517print 'initgl()'
518print '{'
Guido van Rossum1ed5e571997-04-29 21:34:16 +0000519print '\t(void) Py_InitModule("gl", gl_methods);'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000520print '}'