develop
Warmist 2012-04-16 17:42:49 +03:00
commit cd51ddc272
6 changed files with 348 additions and 107 deletions

@ -1,7 +1,7 @@
PROJECT ( lua CXX ) PROJECT ( lua CXX )
CMAKE_MINIMUM_REQUIRED(VERSION 2.8) CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-DLUA_USE_APICHECK") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DLUA_USE_APICHECK")
IF(WIN32) IF(WIN32)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE ) ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE )

@ -403,7 +403,7 @@ static void finishCcall (lua_State *L) {
lua_assert(ci->u.c.k != NULL); /* must have a continuation */ lua_assert(ci->u.c.k != NULL); /* must have a continuation */
lua_assert(L->nny == 0); lua_assert(L->nny == 0);
/* finish 'luaD_call' */ /* finish 'luaD_call' */
L->nCcalls--; //L->nCcalls--;
/* finish 'lua_callk' */ /* finish 'lua_callk' */
adjustresults(L, ci->nresults); adjustresults(L, ci->nresults);
/* call continuation function */ /* call continuation function */
@ -513,7 +513,7 @@ static void resume (lua_State *L, void *ud) {
api_checknelems(L, n); api_checknelems(L, n);
firstArg = L->top - n; /* yield results come from continuation */ firstArg = L->top - n; /* yield results come from continuation */
} }
L->nCcalls--; /* finish 'luaD_call' */ //L->nCcalls--; /* finish 'luaD_call' */
luaD_poscall(L, firstArg); /* finish 'luaD_precall' */ luaD_poscall(L, firstArg); /* finish 'luaD_precall' */
} }
unroll(L, NULL); unroll(L, NULL);

@ -70,6 +70,10 @@ inline void AssertCoreSuspend(lua_State *state)
assert(!Lua::IsCoreContext(state) || DFHack::Core::getInstance().isSuspended()); assert(!Lua::IsCoreContext(state) || DFHack::Core::getInstance().isSuspended());
} }
/*
* Public DF object reference handling API
*/
void DFHack::Lua::PushDFObject(lua_State *state, type_identity *type, void *ptr) void DFHack::Lua::PushDFObject(lua_State *state, type_identity *type, void *ptr)
{ {
push_object_internal(state, type, ptr, false); push_object_internal(state, type, ptr, false);
@ -110,6 +114,10 @@ void *DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, int val_
return rv; return rv;
} }
/*
* Console I/O wrappers
*/
static int DFHACK_OSTREAM_TOKEN = 0; static int DFHACK_OSTREAM_TOKEN = 0;
color_ostream *DFHack::Lua::GetOutput(lua_State *L) color_ostream *DFHack::Lua::GetOutput(lua_State *L)
@ -232,15 +240,14 @@ static int lua_dfhack_is_interactive(lua_State *S)
return 1; return 1;
} }
static int lua_dfhack_lineedit(lua_State *S) static int dfhack_lineedit_sync(lua_State *S, Console *pstream)
{ {
const char *prompt = luaL_optstring(S, 1, ">> ");
const char *hfile = luaL_optstring(S, 2, NULL);
Console *pstream = get_console(S);
if (!pstream) if (!pstream)
return 2; return 2;
const char *prompt = luaL_optstring(S, 1, ">> ");
const char *hfile = luaL_optstring(S, 2, NULL);
DFHack::CommandHistory hist; DFHack::CommandHistory hist;
if (hfile) if (hfile)
hist.load(hfile); hist.load(hfile);
@ -263,6 +270,51 @@ static int lua_dfhack_lineedit(lua_State *S)
} }
} }
static int DFHACK_QUERY_COROTABLE_TOKEN = 0;
static int yield_helper(lua_State *S)
{
return lua_yield(S, lua_gettop(S));
}
namespace {
int dfhack_lineedit_cont(lua_State *L, int status, int)
{
if (Lua::IsSuccess(status))
return lua_gettop(L) - 2;
else
return dfhack_lineedit_sync(L, get_console(L));
}
}
static int dfhack_lineedit(lua_State *S)
{
lua_settop(S, 2);
Console *pstream = get_console(S);
if (!pstream)
return 2;
lua_rawgetp(S, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN);
lua_rawgetp(S, -1, S);
bool in_coroutine = !lua_isnil(S, -1);
lua_settop(S, 2);
if (in_coroutine)
{
lua_pushcfunction(S, yield_helper);
lua_pushvalue(S, 1);
lua_pushvalue(S, 2);
return Lua::TailPCallK<dfhack_lineedit_cont>(S, 2, LUA_MULTRET, 0, 0);
}
return dfhack_lineedit_sync(S, pstream);
}
/*
* Exception handling
*/
static int DFHACK_EXCEPTION_META_TOKEN = 0; static int DFHACK_EXCEPTION_META_TOKEN = 0;
static void error_tostring(lua_State *L, bool keep_old = false) static void error_tostring(lua_State *L, bool keep_old = false)
@ -304,8 +356,21 @@ static void report_error(lua_State *L, color_ostream *out = NULL)
lua_pop(L, 1); lua_pop(L, 1);
} }
static bool convert_to_exception(lua_State *L) static bool convert_to_exception(lua_State *L, int slevel, lua_State *thread = NULL)
{ {
if (!thread)
thread = L;
if (thread == L)
lua_pushthread(L);
else
{
lua_pushthread(thread);
lua_xmove(thread, L, 1);
}
lua_swap(L);
int base = lua_gettop(L); int base = lua_gettop(L);
bool force_unknown = false; bool force_unknown = false;
@ -316,13 +381,33 @@ static bool convert_to_exception(lua_State *L)
bool is_exception = lua_rawequal(L, -1, -2); bool is_exception = lua_rawequal(L, -1, -2);
lua_settop(L, base); lua_settop(L, base);
// If it is an exception, return as is
if (is_exception) if (is_exception)
return false; {
// If it is an exception from the same thread, return as is
lua_getfield(L, base, "thread");
bool same_thread = lua_rawequal(L, -1, base-1);
lua_settop(L, base);
force_unknown = true; if (same_thread)
{
lua_remove(L, base-1);
return false;
}
// Create a new exception for this thread
lua_newtable(L);
luaL_where(L, 1);
lua_pushstring(L, "coroutine resume failed");
lua_concat(L, 2);
lua_setfield(L, -2, "message");
lua_swap(L);
lua_setfield(L, -2, "cause");
}
else
force_unknown = true;
} }
// Promote non-table to table, and do some sanity checks
if (!lua_istable(L, base) || force_unknown) if (!lua_istable(L, base) || force_unknown)
{ {
lua_newtable(L); lua_newtable(L);
@ -352,6 +437,10 @@ static bool convert_to_exception(lua_State *L)
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN); lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN);
lua_setmetatable(L, base); lua_setmetatable(L, base);
lua_swap(L);
lua_setfield(L, -2, "thread");
luaL_traceback(L, thread, NULL, slevel);
lua_setfield(L, -2, "stacktrace");
return true; return true;
} }
@ -360,13 +449,7 @@ static int dfhack_onerror(lua_State *L)
luaL_checkany(L, 1); luaL_checkany(L, 1);
lua_settop(L, 1); lua_settop(L, 1);
bool changed = convert_to_exception(L); convert_to_exception(L, 1);
if (!changed)
return 1;
luaL_traceback(L, L, NULL, 1);
lua_setfield(L, 1, "stacktrace");
return 1; return 1;
} }
@ -403,14 +486,8 @@ static void push_simple_error(lua_State *L, const char *str)
{ {
lua_pushstring(L, str); lua_pushstring(L, str);
if (lua_checkstack(L, 5))
convert_to_exception(L);
if (lua_checkstack(L, LUA_MINSTACK)) if (lua_checkstack(L, LUA_MINSTACK))
{ convert_to_exception(L, 0);
luaL_traceback(L, L, NULL, 1);
lua_setfield(L, -2, "stacktrace");
}
} }
static bool do_finish_pcall(lua_State *L, bool success, int base = 1, int space = 2) static bool do_finish_pcall(lua_State *L, bool success, int base = 1, int space = 2)
@ -430,29 +507,24 @@ static bool do_finish_pcall(lua_State *L, bool success, int base = 1, int space
} }
} }
static int finish_dfhack_safecall (lua_State *L, bool success) namespace {
{ int safecall_cont(lua_State *L, int status, int)
success = do_finish_pcall(L, success); {
bool success = do_finish_pcall(L, Lua::IsSuccess(status));
if (!success)
report_error(L);
return lua_gettop(L); if (!success)
} report_error(L);
static int safecall_cont (lua_State *L) return lua_gettop(L);
{ }
int status = lua_getctx(L, NULL);
return finish_dfhack_safecall(L, (status == LUA_YIELD));
} }
static int lua_dfhack_safecall (lua_State *L) static int dfhack_safecall (lua_State *L)
{ {
luaL_checkany(L, 1); luaL_checkany(L, 1);
lua_pushcfunction(L, dfhack_onerror); lua_pushcfunction(L, dfhack_onerror);
lua_insert(L, 1); lua_insert(L, 1);
int status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 1, 0, safecall_cont); return Lua::TailPCallK<safecall_cont>(L, lua_gettop(L) - 2, LUA_MULTRET, 1, 0);
return finish_dfhack_safecall(L, (status == LUA_OK));
} }
bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres, bool perr) bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres, bool perr)
@ -481,6 +553,134 @@ bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres
return ok; return ok;
} }
// Copied from lcorolib.c, with error handling modifications
static int resume_helper(lua_State *L, lua_State *co, int narg, int nres)
{
if (!co) {
lua_pop(L, narg);
push_simple_error(L, "coroutine expected in resume");
return LUA_ERRRUN;
}
if (!lua_checkstack(co, narg)) {
lua_pop(L, narg);
push_simple_error(L, "too many arguments to resume");
return LUA_ERRRUN;
}
if (lua_status(co) == LUA_OK && lua_gettop(co) == 0) {
lua_pop(L, narg);
push_simple_error(L, "cannot resume dead coroutine");
return LUA_ERRRUN;
}
lua_xmove(L, co, narg);
int status = lua_resume(co, L, narg);
if (Lua::IsSuccess(status))
{
int nact = lua_gettop(co);
if (nres == LUA_MULTRET)
nres = nact;
else if (nres < nact)
lua_settop(co, nact = nres);
if (!lua_checkstack(L, nres + 1)) {
lua_settop(co, 0);
push_simple_error(L, "too many results to resume");
return LUA_ERRRUN;
}
int ttop = lua_gettop(L) + nres;
lua_xmove(co, L, nact);
lua_settop(L, ttop);
}
else
{
lua_xmove(co, L, 1);
// A cross-thread version of dfhack_onerror
if (lua_checkstack(L, LUA_MINSTACK))
convert_to_exception(L, 0, co);
}
return status;
}
static int dfhack_coresume (lua_State *L) {
lua_State *co = lua_tothread(L, 1);
luaL_argcheck(L, !!co, 1, "coroutine expected");
int r = resume_helper(L, co, lua_gettop(L) - 1, LUA_MULTRET);
bool ok = Lua::IsSuccess(r);
lua_pushboolean(L, ok);
lua_insert(L, 2);
return lua_gettop(L) - 1;
}
static int dfhack_saferesume (lua_State *L) {
lua_State *co = lua_tothread(L, 1);
luaL_argcheck(L, !!co, 1, "coroutine expected");
int r = resume_helper(L, co, lua_gettop(L) - 1, LUA_MULTRET);
bool ok = Lua::IsSuccess(r);
lua_pushboolean(L, ok);
lua_insert(L, 2);
if (!ok)
report_error(L);
return lua_gettop(L) - 1;
}
static int dfhack_coauxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = resume_helper(L, co, lua_gettop(L), LUA_MULTRET);
if (Lua::IsSuccess(r))
return lua_gettop(L);
else
return lua_error(L);
}
static int dfhack_cowrap (lua_State *L) {
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_settop(L, 1);
Lua::NewCoroutine(L);
lua_pushcclosure(L, dfhack_coauxwrap, 1);
return 1;
}
lua_State *DFHack::Lua::NewCoroutine(lua_State *L) {
lua_State *NL = lua_newthread(L);
lua_swap(L);
lua_xmove(L, NL, 1); /* move function from L to NL */
return NL;
}
int DFHack::Lua::SafeResume(color_ostream &out, lua_State *from, lua_State *thread, int nargs, int nres, bool perr)
{
AssertCoreSuspend(from);
color_ostream *cur_out = Lua::GetOutput(from);
set_dfhack_output(from, &out);
int rv = resume_helper(from, thread, nargs, nres);
if (!Lua::IsSuccess(rv) && perr)
{
report_error(from, &out);
lua_pop(from, 1);
}
set_dfhack_output(from, cur_out);
return rv;
}
int DFHack::Lua::SafeResume(color_ostream &out, lua_State *from, int nargs, int nres, bool perr)
{
int base = lua_gettop(from) - nargs;
lua_State *thread = lua_tothread(from, base);
int rv = SafeResume(out, from, thread, nargs, nres, perr);
lua_remove(from, base);
return rv;
}
/*
* Module loading
*/
static int DFHACK_LOADED_TOKEN = 0; static int DFHACK_LOADED_TOKEN = 0;
bool DFHack::Lua::PushModule(color_ostream &out, lua_State *state, const char *module) bool DFHack::Lua::PushModule(color_ostream &out, lua_State *state, const char *module)
@ -586,29 +786,28 @@ bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std
return Lua::SafeCall(out, state, nargs, nres, perr); return Lua::SafeCall(out, state, nargs, nres, perr);
} }
/*
* Coroutine interactive query loop
*/
static int resume_query_loop(color_ostream &out, static int resume_query_loop(color_ostream &out,
lua_State *thread, lua_State *state, bool start, lua_State *thread, lua_State *state, int nargs,
std::string &prompt, std::string &histfile) std::string &prompt, std::string &histfile)
{ {
color_ostream *cur_out = Lua::GetOutput(state); int rv = Lua::SafeResume(out, state, thread, nargs, 2);
set_dfhack_output(state, &out);
int rv = lua_resume(thread, state, lua_gettop(thread)-(start?1:0));
set_dfhack_output(state, cur_out);
if (rv == LUA_YIELD || rv == LUA_OK) if (Lua::IsSuccess(rv))
{ {
lua_settop(thread, 2); prompt = ifnull(lua_tostring(state, -2), "");
prompt = ifnull(lua_tostring(thread, 1), ""); histfile = ifnull(lua_tostring(state, -1), "");
histfile = ifnull(lua_tostring(thread, 2), ""); lua_pop(state, 2);
} }
return rv; return rv;
} }
bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state, bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
bool (*init)(color_ostream&, lua_State*, lua_State*, void*), bool (*init)(color_ostream&, lua_State*, void*),
void *arg) void *arg)
{ {
if (!out.is_console()) if (!out.is_console())
@ -630,18 +829,21 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
CoreSuspender suspend; CoreSuspender suspend;
int base = lua_gettop(state); int base = lua_gettop(state);
thread = lua_newthread(state);
if (!init(out, state, thread, arg)) if (!init(out, state, arg))
{ {
lua_settop(state, base); lua_settop(state, base);
return false; return false;
} }
lua_settop(state, base+1); lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN);
lua_rawsetp(state, LUA_REGISTRYINDEX, thread); lua_pushvalue(state, base+1);
lua_remove(state, base+1);
thread = Lua::NewCoroutine(state);
lua_rawsetp(state, -2, thread);
lua_pop(state, 1);
rv = resume_query_loop(out, thread, state, true, prompt, histfile); rv = resume_query_loop(out, thread, state, lua_gettop(state)-base, prompt, histfile);
} }
while (rv == LUA_YIELD) while (rv == LUA_YIELD)
@ -668,10 +870,8 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
{ {
CoreSuspender suspend; CoreSuspender suspend;
lua_settop(thread, 0); lua_pushlstring(state, curline.data(), curline.size());
lua_pushlstring(thread, curline.data(), curline.size()); rv = resume_query_loop(out, thread, state, 1, prompt, histfile);
rv = resume_query_loop(out, thread, state, false, prompt, histfile);
} }
} }
@ -681,22 +881,10 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
{ {
CoreSuspender suspend; CoreSuspender suspend;
if (rv != LUA_OK) lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN);
{
lua_xmove(thread, state, 1);
if (convert_to_exception(state))
{
luaL_traceback(state, thread, NULL, 1);
lua_setfield(state, -2, "stacktrace");
}
report_error(state, &out);
lua_pop(state, 1);
}
lua_pushnil(state); lua_pushnil(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, thread); lua_rawsetp(state, -2, thread);
lua_pop(state, 1);
} }
return (rv == LUA_OK); return (rv == LUA_OK);
@ -709,15 +897,14 @@ namespace {
}; };
} }
static bool init_interpreter(color_ostream &out, lua_State *state, lua_State *thread, void *info) static bool init_interpreter(color_ostream &out, lua_State *state, void *info)
{ {
auto args = (InterpreterArgs*)info; auto args = (InterpreterArgs*)info;
lua_getglobal(state, "dfhack"); lua_getglobal(state, "dfhack");
lua_getfield(state, -1, "interpreter"); lua_getfield(state, -1, "interpreter");
lua_remove(state, -2);
lua_pushstring(state, args->prompt); lua_pushstring(state, args->prompt);
lua_pushstring(state, args->hfile); lua_pushstring(state, args->hfile);
lua_xmove(state, thread, 3);
lua_pop(state, 1);
return true; return true;
} }
@ -758,8 +945,10 @@ static bool do_invoke_cleanup(lua_State *L, int nargs, int errorfun, bool succes
return success; return success;
} }
static int finish_dfhack_cleanup (lua_State *L, bool success) int dfhack_cleanup_cont(lua_State *L, int status, int)
{ {
bool success = Lua::IsSuccess(status);
int nargs = lua_tointeger(L, 1); int nargs = lua_tointeger(L, 1);
bool always = lua_toboolean(L, 2); bool always = lua_toboolean(L, 2);
int rvbase = 4+nargs; int rvbase = 4+nargs;
@ -800,12 +989,6 @@ static int finish_dfhack_cleanup (lua_State *L, bool success)
return numret; return numret;
} }
static int dfhack_cleanup_cont (lua_State *L)
{
int status = lua_getctx(L, NULL);
return finish_dfhack_cleanup(L, (status == LUA_YIELD));
}
static int dfhack_call_with_finalizer (lua_State *L) static int dfhack_call_with_finalizer (lua_State *L)
{ {
int nargs = luaL_checkint(L, 1); int nargs = luaL_checkint(L, 1);
@ -840,8 +1023,7 @@ static int dfhack_call_with_finalizer (lua_State *L)
// Actually invoke // Actually invoke
// stack: [nargs] [always] [errorfun] [cleanup fun] [cleanup args...] |rvbase+1:| [fun] [args...] // stack: [nargs] [always] [errorfun] [cleanup fun] [cleanup args...] |rvbase+1:| [fun] [args...]
int status = lua_pcallk(L, lua_gettop(L)-rvbase-1, LUA_MULTRET, 3, 0, dfhack_cleanup_cont); return Lua::TailPCallK<dfhack_cleanup_cont>(L, lua_gettop(L)-rvbase-1, LUA_MULTRET, 3, 0);
return finish_dfhack_cleanup(L, (status == LUA_OK));
} }
static int lua_dfhack_with_suspend(lua_State *L) static int lua_dfhack_with_suspend(lua_State *L)
@ -886,8 +1068,9 @@ static const luaL_Reg dfhack_funcs[] = {
{ "printerr", lua_dfhack_printerr }, { "printerr", lua_dfhack_printerr },
{ "color", lua_dfhack_color }, { "color", lua_dfhack_color },
{ "is_interactive", lua_dfhack_is_interactive }, { "is_interactive", lua_dfhack_is_interactive },
{ "lineedit", lua_dfhack_lineedit }, { "lineedit", dfhack_lineedit },
{ "safecall", lua_dfhack_safecall }, { "safecall", dfhack_safecall },
{ "saferesume", dfhack_saferesume },
{ "onerror", dfhack_onerror }, { "onerror", dfhack_onerror },
{ "call_with_finalizer", dfhack_call_with_finalizer }, { "call_with_finalizer", dfhack_call_with_finalizer },
{ "with_suspend", lua_dfhack_with_suspend }, { "with_suspend", lua_dfhack_with_suspend },
@ -895,6 +1078,12 @@ static const luaL_Reg dfhack_funcs[] = {
{ NULL, NULL } { NULL, NULL }
}; };
static const luaL_Reg dfhack_coro_funcs[] = {
{ "resume", dfhack_coresume },
{ "wrap", dfhack_cowrap },
{ NULL, NULL }
};
/************ /************
* Events * * Events *
************/ ************/
@ -981,7 +1170,7 @@ void DFHack::Lua::InvokeEvent(color_ostream &out, lua_State *state, void *key, i
set_dfhack_output(state, cur_out); set_dfhack_output(state, cur_out);
} }
void DFHack::Lua::CreateEvent(lua_State *state, void *key) void DFHack::Lua::MakeEvent(lua_State *state, void *key)
{ {
lua_rawgetp(state, LUA_REGISTRYINDEX, key); lua_rawgetp(state, LUA_REGISTRYINDEX, key);
@ -1009,7 +1198,7 @@ void DFHack::Lua::Notification::bind(lua_State *state, void *key)
void DFHack::Lua::Notification::bind(lua_State *state, const char *name) void DFHack::Lua::Notification::bind(lua_State *state, const char *name)
{ {
CreateEvent(state, this); MakeEvent(state, this);
if (handler) if (handler)
{ {
@ -1035,6 +1224,10 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
luaL_openlibs(state); luaL_openlibs(state);
AttachDFGlobals(state); AttachDFGlobals(state);
// Table of query coroutines
lua_newtable(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN);
// Replace the print function of the standard library // Replace the print function of the standard library
lua_pushcfunction(state, lua_dfhack_println); lua_pushcfunction(state, lua_dfhack_println);
lua_setglobal(state, "print"); lua_setglobal(state, "print");
@ -1077,6 +1270,11 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_LOADED_TOKEN); lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_LOADED_TOKEN);
lua_pop(state, 1); lua_pop(state, 1);
// replace some coroutine functions
lua_getglobal(state, "coroutine");
luaL_setfuncs(state, dfhack_coro_funcs, 0);
lua_pop(state, 1);
// load dfhack.lua // load dfhack.lua
Require(out, state, "dfhack"); Require(out, state, "dfhack");

@ -537,7 +537,7 @@ void Plugin::open_lua(lua_State *state, int table)
{ {
for (auto it = lua_events.begin(); it != lua_events.end(); ++it) for (auto it = lua_events.begin(); it != lua_events.end(); ++it)
{ {
Lua::CreateEvent(state, it->second); Lua::MakeEvent(state, it->second);
push_function(state, &it->second->handler); push_function(state, &it->second->handler);
lua_rawsetp(state, -2, NULL); lua_rawsetp(state, -2, NULL);

@ -139,12 +139,56 @@ namespace DFHack {namespace Lua {
return AssignDFObject(out, state, df::identity_traits<T>::get(), target, val_index, perr); return AssignDFObject(out, state, df::identity_traits<T>::get(), target, val_index, perr);
} }
/**
* Check if the status is a success, i.e. LUA_OK or LUA_YIELD.
*/
inline bool IsSuccess(int status) {
return (status == LUA_OK || status == LUA_YIELD);
}
// Internal helper
template<int (*cb)(lua_State*,int,int)>
int TailPCallK_Thunk(lua_State *state) {
int tmp;
int rv = lua_getctx(state, &tmp);
return cb(state, rv, tmp);
}
/**
* A utility for using the restartable pcall feature more conveniently;
* specifically, the callback is called with the same kind of arguments
* in both yield and non-yield case.
*/
template<int (*cb)(lua_State*,int,int)>
int TailPCallK(lua_State *state, int narg, int nret, int errfun, int ctx) {
int rv = lua_pcallk(state, narg, nret, errfun, ctx, &TailPCallK_Thunk<cb>);
return cb(state, rv, ctx);
}
/** /**
* 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);
/**
* Pops a function from the top of the stack, and pushes a new coroutine.
*/
DFHACK_EXPORT lua_State *NewCoroutine(lua_State *state);
/**
* Resume the coroutine using nargs values from state from. Results or the error are moved back.
* If an error is signalled, and perr is true, it is printed and popped from the stack.
* Returns the lua_resume return value.
*/
DFHACK_EXPORT int SafeResume(color_ostream &out, lua_State *from, lua_State *thread, int nargs, int nres, bool perr = true);
/**
* Works just like SafeCall, only expects a coroutine on the stack
* instead of a function. Returns the lua_resume return value.
*/
DFHACK_EXPORT int SafeResume(color_ostream &out, lua_State *from, int nargs, int nres, bool perr = true);
/** /**
* Parse code from string with debug_tag and env_idx, then call it using SafeCall. * 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. * In case of error, it is either left on the stack, or printed like SafeCall does.
@ -166,15 +210,18 @@ namespace DFHack {namespace Lua {
const char *prompt = NULL, const char *hfile = NULL); const char *prompt = NULL, const char *hfile = NULL);
/** /**
* Run an interactive prompt loop. All access to lua is done inside CoreSuspender. * Run an interactive prompt loop. All access to the lua state
* is done inside CoreSuspender, while waiting for input happens
* without the suspend lock.
*/ */
DFHACK_EXPORT bool RunCoreQueryLoop(color_ostream &out, lua_State *state, DFHACK_EXPORT bool RunCoreQueryLoop(color_ostream &out, lua_State *state,
bool (*init)(color_ostream&, lua_State*, lua_State*, void*), bool (*init)(color_ostream&, lua_State*, void*),
void *arg); void *arg);
/** /**
* Push utility functions * Push utility functions
*/ */
#if 0
#define NUMBER_PUSH(type) inline void Push(lua_State *state, type value) { lua_pushnumber(state, value); } #define NUMBER_PUSH(type) inline void Push(lua_State *state, type value) { lua_pushnumber(state, value); }
NUMBER_PUSH(char) NUMBER_PUSH(char)
NUMBER_PUSH(int8_t) NUMBER_PUSH(uint8_t) NUMBER_PUSH(int8_t) NUMBER_PUSH(uint8_t)
@ -183,6 +230,11 @@ namespace DFHack {namespace Lua {
NUMBER_PUSH(int64_t) NUMBER_PUSH(uint64_t) NUMBER_PUSH(int64_t) NUMBER_PUSH(uint64_t)
NUMBER_PUSH(float) NUMBER_PUSH(double) NUMBER_PUSH(float) NUMBER_PUSH(double)
#undef NUMBER_PUSH #undef NUMBER_PUSH
#else
template<class T> inline void Push(lua_State *state, T value) {
lua_pushnumber(state, lua_Number(value));
}
#endif
inline void Push(lua_State *state, bool value) { inline void Push(lua_State *state, bool value) {
lua_pushboolean(state, value); lua_pushboolean(state, value);
} }
@ -212,7 +264,7 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT bool IsCoreContext(lua_State *state); DFHACK_EXPORT bool IsCoreContext(lua_State *state);
DFHACK_EXPORT int NewEvent(lua_State *state); DFHACK_EXPORT int NewEvent(lua_State *state);
DFHACK_EXPORT void CreateEvent(lua_State *state, void *key); DFHACK_EXPORT void MakeEvent(lua_State *state, void *key);
DFHACK_EXPORT void InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args); DFHACK_EXPORT void InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args);
/** /**

@ -130,15 +130,6 @@ end
-- Interactive -- Interactive
function dfhack.query(prompt,hfile)
local _,main = coroutine.running()
if main then
return dfhack.lineedit(prompt,hfile)
else
return coroutine.yield(prompt,hfile)
end
end
local print_banner = true local print_banner = true
function dfhack.interpreter(prompt,hfile,env) function dfhack.interpreter(prompt,hfile,env)
@ -163,7 +154,7 @@ function dfhack.interpreter(prompt,hfile,env)
setmetatable(prompt_env, { __index = env or _G }) setmetatable(prompt_env, { __index = env or _G })
while true do while true do
local cmdline = dfhack.query(prompt_str, hfile) local cmdline = dfhack.lineedit(prompt_str, hfile)
if cmdline == nil or cmdline == 'quit' then if cmdline == nil or cmdline == 'quit' then
break break