Note: The code in here is adapted from an actual project, however I've not yet had time to verify it doesn't have typos. Give me a weekend or two. I just found so little information on the new version I wanted to dump this to the web now in the hopes of being at least vaguely helpful.

Lua is a really cool, clean little programming language that is easy to embed in your applications. Not only is it under a permissive license, it's ANSI C.

However, recent updates have made most of the documentation about it on the web a bit outdated, so I thought I'd drop this quick tutorial on how to add Lua to your application and do some of the typical inter-operation things with it that you'd want to do when hosting scripts in your application.

Building Lua

Building Lua is very easy. After getting the source code, you duplicate the file lua/src/luaconf.h.orig under the name lua/src/luaconf.h. Then you point Terminal at Lua's folder and do

make macosx test

(Or if you're not on a Mac, use the appropriate platform name here, you can see available ones by just calling make without parameters in that folder)

This will churn a short moment, and then you'll have a liblua.a file. Add that to your Xcode project (or equivalent) so it gets linked in, and make sure the header search paths include the lua/src/ folder. That's it, now you can use Lua in your application.

Running a Lua script

To use Lua, you include the following headers:

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

(If you're using C++, be sure to wrap them in extern "C" or you'll get link errors) Then you can simply compile the following code to initialize a Lua context and run a script from a text file:

lua_State *L = luaL_newstate();	// Create a context.
luaL_openlibs(L);	// Load Lua standard library.

// Load the file:
int s = luaL_loadfile( L, "/path/to/file.lua" );

if( s == 0 )
{
	// Run it, with 0 params, accepting an arbitrary number of return values.
	//	Last 0 is error handler Lua function's stack index, or 0 to ignore.
	s = lua_pcall(L, 0, LUA_MULTRET, 0);
}

// Was an error? Get error message off the stack and send it back:
if( s != 0 )
{
	printf("Error: %s\n", lua_tostring(L, -1) );
	lua_pop(L, 1); // Remove error message from stack.
}
	
lua_close(L);	// Dispose of the script context.

The script file would contain something like:

-- this is a comment
io.write("Hello world, from ",_VERSION,"!\n")

Now you can run a file full of commands. But how do you have it call back into your application? There's a special call for that, lua_register, which creates a new function that actually wraps a special C function. You call it like this:

// Create a C-backed Lua function, myavg():
lua_register( L, "myavg", foo );	// Create a global named "myavg" and stash an unnamed function with C function "foo" as its implementation in it.

to register a C function named foo as a Lua function named myavg. The actual function would look like this:

// An example C function that we call from Lua:
static int foo (lua_State *L)
{
	int n = lua_gettop(L);    /* number of arguments */
	lua_Number sum = 0;
	int i;
	for (i = 1; i <= n; i++)
	{
		if (!lua_isnumber(L, i))
		{
			lua_pushstring(L, "incorrect argument");
			lua_error(L);
		}
		sum += lua_tonumber(L, i);
	}
	lua_pushnumber(L, sum/n);        /* first result */
	lua_pushnumber(L, sum);         /* second result */
	return 2;                   /* number of results */
}

This example function loops over all parameters that have been passed (using lua_isnumber to check they're numbers, and lua_tonumber to actually retrieve them as ints), which may be a variable number, adds and averages them, and then pushes two return values on the stack (the average and the sum), and returns the number of return values it gave.

You could now call it like:

io.write( "Average is: ", myavg(1,2,3,4,5) )

from Lua. The funny thing here is, in Lua, there are no functions in the traditional sense. It's a prototype-based programming language, so all functions are closures/blocks/lambdas, and can be treated just like any value, like an integer or a string. To declare a function, lua_register simply creates a global variable named myavg and sticks such a function object in it.

When you declare a function in Lua, it's also really just a shorthand for an assignment statement. So to run a function declared in a Lua file, like:

function main( magicNumber )
    io.write("Main was called with magicNumber ", magicNumber, "!")
end

you first have to execute it, which will create the global named main and stick a function in it. Only now do you look up the function object from that global and call it, again using lua_pcall like here:

lua_getglobal(L,"main");
lua_pushinteger(L,5);
s = lua_pcall(L, 1, LUA_MULTRET, 0);	// Tell Lua to expect 1 param & run it.

The 2nd parameter to lua_pcall tells it how many parameters to expect. Objects are likewise just tables (i.e. key-value dictionaries) where ivars are just values, and methods are functions stored as values. So, to create a new object with methods implemented in C, you do:

// Create a C-backed Lua object:
lua_newtable( L );	// Create a new object & push it on the stack.
	
// Define mymath.durchschnitt() for averaging numbers:
lua_pushcfunction( L, foo );	// Create an (unnamed) function with C function "foo" as the implementation.
lua_setfield( L, -2, "durchschnitt" );	// Pop the function off the back of the stack and into the object (-2 == penultimate object on stack) using the key "durchschnitt" (i.e. method name).
lua_setglobal( L, "mymath" );	// Pop the object off the stack into a global named "mymath".

To call this, function, you do it analogous to before, just that you first use lua_getglobal( L, "mymath" ) to push the object on the stack, then lua_getfield to actually push the "durchschnitt" function stored under that key in the object.

Since functions are closures/blocks/lambdas, they can also capture variables ("upvalues"). To set those, you use lua_pushcclosure instead of lua_pushcfunction and pass the number of values you pushed on the stack to capture as the last parameter. E.g. if you wanted to pass along a pointer to an object in your program that the session object wraps, instead of stashing it in an ivar, you could capture it like:

// Define session.write() for sending a reply back to the client:
lua_pushlightuserdata( L, sessionPtr );	// Create a value wrapping a pointer to a C++ object (this would be dangerous if we let the script run longer than the object was around).
lua_pushcclosure( L, session_write, 1 );// Create an (unnamed) function with C function "session_write" as the implementation and one associated value (think "captured variable", our userdata on the back of the stack).
lua_setfield( L, -2, "write" );	// Pop the function value off the back of the stack and into the object (-2 == penultimate object on stack) using the key "write" (i.e. method name).
lua_setglobal( L, "session" );	// Pop the object off the stack into a global named "session".

and inside the session_write function, you'd retrieve it again like:

	session*	sessionPtr = (session*) lua_touserdata( L, lua_upvalueindex(1) );

And now you know all you need to call Lua from C, and have Lua call your C functions back.