From cf5a2d7462146b837ff27421b7caaf0fe6444828 Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Thu, 4 Nov 2021 11:20:29 -0400 Subject: [PATCH] Refactor luaconsole to make it easier to use --- luprex/core/cpp/lpxclient.cpp | 26 ++++------------ luprex/core/cpp/luaconsole.cpp | 21 +++++-------- luprex/core/cpp/luaconsole.hpp | 54 +++++++++++++++------------------- luprex/core/cpp/textgame.cpp | 44 +++++++++++++++------------ 4 files changed, 63 insertions(+), 82 deletions(-) diff --git a/luprex/core/cpp/lpxclient.cpp b/luprex/core/cpp/lpxclient.cpp index 8565cbc7..7a3f25e8 100644 --- a/luprex/core/cpp/lpxclient.cpp +++ b/luprex/core/cpp/lpxclient.cpp @@ -34,10 +34,6 @@ public: channel_ = new_outgoing_channel("localhost:8085"); } - void do_lua(const std::string &lua) { - stdostream() << "Lua: " << lua << std::endl; - } - void do_command(const util::StringVec &words) { stdostream() << "Command: "; for (const std::string &word : words) { @@ -46,27 +42,17 @@ public: stdostream() << std::endl; } - void console_process(const std::string &line) { - console_.add(line); - int action = console_.action(); - if (action == LuaConsole::DO_LUA) { - do_lua(console_.lua_expression()); - console_.clear(); - } else if (action == LuaConsole::DO_COMMAND) { - do_command(console_.words()); - console_.clear(); - } else if (action == LuaConsole::DO_SYNTAX) { - stdostream() << console_.syntax() << std::endl; - console_.clear(); - } - } - virtual void event_update() { // Check for keyboard input on stdin. while (true) { std::string line = get_stdio_channel()->in()->readline(); if (line == "") break; - console_process(line); + console_.add(line); + const util::StringVec &words = console_.words(); + if (!words.empty()) { + do_command(words); + console_.clear(); + } get_stdio_channel()->out()->write_bytes(console_.get_prompt()); } diff --git a/luprex/core/cpp/luaconsole.cpp b/luprex/core/cpp/luaconsole.cpp index 44b7f823..6214887e 100644 --- a/luprex/core/cpp/luaconsole.cpp +++ b/luprex/core/cpp/luaconsole.cpp @@ -20,13 +20,11 @@ LuaConsole::~LuaConsole() { void LuaConsole::clear() { raw_input_ = ""; lines_ = 0; - lua_expression_ = ""; words_.clear(); - syntax_ = ""; - action_ = DO_NOTHING; prompt_ = "> "; } + void LuaConsole::split_words() { words_.clear(); std::string acc; @@ -64,7 +62,7 @@ void LuaConsole::add(std::string line) { split_words(); if (words_.size() >= 1) { if (is_single_letter(words_[0]) || (util::validinteger(words_[0]))) { - action_ = DO_COMMAND; + // We have a valid parsed command. Return it. return; } } @@ -89,19 +87,16 @@ void LuaConsole::add(std::string line) { size_t lmsg; const char *msg = lua_tolstring(lua_state_, -1, &lmsg); const char *tp = msg + lmsg - leof; - if (strstr(msg, eof) == tp) { - action_ = DO_NOTHING; - } else { - action_ = DO_SYNTAX; - syntax_ = msg; + if (strstr(msg, eof) != tp) { + words_.push_back("syntax"); + words_.push_back(msg); } } else { - action_ = DO_LUA; - lua_expression_ = partial; + words_.push_back("lua"); + words_.push_back(partial); } lua_settop(lua_state_, top); - - if (action_ == DO_NOTHING) { + if (words_.empty()) { prompt_ = ">> "; } } diff --git a/luprex/core/cpp/luaconsole.hpp b/luprex/core/cpp/luaconsole.hpp index 7058fa90..b2fef0b6 100644 --- a/luprex/core/cpp/luaconsole.hpp +++ b/luprex/core/cpp/luaconsole.hpp @@ -3,22 +3,26 @@ // LuaConsole: // // Used to parse commands that are being interactively typed -// in by a user. +// in by a user. The common usage pattern is: // -// The command syntax is always a single character command, -// followed by arguments. Only two commands are hardwired: +// 1. Print the prompt suggested by 'get_prompt'. +// 2. Read a line of text from stdin. +// 3. Add the line to the LuaConsole using 'add'. +// 4. Get the command words using 'words'. +// 5. If the words are empty, do nothing. +// 6. If the words contain something, execute and call 'clear'. // -// l - evaluate lua expression -// r - evaluate lua expression with 'return' prepended +// The 'words' returned by the luaconsole can be empty, meaning +// that there's no command to execute. If the words are nonempty, +// then the first word is the command. There are two hardwired +// commands: // -// The console expects you to add lines of text one at a time. -// After adding a line, the console will recommend one of these -// actions: +// lua - execute a lua expression +// syntax - print a syntax error message // -// 1. DO_NOTHING: Add more lines of text. -// 2. DO_LUA: Evaluate a lua expression. -// 3. DO_OTHER: Execute a non-lua command. -// 4. DO_SYNTAX: Print a lua syntax error. +// If the first word is anything else, it means the user typed +// that word on the command line. It is up to you to interpret +// such commands. // ////////////////////////////////////////////////////// @@ -30,46 +34,34 @@ class LuaConsole { public: - enum { - DO_NOTHING, // We need more input text. - DO_COMMAND, // We have a valid slash command. - DO_LUA, // We have a valid lua expression. - DO_SYNTAX, // We have a syntax error. - }; using StringVec = std::vector; private: lua_State *lua_state_; - int action_; std::string raw_input_; int lines_; - std::string lua_expression_; StringVec words_; - std::string syntax_; std::string prompt_; void split_words(); + void clear_command(); public: LuaConsole(); ~LuaConsole(); - // Get the recommended action. - int action() const { return action_; } - // Fetch the stored prompt. Also clears the stored prompt. You should fetch // and print the prompt after 'add' or 'clear'. std::string get_prompt(); - // When action is DO_COMMAND, get the command words. + // Get the command words. + // + // Returns the empty vector if there is no command. + // If there is a command, the first word is the command word. + // See the file comment for certain built-in command words. + // const StringVec &words() const { return words_; } - // When action is DO_LUA, get the valid lua expression. - std::string lua_expression() const { return lua_expression_; } - - // When action is DO_SYNTAX, get the syntax error. - const std::string &syntax() const { return syntax_; } - // Add a line of text that was just read from the console. // If more input is needed, stores the ">> " prompt. void add(std::string line); diff --git a/luprex/core/cpp/textgame.cpp b/luprex/core/cpp/textgame.cpp index 670c2cc7..6600bb0f 100644 --- a/luprex/core/cpp/textgame.cpp +++ b/luprex/core/cpp/textgame.cpp @@ -28,6 +28,8 @@ private: int64_t actor_id_; int64_t printbuffer_line_; + void do_lua_command(const StringVec &cmd); + void do_syntax_command(const StringVec &cmd); void do_view_command(const StringVec &cmd); void do_menu_command(const StringVec &cmd); void do_choose_command(const StringVec &cmd); @@ -36,7 +38,6 @@ private: void do_rollback_command(const StringVec &cmd); void do_tick_command(const StringVec &cmd); - void do_lua(const std::string &exp); void do_command(const StringVec &exp); void check_redirects(); @@ -47,7 +48,25 @@ public: virtual void event_update(); }; +void TextGame::do_lua_command(const StringVec &words) { + assert(world_->stack_is_clear()); + if (words.size() != 2) { + stdostream() << "lua command (lua) takes a single string" << std::endl; + return; + } + const std::string &exp = words[1]; + InvocationData dummyresult; + Invocation inv(Invocation::KIND_LUA, actor_id_, actor_id_, exp, dummyresult); + world_->invoke(inv); +} +void TextGame::do_syntax_command(const StringVec &words) { + stdostream() << "Syntax Error: "; + for (int i = 1; i < int(words.size()); i++) { + stdostream() << words[i] << " "; + } + stdostream() << std::endl; +} void TextGame::do_view_command(const StringVec &cmd) { if (cmd.size() != 1) { @@ -102,12 +121,6 @@ void TextGame::do_choose_command(const StringVec &cmd) { world_->invoke(inv); } -void TextGame::do_lua(const std::string &exp) { - assert(world_->stack_is_clear()); - InvocationData dummyresult; - Invocation inv(Invocation::KIND_LUA, actor_id_, actor_id_, exp, dummyresult); - world_->invoke(inv); -} void TextGame::do_snapshot_command(const StringVec &cmd) { if (cmd.size() != 1) { @@ -146,7 +159,9 @@ void TextGame::do_quit_command(const StringVec &cmd) { } void TextGame::do_command(const StringVec &words) { - if (words[0] == "v") do_view_command(words); + if (words[0] == "lua") do_lua_command(words); + else if (words[0] == "syntax") do_syntax_command(words); + else if (words[0] == "v") do_view_command(words); else if (words[0] == "m") do_menu_command(words); else if (words[0] == "q") do_quit_command(words); else if (words[0] == "s") do_snapshot_command(words); @@ -205,18 +220,11 @@ void TextGame::event_update() std::string line = get_stdio_channel()->in()->readline(); if (line == "") break; console_.add(line); - int action = console_.action(); - if (action == LuaConsole::DO_LUA) { - do_lua(console_.lua_expression()); + const StringVec &words = console_.words(); + if (!words.empty()) { + do_command(words); console_.clear(); channel_printbuffer(); - } else if (action == LuaConsole::DO_COMMAND) { - do_command(console_.words()); - console_.clear(); - channel_printbuffer(); - } else if (action == LuaConsole::DO_SYNTAX) { - stdostream() << console_.syntax() << std::endl; - console_.clear(); } check_redirects(); if (actor_id_ == 0) {