Updated to work with nim 0.20

- Attempt to fix deprecated this:self pragma by injecting self to methods and properties (needs more testing)
- Changed import headers to use / (slash) instead of . (period)
- Fixed object branch in godotapigen.nim
- Fixed deprecated nimNode.ident with nimNode.strVal

Fix toGodotName function

Consider procCall calls as special cases, otherwise it gives problems in _init function

Fix infinite recursive call in Godot 3.2
This commit is contained in:
Dwana Games 2019-06-21 20:11:29 -05:00 • committed by Ruslan Mustakov
commit 0fecfdb3b8
23 changed files with 294 additions and 211 deletions

View file

@ -1,8 +1,8 @@
# Copyright 2018 Xored Software, Inc.
import macros, tables, typetraits, strutils, sets, sequtils, options
import godotinternal, internal.godotvariants
import godotnim, core.variants
import macros, tables, typetraits, strutils, sets, sequtils, options, algorithm
import godotinternal, internal/godotvariants
import godotnim, core/variants
type
VarDecl = ref object
@ -51,15 +51,15 @@ template parseError(node: NimNode, msg: string) =
proc extractNames(definition: NimNode):
tuple[name, parentName: string] =
if definition.kind == nnkIdent:
result.name = $(definition.ident)
result.name = definition.strVal
else:
if not (definition.kind == nnkInfix and
definition[0].ident == toNimIdent("of")):
definition[0].strVal == "of"):
parseError(definition, "invalid type definition")
result.name = $(definition[1].ident)
result.name = definition[1].strVal
case definition[2].kind:
of nnkIdent:
result.parentName = $definition[2].ident
result.parentName = definition[2].strVal
else:
parseError(definition[2], "parent type expected")
@ -72,13 +72,13 @@ proc newCStringLit(s: string): NimNode {.compileTime.} =
newNimNode(nnkCallStrLit).add(ident("cstring"), newRStrLit(s))
iterator pragmas(node: NimNode):
tuple[key: NimIdent, value: NimNode, index: int] =
tuple[key: string, value: NimNode, index: int] =
assert node.kind in {nnkPragma, nnkEmpty}
for index in countdown(node.len - 1, 0):
if node[index].kind == nnkExprColonExpr:
yield (node[index][0].ident, node[index][1], index)
yield (node[index][0].strVal, node[index][1], index)
elif node[index].kind == nnkIdent:
yield (node[index].ident, nil, index)
yield (node[index].strVal, nil, index)
proc removePragmaNode(statement: NimNode,
pname: string): NimNode {.compileTime.} =
@ -91,9 +91,8 @@ proc removePragmaNode(statement: NimNode,
result = nil
var pragmas = if RoutineNodes.contains(statement.kind): statement.pragma()
else: statement[1]
let pnameIdent = toNimIdent(pname)
for ident, val, i in pragmas(pragmas):
if ident == pnameIdent:
if ident == pname:
pragmas.del(i)
return val
@ -104,9 +103,8 @@ proc removePragma(statement: NimNode, pname: string): bool =
return false
var pragmas = if RoutineNodes.contains(statement.kind): statement.pragma()
else: statement[1]
let pnameIdent = toNimIdent(pname)
for ident, val, i in pragmas(pragmas):
if ident == pnameIdent:
if ident == pname:
pragmas.del(i)
return true
@ -178,8 +176,9 @@ proc parseVarSection(decl: NimNode): seq[VarDecl] =
else:
result.add(identDefsToVarDecls(decl[i]))
proc parseType(definition, callSite: NimNode): ObjectDecl =
let body = callSite[^1]
proc parseType(ast: NimNode): ObjectDecl =
let definition = ast[0]
let body = ast[^1]
result = ObjectDecl(
fields: newSeq[VarDecl](),
methods: newSeq[MethodDecl]()
@ -187,11 +186,11 @@ proc parseType(definition, callSite: NimNode): ObjectDecl =
(result.name, result.parentName) = extractNames(definition)
var isTool = false
for i in 2..(callSite.len - 2):
let option = callSite[i]
for i in 1..(ast.len - 2):
let option = ast[i]
if option.kind != nnkIdent:
parseError(option, "type specifier expected")
if option.ident == toNimIdent("tool"):
if option.strVal == "tool":
isTool = true
else:
parseError(option, "valid type specifier expected")
@ -214,7 +213,7 @@ proc parseType(definition, callSite: NimNode): ObjectDecl =
macro invokeVarArgs(procIdent, objIdent;
minArgs, maxArgs: static[int], numArgsIdent,
argSeqIdent; argTypes: seq[NimNode],
hasReturnValue, isStaticCall: static[bool]): typed =
hasReturnValue, isStaticCall: static[bool]): untyped =
## Produces statement in form:
##
## .. code-block:: nim
@ -287,7 +286,7 @@ macro invokeVarArgs(procIdent, objIdent;
" arguments, but got " & $numArgs)
result.add(newNimNode(nnkElse).add(getAst(
printInvokeErr($procIdent.ident, minArgs, maxArgs, numArgsIdent))))
printInvokeErr(procIdent.strVal, minArgs, maxArgs, numArgsIdent))))
proc typeError(nimType: string, value: string, godotType: VariantType,
className: cstring, propertyName: cstring): string =
@ -433,6 +432,73 @@ 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)
@ -480,11 +546,6 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
else:
initMethod.body.insert(0, initBody)
# {.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:
@ -539,6 +600,25 @@ 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,
@ -604,7 +684,7 @@ N_NOINLINE(void, nimGC_setStackBottom)(void* thestackbottom);
let hasReturnValueBool = not (meth.returnType.isNil or
meth.returnType.kind == nnkEmpty or
(meth.returnType.kind == nnkIdent and
meth.returnType.ident == toNimIdent("void")))
meth.returnType.strVal == "void"))
let hasReturnValue = if hasReturnValueBool: ident("true")
else: ident("false")
result.add(getAst(
@ -630,11 +710,7 @@ N_NOINLINE(void, nimGC_setStackBottom)(void* thestackbottom);
GodotMethodAttributes(), refDec)
result.add(getAst(registerRefIncDec(classNameLit)))
{.push warning[Deprecated]: off.}
# immediate macros are deprecated, but `untyped` doesn't make it immediate,
# as the warning and the documentation claim.
macro gdobj*(definition: untyped, body: typed): typed {.immediate.} =
macro gdobj*(ast: varargs[untyped]): untyped =
## Generates Godot type. Self-documenting example:
##
## .. code-block:: nim
@ -678,7 +754,7 @@ macro gdobj*(definition: untyped, body: typed): typed {.immediate.} =
## `gdnew <godotnim.html#gdnew>`_ or by using
## `load <godotapi/resource_loader.html#load,string,string,bool>`_ or any other way
## that you can find in `Godot API <index.html#modules-godot-api>`_.
let typeDef = parseType(definition, callsite())
let typeDef = parseType(ast)
result = genType(typeDef)
{.push warning[Deprecated]: on.}

View file

@ -2,13 +2,13 @@
import tables, typetraits, macros, unicode, strutils, sets
import gdnativeapi
import core.godotcoretypes, core.godotbase
import core.vector2, core.rect2,
core.vector3, core.transform2d,
core.planes, core.quats, core.aabb,
core.basis, core.transforms, core.colors,
core.nodepaths, core.rids, core.dictionaries,
core.arrays, core.poolarrays, core.variants
import core/godotcoretypes, core/godotbase
import core/vector2, core/rect2,
core/vector3, core/transform2d,
core/planes, core/quats, core/aabb,
core/basis, core/transforms, core/colors,
core/nodepaths, core/rids, core/dictionaries,
core/arrays, core/poolarrays, core/variants
import godotinternal
## This module defines ``NimGodotObject`` and ``toVariant``/``fromVariant``
@ -343,20 +343,20 @@ proc newRStrLit(s: string): NimNode {.compileTime.} =
result = newNimNode(nnkRStrLit)
result.strVal = s
macro toGodotName(T: typedesc): string =
macro toGodotName(T: typedesc): untyped =
if T is GodotString or T is string:
"String"
newStrLitNode"String"
elif T is SomeFloat:
"float"
newStrLitNode"float"
elif T is SomeUnsignedInt or T is SomeSignedInt:
"int"
newStrLitNode"int"
else:
let nameStr = (($T.getType()[1][1].symbol).split(':')[0])
let nameStr = ((T.getType()[1][1].strVal).split(':')[0])
case nameStr:
of "File", "Directory", "Thread", "Mutex", "Semaphore":
"_" & nameStr
newStrLitNode("_" & nameStr)
else:
nameStr
newStrLitNode(nameStr)
macro asCString(s: static[string]): cstring =
result = newNimNode(nnkCallStrLit).add(

View file

@ -1 +1,2 @@
--path:"$projectdir/../"
--path:"$projectdir/../"
-d:userealtimeGC