develop
Warmist 2012-04-15 20:56:49 +03:00
commit ea4c1e4928
4 changed files with 197 additions and 90 deletions

@ -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)
prompt = "lua";
DFHack::CommandHistory hist;
hist.load(hfile);
out.print("Type quit to exit interactive lua interpreter.\n"); lua_State *thread;
int rv;
std::string prompt;
std::string histfile;
static bool print_banner = true; DFHack::CommandHistory hist;
if (print_banner) { std::string histname;
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); {
CoreSuspender suspend;
// Make a proxy global environment.
lua_newtable(state);
int base = lua_gettop(state); int base = lua_gettop(state);
thread = lua_newthread(state);
lua_newtable(state); if (!init(out, state, thread, arg))
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
int vcnt = 1;
string curline;
string prompt_str = "[" + string(prompt) + "]# ";
for (;;) {
lua_settop(state, base); lua_settop(state, base);
return false;
}
con.lineedit(prompt_str,curline,hist); lua_settop(state, base+1);
lua_rawsetp(state, LUA_REGISTRYINDEX, thread);
if (curline.empty()) rv = resume_query_loop(out, thread, state, true, prompt, histfile);
continue; }
if (curline == "quit")
break;
hist.add(curline); while (rv == LUA_YIELD)
{
if (histfile != histname)
{
if (!histname.empty())
hist.save(histname.c_str());
char pfix = curline[0]; hist.clear();
histname = histfile;
if (pfix == '=' || pfix == '!') if (!histname.empty())
{ hist.load(histname.c_str());
curline = "return " + curline.substr(1); }
if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base)) if (prompt.empty())
continue; prompt = ">> ";
int numret = lua_gettop(state) - base; std::string curline;
con.lineedit(prompt,curline,hist);
hist.add(curline);
if (numret >= 1)
{ {
lua_pushvalue(state, base+1); CoreSuspender suspend;
lua_setfield(state, base, "_");
if (pfix == '!') lua_settop(thread, 0);
{ lua_pushlstring(thread, curline.data(), curline.size());
lua_pushcfunction(state, lua_dfhack_println);
lua_insert(state, base+1); rv = resume_query_loop(out, thread, state, false, prompt, histfile);
SafeCall(out, state, numret, 0);
continue;
} }
} }
for (int i = 1; i <= numret; i++) if (!histname.empty())
hist.save(histname.c_str());
{ {
std::string name = stl_sprintf("_%d", vcnt++); CoreSuspender suspend;
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);
}
}
else
{ {
if (!Lua::SafeCallString(out, state, curline, 0, LUA_MULTRET, true, "=(interactive)", base)) luaL_traceback(state, thread, NULL, 1);
continue; lua_setfield(state, -2, "stacktrace");
} }
report_error(state, &out);
lua_pop(state, 1);
} }
lua_settop(state, base-1); lua_pushnil(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, thread);
}
hist.save(hfile); return (rv == LUA_OK);
}
namespace {
struct InterpreterArgs {
const char *prompt;
const char *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 },

@ -160,9 +160,17 @@ namespace DFHack {namespace Lua {
/** /**
* Run an interactive interpreter loop if possible, or return false. * Run an interactive interpreter loop if possible, or return false.
* Uses RunCoreQueryLoop internally.
*/ */
DFHACK_EXPORT bool InterpreterLoop(color_ostream &out, lua_State *state, DFHACK_EXPORT bool InterpreterLoop(color_ostream &out, lua_State *state,
const char *prompt = NULL, int env = 0, 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.
*/
DFHACK_EXPORT bool RunCoreQueryLoop(color_ostream &out, lua_State *state,
bool (*init)(color_ostream&, lua_State*, lua_State*, void*),
void *arg);
/** /**
* Push utility functions * Push utility functions

@ -128,5 +128,80 @@ function dfhack.maps.getTileSize()
return map.x_count, map.y_count, map.z_count return map.x_count, map.y_count, map.z_count
end end
-- 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
function dfhack.interpreter(prompt,hfile,env)
if not dfhack.is_interactive() then
return nil, 'not interactive'
end
print("Type quit to exit interactive lua interpreter.")
if print_banner then
print("Shortcuts:\n"..
" '= foo' => '_1,_2,... = foo'\n"..
" '! foo' => 'print(foo)'\n"..
"Both save the first result as '_'.")
print_banner = false
end
local prompt_str = "["..(prompt or 'lua').."]# "
local prompt_env = {}
local vcnt = 1
setmetatable(prompt_env, { __index = env or _G })
while true do
local cmdline = dfhack.query(prompt_str, hfile)
if cmdline == nil or cmdline == 'quit' then
break
elseif cmdline ~= '' then
local pfix = string.sub(cmdline,1,1)
if pfix == '!' or pfix == '=' then
cmdline = 'return '..string.sub(cmdline,2)
end
local code,err = load(cmdline, '=(interactive)', 't', prompt_env)
if code == nil then
dfhack.printerr(err)
else
local data = table.pack(safecall(code))
if data[1] and data.n > 1 then
prompt_env._ = data[2]
if pfix == '!' then
safecall(print, table.unpack(data,2,data.n))
else
for i=2,data.n do
local varname = '_'..vcnt
prompt_env[varname] = data[i]
dfhack.print(varname..' = ')
safecall(print, data[i])
vcnt = vcnt + 1
end
end
end
end
end
end
return true
end
-- Feed the table back to the require() mechanism. -- Feed the table back to the require() mechanism.
return dfhack return dfhack

@ -123,8 +123,7 @@ command_result lua_run (color_ostream &out, std::vector <std::string> &parameter
{ {
if (!parameters.empty() && parameters[0] == "--core-context") if (!parameters.empty() && parameters[0] == "--core-context")
{ {
CoreSuspender suspend; Lua::InterpreterLoop(out, Lua::Core::State, "core lua");
Lua::InterpreterLoop(out, Lua::Core::State);
return CR_OK; return CR_OK;
} }