lua-users home
lua-l archive

script for converting a Lua module to a C module

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


For a class I'm teaching, students know C and not Lua.
I wanted to write some code in Lua without having them
worry about what Lua was, where their path was set, and
so on. So I just wrote a short Lua script to take a 
Lua file, turn either the source code or a compiled binary
into a C program, and write the C wrapper function that
loads it into C. So for example, if you have module
uname.lua, you can turn it into a shared library uname.so
as follows:
 lua2c uname.lua > luname.c
 gcc -I/usr/include/lua5.1 -fPIC -shared \
 -Wl,-soname,uname.so -o uname.so luname.c
Or in my case you can turn it into an ordinary .o file
and link it in with a .a file. So I can hand my students 
a libmumble.a and they don't even know they're using Lua.
Plus I know the code is always the code that is compiled in---
there is never any worry about paths or versions.
Luiz encouraged me to post the code. I'm embarrassed to 
have anyone else see it, since there are all sorts of gross
Unixy OS hacks in there, but I have patched it only to 
the point where it doesn't use 'require', so others
might be able to use it. Lots of ugliness packed in
a mere 110 lines!
If somebody feels like cleaning it up and putting it on the
Lua Users' Wiki, we'll all be in your debt. The worst thing
in there now is a call to the Unix function 'tempfile', which
is a Debianism. The right thing would be to try mktemp if
that fails, and so on.
Norman
P.S. This script highlights the property that compiled Lua
binaries are *not* portable across platforms! I made
compilation the default because I didn't want people snooping
around with strings(1) or mucking with the source code...
#!/usr/bin/env lua
----- lua 5.1 script to convert a lua file into a C file
-----
----- Usage: lua2c foo.lua > lfoo.c # compiled, so not portable
----- lua2c -s foo.lua > lfoo.c # source, and so portable
---------------- general utility functions -------------
function io.contents(filename)
 local f, msg = io.open(filename, 'r')
 if not f then return f, msg end
 local s = assert(f:read '*a')
 f:close()
 return s
end
function os.capture(cmd, raw)
 assert(io.popen)
 local f = assert(io.popen(cmd, 'r'))
 local s = assert(f:read('*a'))
 f:close()
 if raw then return s end
 s = string.gsub(s, '^%s+', '')
 s = string.gsub(s, '%s+$', '')
 s = string.gsub(s, '[\n\r]+', ' ')
 return s
end
local quote_me = '[^%w%+%-%=%@%_%/]' 
 -- easier to complement what doesn't need quotes
local strfind = string.find
function os.quote(s)
 if strfind(s, quote_me) or s == '' then
 return "'" .. string.gsub(s, "'", [['"'"']]) .. "'"
 else
 return s
 end
end
function os.runf(...) return os.execute(string.format(...)) end
local function printf(...) return outfile:write(string.format(...)) end
-------------------------------------------------------
local lua -- program to be loaded (source or binary)
if arg[1] == '-a' or arg[1] == '-s' then -- don't compile; 
	 -- use ascii source
 table.remove(arg, 1)
 assert(#arg == 1)
 lua = io.contents(arg[1]):gsub('^#!.-\n', '')
else 
 assert(#arg == 1)
 local bin = os.capture 'tempfile'
 assert(os.runf('luac -o %s %s', os.quote(bin), os.quote(arg[1])) == 0)
 lua = io.contents(bin)
 os.remove(bin)
end
local libname = arg[1]:gsub('.*/', ''):gsub('%.lua$', '')
outfile = io.stdout
------------------------
outfile:write [[
#include <lua.h>
#include <lauxlib.h>
static unsigned char program[] = {
]]
outfile:write ' '
local last = lua:len()
for i = 1, last do
 local n = string.byte(lua, i)
 printf('%3d%s', n, i < last and ', ' or '')
 if i % 10 == 0 then printf '\n ' end
end
printf '\n};\n\n'
outfile:write((([[
int luaload_$lib(lua_State *L) {
 return luaL_loadbuffer(L, (const char*)program, sizeof(program), "@$source");
}
int luaopen_$lib(lua_State *L) {
 if (luaL_loadbuffer(L, (const char*)program, sizeof(program), "@$source"))
 return luaL_error(L, "Internal library '%s' failed to parse", "$lib");
 if (lua_pcall(L, 0, 1, 0))
 return luaL_error(L, "Internal library '%s' failed to run: %s", "$lib",
 lua_tostring(L, -1));
 if (lua_isnil(L, -1)) {
 lua_pop(L, 1);
 lua_pushboolean(L, 1);
 }
 lua_getglobal(L, "package"); // s: lib package
 lua_getfield(L, -1, "loaded"); // s: lib package loaded
 lua_remove(L, -2); // s: lib loaded
 lua_pushstring(L, "$lib"); // s: lib loaded libname
 lua_pushvalue(L, -3); // s: lib loaded libname lib
 lua_settable(L, -3); // s: lib loaded
 lua_pop(L, 1); // s: lib
 return 1;
}
]]):gsub('$(%a+)', { lib = libname, source = arg[1]:gsub('.*/', '') })))

AltStyle によって変換されたページ (->オリジナル) /