BlitzMax/Modules/Axe/Lua

Tutorial

edit

Initialization and Deinitialization

edit

Lua scripts are usually run in a VM (Virtual Machine). Therefore we first start one and then load the basic Lua libraries:

Local LuaState:byte ptr = luaL_newstate()
luaL_openlibs(LuaState)

The byte pointer returned by luaL_newstate() is needed for every Lua function call. Now you're done with initialization and can begin running scripts.

Closing the Lua VM afterwards is done by a single function:

lua_close(LuaState)

Running a simple script

edit

Lua Scripts can be executed directly from Blitzmax Strings.

Local Script:String = "print(~qHello World!~q)"
luaL_loadString(LuaState,Script)

This code loads and compiles the script. luaL_loadString() returns a value <> 0 if something went wrong.

lua_getfield(LuaState, LUA_GLOBALSINDEX, "debug")' get global "debug" 
lua_getfield(LuaState, -1, "traceback")          ' get "debug.traceback" 
lua_remove (LuaState, -2)                        ' remove "debug" table from stack 

These commands are not important for now, they're just there to get proper error messages.

lua_pcall(LuaState,1,-1,-1)' use "debug.traceback" as err.hdlr

This line actually executes the script. Again, if it's return value is <> 0, an error occurred. If everything worked though, the program will display a "Hello World!", as intended.

Calling BlitzMax functions from Lua

edit

Basics

edit

As you may have noticed, being able only to run a script but not to interact with BlitzMax isn't extraordinarily useful. Therefore the next step consists of learning how to call a BlitzMax function from a Lua script. Unfortunately we can't just use an arbitrary function, as the function has to follow a specific layout:

Function BMXName:int (LuaState:Byte Ptr)   
   ...      ' handling of parameters passed from Lua (if required)     
      ...   ' actual function body   
   ...      ' passing results back to Lua (if required)   
   return 0 ' number of values returned to Lua function  
End Function

Obviously, the arguments submitted with the function in the Lua script aren't directly transmitted to the BlitzMax function. We'll have to handle them manually, but not right away. First we have to register the function to Lua:

lua_register(LuaState, "luaname", BMXName)

BMXName() can now be called from Lua (globally) as luaname(). You may choose to name your functions the same as in BlitzMax, but you don't have to.

Handling parameters from Lua

edit

Let's say we've tried to call

luaname(810, "Hallo!")

from Lua using the example function above. Lua didn't complain about the additional parameters but we're not able to access them from within the function, either. Where have they gone? Lua has placed them onto the parameter stack. Each parameter is assigned an index through which it can be identified. The indices are consecutively numbered and begin with 1. Thus, 810 can be found at index 1 and "Hallo!" at index 2. Now we just need to know the respective commands to read the values from the parameter stack and we're done:

Function BMXName:Int (LuaState:Byte Ptr)   
   Local int_value:Int    = luaL_checkinteger(LuaState, 1)
   Local str_value:String = luaL_checkstring(LuaState, 2)
   Print int_value
   Print str_value
   Return 0 ' number of values returned to Lua function  
End Function 

Returning Values to Lua

edit

Returning one or several values to the Lua script works similarly, but you don't have to specify indices as values are automatically appended to the stack. Here's an example:

Function BMXName:Int (LuaState:Byte Ptr)   
   lua_pushnumber(LuaState, 810)
   lua_pushstring(LuaState, "Hallo!")
   Return 2 ' number of values returned to Lua function  
End Function 

In most cases you'll be returning only one or no value at all, but as shown, more are possible, too.

Calling single Lua functions from BlitzMax

edit

We know how to execute whole Lua scripts, but what if we only need a single function? In that case, you first have to put the function to be called onto the stack, then the parameters. Here's an example. Lua Script:

function luafunc(para, str)
   print(para, str)
end

BlitzMax Code:

lua_getfield(LuaState, LUA_GLOBALSINDEX, "luafunc") 'Puts the function onto the stack
lua_pushinteger(LuaState, 810)                'First Argument
lua_pushstring(LuaState, "Hallo!")            'Second Argument
lua_call(LuaState, 2, 0)                      'Call the function with 2 arguments and no result 

Returned values, if any, are placed onto the stack after the function was executed.

Accessing Lua Globals from BlitzMax

edit

Defining or changing a global variable

edit
lua_pushstring(LuaState, "Hallo!")
lua_setglobal (LuaState, "luaglobal") 

The first command writes "Hallo!" onto the Stack and the second puts it into the global luaglobal.

Reading a global variable

edit
lua_getglobal(LuaState, "luaglobal")
Local str:String = lua_tostring(LuaState, -1)

The first command puts the value onto the stack, the second puts it into the BlitzMax variable.

Accessing all global variables

edit

If you want to save the current status of your Lua VM, you need to find a way to get all global variables. One way to do this is using the lua_next function:

lua_pushnil(LuaState)                                         ' first key 
While (lua_next(LuaState, LUA_GLOBALSINDEX) <> 0)             ' iterate through all values of the global environment table
   ' uses 'key' (at index -2) and 'value' (at index -1) 
   Print(lua_typename(LuaState,lua_type(LuaState , - 1))+" - "+lua_tostring(LuaState,-2)+"-"+lua_tostring(LuaState,-1))
   ' removes 'value'; keeps 'key' for next iteration 
   lua_pop(LuaState, 1);
Wend

Note that this method returns not only global variables, but also functions, tables, etc. which you need to sort out using the lua_type command.