diff --git a/luprex/Makefile b/luprex/Makefile index f57e4017..54ad7c25 100644 --- a/luprex/Makefile +++ b/luprex/Makefile @@ -1,24 +1,25 @@ -CXX=g++ -std=c++17 -Wall -g -Iinc -Isyscpp +CXX=g++ -std=c++17 -Wall -g -Iinc -Icpp CPP_FILES=\ - syscpp/util.cpp\ - syscpp/luastack.cpp\ - syscpp/traceback.cpp\ - syscpp/planemap.cpp\ - syscpp/idalloc.cpp\ - syscpp/globaldb.cpp\ - syscpp/table.cpp\ - syscpp/animqueue.cpp\ - syscpp/source.cpp\ - syscpp/world.cpp\ - syscpp/viewer.cpp\ - syscpp/textgame.cpp\ - syscpp/main.cpp + cpp/util.cpp\ + cpp/luastack.cpp\ + cpp/traceback.cpp\ + cpp/planemap.cpp\ + cpp/luaconsole.cpp\ + cpp/idalloc.cpp\ + cpp/globaldb.cpp\ + cpp/table.cpp\ + cpp/animqueue.cpp\ + cpp/source.cpp\ + cpp/world.cpp\ + cpp/viewer.cpp\ + cpp/textgame.cpp\ + cpp/main.cpp -OBJ_FILES=$(patsubst syscpp/%.cpp,obj/%.o,$(CPP_FILES)) +OBJ_FILES=$(patsubst cpp/%.cpp,obj/%.o,$(CPP_FILES)) -obj/%.o: syscpp/%.cpp +obj/%.o: cpp/%.cpp $(CXX) -c -MMD $< -o $@ main.exe: $(OBJ_FILES) diff --git a/luprex/syscpp/animqueue.cpp b/luprex/cpp/animqueue.cpp similarity index 100% rename from luprex/syscpp/animqueue.cpp rename to luprex/cpp/animqueue.cpp diff --git a/luprex/syscpp/animqueue.hpp b/luprex/cpp/animqueue.hpp similarity index 100% rename from luprex/syscpp/animqueue.hpp rename to luprex/cpp/animqueue.hpp diff --git a/luprex/syscpp/globaldb.cpp b/luprex/cpp/globaldb.cpp similarity index 100% rename from luprex/syscpp/globaldb.cpp rename to luprex/cpp/globaldb.cpp diff --git a/luprex/syscpp/globaldb.hpp b/luprex/cpp/globaldb.hpp similarity index 100% rename from luprex/syscpp/globaldb.hpp rename to luprex/cpp/globaldb.hpp diff --git a/luprex/syscpp/idalloc.cpp b/luprex/cpp/idalloc.cpp similarity index 100% rename from luprex/syscpp/idalloc.cpp rename to luprex/cpp/idalloc.cpp diff --git a/luprex/syscpp/idalloc.hpp b/luprex/cpp/idalloc.hpp similarity index 100% rename from luprex/syscpp/idalloc.hpp rename to luprex/cpp/idalloc.hpp diff --git a/luprex/cpp/luaconsole.cpp b/luprex/cpp/luaconsole.cpp new file mode 100644 index 00000000..60518cff --- /dev/null +++ b/luprex/cpp/luaconsole.cpp @@ -0,0 +1,80 @@ + +#include +#include "luaconsole.hpp" + +LuaConsole::LuaConsole() { + lua_state_ = lua_open(); + clear(); +} + +LuaConsole::~LuaConsole() { + lua_close(lua_state_); +} + +void LuaConsole::clear() { + expression_=""; + lines_ = 0; + syntax_=""; + state_=STATE_INCOMPLETE; +} + +void LuaConsole::add(std::string line) { + for (int i = 0; i < int(line.size()); i++) { + if (line[i] == '\n') line[i] = ' '; + } + expression_ += line; + expression_ += '\n'; + lines_ += 1; + + // Convert leading = to 'return' + if (expression_[0] == '=') { + expression_ = std::string("return ") + expression_.substr(1); + } + + // Analyze slash-commands. + if (expression_[0] == '/') { + if (lines_ == 1) { + state_ = STATE_SLASHCOMMAND; + syntax_ = ""; + } else { + state_ = STATE_SYNTAX; + syntax_ = "slash command has more than one line"; + } + return; + } + + // Analyze lua expressions. + int top = lua_gettop(lua_state_); + int status = luaL_loadbuffer(lua_state_, expression_.c_str(), expression_.size(), "=stdin"); + if (status == LUA_ERRSYNTAX) + { + const char *eof = "''"; + size_t lmsg; + const char *msg = lua_tolstring(lua_state_, -1, &lmsg); + const char *tp = msg + lmsg - (sizeof(eof) - 1); + if (strstr(msg, eof) == tp) { + syntax_ = ""; + state_ = STATE_INCOMPLETE; + } else { + syntax_ = msg; + state_ = STATE_SYNTAX; + } + } else { + state_ = STATE_COMPLETE; + syntax_ = ""; + } + lua_settop(lua_state_, top); +} + +void LuaConsole::add_stdin() { + const int MAXINPUT = 1000; + char buf[MAXINPUT]; + fputs(expression_.empty() ? "> " : ">> ", stdout); + fflush(stdout); + if (fgets(buf, MAXINPUT, stdin)) { + size_t len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + add(buf); + } +} diff --git a/luprex/cpp/luaconsole.hpp b/luprex/cpp/luaconsole.hpp new file mode 100644 index 00000000..9b6fa208 --- /dev/null +++ b/luprex/cpp/luaconsole.hpp @@ -0,0 +1,45 @@ +////////////////////////////////////////////////////// +// +// LuaConsole: +// +// Parses lines of text, and tells you if you have a valid +// lua expression. +// +////////////////////////////////////////////////////// + +#ifndef LUACONSOLE_HPP +#define LUACONSOLE_HPP + +#include +#include "luastack.hpp" + +class LuaConsole { +public: + enum { + STATE_INCOMPLETE, // We need more input text. + STATE_COMPLETE, // We have a valid lua expression. + STATE_SLASHCOMMAND, // We have a slash-command. + STATE_SYNTAX, // We have a syntax error. + }; +private: + lua_State *lua_state_; + int state_; + int lines_; + std::string expression_; + std::string syntax_; + +public: + LuaConsole(); + ~LuaConsole(); + + int state() const { return state_; } + int lines() const { return lines_; } + const std::string &expression() const { return expression_; } + const std::string &syntax() const { return syntax_; } + + void add(std::string line); + void add_stdin(); + void clear(); +}; + +#endif //LUACONSOLE_HPP diff --git a/luprex/syscpp/luastack.cpp b/luprex/cpp/luastack.cpp similarity index 100% rename from luprex/syscpp/luastack.cpp rename to luprex/cpp/luastack.cpp diff --git a/luprex/syscpp/luastack.hpp b/luprex/cpp/luastack.hpp similarity index 100% rename from luprex/syscpp/luastack.hpp rename to luprex/cpp/luastack.hpp diff --git a/luprex/syscpp/main.cpp b/luprex/cpp/main.cpp similarity index 100% rename from luprex/syscpp/main.cpp rename to luprex/cpp/main.cpp diff --git a/luprex/syscpp/planemap.cpp b/luprex/cpp/planemap.cpp similarity index 100% rename from luprex/syscpp/planemap.cpp rename to luprex/cpp/planemap.cpp diff --git a/luprex/syscpp/planemap.hpp b/luprex/cpp/planemap.hpp similarity index 100% rename from luprex/syscpp/planemap.hpp rename to luprex/cpp/planemap.hpp diff --git a/luprex/syscpp/source.cpp b/luprex/cpp/source.cpp similarity index 98% rename from luprex/syscpp/source.cpp rename to luprex/cpp/source.cpp index 1673e635..032be5fb 100644 --- a/luprex/syscpp/source.cpp +++ b/luprex/cpp/source.cpp @@ -127,11 +127,11 @@ static int source_updatefile(lua_State *L) { old_fingerprint = LS.ckstring(fingerprint); } // std::cerr << "Probing " << cfn << std::endl; - std::string new_fingerprint = util::get_file_fingerprint("syslua/" + cfn); + std::string new_fingerprint = util::get_file_fingerprint("lua/" + cfn); LS.set(null, LuaNil); if ((old_fingerprint == "") || (old_fingerprint != new_fingerprint)) { std::cerr << "Rereading " << cfn << std::endl; - std::string ccode = util::get_file_contents("syslua/" + cfn); + std::string ccode = util::get_file_contents("lua/" + cfn); LS.setfield(info, "name", fn); LS.setfield(info, "fingerprint", new_fingerprint); LS.setfield(info, "code", ccode); @@ -158,7 +158,7 @@ void SourceDB::update() { } // Read the list of filenames. - std::string ctrl = "syslua/control.lst"; + std::string ctrl = "lua/control.lst"; util::stringvec filenames = util::trim_and_uncomment(util::read_lines(ctrl)); if (filenames.empty()) { luaL_error(L, "cannot read source database control.lst"); diff --git a/luprex/syscpp/source.hpp b/luprex/cpp/source.hpp similarity index 100% rename from luprex/syscpp/source.hpp rename to luprex/cpp/source.hpp diff --git a/luprex/syscpp/table.cpp b/luprex/cpp/table.cpp similarity index 100% rename from luprex/syscpp/table.cpp rename to luprex/cpp/table.cpp diff --git a/luprex/syscpp/table.hpp b/luprex/cpp/table.hpp similarity index 100% rename from luprex/syscpp/table.hpp rename to luprex/cpp/table.hpp diff --git a/luprex/cpp/textgame.cpp b/luprex/cpp/textgame.cpp new file mode 100644 index 00000000..d6550f7a --- /dev/null +++ b/luprex/cpp/textgame.cpp @@ -0,0 +1,119 @@ + +#include +#include +#include +#include +#include +#include +#include +#include "luastack.hpp" +#include "util.hpp" +#include "viewer.hpp" +#include "traceback.hpp" +#include "textgame.hpp" +#include "luaconsole.hpp" + +// Add another error status. + +static lua_State *globalL = NULL; + + +static void lstop(lua_State *L, lua_Debug *ar) +{ + (void)ar; /* unused arg. */ + lua_sethook(L, NULL, 0, 0); + /* Avoid luaL_error -- a C hook doesn't add an extra frame. */ + luaL_where(L, 0); + lua_pushfstring(L, "%sinterrupted!", lua_tostring(L, -1)); + lua_error(L); +} + +static void laction(int i) +{ + signal(i, SIG_DFL); /* if another SIGINT happens before lstop, + terminate process (default action) */ + lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); +} + +static void l_message(const char *msg) +{ + fputs(msg, stderr); + fputc('\n', stderr); + fflush(stderr); +} + +static int report(lua_State *L, int status) +{ + if (status && !lua_isnil(L, -1)) + { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) + msg = "(error object is not a string)"; + l_message(msg); + lua_pop(L, 1); + } + return status; +} + +static int docall(lua_State *L, int narg, int nret) +{ + signal(SIGINT, laction); + int status = traceback_pcall(L, narg, nret); + signal(SIGINT, SIG_DFL); + /* force a complete garbage collection in case of errors */ + if (status != LUA_OK) + lua_gc(L, LUA_GCCOLLECT, 0); + return status; +} + +static void dotty(lua_State *L) +{ + LuaConsole console; + int status; + + while (true) { + switch (console.state()) { + case LuaConsole::STATE_INCOMPLETE: + console.add_stdin(); + break; + case LuaConsole::STATE_COMPLETE: { + const std::string &exp = console.expression(); + status = luaL_loadbuffer(L, exp.c_str(), exp.size(), "=stdin"); + assert(status == LUA_OK); + status = docall(L, 0, LUA_MULTRET); + report(L, status); + if (status == LUA_OK && lua_gettop(L) > 0) { + // any result to print? + lua_getglobal(L, "pprint"); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_getglobal(L, "print"); + } + lua_insert(L, 1); + if (lua_pcall(L, lua_gettop(L) - 1, 0, 0) != 0) + { + l_message( + lua_pushfstring(L, "error calling 'print' (%s)", + lua_tostring(L, -1))); + } + } + console.clear(); + break; + } + case LuaConsole::STATE_SLASHCOMMAND: + std::cerr << "Slash commands not supported yet." << std::endl; + console.clear(); + break; + case LuaConsole::STATE_SYNTAX: + std::cerr << console.syntax() << std::endl; + console.clear(); + break; + } + } +} + +void TextGame::run() +{ + dotty(viewer_.get_lua_state()); +} + diff --git a/luprex/syscpp/textgame.hpp b/luprex/cpp/textgame.hpp similarity index 100% rename from luprex/syscpp/textgame.hpp rename to luprex/cpp/textgame.hpp diff --git a/luprex/syscpp/traceback.cpp b/luprex/cpp/traceback.cpp similarity index 100% rename from luprex/syscpp/traceback.cpp rename to luprex/cpp/traceback.cpp diff --git a/luprex/syscpp/traceback.hpp b/luprex/cpp/traceback.hpp similarity index 100% rename from luprex/syscpp/traceback.hpp rename to luprex/cpp/traceback.hpp diff --git a/luprex/syscpp/util.cpp b/luprex/cpp/util.cpp similarity index 100% rename from luprex/syscpp/util.cpp rename to luprex/cpp/util.cpp diff --git a/luprex/syscpp/util.hpp b/luprex/cpp/util.hpp similarity index 100% rename from luprex/syscpp/util.hpp rename to luprex/cpp/util.hpp diff --git a/luprex/syscpp/viewer.cpp b/luprex/cpp/viewer.cpp similarity index 100% rename from luprex/syscpp/viewer.cpp rename to luprex/cpp/viewer.cpp diff --git a/luprex/syscpp/viewer.hpp b/luprex/cpp/viewer.hpp similarity index 100% rename from luprex/syscpp/viewer.hpp rename to luprex/cpp/viewer.hpp diff --git a/luprex/syscpp/world.cpp b/luprex/cpp/world.cpp similarity index 100% rename from luprex/syscpp/world.cpp rename to luprex/cpp/world.cpp diff --git a/luprex/syscpp/world.hpp b/luprex/cpp/world.hpp similarity index 100% rename from luprex/syscpp/world.hpp rename to luprex/cpp/world.hpp diff --git a/luprex/syslua/control.lst b/luprex/lua/control.lst similarity index 100% rename from luprex/syslua/control.lst rename to luprex/lua/control.lst diff --git a/luprex/syslua/inspect.lua b/luprex/lua/inspect.lua similarity index 100% rename from luprex/syslua/inspect.lua rename to luprex/lua/inspect.lua diff --git a/luprex/syslua/ut-globaldb.lua b/luprex/lua/ut-globaldb.lua similarity index 100% rename from luprex/syslua/ut-globaldb.lua rename to luprex/lua/ut-globaldb.lua diff --git a/luprex/syslua/ut-table.lua b/luprex/lua/ut-table.lua similarity index 100% rename from luprex/syslua/ut-table.lua rename to luprex/lua/ut-table.lua diff --git a/luprex/syscpp/textgame.cpp b/luprex/syscpp/textgame.cpp deleted file mode 100644 index 29823057..00000000 --- a/luprex/syscpp/textgame.cpp +++ /dev/null @@ -1,172 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include "luastack.hpp" -#include "util.hpp" -#include "viewer.hpp" -#include "traceback.hpp" -#include "textgame.hpp" - -// Add another error status. - -static lua_State *globalL = NULL; - - -static void lstop(lua_State *L, lua_Debug *ar) -{ - (void)ar; /* unused arg. */ - lua_sethook(L, NULL, 0, 0); - /* Avoid luaL_error -- a C hook doesn't add an extra frame. */ - luaL_where(L, 0); - lua_pushfstring(L, "%sinterrupted!", lua_tostring(L, -1)); - lua_error(L); -} - -static void laction(int i) -{ - signal(i, SIG_DFL); /* if another SIGINT happens before lstop, - terminate process (default action) */ - lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); -} - -static void l_message(const char *msg) -{ - fputs(msg, stderr); - fputc('\n', stderr); - fflush(stderr); -} - -static int report(lua_State *L, int status) -{ - if (status && !lua_isnil(L, -1)) - { - const char *msg = lua_tostring(L, -1); - if (msg == NULL) - msg = "(error object is not a string)"; - l_message(msg); - lua_pop(L, 1); - } - return status; -} - -static int docall(lua_State *L, int narg, int nret) -{ - signal(SIGINT, laction); - int status = traceback_pcall(L, narg, nret); - signal(SIGINT, SIG_DFL); - /* force a complete garbage collection in case of errors */ - if (status != LUA_OK) - lua_gc(L, LUA_GCCOLLECT, 0); - return status; -} - -// Print a prompt, read a line of text, and push it on the stack. -// If it's the first line, translate "=" to "return" -// -static int pushline(lua_State *L, int firstline) -{ - const int MAXINPUT = 1000; - char buf[MAXINPUT]; - fputs(firstline ? "> " : ">> ", stdout); - fflush(stdout); - if (fgets(buf, MAXINPUT, stdin)) - { - size_t len = strlen(buf); - if (len > 0 && buf[len - 1] == '\n') - buf[len - 1] = '\0'; - if (firstline && buf[0] == '=') - lua_pushfstring(L, "return %s", buf + 1); - else - lua_pushstring(L, buf); - return 1; - } - return 0; -} - -// Same as lua_loadbuffer, except: if the form passed in -// is a partial form, returns LUA_ERRPARTIAL. -// -static int loadbuffer_partial(lua_State *L, const char *s, int len, const char *fn) -{ - const char *eof = "''"; - int status = luaL_loadbuffer(L, s, len, fn); - if (status != LUA_ERRSYNTAX) - { - return status; - } - size_t lmsg; - const char *msg = lua_tolstring(L, -1, &lmsg); - const char *tp = msg + lmsg - (sizeof(eof) - 1); - if (strstr(msg, eof) == tp) - { - return LUA_ERRPARTIAL; - } - else - { - return status; - } -} - -// Read an entire form. On success, returns a closure. On EOF, -// returns just the LUA_ERREOF status code. On failure, returns an -// error message and a status code. -static int read_and_load(lua_State *L) -{ - int status; - lua_settop(L, 0); - if (!pushline(L, 1)) - return LUA_ERREOF; /* no input */ - for (;;) - { /* repeat until gets a complete line */ - status = loadbuffer_partial(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); - if (status != LUA_ERRPARTIAL) - break; - lua_pop(L, 1); /* pop the error message */ - if (!pushline(L, 0)) /* no more input? */ - return LUA_ERREOF; - lua_pushliteral(L, "\n"); /* add a new line... */ - lua_insert(L, -2); /* ...between the two lines */ - lua_concat(L, 3); /* join them */ - } - lua_remove(L, 1); /* remove line */ - return status; -} - -static void dotty(lua_State *L) -{ - while (1) - { - int status = read_and_load(L); - if (status == LUA_ERREOF) break; - if (status == LUA_OK) status = docall(L, 0, LUA_MULTRET); - report(L, status); - if (status == LUA_OK && lua_gettop(L) > 0) - { /* any result to print? */ - lua_getglobal(L, "pprint"); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); - lua_getglobal(L, "print"); - } - lua_insert(L, 1); - if (lua_pcall(L, lua_gettop(L) - 1, 0, 0) != 0) - { - l_message( - lua_pushfstring(L, "error calling 'print' (%s)", - lua_tostring(L, -1))); - } - } - } - lua_settop(L, 0); /* clear stack */ - fputs("\n", stdout); - fflush(stdout); -} - -void TextGame::run() -{ - dotty(viewer_.get_lua_state()); -} -