I know I have been “missing in action” lately but I am working furiously, and I seem to have too little time for my blog (very sad face). But, just for a breath of fresh air, I thought I’d share something with the world.
Entering lua2c.lua
Lately I became quite interested in Lua (a lot actually). It has phenomenal speed, exceptional interfacing with C and some features and libraries that just make my day (i.e. coroutines, lpeg, lua-ev and others), and since I needed to embed some Lua scripts (entirely) in a C project I’m currently working on, I ended up adapting Mike Edgar’s “bin2c.lua” script (which takes a Lua script and turns it into a C header file) to suit my needs.
Basic functionality
Specifically, this adaptation generates a function that takes a Lua state as the only argument and then runs the embedded Lua code in the given state after which it returns the status (as opposed to putting the code straight in the top-level scope of the generated file). This makes it easier to embed code in C and then invoke it, and also to apply the same code onto multiple Lua states (e.g. multiple threads).
Check further down for a short usage sample.
Options
-c
compiles to Lua bytecode for (slightly) faster loading (does not work with LuaJIT)-s
minifies the source code before embedding to minimize wastes-e
applies a mild (XOR-based) obfuscation to prevent embedding of plain text-u
produces a (void) function that never fails and panics on errors
Download
So here it is, enjoy: lua2c.lua (MIT license, same as Lua)
Just to be clear
For example, given a lua script file called ‘test.lua’ (shown below) this is how we would go about embedding it into a simple C program called ‘test.c’.
-- test.lua: a small sign that we're up and running... print("I am an embedded Lua script running inside a C binary! Hmm... cozy!")
And here is the C file (rather overly commented)…
/* * Include our embedded script. */ #include "test.h" /* * Invoke the script. */ int main(int argc, char* argv[]) { // create a Lua state lua_State* L = lua_open(); // load basic libraries into state luaL_openlibs(L); // invoke embedded script into state // by calling the generated function load_test_lua(L); // finish lua_close(L); return 0; }
And here’s how we can compile all this into an executable. Note that I tested this on Ubuntu Lucid and the following packages had to be installed: lua5.1, liblua5.1-0-dev, liblua5.1-lpeg2, liblua5.1-bitop0. Your configuration may differ, and if you don’t have the pre-built Lua packages, you will have to install them manyally (along with the dependencies)!
# generate (unprotected, minified and encrypted) C code lua lua2c.lua -seu test.lua > test.h # compile C program against Lua library gcc test.c -o test `pkg-config lua5.1 --libs --cflags` # run program ./test I am an embedded script running from inside a C binary! Hmm... cozy!
Let me know if it works fr you, or if you think of some cool improvement! 😀
Cheers!
=== modified file src/lua/utility/lua2c.lua
— src/lua/utility/lua2c.lua 2014-05-08 16:14:52 +0000
+++ src/lua/utility/lua2c.lua 2014-05-08 22:33:55 +0000
@@ -224,7 +224,7 @@
ok, so probably we dont open the output file in binary mode
— Submit.
if ( destination ) then
– local outfile = assert ( io.open(destination, ‘wb’ ) )
+ local outfile = assert ( io.open(destination, ‘w’ ) )
outfile:write ( output )
outfile:close ()
else
Also maybe a lua newbee can add an output file option
=== modified file src/lua/utility/lua2c.lua
— src/lua/utility/lua2c.lua 2014-05-08 15:24:36 +0000
+++ src/lua/utility/lua2c.lua 2014-05-08 15:53:29 +0000
@@ -45,6 +45,9 @@
result status, but instead terminates the program on error. This
behaviour is desirable in many cases when it is unacceptable for
an embedded file not to load successfully.
+
+ -o
+ specifies the name of the output file
Compiles a Lua source into a C header file which can be included into
an existing C program via the ‘#include’ directive. This file defines
@@ -70,20 +73,28 @@
— Arguments.
local source
+local destination
local do_help
local do_compile
local do_minify
local do_encrypt
local do_unsafe
+local do_out_file
for i, v in ipairs(arg or {}) do
– if v:find(‘^-.+$’) then
– do_help = v:find(‘h’, 1, true)
– do_compile = v:find(‘c’, 1, true)
– do_minify = v:find(‘s’, 1, true)
– do_encrypt = v:find(‘e’, 1, true)
– do_unsafe = v:find(‘u’, 1, true)
– else
– source = arg[i]
+ if ( do_out_file ) then
+ destination = arg[i]
+ do_out_file = false
+ else
+ if v:find(‘^-.+$’) then
+ do_help = v:find(‘h’, 1, true)
+ do_compile = v:find(‘c’, 1, true)
+ do_minify = v:find(‘s’, 1, true)
+ do_encrypt = v:find(‘e’, 1, true)
+ do_unsafe = v:find(‘u’, 1, true)
+ do_out_file = v:find(‘o’, 1, true)
+ else
+ source = arg[i]
+ end
end
end
do_help = do_help or not source
@@ -94,12 +105,12 @@
return
end
— Read file.
+– Read input file.
local identity = source:lower():gsub(‘^.+/’, ”):gsub(‘[^%w_]’, ‘_’)
-local file = assert(io.open(source, ‘rb’))
-local content = file:read’*a’
+local infile = assert(io.open(source, ‘rb’))
+local content = infile:read’*a’
local length = content:len()
-file:close()
+infile:close()
— Minify.
if do_minify then
@@ -212,4 +223,10 @@
identity, length, content, length, source)
— Submit.
-io.write(output)
+if ( destination ) then
+ local outfile = assert ( io.open(destination, ‘wb’ ) )
+ outfile:write ( output )
+ outfile:close ()
+else
+ io.write(output)
+end
Hi,
A lua newbee am I, but perhaps some changes may be needed for lua 5.2 and latest lpeg.
Thanks,
Jeff Hill
=== modified file src/lua/utility/lua2c.lua
— src/lua/utility/lua2c.lua 2014-05-08 14:58:55 +0000
+++ src/lua/utility/lua2c.lua 2014-05-08 15:00:30 +0000
@@ -13,8 +13,8 @@
—
— Requirements.
-require’bit’
-require’lpeg’
+–require’bit’
+local lpeg = require”lpeg”
— Aliases.
local P, R, V, S, C, Cs, Cb, Cg, Cmt =
@@ -113,7 +113,7 @@
level = assert(text:find(‘]’..level..’]’, start, true),
‘unclosed long brackets’)
return level + 2
– end,
+ end * 1,
string = (‘”‘ * ((P’\\\\’ + P’\\”‘ + 1) – P'”‘)^0 * ‘”‘) +
(“‘” * ((P’\\\\’ + P”\\'” + 1) – P”‘”)^0 * “‘”),
strings = V’heredoc’ + V’string’,