shithub: 9ficl

ref: 7c76dac1b268038f567939a70a18228e790a5cbc
dir: /contrib/xclasses/xclasses.py/

View raw version
import copy
import string
import sys
import time
import types


def capitalize(s):
	return string.upper(s[0]) + s[1:]

def fprint(f, s):
	print >> f, s


def fprintHeader(f, comment = "//"):
	fprint(f, comment)
	fprint(f, comment + " Generated by xclasses.py at " + time.strftime("%Y/%m/%d %H:%M:%S"))
	fprint(f, comment)
	fprint(f, comment)
	fprint(f, "")

def fprintFooter(f, comment = "//"):
	fprint(f, "")
	fprint(f, "")
	fprint(f, comment + " end of file")
	fprint(f, "")

multicallCallTypeFunction = 0
multicallCallTypeMethod = 1
multicallCallTypeVirtualMethod = 2

multicallReturnTypeVoid = 0
multicallReturnTypeInteger = 16
multicallReturnTypeCstring = 32
multicallReturnTypeFloat = 48

multicallExplicitVtable = 512


ficlVmName = "ficlVm"

h_headers = []
def xAddHHeader(line):
	h_headers.append(line)

h_footers = []
def xAddHFooter(line):
	h_footers.append(line)

ficl_headers = []
def xAddFiclHeader(line):
	ficl_headers.append(line)

ficl_footers = []
def xAddFiclFooter(line):
	ficl_footers.append(line)

c_headers = []
def xAddCHeader(line):
	c_headers.append(line)

c_footers = []
def xAddCFooter(line):
	c_footers.append(line)


classes = []

class xVariable:
	def __init__(self, name, typeCPP = None, cells = None, count = None, defaultValue = None, cstring = None):
		self.comments = []
		self.setName(name)
		self.setCells(cells)
		self.setCount(count)
		self.setDefaultValue(defaultValue)
		self.setCString(cstring)
		self.setTypeCPP(typeCPP)

	def setName(self, name):
		self.name = name
		return self

	def setTypeCPP(self, typeCPP):
		self.typeCPP = typeCPP
		if (typeCPP == "char *"):
			self.setCString(1)
		return self

	def setCells(self, cells):
		if cells == None:
			self.cells = 1
		else:
			self.cells = cells
		return self

	def setCString(self, cstring):
		self.cstring = cstring
		return self

	def isCString(self):
		return self.cstring

	def getTotalSize(self):
		return self.cells * self.count

	def setCount(self, count):
		if type(count) != types.IntType:
			count = 1
		self.count = count
		return self

	def setDefaultValue(self, defaultValue):
		if (defaultValue != None) and (type(defaultValue) != types.StringType):
			defaultValue = str(defaultValue)
		self.defaultValue = defaultValue
		return self

	def addComment(self, c):
		self.comments.append(c)
		return self

	def isFloat(self):
		return self.typeCPP == "float"

	def stringCPP(self, wantDefaultValues=1):
		if (type(self.typeCPP) != types.StringType):
			sys.exit("didn't set a CPP type on variable " + self.name + "!")
		output = self.typeCPP
		if (self.typeCPP[-1] != "*") and (self.typeCPP[-1] != "&"):
			output += " "
		output += self.name
		if self.count > 1:
			output += "[" + str(self.count) + "]"
		if self.count == 0:
			output += "[]"
		if wantDefaultValues and (self.defaultValue != None):
			output += " = " + self.defaultValue
		return output

	def printH(self, f):
		if len(self.comments):
			for comment in self.comments:
				fprint(f, "\t" + "// " + comment)
		fprint(f, "\t" + self.stringCPP() + ";")

	def printF(self, f):
		totalCells = self.count * self.cells
		if (totalCells <= 1):
			typeF = "cell:"
		else:
			typeF = str(totalCells) + " cells:"
		if len(self.comments):
			for comment in self.comments:
				fprint(f, "\t" + "// " + comment)
		fprint(f, "\t" + "S\" " + typeF + " ." + self.name + " \" evaluate")


class xMethod:
	def __init__(self, name, returnType = None, virtual = None, static = None, body = None):
		self.arguments = []
		self.comments = []

		self.setName(name)
		self.setReturnType(returnType)
		self.setVirtual(virtual)
		self.setStatic(static)
		self.setBody(body)
		self.setThunkVariable(None)
		self.vtableOffset = 0

	def copy():
		clone = xMethod(self.name, self.returnType, self.virtual, self.static)
		clone.arguments = self.arguments
		clone.comments = self.comments

	def setName(self, name):
		self.name = name
		return self

	def setReturnType(self, returnType):
		if returnType.__class__ == xVariable:
			self.returnType = returnType
		elif type(returnType) == types.StringType:
			self.returnType = xVariable("ignored", returnType)
		else:
			self.returnType = None
		return self

	def returnTypeIsVoid(self):
		return(self.returnType == None) or (self.returnType.typeCPP == None) or (self.returnType.typeCPP == "") or (self.returnType.typeCPP == "void")

	def setVirtual(self, virtual):
		self.virtual = virtual
		return self

	def isVirtual(self):
		return self.virtual > 0

	def isPureVirtual(self):
		return self.virtual > 1

	def setStatic(self, static):
		self.static = static
		return self

	def setThunkVariable(self, thunkVariable):
		self.thunkVariable = thunkVariable
		return self

	def isStatic(self):
		return self.static

	# a constructor or a destructor
	def isClassSpecial(self):
		return (self.returnType == None) or (self.returnType.typeCPP == None) or (self.returnType.typeCPP == "")

	def setBody(self, body):
		self.body = body
		return self

	def addArgument(self, argument):
		self.arguments.append(argument)
		return self

	def addComment(self, c):
		self.comments.append(c)
		return self

	def prototype(self, isDefinition=None):
		arguments = ""
		for a in self.arguments:
			if len(arguments):
				arguments += ", "
			arguments += a.stringCPP(not isDefinition)

		if len(arguments) == 0:
			arguments = "void"
		className = ""
		if (isDefinition):
			className = self.memberOf.name + "::"
		modifiers = ""
		if self.virtual and (not isDefinition):
			modifiers += "virtual "
		if self.static and (not isDefinition):
			modifiers += "static "
		returnType = ""
		name = self.name
		if (name == "") or (name == "~"):
			name += self.memberOf.name
		if (self.returnType != None) and (len(self.returnType.typeCPP) > 0):
			returnType = self.returnType.typeCPP + " "
		return modifiers + returnType + className + name + "(" + arguments + ")"

	def printH(self, f):
		pureVirtual = ""
		if (self.virtual > 1):
			pureVirtual = " = 0"
		suffix = ";"
		modifiers = ""
		if self.body != None:
			modifiers = "inline "
			suffix = " " + self.body
		fprint(f, "\t" + modifiers + self.prototype() + pureVirtual + suffix)

	def printF(self, f):
		if not self.isVirtual():
			return

		if len(self.comments):
			for comment in self.comments:
				fprint(f, "\t" + "// " + comment)

		flags = multicallReturnTypeInteger
		if self.returnTypeIsVoid():
			flags = multicallReturnTypeVoid
		elif (self.returnType.isCString()):
			flags = multicallReturnTypeCString
		elif (self.returnType.typeCPP == "float"):
			flags = multicallReturnTypeFloat
		flags |= multicallCallTypeVirtualMethod
		# move floating-point arguments from float stack
		floatArgumentsBitfield = 0
		cstringArgumentsBitfield = 0
		argumentNumber = 0
		cstrings = 0
		name = self.name
		if (self.memberOf.pureVirtual):
			vtable = ""
		else:
			vtable = " drop [ " + self.memberOf.name + "-vtable literal ] "
			flags |= multicallExplicitVtable
		if (name == "") or (name == "~"):
			name += self.memberOf.name
		for a in self.arguments:
			if a.isFloat():
				floatArgumentsBitfield |= (1 << argumentNumber)
			elif a.isCString():
				cstringArgumentsBitfield |= (1 << argumentNumber)
				cstrings += 1
			argumentNumber += 1
		fprint(f, "\tS\" : " + name + vtable + str(len(self.arguments) + cstrings) + " " + str(floatArgumentsBitfield) + " " + str(cstringArgumentsBitfield) + " " + str(self.vtableOffset) + " " + str(flags) + " multicall ; \" evaluate ")

	def printCPP(self, f):
		if (self.thunkVariable != None):
			if (self.returnType != None) and (self.returnType.isCString()):
				sys.exit("Can't thunk char * return values, sorry.")
			fprint(f, "")
			fprint(f, self.prototype(1))
			fprint(f, "\t{")
			fprint(f, "\tif (" + self.thunkVariable.name + " == NULL)")
			if self.isClassSpecial() or self.returnTypeIsVoid():
				fprint(f, "\t\treturn;")
			elif (self.returnType.isFloat()):
				fprint(f, "\t\treturn 0.0f;")
			else:
				fprint(f, "\t\treturn (" + self.returnType.typeCPP + ")0;")
			fprint(f, "")

			ficlVmName = self.memberOf.getFiclVmName()

			## output stack-checking code!  how cool is that?  --lch
			dataStackPush = 2  # why 2? we always push this and ficlClass.
			dataStackPop = 0
			floatStackPush = 0
			floatStackPop = 0

			for a in self.arguments:
				if (a.isCString()):
					dataStackPush = dataStackPush + 2
				elif (a.isFloat()):
					floatStackPush = floatStackPush + 1
				else:
					dataStackPush = dataStackPush + 1

			if (not self.returnTypeIsVoid()):
				if (self.returnType.isFloat()):
					floatStackPop = 1
				else:
					dataStackPop = 1

			if (dataStackPush or dataStackPop or floatStackPush or floatStackPop):
				fprint(f, "#ifdef _DEBUG")
				if (dataStackPush or dataStackPop):
					fprint(f, "\tficlStackCheck(" + ficlVmName + "->dataStack, " + str(dataStackPush) + ", " + str(dataStackPop) + ");")
				if (floatStackPush or floatStackPop):
					fprint(f, "\tficlStackCheck(" + ficlVmName + "->floatStack, " + str(floatStackPush) + ", " + str(floatStackPop) + ");")
				fprint(f, "#endif // _DEBUG")

			reversedArguments = copy.copy(self.arguments)
			reversedArguments.reverse()

			for a in reversedArguments:
				if (a.isCString()):
					fprint(f, "\tficlStackPushPointer(" + ficlVmName + "->dataStack, " + a.name + ");")
					fprint(f, "\tficlStackPushInteger(" + ficlVmName + "->dataStack, strlen(" + a.name + "));")
				elif (a.isFloat()):
					fprint(f, "\tficlStackPushFloat(" + ficlVmName + "->floatStack, " + a.name + ");")
				else:
					fprint(f, "\tficlStackPushInteger(" + ficlVmName + "->dataStack, (int)" + a.name + ");")
			fprint(f, "\tficlStackPushPointer(" + ficlVmName + "->dataStack, this);")
			fprint(f, "\tficlStackPushPointer(" + ficlVmName + "->dataStack, ficlClass);")
			fprint(f, "\tficlVmExecuteXT(" + ficlVmName + ", " + self.thunkVariable.name + ");")
			if (not self.returnTypeIsVoid()):
				if (self.returnType.isFloat()):
					fprint(f, "\treturn ficlStackPopFloat(" + ficlVmName + "->floatStack);")
				else:
					fprint(f, "\treturn (" + self.returnType.typeCPP + ")ficlStackPopInteger(" + ficlVmName + "->dataStack);")
			fprint(f, "\t}")
			fprint(f, "")

		# don't do virtual functions
		if self.isVirtual() or self.isClassSpecial():
			return

		name = self.name
		if (name == "") or (name == "~"):
			name += self.memberOf.name

		fprint(f, "// " + self.memberOf.name + "::" + name)
		if len(self.comments):
			fprint(f, "\t" + "//")
			for comment in self.comments:
				fprint(f, "\t" + "// " + comment)

		arguments = ""
		for a in self.arguments:
			if len(arguments):
				arguments += ", "
			arguments += a.stringCPP()

		if len(arguments) == 0:
			arguments = "void"
		classModifier = self.memberOf.name + "::"
		calltype = "FICL_MULTICALL_CALLTYPE_METHOD"
		if self.isStatic():
			classModifier = ""
			calltype = "FICL_MULTICALL_CALLTYPE_FUNCTION"
		returnString = "FICL_MULTICALL_RETURNTYPE_INTEGER"
		if self.returnTypeIsVoid():
			returnString = "FICL_MULTICALL_RETURNTYPE_VOID"
		elif (self.returnType.typeCPP == "float"):
			returnString = "FICL_MULTICALL_RETURNTYPE_FLOAT"
		elif (self.returnType.isCString()):
			returnString = "FICL_MULTICALL_RETURNTYPE_CSTRING"

		# set bits in argumentFlags
		floatArgumentsBitfield = 0
		cstringArgumentsBitfield = 0
		argumentNumber = 0
		cstrings = 0
		for a in self.arguments:
			if a.isFloat():
				floatArgumentsBitfield |= (1 << argumentNumber)
			elif a.isCString():
				cstringArgumentsBitfield |= (1 << argumentNumber)
				cstrings += 1
			argumentNumber += 1

		uniqueSuffix = "_" + self.memberOf.name + "_" + name
		# constructor is blank!
		if (self.name == ""):
			uniqueSuffix = "_" + self.memberOf.name + "_constructor"
		# destructor is just a squiggle!
		elif (self.name == "~"):
			uniqueSuffix = "_" + self.memberOf.name + "_destructor"
		printingHash = {}
		printingHash["classname"] = "xMethod" + uniqueSuffix
		printingHash["variablename"] = "instance" + uniqueSuffix
		printingHash["address"] = self.returnType.typeCPP + " (" + classModifier + "*address)(" + arguments + ")"
		printingHash["function"] = self.memberOf.name + "::" + name
		printingHash["methodname"] = name
		printingHash["argumentCount"] = str(len(self.arguments) + cstrings)
		printingHash["floatArgumentsBitfield"] = str(floatArgumentsBitfield)
		printingHash["cstringArgumentsBitfield"] = str(cstringArgumentsBitfield)
		printingHash["flags"] = calltype + " | " + returnString
		fprint(f, """
struct %(classname)s
	{
	char *name;
	int argumentCount;
	int floatArgumentBitfield;
	int cstringArgumentBitfield;
	int flags;
	%(address)s;
	int zero;
	};

static %(classname)s %(variablename)s = { "%(methodname)s", %(argumentCount)s, %(floatArgumentsBitfield)s, %(cstringArgumentsBitfield)s, %(flags)s, %(function)s, 0 };
""" % printingHash)





class xClass:
	def __init__(self, name):
		self.members = []
		self.methods = []
		self.verbatim = []
		self.name = name
		self.superclass = None
		self.superclassName = None
		self.containsVtable = 0
		self.vtableEntries = 0
		self.firstMember = None
		self.memberCellsTotal = 0
		self.thunkedSubclass = None
		self.pureVirtual = 0
		self.setFiclVmName(None)
		classes.append(self)


	def subclassOf(self, superclass):
		if type(superclass) == types.StringType:
			self.superclassName = superclass
		else:
			self.superclass = superclass
			self.superclassName = superclass.name
			if superclass.containsVtable:
				self.containsVtable = 2
				self.pureVirtual = superclass.pureVirtual
				self.vtableEntries = superclass.vtableEntries
			else:
				self.containsVtable = 0
		return self

	def thunkedSubclassOf(self, superclass):
		self.subclassOf(superclass)
		self.addMember(xVariable("ficlClass", "void *"))
		for method in superclass.methods:
			if not method.isClassSpecial() or method.isPureVirtual():
				method = copy.deepcopy(method)
				if method.isPureVirtual():
					method.setVirtual(1)
				self.addThunkedMethod(method)
		self.constructor = xMethod("")
		self.addMethod(self.constructor)
		self.thunkedSubclass = 1
		return self

	def forwardDeclare(self):
		xAddHHeader("class " + self.name + ";")


	def addVerbatim(self, v):
		self.verbatim.append(v)
		return self

	def addMember(self, variable):
		self.members.append(variable)
		self.memberCellsTotal += variable.getTotalSize()
		if (self.firstMember == None):
			self.firstMember = variable
		return self

	def removeMember(self, variable):
		self.members.remove(variable)
		self.memberCellsTotal -= variable.getTotalSize()
		if (self.firstMember == variable):
			self.firstMember = self.members[0]
		return self

	def addMemberArray(self, array):
		map(self.addMember, copy.deepcopy(array))


	def findPreviousInstanceOfVirtualMethod(self, name):
		for method in self.methods:
			if method.name == name:
				return method
		if (self.superclass != None) and (type(self.superclass) != types.StringType):
			return self.superclass.findPreviousInstanceOfVirtualMethod(name)
		return None

	def setFiclVmName(self, name):
		self.ficlVmName = name
		return self

	def getFiclVmName(self):
		if self.ficlVmName != None:
			return self.ficlVmName

		global ficlVmName
		return ficlVmName

	def addMethod(self, method):
		method.memberOf = self
		if method.virtual:
			previousInstance = self.findPreviousInstanceOfVirtualMethod(method.name)
			if (previousInstance != None):
				method.vtableOffset = previousInstance.vtableOffset
				if previousInstance.isPureVirtual() and (not method.isPureVirtual()):
					self.pureVirtual -= 1
			else:
				method.vtableOffset = self.vtableEntries
				self.vtableEntries = self.vtableEntries + 1
				if (not self.containsVtable):
					self.containsVtable = 1
				if method.isPureVirtual():
					self.pureVirtual += 1
		self.methods.append(method)
		return self

	def lookupMethod(self, methodName):
		for m in self.methods:
			if (m.name == methodName):
				return m
		return None

	def removeMethod(self, method):
		if (type(method) == types.StringType):
			method = self.lookupMethod(method)
			if method == None:
				return None
		method.memberOf = None
		self.methods.remove(method)
		if method.virtual:
			previousInstance = self.findPreviousInstanceOfVirtualMethod(method.name)
			if (previousInstance == None):
				for m in self.methods:
					if (m.vtableOffset >= method.vtableOffset):
						m.vtableOffset = m.vtableOffset - 1
				self.vtableEntries = self.vtableEntries - 1
				if (self.vtableEntries == 0):
					self.containsVtable = 0
				if previousInstance.isPureVirtual() and (not method.isPureVirtual()):
					self.pureVirtual += 1
			else:
				if method.isPureVirtual():
					self.pureVirtual -= 1

		if method.thunkVariable != None:
			self.removeMember(method.thunkVariable)

		return self

	def addThunkedMethod(self, method):
		method = copy.deepcopy(method)
		self.addMethod(method)
		name = capitalize(method.name)
		if (method.isClassSpecial()):
			if (name == ""):
				name = "Constructor"
			else:
				name = "Destructor"
		thunkVariable = xVariable("xt" + name, "ficlWord *")
		self.addMember(thunkVariable)
		method.setThunkVariable(thunkVariable)
		return self

	def addNoopConstructor(self):
		self.addVerbatim(self.name + "() { }")
		return self

	def addConstructor(self, virtual = 0):
		method = xMethod("")
		method.setVirtual(virtual)
		self.addMethod(method)
		return method

	def addDestructor(self, virtual = 0):
		method = xMethod("~")
		method.setVirtual(virtual)
		self.addMethod(method)
		return method

	def addMemberWithAccessors(self, variable, writeBodiesToo = 1):
		self.addMember(variable)
		capitalizedName = capitalize(variable.name)

		m = xMethod("set" + capitalizedName, "void").addArgument(variable)
		if writeBodiesToo:
			m.setBody("\t{ this->" + variable.name + " = " + variable.name + "; }")
		self.addMethod(m)

		m = xMethod("get" + capitalizedName, variable.typeCPP)
		if writeBodiesToo:
			m.setBody("\t{ return this->" + variable.name + "; }")
		self.addMethod(m)

	def addMethodArray(self, array):
		map(self.addMethod, copy.deepcopy(array))

	def addThunkedMethodArray(self, array):
		map(self.addThunkedMethod, copy.deepcopy(array))

	def printHforward(self, f):
		fprint(f, "class " + self.name + ";")

	def printH(self, f):
		if (self.thunkedSubclass):
			body = "\n\t\t{\n"
			for m in self.methods:
				if m.thunkVariable != None:
					body += "\t\t" + m.thunkVariable.name + " = NULL;\n"
			body += "\t\t}\n"
			self.constructor.setBody(body)
		s = ""
		if self.superclassName != None:
			s = " : public " + self.superclassName
		fprint(f, "class " + self.name + s)
		fprint(f, "\t" + "{")
		fprint(f, "\t" + "public:")
		fprint(f, "")
		for member in self.members:
			member.printH(f)
		fprint(f, "")
		for method in self.methods:
			method.printH(f)
		for v in self.verbatim:
			fprint(f, "\t" + v + "\n")
		fprint(f, "\t" + "};\n\n")

	def printF(self, f):
		s = self.superclassName
		if s == None:
			s = "object"

		fprint(f, "")
		fprint(f, "//")
		fprint(f, "// " + self.name)
		fprint(f, "//")
		fprint(f, ": declare-" + self.name)
		fprint(f, "\t" + "S\" " + s + " subclass " + self.name + " \" evaluate")
		fprint(f, "")
		if self.containsVtable == 1:
			fprint(f, "\t" + "S\" cell: .vtable\" evaluate")
		for member in self.members:
			member.printF(f)
		fprint(f, "")
		if (self.firstMember == None):
			fprint(f, "\t" + "S\" : default-init 2drop ; \" evaluate  // no members!")
		else:
			storeFiclClass = ""
			if (self.thunkedSubclass != None):
				storeFiclClass = "this this my=> .ficlClass ! drop "
			setVtable = ""
			if self.containsVtable and (not self.pureVirtual):
				setVtable = self.name + "-vtable this my=> .vtable ! "
			fprint(f, "\t" + "S\" : default-init { 2:this -- } this my=> super my=> init this my=> ." + self.firstMember.name + "  " + str(self.memberCellsTotal) + " cells  0 fill " + setVtable + storeFiclClass + "; \" evaluate")
		fprint(f, "\t// " + self.name + " methods:")
		fprint(f, "\t" + self.name + "-declare-methods")
		for method in self.methods:
			method.printF(f)
		fprint(f, "\t;")
		fprint(f, "")
		fprint(f, ": end-" + self.name)
		fprint(f, "\t" + "S\" end-class \" evaluate")
		fprint(f, "\t" + "S\" " + self.name + " 2constant " + self.name + ".constant \" evaluate")
		fprint(f, "\t;")
		fprint(f, "")

	def printCPP(self, f):
		fprint(f, "//")
		fprint(f, "// " + self.name)
		fprint(f, "//")
		for method in self.methods:
			method.printCPP(f)
		fprint(f, "")
		fprint(f, "// " + self.name + " final structure")
		fprint(f, "static xMethod *" + self.name + "_methods[] =")
		fprint(f, "\t" + "{")
		for method in self.methods:
			if (method.isVirtual() or method.isClassSpecial()):
				continue
			fprint(f, "\t" + "(xMethod *)(&instance_" + self.name + "_" + method.name + "),")
		fprint(f, "\t" + "NULL")
		fprint(f, "\t" + "};")
		if self.containsVtable and (not self.pureVirtual):
			fprint(f, "")
			fprint(f, "// " + self.name + " instance, so we can get the vtable")
			fprint(f, "static " + self.name + " " + self.name + "_instance;" )
		fprint(f, "")



def xclassesFooter():
	f = open("xclasses.h", "wt")

	fprintHeader(f)
	fprint(f, "#ifndef __XCLASSES_H")
	fprint(f, "#define __XCLASSES_H")
	fprint(f, "")
	fprint(f, "extern void xclassesDefineMethods(ficlVm *vm);")
	fprint(f, "")
	fprint(f, "enum xtype");
	fprint(f, "\t{");
	fprint(f, "\txtypeInvalid = 0,");
	for c in classes:
		fprint(f, "\txtype_" + c.name + ",");
	fprint(f, "\txtypeLast,");
	fprint(f, "\t};");
	fprint(f, "");
	for line in h_headers:
		fprint(f, line)
		fprint(f, "")
	fprint(f, "")
	for c in classes:
		c.printH(f)
	for line in h_footers:
		fprint(f, line)
		fprint(f, "")
	fprint(f, "#endif // __XCLASSES_H")
	fprintFooter(f)
	f.close()


	f = open("xclasses.f", "wt")
	fprintHeader(f)
	fprint(f, ": use-default-init S\" : init { 2:this } this my=> super my=> init  this my=> default-init  ; \" evaluate ;");
	for line in ficl_headers:
		fprint(f, line)
		fprint(f, "")
	for c in classes:
		c.printF(f)
	for line in ficl_footers:
		fprint(f, line)
		fprint(f, "")
	fprintFooter(f)
	f.close()


	f = open("xclasses.cpp", "wt")
	fprintHeader(f)

	for line in c_headers:
		fprint(f, line)
		fprint(f, "")

	fprint(f, "#include \"xclasses.h\"")
	fprint(f, """

struct xMethod
	{
	char *name;
	int argumentCount;
	int floatArgumentBitfield;
	int cstringArgumentBitfield;
	int flags;
	void *address;
	int zero;
	};

struct xClass
	{
	char *name;
	xMethod **methods;
	void **instance;
	};

""")

	for c in classes:
		c.printCPP(f)
	fprint(f, """
static xClass classes[] =
	{
""")
	for c in classes:
		vtableVariable = "NULL"
		if c.containsVtable and (not c.pureVirtual):
			vtableVariable = "(void **)&" + c.name + "_instance"
		fprint(f, "\t" + "{ \"" + c.name + "\", " + c.name + "_methods, " + vtableVariable + " },")
	fprint(f, """
	{ NULL, NULL }
	};

void xclassesDefineMethods(ficlVm *vm)
	{
	char buffer[1024];
	xClass *c;
	xMethod **m;
	
	for (c = classes; c->name != NULL; c++)
		{
		sprintf(buffer, " : %s-declare-methods ", c->name);
		ficlVmEvaluate(vm, buffer);
		for (m = c->methods; *m != NULL; m++)
			{
			xMethod *method = *m;
			/* why is this here?  I dunno, but MSVC seems to be packing my struct.  So if address is zero, the next dword has the address.  --lch */
			if (method->address == NULL)
				method->address = (void *)method->zero;
			sprintf(buffer, " S\\" : %s drop %d %d %d %d %d multicall ; \\" evaluate ",
				method->name,
				method->argumentCount,
				method->floatArgumentBitfield,
				method->cstringArgumentBitfield,
				method->address,
				method->flags
				);
			ficlVmEvaluate(vm, buffer);
			}
		ficlVmEvaluate(vm, " ; ");
		if (c->instance != NULL)
			{
			sprintf(buffer, "%s-vtable", c->name);
			ficlDictionarySetConstantPointer(ficlVmGetDictionary(vm), buffer, *(c->instance));
			}
		}
	}
""")
	for line in c_footers:
		fprint(f, line)
		fprint(f, "")
	fprintFooter(f)
	f.close()