I'm trying to pass a JsonNode from my script into the compiled program. I can get it to work by marshalling the data and unmarshalling on the other side, but if I switch to the strategy of getting a PNode instead of a discreet value, like with VMArgs.getStr etc, I can't then get Nim to correctly interpret the node as a JsonNode.
It does seem to be a JsonNode though, I can dump it using $ and get things like this:
(isUnquoted: false, kind: JString, str: "Accelerate static site generator", num: 0, fnum: 0.0, bval: false, fields: (data: [], counter: 0, first: 0, last: 0), elems: [])
So it looks like a JsonNode of type JString - which matches what I expect - but if I try JsonNode(value) (where value is the PNode I get from VMArgs.getNode()) it seems to ignore it and gives me:
Error: type mismatch: got <JsonNode, seq[string], PNode>
but expected one of:
proc `{}=`(node: JsonNode; keys: varargs[string]; value: JsonNode)
first type mismatch at position: 3
required type for value: JsonNode
but expression 'value' is of type: PNode
The full code is quite extensive, but in part I have this:
The code that gets called from the script, state.context is a JsonNode of JObject type:
proc setInContext*( path: string, value: JsonNode ) = discard
proc setInContext*( args: VmArgs ) = {.gcsafe.}:
var path = args.getString( 0 )
let value = args.getNode( 1 )
state.context{ path.split(".") }= JsonNode(value)
The script call:
proc store_value( node:JsonNode ) =
setInContext( "some.dot.path", node )
Hi @araq. Yes that is ofc one way to do it :) In fact that was basically what I was doing, I had ´setInContext( "some.dot.path", $node )´ in the script call, proc setInContext*( path: string, value: string ) = discard in the host function declaration and let value = args.getString( 1 ) in the definition.
But I was going to switch to proc setInContext*( path: string, value: any ) = discard and case out the kind of PNode on the host side to allow several types. But while testing around with that I noticed - as per the dump above - that it actually sent the JsonNode through.
Since I can dump the value on the host side and see that it is a JsonNode it seems to have transfered the data across correctly - but how do I read it as a JsonNode?
And if I can't read it as a JsonNode on the host side, why does it think it is a JsonNode and not a PNode? :)
Would you suggest that I marshal and unmarshal via string instead? I mean it is straightforward, but it takes a lot of boilerplate code on both sides. I don't mind the boilerplate on the host side, but the script side I would like to keep as clean as possible ...
Can you do type overloading on these? That way I could keep the script side clean and just do the dance on the host side.
To use overloading use something like:
proc aInt(x: int) = discard "VM implements it"
proc aString(x: string) = discard "VM implements it"
proc a(x: int) = aInt(x)
proc a(x: string) = aString(x)
To reduce the boiler plate I highly suggest looking into my rewrite of Nimscripter(it's still not pushed as the master branch due to some bug on the Nim -> Nimscript front, but the code works for interop). A simple example is as follows:
# Nim
import std/json
import nimscripter
import nimscripter/expose
import compiler/nimeval
proc echoJson(j: JsonNode){.exportToScript: jsontest.} = echo j.pretty()
const
jsonModule = implNimscriptModule(jsontest)
stdlib = findNimStdlibCompileTime()
let intr = loadScript("tests/example/jsonscript.nims", jsonModule, modules = ["json"], stdpath = stdlib)
# Nimscript
echoJson(% 100)
echoJson(% true)
echoJson(% "Hello world")
If one did not want to use my scaffolding of API they could use my macros/procedures instead located here.I looked at your code quite a lot when I did my implementation. If you had had the new version back then I would probably have used it, but at this point I'm pretty much where I need to be with my own implementation so it makes less sense to replace it.
But I will pilfer more ideas from your new code no doubt :)