improve treatment of multi-line replies, ignore empty lines
[python/dscho.git] / Modules / cgen.py
blobb06308c85b226e3f4074029c2c63645759950a34
1 ########################################################################
2 # Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
3 # The Netherlands.
5 # All Rights Reserved
7 # Permission to use, copy, modify, and distribute this software and its
8 # documentation for any purpose and without fee is hereby granted,
9 # provided that the above copyright notice appear in all copies and that
10 # both that copyright notice and this permission notice appear in
11 # supporting documentation, and that the names of Stichting Mathematisch
12 # Centrum or CWI not be used in advertising or publicity pertaining to
13 # distribution of the software without specific, written prior permission.
15 # STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
16 # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 # FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
18 # FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 ########################################################################
24 # Python script to parse cstubs file for gl and generate C stubs.
25 # usage: python cgen.py <cstubs >glmodule.c
27 # NOTE: You must first make a python binary without the "GL" option
28 # before you can run this, when building Python for the first time.
29 # See comments in the Makefile.
31 # XXX BUG return arrays generate wrong code
32 # XXX need to change error returns into gotos to free mallocked arrays
35 import string
36 import sys
39 # Function to print to stderr
41 def err(args):
42 savestdout = sys.stdout
43 try:
44 sys.stdout = sys.stderr
45 for i in args:
46 print i,
47 print
48 finally:
49 sys.stdout = savestdout
52 # The set of digits that form a number
54 digits = '0123456789'
57 # Function to extract a string of digits from the front of the string.
58 # Returns the leading string of digits and the remaining string.
59 # If no number is found, returns '' and the original string.
61 def getnum(s):
62 n = ''
63 while s and s[0] in digits:
64 n = n + s[0]
65 s = s[1:]
66 return n, s
69 # Function to check if a string is a number
71 def isnum(s):
72 if not s: return 0
73 for c in s:
74 if not c in digits: return 0
75 return 1
78 # Allowed function return types
80 return_types = ['void', 'short', 'long']
83 # Allowed function argument types
85 arg_types = ['char', 'string', 'short', 'u_short', 'float', 'long', 'double']
88 # Need to classify arguments as follows
89 # simple input variable
90 # simple output variable
91 # input array
92 # output array
93 # input giving size of some array
95 # Array dimensions can be specified as follows
96 # constant
97 # argN
98 # constant * argN
99 # retval
100 # constant * retval
102 # The dimensions given as constants * something are really
103 # arrays of points where points are 2- 3- or 4-tuples
105 # We have to consider three lists:
106 # python input arguments
107 # C stub arguments (in & out)
108 # python output arguments (really return values)
110 # There is a mapping from python input arguments to the input arguments
111 # of the C stub, and a further mapping from C stub arguments to the
112 # python return values
115 # Exception raised by checkarg() and generate()
117 arg_error = 'bad arg'
120 # Function to check one argument.
121 # Arguments: the type and the arg "name" (really mode plus subscript).
122 # Raises arg_error if something's wrong.
123 # Return type, mode, factor, rest of subscript; factor and rest may be empty.
125 def checkarg(type, arg):
127 # Turn "char *x" into "string x".
129 if type == 'char' and arg[0] == '*':
130 type = 'string'
131 arg = arg[1:]
133 # Check that the type is supported.
135 if type not in arg_types:
136 raise arg_error, ('bad type', type)
137 if type[:2] == 'u_':
138 type = 'unsigned ' + type[2:]
140 # Split it in the mode (first character) and the rest.
142 mode, rest = arg[:1], arg[1:]
144 # The mode must be 's' for send (= input) or 'r' for return argument.
146 if mode not in ('r', 's'):
147 raise arg_error, ('bad arg mode', mode)
149 # Is it a simple argument: if so, we are done.
151 if not rest:
152 return type, mode, '', ''
154 # Not a simple argument; must be an array.
155 # The 'rest' must be a subscript enclosed in [ and ].
156 # The subscript must be one of the following forms,
157 # otherwise we don't handle it (where N is a number):
159 # argN
160 # retval
161 # N*argN
162 # N*retval
164 if rest[:1] <> '[' or rest[-1:] <> ']':
165 raise arg_error, ('subscript expected', rest)
166 sub = rest[1:-1]
168 # Is there a leading number?
170 num, sub = getnum(sub)
171 if num:
172 # There is a leading number
173 if not sub:
174 # The subscript is just a number
175 return type, mode, num, ''
176 if sub[:1] == '*':
177 # There is a factor prefix
178 sub = sub[1:]
179 else:
180 raise arg_error, ('\'*\' expected', sub)
181 if sub == 'retval':
182 # size is retval -- must be a reply argument
183 if mode <> 'r':
184 raise arg_error, ('non-r mode with [retval]', mode)
185 elif not isnum(sub) and (sub[:3] <> 'arg' or not isnum(sub[3:])):
186 raise arg_error, ('bad subscript', sub)
188 return type, mode, num, sub
191 # List of functions for which we have generated stubs
193 functions = []
196 # Generate the stub for the given function, using the database of argument
197 # information build by successive calls to checkarg()
199 def generate(type, func, database):
201 # Check that we can handle this case:
202 # no variable size reply arrays yet
204 n_in_args = 0
205 n_out_args = 0
207 for a_type, a_mode, a_factor, a_sub in database:
208 if a_mode == 's':
209 n_in_args = n_in_args + 1
210 elif a_mode == 'r':
211 n_out_args = n_out_args + 1
212 else:
213 # Can't happen
214 raise arg_error, ('bad a_mode', a_mode)
215 if (a_mode == 'r' and a_sub) or a_sub == 'retval':
216 e = 'Function', func, 'too complicated:'
217 err(e + (a_type, a_mode, a_factor, a_sub))
218 print '/* XXX Too complicated to generate code for */'
219 return
221 functions.append(func)
223 # Stub header
225 print
226 print 'static object *'
227 print 'gl_' + func + '(self, args)'
228 print '\tobject *self;'
229 print '\tobject *args;'
230 print '{'
232 # Declare return value if any
234 if type <> 'void':
235 print '\t' + type, 'retval;'
237 # Declare arguments
239 for i in range(len(database)):
240 a_type, a_mode, a_factor, a_sub = database[i]
241 print '\t' + a_type,
242 brac = ket = ''
243 if a_sub and not isnum(a_sub):
244 if a_factor:
245 brac = '('
246 ket = ')'
247 print brac + '*',
248 print 'arg' + `i+1` + ket,
249 if a_sub and isnum(a_sub):
250 print '[', a_sub, ']',
251 if a_factor:
252 print '[', a_factor, ']',
253 print ';'
255 # Find input arguments derived from array sizes
257 for i in range(len(database)):
258 a_type, a_mode, a_factor, a_sub = database[i]
259 if a_mode == 's' and a_sub[:3] == 'arg' and isnum(a_sub[3:]):
260 # Sending a variable-length array
261 n = eval(a_sub[3:])
262 if 1 <= n <= len(database):
263 b_type, b_mode, b_factor, b_sub = database[n-1]
264 if b_mode == 's':
265 database[n-1] = b_type, 'i', a_factor, `i`
266 n_in_args = n_in_args - 1
268 # Assign argument positions in the Python argument list
270 in_pos = []
271 i_in = 0
272 for i in range(len(database)):
273 a_type, a_mode, a_factor, a_sub = database[i]
274 if a_mode == 's':
275 in_pos.append(i_in)
276 i_in = i_in + 1
277 else:
278 in_pos.append(-1)
280 # Get input arguments
282 for i in range(len(database)):
283 a_type, a_mode, a_factor, a_sub = database[i]
284 if a_type[:9] == 'unsigned ':
285 xtype = a_type[9:]
286 else:
287 xtype = a_type
288 if a_mode == 'i':
290 # Implicit argument;
291 # a_factor is divisor if present,
292 # a_sub indicates which arg (`database index`)
294 j = eval(a_sub)
295 print '\tif',
296 print '(!geti' + xtype + 'arraysize(args,',
297 print `n_in_args` + ',',
298 print `in_pos[j]` + ',',
299 if xtype <> a_type:
300 print '('+xtype+' *)',
301 print '&arg' + `i+1` + '))'
302 print '\t\treturn NULL;'
303 if a_factor:
304 print '\targ' + `i+1`,
305 print '= arg' + `i+1`,
306 print '/', a_factor + ';'
307 elif a_mode == 's':
308 if a_sub and not isnum(a_sub):
309 # Allocate memory for varsize array
310 print '\tif ((arg' + `i+1`, '=',
311 if a_factor:
312 print '('+a_type+'(*)['+a_factor+'])',
313 print 'NEW(' + a_type, ',',
314 if a_factor:
315 print a_factor, '*',
316 print a_sub, ')) == NULL)'
317 print '\t\treturn err_nomem();'
318 print '\tif',
319 if a_factor or a_sub: # Get a fixed-size array array
320 print '(!geti' + xtype + 'array(args,',
321 print `n_in_args` + ',',
322 print `in_pos[i]` + ',',
323 if a_factor: print a_factor,
324 if a_factor and a_sub: print '*',
325 if a_sub: print a_sub,
326 print ',',
327 if (a_sub and a_factor) or xtype <> a_type:
328 print '('+xtype+' *)',
329 print 'arg' + `i+1` + '))'
330 else: # Get a simple variable
331 print '(!geti' + xtype + 'arg(args,',
332 print `n_in_args` + ',',
333 print `in_pos[i]` + ',',
334 if xtype <> a_type:
335 print '('+xtype+' *)',
336 print '&arg' + `i+1` + '))'
337 print '\t\treturn NULL;'
339 # Begin of function call
341 if type <> 'void':
342 print '\tretval =', func + '(',
343 else:
344 print '\t' + func + '(',
346 # Argument list
348 for i in range(len(database)):
349 if i > 0: print ',',
350 a_type, a_mode, a_factor, a_sub = database[i]
351 if a_mode == 'r' and not a_factor:
352 print '&',
353 print 'arg' + `i+1`,
355 # End of function call
357 print ');'
359 # Free varsize arrays
361 for i in range(len(database)):
362 a_type, a_mode, a_factor, a_sub = database[i]
363 if a_mode == 's' and a_sub and not isnum(a_sub):
364 print '\tDEL(arg' + `i+1` + ');'
366 # Return
368 if n_out_args:
370 # Multiple return values -- construct a tuple
372 if type <> 'void':
373 n_out_args = n_out_args + 1
374 if n_out_args == 1:
375 for i in range(len(database)):
376 a_type, a_mode, a_factor, a_sub = database[i]
377 if a_mode == 'r':
378 break
379 else:
380 raise arg_error, 'expected r arg not found'
381 print '\treturn',
382 print mkobject(a_type, 'arg' + `i+1`) + ';'
383 else:
384 print '\t{ object *v = newtupleobject(',
385 print n_out_args, ');'
386 print '\t if (v == NULL) return NULL;'
387 i_out = 0
388 if type <> 'void':
389 print '\t settupleitem(v,',
390 print `i_out` + ',',
391 print mkobject(type, 'retval') + ');'
392 i_out = i_out + 1
393 for i in range(len(database)):
394 a_type, a_mode, a_factor, a_sub = database[i]
395 if a_mode == 'r':
396 print '\t settupleitem(v,',
397 print `i_out` + ',',
398 s = mkobject(a_type, 'arg' + `i+1`)
399 print s + ');'
400 i_out = i_out + 1
401 print '\t return v;'
402 print '\t}'
403 else:
405 # Simple function return
406 # Return None or return value
408 if type == 'void':
409 print '\tINCREF(None);'
410 print '\treturn None;'
411 else:
412 print '\treturn', mkobject(type, 'retval') + ';'
414 # Stub body closing brace
416 print '}'
419 # Subroutine to return a function call to mknew<type>object(<arg>)
421 def mkobject(type, arg):
422 if type[:9] == 'unsigned ':
423 type = type[9:]
424 return 'mknew' + type + 'object((' + type + ') ' + arg + ')'
425 return 'mknew' + type + 'object(' + arg + ')'
428 defined_archs = []
430 # usage: cgen [ -Dmach ... ] [ file ]
431 for arg in sys.argv[1:]:
432 if arg[:2] == '-D':
433 defined_archs.append(arg[2:])
434 else:
435 # Open optional file argument
436 sys.stdin = open(arg, 'r')
439 # Input line number
440 lno = 0
443 # Input is divided in two parts, separated by a line containing '%%'.
444 # <part1> -- literally copied to stdout
445 # <part2> -- stub definitions
447 # Variable indicating the current input part.
449 part = 1
451 # Main loop over the input
453 while 1:
454 try:
455 line = raw_input()
456 except EOFError:
457 break
459 lno = lno+1
460 words = string.split(line)
462 if part == 1:
464 # In part 1, copy everything literally
465 # except look for a line of just '%%'
467 if words == ['%%']:
468 part = part + 1
469 else:
471 # Look for names of manually written
472 # stubs: a single percent followed by the name
473 # of the function in Python.
474 # The stub name is derived by prefixing 'gl_'.
476 if words and words[0][0] == '%':
477 func = words[0][1:]
478 if (not func) and words[1:]:
479 func = words[1]
480 if func:
481 functions.append(func)
482 else:
483 print line
484 continue
485 if not words:
486 continue # skip empty line
487 elif words[0] == 'if':
488 # if XXX rest
489 # if !XXX rest
490 if words[1][0] == '!':
491 if words[1][1:] in defined_archs:
492 continue
493 elif words[1] not in defined_archs:
494 continue
495 words = words[2:]
496 if words[0] == '#include':
497 print line
498 elif words[0][:1] == '#':
499 pass # ignore comment
500 elif words[0] not in return_types:
501 err('Line', lno, ': bad return type :', words[0])
502 elif len(words) < 2:
503 err('Line', lno, ': no funcname :', line)
504 else:
505 if len(words) % 2 <> 0:
506 err('Line', lno, ': odd argument list :', words[2:])
507 else:
508 database = []
509 try:
510 for i in range(2, len(words), 2):
511 x = checkarg(words[i], words[i+1])
512 database.append(x)
513 print
514 print '/*',
515 for w in words: print w,
516 print '*/'
517 generate(words[0], words[1], database)
518 except arg_error, msg:
519 err('Line', lno, ':', msg)
522 print
523 print 'static struct methodlist gl_methods[] = {'
524 for func in functions:
525 print '\t{"' + func + '", gl_' + func + '},'
526 print '\t{NULL, NULL} /* Sentinel */'
527 print '};'
528 print
529 print 'initgl()'
530 print '{'
531 print '\tinitmodule("gl", gl_methods);'
532 print '}'