|
|
@ -426,7 +426,7 @@ static bool do_finish_pcall(lua_State *L, bool success, int base = 1, int space
|
|
|
|
{
|
|
|
|
{
|
|
|
|
lua_pushboolean(L, success);
|
|
|
|
lua_pushboolean(L, success);
|
|
|
|
lua_replace(L, base); /* put first result in first slot */
|
|
|
|
lua_replace(L, base); /* put first result in first slot */
|
|
|
|
return true;
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -586,131 +586,157 @@ 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
|
|
|
|
static int resume_query_loop(color_ostream &out,
|
|
|
|
const char *prompt, int env, const char *hfile)
|
|
|
|
lua_State *thread, lua_State *state, bool start,
|
|
|
|
|
|
|
|
std::string &prompt, std::string &histfile)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
AssertCoreSuspend(state);
|
|
|
|
color_ostream *cur_out = Lua::GetOutput(state);
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_settop(thread, 2);
|
|
|
|
|
|
|
|
prompt = ifnull(lua_tostring(thread, 1), "");
|
|
|
|
|
|
|
|
histfile = ifnull(lua_tostring(thread, 2), "");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
|
|
|
|
|
|
|
|
bool (*init)(color_ostream&, lua_State*, lua_State*, void*),
|
|
|
|
|
|
|
|
void *arg)
|
|
|
|
|
|
|
|
{
|
|
|
|
if (!out.is_console())
|
|
|
|
if (!out.is_console())
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
if (!lua_checkstack(state, 20))
|
|
|
|
if (!lua_checkstack(state, 20))
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
if (!hfile)
|
|
|
|
Console &con = static_cast<Console&>(out);
|
|
|
|
hfile = "lua.history";
|
|
|
|
|
|
|
|
if (!prompt)
|
|
|
|
lua_State *thread;
|
|
|
|
prompt = "lua";
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
std::string prompt;
|
|
|
|
|
|
|
|
std::string histfile;
|
|
|
|
|
|
|
|
|
|
|
|
DFHack::CommandHistory hist;
|
|
|
|
DFHack::CommandHistory hist;
|
|
|
|
hist.load(hfile);
|
|
|
|
std::string histname;
|
|
|
|
|
|
|
|
|
|
|
|
out.print("Type quit to exit interactive lua interpreter.\n");
|
|
|
|
{
|
|
|
|
|
|
|
|
CoreSuspender suspend;
|
|
|
|
|
|
|
|
|
|
|
|
static bool print_banner = true;
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
if (print_banner) {
|
|
|
|
thread = lua_newthread(state);
|
|
|
|
out.print("Shortcuts:\n"
|
|
|
|
|
|
|
|
" '= foo' => '_1,_2,... = foo'\n"
|
|
|
|
|
|
|
|
" '! foo' => 'print(foo)'\n"
|
|
|
|
|
|
|
|
"Both save the first result as '_'.\n");
|
|
|
|
|
|
|
|
print_banner = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Console &con = static_cast<Console&>(out);
|
|
|
|
if (!init(out, state, thread, arg))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_settop(state, base);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Make a proxy global environment.
|
|
|
|
lua_settop(state, base+1);
|
|
|
|
lua_newtable(state);
|
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, thread);
|
|
|
|
int base = lua_gettop(state);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua_newtable(state);
|
|
|
|
rv = resume_query_loop(out, thread, state, true, prompt, histfile);
|
|
|
|
if (env)
|
|
|
|
}
|
|
|
|
lua_pushvalue(state, env);
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
lua_rawgeti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
|
|
|
|
|
|
|
|
lua_setfield(state, -2, "__index");
|
|
|
|
|
|
|
|
lua_setmetatable(state, -2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Main interactive loop
|
|
|
|
while (rv == LUA_YIELD)
|
|
|
|
int vcnt = 1;
|
|
|
|
{
|
|
|
|
string curline;
|
|
|
|
if (histfile != histname)
|
|
|
|
string prompt_str = "[" + string(prompt) + "]# ";
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!histname.empty())
|
|
|
|
|
|
|
|
hist.save(histname.c_str());
|
|
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
hist.clear();
|
|
|
|
lua_settop(state, base);
|
|
|
|
histname = histfile;
|
|
|
|
|
|
|
|
|
|
|
|
con.lineedit(prompt_str,curline,hist);
|
|
|
|
if (!histname.empty())
|
|
|
|
|
|
|
|
hist.load(histname.c_str());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (curline.empty())
|
|
|
|
if (prompt.empty())
|
|
|
|
continue;
|
|
|
|
prompt = ">> ";
|
|
|
|
if (curline == "quit")
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::string curline;
|
|
|
|
|
|
|
|
con.lineedit(prompt,curline,hist);
|
|
|
|
hist.add(curline);
|
|
|
|
hist.add(curline);
|
|
|
|
|
|
|
|
|
|
|
|
char pfix = curline[0];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pfix == '=' || pfix == '!')
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
curline = "return " + curline.substr(1);
|
|
|
|
CoreSuspender suspend;
|
|
|
|
|
|
|
|
|
|
|
|
if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base))
|
|
|
|
lua_settop(thread, 0);
|
|
|
|
continue;
|
|
|
|
lua_pushlstring(thread, curline.data(), curline.size());
|
|
|
|
|
|
|
|
|
|
|
|
int numret = lua_gettop(state) - base;
|
|
|
|
rv = resume_query_loop(out, thread, state, false, prompt, histfile);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (numret >= 1)
|
|
|
|
if (!histname.empty())
|
|
|
|
{
|
|
|
|
hist.save(histname.c_str());
|
|
|
|
lua_pushvalue(state, base+1);
|
|
|
|
|
|
|
|
lua_setfield(state, base, "_");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pfix == '!')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_pushcfunction(state, lua_dfhack_println);
|
|
|
|
|
|
|
|
lua_insert(state, base+1);
|
|
|
|
|
|
|
|
SafeCall(out, state, numret, 0);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= numret; i++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
CoreSuspender suspend;
|
|
|
|
std::string name = stl_sprintf("_%d", vcnt++);
|
|
|
|
|
|
|
|
lua_pushvalue(state, base + i);
|
|
|
|
|
|
|
|
lua_setfield(state, base, name.c_str());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
out.print("%s = ", name.c_str());
|
|
|
|
if (rv != LUA_OK)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lua_xmove(thread, state, 1);
|
|
|
|
|
|
|
|
|
|
|
|
lua_pushcfunction(state, lua_dfhack_println);
|
|
|
|
if (convert_to_exception(state))
|
|
|
|
lua_pushvalue(state, base + i);
|
|
|
|
{
|
|
|
|
SafeCall(out, state, 1, 0);
|
|
|
|
luaL_traceback(state, thread, NULL, 1);
|
|
|
|
|
|
|
|
lua_setfield(state, -2, "stacktrace");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
report_error(state, &out);
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
lua_pushnil(state);
|
|
|
|
if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base))
|
|
|
|
lua_rawsetp(state, LUA_REGISTRYINDEX, thread);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
lua_settop(state, base-1);
|
|
|
|
return (rv == LUA_OK);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
struct InterpreterArgs {
|
|
|
|
|
|
|
|
const char *prompt;
|
|
|
|
|
|
|
|
const char *hfile;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
hist.save(hfile);
|
|
|
|
static bool init_interpreter(color_ostream &out, lua_State *state, lua_State *thread, void *info)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
auto args = (InterpreterArgs*)info;
|
|
|
|
|
|
|
|
lua_getglobal(state, "dfhack");
|
|
|
|
|
|
|
|
lua_getfield(state, -1, "interpreter");
|
|
|
|
|
|
|
|
lua_pushstring(state, args->prompt);
|
|
|
|
|
|
|
|
lua_pushstring(state, args->hfile);
|
|
|
|
|
|
|
|
lua_xmove(state, thread, 3);
|
|
|
|
|
|
|
|
lua_pop(state, 1);
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int lua_dfhack_interpreter(lua_State *state)
|
|
|
|
bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
|
|
|
|
|
|
|
|
const char *prompt, const char *hfile)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Console *pstream = get_console(state);
|
|
|
|
if (!out.is_console())
|
|
|
|
if (!pstream)
|
|
|
|
return false;
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int argc = lua_gettop(state);
|
|
|
|
if (!hfile)
|
|
|
|
|
|
|
|
hfile = "lua.history";
|
|
|
|
|
|
|
|
if (!prompt)
|
|
|
|
|
|
|
|
prompt = "lua";
|
|
|
|
|
|
|
|
|
|
|
|
const char *prompt = (argc >= 1 ? lua_tostring(state, 1) : NULL);
|
|
|
|
InterpreterArgs args;
|
|
|
|
int env = (argc >= 2 && !lua_isnil(state,2) ? 2 : 0);
|
|
|
|
args.prompt = prompt;
|
|
|
|
const char *hfile = (argc >= 3 ? lua_tostring(state, 3) : NULL);
|
|
|
|
args.hfile = hfile;
|
|
|
|
|
|
|
|
|
|
|
|
lua_pushboolean(state, Lua::InterpreterLoop(*pstream, state, prompt, env, hfile));
|
|
|
|
return RunCoreQueryLoop(out, state, init_interpreter, &args);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool do_invoke_cleanup(lua_State *L, int nargs, int errorfun, bool success)
|
|
|
|
static bool do_invoke_cleanup(lua_State *L, int nargs, int errorfun, bool success)
|
|
|
@ -861,7 +887,6 @@ static const luaL_Reg dfhack_funcs[] = {
|
|
|
|
{ "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", lua_dfhack_lineedit },
|
|
|
|
{ "interpreter", lua_dfhack_interpreter },
|
|
|
|
|
|
|
|
{ "safecall", lua_dfhack_safecall },
|
|
|
|
{ "safecall", lua_dfhack_safecall },
|
|
|
|
{ "onerror", dfhack_onerror },
|
|
|
|
{ "onerror", dfhack_onerror },
|
|
|
|
{ "call_with_finalizer", dfhack_call_with_finalizer },
|
|
|
|
{ "call_with_finalizer", dfhack_call_with_finalizer },
|
|
|
|