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_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 },

@ -160,9 +160,17 @@ namespace DFHack {namespace Lua {
/**
* Run an interactive interpreter loop if possible, or return false.
* Uses RunCoreQueryLoop internally.
*/
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

@ -128,5 +128,80 @@ function dfhack.maps.getTileSize()
return map.x_count, map.y_count, map.z_count
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.
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")
{
CoreSuspender suspend;
Lua::InterpreterLoop(out, Lua::Core::State);
Lua::InterpreterLoop(out, Lua::Core::State, "core lua");
return CR_OK;
}