text: ########################################################################
  # Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
  # The Netherlands.
  #
  #                         All Rights Reserved
  #
  # Permission to use, copy, modify, and distribute this software and its
  # documentation for any purpose and without fee is hereby granted,
  # provided that the above copyright notice appear in all copies and that
  # both that copyright notice and this permission notice appear in
  # supporting documentation, and that the names of Stichting Mathematisch
  # Centrum or CWI or Corporation for National Research Initiatives or
  # CNRI not be used in advertising or publicity pertaining to
  # distribution of the software without specific, written prior
  # permission.
  # 
  # While CWI is the initial source for this software, a modified version
  # is made available by the Corporation for National Research Initiatives
  # (CNRI) at the Internet address ftp://ftp.python.org.
  # 
  # STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH
  # REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
  # MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH
  # CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
  # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  # PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  # PERFORMANCE OF THIS SOFTWARE.
  ########################################################################
  
  # Python script to parse cstubs file for gl and generate C stubs.
  # usage: python cgen.py <cstubs >glmodule.c
  #
  # NOTE: You  must first make a python binary without the "GL" option
  #	before you can run this, when building Python for the first time.
  #	See comments in the Makefile.
  #
  # XXX BUG return arrays generate wrong code
  # XXX need to change error returns into gotos to free mallocked arrays
  
  
  import string
  import sys
  
  
  # Function to print to stderr
  #
  def err(*args):
  	savestdout = sys.stdout
  	try:
  		sys.stdout = sys.stderr
  		for i in args:
  			print i,
  		print
  	finally:
  		sys.stdout = savestdout
  
  
  # The set of digits that form a number
  #
  digits = '0123456789'
  
  
  # Function to extract a string of digits from the front of the string.
  # Returns the leading string of digits and the remaining string.
  # If no number is found, returns '' and the original string.
  #
  def getnum(s):
  	n = ''
  	while s and s[0] in digits:
  		n = n + s[0]
  		s = s[1:]
  	return n, s
  
  
  # Function to check if a string is a number
  #
  def isnum(s):
  	if not s: return 0
  	for c in s:
  		if not c in digits: return 0
  	return 1
  
  
  # Allowed function return types
  #
  return_types = ['void', 'short', 'long']
  
  
  # Allowed function argument types
  #
  arg_types = ['char', 'string', 'short', 'u_short', 'float', 'long', 'double']
  
  
  # Need to classify arguments as follows
  #	simple input variable
  #	simple output variable
  #	input array
  #	output array
  #	input giving size of some array
  #
  # Array dimensions can be specified as follows
  #	constant
  #	argN
  #	constant * argN
  #	retval
  #	constant * retval
  #
  # The dimensions given as constants * something are really
  # arrays of points where points are 2- 3- or 4-tuples
  #
  # We have to consider three lists:
  #	python input arguments
  #	C stub arguments (in & out)
  #	python output arguments (really return values)
  #
  # There is a mapping from python input arguments to the input arguments
  # of the C stub, and a further mapping from C stub arguments to the
  # python return values
  
  
  # Exception raised by checkarg() and generate()
  #
  arg_error = 'bad arg'
  
  
  # Function to check one argument.
  # Arguments: the type and the arg "name" (really mode plus subscript).
  # Raises arg_error if something's wrong.
  # Return type, mode, factor, rest of subscript; factor and rest may be empty.
  #
  def checkarg(type, arg):
  	#
  	# Turn "char *x" into "string x".
  	#
  	if type == 'char' and arg[0] == '*':
  		type = 'string'
  		arg = arg[1:]
  	#
  	# Check that the type is supported.
  	#
  	if type not in arg_types:
  		raise arg_error, ('bad type', type)
  	if type[:2] == 'u_':
  		type = 'unsigned ' + type[2:]
  	#
  	# Split it in the mode (first character) and the rest.
  	#
  	mode, rest = arg[:1], arg[1:]
  	#
  	# The mode must be 's' for send (= input) or 'r' for return argument.
  	#
  	if mode not in ('r', 's'):
  		raise arg_error, ('bad arg mode', mode)
  	#
  	# Is it a simple argument: if so, we are done.
  	#
  	if not rest:
  		return type, mode, '', ''
  	#	
  	# Not a simple argument; must be an array.
  	# The 'rest' must be a subscript enclosed in [ and ].
  	# The subscript must be one of the following forms,
  	# otherwise we don't handle it (where N is a number):
  	#	N
  	#	argN
  	#	retval
  	#	N*argN
  	#	N*retval
  	#
  	if rest[:1] <> '[' or rest[-1:] <> ']':
  		raise arg_error, ('subscript expected', rest)
  	sub = rest[1:-1]
  	#
  	# Is there a leading number?
  	#
  	num, sub = getnum(sub)
  	if num:
  		# There is a leading number
  		if not sub:
  			# The subscript is just a number
  			return type, mode, num, ''
  		if sub[:1] == '*':
  			# There is a factor prefix
  			sub = sub[1:]
  		else:
  			raise arg_error, ('\'*\' expected', sub)
  	if sub == 'retval':
  		# size is retval -- must be a reply argument
  		if mode <> 'r':
  			raise arg_error, ('non-r mode with [retval]', mode)
  	elif not isnum(sub) and (sub[:3] <> 'arg' or not isnum(sub[3:])):
  		raise arg_error, ('bad subscript', sub)
  	#
  	return type, mode, num, sub
  
  
  # List of functions for which we have generated stubs
  #
  functions = []
  
  
  # Generate the stub for the given function, using the database of argument
  # information build by successive calls to checkarg()
  #
  def generate(type, func, database):
  	#
  	# Check that we can handle this case:
  	# no variable size reply arrays yet
  	#
  	n_in_args = 0
  	n_out_args = 0
  	#
  	for a_type, a_mode, a_factor, a_sub in database:
  		if a_mode == 's':
  			n_in_args = n_in_args + 1
  		elif a_mode == 'r':
  			n_out_args = n_out_args + 1
  		else:
  			# Can't happen
  			raise arg_error, ('bad a_mode', a_mode)
  		if (a_mode == 'r' and a_sub) or a_sub == 'retval':
  			err('Function', func, 'too complicated:',
  			    a_type, a_mode, a_factor, a_sub)
  			print '/* XXX Too complicated to generate code for */'
  			return
  	#
  	functions.append(func)
  	#
  	# Stub header
  	#
  	print
  	print 'static object *'
  	print 'gl_' + func + '(self, args)'
  	print '\tobject *self;'
  	print '\tobject *args;'
  	print '{'
  	#
  	# Declare return value if any
  	#
  	if type <> 'void':
  		print '\t' + type, 'retval;'
  	#
  	# Declare arguments
  	#
  	for i in range(len(database)):
  		a_type, a_mode, a_factor, a_sub = database[i]
  		print '\t' + a_type,
  		brac = ket = ''
  		if a_sub and not isnum(a_sub):
  			if a_factor:
  				brac = '('
  				ket = ')'
  			print brac + '*',
  		print 'arg' + `i+1` + ket,
  		if a_sub and isnum(a_sub):
  			print '[', a_sub, ']',
  		if a_factor:
  			print '[', a_factor, ']',
  		print ';'
  	#
  	# Find input arguments derived from array sizes
  	#
  	for i in range(len(database)):
  		a_type, a_mode, a_factor, a_sub = database[i]
  		if a_mode == 's' and a_sub[:3] == 'arg' and isnum(a_sub[3:]):
  			# Sending a variable-length array
  			n = eval(a_sub[3:])
  			if 1 <= n <= len(database):
  			    b_type, b_mode, b_factor, b_sub = database[n-1]
  			    if b_mode == 's':
  			        database[n-1] = b_type, 'i', a_factor, `i`
  			        n_in_args = n_in_args - 1
  	#
  	# Assign argument positions in the Python argument list
  	#
  	in_pos = []
  	i_in = 0
  	for i in range(len(database)):
  		a_type, a_mode, a_factor, a_sub = database[i]
  		if a_mode == 's':
  			in_pos.append(i_in)
  			i_in = i_in + 1
  		else:
  			in_pos.append(-1)
  	#
  	# Get input arguments
  	#
  	for i in range(len(database)):
  		a_type, a_mode, a_factor, a_sub = database[i]
  		if a_type[:9] == 'unsigned ':
  			xtype = a_type[9:]
  		else:
  			xtype = a_type
  		if a_mode == 'i':
  			#
  			# Implicit argument;
  			# a_factor is divisor if present,
  			# a_sub indicates which arg (`database index`)
  			#
  			j = eval(a_sub)
  			print '\tif',
  			print '(!geti' + xtype + 'arraysize(args,',
  			print `n_in_args` + ',',
  			print `in_pos[j]` + ',',
  			if xtype <> a_type:
  				print '('+xtype+' *)',
  			print '&arg' + `i+1` + '))'
  			print '\t\treturn NULL;'
  			if a_factor:
  				print '\targ' + `i+1`,
  				print '= arg' + `i+1`,
  				print '/', a_factor + ';'
  		elif a_mode == 's':
  			if a_sub and not isnum(a_sub):
  				# Allocate memory for varsize array
  				print '\tif ((arg' + `i+1`, '=',
  				if a_factor:
  					print '('+a_type+'(*)['+a_factor+'])',
  				print 'NEW(' + a_type, ',',
  				if a_factor:
  					print a_factor, '*',
  				print a_sub, ')) == NULL)'
  				print '\t\treturn err_nomem();'
  			print '\tif',
  			if a_factor or a_sub: # Get a fixed-size array array
  				print '(!geti' + xtype + 'array(args,',
  				print `n_in_args` + ',',
  				print `in_pos[i]` + ',',
  				if a_factor: print a_factor,
  				if a_factor and a_sub: print '*',
  				if a_sub: print a_sub,
  				print ',',
  				if (a_sub and a_factor) or xtype <> a_type:
  					print '('+xtype+' *)',
  				print 'arg' + `i+1` + '))'
  			else: # Get a simple variable
  				print '(!geti' + xtype + 'arg(args,',
  				print `n_in_args` + ',',
  				print `in_pos[i]` + ',',
  				if xtype <> a_type:
  					print '('+xtype+' *)',
  				print '&arg' + `i+1` + '))'
  			print '\t\treturn NULL;'
  	#
  	# Begin of function call
  	#
  	if type <> 'void':
  		print '\tretval =', func + '(',
  	else:
  		print '\t' + func + '(',
  	#
  	# Argument list
  	#
  	for i in range(len(database)):
  		if i > 0: print ',',
  		a_type, a_mode, a_factor, a_sub = database[i]
  		if a_mode == 'r' and not a_factor:
  			print '&',
  		print 'arg' + `i+1`,
  	#
  	# End of function call
  	#
  	print ');'
  	#
  	# Free varsize arrays
  	#
  	for i in range(len(database)):
  		a_type, a_mode, a_factor, a_sub = database[i]
  		if a_mode == 's' and a_sub and not isnum(a_sub):
  			print '\tDEL(arg' + `i+1` + ');'
  	#
  	# Return
  	#
  	if n_out_args:
  		#
  		# Multiple return values -- construct a tuple
  		#
  		if type <> 'void':
  			n_out_args = n_out_args + 1
  		if n_out_args == 1:
  			for i in range(len(database)):
  				a_type, a_mode, a_factor, a_sub = database[i]
  				if a_mode == 'r':
  					break
  			else:
  				raise arg_error, 'expected r arg not found'
  			print '\treturn',
  			print mkobject(a_type, 'arg' + `i+1`) + ';'
  		else:
  			print '\t{ object *v = newtupleobject(',
  			print n_out_args, ');'
  			print '\t  if (v == NULL) return NULL;'
  			i_out = 0
  			if type <> 'void':
  				print '\t  settupleitem(v,',
  				print `i_out` + ',',
  				print mkobject(type, 'retval') + ');'
  				i_out = i_out + 1
  			for i in range(len(database)):
  				a_type, a_mode, a_factor, a_sub = database[i]
  				if a_mode == 'r':
  					print '\t  settupleitem(v,',
  					print `i_out` + ',',
  					s = mkobject(a_type, 'arg' + `i+1`)
  					print s + ');'
  					i_out = i_out + 1
  			print '\t  return v;'
  			print '\t}'
  	else:
  		#
  		# Simple function return
  		# Return None or return value
  		#
  		if type == 'void':
  			print '\tINCREF(None);'
  			print '\treturn None;'
  		else:
  			print '\treturn', mkobject(type, 'retval') + ';'
  	#
  	# Stub body closing brace
  	#
  	print '}'
  
  
  # Subroutine to return a function call to mknew<type>object(<arg>)
  #
  def mkobject(type, arg):
  	if type[:9] == 'unsigned ':
  		type = type[9:]
  		return 'mknew' + type + 'object((' + type + ') ' + arg + ')'
  	return 'mknew' + type + 'object(' + arg + ')'
  
  
  defined_archs = []
  
  # usage: cgen [ -Dmach ... ] [ file ]
  for arg in sys.argv[1:]:
  	if arg[:2] == '-D':
  		defined_archs.append(arg[2:])
  	else:
  		# Open optional file argument
  		sys.stdin = open(arg, 'r')
  
  
  # Input line number
  lno = 0
  
  
  # Input is divided in two parts, separated by a line containing '%%'.
  #	<part1>		-- literally copied to stdout
  #	<part2>		-- stub definitions
  
  # Variable indicating the current input part.
  #
  part = 1
  
  # Main loop over the input
  #
  while 1:
  	try:
  		line = raw_input()
  	except EOFError:
  		break
  	#
  	lno = lno+1
  	words = string.split(line)
  	#
  	if part == 1:
  		#
  		# In part 1, copy everything literally
  		# except look for a line of just '%%'
  		#
  		if words == ['%%']:
  			part = part + 1
  		else:
  			#
  			# Look for names of manually written
  			# stubs: a single percent followed by the name
  			# of the function in Python.
  			# The stub name is derived by prefixing 'gl_'.
  			#
  			if words and words[0][0] == '%':
  				func = words[0][1:]
  				if (not func) and words[1:]:
  					func = words[1]
  				if func:
  					functions.append(func)
  			else:
  				print line
  		continue
  	if not words:
  		continue		# skip empty line
  	elif words[0] == 'if':
  		# if XXX rest
  		# if !XXX rest
  		if words[1][0] == '!':
  			if words[1][1:] in defined_archs:
  				continue
  		elif words[1] not in defined_archs:
  			continue
  		words = words[2:]
  	if words[0] == '#include':
  		print line
  	elif words[0][:1] == '#':
  		pass			# ignore comment
  	elif words[0] not in return_types:
  		err('Line', lno, ': bad return type :', words[0])
  	elif len(words) < 2:
  		err('Line', lno, ': no funcname :', line)
  	else:
  		if len(words) % 2 <> 0:
  			err('Line', lno, ': odd argument list :', words[2:])
  		else:
  			database = []
  			try:
  				for i in range(2, len(words), 2):
  					x = checkarg(words[i], words[i+1])
  					database.append(x)
  				print
  				print '/*',
  				for w in words: print w,
  				print '*/'
  				generate(words[0], words[1], database)
  			except arg_error, msg:
  				err('Line', lno, ':', msg)
  
  
  print
  print 'static struct methodlist gl_methods[] = {'
  for func in functions:
  	print '\t{"' + func + '", gl_' + func + '},'
  print '\t{NULL, NULL} /* Sentinel */'
  print '};'
  print
  print 'initgl()'
  print '{'
  print '\tinitmodule("gl", gl_methods);'
  print '}'
  