* * * * *

                       A failed programming experiment

I have a Lua [1] module that embeds a C compiler [2] (and there's an
extension module that allows you to load a Lua module straight from C code
[3]) 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 [4] to the Lua mailing list [5] 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 [6].

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.

[1] http://www.lua.org/
[2] https://github.com/spc476/lua-conmanorg/blob/master/src/tcc.c
[3] https://github.com/spc476/lua-conmanorg/blob/master/lua/cc.lua
[4] http://lua-users.org/lists/lua-l/2014-05/msg00403.html
[5] http://www.lua.org/lua-l.html
[6] http://en.wikipedia.org/wiki/Bob's_your_uncle

Email author at [email protected]