blob: 434765b783f7ce73a0df2941e9cdcf938cbfecd0 [file] [log] [blame]
Guido van Rossum85a5fbb1990-10-14 12:07:46 +00001# Python script to parse cstubs file for gl and generate C stubs.
Guido van Rossum670690e1991-08-16 08:57:40 +00002# usage: python cgen.py <cstubs >glmodule.c
Guido van Rossum85a5fbb1990-10-14 12:07:46 +00003#
Guido van Rossum1242f1d1991-10-20 20:28:02 +00004# NOTE: You must first make a python binary without the "GL" option
5# before you can run this, when building Python for the first time.
6# See comments in the Makefile.
7#
Guido van Rossum85a5fbb1990-10-14 12:07:46 +00008# XXX BUG return arrays generate wrong code
9# XXX need to change error returns into gotos to free mallocked arrays
10
11
12import string
13import sys
14
15
16# Function to print to stderr
17#
18def err(args):
19 savestdout = sys.stdout
20 try:
21 sys.stdout = sys.stderr
22 for i in args:
23 print i,
24 print
25 finally:
26 sys.stdout = savestdout
27
28
29# The set of digits that form a number
30#
31digits = '0123456789'
32
33
34# Function to extract a string of digits from the front of the string.
35# Returns the leading string of digits and the remaining string.
36# If no number is found, returns '' and the original string.
37#
38def getnum(s):
39 n = ''
Guido van Rossum3f5da241990-12-20 15:06:42 +000040 while s and s[0] in digits:
41 n = n + s[0]
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000042 s = s[1:]
43 return n, s
44
45
46# Function to check if a string is a number
47#
48def isnum(s):
49 if not s: return 0
50 for c in s:
51 if not c in digits: return 0
52 return 1
53
54
55# Allowed function return types
56#
57return_types = ['void', 'short', 'long']
58
59
60# Allowed function argument types
61#
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +000062arg_types = ['char', 'string', 'short', 'u_short', 'float', 'long', 'double']
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000063
64
65# Need to classify arguments as follows
66# simple input variable
67# simple output variable
68# input array
69# output array
70# input giving size of some array
71#
72# Array dimensions can be specified as follows
73# constant
74# argN
75# constant * argN
76# retval
77# constant * retval
78#
79# The dimensions given as constants * something are really
80# arrays of points where points are 2- 3- or 4-tuples
81#
82# We have to consider three lists:
83# python input arguments
84# C stub arguments (in & out)
85# python output arguments (really return values)
86#
87# There is a mapping from python input arguments to the input arguments
88# of the C stub, and a further mapping from C stub arguments to the
89# python return values
90
91
92# Exception raised by checkarg() and generate()
93#
94arg_error = 'bad arg'
95
96
97# Function to check one argument.
98# Arguments: the type and the arg "name" (really mode plus subscript).
99# Raises arg_error if something's wrong.
100# Return type, mode, factor, rest of subscript; factor and rest may be empty.
101#
102def checkarg(type, arg):
103 #
104 # Turn "char *x" into "string x".
105 #
Guido van Rossum28178751992-01-12 17:18:12 +0000106 if type == 'char' and arg[0] == '*':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000107 type = 'string'
108 arg = arg[1:]
109 #
110 # Check that the type is supported.
111 #
112 if type not in arg_types:
113 raise arg_error, ('bad type', type)
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000114 if type[:2] == 'u_':
115 type = 'unsigned ' + type[2:]
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000116 #
117 # Split it in the mode (first character) and the rest.
118 #
119 mode, rest = arg[:1], arg[1:]
120 #
121 # The mode must be 's' for send (= input) or 'r' for return argument.
122 #
123 if mode not in ('r', 's'):
124 raise arg_error, ('bad arg mode', mode)
125 #
126 # Is it a simple argument: if so, we are done.
127 #
128 if not rest:
129 return type, mode, '', ''
130 #
131 # Not a simple argument; must be an array.
132 # The 'rest' must be a subscript enclosed in [ and ].
133 # The subscript must be one of the following forms,
134 # otherwise we don't handle it (where N is a number):
135 # N
136 # argN
137 # retval
138 # N*argN
139 # N*retval
140 #
141 if rest[:1] <> '[' or rest[-1:] <> ']':
142 raise arg_error, ('subscript expected', rest)
143 sub = rest[1:-1]
144 #
145 # Is there a leading number?
146 #
147 num, sub = getnum(sub)
148 if num:
149 # There is a leading number
150 if not sub:
151 # The subscript is just a number
152 return type, mode, num, ''
Guido van Rossum28178751992-01-12 17:18:12 +0000153 if sub[:1] == '*':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000154 # There is a factor prefix
155 sub = sub[1:]
156 else:
157 raise arg_error, ('\'*\' expected', sub)
Guido van Rossum28178751992-01-12 17:18:12 +0000158 if sub == 'retval':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000159 # size is retval -- must be a reply argument
160 if mode <> 'r':
161 raise arg_error, ('non-r mode with [retval]', mode)
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000162 elif not isnum(sub) and (sub[:3] <> 'arg' or not isnum(sub[3:])):
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000163 raise arg_error, ('bad subscript', sub)
164 #
165 return type, mode, num, sub
166
167
168# List of functions for which we have generated stubs
169#
170functions = []
171
172
173# Generate the stub for the given function, using the database of argument
174# information build by successive calls to checkarg()
175#
176def generate(type, func, database):
177 #
178 # Check that we can handle this case:
179 # no variable size reply arrays yet
180 #
181 n_in_args = 0
182 n_out_args = 0
183 #
184 for a_type, a_mode, a_factor, a_sub in database:
Guido van Rossum28178751992-01-12 17:18:12 +0000185 if a_mode == 's':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000186 n_in_args = n_in_args + 1
Guido van Rossum28178751992-01-12 17:18:12 +0000187 elif a_mode == 'r':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000188 n_out_args = n_out_args + 1
189 else:
190 # Can't happen
191 raise arg_error, ('bad a_mode', a_mode)
Guido van Rossum28178751992-01-12 17:18:12 +0000192 if (a_mode == 'r' and a_sub) or a_sub == 'retval':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000193 e = 'Function', func, 'too complicated:'
194 err(e + (a_type, a_mode, a_factor, a_sub))
195 print '/* XXX Too complicated to generate code for */'
196 return
197 #
198 functions.append(func)
199 #
200 # Stub header
201 #
202 print
203 print 'static object *'
204 print 'gl_' + func + '(self, args)'
205 print '\tobject *self;'
206 print '\tobject *args;'
207 print '{'
208 #
209 # Declare return value if any
210 #
211 if type <> 'void':
212 print '\t' + type, 'retval;'
213 #
214 # Declare arguments
215 #
216 for i in range(len(database)):
217 a_type, a_mode, a_factor, a_sub = database[i]
218 print '\t' + a_type,
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000219 brac = ket = ''
220 if a_sub and not isnum(a_sub):
221 if a_factor:
222 brac = '('
223 ket = ')'
224 print brac + '*',
225 print 'arg' + `i+1` + ket,
226 if a_sub and isnum(a_sub):
227 print '[', a_sub, ']',
228 if a_factor:
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000229 print '[', a_factor, ']',
230 print ';'
231 #
232 # Find input arguments derived from array sizes
233 #
234 for i in range(len(database)):
235 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000236 if a_mode == 's' and a_sub[:3] == 'arg' and isnum(a_sub[3:]):
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000237 # Sending a variable-length array
238 n = eval(a_sub[3:])
239 if 1 <= n <= len(database):
240 b_type, b_mode, b_factor, b_sub = database[n-1]
Guido van Rossum28178751992-01-12 17:18:12 +0000241 if b_mode == 's':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000242 database[n-1] = b_type, 'i', a_factor, `i`
243 n_in_args = n_in_args - 1
244 #
245 # Assign argument positions in the Python argument list
246 #
247 in_pos = []
248 i_in = 0
249 for i in range(len(database)):
250 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000251 if a_mode == 's':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000252 in_pos.append(i_in)
253 i_in = i_in + 1
254 else:
255 in_pos.append(-1)
256 #
257 # Get input arguments
258 #
259 for i in range(len(database)):
260 a_type, a_mode, a_factor, a_sub = database[i]
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000261 if a_type[:9] == 'unsigned ':
262 xtype = a_type[9:]
263 else:
264 xtype = a_type
Guido van Rossum28178751992-01-12 17:18:12 +0000265 if a_mode == 'i':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000266 #
267 # Implicit argument;
268 # a_factor is divisor if present,
269 # a_sub indicates which arg (`database index`)
270 #
271 j = eval(a_sub)
272 print '\tif',
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000273 print '(!geti' + xtype + 'arraysize(args,',
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000274 print `n_in_args` + ',',
275 print `in_pos[j]` + ',',
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000276 if xtype <> a_type:
277 print '('+xtype+' *)',
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000278 print '&arg' + `i+1` + '))'
279 print '\t\treturn NULL;'
280 if a_factor:
281 print '\targ' + `i+1`,
282 print '= arg' + `i+1`,
283 print '/', a_factor + ';'
Guido van Rossum28178751992-01-12 17:18:12 +0000284 elif a_mode == 's':
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000285 if a_sub and not isnum(a_sub):
286 # Allocate memory for varsize array
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000287 print '\tif ((arg' + `i+1`, '=',
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000288 if a_factor:
289 print '('+a_type+'(*)['+a_factor+'])',
290 print 'NEW(' + a_type, ',',
291 if a_factor:
292 print a_factor, '*',
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000293 print a_sub, ')) == NULL)'
294 print '\t\treturn err_nomem();'
295 print '\tif',
296 if a_factor or a_sub: # Get a fixed-size array array
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000297 print '(!geti' + xtype + 'array(args,',
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000298 print `n_in_args` + ',',
299 print `in_pos[i]` + ',',
300 if a_factor: print a_factor,
301 if a_factor and a_sub: print '*',
302 if a_sub: print a_sub,
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000303 print ',',
304 if (a_sub and a_factor) or xtype <> a_type:
305 print '('+xtype+' *)',
306 print 'arg' + `i+1` + '))'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000307 else: # Get a simple variable
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000308 print '(!geti' + xtype + 'arg(args,',
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000309 print `n_in_args` + ',',
310 print `in_pos[i]` + ',',
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000311 if xtype <> a_type:
312 print '('+xtype+' *)',
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000313 print '&arg' + `i+1` + '))'
314 print '\t\treturn NULL;'
315 #
316 # Begin of function call
317 #
318 if type <> 'void':
319 print '\tretval =', func + '(',
320 else:
321 print '\t' + func + '(',
322 #
323 # Argument list
324 #
325 for i in range(len(database)):
326 if i > 0: print ',',
327 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000328 if a_mode == 'r' and not a_factor:
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000329 print '&',
330 print 'arg' + `i+1`,
331 #
332 # End of function call
333 #
334 print ');'
335 #
336 # Free varsize arrays
337 #
338 for i in range(len(database)):
339 a_type, a_mode, a_factor, a_sub = database[i]
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000340 if a_mode == 's' and a_sub and not isnum(a_sub):
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000341 print '\tDEL(arg' + `i+1` + ');'
342 #
343 # Return
344 #
345 if n_out_args:
346 #
347 # Multiple return values -- construct a tuple
348 #
349 if type <> 'void':
350 n_out_args = n_out_args + 1
Guido van Rossum28178751992-01-12 17:18:12 +0000351 if n_out_args == 1:
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000352 for i in range(len(database)):
353 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000354 if a_mode == 'r':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000355 break
356 else:
357 raise arg_error, 'expected r arg not found'
358 print '\treturn',
359 print mkobject(a_type, 'arg' + `i+1`) + ';'
360 else:
361 print '\t{ object *v = newtupleobject(',
362 print n_out_args, ');'
363 print '\t if (v == NULL) return NULL;'
364 i_out = 0
365 if type <> 'void':
366 print '\t settupleitem(v,',
367 print `i_out` + ',',
368 print mkobject(type, 'retval') + ');'
369 i_out = i_out + 1
370 for i in range(len(database)):
371 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000372 if a_mode == 'r':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000373 print '\t settupleitem(v,',
374 print `i_out` + ',',
375 s = mkobject(a_type, 'arg' + `i+1`)
376 print s + ');'
377 i_out = i_out + 1
378 print '\t return v;'
379 print '\t}'
380 else:
381 #
382 # Simple function return
383 # Return None or return value
384 #
Guido van Rossum28178751992-01-12 17:18:12 +0000385 if type == 'void':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000386 print '\tINCREF(None);'
387 print '\treturn None;'
388 else:
389 print '\treturn', mkobject(type, 'retval') + ';'
390 #
391 # Stub body closing brace
392 #
393 print '}'
394
395
396# Subroutine to return a function call to mknew<type>object(<arg>)
397#
398def mkobject(type, arg):
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000399 if type[:9] == 'unsigned ':
400 type = type[9:]
401 return 'mknew' + type + 'object((' + type + ') ' + arg + ')'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000402 return 'mknew' + type + 'object(' + arg + ')'
403
404
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000405defined_archs = []
406
407# usage: cgen [ -Dmach ... ] [ file ]
408for arg in sys.argv[1:]:
409 if arg[:2] == '-D':
410 defined_archs.append(arg[2:])
411 else:
412 # Open optional file argument
413 sys.stdin = open(arg, 'r')
Guido van Rossum5c850621992-09-11 23:55:51 +0000414
415
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000416# Input line number
417lno = 0
418
419
420# Input is divided in two parts, separated by a line containing '%%'.
421# <part1> -- literally copied to stdout
422# <part2> -- stub definitions
423
424# Variable indicating the current input part.
425#
426part = 1
427
428# Main loop over the input
429#
430while 1:
431 try:
432 line = raw_input()
433 except EOFError:
434 break
435 #
436 lno = lno+1
437 words = string.split(line)
438 #
Guido van Rossum28178751992-01-12 17:18:12 +0000439 if part == 1:
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000440 #
441 # In part 1, copy everything literally
442 # except look for a line of just '%%'
443 #
Guido van Rossum28178751992-01-12 17:18:12 +0000444 if words == ['%%']:
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000445 part = part + 1
446 else:
447 #
448 # Look for names of manually written
449 # stubs: a single percent followed by the name
450 # of the function in Python.
451 # The stub name is derived by prefixing 'gl_'.
452 #
Guido van Rossum28178751992-01-12 17:18:12 +0000453 if words and words[0][0] == '%':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000454 func = words[0][1:]
455 if (not func) and words[1:]:
456 func = words[1]
457 if func:
458 functions.append(func)
459 else:
460 print line
Sjoerd Mullenderc4f169c1993-12-21 17:06:12 +0000461 continue
462 if not words:
463 continue # skip empty line
464 elif words[0] == 'if':
465 # if XXX rest
466 # if !XXX rest
467 if words[1][0] == '!':
468 if words[1][1:] in defined_archs:
469 continue
470 elif words[1] not in defined_archs:
471 continue
472 words = words[2:]
473 if words[0] == '#include':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000474 print line
Guido van Rossum28178751992-01-12 17:18:12 +0000475 elif words[0][:1] == '#':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000476 pass # ignore comment
477 elif words[0] not in return_types:
478 err('Line', lno, ': bad return type :', words[0])
479 elif len(words) < 2:
480 err('Line', lno, ': no funcname :', line)
481 else:
482 if len(words) % 2 <> 0:
483 err('Line', lno, ': odd argument list :', words[2:])
484 else:
485 database = []
486 try:
487 for i in range(2, len(words), 2):
488 x = checkarg(words[i], words[i+1])
489 database.append(x)
490 print
491 print '/*',
492 for w in words: print w,
493 print '*/'
494 generate(words[0], words[1], database)
495 except arg_error, msg:
496 err('Line', lno, ':', msg)
497
498
499print
500print 'static struct methodlist gl_methods[] = {'
501for func in functions:
502 print '\t{"' + func + '", gl_' + func + '},'
503print '\t{NULL, NULL} /* Sentinel */'
504print '};'
505print
506print 'initgl()'
507print '{'
508print '\tinitmodule("gl", gl_methods);'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000509print '}'