blob: ea4626365d6265009455206fd7e5991118750261 [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#
62arg_types = ['char', 'string', 'short', 'float', 'long', 'double']
63
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)
114 #
115 # Split it in the mode (first character) and the rest.
116 #
117 mode, rest = arg[:1], arg[1:]
118 #
119 # The mode must be 's' for send (= input) or 'r' for return argument.
120 #
121 if mode not in ('r', 's'):
122 raise arg_error, ('bad arg mode', mode)
123 #
124 # Is it a simple argument: if so, we are done.
125 #
126 if not rest:
127 return type, mode, '', ''
128 #
129 # Not a simple argument; must be an array.
130 # The 'rest' must be a subscript enclosed in [ and ].
131 # The subscript must be one of the following forms,
132 # otherwise we don't handle it (where N is a number):
133 # N
134 # argN
135 # retval
136 # N*argN
137 # N*retval
138 #
139 if rest[:1] <> '[' or rest[-1:] <> ']':
140 raise arg_error, ('subscript expected', rest)
141 sub = rest[1:-1]
142 #
143 # Is there a leading number?
144 #
145 num, sub = getnum(sub)
146 if num:
147 # There is a leading number
148 if not sub:
149 # The subscript is just a number
150 return type, mode, num, ''
Guido van Rossum28178751992-01-12 17:18:12 +0000151 if sub[:1] == '*':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000152 # There is a factor prefix
153 sub = sub[1:]
154 else:
155 raise arg_error, ('\'*\' expected', sub)
Guido van Rossum28178751992-01-12 17:18:12 +0000156 if sub == 'retval':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000157 # size is retval -- must be a reply argument
158 if mode <> 'r':
159 raise arg_error, ('non-r mode with [retval]', mode)
160 elif sub[:3] <> 'arg' or not isnum(sub[3:]):
161 raise arg_error, ('bad subscript', sub)
162 #
163 return type, mode, num, sub
164
165
166# List of functions for which we have generated stubs
167#
168functions = []
169
170
171# Generate the stub for the given function, using the database of argument
172# information build by successive calls to checkarg()
173#
174def generate(type, func, database):
175 #
176 # Check that we can handle this case:
177 # no variable size reply arrays yet
178 #
179 n_in_args = 0
180 n_out_args = 0
181 #
182 for a_type, a_mode, a_factor, a_sub in database:
Guido van Rossum28178751992-01-12 17:18:12 +0000183 if a_mode == 's':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000184 n_in_args = n_in_args + 1
Guido van Rossum28178751992-01-12 17:18:12 +0000185 elif a_mode == 'r':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000186 n_out_args = n_out_args + 1
187 else:
188 # Can't happen
189 raise arg_error, ('bad a_mode', a_mode)
Guido van Rossum28178751992-01-12 17:18:12 +0000190 if (a_mode == 'r' and a_sub) or a_sub == 'retval':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000191 e = 'Function', func, 'too complicated:'
192 err(e + (a_type, a_mode, a_factor, a_sub))
193 print '/* XXX Too complicated to generate code for */'
194 return
195 #
196 functions.append(func)
197 #
198 # Stub header
199 #
200 print
201 print 'static object *'
202 print 'gl_' + func + '(self, args)'
203 print '\tobject *self;'
204 print '\tobject *args;'
205 print '{'
206 #
207 # Declare return value if any
208 #
209 if type <> 'void':
210 print '\t' + type, 'retval;'
211 #
212 # Declare arguments
213 #
214 for i in range(len(database)):
215 a_type, a_mode, a_factor, a_sub = database[i]
216 print '\t' + a_type,
217 if a_sub:
218 print '*',
219 print 'arg' + `i+1`,
220 if a_factor and not a_sub:
221 print '[', a_factor, ']',
222 print ';'
223 #
224 # Find input arguments derived from array sizes
225 #
226 for i in range(len(database)):
227 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000228 if a_mode == 's' and a_sub[:3] == 'arg' and isnum(a_sub[3:]):
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000229 # Sending a variable-length array
230 n = eval(a_sub[3:])
231 if 1 <= n <= len(database):
232 b_type, b_mode, b_factor, b_sub = database[n-1]
Guido van Rossum28178751992-01-12 17:18:12 +0000233 if b_mode == 's':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000234 database[n-1] = b_type, 'i', a_factor, `i`
235 n_in_args = n_in_args - 1
236 #
237 # Assign argument positions in the Python argument list
238 #
239 in_pos = []
240 i_in = 0
241 for i in range(len(database)):
242 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000243 if a_mode == 's':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000244 in_pos.append(i_in)
245 i_in = i_in + 1
246 else:
247 in_pos.append(-1)
248 #
249 # Get input arguments
250 #
251 for i in range(len(database)):
252 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000253 if a_mode == 'i':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000254 #
255 # Implicit argument;
256 # a_factor is divisor if present,
257 # a_sub indicates which arg (`database index`)
258 #
259 j = eval(a_sub)
260 print '\tif',
261 print '(!geti' + a_type + 'arraysize(args,',
262 print `n_in_args` + ',',
263 print `in_pos[j]` + ',',
264 print '&arg' + `i+1` + '))'
265 print '\t\treturn NULL;'
266 if a_factor:
267 print '\targ' + `i+1`,
268 print '= arg' + `i+1`,
269 print '/', a_factor + ';'
Guido van Rossum28178751992-01-12 17:18:12 +0000270 elif a_mode == 's':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000271 if a_sub: # Allocate memory for varsize array
272 print '\tif ((arg' + `i+1`, '=',
273 print 'NEW(' + a_type + ',',
274 if a_factor: print a_factor, '*',
275 print a_sub, ')) == NULL)'
276 print '\t\treturn err_nomem();'
277 print '\tif',
278 if a_factor or a_sub: # Get a fixed-size array array
279 print '(!geti' + a_type + 'array(args,',
280 print `n_in_args` + ',',
281 print `in_pos[i]` + ',',
282 if a_factor: print a_factor,
283 if a_factor and a_sub: print '*',
284 if a_sub: print a_sub,
285 print ', arg' + `i+1` + '))'
286 else: # Get a simple variable
287 print '(!geti' + a_type + 'arg(args,',
288 print `n_in_args` + ',',
289 print `in_pos[i]` + ',',
290 print '&arg' + `i+1` + '))'
291 print '\t\treturn NULL;'
292 #
293 # Begin of function call
294 #
295 if type <> 'void':
296 print '\tretval =', func + '(',
297 else:
298 print '\t' + func + '(',
299 #
300 # Argument list
301 #
302 for i in range(len(database)):
303 if i > 0: print ',',
304 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000305 if a_mode == 'r' and not a_factor:
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000306 print '&',
307 print 'arg' + `i+1`,
308 #
309 # End of function call
310 #
311 print ');'
312 #
313 # Free varsize arrays
314 #
315 for i in range(len(database)):
316 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000317 if a_mode == 's' and a_sub:
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000318 print '\tDEL(arg' + `i+1` + ');'
319 #
320 # Return
321 #
322 if n_out_args:
323 #
324 # Multiple return values -- construct a tuple
325 #
326 if type <> 'void':
327 n_out_args = n_out_args + 1
Guido van Rossum28178751992-01-12 17:18:12 +0000328 if n_out_args == 1:
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000329 for i in range(len(database)):
330 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000331 if a_mode == 'r':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000332 break
333 else:
334 raise arg_error, 'expected r arg not found'
335 print '\treturn',
336 print mkobject(a_type, 'arg' + `i+1`) + ';'
337 else:
338 print '\t{ object *v = newtupleobject(',
339 print n_out_args, ');'
340 print '\t if (v == NULL) return NULL;'
341 i_out = 0
342 if type <> 'void':
343 print '\t settupleitem(v,',
344 print `i_out` + ',',
345 print mkobject(type, 'retval') + ');'
346 i_out = i_out + 1
347 for i in range(len(database)):
348 a_type, a_mode, a_factor, a_sub = database[i]
Guido van Rossum28178751992-01-12 17:18:12 +0000349 if a_mode == 'r':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000350 print '\t settupleitem(v,',
351 print `i_out` + ',',
352 s = mkobject(a_type, 'arg' + `i+1`)
353 print s + ');'
354 i_out = i_out + 1
355 print '\t return v;'
356 print '\t}'
357 else:
358 #
359 # Simple function return
360 # Return None or return value
361 #
Guido van Rossum28178751992-01-12 17:18:12 +0000362 if type == 'void':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000363 print '\tINCREF(None);'
364 print '\treturn None;'
365 else:
366 print '\treturn', mkobject(type, 'retval') + ';'
367 #
368 # Stub body closing brace
369 #
370 print '}'
371
372
373# Subroutine to return a function call to mknew<type>object(<arg>)
374#
375def mkobject(type, arg):
376 return 'mknew' + type + 'object(' + arg + ')'
377
378
Guido van Rossum5c850621992-09-11 23:55:51 +0000379# Open optional file argument
380if sys.argv[1:]:
381 sys.stdin = open(sys.argv[1], 'r')
382
383
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000384# Input line number
385lno = 0
386
387
388# Input is divided in two parts, separated by a line containing '%%'.
389# <part1> -- literally copied to stdout
390# <part2> -- stub definitions
391
392# Variable indicating the current input part.
393#
394part = 1
395
396# Main loop over the input
397#
398while 1:
399 try:
400 line = raw_input()
401 except EOFError:
402 break
403 #
404 lno = lno+1
405 words = string.split(line)
406 #
Guido van Rossum28178751992-01-12 17:18:12 +0000407 if part == 1:
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000408 #
409 # In part 1, copy everything literally
410 # except look for a line of just '%%'
411 #
Guido van Rossum28178751992-01-12 17:18:12 +0000412 if words == ['%%']:
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000413 part = part + 1
414 else:
415 #
416 # Look for names of manually written
417 # stubs: a single percent followed by the name
418 # of the function in Python.
419 # The stub name is derived by prefixing 'gl_'.
420 #
Guido van Rossum28178751992-01-12 17:18:12 +0000421 if words and words[0][0] == '%':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000422 func = words[0][1:]
423 if (not func) and words[1:]:
424 func = words[1]
425 if func:
426 functions.append(func)
427 else:
428 print line
429 elif not words:
430 pass # skip empty line
Guido van Rossum28178751992-01-12 17:18:12 +0000431 elif words[0] == '#include':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000432 print line
Guido van Rossum28178751992-01-12 17:18:12 +0000433 elif words[0][:1] == '#':
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000434 pass # ignore comment
435 elif words[0] not in return_types:
436 err('Line', lno, ': bad return type :', words[0])
437 elif len(words) < 2:
438 err('Line', lno, ': no funcname :', line)
439 else:
440 if len(words) % 2 <> 0:
441 err('Line', lno, ': odd argument list :', words[2:])
442 else:
443 database = []
444 try:
445 for i in range(2, len(words), 2):
446 x = checkarg(words[i], words[i+1])
447 database.append(x)
448 print
449 print '/*',
450 for w in words: print w,
451 print '*/'
452 generate(words[0], words[1], database)
453 except arg_error, msg:
454 err('Line', lno, ':', msg)
455
456
457print
458print 'static struct methodlist gl_methods[] = {'
459for func in functions:
460 print '\t{"' + func + '", gl_' + func + '},'
461print '\t{NULL, NULL} /* Sentinel */'
462print '};'
463print
464print 'initgl()'
465print '{'
466print '\tinitmodule("gl", gl_methods);'
Guido van Rossum85a5fbb1990-10-14 12:07:46 +0000467print '}'