I am working on embedding Lua as a means of doing policy control in an email filtering application. I have it working quite nicely (it was not as hard as I expected!), but I would like to improve one aspect of its operation. Ideally I’d like the filter to read the Lua script(s) it’s configured to use and attempt to validate the syntax of the script as much as possible before the filter is allowed to fork and begin operating; sort of a “dry run”. This would be akin to “sh –x” or “perl –c”, or compilation of a C program before actually running it.
I export a few C global variables and a number of C functions to the scripts, and then use lua_load() to load them and check for errors. However, I observed that if I insert a typo into one of the exported function names, lua_load() doesn’t return an error even though that’s now an unrecognized symbol. For example, if I export a function “foo” that takes a user data (pointer) and a string as arguments, and then test this script:
-- test script; “ctx” is a global context pointer exported from C
io.write(xfoo(ctx, “hi there”), “\n”)
…lua_load() doesn’t complain at all. Only if I use lua_pcall() will I get some kind of error report out. That correctly notes that “xfoo” is actually “nil”. But now if I change it back to “foo”, i.e. it’s correctly spelled, lua_pcall() will of course actually try to run the script which could have side effects I don’t want when simply testing the script for syntax errors.
To try to reach some middle ground, I’ve coded the C side of “foo” so that if it gets a NULL value as “ctx” it will push some dummy value and return 1 immediately, so there are no side effects. Since “foo” uses the lua_gettop() and lua_is*() functions to check that the stack is the right size and contains the right kinds of arguments, this works to validate that the functions are being called correctly inside the Lua script. But then I have to make sure that the dummy value(s) returned in test mode by one function are valid as possible input to other functions; e.g. if the output of “foo” might be used as the input to “bar” and “bar” expects strings, then “foo” can only output a string, and I have to think of all possible combinations of these.
Is there some common wisdom out there for how to go about doing this sort of work? Am I on the right track?