Skip to content

Commit

Permalink
Finalize support for Nim 0.20
Browse files Browse the repository at this point in the history
Closes #32, closes #41.
  • Loading branch information
endragor committed Sep 15, 2019
1 parent 0fecfdb commit 19b0c23
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 131 deletions.
2 changes: 1 addition & 1 deletion godot.nimble
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "0.7.20"
version = "0.7.21"
author = "Xored Software, Inc."
description = "Godot Engine bindings"
license = "MIT"
Expand Down
15 changes: 9 additions & 6 deletions godot/core/godotbase.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
18 changes: 13 additions & 5 deletions godot/godotapigen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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:
Expand Down
19 changes: 19 additions & 0 deletions godot/internal/backwardcompat.inc.nim
Original file line number Diff line number Diff line change
@@ -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]()
8 changes: 7 additions & 1 deletion godot/internal/godotstrings.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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``
Expand Down
116 changes: 18 additions & 98 deletions godot/nim/godotmacros.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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,
Expand All @@ -627,20 +549,20 @@ 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,
userData: pointer, numArgs: cint,
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
Expand Down Expand Up @@ -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``.
Expand All @@ -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``.
##
Expand All @@ -756,5 +678,3 @@ macro gdobj*(ast: varargs[untyped]): untyped =
## that you can find in `Godot API <index.html#modules-godot-api>`_.
let typeDef = parseType(ast)
result = genType(typeDef)


42 changes: 23 additions & 19 deletions godot/nim/godotnim.nim
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ type

FNV1Hash = uint32

include "internal/backwardcompat.inc.nim"

proc isFinalized*(obj: NimGodotObject): bool {.inline.} =
obj.isFinalized

Expand All @@ -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.} =
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 =
Expand All @@ -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()

Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion godot/nim/nim.cfg
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
--path:"$projectdir/../"
-d:userealtimeGC
-d:useRealtimeGc

0 comments on commit 19b0c23

Please sign in to comment.