1 | ########################################################################
|
---|
2 | # 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.
|
---|
6 | #
|
---|
7 | # See the file "Misc/COPYRIGHT" for information on usage and
|
---|
8 | # redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
---|
9 | ########################################################################
|
---|
10 |
|
---|
11 | # Python script to parse cstubs file for gl and generate C stubs.
|
---|
12 | # usage: python cgen.py <cstubs >glmodule.c
|
---|
13 | #
|
---|
14 | # NOTE: You must first make a python binary without the "GL" option
|
---|
15 | # before you can run this, when building Python for the first time.
|
---|
16 | # See comments in the Makefile.
|
---|
17 | #
|
---|
18 | # XXX BUG return arrays generate wrong code
|
---|
19 | # XXX need to change error returns into gotos to free mallocked arrays
|
---|
20 |
|
---|
21 |
|
---|
22 | import string
|
---|
23 | import sys
|
---|
24 |
|
---|
25 |
|
---|
26 | # Function to print to stderr
|
---|
27 | #
|
---|
28 | def err(*args):
|
---|
29 | 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
|
---|
37 |
|
---|
38 |
|
---|
39 | # The set of digits that form a number
|
---|
40 | #
|
---|
41 | digits = '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 | #
|
---|
48 | def getnum(s):
|
---|
49 | n = ''
|
---|
50 | while s and s[0] in digits:
|
---|
51 | n = n + s[0]
|
---|
52 | s = s[1:]
|
---|
53 | return n, s
|
---|
54 |
|
---|
55 |
|
---|
56 | # Function to check if a string is a number
|
---|
57 | #
|
---|
58 | def isnum(s):
|
---|
59 | if not s: return False
|
---|
60 | for c in s:
|
---|
61 | if not c in digits: return False
|
---|
62 | return True
|
---|
63 |
|
---|
64 |
|
---|
65 | # Allowed function return types
|
---|
66 | #
|
---|
67 | return_types = ['void', 'short', 'long']
|
---|
68 |
|
---|
69 |
|
---|
70 | # Allowed function argument types
|
---|
71 | #
|
---|
72 | arg_types = ['char', 'string', 'short', 'u_short', 'float', 'long', 'double']
|
---|
73 |
|
---|
74 |
|
---|
75 | # Need to classify arguments as follows
|
---|
76 | # simple input variable
|
---|
77 | # simple output variable
|
---|
78 | # input array
|
---|
79 | # output array
|
---|
80 | # input giving size of some array
|
---|
81 | #
|
---|
82 | # Array dimensions can be specified as follows
|
---|
83 | # constant
|
---|
84 | # argN
|
---|
85 | # constant * argN
|
---|
86 | # retval
|
---|
87 | # constant * retval
|
---|
88 | #
|
---|
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:
|
---|
93 | # python input arguments
|
---|
94 | # C stub arguments (in & out)
|
---|
95 | # python output arguments (really return values)
|
---|
96 | #
|
---|
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 | #
|
---|
104 | arg_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 | #
|
---|
112 | def checkarg(type, arg):
|
---|
113 | #
|
---|
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
|
---|
176 |
|
---|
177 |
|
---|
178 | # List of functions for which we have generated stubs
|
---|
179 | #
|
---|
180 | functions = []
|
---|
181 |
|
---|
182 |
|
---|
183 | # Generate the stub for the given function, using the database of argument
|
---|
184 | # information build by successive calls to checkarg()
|
---|
185 | #
|
---|
186 | def generate(type, func, database):
|
---|
187 | #
|
---|
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 '}'
|
---|
404 |
|
---|
405 |
|
---|
406 | # Subroutine to return a function call to mknew<type>object(<arg>)
|
---|
407 | #
|
---|
408 | def mkobject(type, arg):
|
---|
409 | if type[:9] == 'unsigned ':
|
---|
410 | type = type[9:]
|
---|
411 | return 'mknew' + type + 'object((' + type + ') ' + arg + ')'
|
---|
412 | return 'mknew' + type + 'object(' + arg + ')'
|
---|
413 |
|
---|
414 |
|
---|
415 | defined_archs = []
|
---|
416 |
|
---|
417 | # usage: cgen [ -Dmach ... ] [ file ]
|
---|
418 | for arg in sys.argv[1:]:
|
---|
419 | if arg[:2] == '-D':
|
---|
420 | defined_archs.append(arg[2:])
|
---|
421 | else:
|
---|
422 | # Open optional file argument
|
---|
423 | sys.stdin = open(arg, 'r')
|
---|
424 |
|
---|
425 |
|
---|
426 | # Input line number
|
---|
427 | lno = 0
|
---|
428 |
|
---|
429 |
|
---|
430 | # Input is divided in two parts, separated by a line containing '%%'.
|
---|
431 | # <part1> -- literally copied to stdout
|
---|
432 | # <part2> -- stub definitions
|
---|
433 |
|
---|
434 | # Variable indicating the current input part.
|
---|
435 | #
|
---|
436 | part = 1
|
---|
437 |
|
---|
438 | # Main loop over the input
|
---|
439 | #
|
---|
440 | while 1:
|
---|
441 | 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)
|
---|
507 |
|
---|
508 |
|
---|
509 | print
|
---|
510 | print 'static struct PyMethodDef gl_methods[] = {'
|
---|
511 | for func in functions:
|
---|
512 | print '\t{"' + func + '", gl_' + func + '},'
|
---|
513 | print '\t{NULL, NULL} /* Sentinel */'
|
---|
514 | print '};'
|
---|
515 | print
|
---|
516 | print 'void'
|
---|
517 | print 'initgl()'
|
---|
518 | print '{'
|
---|
519 | print '\t(void) Py_InitModule("gl", gl_methods);'
|
---|
520 | print '}'
|
---|