From 19b0c23d6223e448d512d6637531b53e943f76e7 Mon Sep 17 00:00:00 2001 From: Ruslan Mustakov Date: Sun, 15 Sep 2019 15:39:10 +0700 Subject: [PATCH] Finalize support for Nim 0.20 Closes #32, closes #41. --- godot.nimble | 2 +- godot/core/godotbase.nim | 15 ++-- godot/godotapigen.nim | 18 ++-- godot/internal/backwardcompat.inc.nim | 19 +++++ godot/internal/godotstrings.nim | 8 +- godot/nim/godotmacros.nim | 116 ++++---------------------- godot/nim/godotnim.nim | 42 +++++----- godot/nim/nim.cfg | 2 +- 8 files changed, 91 insertions(+), 131 deletions(-) create mode 100644 godot/internal/backwardcompat.inc.nim diff --git a/godot.nimble b/godot.nimble index 66794fca..c5472c91 100644 --- a/godot.nimble +++ b/godot.nimble @@ -1,4 +1,4 @@ -version = "0.7.20" +version = "0.7.21" author = "Xored Software, Inc." description = "Godot Engine bindings" license = "MIT" diff --git a/godot/core/godotbase.nim b/godot/core/godotbase.nim index d76f5443..7b5b8047 100644 --- a/godot/core/godotbase.nim +++ b/godot/core/godotbase.nim @@ -30,14 +30,17 @@ proc stepify*(value, step: float32): float32 {.inline, noinit.} = else: value -proc min*(x, y: float32): float32 {.inline, noinit.} = - if x <= y: x else: y +when (NimMajor, NimMinor, NimPatch) < (0, 20, 4): + # Newer Nim has these procs in system module -proc abs*(x: float32): float32 {.inline, noinit.} = - if x < 0.0: -x else: x + proc min*(x, y: float32): float32 {.inline, noinit.} = + if x <= y: x else: y -proc max*(x, y: float32): float32 {.inline, noinit.} = - if y <= x: x else: y + proc abs*(x: float32): float32 {.inline, noinit.} = + if x < 0.0: -x else: x + + proc max*(x, y: float32): float32 {.inline, noinit.} = + if y <= x: x else: y {.pop.} # stackTrace: off diff --git a/godot/godotapigen.nim b/godot/godotapigen.nim index a53ee6f6..ca0dfaff 100644 --- a/godot/godotapigen.nim +++ b/godot/godotapigen.nim @@ -2,7 +2,12 @@ import streams, json, os, strutils, times, sets, tables, options import sequtils, algorithm -import compiler/ast, compiler/renderer, compiler/idents, compiler/astalgo, compiler/lineinfos +import compiler/ast, compiler/renderer, compiler/idents, compiler/astalgo + +include "internal/backwardcompat.inc.nim" + +when (NimMajor, NimMinor, NimPatch) >= (0, 19, 0): + import compiler/lineinfos when (NimMajor, NimMinor, NimPatch) >= (0, 19, 0): var gic = newIdentCache() @@ -14,10 +19,13 @@ proc ident(ident: string): PNode = result = PNode(kind: nkIdent, ident: getIdent(ident)) proc newPNode(kind: TNodeKind): PNode = - result = PNode(kind: kind) - result.info.fileIndex = InvalidFileIdx - result.info.col = int16(-1) - result.info.line = uint16(0) + when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): + result = newNode(kind) + else: + result = PNode(kind: kind) + result.info.fileIndex = InvalidFileIdx + result.info.col = int16(-1) + result.info.line = uint16(0) proc addChain(node: PNode, others: varargs[PNode]): PNode {.discardable.} = for other in others: diff --git a/godot/internal/backwardcompat.inc.nim b/godot/internal/backwardcompat.inc.nim new file mode 100644 index 00000000..9d318444 --- /dev/null +++ b/godot/internal/backwardcompat.inc.nim @@ -0,0 +1,19 @@ +# Copyright 2019 Xored Software, Inc. + +when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): + import macros, sets + + proc strVal(n: NimNode): string {.used.} = + case n.kind + of nnkIdent: + $n.ident + of nnkSym: + $n.symbol + else: + macros.strVal(n) + + proc toHashSet[A](keys: openArray[A]): HashSet[A] {.inline, used.} = + toSet(keys) + + proc initHashSet[A](): HashSet[A] {.inline, used.} = + initSet[A]() diff --git a/godot/internal/godotstrings.nim b/godot/internal/godotstrings.nim index 18bcf635..e67acfd3 100644 --- a/godot/internal/godotstrings.nim +++ b/godot/internal/godotstrings.nim @@ -38,7 +38,13 @@ proc `$`*(self: GodotString): string = proc toGodotString*(s: string): GodotString {.inline.} = ## Converts the Nim string into ``GodotString`` - initGodotString(result, cstring(s), cint(s.len + 1)) + when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): + if s.isNil: + initGodotString(result) + else: + initGodotString(result, cstring(s), cint(s.len + 1)) + else: + initGodotString(result, cstring(s), cint(s.len + 1)) proc toGodotString*(s: cstring): GodotString {.inline.} = ## Converts the cstring into ``GodotString`` diff --git a/godot/nim/godotmacros.nim b/godot/nim/godotmacros.nim index f6a613ad..90e9ea77 100644 --- a/godot/nim/godotmacros.nim +++ b/godot/nim/godotmacros.nim @@ -32,6 +32,8 @@ type ParseError = object of Exception +include "internal/backwardcompat.inc.nim" + proc godotToNim[T](val: Variant): (T, ConversionResult) = mixin fromVariant result[1] = fromVariant(result[0], val) @@ -432,73 +434,6 @@ proc toGodotStyle(s: string): string {.compileTime.} = else: result.add(c) -proc checkReplaceSelfVar(node: NimNode, childIndex: int, varsList, propsList: seq[string]) {.compileTime.} = - let identName = node[childIndex].strVal - if varsList.binarySearch(identName) == -1 and propsList.binarySearch(identName) != -1: - var newChild = newDotExpr(ident("self"), node[childIndex]) - node.del childIndex - node.insert(childIndex, newChild) - -proc checkReplaceSelfMethod(node: NimNode, childIndex: int, methodsList: seq[string]) {.compileTime.} = - if methodsList.binarySearch(node[childIndex].strVal) != -1: - var newChild = newDotExpr(ident("self"), node[childIndex]) - node.del childIndex - node.insert(childIndex, newChild) - -proc applySelf(node: NimNode, varsList: ref seq[string], propsList, methodsList: seq[string]) {.compileTime.} = - var lowIndx = -1 - var highIndx = node.len - 1 - var isBlock: bool = false - case node.kind: - of nnkCommand, nnkCall: - if node[0].kind == nnkDotExpr: - lowIndx = 0 - elif node[0].kind == nnkIdent: - if node[0].strVal != "procCall": - checkReplaceSelfMethod(node, 0, methodsList) - lowIndx = 1 - else: - lowIndx = 1 - of nnkInfix..nnkPostfix, nnkExprColonExpr, nnkObjConstr, nnkCast: - lowIndx = 1 - of nnkExprEqExpr, nnkPar, nnkCurly..nnkBracketExpr, nnkRange, nnkDerefExpr, nnkIfExpr, nnkTableConstr, nnkAddr, nnkAsgn, - nnkIfStmt, nnkCaseStmt, nnkVarSection, nnkLetSection, nnkConstSection, nnkDefer, nnkRaiseStmt, nnkReturnStmt, nnkStmtList, - nnkStmtListExpr, nnkTupleConstr: - lowIndx = 0 - of nnkIdentDefs: - lowIndx = 2 - if node[0].kind == nnkIdent: - let index = lowerBound(varsList[], node[0].strVal) - varsList[].insert(node[0].strVal, index) - of nnkVarTuple: - lowIndx = node.len - 1 - for i in 0..node.len - 3: - if node[i].kind == nnkIdent: - let index = lowerBound(varsList[], node[i].strVal) - varsList[].insert(node[i].strVal, index) - of nnkDotExpr: - lowIndx = 0 - highIndx = 0 - of nnkElifExpr, nnkElseExpr, nnkOfBranch..nnkElse, nnkWhenStmt..nnkWhileStmt, nnkTryStmt, nnkFinally, nnkBlockStmt, nnkBlockExpr: - isBlock = true - lowIndx = 0 - of nnkConstDef: - if node[0].kind == nnkIdent: - let index = lowerBound(varsList[], node[0].strVal) - varsList[].insert(node[0].strVal, index) - else: - discard - if lowIndx != -1: - var nodeVars = varsList - for i in lowIndx..highIndx: - if isBlock and i == highIndx: - new(nodeVars) - nodeVars[] = varsList[] - if node[i].kind == nnkIdent: - checkReplaceSelfVar(node, i, nodeVars[], propsList) - else: - applySelf(node[i], nodeVars, propsList, methodsList) - proc genType(obj: ObjectDecl): NimNode {.compileTime.} = result = newNimNode(nnkStmtList) @@ -546,6 +481,12 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} = else: initMethod.body.insert(0, initBody) + when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): + # {.this: self.} for convenience + result.add(newNimNode(nnkPragma).add(newNimNode(nnkExprColonExpr).add( + ident("this"), ident("self") + ))) + # Nim proc defintions var decls = newSeqOfCap[NimNode](obj.methods.len) for meth in obj.methods: @@ -600,25 +541,6 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} = genSym(nskProc, "getFunc"), newStrLitNode(hintStr), hintIdent, usage, ident($hasDefaultValue), field.defaultValue))) - - # Keep track of object's fields and methods (used instead of deprecated this:self pragma) - var propsList: seq[string] = @[] - for field in obj.fields: - let indx = lowerBound(propsList, field.name.strVal) - propsList.insert(field.name.strVal, indx) - var methodsList: seq[string] = @[] - for meth in obj.methods: - let indx = lowerBound(methodsList, meth.name) - methodsList.insert(meth.name, indx) - - # Inject self to properties and methods - for meth in obj.methods: - let varsList = new(seq[string]) - for v in meth.nimNode[3]: - if v.kind == nnkIdentDefs: - varsList[].add v[0].strVal - applySelf(meth.nimNode[6], varsList, propsList, methodsList) - # Register methods template registerGodotMethod(classNameLit, classNameIdent, methodNameIdent, @@ -627,10 +549,6 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} = when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): {.emit: """/*TYPESECTION*/ N_NOINLINE(void, setStackBottom)(void* thestackbottom); -""".} - else: - {.emit: """ -N_NOINLINE(void, nimGC_setStackBottom)(void* thestackbottom); """.} proc methFuncIdent(obj: ptr GodotObject, methodData: pointer, @@ -638,9 +556,13 @@ N_NOINLINE(void, nimGC_setStackBottom)(void* thestackbottom); args: var array[MAX_ARG_COUNT, ptr GodotVariant]): GodotVariant {.noconv.} = var stackBottom {.volatile.}: pointer - {.emit: """ - nimGC_setStackBottom((void*)(&`stackBottom`)); - """.} + stackBottom = addr(stackBottom) + when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): + {.emit: """ + setStackBottom((void*)(&`stackBottom`)); + """.} + else: + nimGC_setStackBottom(stackBottom) let self = cast[classNameIdent](userData) const isStaticCall = methodNameLit == cstring"_ready" or methodNameLit == cstring"_process" or @@ -718,7 +640,7 @@ macro gdobj*(ast: varargs[untyped]): untyped = ## ## gdobj MyObj of Node: ## var myField: int - ## ## Not exported to Godot (i.e. editor will not see this field). + ## ## Not exported to Godot (i.e. editor and scripts will not see this field). ## ## var myString* {.gdExport, hint: Length, hintStr: "20".}: string ## ## Exported to Godot as ``my_string``. @@ -731,12 +653,12 @@ macro gdobj*(ast: varargs[untyped]): untyped = ## ## Exported methods are exported to Godot by default, ## ## and their Godot names are prefixed with ``_`` ## ## (in this case ``_ready``) - ## print("I am ready! myString is: " & myString) + ## print("I am ready! myString is: " & self.myString) ## ## proc myProc*() {.gdExport.} = ## ## Exported to Godot as ``my_proc`` ## print("myProc is called! Incrementing myField.") - ## inc myField + ## inc self.myField ## ## If parent type is omitted, the type is inherited from ``Object``. ## @@ -756,5 +678,3 @@ macro gdobj*(ast: varargs[untyped]): untyped = ## that you can find in `Godot API `_. let typeDef = parseType(ast) result = genType(typeDef) - - diff --git a/godot/nim/godotnim.nim b/godot/nim/godotnim.nim index 13614505..bba8b980 100644 --- a/godot/nim/godotnim.nim +++ b/godot/nim/godotnim.nim @@ -91,6 +91,8 @@ type FNV1Hash = uint32 +include "internal/backwardcompat.inc.nim" + proc isFinalized*(obj: NimGodotObject): bool {.inline.} = obj.isFinalized @@ -110,13 +112,13 @@ template initFNV1Hash(hash: var FNV1Hash) = template appendFNV1Hash(hash: var FNV1Hash, val: uint8) = block: let u64hash = hash.uint64 - hash = ( + hash = (( u64hash + (u64hash shl 1'u64) + (u64hash shl 4'u64) + (u64hash shl 7'u64) + (u64hash shl 8'u64) + - (u64hash shl 24'u64)).uint32 xor val + (u64hash shl 24'u64)) and 0xFFFFFFFF'u32).uint32 xor val {.push stackTrace:off.} proc lsb(c: ptr cwchar_t): char {.noinit, inline.} = @@ -187,7 +189,7 @@ proc nimGodotObjectFinalizer*[T: NimGodotObject](obj: T) = if obj.godotObject.isNil or obj.isNative: return # important to set it before so that ``unreference`` is aware obj.isFinalized = true - if (obj.isRef or not obj.linkedObject.isNil and obj.linkedObject.isRef) and + if (obj.isRef or not obj.linkedObject.isNil and obj.linkedObject.isRef) and obj.godotObject.unreference(): obj.deinit() @@ -708,16 +710,13 @@ proc toVariant*(s: string): Variant {.inline.} = proc fromVariant*(s: var string, val: Variant): ConversionResult = if val.getType() == VariantType.String: s = val.asString() - when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): - if val.getType() == VariantType.Nil: + elif val.getType() == VariantType.Nil: + when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): s = nil else: - result = ConversionResult.TypeError - else: - if val.getType() == VariantType.Nil: s = "" - else: - result = ConversionResult.TypeError + else: + result = ConversionResult.TypeError template arrTypeInfo(T) = result.variantType = VariantType.Array @@ -840,11 +839,6 @@ when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): N_LIB_EXPORT N_CDECL(void, NimMain)(void); N_NOINLINE(void, setStackBottom)(void* thestackbottom); """.} -else: - {.emit: """/*TYPESECTION*/ - N_LIB_EXPORT N_CDECL(void, NimMain)(void); - N_NOINLINE(void, nimGC_setStackBottom)(void* thestackbottom); - """.} var nativeLibHandle: pointer proc getNativeLibHandle*(): pointer = @@ -857,10 +851,16 @@ proc godot_nativescript_init(handle: pointer) {. nativeLibHandle = handle var stackBottom {.volatile.}: pointer + stackBottom = addr(stackBottom) {.emit: """ NimMain(); - nimGC_setStackBottom((void*)(&`stackBottom`)); """.} + when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): + {.emit: """ + setStackBottom((void*)(&`stackBottom`)); + """.} + else: + nimGC_setStackBottom(stackBottom) GC_fullCollect() GC_disable() @@ -896,9 +896,13 @@ proc registerFrameCallback*(cb: proc () {.closure.}) = proc godot_nativescript_frame() {.cdecl, exportc, dynlib.} = var stackBottom {.volatile.}: pointer - {.emit: """ - nimGC_setStackBottom((void*)(&`stackBottom`)); - """.} + stackBottom = addr(stackBottom) + when (NimMajor, NimMinor, NimPatch) < (0, 19, 0): + {.emit: """ + setStackBottom((void*)(&`stackBottom`)); + """.} + else: + nimGC_setStackBottom(stackBottom) for cb in idleCallbacks: cb() GC_step(nimGcStepLengthUs, true, 0) diff --git a/godot/nim/nim.cfg b/godot/nim/nim.cfg index 1f128607..add9a4df 100644 --- a/godot/nim/nim.cfg +++ b/godot/nim/nim.cfg @@ -1,2 +1,2 @@ --path:"$projectdir/../" --d:userealtimeGC \ No newline at end of file +-d:useRealtimeGc \ No newline at end of file