ref: 7c76dac1b268038f567939a70a18228e790a5cbc
dir: /contrib/xclasses/xclasses.py/
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()