The Boston Diaries

The ongoing saga of a programmer who doesn't live in Boston, nor does he even like Boston, but yet named his weblog/journal “The Boston Diaries.”

Go figure.

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.

Obligatory Picture

[The future's so bright, I gotta wear shades]

Obligatory Contact Info

Obligatory Feeds

Obligatory Links

Obligatory Miscellaneous

You have my permission to link freely to any entry here. Go ahead, I won't bite. I promise.

The dates are the permanent links to that day's entries (or entry, if there is only one entry). The titles are the permanent links to that entry only. The format for the links are simple: Start with the base link for this site: https://boston.conman.org/, then add the date you are interested in, say 2000/08/01, so that would make the final URL:

https://boston.conman.org/2000/08/01

You can also specify the entire month by leaving off the day portion. You can even select an arbitrary portion of time.

You may also note subtle shading of the links and that's intentional: the “closer” the link is (relative to the page) the “brighter” it appears. It's an experiment in using color shading to denote the distance a link is from here. If you don't notice it, don't worry; it's not all that important.

It is assumed that every brand name, slogan, corporate name, symbol, design element, et cetera mentioned in these pages is a protected and/or trademarked entity, the sole property of its owner(s), and acknowledgement of this status is implied.

Copyright © 1999-2024 by Sean Conner. All Rights Reserved.