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 0I 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 = 200Output:
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 = 200Output (same as before)
Hello World!
This is x: 100
test.lua .. OK
Final value: 200Hope 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!)