blob: c63962b11d87cebf36e5e1222f25011779d8f88e [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
Brett Cannon044616a2008-05-15 02:33:55 +000020from warnings import warnpy3k
21warnpy3k("the cgen module has been removed in Python 3.0", stacklevel=2)
22del warnpy3k
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000023
24
25import string
26import sys
27
28
29# Function to print to stderr
30#
Guido van Rossum064a62b1995-07-07 22:39:14 +000031def err(*args):
Tim Peters182b5ac2004-07-18 06:16:08 +000032 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
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000040
41
42# The set of digits that form a number
43#
44digits = '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#
51def getnum(s):
Tim Peters182b5ac2004-07-18 06:16:08 +000052 n = ''
53 while s and s[0] in digits:
54 n = n + s[0]
55 s = s[1:]
56 return n, s
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000057
58
59# Function to check if a string is a number
60#
61def isnum(s):
Tim Peters182b5ac2004-07-18 06:16:08 +000062 if not s: return False
63 for c in s:
64 if not c in digits: return False
65 return True
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000066
67
68# Allowed function return types
69#
70return_types = ['void', 'short', 'long']
71
72
73# Allowed function argument types
74#
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +000075arg_types = ['char', 'string', 'short', 'u_short', 'float', 'long', 'double']
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000076
77
78# Need to classify arguments as follows
Tim Peters182b5ac2004-07-18 06:16:08 +000079# simple input variable
80# simple output variable
81# input array
82# output array
83# input giving size of some array
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000084#
85# Array dimensions can be specified as follows
Tim Peters182b5ac2004-07-18 06:16:08 +000086# constant
87# argN
88# constant * argN
89# retval
90# constant * retval
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000091#
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:
Tim Peters182b5ac2004-07-18 06:16:08 +000096# python input arguments
97# C stub arguments (in & out)
98# python output arguments (really return values)
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000099#
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#
107arg_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#
115def checkarg(type, arg):
Tim Peters182b5ac2004-07-18 06:16:08 +0000116 #
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
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000179
180
181# List of functions for which we have generated stubs
182#
183functions = []
184
185
186# Generate the stub for the given function, using the database of argument
187# information build by successive calls to checkarg()
188#
189def generate(type, func, database):
Tim Peters182b5ac2004-07-18 06:16:08 +0000190 #
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 '}'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000407
408
409# Subroutine to return a function call to mknew<type>object(<arg>)
410#
411def mkobject(type, arg):
Tim Peters182b5ac2004-07-18 06:16:08 +0000412 if type[:9] == 'unsigned ':
413 type = type[9:]
414 return 'mknew' + type + 'object((' + type + ') ' + arg + ')'
415 return 'mknew' + type + 'object(' + arg + ')'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000416
417
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000418defined_archs = []
419
420# usage: cgen [ -Dmach ... ] [ file ]
421for arg in sys.argv[1:]:
Tim Peters182b5ac2004-07-18 06:16:08 +0000422 if arg[:2] == '-D':
423 defined_archs.append(arg[2:])
424 else:
425 # Open optional file argument
426 sys.stdin = open(arg, 'r')
Guido van Rossum5c850621992-09-11 23:55:51 +0000427
428
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000429# Input line number
430lno = 0
431
432
433# Input is divided in two parts, separated by a line containing '%%'.
Tim Peters182b5ac2004-07-18 06:16:08 +0000434# <part1> -- literally copied to stdout
435# <part2> -- stub definitions
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000436
437# Variable indicating the current input part.
438#
439part = 1
440
441# Main loop over the input
442#
443while 1:
Tim Peters182b5ac2004-07-18 06:16:08 +0000444 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)
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000510
511
512print
Guido van Rossum0a3eaf01997-04-29 15:39:28 +0000513print 'static struct PyMethodDef gl_methods[] = {'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000514for func in functions:
Tim Peters182b5ac2004-07-18 06:16:08 +0000515 print '\t{"' + func + '", gl_' + func + '},'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000516print '\t{NULL, NULL} /* Sentinel */'
517print '};'
518print
Guido van Rossum7ce52be1996-12-09 18:51:51 +0000519print 'void'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000520print 'initgl()'
521print '{'
Guido van Rossum1ed5e571997-04-29 21:34:16 +0000522print '\t(void) Py_InitModule("gl", gl_methods);'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000523print '}'