blob: 1e345d1cec4a03a9b7c665b72b3a8cb5ed5c7055 [file] [log] [blame]
Guido van Rossum85a5fbb1990-10-14 12:07:46 +00001# Python script to parse cstubs file for gl and generate C stubs.
2# usage: python cgen <cstubs >glmodule.c
3#
4# XXX BUG return arrays generate wrong code
5# XXX need to change error returns into gotos to free mallocked arrays
6
7
8import string
9import sys
10
11
12# Function to print to stderr
13#
14def err(args):
15 savestdout = sys.stdout
16 try:
17 sys.stdout = sys.stderr
18 for i in args:
19 print i,
20 print
21 finally:
22 sys.stdout = savestdout
23
24
25# The set of digits that form a number
26#
27digits = '0123456789'
28
29
30# Function to extract a string of digits from the front of the string.
31# Returns the leading string of digits and the remaining string.
32# If no number is found, returns '' and the original string.
33#
34def getnum(s):
35 n = ''
Guido van Rossum3f5da241990-12-20 15:06:42 +000036 while s and s[0] in digits:
37 n = n + s[0]
Guido van Rossum85a5fbb1990-10-14 12:07:46 +000038 s = s[1:]
39 return n, s
40
41
42# Function to check if a string is a number
43#
44def isnum(s):
45 if not s: return 0
46 for c in s:
47 if not c in digits: return 0
48 return 1
49
50
51# Allowed function return types
52#
53return_types = ['void', 'short', 'long']
54
55
56# Allowed function argument types
57#
58arg_types = ['char', 'string', 'short', 'float', 'long', 'double']
59
60
61# Need to classify arguments as follows
62# simple input variable
63# simple output variable
64# input array
65# output array
66# input giving size of some array
67#
68# Array dimensions can be specified as follows
69# constant
70# argN
71# constant * argN
72# retval
73# constant * retval
74#
75# The dimensions given as constants * something are really
76# arrays of points where points are 2- 3- or 4-tuples
77#
78# We have to consider three lists:
79# python input arguments
80# C stub arguments (in & out)
81# python output arguments (really return values)
82#
83# There is a mapping from python input arguments to the input arguments
84# of the C stub, and a further mapping from C stub arguments to the
85# python return values
86
87
88# Exception raised by checkarg() and generate()
89#
90arg_error = 'bad arg'
91
92
93# Function to check one argument.
94# Arguments: the type and the arg "name" (really mode plus subscript).
95# Raises arg_error if something's wrong.
96# Return type, mode, factor, rest of subscript; factor and rest may be empty.
97#
98def checkarg(type, arg):
99 #
100 # Turn "char *x" into "string x".
101 #
102 if type = 'char' and arg[0] = '*':
103 type = 'string'
104 arg = arg[1:]
105 #
106 # Check that the type is supported.
107 #
108 if type not in arg_types:
109 raise arg_error, ('bad type', type)
110 #
111 # Split it in the mode (first character) and the rest.
112 #
113 mode, rest = arg[:1], arg[1:]
114 #
115 # The mode must be 's' for send (= input) or 'r' for return argument.
116 #
117 if mode not in ('r', 's'):
118 raise arg_error, ('bad arg mode', mode)
119 #
120 # Is it a simple argument: if so, we are done.
121 #
122 if not rest:
123 return type, mode, '', ''
124 #
125 # Not a simple argument; must be an array.
126 # The 'rest' must be a subscript enclosed in [ and ].
127 # The subscript must be one of the following forms,
128 # otherwise we don't handle it (where N is a number):
129 # N
130 # argN
131 # retval
132 # N*argN
133 # N*retval
134 #
135 if rest[:1] <> '[' or rest[-1:] <> ']':
136 raise arg_error, ('subscript expected', rest)
137 sub = rest[1:-1]
138 #
139 # Is there a leading number?
140 #
141 num, sub = getnum(sub)
142 if num:
143 # There is a leading number
144 if not sub:
145 # The subscript is just a number
146 return type, mode, num, ''
147 if sub[:1] = '*':
148 # There is a factor prefix
149 sub = sub[1:]
150 else:
151 raise arg_error, ('\'*\' expected', sub)
152 if sub = 'retval':
153 # size is retval -- must be a reply argument
154 if mode <> 'r':
155 raise arg_error, ('non-r mode with [retval]', mode)
156 elif sub[:3] <> 'arg' or not isnum(sub[3:]):
157 raise arg_error, ('bad subscript', sub)
158 #
159 return type, mode, num, sub
160
161
162# List of functions for which we have generated stubs
163#
164functions = []
165
166
167# Generate the stub for the given function, using the database of argument
168# information build by successive calls to checkarg()
169#
170def generate(type, func, database):
171 #
172 # Check that we can handle this case:
173 # no variable size reply arrays yet
174 #
175 n_in_args = 0
176 n_out_args = 0
177 #
178 for a_type, a_mode, a_factor, a_sub in database:
179 if a_mode = 's':
180 n_in_args = n_in_args + 1
181 elif a_mode = 'r':
182 n_out_args = n_out_args + 1
183 else:
184 # Can't happen
185 raise arg_error, ('bad a_mode', a_mode)
186 if (a_mode = 'r' and a_sub) or a_sub = 'retval':
187 e = 'Function', func, 'too complicated:'
188 err(e + (a_type, a_mode, a_factor, a_sub))
189 print '/* XXX Too complicated to generate code for */'
190 return
191 #
192 functions.append(func)
193 #
194 # Stub header
195 #
196 print
197 print 'static object *'
198 print 'gl_' + func + '(self, args)'
199 print '\tobject *self;'
200 print '\tobject *args;'
201 print '{'
202 #
203 # Declare return value if any
204 #
205 if type <> 'void':
206 print '\t' + type, 'retval;'
207 #
208 # Declare arguments
209 #
210 for i in range(len(database)):
211 a_type, a_mode, a_factor, a_sub = database[i]
212 print '\t' + a_type,
213 if a_sub:
214 print '*',
215 print 'arg' + `i+1`,
216 if a_factor and not a_sub:
217 print '[', a_factor, ']',
218 print ';'
219 #
220 # Find input arguments derived from array sizes
221 #
222 for i in range(len(database)):
223 a_type, a_mode, a_factor, a_sub = database[i]
224 if a_mode = 's' and a_sub[:3] = 'arg' and isnum(a_sub[3:]):
225 # Sending a variable-length array
226 n = eval(a_sub[3:])
227 if 1 <= n <= len(database):
228 b_type, b_mode, b_factor, b_sub = database[n-1]
229 if b_mode = 's':
230 database[n-1] = b_type, 'i', a_factor, `i`
231 n_in_args = n_in_args - 1
232 #
233 # Assign argument positions in the Python argument list
234 #
235 in_pos = []
236 i_in = 0
237 for i in range(len(database)):
238 a_type, a_mode, a_factor, a_sub = database[i]
239 if a_mode = 's':
240 in_pos.append(i_in)
241 i_in = i_in + 1
242 else:
243 in_pos.append(-1)
244 #
245 # Get input arguments
246 #
247 for i in range(len(database)):
248 a_type, a_mode, a_factor, a_sub = database[i]
249 if a_mode = 'i':
250 #
251 # Implicit argument;
252 # a_factor is divisor if present,
253 # a_sub indicates which arg (`database index`)
254 #
255 j = eval(a_sub)
256 print '\tif',
257 print '(!geti' + a_type + 'arraysize(args,',
258 print `n_in_args` + ',',
259 print `in_pos[j]` + ',',
260 print '&arg' + `i+1` + '))'
261 print '\t\treturn NULL;'
262 if a_factor:
263 print '\targ' + `i+1`,
264 print '= arg' + `i+1`,
265 print '/', a_factor + ';'
266 elif a_mode = 's':
267 if a_sub: # Allocate memory for varsize array
268 print '\tif ((arg' + `i+1`, '=',
269 print 'NEW(' + a_type + ',',
270 if a_factor: print a_factor, '*',
271 print a_sub, ')) == NULL)'
272 print '\t\treturn err_nomem();'
273 print '\tif',
274 if a_factor or a_sub: # Get a fixed-size array array
275 print '(!geti' + a_type + 'array(args,',
276 print `n_in_args` + ',',
277 print `in_pos[i]` + ',',
278 if a_factor: print a_factor,
279 if a_factor and a_sub: print '*',
280 if a_sub: print a_sub,
281 print ', arg' + `i+1` + '))'
282 else: # Get a simple variable
283 print '(!geti' + a_type + 'arg(args,',
284 print `n_in_args` + ',',
285 print `in_pos[i]` + ',',
286 print '&arg' + `i+1` + '))'
287 print '\t\treturn NULL;'
288 #
289 # Begin of function call
290 #
291 if type <> 'void':
292 print '\tretval =', func + '(',
293 else:
294 print '\t' + func + '(',
295 #
296 # Argument list
297 #
298 for i in range(len(database)):
299 if i > 0: print ',',
300 a_type, a_mode, a_factor, a_sub = database[i]
301 if a_mode = 'r' and not a_factor:
302 print '&',
303 print 'arg' + `i+1`,
304 #
305 # End of function call
306 #
307 print ');'
308 #
309 # Free varsize arrays
310 #
311 for i in range(len(database)):
312 a_type, a_mode, a_factor, a_sub = database[i]
313 if a_mode = 's' and a_sub:
314 print '\tDEL(arg' + `i+1` + ');'
315 #
316 # Return
317 #
318 if n_out_args:
319 #
320 # Multiple return values -- construct a tuple
321 #
322 if type <> 'void':
323 n_out_args = n_out_args + 1
324 if n_out_args = 1:
325 for i in range(len(database)):
326 a_type, a_mode, a_factor, a_sub = database[i]
327 if a_mode = 'r':
328 break
329 else:
330 raise arg_error, 'expected r arg not found'
331 print '\treturn',
332 print mkobject(a_type, 'arg' + `i+1`) + ';'
333 else:
334 print '\t{ object *v = newtupleobject(',
335 print n_out_args, ');'
336 print '\t if (v == NULL) return NULL;'
337 i_out = 0
338 if type <> 'void':
339 print '\t settupleitem(v,',
340 print `i_out` + ',',
341 print mkobject(type, 'retval') + ');'
342 i_out = i_out + 1
343 for i in range(len(database)):
344 a_type, a_mode, a_factor, a_sub = database[i]
345 if a_mode = 'r':
346 print '\t settupleitem(v,',
347 print `i_out` + ',',
348 s = mkobject(a_type, 'arg' + `i+1`)
349 print s + ');'
350 i_out = i_out + 1
351 print '\t return v;'
352 print '\t}'
353 else:
354 #
355 # Simple function return
356 # Return None or return value
357 #
358 if type = 'void':
359 print '\tINCREF(None);'
360 print '\treturn None;'
361 else:
362 print '\treturn', mkobject(type, 'retval') + ';'
363 #
364 # Stub body closing brace
365 #
366 print '}'
367
368
369# Subroutine to return a function call to mknew<type>object(<arg>)
370#
371def mkobject(type, arg):
372 return 'mknew' + type + 'object(' + arg + ')'
373
374
375# Input line number
376lno = 0
377
378
379# Input is divided in two parts, separated by a line containing '%%'.
380# <part1> -- literally copied to stdout
381# <part2> -- stub definitions
382
383# Variable indicating the current input part.
384#
385part = 1
386
387# Main loop over the input
388#
389while 1:
390 try:
391 line = raw_input()
392 except EOFError:
393 break
394 #
395 lno = lno+1
396 words = string.split(line)
397 #
398 if part = 1:
399 #
400 # In part 1, copy everything literally
401 # except look for a line of just '%%'
402 #
403 if words = ['%%']:
404 part = part + 1
405 else:
406 #
407 # Look for names of manually written
408 # stubs: a single percent followed by the name
409 # of the function in Python.
410 # The stub name is derived by prefixing 'gl_'.
411 #
412 if words and words[0][0] = '%':
413 func = words[0][1:]
414 if (not func) and words[1:]:
415 func = words[1]
416 if func:
417 functions.append(func)
418 else:
419 print line
420 elif not words:
421 pass # skip empty line
422 elif words[0] = '#include':
423 print line
424 elif words[0][:1] = '#':
425 pass # ignore comment
426 elif words[0] not in return_types:
427 err('Line', lno, ': bad return type :', words[0])
428 elif len(words) < 2:
429 err('Line', lno, ': no funcname :', line)
430 else:
431 if len(words) % 2 <> 0:
432 err('Line', lno, ': odd argument list :', words[2:])
433 else:
434 database = []
435 try:
436 for i in range(2, len(words), 2):
437 x = checkarg(words[i], words[i+1])
438 database.append(x)
439 print
440 print '/*',
441 for w in words: print w,
442 print '*/'
443 generate(words[0], words[1], database)
444 except arg_error, msg:
445 err('Line', lno, ':', msg)
446
447
448print
449print 'static struct methodlist gl_methods[] = {'
450for func in functions:
451 print '\t{"' + func + '", gl_' + func + '},'
452print '\t{NULL, NULL} /* Sentinel */'
453print '};'
454print
455print 'initgl()'
456print '{'
457print '\tinitmodule("gl", gl_methods);'
458print '}'