Friday, May 16, 2014
A failed programming experiment
I have a Lua module that embeds a C compiler (and there's an extension module that allows you to load a Lua module straight from C code) which allows you to embed C code inside Lua code and compile it directly into memory:
cc = require "org.conman.cc" load = cc.compile('load',[[ #include <lua.h> int load(lua_State *L) { double load[3]; getloadavg(laod,3); lua_pushnumber(L,load[0]); lua_pushnumber(L,load[1]); lua_pushnumber(L,load[2]); return 3; } ]]) print(load())
I use it as a means to quickly test Lua functions in C without having to muck about with external files, C compilers and linkers. For that, it's wonderful except …
Errors. If there's an error in the C portion, I get:
[spc]lucy:/tmp>lua load.lua tcc: <string>:7: error: 'laod' undeclared
Yeah, the line number is correct as far as it goes—it's in line 7 of the code, but the actual line number is 10 of the file. Okay, in this case, I can do a simple search on “laod” but for instance, this error:
[spc]lucy:/tmp>lua load.lua tcc: <string>:10: error: ';' expected (got "lua_pushnumber")
It's actually line 12 of the file.
A recent message to the Lua mailing list reminded me that it is, indeed, possible, to get what I want from the output:
[spc]lucy:/tmp>lua load.lua tcc: load.lua:12: error: ';' expected (got "lua_pushnumber")
and that's by using the #line
C preprocessor directive.
It's a relatively straightforward matter to add such a line in Lua—just
generate a proper #line
directive and concatenate the code to
it before feeding it to the compiler. Getting the line information from Lua
is, again, straightforward:
static const char *itcc_add_line_info(lua_State *L,int idx) { lua_Debug info; /*------------------------------------------------------ ; get caller info and return linenumber and source file ;-------------------------------------------------------*/ lua_getstack(L,3,&info); lua_getinfo(L,"lS",&info); /*----------------------------------------------------------------------- ; line number will be negative if it's a Lua function written in C or if ; the source can't be located. If that's the case just return the ; original string, otherwise, prepend a #line directive and return the ; modified string. ;------------------------------------------------------------------------*/ if (info.currentline > 0) { char lineinfo[FILENAME_MAX + 32]; size_t len = snprintf( lineinfo, sizeof(lineinfo), "#line %d \"%s\"\n", info.currentline, info.short_src ); lua_pushlstring(L,lineinfo,len); lua_pushvalue(L,idx); lua_concat(L,2); return lua_tostring(L,-1); } else return lua_tostring(L,idx); }
Add the call to that function in the right spot, and voilà, you now have an uncle named Robert.
As I was coding this up and testing it, I realized something else—I don't always include the code as a literal to the function. Sometimes, I declare the code as a variable:
cc = require "org.conman.cc" LOAD = [[ #include <lua.h> int load(lua_State *L) { double load[3]; getloadavg(load,3); lua_pushnumber(L,load[0]); lua_pushnumber(L,load[1]) lua_pushnumber(L,load[2]); return 3; } ]] load = cc.compile('load',LOAD) print(load())
I do this when the C code is longer, or I have additional parameters to
pass to cc.compile()
. And in this case, the line number
reported will be the call site (for the above example, line 18) instead of
the actual error (line 12).
Well … darn.
It wasn't as easy as I thought it would be.