diff --git a/denim.nimble b/denim.nimble index 3987044..f7e9f88 100644 --- a/denim.nimble +++ b/denim.nimble @@ -1,6 +1,6 @@ # Package -version = "0.1.7" +version = "0.1.8" author = "George Lemon" description = "DENIM - Nim code to Bun.js/Node.js in seconds via NAPI" license = "MIT" @@ -20,4 +20,4 @@ task dev, "Compile denim": exec "nim c --gc:arc -d:denimcli -o:" & path & "/denim src/denim.nim" task docgenx, "Build documentation website": - exec "nim doc --index:on -d:napibuild --project --git.url:https://github.com/openpeeps/denim --git.commit:main src/denim.nim" \ No newline at end of file + exec "nim doc --index:on -d:napibuild --project --git.url:https://github.com/openpeeps/denim --git.commit:main src/denim.nim" diff --git a/src/denimpkg/napi/bindings.nim b/src/denimpkg/napi/bindings.nim index 2ed77a9..236637b 100644 --- a/src/denimpkg/napi/bindings.nim +++ b/src/denimpkg/napi/bindings.nim @@ -57,7 +57,7 @@ proc newNodeValue*(val: napi_value, env: napi_env): Module = Module(val: val, env: env, descriptors: @[]) proc kind(env: napi_env, val: napi_value): NapiValueType = - assert ( napi_typeof(env, val, addr result) ) + assert ( napi_typeof(env, val, addr result) ) proc expect*(env: napi_env, n: napi_value, expectKind: NapiValueType): bool = return kind(env, n) == expectKind @@ -213,12 +213,17 @@ macro `%*`*(x: typed): untyped = ## An elegant way to convert Nim types to `napi_value`. return toNapiValue(x) +macro toJsObject*(x: typed): untyped = + ## An elegant way to convert Nim types to `napi_value`. + return toNapiValue(x) + macro toObject*(x: untyped): untyped = ## Convert {"a": "val", "b": "val"} to JavaScript Object var table = newNimNode(nnkTableConstr) for i in 0.. napi_null result = ("napi_null", n.strVal) - elif n.eqIdent("int"): + elif n.kind == nnkIdent and ($n).startsWith("int"): # int > napi_number result = ("napi_number", n.strVal) elif n.eqIdent("symbol"): # symbol > napi_symbol result = ("napi_symbol", "symbol") - elif n.kind == nnkObjectTy: + elif n.kind == nnkObjectTy or n.eqIdent("napi_object"): # object > napi_object result = ("napi_object", "object") - elif n.kind == nnkProcTy: + elif n.kind == nnkProcTy or n.eqIdent("napi_function"): # func > napi_function result = ("napi_function", "func") - elif n.eqIdent("array"): + elif n.eqIdent("array") or n.eqIdent("napi_array"): result = ("napi_object", "array") elif n.eqIdent("external"): result = ("napi_external", n.strVal) @@ -566,96 +578,111 @@ macro export_napi*(fn: untyped) = ## proc hello(name: string): string {.export_napi.} = ## return %* args.get("name") ## ``` - expectKind(fn, nnkProcDef) - expectKind(fn[6], nnkStmtList) # body - expectKind(fn[3], nnkFormalParams) # params - result = newStmtList() - let fnName = fn[0].strVal - var - fnBody = newStmtList() - params = fn[3][1..^1] - paramsLen = params.len - hasParams = paramsLen != 0 - countless: bool # enabled when an argument has `varargs[]` type - args = newNimNode(nnkBracket) - argsCond = nnkIfStmt.newTree() - if hasParams: - for i in 0 .. params.high: - var typedArg = getNimNapiType(params[i][1], countless) - var types = nnkTupleConstr.newTree( - newLit(params[i][0].strVal), - newLit(typedArg[1]), - ident(typedArg[0]), - newLit(false) - ) - var argCond = - nnkElifBranch.newTree( - nnkInfix.newTree(ident("=="), ident("argName"), newLit(params[i][0].strVal)), - nnkReturnStmt.newTree( - nnkBracketExpr.newTree(ident("args"), newLit(i)) - ) + if fn.kind == nnkProcDef: + expectKind(fn[6], nnkStmtList) # body + expectKind(fn[3], nnkFormalParams) # params + result = newStmtList() + let fnName = fn[0].strVal + var + fnBody = newStmtList() + params = fn[3][1..^1] + paramsLen = params.len + hasParams = paramsLen != 0 + countless: bool # enabled when an argument has `varargs[]` type + args = newNimNode(nnkBracket) + argsCond = nnkIfStmt.newTree() + if hasParams: + for i in 0 .. params.high: + var typedArg = getNimNapiType(params[i][1], countless) + var types = nnkTupleConstr.newTree( + newLit(params[i][0].strVal), + newLit(typedArg[1]), + ident(typedArg[0]), + newLit(false) ) - add args, types - add argsCond, argCond - add result, newCall(ident("addDocBlock"), newLit(fnName), args) - - let - callExpectProc = newCall( - ident("expect"), - ident("Env"), - ident("args"), - newLit(""), - newLit(fnName) - ) - typeChecker = newIfStmt( - ( - nnkPrefix.newTree(ident("not"), callExpectProc), - newStmtList( + var argCond = + nnkElifBranch.newTree( + nnkInfix.newTree(ident("=="), ident("argName"), newLit(params[i][0].strVal)), nnkReturnStmt.newTree( - newEmptyNode() + nnkBracketExpr.newTree(ident("args"), newLit(i)) ) ) - )) - paramsLen = if countless: 100 else: params.len - var argsGetterProc = - newProc( - ident("get"), - [ - ident("napi_value"), # return type - nnkIdentDefs.newTree( - ident("args"), - nnkBracketExpr.newTree(ident("seq"), ident("napi_value")), - newEmptyNode() - ), - nnkIdentDefs.newTree( - ident("argName"), - ident("string"), - newEmptyNode() - ) - ], - body = newStmtList(argsCond) + add args, types + add argsCond, argCond + add result, newCall(ident("addDocBlock"), newLit(fnName), args) + + let + callExpectProc = newCall( + ident("expect"), + ident("Env"), + ident("args"), + newLit(""), + newLit(fnName) + ) + typeChecker = newIfStmt( + ( + nnkPrefix.newTree(ident("not"), callExpectProc), + newStmtList( + nnkReturnStmt.newTree( + newEmptyNode() + ) + ) + )) + paramsLen = if countless: 100 else: params.len + var argsGetterProc = + newProc( + ident("get"), + [ + ident("napi_value"), # return type + nnkIdentDefs.newTree( + ident("args"), + nnkBracketExpr.newTree(ident("seq"), ident("napi_value")), + newEmptyNode() + ), + nnkIdentDefs.newTree( + ident("argName"), + ident("string"), + newEmptyNode() + ) + ], + body = newStmtList(argsCond) + ) + add fnBody, typeChecker + add fnBody, argsGetterProc + add fnBody, fn[6] + + template runtimeErrorWrapper(fnBody) = + try: + fnBody + except: + var runtimeError: napi_value + assert env.napi_create_error(%* "NimRuntime", %*(getCurrentExceptionMsg() & "\n" & getCurrentException().getStackTrace), runtimeError.addr) + assert env.napi_throw(runtimeError) + + result.add( + newCall( + ident("registerFn"), + ident("module"), + newLit(paramsLen), + newLit(fnName), + getAst(runtimeErrorWrapper(fnBody)) ) - add fnBody, typeChecker - add fnBody, argsGetterProc - add fnBody, fn[6] - - template runtimeErrorWrapper(fnBody) = - try: - fnBody - except: - var runtimeError: napi_value - assert env.napi_create_error(%* "NimRuntime", %*(getCurrentExceptionMsg() & "\n" & getCurrentException().getStackTrace), runtimeError.addr) - assert env.napi_throw(runtimeError) - - result.add( - newCall( - ident("registerFn"), - ident("module"), - newLit(paramsLen), - newLit(fnName), - getAst(runtimeErrorWrapper(fnBody)) ) - ) + elif fn.kind in {nnkVarSection, nnkLetSection}: # This will works only since Nim 2.0.0 + for identDef in fn.children: + var + vName = identDef[0] + vVal = identDef[2] + expectKind(vName, nnkIdent) + result = newStmtList() + result.add( + newCall( + ident("register"), + ident("module"), + newLit(vName.strVal), + vVal + ) + ) # # Promise - High-Level API @@ -689,9 +716,50 @@ proc toSeq*(n: napi_value): seq[napi_value] = for i in n: add result, i -# iterator pairs*(n: napi_value): napi_value = -# for index in 0..