Overhaul of command parsing

This commit is contained in:
2021-11-16 12:20:11 -05:00
parent f933f451ad
commit 077b2fc23b
11 changed files with 138 additions and 158 deletions

View File

@@ -35,6 +35,12 @@ bool Gui::has_action(const std::string &action) const {
return false; return false;
} }
std::string Gui::get_action(int index) {
if ((index < 0) || (index >= elts_.size())) {
return "";
}
return elts_[index].action();
}
LuaDefine(gui_menu_item, "c") { LuaDefine(gui_menu_item, "c") {
Gui *gui = Gui::fetch_global_pointer(L); Gui *gui = Gui::fetch_global_pointer(L);

View File

@@ -36,6 +36,7 @@ public:
void clear() { elts_.clear(); } void clear() { elts_.clear(); }
bool has_action(const std::string &action) const; bool has_action(const std::string &action) const;
void menu_item(const std::string &action, const std::string &label); void menu_item(const std::string &action, const std::string &label);
std::string get_action(int index);
// Put a pointer to a gui into the lua registry. // Put a pointer to a gui into the lua registry.
// //

View File

@@ -36,6 +36,9 @@ Invocation::Invocation() : kind_(KIND_INVALID), actor_(0), place_(0) {}
Invocation::Invocation(Kind kind, int64_t actor, int64_t place, const std::string &action, const InvocationData &data) Invocation::Invocation(Kind kind, int64_t actor, int64_t place, const std::string &action, const InvocationData &data)
: kind_(kind), actor_(actor), place_(place), action_(action), data_(data) {} : kind_(kind), actor_(actor), place_(place), action_(action), data_(data) {}
Invocation::Invocation(Kind kind, int64_t actor, int64_t place, const std::string &action)
: kind_(kind), actor_(actor), place_(place), action_(action) {}
void Invocation::serialize(StreamBuffer *sb) const { void Invocation::serialize(StreamBuffer *sb) const {
sb->write_uint8(kind_); sb->write_uint8(kind_);
sb->write_int64(actor_); sb->write_int64(actor_);

View File

@@ -33,7 +33,9 @@ private:
public: public:
Invocation(); Invocation();
Invocation(Kind kind, int64_t actor, int64_t place, const std::string &action, const InvocationData &data); Invocation(Kind kind, int64_t actor, int64_t place, const std::string &action, const InvocationData &data);
Invocation(Kind kind, int64_t actor, int64_t place, const std::string &action);
bool valid() const { return kind_ != KIND_INVALID; }
Kind kind() const { return kind_; } Kind kind() const { return kind_; }
int64_t actor() const { return actor_; } int64_t actor() const { return actor_; }
int64_t place() const { return place_; } int64_t place() const { return place_; }

View File

@@ -96,29 +96,14 @@ public:
} }
void do_lua_command(const StringVec &words) { void do_lua_command(const StringVec &words) {
if (words.size() != 2) { send_invocation(Invocation(Invocation::KIND_LUA, actor_id_, actor_id_, words[1]));
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);
send_invocation(inv);
} }
void do_syntax_command(const StringVec &words) { void do_syntax_command(const StringVec &words) {
stdostream() << "Syntax Error: "; stdostream() << "Syntax Error: " << words[1] << std::endl;
for (int i = 1; i < int(words.size()); i++) {
stdostream() << words[i] << " ";
}
stdostream() << std::endl;
} }
void do_view_command(const StringVec &cmd) { void do_view_command(const StringVec &cmd) {
if (cmd.size() != 1) {
stdostream() << "view command takes no arguments" << std::endl;
return;
}
for (int64_t id : world_->get_near(actor_id_, 100, true)) { for (int64_t id : world_->get_near(actor_id_, 100, true)) {
const Tangible *tan = world_->tangible_get(id); const Tangible *tan = world_->tangible_get(id);
const AnimStep &aqback = tan->anim_queue_.back(); const AnimStep &aqback = tan->anim_queue_.back();
@@ -127,18 +112,9 @@ public:
} }
void do_menu_command(const StringVec &cmd) { void do_menu_command(const StringVec &cmd) {
int64_t id;
if (cmd.size() == 1) {
id = actor_id_;
} else if (cmd.size() == 2) {
id = util::strtoint(cmd[1], -1);
} else {
stdostream() << "menu command expects a tangible ID or defaults to actor_id" << std::endl;
return;
}
world_to_asynchronous(); world_to_asynchronous();
gui_place_ = id; gui_place_ = util::strtoint(cmd[1], actor_id_);
world_->update_gui(actor_id_, id, &gui_); world_->update_gui(actor_id_, gui_place_, &gui_);
int index = 0; int index = 0;
for (const GuiElt &elt : gui_.elts()) { for (const GuiElt &elt : gui_.elts()) {
stdostream() << index << " " << elt.label() << std::endl; stdostream() << index << " " << elt.label() << std::endl;
@@ -147,32 +123,17 @@ public:
} }
void do_choose_command(const StringVec &cmd) { void do_choose_command(const StringVec &cmd) {
int64_t index; std::string action = gui_.get_action(util::strtoint(cmd[1], -1));
if (cmd.size() == 1) { if (action == "") {
index = util::strtoint(cmd[0], -1); stdostream() << "Invalid menu item #" << std::endl;
} else {
stdostream() << "choose command consists of a single menu line number" << std::endl;
return; return;
} }
const Gui::EltVec &elts = gui_.elts();
if ((index < 0) || (index >= int(elts.size()))) {
stdostream() << "No menu item #" << index << std::endl;
return;
}
std::string action = elts[index].action();
stdostream() << "Invoking plan: " << action << std::endl; stdostream() << "Invoking plan: " << action << std::endl;
InvocationData dummyresult; Invocation inv(Invocation::KIND_PLAN, actor_id_, gui_place_, action);
dummyresult["flavor"] = "chocolate";
dummyresult["color"] = "blue";
Invocation inv(Invocation::KIND_PLAN, actor_id_, gui_place_, action, dummyresult);
send_invocation(inv); send_invocation(inv);
} }
void do_quit_command(const util::StringVec &words) { void do_quit_command(const util::StringVec &words) {
if (words.size() != 1) {
stdostream() << "quit command takes no arguments" << std::endl;
return;
}
abandon_server(); abandon_server();
stop_driver(); stop_driver();
} }
@@ -184,9 +145,9 @@ public:
else if (words[0] == "view") do_view_command(words); else if (words[0] == "view") do_view_command(words);
else if (words[0] == "menu") do_menu_command(words); else if (words[0] == "menu") do_menu_command(words);
else if (words[0] == "quit") do_quit_command(words); else if (words[0] == "quit") do_quit_command(words);
else if (util::validinteger(words[0])) do_choose_command(words); else if (words[0] == "choose") do_choose_command(words);
else { else {
stdostream() << "Unknown command: " << words[0] << std::endl; stdostream() << "Unsupported command: " << words[0] << std::endl;
} }
} }

View File

@@ -43,29 +43,14 @@ public:
} }
void do_lua_command(const util::StringVec &words) { void do_lua_command(const util::StringVec &words) {
if (words.size() != 2) { master_->invoke(Invocation(Invocation::KIND_LUA, admin_id_, admin_id_, words[1]));
stdostream() << "lua command (lua) takes a single string" << std::endl;
return;
}
const std::string &exp = words[1];
InvocationData dummyresult;
Invocation inv(Invocation::KIND_LUA, admin_id_, admin_id_, exp, dummyresult);
master_->invoke(inv);
} }
void do_syntax_command(const util::StringVec &words) { void do_syntax_command(const util::StringVec &words) {
stdostream() << "Syntax Error: "; stdostream() << "Syntax Error: " << words[1] << std::endl;
for (int i = 1; i < int(words.size()); i++) {
stdostream() << words[i] << " ";
}
stdostream() << std::endl;
} }
void do_quit_command(const util::StringVec &words) { void do_quit_command(const util::StringVec &words) {
if (words.size() != 1) {
stdostream() << "quit command takes no arguments" << std::endl;
return;
}
stop_driver(); stop_driver();
} }
@@ -75,7 +60,7 @@ public:
else if (words[0] == "syntax") do_syntax_command(words); else if (words[0] == "syntax") do_syntax_command(words);
else if (words[0] == "quit") do_quit_command(words); else if (words[0] == "quit") do_quit_command(words);
else { else {
stdostream() << "Unknown command: " << words[0] << std::endl; stdostream() << "Unsupported command: " << words[0] << std::endl;
} }
} }

View File

@@ -38,6 +38,65 @@ LuaConsole::StringVec LuaConsole::get_command() {
return result; return result;
} }
void LuaConsole::synerr(const std::string &msg) {
words_.clear();
words_.push_back("syntax");
words_.push_back(msg);
}
void LuaConsole::simplify(const StringVec &words) {
words_ = words;
if (words.size() == 0) {
return;
} else if (util::validinteger(words[0])) {
if (words.size() == 1) {
words_.clear();
words_.push_back("choose");
words_.push_back(words[0]);
} else {
synerr("/choose command takes no arguments");
}
} else if (words[0] == "choose") {
if ((words.size() == 2)&&(util::validinteger(words[1]))) {
// OK
} else {
synerr("/choose [menu-line-number]");
}
} else if (words[0] == "view") {
if (words.size() != 1) {
synerr("/view takes no arguments");
}
} else if (words[0] == "menu") {
if (words.size() == 1) {
words_.push_back("-");
} else if ((words.size() == 2)&&(util::validinteger(words[1]))) {
// OK
} else {
synerr("/menu [optional-tangible-id]");
}
} else if (words[0] == "quit") {
if (words.size() != 1) {
synerr("/quit takes no arguments");
}
} else if (words[0] == "snap") {
if (words.size() != 1) {
synerr("/snap takes no arguments");
}
} else if (words[0] == "roll") {
if (words.size() != 1) {
synerr("/roll takes no arguments");
}
} else if (words[0] == "tick") {
if ((words.size() == 2)&&(util::validinteger(words[1]))) {
// OK
} else {
synerr("/tick [optional-timestamp]");
}
} else {
synerr("unrecognized command");
}
}
void LuaConsole::clear_raw_input() { void LuaConsole::clear_raw_input() {
raw_input_ = ""; raw_input_ = "";
lines_ = 0; lines_ = 0;
@@ -52,10 +111,11 @@ void LuaConsole::add(std::string line) {
raw_input_ += '\n'; raw_input_ += '\n';
lines_ += 1; lines_ += 1;
prompt_ = ">> "; prompt_ = ">> ";
words_.clear();
// Try to interpret it as a slash-command. // Try to interpret it as a slash-command.
if ((lines_ == 1)&&(raw_input_[0] == '/')) { if ((lines_ == 1)&&(raw_input_[0] == '/')) {
words_ = split_words(raw_input_.substr(1)); simplify(split_words(raw_input_.substr(1)));
clear_raw_input(); clear_raw_input();
return; return;
} }

View File

@@ -8,23 +8,28 @@
// 1. Print the prompt suggested by 'get_prompt'. // 1. Print the prompt suggested by 'get_prompt'.
// 2. Read a line of text from stdin. // 2. Read a line of text from stdin.
// 3. Add the line to the LuaConsole using 'add'. // 3. Add the line to the LuaConsole using 'add'.
// 4. Get the command words using 'get_command'. // 4. Get the command word using get_command.
// 5. If the command is empty, do nothing. // 5. If the command is empty, do nothing.
// 6. If the command is nonempty, execute it. // 6. If the command is nonempty, get the args and execute it.
// //
// The LuaConsole expects you to type one of three things: // The LuaConsole expects you to type a lua command, or one
// of the following slash-commands:
// //
// * Valid Lua Code. // /quit - exit the program
// * Valid Lua Code preceded by '=' as shorthand for 'return' // /view - display the nearby tangibles
// * A slash-command. // /menu [tanid] - display the menu for tangible
// /snap - snapshot current state
// /roll - rollback to previous state
// /tick [timevalue] - advance the simulation clock
// /1234 - choose menu item 1234
// //
// The 'get_command' returned by the luaconsole can be one of // If you type anything else, the LuaConsole will generate a
// the following: // syntax error. Note that not all of the commands above are
// supported in all interpreters.
// //
// empty vector - still collecting input: do nothing. // Once a command has been typed (or a syntax error has been
// lua <expr> - found a lua command: execute it as lua. // typed), the LuaConsole will return the command. The command
// syntax <words> - detected a syntax error: print the error. // can be fetched using command(), int_arg(), and str_arg()
// word word ... - read a slash-command: execute the command.
// //
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
@@ -42,10 +47,12 @@ private:
lua_State *lua_state_; lua_State *lua_state_;
std::string raw_input_; std::string raw_input_;
int lines_; int lines_;
StringVec words_;
std::string prompt_; std::string prompt_;
StringVec words_;
void clear_raw_input(); void clear_raw_input();
void simplify(const StringVec &words);
void synerr(const std::string &msg);
public: public:
LuaConsole(); LuaConsole();
@@ -57,15 +64,14 @@ public:
// //
const std::string &get_prompt() { return prompt_; } const std::string &get_prompt() { return prompt_; }
// Fetch the command to execute. // Get the command words.
//
// Note that the command words may not be exactly what
// the user typed. Typically, the LuaConsole simplifies
// the command before returning it to the caller.
// //
// You should fetch the command after calling 'add'.
// 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.
//
StringVec get_command(); StringVec get_command();
// Add a line of text that was just read from the console. // Add a line of text that was just read from the console.
// //
void add(std::string line); void add(std::string line);

View File

@@ -29,32 +29,15 @@ private:
int64_t gui_place_; int64_t gui_place_;
int64_t actor_id_; int64_t actor_id_;
void do_lua_command(const StringVec &words) { void do_lua_command(const StringVec &words) {
assert(world_->stack_is_clear()); world_->invoke(Invocation(Invocation::KIND_LUA, actor_id_, actor_id_, words[1]));
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 do_syntax_command(const StringVec &words) { void do_syntax_command(const StringVec &words) {
stdostream() << "Syntax Error: "; stdostream() << "Syntax Error: " << words[1] << std::endl;
for (int i = 1; i < int(words.size()); i++) {
stdostream() << words[i] << " ";
}
stdostream() << std::endl;
} }
void do_view_command(const StringVec &cmd) { void do_view_command(const StringVec &cmd) {
if (cmd.size() != 1) {
stdostream() << "v command (view) takes no arguments" << std::endl;
return;
}
for (int64_t id : world_->get_near(actor_id_, 100, true)) { for (int64_t id : world_->get_near(actor_id_, 100, true)) {
const Tangible *tan = world_->tangible_get(id); const Tangible *tan = world_->tangible_get(id);
const AnimStep &aqback = tan->anim_queue_.back(); const AnimStep &aqback = tan->anim_queue_.back();
@@ -63,17 +46,8 @@ private:
} }
void do_menu_command(const StringVec &cmd) { void do_menu_command(const StringVec &cmd) {
int64_t id; gui_place_ = util::strtoint(cmd[1], actor_id_);
if (cmd.size() == 1) { world_->update_gui(actor_id_, gui_place_, &gui_);
id = actor_id_;
} else if (cmd.size() == 2) {
id = util::strtoint(cmd[1], -1);
} else {
stdostream() << "m command (menu) expects a tangible ID or defaults to actor_id" << std::endl;
return;
}
gui_place_ = id;
world_->update_gui(actor_id_, id, &gui_);
int index = 0; int index = 0;
for (const GuiElt &elt : gui_.elts()) { for (const GuiElt &elt : gui_.elts()) {
stdostream() << index << " " << elt.label() << std::endl; stdostream() << index << " " << elt.label() << std::endl;
@@ -82,60 +56,29 @@ private:
} }
void do_choose_command(const StringVec &cmd) { void do_choose_command(const StringVec &cmd) {
int64_t index; std::string action = gui_.get_action(util::strtoint(cmd[1], -1));
if (cmd.size() == 1) { if (action == "") {
index = util::strtoint(cmd[0], -1); stdostream() << "Invalid menu item #" << std::endl;
} else {
stdostream() << "c command (choose) expects a menu line number" << std::endl;
return; return;
} }
const Gui::EltVec &elts = gui_.elts(); Invocation inv(Invocation::KIND_PLAN, actor_id_, gui_place_, action);
if ((index < 0) || (index >= int(elts.size()))) { stdostream() << "Invoking: " << inv.debug_string() << std::endl;
stdostream() << "No menu item #" << index << std::endl;
return;
}
std::string action = elts[index].action();
stdostream() << "Invoking plan: " << action << std::endl;
InvocationData dummyresult;
dummyresult["flavor"] = "chocolate";
dummyresult["color"] = "blue";
Invocation inv(Invocation::KIND_PLAN, actor_id_, gui_place_, action, dummyresult);
world_->invoke(inv); world_->invoke(inv);
} }
void do_snapshot_command(const StringVec &cmd) { void do_snapshot_command(const StringVec &cmd) {
if (cmd.size() != 1) {
stdostream() << "s command (snapshot) takes no arguments" << std::endl;
return;
}
world_->snapshot(); world_->snapshot();
} }
void do_rollback_command(const StringVec &cmd) { void do_rollback_command(const StringVec &cmd) {
if (cmd.size() != 1) {
stdostream() << "r command (rollback) takes no arguments" << std::endl;
return;
}
world_->rollback(); world_->rollback();
} }
void do_tick_command(const StringVec &cmd) { void do_tick_command(const StringVec &cmd) {
int64_t clock; world_->run_scheduled_threads(util::strtoint(cmd[1], -1));
if (cmd.size() == 2) {
clock = util::strtoint(cmd[1], -1);
} else {
stdostream() << "t command (tick) expects a time value" << std::endl;
return;
}
world_->run_scheduled_threads(clock);
} }
void do_quit_command(const StringVec &cmd) { void do_quit_command(const StringVec &cmd) {
if (cmd.size() != 1) {
stdostream() << "q command (quit) takes no arguments" << std::endl;
return;
}
actor_id_ = 0; actor_id_ = 0;
} }
@@ -149,9 +92,9 @@ private:
else if (words[0] == "snap") do_snapshot_command(words); else if (words[0] == "snap") do_snapshot_command(words);
else if (words[0] == "roll") do_rollback_command(words); else if (words[0] == "roll") do_rollback_command(words);
else if (words[0] == "tick") do_tick_command(words); else if (words[0] == "tick") do_tick_command(words);
else if (util::validinteger(words[0])) do_choose_command(words); else if (words[0] == "choose") do_choose_command(words);
else { else {
stdostream() << "Unknown command: " << words[0] << std::endl; stdostream() << "Unsupported command: " << words[0] << std::endl;
} }
} }

View File

@@ -142,6 +142,16 @@ StringVec split(const std::string &s, char sep) {
return result; return result;
} }
std::string join(const StringVec &strs, const std::string &sep) {
if (strs.empty()) return "";
std::ostringstream oss;
oss << strs[0];
for (int i = 1; i < strs.size(); i++) {
oss << sep << strs[i];
}
return oss.str();
}
std::string repeat_string(const std::string &a, int n) { std::string repeat_string(const std::string &a, int n) {
int len = a.size(); int len = a.size();
std::string result(len * n, ' '); std::string result(len * n, ' ');

View File

@@ -63,6 +63,9 @@ std::string hash_to_hex(const HashValue &hash);
// Split a string into multiple strings // Split a string into multiple strings
StringVec split(const std::string &s, char sep); StringVec split(const std::string &s, char sep);
// Join multiple strings into one string
std::string join(const StringVec &strs, std::string sep);
// Return N repetitions of string A // Return N repetitions of string A
std::string repeat_string(const std::string &a, int n); std::string repeat_string(const std::string &a, int n);