A few more utility functions for lua.

develop
Alexander Gavrilov 2012-04-03 20:02:01 +04:00
parent 42a9b0a592
commit 2d4af4ac3e
4 changed files with 110 additions and 18 deletions

@ -410,6 +410,10 @@ Currently it defines the following features:
Same as println; intended for errors. Uses red color and logs to stderr.log. Same as println; intended for errors. Uses red color and logs to stderr.log.
* ``safecall(f[,args...])``, ``dfhack.safecall(f[,args...])``
Just like pcall, but prints the error with traceback using printerr.
* ``dfhack.interpreter([prompt[,env[,history_filename]]])`` * ``dfhack.interpreter([prompt[,env[,history_filename]]])``
Starts an interactive lua interpreter, using the specified prompt Starts an interactive lua interpreter, using the specified prompt

@ -141,6 +141,49 @@ static int traceback (lua_State *L) {
return 1; return 1;
} }
static int finish_dfhack_safecall (lua_State *L, bool success)
{
if (!lua_checkstack(L, 1))
{
lua_settop(L, 0); /* create space for return values */
lua_pushboolean(L, 0);
lua_pushstring(L, "stack overflow in dfhack.safecall()");
success = false;
}
else
{
lua_pushboolean(L, success);
lua_replace(L, 1); /* put first result in first slot */
}
if (!success)
{
const char *msg = lua_tostring(L, -1);
if (!msg) msg = "In dfhack.safecall: error message is not a string.";
if (color_ostream *out = Lua::GetOutput(L))
out->printerr("%s\n", msg);
else
Core::printerr("%s\n", msg);
}
return lua_gettop(L);
}
static int safecall_cont (lua_State *L)
{
int status = lua_getctx(L, NULL);
return finish_dfhack_safecall(L, (status == LUA_YIELD));
}
static int lua_dfhack_safecall (lua_State *L)
{
luaL_checkany(L, 1);
lua_pushcfunction(L, traceback);
lua_insert(L, 1);
int status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 1, 0, safecall_cont);
return finish_dfhack_safecall(L, (status == LUA_OK));
}
static void report_error(color_ostream &out, lua_State *L) static void report_error(color_ostream &out, lua_State *L)
{ {
const char *msg = lua_tostring(L, -1); const char *msg = lua_tostring(L, -1);
@ -189,24 +232,48 @@ bool DFHack::Lua::Require(color_ostream &out, lua_State *state,
return true; return true;
} }
static bool load_with_env(color_ostream &out, lua_State *state, const std::string &code, int eidx) bool DFHack::Lua::AssignDFObject(color_ostream &out, lua_State *state,
type_identity *type, void *target, int val_index, bool perr)
{ {
if (luaL_loadbuffer(state, code.data(), code.size(), "=(interactive)") != LUA_OK) val_index = lua_absindex(state, val_index);
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
PushDFObject(state, type, target);
lua_pushvalue(state, val_index);
return Lua::SafeCall(out, state, 2, 0, perr);
}
bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std::string &code,
int nargs, int nres, bool perr,
const char *debug_tag, int env_idx)
{
if (!debug_tag)
debug_tag = code.c_str();
if (env_idx)
env_idx = lua_absindex(state, env_idx);
int base = lua_gettop(state);
// Parse the code
if (luaL_loadbuffer(state, code.data(), code.size(), debug_tag) != LUA_OK)
{ {
report_error(out, state); if (perr)
report_error(out, state);
return false; return false;
} }
// Replace _ENV // Replace _ENV
lua_pushvalue(state, eidx); if (env_idx)
if (!lua_setupvalue(state, -2, 1))
{ {
out.printerr("No _ENV upvalue.\n"); lua_pushvalue(state, env_idx);
return false; lua_setupvalue(state, -2, 1);
assert(lua_gettop(state) == base+1);
} }
return true; if (nargs > 0)
lua_insert(state, -1-nargs);
return Lua::SafeCall(out, state, nargs, nres, perr);
} }
bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state, bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
@ -273,9 +340,7 @@ bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
{ {
curline = "return " + curline.substr(1); curline = "return " + curline.substr(1);
if (!load_with_env(out, state, curline, base)) if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base))
continue;
if (!SafeCall(out, state, 0, LUA_MULTRET))
continue; continue;
int numret = lua_gettop(state) - base; int numret = lua_gettop(state) - base;
@ -309,9 +374,7 @@ bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
} }
else else
{ {
if (!load_with_env(out, state, curline, base)) if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base))
continue;
if (!SafeCall(out, state, 0, 0))
continue; continue;
} }
} }
@ -352,8 +415,7 @@ static int lua_dfhack_interpreter(lua_State *state)
static int lua_dfhack_with_suspend(lua_State *L) static int lua_dfhack_with_suspend(lua_State *L)
{ {
int ctx; int rv = lua_getctx(L, NULL);
int rv = lua_getctx(L, &ctx);
// Non-resume entry point: // Non-resume entry point:
if (rv == LUA_OK) if (rv == LUA_OK)
@ -385,7 +447,8 @@ static const luaL_Reg dfhack_funcs[] = {
{ "print", lua_dfhack_print }, { "print", lua_dfhack_print },
{ "println", lua_dfhack_println }, { "println", lua_dfhack_println },
{ "printerr", lua_dfhack_printerr }, { "printerr", lua_dfhack_printerr },
{ "traceback", traceback }, { "safecall", lua_dfhack_safecall },
{ "onerror", traceback },
{ "interpreter", lua_dfhack_interpreter }, { "interpreter", lua_dfhack_interpreter },
{ "with_suspend", lua_dfhack_with_suspend }, { "with_suspend", lua_dfhack_with_suspend },
{ NULL, NULL } { NULL, NULL }

@ -79,6 +79,13 @@ namespace DFHack { namespace Lua {
*/ */
DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type = false); DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type = false);
/**
* Assign the value at val_index to the target of given identity using df.assign().
* Return behavior is of SafeCall below.
*/
DFHACK_EXPORT bool AssignDFObject(color_ostream &out, lua_State *state,
type_identity *type, void *target, int val_index, bool perr = true);
/** /**
* Push the pointer onto the stack as a wrapped DF object of a specific type. * Push the pointer onto the stack as a wrapped DF object of a specific type.
*/ */
@ -95,12 +102,28 @@ namespace DFHack { namespace Lua {
return (T*)GetDFObject(state, df::identity_traits<T>::get(), val_index, exact_type); return (T*)GetDFObject(state, df::identity_traits<T>::get(), val_index, exact_type);
} }
/**
* Assign the value at val_index to the target using df.assign().
*/
template<class T>
bool AssignDFObject(color_ostream &out, lua_State *state, T *target, int val_index, bool perr = true) {
return AssignDFObject(out, state, df::identity_traits<T>::get(), target, val_index, perr);
}
/** /**
* Invoke lua function via pcall. Returns true if success. * Invoke lua function via pcall. Returns true if success.
* If an error is signalled, and perr is true, it is printed and popped from the stack. * If an error is signalled, and perr is true, it is printed and popped from the stack.
*/ */
DFHACK_EXPORT bool SafeCall(color_ostream &out, lua_State *state, int nargs, int nres, bool perr = true); DFHACK_EXPORT bool SafeCall(color_ostream &out, lua_State *state, int nargs, int nres, bool perr = true);
/**
* Parse code from string with debug_tag and env_idx, then call it using SafeCall.
* In case of error, it is either left on the stack, or printed like SafeCall does.
*/
DFHACK_EXPORT bool SafeCallString(color_ostream &out, lua_State *state, const std::string &code,
int nargs, int nres, bool perr = true,
const char *debug_tag = NULL, int env_idx = 0);
/** /**
* Returns the ostream passed to SafeCall. * Returns the ostream passed to SafeCall.
*/ */

@ -1,6 +1,8 @@
-- Common startup file for all dfhack plugins with lua support -- Common startup file for all dfhack plugins with lua support
-- The global dfhack table is already created by C++ init code. -- The global dfhack table is already created by C++ init code.
safecall = dfhack.safecall
function mkmodule(module,env) function mkmodule(module,env)
local pkg = package.loaded[module] local pkg = package.loaded[module]
if pkg == nil then if pkg == nil then