Hello, I'd like to access variables created in Nim from Lua. Using NimLUA I can access and use e.g. consts, enums, procs and so on. There is also a comparison of Nim types and the equivalent Lua types. However, if I create a variable in Nim this is not supported by the library.
Is there a simple way using the general compatibility types like cint, cdouble and so on? I want to use a dedicated file for the Lua code, and not directly embed it like the official Lua bindings.
Here is an example of what I'd like to do:
Nim code
import nimLUA
let
x {.exportc.}: cint = 20
proc test(L: PState, fileName: string) =
if L.doFile(fileName) != 0.cint:
echo L.toString(-1)
L.pop(1)
else:
echo fileName & " .. OK"
proc main() =
var L = newNimLua()
L.pushInteger(x)
L.test("test.lua")
L.close()
main()
Lua code
-- test.lua
print("Hello World!")
print("This is x: ", x)
return 0
I naively tried using exportc, but this obviously did not work.
Hey! It may be possible to wrap a single integer variable by exposing getter/setter functions to Lua.
But an easier and more flexible solution would be to bundle your variables into an object, and give a function to Lua which returns the object:
import nimLUA
type AppState = ref object
x: int
let state = new(AppState)
proc getAppState(): AppState =
state
proc test(L: PState, fileName: string) =
if L.doFile(fileName) != 0.cint:
echo L.toString(-1)
L.pop(1)
else:
echo fileName & " .. OK"
proc main() =
var L = newNimLua()
L.bindObject(AppState):
x(get, set)
L.bindFunction(getAppState)
state.x = 100
L.test("test.lua")
L.close()
echo "Final value: ", state.x
main()
Lua code
local state = getAppState()
print("Hello World!")
print("This is x: ", state.x)
state.x = 200
Output:
Hello World!
This is x: 100
test.lua .. OK
Final value: 200
If you wanted to treat x like a global variable in Lua, one solution is to load the script into a function and then swap out its environment before execution.
This is also how you can sandbox your scripts to restrict their capabilities!
proc test(L: PState, fileName: string) =
# Set up the state which will be our script's environment.
# Note that we need to add `print`, else it won't be available once we change environment.
L.dostring("""
state = getAppState()
state.print = print
""")
# Load the script into a function on the stack
if L.loadfile(fileName) != 0.cint:
echo L.tostring(-1)
L.pop(1)
return
# Push the state object onto the stack, and then set the _ENV upvalue
# of our function to the state object at the head of the stack.
# See: https://stackoverflow.com/q/19167986
L.getglobal("state")
let name = L.setupvalue(-2, 1)
doAssert(name == "_ENV")
# Execute the script
if L.pcall(0,0,0) != 0.cint:
echo L.tostring(-1)
L.pop(1)
return
L.pop(2)
echo fileName & " .. OK"
Lua code
print("Hello World!")
print("This is x: "..x)
x = 200
Output (same as before)
Hello World!
This is x: 100
test.lua .. OK
Final value: 200
Hope this helps, and apologies for any errors in my code (I've done a lot of Lua but never embedded it myself before, very impressed by how easy this library makes it!)