Previous Post | Top | Next Post |
TOC
This was originally written and created around 2013 and may require to be updated. (2021)
Embedded Lua
The Lua interpreter embedded into a small C program offers an ideal configuration system. It can execute any functions of the hosting C program under specified conditions and sequences with arbitrary parameter values.
- Lua is small.
- Lua has simple procedural syntax.
- Lua has powerful data constructs based on associative arrays.
- Lua has extensible semantics.
- Lua is dynamically typed.
- Lua has automatic memory management with incremental garbage collection.
Learn Lua:
In Debian, Lua interpreter is offered as a shared library linkable from any C codes.
Pseudo stack
The hosting C program and the embedded Lua interpreter needs to communicate each other. This communication is done by passing parameters via the shared Lua pseudo stack. Here is a private library function to print out its content.
dump.c defining the dump_stack(lua_State* L) function source
#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
void dump_stack(lua_State* L)
{
int i;
int stack_size = lua_gettop(L); /* Lua virtual stack size */
printf ("=== DUMP STACK top=[%i] --> bottom=[1] ===\n", stack_size);
for (i = stack_size; i >= 1; i--) {
int type = lua_type(L, i);
printf("C: stack[%2i or %3i] type=%10s", i, i - stack_size - 1,
lua_typename(L,type));
switch(type) {
case LUA_TNUMBER: /* Lua type: number */
printf("; value= %g", lua_tonumber(L, i));
break;
case LUA_TBOOLEAN: /* Lua type: boolean */
if (lua_toboolean(L, i)) {
printf("; value= true");
} else {
printf("; value= false");
}
break;
case LUA_TSTRING: /* Lua type: string */
printf("; value= %s", lua_tostring(L, i) );
break;
case LUA_TNIL: /* Lua type: nil */
printf("; value= nil (nil == 0)");
break;
default:
printf("; value= non-printable");
break;
}
printf("\n");
}
}
dump.h defining the dump_stack(lua_State* L) function header
void dump_stack(lua_State* L);
Here is an example hosting C program of Lua interpreter luastack.c
. This
manipulates the shared parameter passing pseudo stack from the hosting C
program.
The hosting luastack.c source
#include <stdlib.h>
#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "dump.h"
#define TRUE 1
#define FALSE 0
int
main (void)
{
lua_State* L = luaL_newstate();
printf("lua_pushboolean(L, TRUE)\n");
lua_pushboolean(L, TRUE);
dump_stack(L);
printf("lua_pushnumber(L, 3.141592) -- push 3.141592\n");
lua_pushnumber(L, 3.141592);
dump_stack(L);
printf("lua_pushinteger(L, -200) -- push -200\n");
lua_pushinteger(L, -200);
dump_stack(L);
printf("lua_pushnil(L) -- push nil\n");
lua_pushnil(L);
dump_stack(L);
printf("lua_pushstring(L, \"Hello world!\") -- push \"Hello world!\"\n");
lua_pushstring(L, "Hello world!");
dump_stack(L);
printf("lua_replace(L, 2) -- replace stack[2] with stack top\n");
lua_replace(L, 2);
dump_stack(L);
printf("lua_pushvalue(L, 1) -- push value of stack[1]\n");
lua_pushvalue(L, 1);
dump_stack(L);
printf("lua_pushvalue(L, -2) -- push value of stack[-2]\n");
lua_pushvalue(L, -2);
dump_stack(L);
printf("lua_insert(L, 2) -- insert stack top to stack[2]\n");
lua_insert(L, 2);
dump_stack(L);
printf("lua_remove(L, 2) -- remove stack[2]\n");
lua_remove(L, 2);
dump_stack(L);
printf("lua_settop(L, -1) -- remove 1 item from stack top\n");
lua_settop(L, -1);
dump_stack(L);
printf("\n");
lua_close(L);
return EXIT_SUCCESS;
}
Let’s compile these and run luastack
.
Compile and run of luastack
$ gcc -Wall `pkg-config --cflags --libs lua5.1` -c dump.c
$ gcc -Wall `pkg-config --cflags --libs lua5.1` -c luastack.c
$ gcc -Wall `pkg-config --cflags --libs lua5.1` dump.o luastack.o -o luastack
$ ./luastack
lua_pushboolean(L, TRUE)
=== DUMP STACK top=[1] --> bottom=[1] ===
C: stack[ 1 or -1] type= boolean; value= true
lua_pushnumber(L, 3.141592) -- push 3.141592
=== DUMP STACK top=[2] --> bottom=[1] ===
C: stack[ 2 or -1] type= number; value= 3.14159
C: stack[ 1 or -2] type= boolean; value= true
lua_pushinteger(L, -200) -- push -200
=== DUMP STACK top=[3] --> bottom=[1] ===
C: stack[ 3 or -1] type= number; value= -200
C: stack[ 2 or -2] type= number; value= 3.14159
C: stack[ 1 or -3] type= boolean; value= true
lua_pushnil(L) -- push nil
=== DUMP STACK top=[4] --> bottom=[1] ===
C: stack[ 4 or -1] type= nil; value= nil (nil == 0)
C: stack[ 3 or -2] type= number; value= -200
C: stack[ 2 or -3] type= number; value= 3.14159
C: stack[ 1 or -4] type= boolean; value= true
lua_pushstring(L, "Hello world!") -- push "Hello world!"
=== DUMP STACK top=[5] --> bottom=[1] ===
C: stack[ 5 or -1] type= string; value= Hello world!
C: stack[ 4 or -2] type= nil; value= nil (nil == 0)
C: stack[ 3 or -3] type= number; value= -200
C: stack[ 2 or -4] type= number; value= 3.14159
C: stack[ 1 or -5] type= boolean; value= true
lua_replace(L, 2) -- replace stack[2] with stack top
=== DUMP STACK top=[4] --> bottom=[1] ===
C: stack[ 4 or -1] type= nil; value= nil (nil == 0)
C: stack[ 3 or -2] type= number; value= -200
C: stack[ 2 or -3] type= string; value= Hello world!
C: stack[ 1 or -4] type= boolean; value= true
lua_pushvalue(L, 1) -- push value of stack[1]
=== DUMP STACK top=[5] --> bottom=[1] ===
C: stack[ 5 or -1] type= boolean; value= true
C: stack[ 4 or -2] type= nil; value= nil (nil == 0)
C: stack[ 3 or -3] type= number; value= -200
C: stack[ 2 or -4] type= string; value= Hello world!
C: stack[ 1 or -5] type= boolean; value= true
lua_pushvalue(L, -2) -- push value of stack[-2]
=== DUMP STACK top=[6] --> bottom=[1] ===
C: stack[ 6 or -1] type= nil; value= nil (nil == 0)
C: stack[ 5 or -2] type= boolean; value= true
C: stack[ 4 or -3] type= nil; value= nil (nil == 0)
C: stack[ 3 or -4] type= number; value= -200
C: stack[ 2 or -5] type= string; value= Hello world!
C: stack[ 1 or -6] type= boolean; value= true
lua_insert(L, 2) -- insert stack top to stack[2]
=== DUMP STACK top=[6] --> bottom=[1] ===
C: stack[ 6 or -1] type= boolean; value= true
C: stack[ 5 or -2] type= nil; value= nil (nil == 0)
C: stack[ 4 or -3] type= number; value= -200
C: stack[ 3 or -4] type= string; value= Hello world!
C: stack[ 2 or -5] type= nil; value= nil (nil == 0)
C: stack[ 1 or -6] type= boolean; value= true
lua_remove(L, 2) -- remove stack[2]
=== DUMP STACK top=[5] --> bottom=[1] ===
C: stack[ 5 or -1] type= boolean; value= true
C: stack[ 4 or -2] type= nil; value= nil (nil == 0)
C: stack[ 3 or -3] type= number; value= -200
C: stack[ 2 or -4] type= string; value= Hello world!
C: stack[ 1 or -5] type= boolean; value= true
lua_settop(L, -1) -- remove 1 item from stack top
=== DUMP STACK top=[5] --> bottom=[1] ===
C: stack[ 5 or -1] type= boolean; value= true
C: stack[ 4 or -2] type= nil; value= nil (nil == 0)
C: stack[ 3 or -3] type= number; value= -200
C: stack[ 2 or -4] type= string; value= Hello world!
C: stack[ 1 or -5] type= boolean; value= true
C + Lua
Let’s write a hosting C program luahost.c
which can load a Lua script
script.lua
into the embedded Lua interpreter.
luahost.c source
#include <stdio.h>
#include <stdlib.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "dump.h"
#include "average.h"
#define TRUE 1
#define FALSE 0
int
main(int argc, char *argv[])
{
double w;
double h;
double a;
puts("-- lua_State* L = luaL_newstate(); /* initialize Lua */");
lua_State* L = luaL_newstate();
puts("-- luaL_openlibs(L); /* standard libraries */");
luaL_openlibs(L);
puts("-- lua_register(L, \"average\", c_average); /* private library */");
lua_register(L, "average", c_average);
puts("-- luaL_loadfile(L, argv[1]) ...");
if (luaL_loadfile(L, argv[1]) || lua_pcall(L, 0, 0, 0))
{
printf("cannot run: %s\n", lua_tostring(L, -1));
return EXIT_FAILURE;
}
else
printf("-- %s load and executed.\n", argv[1]);
puts("-- lua_getglobal(L, \"width\"); /* push Lua variable width */");
lua_getglobal(L, "width");
dump_stack(L);
puts("-- lua_getglobal(L, \"height\"); /* push Lua variable height */");
lua_getglobal(L, "height");
dump_stack(L);
puts("-- lua_getglobal(L, \"ave\"); /* push Lua variable ave */");
lua_getglobal(L, "ave");
dump_stack(L);
puts("-- a = lua_tonumber(L, -1); /* read ave at -1 in the stack */");
a = lua_tonumber(L, -1);
printf("C: Average = %g\n", a);
puts("-- h = lua_tonumber(L, -2); /* read height at -2 in the stack */");
h = lua_tonumber(L, -2);
printf("C: Height = %g\n", h);
puts("-- h = lua_tonumber(L, -3); /* read width at -3 in the stack */");
w = lua_tonumber(L, -3);
printf("C: Width = %g\n", w);
printf("C: Volume %g * %g * %g = %g\n", w, h, a, w*h*a);
lua_close(L);
return EXIT_SUCCESS;
}
script.lua source
-- lua example
ave = average(1.0, 2.0, 3.0, 4.0)
print(string.format("Lua: ave = %g", ave))
if os.getenv("SMALL") == "YES" then
width = 5
height = 50
else
width = 100
height = 200
end
print(string.format("Lua: width = %g", width))
print(string.format("Lua: height = %g", height))
Let’s see how they interact.
Compile and run of luahost
$ gcc -Wall `pkg-config --cflags --libs lua5.1` -c average.c
$ gcc -Wall `pkg-config --cflags --libs lua5.1` -c luahost.c
$ gcc -Wall `pkg-config --cflags --libs lua5.1` dump.o average.o \
luahost.o -o luahost
$ export SMALL
$ SMALL="NO"
$ ./luahost script.lua
-- lua_State* L = luaL_newstate(); /* initialize Lua */
-- luaL_openlibs(L); /* standard libraries */
-- lua_register(L, "average", c_average); /* private library */
-- luaL_loadfile(L, argv[1]) ...
C: argument[ 4 or -1]= 4
C: argument[ 3 or -2]= 3
C: argument[ 2 or -3]= 2
C: argument[ 1 or -4]= 1
Lua: ave = 2.5
Lua: width = 100
Lua: height = 200
-- script.lua load and executed.
-- lua_getglobal(L, "width"); /* push Lua variable width */
=== DUMP STACK top=[1] --> bottom=[1] ===
C: stack[ 1 or -1] type= number; value= 100
-- lua_getglobal(L, "height"); /* push Lua variable height */
=== DUMP STACK top=[2] --> bottom=[1] ===
C: stack[ 2 or -1] type= number; value= 200
C: stack[ 1 or -2] type= number; value= 100
-- lua_getglobal(L, "ave"); /* push Lua variable ave */
=== DUMP STACK top=[3] --> bottom=[1] ===
C: stack[ 3 or -1] type= number; value= 2.5
C: stack[ 2 or -2] type= number; value= 200
C: stack[ 1 or -3] type= number; value= 100
-- a = lua_tonumber(L, -1); /* read ave at -1 in the stack */
C: Average = 2.5
-- h = lua_tonumber(L, -2); /* read height at -2 in the stack */
C: Height = 200
-- h = lua_tonumber(L, -3); /* read width at -3 in the stack */
C: Width = 100
C: Volume 100 * 200 * 2.5 = 50000
$ SMALL="YES"
$ ./luahost script.lua
-- lua_State* L = luaL_newstate(); /* initialize Lua */
-- luaL_openlibs(L); /* standard libraries */
-- lua_register(L, "average", c_average); /* private library */
-- luaL_loadfile(L, argv[1]) ...
C: argument[ 4 or -1]= 4
C: argument[ 3 or -2]= 3
C: argument[ 2 or -3]= 2
C: argument[ 1 or -4]= 1
Lua: ave = 2.5
Lua: width = 100
Lua: height = 200
-- script.lua load and executed.
-- lua_getglobal(L, "width"); /* push Lua variable width */
=== DUMP STACK top=[1] --> bottom=[1] ===
C: stack[ 1 or -1] type= number; value= 100
-- lua_getglobal(L, "height"); /* push Lua variable height */
=== DUMP STACK top=[2] --> bottom=[1] ===
C: stack[ 2 or -1] type= number; value= 200
C: stack[ 1 or -2] type= number; value= 100
-- lua_getglobal(L, "ave"); /* push Lua variable ave */
=== DUMP STACK top=[3] --> bottom=[1] ===
C: stack[ 3 or -1] type= number; value= 2.5
C: stack[ 2 or -2] type= number; value= 200
C: stack[ 1 or -3] type= number; value= 100
-- a = lua_tonumber(L, -1); /* read ave at -1 in the stack */
C: Average = 2.5
-- h = lua_tonumber(L, -2); /* read height at -2 in the stack */
C: Height = 200
-- h = lua_tonumber(L, -3); /* read width at -3 in the stack */
C: Width = 100
C: Volume 100 * 200 * 2.5 = 50000
Lua program can call C functions and use values of C variables such as ones passed from OS environment variables via the hosting C program. The hosting C program can use the execution result of the Lua script.
Previous Post | Top | Next Post |