diff --git a/.gitignore b/.gitignore index 7ed1b30..496ee2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ -nimcache/ -.DS_Store -/godot/apigen/apigen -/godot/apigen/apigen.exe +.DS_Store \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index eec510f..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "editor.tabSize": 2, - "editor.rulers": [80], - "editor.insertSpaces": true, - "editor.detectIndentation": false, - "files.eol": "\n", - "files.trimTrailingWhitespace": true, - "nim.project": ["godot/core/godot.nim", - "godot/apigen/apigen.nim"], - "nim.buildOnSave": false, - "nim.lintOnSave": true, - "nim.licenseString": "# Copyright 2017 Xored Software, Inc.\n\n", - "files.exclude": { - "**/nimcache/**": true - } -} \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 16a448a..0000000 --- a/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -godot-nim - Nim bindings for Godot Engine - -The MIT License - -Copyright (c) 2017 Xored Software Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH -THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 40ee649..0000000 --- a/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Nim bindings for Godot Engine - -Nim bindings for Godot 3.0 based on [GDNative](https://godotengine.org/article/dlscript-here). - -## Links - -- [Godot Engine](http://www.godotengine.org/) -- [Nim](http://nim-lang.org/) - -## License - -This project is licensed under the MIT license. Read [LICENSE](https://github.com/pragmagic/godot-nim/blob/master/LICENSE) file for details. - -Copyright (c) 2017 Xored Software, Inc. \ No newline at end of file diff --git a/godot.nimble b/godot.nimble deleted file mode 100644 index a1e3696..0000000 --- a/godot.nimble +++ /dev/null @@ -1,7 +0,0 @@ -[Package] -name: "godot" -version: "0.1.0" -author: "Xored Software, Inc." -description: "Godot Engine bindings" -license: "MIT" -srcDir: "godot" diff --git a/godot/apigen/apigen.nim b/godot/apigen/apigen.nim deleted file mode 100644 index 711400a..0000000 --- a/godot/apigen/apigen.nim +++ /dev/null @@ -1,862 +0,0 @@ -# Copyright 2017 Xored Software, Inc. - -import streams, json, ospaths, strutils, times, sets, tables -import sequtils, algorithm -import compiler / [ast, renderer, idents, astalgo] - -proc ident(ident: string): PNode = - result = newNode(nkIdent) - result.ident = getIdent(ident) - -proc addChain(node: PNode, others: varargs[PNode]): PNode {.discardable.} = - for other in others: - node.add(other) - result = node - -proc newIdentDefs(name, typ: PNode, - defaultVal: PNode = nil): PNode = - result = newNode(nkIdentDefs) - result.add(name) - result.add(typ) - if defaultVal.isNil: - result.add(newNode(nkEmpty)) - else: - result.add(defaultVal) - -proc newProc(name: PNode, params: openArray[PNode], body = newNode(nkEmpty), - procType = nkProcDef): PNode = - result = newNode(procType) - result.add(name) - result.add(newNode(nkEmpty)) - result.add(newNode(nkEmpty)) - let formalParams = newNode(nkFormalParams) - for param in params: - formalParams.add(param) - result.add(formalParams) - result.add(newNode(nkEmpty)) - result.add(newNode(nkEmpty)) - result.add(body) - -proc newIfStmt(condition, body: PNode): PNode = - result = newNode(nkIfStmt) - let branch = newNode(nkElifBranch) - result.add(branch) - branch.add(condition) - branch.add(body) - -proc newCall(theProc: PNode, args: varargs[PNode]): PNode = - result = newNode(nkCall) - result.add(theProc) - for arg in args: - result.add(arg) - -proc newCall(theProc: string, args: varargs[PNode]): PNode = - newCall(ident(theProc), args) - -proc newStrLit(s: string): PNode = - result = newNode(nkStrLit) - result.strVal = s - -proc newRStrLit(s: string): PNode = - result = newNode(nkRStrLit) - result.strVal = s - -proc newCStringLit(s: string): PNode = - newNode(nkCallStrLit).addChain(ident("cstring"), newRStrLit(s)) - -proc newIntLit(val: BiggestInt): PNode = - result = newNode(nkIntLit) - result.intVal = val - -proc newInt64Lit(val: BiggestInt): PNode = - result = newNode(nkInt64Lit) - result.intVal = val - -proc newFloatLit(val: BiggestFloat): PNode = - result = newNode(nkFloatLit) - result.floatVal = val - -proc newFloat64Lit(val: BiggestFloat): PNode = - result = newNode(nkFloat64Lit) - result.floatVal = val - -proc newNilLit(): PNode = - result = newNode(nkNilLit) - -proc newDotExpr(left, right: PNode): PNode = - result = newNode(nkDotExpr) - result.add(left) - result.add(right) - -proc newCommand(left, right: PNode): PNode = - result = newNode(nkCommand) - result.add(left) - result.add(right) - -proc newInfix(left, op, right: PNode): PNode = - result = newNode(nkInfix) - result.add(op) - result.add(left) - result.add(right) - -proc newInfix(left: PNode, op: string, right: PNode): PNode = - newInfix(left, ident(op), right) - -proc postfix(left: PNode, op: string): PNode = - result = newNode(nkPostfix) - result.add(ident(op)) - result.add(left) - -proc newExprColonExpr(key, value: PNode): PNode = - newNode(nkExprColonExpr).addChain(key, value) - -proc pragma(key: PNode, value: PNode): PNode = - newNode(nkPragma).addChain(key, value) - -proc newBracketExpr(first, bracket: PNode): PNode = - newNode(nkBracketExpr).addChain(first, bracket) - -proc newEmptyNode(): PNode = - newNode(nkEmpty) - -type GodotType = ref object - name: string - godotName: string - baseName: string - doc: string - derivedCount: int - isSingleton: bool - jsonNode: JsonNode - -proc findCommonRoot(typeRegistry: Table[string, GodotType], - types: seq[string]): string = - if types.len == 0: return "Object" - var chains = newSeq[seq[string]](types.len) - for idx, typ in types: - chains[idx] = newSeq[string]() - chains[idx].add(typ) - var curTyp = typeRegistry[typ] - while curTyp.baseName != "NimGodotObject": - chains[idx].add(curTyp.baseName) - curTyp = typeRegistry[curTyp.baseName] - chains[idx].reverse() - - var idx = 0 - while true: - var curType: string - var allEqual = true - for chain in chains: - if idx >= chain.len: - allEqual = false - break - if curType.isNil: - curType = chain[idx] - elif curType != chain[idx]: - allEqual = false - break - if allEqual: - result = curType - else: - break - inc idx - -proc incDerivedCount(types: var Table[string, GodotType], - className: string) = - let typ = types.getOrDefault(className) - if not typ.isNil: - inc typ.derivedCount - types.incDerivedCount(typ.baseName) - -proc toNimType(godotType: string): string = - case godotType: - of "float": "float64" - of "int": "int64" - of "String": "string" - of "File": "godottypes.File" - else: godotType - -proc toNimType(types: Table[string, GodotType], godotType: string): string = - let origType = - if godotType.contains(','): - findCommonRoot(types, godotType.split(',')) - else: - godotType - result = toNimType(origType) - -proc toNimStyle(name: string): string = - result = newStringOfCap(name.len + 1) - var i = 0 - var makeUpperCase: bool - while i < name.len: - if name[i] == '_': - if result.len != 0: - makeUpperCase = true - elif name[i] != '/': # remove slashes - if makeUpperCase: - result.add(name[i].toUpperAscii()) - makeUpperCase = false - else: - result.add(name[i]) - inc i - - while isKeyword(getIdent(result)): - result.add(result[result.len - 1]) - - if result == "result": - result = "resultVal" - -proc newRefTypeNode(typeSection: PNode, typ, base, doc: string, - isExported = true) = - let typeDef = newNode(nkTypeDef) - typeSection.add(typeDef) - if isExported: - typeDef.add(postfix(ident(typ), "*")) - else: - typeDef.add(ident(typ)) - - typeDef.add(newNode(nkEmpty)) # generic - let objTy = newNode(nkObjectTy) - let refTy = newNode(nkRefTy) - refTy.add(objTy) - typeDef.add(refTy) - - objTy.add(newNode(nkEmpty)) - let inherit = newNode(nkOfInherit) - inherit.add(ident(base)) - objTy.add(inherit) - - if not doc.isNil: - let recList = newNode(nkRecList) - let docNode = newNode(nkCommentStmt) - docNode.comment = doc - recList.add(docNode) - objTy.add(recList) - else: - objTy.add(newNode(nkEmpty)) - -proc makeConstSection(constObj: JsonNode): PNode = - if constObj.len == 0: - return newNode(nkEmpty) - - result = newNode(nkConstSection) - for field, val in constObj: - let def = newNode(nkConstDef) - def.add(postfix(ident(field), "*")) - def.add(newNode(nkEmpty)) # infer type - var valNode: PNode - if val.kind == JInt: - valNode = newInt64Lit(val.num) - # valNode.intVal = val.num - elif val.kind == JFloat: - valNode = newFloat64Lit(val.fnum) - elif val.kind == JBool: - valNode = ident($val.bval) - elif val.kind == JString: - valNode = newNode(nkStrLit) - valNode.strVal = val.str - else: - raise newException(ValueError, "Unexpected constant kind: " & $val.kind) - def.add(valNode) - result.add(def) - -type MethodArg = tuple[name, typ: string, defaultVal: JsonNode, isVarargs: bool] - -const standardTypes = toSet( - ["bool", "cint", "int", "uint8", "int8", "uint16", "int16", "uint32", "int32", - "uint64", - "int64", "float32", "cfloat", "float64", "GodotString", "Vector2", "Rect2", - "Vector3", "Transform2D", "Plane", "Quat", "Rect3", "Basis", "Transform", - "Color", "NodePath", "RID", "Dictionary", "Array", "PoolByteArray", - "PoolIntArray", "PoolRealArray", "PoolStringArray", "PoolVector2Array", - "PoolVector3Array", "PoolColorArray", "Variant"]) - -const smallIntTypes = toSet(["uint8", "int8", "uint16", "int16", "uint32", "int32", - "cint", "int", "Error"]) -const int64Types = toSet(["uint64", "int64"]) -const intTypes = union(smallIntTypes, int64Types) -const float64Types = toSet(["float64", "cdouble"]) -const float32Types = toSet(["float32", "cfloat"]) -const floatTypes = union(float64Types, float32Types) -const arrayTypes = toSet(["Array", "PoolByteArray", - "PoolIntArray", "PoolRealArray", "PoolStringArray", "PoolVector2Array", - "PoolVector3Array", "PoolColorArray"]) - -proc newNilCheck(ident, toAssign: PNode): PNode = - newIfStmt(newCall("isNil", ident), - newNode(nkAsgn).addChain(ident).addChain( - toAssign - ) - ) - -proc singletonName(typ: string): PNode = - ident("singleton" & typ) - -proc makeDefaultValue(arg: MethodArg): PNode = - result = newEmptyNode() - if not arg.defaultVal.isNil: - if arg.typ == "bool": - result = ident(arg.defaultVal.str.toLowerAscii()) - elif arg.typ in intTypes: - result = newInt64Lit(parseBiggestInt(arg.defaultVal.str)) - elif arg.typ == "string" or arg.typ == "NodePath": - result = newStrLit(arg.defaultVal.str) - elif arg.typ in floatTypes: - result = newFloatLit(parseFloat(arg.defaultVal.str)) - elif arg.defaultVal.str == "Null": - if arg.typ == "Variant": - result = newCall("variant") - else: - result = newNilLit() - elif arg.typ == "Color": - result = newCall("initColor", - arg.defaultVal.str.split(',').mapIt(newFloatLit(parseFloat(it)))) - # elif arg.typ in arrayTypes and (arg.defaultVal.str == "[]" or - # arg.defaultVal.str == "[" & arg.typ & "]"): - # result = newCall("init" & arg.typ) - elif arg.typ == "RID" and arg.defaultVal.str == "[RID]": - result = newCall("initRID") - elif arg.typ == "Vector2" or arg.typ == "Vector3" or arg.typ == "Rect2": - let parts = arg.defaultVal.str.replace("(", "").replace(")", ""). - replace(" ", ""). - split(",").mapIt(newFloatLit(parseFloat(it))) - if arg.typ.endsWith("r2"): - result = newCall("vec2", parts) - elif arg.typ.endsWith('3'): - result = newCall("vec3", parts) - else: - result = newCall("initRect2", parts) - elif arg.defaultVal.str == "[Object:null]": - result = newNilLit() - elif arg.typ != "Transform" and arg.typ != "Transform2D" and - arg.typ != "Variant" and arg.typ notin arrayTypes: # TODO - raise newException( - ValueError, - "Cannot build default value from $# ($#)" % - [$arg.defaultVal, arg.typ]) - -proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string], - name: PNode, godotMethodName: string, - typ: GodotType, args: seq[MethodArg], - returnType: string, isVirtual, - isBase, withImplementation: bool) = - var body: PNode - if not withImplementation: - body = newNode(nkEmpty) - else: - var methodBindName = toNimStyle( - typ.name & "_" & godotMethodName & "_method_bind") - if methodBindName[0].isUpperAscii(): - methodBindName[0] = methodBindName[0].toLowerAscii() - if methodBindName notin methodBindRegistry: - let methodBindDecl = newNode(nkVarSection).addChain( - newIdentDefs( - newNode(nkPragmaExpr).addChain( - ident(methodBindName)).addChain( - newNode(nkPragma).addChain(ident("threadvar"))), - newNode(nkPtrTy).addChain(ident("GodotMethodBind")) - )) - tree.add(methodBindDecl) - methodBindRegistry.incl(methodBindName) - - let nilCheck = newNilCheck(ident(methodBindName), - newCall("getMethod", newCStringLit(typ.godotName), - newCStringLit(godotMethodName)) - ) - let vars = newNode(nkVarSection) - var varargsName: string - var isVarargs: bool - var staticArgsLen: int - var argLenNode = newIntLit(0) - let argsName = ident("argsToPassToGodot") - if args.len > 0: - for arg in args: - if arg.isVarargs: - varargsName = arg.name - isVarargs = true - vars.add(newIdentDefs(ident("callError"), ident("VariantCallError"))) - break - let argsAlloc = newNode(nkCast).addChain( - newNode(nkPtrTy).addChain( - newNode(nkBracketExpr).addChain( - ident("array"), ident("MAX_ARG_COUNT"), - if isVarargs: ident("Variant") else: ident("pointer")))) - staticArgsLen = if varargsName.isNil: args.len - else: args.len - 1 - if not varargsName.isNil: - argLenNode = newCall("cint", - newInfix(newIntLit(staticArgsLen), - ident("+"), - newDotExpr(ident(varargsName), ident("len")))) - argsAlloc.add(newCall("godotAlloc", newCall("cint", newInfix( - newCall("sizeof", ident("Variant")), ident("*"), - newNode(nkPar).addChain(argLenNode)))) - ) - else: - argLenNode = newCall("cint", newIntLit(staticArgsLen)) - vars.add(newIdentDefs( - ident("argsStatic"), - newNode(nkBracketExpr).addChain( - ident("array"), newIntLit(staticArgsLen), ident("pointer")), - newNode(nkEmpty))) - argsAlloc.add(newCommand(ident("addr"), ident("argsStatic"))) - vars.add(newIdentDefs(argsName, newNode(nkEmpty), argsAlloc)) - - let argConversions = newNode(nkStmtList) - for idx, arg in args: - var argName = ident(arg.name) - if arg.isVarargs: - argName = newNode(nkBracketExpr).addChain( - ident(varargsName), - newInfix(ident("idx"), ident("-"), newIntLit(staticArgsLen))) - let argIdx = if arg.isVarargs: ident("idx") else: newIntLit(idx) - let isStandardType = arg.typ in standardTypes - let convArg = if not varargsName.isNil: newCall("toGodot", argName) - elif isStandardType: newCall("unsafeAddr", argName) - elif arg.typ == "string": newCall("unsafeAddr", - ident("argToPassToGodot" & $idx)) - else: ident("argToPassToGodot" & $idx) # object - if not isStandardType and arg.isVarargs: - raise newException(ValueError, - "Non-standard type $# for varargs params of method $# of type $#" % - [arg.typ, godotMethodName, typ.godotName]) - if not isStandardType: - if arg.typ == "string": - argConversions.add(newNode(nkLetSection).addChain( - newIdentDefs(ident("argToPassToGodot" & $idx), newEmptyNode(), - newCall("toGodotString", argName)))) - else: - argConversions.add(newNode(nkLetSection).addChain( - newIdentDefs(ident("argToPassToGodot" & $idx), newEmptyNode(), - newDotExpr(argName, ident("godotObject"))))) - let argAsgn = newNode(nkAsgn).addChain( - newNode(nkBracketExpr).addChain( - newNode(nkBracketExpr).addChain(argsName), argIdx), - convArg - ) - if not arg.isVarargs: - argConversions.add( - argAsgn - ) - else: - argConversions.add(newNode(nkVarSection).addChain( - newIdentDefs(ident("idx"), newNode(nkEmpty), newIntLit(staticArgsLen)))) - let argLoop = newNode(nkWhileStmt) - argConversions.add(argLoop) - argLoop.add(newInfix(ident("idx"), ident("<"), argLenNode) - ) - argLoop.add(argAsgn) - argLoop.add(newCommand(ident("inc"), ident("idx"))) - - let retName = if isVarargs: "callRet" else: "ptrCallRet" - let theCall = newCall( - newDotExpr(ident(methodBindName), - ident(if isVarargs: "call" else: "ptrCall")), - newDotExpr(ident("self"), ident("godotObject")), - if args.len > 0: argsName else: newNode(nkNilLit), - argLenNode, - if not isVarargs: ident(retName) else: ident("callError") - ) - if not isVarargs: - theCall.sons.delete(3) - let freeCall = newCall("godotFree", argsName) - body = newNode(nkStmtList) - if typ.isSingleton: - let varName = singletonName(typ.name) - body.add(newNilCheck(varName, newCall( - newBracketExpr(ident("getSingleton"), ident(typ.name))))) - body.add(newNode(nkLetSection).addChain(newIdentDefs( - ident("self"), newNode(nkEmpty), varName - ))) - if not varargsName.isNil: - body.add( - pragma(ident("push"), newExprColonExpr( - newBracketExpr(ident("warning"), ident("Destructor")), - ident("off")))) - body.add(nilCheck) - body.add(vars) - body.add(argConversions) - if not isVarargs: - let retPtrDecl = newNode(nkVarSection).addChain( - newIdentDefs(ident(retName), ident("pointer"), newEmptyNode()) - ) - body.add(retPtrDecl) - var isVariantRet: bool - var isConversionRet: bool - var isObjRet: bool - var isStringRet: bool - let retValIdent = if not isVarargs: ident("ptrCallVal") else: ident(retName) - if not returnType.isNil and varargsName.isNil: - var addrToAssign = newCall("addr", ident("ptrCallVal")) - if returnType in smallIntTypes: - isConversionRet = true - body.add(newNode(nkVarSection).addChain(newIdentDefs( - retValIdent, ident("int64"), - ))) - elif returnType in float32Types: - isConversionRet = true - body.add(newNode(nkVarSection).addChain(newIdentDefs( - retValIdent, ident("float64"), - ))) - elif returnType in standardTypes: - addrToAssign = newCall("addr", ident("result")) - elif returnType == "string": - isStringRet = true - body.add(newNode(nkVarSection).addChain(newIdentDefs( - retValIdent, ident("GodotString"), - ))) - else: - isObjRet = true - body.add(newNode(nkVarSection).addChain(newIdentDefs( - retValIdent, newNode(nkPtrTy).addChain(ident("GodotObject")), - ))) - body.add(newNode(nkAsgn).addChain(ident(retName), addrToAssign)) - - if not isVarargs: - body.add(theCall) - else: - if not returnType.isNil: - isVariantRet = true - let callStmt = newNode(nkLetSection).addChain( - newIdentDefs( - newNode(nkPragmaExpr).addChain( - ident(retName), newNode(nkPragma).addChain(ident("used"))), - newNode(nkEmpty), theCall)) - body.add(callStmt) - # if not returnType.isNil and not varargsName.isNil: - # body.add(newCall("fromGodot", ident("result"), ident(retName))) - # body.add(newCall("deinit", ident(retName))) - if args.len > 0 and not varargsName.isNil: - body.add(freeCall) - if not varargsName.isNil: - let errCheck = newIfStmt( - newInfix(newDotExpr(ident("callError"), ident("error")), ident("!="), - newDotExpr(ident("VariantCallErrorType"), ident("OK"))), - newNode(nkRaiseStmt).addChain( - newCall("newCallError", ident("callError"))) - ) - body.add(errCheck) - if isVariantRet: - let convErrDef = newNode(nkLetSection).addChain( - newIdentDefs(ident("convErr"), newNode(nkEmpty), - newCall("fromGodot", ident("result"), retValIdent))) - let convCheck = newIfStmt( - newInfix(ident("convErr"), ident("!="), - newDotExpr(ident("ConversionResult"), ident("OK"))), - newNode(nkRaiseStmt).addChain( - newCall("newConversionError", ident("convErr"))) - ) - body.add(convErrDef) - body.add(convCheck) - if not isVarargs and (isVariantRet or isStringRet): - # varargs use call with "let" so destructor is invoked - body.add(newCall("deinit", retValIdent)) - elif isConversionRet: - body.add(newNode(nkAsgn).addChain(ident("result"), - newCall(returnType, retValIdent))) - elif isObjRet: - body.add(newNode(nkAsgn).addChain(ident("result"), - newCall(newBracketExpr(ident("asNimGodotObject"), - newCall("type", ident("result"))), retValIdent))) - elif isStringRet: - body.add(newNode(nkAsgn).addChain(ident("result"), - newCall("$", retValIdent))) - if not varargsName.isNil: - body.add( - pragma(ident("push"), newExprColonExpr( - newBracketExpr(ident("warning"), ident("Destructor")), - ident("on")))) - - let procType = if isVirtual: nkMethodDef - else: nkProcDef - var params = newSeqOfCap[PNode](args.len + 2) - if not returnType.isNil: - params.add(ident(returnType)) - else: - params.add(newNode(nkEmpty)) - if not typ.isSingleton: - params.add(newIdentDefs(ident("self"), ident(toNimType(typ.name)))) - - for arg in args: - if arg.isVarargs: - params.add(newIdentDefs(ident(arg.name), - newNode(nkBracketExpr).addChain(ident("varargs"), ident(arg.typ)))) - else: - let defaultValue = arg.makeDefaultValue() - params.add(newIdentDefs(ident(arg.name), ident(arg.typ), defaultValue)) - let procDecl = newProc(if not withImplementation: postfix(name, "*") - else: name, - params, body, procType) - if not withImplementation: - let pragma = newNode(nkPragma) - pragma.add(ident("gcsafe")) - pragma.add(newNode(nkExprColonExpr).addChain(ident("locks"), - newIntLit(0))) - if isVirtual and isBase: - pragma.add(ident("base")) - procDecl.sons[4] = pragma - tree.add(procDecl) - -proc generateMethod(tree: PNode, methodBindRegistry: var HashSet[string], - name: PNode, godotMethodName: string, - typ: GodotType, args: seq[MethodArg], - returnType: string, isVirtual, isBase, withImplementation: bool) = - doGenerateMethod( - tree, methodBindRegistry, name, godotMethodName, typ, - args, returnType, isVirtual, isBase, withImplementation) - -proc makeProperty(types: Table[string, GodotType], tree: PNode, - methodBindRegistry: var HashSet[string], - typ: GodotType, - propertyObj: JsonNode, withImplementation: bool) = - let getterNameStr = toNimStyle(propertyObj["name"].str) - let getterName = ident(toNimStyle(propertyObj["name"].str)) - let setterName = newNode(nkAccQuoted).addChain(ident(getterNameStr & "=")) - - let nimType = toNimType(types, propertyObj["type"].str) - generateMethod(tree, methodBindRegistry, - getterName, propertyObj["getter"].str, typ, - @[], nimType, isVirtual = false, - isBase = false, withImplementation) - generateMethod(tree, methodBindRegistry, - setterName, propertyObj["setter"].str, typ, - @[("val", nimType, JsonNode(nil), false)], - nil, isVirtual = false, isBase = false, withImplementation) - -proc makeMethod(types: Table[string, GodotType], tree: PNode, - methodBindRegistry: var HashSet[string], - typ: GodotType, methodObj: JsonNode, - withImplementation: bool) = - let returnType = if methodObj["return_type"].str != "void": - toNimType(types, methodObj["return_type"].str) - else: nil - - for prop in typ.jsonNode["properties"]: - if prop["getter"].str == methodObj["name"].str or - prop["setter"].str == methodObj["name"].str: - return - - var args = newSeqOfCap[MethodArg](methodObj["arguments"].len + 1) - var origArgs = methodObj["arguments"] - var isBase = methodObj["is_virtual"].bval - if methodObj["is_virtual"].bval: - proc getMethod(typNode: JsonNode, methName: string): JsonNode = - for meth in typNode["methods"]: - if meth["is_virtual"].bval and - meth["name"].str == methName: return meth - var curTyp = types.getOrDefault(typ.baseName) - while not curTyp.isNil: - let meth = curTyp.jsonNode.getMethod(methodObj["name"].str) - if not meth.isNil: - origArgs = meth["arguments"] - isBase = false - curTyp = types.getOrDefault(curTyp.baseName) - - for arg in origArgs: - let typ = toNimType(types, arg["type"].str) - let defaultVal = if arg["has_default_value"].bval: arg["default_value"] - else: nil - args.add((toNimStyle(arg["name"].str), typ, - defaultVal, false)) - if methodObj["has_varargs"].bval: - args.add(("variantArgs", "Variant", JsonNode(nil), true)) - - let godotName = methodObj["name"].str - generateMethod(tree, methodBindRegistry, - ident(toNimStyle(godotName)), methodObj["name"].str, - typ, args, - returnType, methodObj["is_virtual"].bval, isBase, - withImplementation) - -proc addAttributeToDoc(doc: var string, propName, propValue: string) = - if doc.len > 0: - doc.add("\n") - doc.add(propName & ": " & propValue) - -const classAttributes = ["singleton", "instanciable", "is_reference"] - -proc typeNameToModuleName(name: string): string = - var wasUpperOrDigit = true - result = newStringOfCap(name.len + 10) - for idx, c in name: - if c.isUpperAscii() or c.isDigit(): - if not wasUpperOrDigit or idx > 0 and idx < name.len - 1 and - name[idx + 1].isLowerAscii(): - result.add('_') - result.add(c.toLowerAscii()) - wasUpperOrDigit = true - else: - result.add(c) - wasUpperOrDigit = false - if result == "object": - # keyword - result = "objects" - if result == "os": - # to avoid clash with stdlib - result = "gd_os" - -proc newRegisterClassNode(typ: GodotType): PNode = - newCall("registerClass", - ident(typ.name), - newCStringLit(typ.godotName), - ident("true") # isNative - ) - -proc genSingletonDecl(typ: string): PNode = - result = newNode(nkVarSection) - let name = singletonName(typ) - let threadvar = newNode(nkPragma).addChain(ident("threadvar")) - result.add(newIdentDefs(newNode(nkPragmaExpr).addChain(name, threadvar), - ident(typ))) - -proc shouldExport(typ: GodotType, types: Table[string, GodotType]): bool = - var curTyp = typ - while not curTyp.isNil and not curTyp.isSingleton: - curTyp = types.getOrDefault(curTyp.baseName) - result = curTyp.isNil - -proc genTypeFile*(types: var Table[string, GodotType], targetDir: string) = - for typ in types.values(): - types.incDerivedCount(typ.baseName) - - var sortedTypes = toSeq(types.values()) - sortedTypes.sort do (x, y: GodotType) -> int: - cmp(y.derivedCount, x.derivedCount) - - let godotApiTypesTree = newNode(nkStmtList) - godotApiTypesTree.add(newNode(nkImportStmt).addChain(ident("godot"))) - let typeSection = newNode(nkTypeSection) - godotApiTypesTree.add(typeSection) - - for typ in sortedTypes: - if not typ.shouldExport(types): continue - newRefTypeNode(typeSection, typ.name, typ.baseName, typ.doc) - - for typ in sortedTypes: - if not typ.shouldExport(types): continue - let regNode = newCall("registerClass", - ident(typ.name), - newCStringLit(typ.godotName), - ident("true") # isNative - ) - godotApiTypesTree.add(regNode) - - renderModule(godotApiTypesTree, targetDir / "godottypes.nim") - -proc genApi*(targetDir: string, apiJsonFile: string) = - let apiJson = parseJson(newFileStream(apiJsonFile), - apiJsonFile.extractFilename()) - var megaImport = newStringOfCap(64 * 1024) - - var types = initTable[string, GodotType]() - for obj in apiJson: - let godotClassName = obj["name"].str - let className = godotClassName.replace("_", "") - var baseClassName = obj["base_class"].str - baseClassName = toNimType(baseClassName.replace("_", "")) - if baseClassName.isNil or baseClassName.len == 0: - baseClassName = "NimGodotObject" - var doc = "" - for attr in classAttributes: - if attr in obj: - addAttributeToDoc(doc, attr, $obj[attr]) - types[className] = GodotType( - name: className, - godotName: godotClassName, - baseName: baseClassName, - doc: doc, - jsonNode: obj, - isSingleton: obj["singleton"].bval - ) - for typ in types.values(): - let tree = newNode(nkStmtList) - let importStmt = newNode(nkImportStmt) - importStmt.add(ident("godot")) - importStmt.add(ident("godottypes")) - importStmt.add(newDotExpr(ident("core"), ident("godotcore"))) - tree.add(importStmt) - let exportStmt = newNode(nkExportStmt) - exportStmt.add(ident("godottypes")) - tree.add(exportStmt) - - var methodBindRegsitry = initSet[string]() - let obj = typ.jsonNode - if typ.baseName != "NimGodotObject": - let baseModule = typeNameToModuleName(typ.baseName) - let baseModuleNode = if getIdent(baseModule).isKeyword: - newStrLit(baseModule & ".nim") - else: - ident(baseModule) - importStmt.add(baseModuleNode) - exportStmt.add(baseModuleNode) - let constSection = makeConstSection(obj["constants"]) - if constSection.kind != nkEmpty: - # renderer doesn't properly render empty stmt after type section - tree.add(makeConstSection(obj["constants"])) - - if typ.isSingleton: - let typeDecl = newNode(nkTypeSection) - newRefTypeNode(typeDecl, typ.name, typ.baseName, typ.doc, - isExported = false) - tree.add(typeDecl) - tree.add(newRegisterClassNode(typ)) - tree.add(genSingletonDecl(typ.name)) - - # first, generate declarations only for ease of - # human readability of the file - for property in obj["properties"]: - makeProperty(types, tree, methodBindRegsitry, typ, property, - withImplementation = false) - for meth in obj["methods"]: - makeMethod(types, tree, methodBindRegsitry, typ, - meth, withImplementation = false) - - for property in obj["properties"]: - makeProperty(types, tree, methodBindRegsitry, typ, - property, withImplementation = true) - for meth in obj["methods"]: - makeMethod(types, tree, methodBindRegsitry, typ, meth, - withImplementation = true) - - let moduleName = typeNameToModuleName(typ.name) - renderModule(tree, targetDir / moduleName & ".nim") - - megaImport.add("import \"" & moduleName & "\"\n") - - genTypeFile(types, targetDir) - - writeFile(targetDir / "godotall.nim", megaImport) - -when isMainModule: - import os - - const usage = slurp("usage.txt") - - proc main() = - if paramCount() < 2: - echo usage - quit(-1) - let godotBin = paramStr(1) - let targetDir = paramStr(2) - - if not dirExists(targetDir): - echo "The specified directory does not exist" - quit(-1) - if not fileExists(godotBin): - echo "Invalid godot binary path" - quit(-1) - - let jsonFile = targetDir / "api.json" - if fileExists(jsonFile) and - godotBin.getLastModificationTime() <= jsonFile.getLastModificationTime(): - echo "api.json if up to date - doing nothing." - return - - discard execShellCmd( - "\"" & godotBin & "\" --gdnative-generate-json-api \"" & jsonFile & "\"") - if not fileExists(jsonFile): - echo "Failed to generate api.json" - quit(-1) - - genApi(targetDir, jsonFile) - - main() diff --git a/godot/apigen/usage.txt b/godot/apigen/usage.txt deleted file mode 100644 index 566634c..0000000 --- a/godot/apigen/usage.txt +++ /dev/null @@ -1,3 +0,0 @@ -Tool for wrapping Godot API into Nim - - apigen [godot binary path] [target directory] \ No newline at end of file diff --git a/godot/core/arrays.nim b/godot/core/arrays.nim deleted file mode 100644 index 8359fc7..0000000 --- a/godot/core/arrays.nim +++ /dev/null @@ -1,145 +0,0 @@ -# Copyright (c) 2017 Xored Software, Inc. - -import hashes -import godotbase, poolarray, variant, strings - -proc initArray(dest: var Array) {. - importc: "godot_array_new", header: "godot_array.h".} -proc initArray(dest: var Array; pca: PoolColorArray) {. - importc: "godot_array_new_pool_coloarray", - header: "godot_array.h".} -proc initArray(dest: var Array; pv3a: PoolVector3Array) {. - importc: "godot_array_new_pool_vector3_array", - header: "godot_array.h".} -proc initArray(dest: var Array; pv2a: PoolVector2Array) {. - importc: "godot_array_new_pool_vector2_array", - header: "godot_array.h".} -proc initArray(dest: var Array; psa: PoolStringArray) {. - importc: "godot_array_new_pool_string_array", - header: "godot_array.h".} -proc initArray(dest: var Array; pra: PoolRealArray) {. - importc: "godot_array_new_pool_real_array", - header: "godot_array.h".} -proc initArray(dest: var Array; pia: PoolIntArray) {. - importc: "godot_array_new_pool_int_array", - header: "godot_array.h".} -proc initArray(dest: var Array; pba: PoolByteArray) {. - importc: "godot_array_new_pool_byte_array", - header: "godot_array.h".} - -proc initArray*(): Array {.inline.} = - initArray(result) - -proc initArray*(pca: PoolColorArray): Array {.inline.} = - initArray(result, pca) - -proc initArray*(pv3a: PoolVector3Array): Array {.inline.} = - initArray(result, pv3a) - -proc initArray*(pv2a: PoolVector2Array): Array {.inline.} = - initArray(result, pv2a) - -proc initArray*(psa: PoolStringArray): Array {.inline.} = - initArray(result, psa) - -proc initArray*(pra: PoolRealArray): Array {.inline.} = - initArray(result, pra) - -proc initArray*(pia: PoolIntArray): Array {.inline.} = - initArray(result, pia) - -proc initArray*(pba: PoolByteArray): Array {.inline.} = - initArray(result, pba) - -proc `[]=`*(self: var Array; idx: cint; - value: Variant) {. - noSideEffect, - importc: "godot_array_set", header: "godot_array.h".} -proc `[]`*(self: Array; idx: cint): Variant {. - noSideEffect, - importc: "godot_array_get", header: "godot_array.h".} -proc mget*(self: var Array; idx: cint): ptr Variant {. - importc: "godot_array_operator_index", header: "godot_array.h".} - -proc add*(self: var Array; value: Variant) {. - noSideEffect, - importc: "godot_array_append", header: "godot_array.h".} -proc clear*(self: var Array) {.importc: "godot_array_clear", - header: "godot_array.h".} -proc count*(self: Array; value: Variant): cint {. - noSideEffect, - importc: "godot_array_count", header: "godot_array.h".} -proc isEmpty*(self: Array): bool {. - noSideEffect, - importc: "godot_array_empty", header: "godot_array.h".} -proc erase*(self: var Array; value: Variant) {. - importc: "godot_array_erase", header: "godot_array.h".} -proc first*(self: Array): Variant {. - noSideEffect, - importc: "godot_array_front", header: "godot_array.h".} -proc last*(self: Array): Variant {. - noSideEffect, - importc: "godot_array_back", header: "godot_array.h".} -proc find*(self: Array; what: Variant; - f: cint): cint {. - noSideEffect, - importc: "godot_array_find", header: "godot_array.h".} -proc findLast*(self: Array; what: Variant): cint {. - noSideEffect, - importc: "godot_array_find_last", header: "godot_array.h".} -proc contains*(self: Array; value: Variant): bool {. - noSideEffect, - importc: "godot_array_has", header: "godot_array.h".} - -proc godotHash*(self: Array): cint {. - noSideEffect, - importc: "godot_array_hash", header: "godot_array.h".} -proc hash*(self: Array): Hash {.inline.} = - hash(godotHash(self)) - -proc insert*(self: var Array; pos: cint; - value: Variant) {. - importc: "godot_array_insert", header: "godot_array.h".} -proc reverse*(self: var Array) {.importc: "godot_array_invert", - header: "godot_array.h".} -proc popLast*(self: var Array): Variant {. - importc: "godot_array_pop_back", header: "godot_array.h".} -proc popFirst*(self: var Array): Variant {. - importc: "godot_array_pop_front", header: "godot_array.h".} -proc addLast*(self: var Array; value: Variant) {. - importc: "godot_array_push_back", header: "godot_array.h".} -proc addFirst*(self: var Array; value: Variant) {. - importc: "godot_array_push_front", header: "godot_array.h".} - -proc delete*(self: var Array; idx: cint) {. - importc: "godot_array_remove", header: "godot_array.h".} -proc setLen*(self: var Array; size: cint) {. - importc: "godot_array_resize", header: "godot_array.h".} -proc rfind*(self: Array; what: Variant; - f: cint): cint {. - noSideEffect, - importc: "godot_array_rfind", header: "godot_array.h".} -proc len*(self: Array): cint {. - noSideEffect, - importc: "godot_array_size", header: "godot_array.h".} -proc sort*(self: var Array) {.importc: "godot_array_sort", - header: "godot_array.h".} -proc sortCustom*(self: var Array; obj: var GodotObject; - funcName: GodotString) {. - importc: "godot_array_sort_custom", header: "godot_array.h".} - -iterator items*(arr: Array): Variant = - for i in 0.. high(T) or intVal < low(T): - result = ConversionResult.RangeError - else: - self = T(intVal) - -proc fromGodot*[T: SomeFloat](self: var T, val: Variant): ConversionResult = - if val.getType() == VariantType.Real: - self = val.asReal() - elif val.getType() == VariantType.Int: - self = T(val.asInt()) - else: - result = ConversionResult.TypeError - -proc fromGodot*[T: SomeGodot](self: var T, val: Variant): ConversionResult = - if godotVariantType(T) != val.getType(): - return ConversionResult.TypeError - when self is bool: - self = val.asBool() - elif self is Vector2: - self = val.asVector2() - elif self is Rect2: - self = val.asRect2() - elif self is Vector3: - self = val.asVector3() - elif self is Transform2D: - self = val.asTransform2D() - elif self is Plane: - self = val.asPlane() - elif self is Quat: - self = val.asQuat() - elif self is Rect3: - self = val.asRect3() - elif self is Basis: - self = val.asBasis() - elif self is Transform: - self = val.asTransform() - elif self is Color: - self = val.asColor() - elif self is NodePath: - self = val.asNodePath() - elif self is RID: - self = val.asRID() - elif self is ptr GodotObject: - self = val.asObject() - elif self is Dictionary: - self = val.asDictionary() - elif self is Array: - self = val.asArray() - elif self is PoolByteArray: - self = val.asPoolByteArray() - elif self is PoolIntArray: - self = val.asPoolIntArray() - elif self is PoolRealArray: - self = val.asPoolRealArray() - elif self is PoolStringArray: - self = val.asPoolStringArray() - elif self is PoolVector2Array: - self = val.asPoolVector2Array() - elif self is PoolVector3Array: - self = val.asPoolVector3Array() - elif self is PoolColorArray: - self = val.asPoolColorArray() - elif self is GodotString: - self = val.asGodotString() - else: - # mustn't reach this - result = ConversionError.TypeError - -proc godotVariantType*(T: typedesc[string]): VariantType {.inline.} = - VariantType.String - -proc toGodot*(s: string): Variant {.inline.} = - variant(s.toGodotString()) - -proc fromGodot*(s: var string, val: Variant): ConversionResult = - if val.getType() != VariantType.String: - result = ConversionResult.TypeError - else: - s = $val.asGodotString() - -proc godotVariantType*(T: typedesc[seq]): VariantType = - VariantType.Array - -proc godotVariantType*(T: typedesc[array]): VariantType = - VariantType.Array - -proc toGodot*[T](s: openarray[T]): Variant = - var arr: Array - initArray(arr) - mixin toGodot - for item in s: - arr.add(toGodot(item)) - result = variant(arr) - -proc fromGodot*[T](s: var seq[T], val: Variant): ConversionResult = - if val.getType() != VariantType.Array: - result = ConversionResult.TypeError - else: - let arr = val.asArray() - s = newSeq[T](arr.len) - for idx, item in arr: - mixin fromGodot - let convResult = fromGodot(s[idx], item) - if convResult != ConversionResult.OK: - s = nil - return convResult - -proc fromGodot*[T: array](s: var T, val: Variant): ConversionResult = - if val.getType() != VariantType.Array: - result = ConversionResult.TypeError - else: - let arr = val.asArray() - if s.len != arr.len: - return ConversionResult.TypeError - for idx, item in arr: - mixin fromGodot - let convResult = fromGodot(s[idx], item) - if convResult != ConversionResult.OK: - return convResult - -proc godotVariantType*(T: typedesc[Table|TableRef]): VariantType {.inline.} = - VariantType.Dictionary - -proc toGodot*[T: Table or TableRef](t: T): Variant = - var dict: Dictionary - initDictionary(dict) - mixin toGodot - for k, v in t.pairs(): - dict[toGodot(k)] = toGodot(v) - result = variant(dict) - -proc fromGodot*[T: Table or TableRef](t: var T, val: Variant): ConversionResult = - if val.getType() != VariantType.Dictionary: - result = ConversionResult.TypeError - else: - let dict = val.asDictionary() - mixin fromGodot - when t is Table: - t = initTable[type(t.keys()), type(t.values())]() - else: - t = newTable[type(t.keys()), type(t.values())]() - for k, v in dict: - var nimKey: type(t.keys()) - var nimVal: type(t.values()) - let keyResult = fromGodot(nimKey, k) - if keyResult != ConversionResult.OK: - when t is ref: - t = nil - return keyResult - let valResult = fromGodot(nimVal, v) - if valResult != ConversionResult.OK: - when t is ref: - t = nil - return valResult - t[nimKey] = nimVal - -proc godotToNim*[T](val: Variant): (T, ConversionResult) = - mixin fromGodot - result[1] = fromGodot(result[0], val) - -proc nimToGodot*[T](val: T): Variant = - mixin toGodot - when compiles(toGodot(val)): - result = toGodot(val) - else: - printError("Failed to convert Nim value of type " & T.name & - " into Variant") - initNilVariant(result) - -{.emit: """/*TYPESECTION*/ -void NimMain(void); -N_NOINLINE(void, setStackBottom)(void* thestackbottom); -""".} - -const stackIncreases = defined(emscripten) - -proc godot_native_init() {.cdecl, exportc, dynlib.} = - let stackBottom = godotStackBottom() - {.emit: """ - NimMain(); - setStackBottom(`stackBottom`); - """.} - GC_fullCollect() - GC_disable() - -proc godot_native_terminate() {.cdecl, exportc, dynlib.} = - discard diff --git a/godot/nimruntime.nim b/godot/nimruntime.nim deleted file mode 100644 index 0a55844..0000000 --- a/godot/nimruntime.nim +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2017 Xored Software, Inc. - -import asyncdispatch -import godot, node - -gdobj NimRuntime of Node: - method process*(delta: float32) = - if asyncdispatch.hasPendingOperations(): - poll(0) - GC_step(2000, false, 0) - -when not defined(release): - onUnhandledException = proc(errorMsg: string) = - printError("Unhandled Nim exception: " & errorMsg)