/* * File: luaterp.cc * Summary: An interactive lua interpreter. * * Based heavily on lua.c from the Lua distribution. * * Deficiencies: * - Currently requires defining a global function __echo * to echo results. * - Currently requires prefixing with '=' if you want results echoed. * - Appears to require delay_message_clear = true. * - Old input lines are not visible. */ #include "AppHdr.h" #include "luaterp.h" #include "cio.h" #include "clua.h" #include "dlua.h" #include "options.h" #ifdef WIZARD static int _incomplete(lua_State *ls, int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; const char *msg = lua_tolstring(ls, -1, &lmsg); const char *tp = msg + lmsg - (sizeof(LUA_QL("")) - 1); if (strstr(msg, LUA_QL("")) == tp) { lua_pop(ls, 1); return 1; } } return 0; } static int _pushline(lua_State *ls, int firstline) { char buffer[80]; char *b = buffer; size_t l; mpr(firstline ? "> " : ". ", MSGCH_PROMPT); if (cancelable_get_line_autohist(buffer, sizeof(buffer))) return (0); l = strlen(b); // XXX: get line doesn't return newline? if (l > 0 && b[l-1] == '\n') // line ends with newline? b[l-1] = '\0'; // remove it if (firstline && l > 0 && b[0] == '=') // first line starts with `=' ? lua_pushfstring(ls, "return %s", b+1); // change it to `return' else lua_pushstring(ls, b); return (1); } static int _loadline(lua_State *ls) { int status; lua_settop(ls, 0); if (!_pushline(ls, 1)) return -1; /* no input */ // repeat until gets a complete line for (;;) { status = luaL_loadbuffer(ls, lua_tostring(ls, 1), lua_strlen(ls, 1), "=repl"); if (!_incomplete(ls, status)) break; if (!_pushline(ls, 0)) return -1; lua_pushliteral(ls, "\n"); // add a new line... lua_insert(ls, -2); // ...between the two lines lua_concat(ls, 3); // join them } lua_remove(ls, 1); // remove line return status; } static int _docall(lua_State *ls) { int status; status = lua_pcall(ls, 0, LUA_MULTRET, 0); return (status); } static int _report(lua_State *ls, int status) { if (status && !lua_isnil(ls, -1)) { const char *msg = lua_tostring(ls, -1); if (msg == NULL) msg = "(error object is not a string)"; mprf(MSGCH_ERROR, msg); lua_pop(ls, 1); } return status; } static bool _luaterp_running = false; void run_clua_interpreter(lua_State *ls) { _luaterp_running = true; int status; mpr("[Hit ESC to exit interpreter.]"); while ((status = _loadline(ls)) != -1) { if (status == 0) status = _docall(ls); _report(ls, status); if (status == 0 && lua_gettop(ls) > 0) { lua_getglobal(ls, "__echo"); lua_insert(ls, 1); if (lua_pcall(ls, lua_gettop(ls) - 1, 0, 0) != 0) { mprf(MSGCH_ERROR, "error calling __echo (%s)", lua_tostring(ls, -1)); } } } lua_settop(ls, 0); // clear stack _luaterp_running = false; } bool luaterp_running() { return (_luaterp_running); } static bool _loaded_terp_files = false; void debug_terp_dlua() { if (!_loaded_terp_files) { dlua.execfile("clua/debug.lua", false, false); for (unsigned int i = 0; i < Options.terp_files.size(); i++) { dlua.execfile(Options.terp_files[i].c_str(), false, false); if (!dlua.error.empty()) mprf(MSGCH_ERROR, "Lua error: %s", dlua.error.c_str()); } _loaded_terp_files = true; } run_clua_interpreter(dlua); } #endif