summaryrefslogtreecommitdiff
path: root/library/LuaTools.cpp
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-04-15 21:50:22 +0400
committerAlexander Gavrilov2012-04-15 21:50:22 +0400
commita1756a864cc7c7a6f498c5cfe96e0a9d0ae57828 (patch)
treef3205fe400716aa715db34cec766e1a9ae82f420 /library/LuaTools.cpp
parent14709e5d4598e11ddce6f9d2cce734528d842ca5 (diff)
downloaddfhack-a1756a864cc7c7a6f498c5cfe96e0a9d0ae57828.tar.gz
dfhack-a1756a864cc7c7a6f498c5cfe96e0a9d0ae57828.tar.bz2
dfhack-a1756a864cc7c7a6f498c5cfe96e0a9d0ae57828.tar.xz
Implement a way to do prompts from core context.
The trick obviously is doing it without forcing DF to wait suspended. Fortunately, lua has built-in coroutine support, so the interactive prompt can simply yield and rely on the external loop to do the job. To use this however the REPL had to be replaced with lua code.
Diffstat (limited to 'library/LuaTools.cpp')
-rw-r--r--library/LuaTools.cpp199
1 files changed, 112 insertions, 87 deletions
diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp
index 304d79bc..bf40e404 100644
--- a/library/LuaTools.cpp
+++ b/library/LuaTools.cpp
@@ -426,7 +426,7 @@ static bool do_finish_pcall(lua_State *L, bool success, int base = 1, int space
{
lua_pushboolean(L, success);
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);
}
-bool DFHack::Lua::InterpreterLoop(color_ostream &out, lua_State *state,
- const char *prompt, int env, const char *hfile)
+static int resume_query_loop(color_ostream &out,
+ 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())
return false;
if (!lua_checkstack(state, 20))
return false;
- if (!hfile)
- hfile = "lua.history";
- if (!prompt)
- prompt = "lua";
+ Console &con = static_cast<Console&>(out);
+
+ lua_State *thread;
+ int rv;
+ std::string prompt;
+ std::string histfile;
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;
- if (print_banner) {
- out.print("Shortcuts:\n"
- " '= foo' => '_1,_2,... = foo'\n"
- " '! foo' => 'print(foo)'\n"
- "Both save the first result as '_'.\n");
- print_banner = false;
- }
+ int base = lua_gettop(state);
+ thread = lua_newthread(state);
- Console &con = static_cast<Console&>(out);
+ if (!init(out, state, thread, arg))
+ {
+ lua_settop(state, base);
+ return false;
+ }
- // Make a proxy global environment.
- lua_newtable(state);
- int base = lua_gettop(state);
+ lua_settop(state, base+1);
+ lua_rawsetp(state, LUA_REGISTRYINDEX, thread);
- lua_newtable(state);
- if (env)
- lua_pushvalue(state, env);
- else
- lua_rawgeti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
- lua_setfield(state, -2, "__index");
- lua_setmetatable(state, -2);
+ rv = resume_query_loop(out, thread, state, true, prompt, histfile);
+ }
- // Main interactive loop
- int vcnt = 1;
- string curline;
- string prompt_str = "[" + string(prompt) + "]# ";
+ while (rv == LUA_YIELD)
+ {
+ if (histfile != histname)
+ {
+ if (!histname.empty())
+ hist.save(histname.c_str());
- for (;;) {
- lua_settop(state, base);
+ hist.clear();
+ histname = histfile;
- con.lineedit(prompt_str,curline,hist);
+ if (!histname.empty())
+ hist.load(histname.c_str());
+ }
- if (curline.empty())
- continue;
- if (curline == "quit")
- break;
+ if (prompt.empty())
+ prompt = ">> ";
+ std::string curline;
+ con.lineedit(prompt,curline,hist);
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))
- continue;
+ lua_settop(thread, 0);
+ 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)
- {
- 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;
- }
- }
+ if (!histname.empty())
+ hist.save(histname.c_str());
- for (int i = 1; i <= numret; i++)
- {
- std::string name = stl_sprintf("_%d", vcnt++);
- lua_pushvalue(state, base + i);
- lua_setfield(state, base, name.c_str());
+ {
+ CoreSuspender suspend;
- out.print("%s = ", name.c_str());
+ if (rv != LUA_OK)
+ {
+ lua_xmove(thread, state, 1);
- lua_pushcfunction(state, lua_dfhack_println);
- lua_pushvalue(state, base + i);
- SafeCall(out, state, 1, 0);
+ if (convert_to_exception(state))
+ {
+ luaL_traceback(state, thread, NULL, 1);
+ lua_setfield(state, -2, "stacktrace");
}
+
+ report_error(state, &out);
+ lua_pop(state, 1);
}
- else
- {
- if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base))
- continue;
- }
+
+ lua_pushnil(state);
+ lua_rawsetp(state, LUA_REGISTRYINDEX, thread);
}
- 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;
}
-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 (!pstream)
- return 2;
+ if (!out.is_console())
+ return false;
- int argc = lua_gettop(state);
+ if (!hfile)
+ hfile = "lua.history";
+ if (!prompt)
+ prompt = "lua";
- const char *prompt = (argc >= 1 ? lua_tostring(state, 1) : NULL);
- int env = (argc >= 2 && !lua_isnil(state,2) ? 2 : 0);
- const char *hfile = (argc >= 3 ? lua_tostring(state, 3) : NULL);
+ InterpreterArgs args;
+ args.prompt = prompt;
+ args.hfile = hfile;
- lua_pushboolean(state, Lua::InterpreterLoop(*pstream, state, prompt, env, hfile));
- return 1;
+ return RunCoreQueryLoop(out, state, init_interpreter, &args);
}
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 },
{ "is_interactive", lua_dfhack_is_interactive },
{ "lineedit", lua_dfhack_lineedit },
- { "interpreter", lua_dfhack_interpreter },
{ "safecall", lua_dfhack_safecall },
{ "onerror", dfhack_onerror },
{ "call_with_finalizer", dfhack_call_with_finalizer },