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;
}
std::string Gui::get_action(int index) {
if ((index < 0) || (index >= elts_.size())) {
return "";
}
return elts_[index].action();
}
LuaDefine(gui_menu_item, "c") {
Gui *gui = Gui::fetch_global_pointer(L);

View File

@@ -36,6 +36,7 @@ public:
void clear() { elts_.clear(); }
bool has_action(const std::string &action) const;
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.
//

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)
: 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 {
sb->write_uint8(kind_);
sb->write_int64(actor_);

View File

@@ -33,7 +33,9 @@ private:
public:
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);
bool valid() const { return kind_ != KIND_INVALID; }
Kind kind() const { return kind_; }
int64_t actor() const { return actor_; }
int64_t place() const { return place_; }

View File

@@ -96,29 +96,14 @@ public:
}
void do_lua_command(const StringVec &words) {
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);
send_invocation(inv);
send_invocation(Invocation(Invocation::KIND_LUA, actor_id_, actor_id_, words[1]));
}
void do_syntax_command(const StringVec &words) {
stdostream() << "Syntax Error: ";
for (int i = 1; i < int(words.size()); i++) {
stdostream() << words[i] << " ";
}
stdostream() << std::endl;
stdostream() << "Syntax Error: " << words[1] << std::endl;
}
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)) {
const Tangible *tan = world_->tangible_get(id);
const AnimStep &aqback = tan->anim_queue_.back();
@@ -127,18 +112,9 @@ public:
}
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();
gui_place_ = id;
world_->update_gui(actor_id_, id, &gui_);
gui_place_ = util::strtoint(cmd[1], actor_id_);
world_->update_gui(actor_id_, gui_place_, &gui_);
int index = 0;
for (const GuiElt &elt : gui_.elts()) {
stdostream() << index << " " << elt.label() << std::endl;
@@ -147,32 +123,17 @@ public:
}
void do_choose_command(const StringVec &cmd) {
int64_t index;
if (cmd.size() == 1) {
index = util::strtoint(cmd[0], -1);
} else {
stdostream() << "choose command consists of a single menu line number" << std::endl;
std::string action = gui_.get_action(util::strtoint(cmd[1], -1));
if (action == "") {
stdostream() << "Invalid menu item #" << std::endl;
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;
InvocationData dummyresult;
dummyresult["flavor"] = "chocolate";
dummyresult["color"] = "blue";
Invocation inv(Invocation::KIND_PLAN, actor_id_, gui_place_, action, dummyresult);
Invocation inv(Invocation::KIND_PLAN, actor_id_, gui_place_, action);
send_invocation(inv);
}
void do_quit_command(const util::StringVec &words) {
if (words.size() != 1) {
stdostream() << "quit command takes no arguments" << std::endl;
return;
}
abandon_server();
stop_driver();
}
@@ -184,9 +145,9 @@ public:
else if (words[0] == "view") do_view_command(words);
else if (words[0] == "menu") do_menu_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 {
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) {
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, admin_id_, admin_id_, exp, dummyresult);
master_->invoke(inv);
master_->invoke(Invocation(Invocation::KIND_LUA, admin_id_, admin_id_, words[1]));
}
void do_syntax_command(const util::StringVec &words) {
stdostream() << "Syntax Error: ";
for (int i = 1; i < int(words.size()); i++) {
stdostream() << words[i] << " ";
}
stdostream() << std::endl;
stdostream() << "Syntax Error: " << words[1] << std::endl;
}
void do_quit_command(const util::StringVec &words) {
if (words.size() != 1) {
stdostream() << "quit command takes no arguments" << std::endl;
return;
}
stop_driver();
}
@@ -75,7 +60,7 @@ public:
else if (words[0] == "syntax") do_syntax_command(words);
else if (words[0] == "quit") do_quit_command(words);
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;
}
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() {
raw_input_ = "";
lines_ = 0;
@@ -52,10 +111,11 @@ void LuaConsole::add(std::string line) {
raw_input_ += '\n';
lines_ += 1;
prompt_ = ">> ";
words_.clear();
// Try to interpret it as a slash-command.
if ((lines_ == 1)&&(raw_input_[0] == '/')) {
words_ = split_words(raw_input_.substr(1));
simplify(split_words(raw_input_.substr(1)));
clear_raw_input();
return;
}

View File

@@ -8,23 +8,28 @@
// 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 'get_command'.
// 4. Get the command word using get_command.
// 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.
// * Valid Lua Code preceded by '=' as shorthand for 'return'
// * A slash-command.
// /quit - exit the program
// /view - display the nearby tangibles
// /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
// the following:
// If you type anything else, the LuaConsole will generate a
// syntax error. Note that not all of the commands above are
// supported in all interpreters.
//
// empty vector - still collecting input: do nothing.
// lua <expr> - found a lua command: execute it as lua.
// syntax <words> - detected a syntax error: print the error.
// word word ... - read a slash-command: execute the command.
// Once a command has been typed (or a syntax error has been
// typed), the LuaConsole will return the command. The command
// can be fetched using command(), int_arg(), and str_arg()
//
//////////////////////////////////////////////////////
@@ -42,10 +47,12 @@ private:
lua_State *lua_state_;
std::string raw_input_;
int lines_;
StringVec words_;
std::string prompt_;
StringVec words_;
void clear_raw_input();
void simplify(const StringVec &words);
void synerr(const std::string &msg);
public:
LuaConsole();
@@ -57,15 +64,14 @@ public:
//
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();
// Add a line of text that was just read from the console.
//
void add(std::string line);

View File

@@ -29,32 +29,15 @@ private:
int64_t gui_place_;
int64_t actor_id_;
void 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);
world_->invoke(Invocation(Invocation::KIND_LUA, actor_id_, actor_id_, words[1]));
}
void do_syntax_command(const StringVec &words) {
stdostream() << "Syntax Error: ";
for (int i = 1; i < int(words.size()); i++) {
stdostream() << words[i] << " ";
}
stdostream() << std::endl;
stdostream() << "Syntax Error: " << words[1] << std::endl;
}
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)) {
const Tangible *tan = world_->tangible_get(id);
const AnimStep &aqback = tan->anim_queue_.back();
@@ -63,17 +46,8 @@ private:
}
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() << "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_);
gui_place_ = util::strtoint(cmd[1], actor_id_);
world_->update_gui(actor_id_, gui_place_, &gui_);
int index = 0;
for (const GuiElt &elt : gui_.elts()) {
stdostream() << index << " " << elt.label() << std::endl;
@@ -82,60 +56,29 @@ private:
}
void do_choose_command(const StringVec &cmd) {
int64_t index;
if (cmd.size() == 1) {
index = util::strtoint(cmd[0], -1);
} else {
stdostream() << "c command (choose) expects a menu line number" << std::endl;
std::string action = gui_.get_action(util::strtoint(cmd[1], -1));
if (action == "") {
stdostream() << "Invalid menu item #" << std::endl;
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;
InvocationData dummyresult;
dummyresult["flavor"] = "chocolate";
dummyresult["color"] = "blue";
Invocation inv(Invocation::KIND_PLAN, actor_id_, gui_place_, action, dummyresult);
Invocation inv(Invocation::KIND_PLAN, actor_id_, gui_place_, action);
stdostream() << "Invoking: " << inv.debug_string() << std::endl;
world_->invoke(inv);
}
void do_snapshot_command(const StringVec &cmd) {
if (cmd.size() != 1) {
stdostream() << "s command (snapshot) takes no arguments" << std::endl;
return;
}
world_->snapshot();
}
void do_rollback_command(const StringVec &cmd) {
if (cmd.size() != 1) {
stdostream() << "r command (rollback) takes no arguments" << std::endl;
return;
}
world_->rollback();
}
void do_tick_command(const StringVec &cmd) {
int64_t clock;
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);
world_->run_scheduled_threads(util::strtoint(cmd[1], -1));
}
void do_quit_command(const StringVec &cmd) {
if (cmd.size() != 1) {
stdostream() << "q command (quit) takes no arguments" << std::endl;
return;
}
actor_id_ = 0;
}
@@ -149,9 +92,9 @@ private:
else if (words[0] == "snap") do_snapshot_command(words);
else if (words[0] == "roll") do_rollback_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 {
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;
}
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) {
int len = a.size();
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
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
std::string repeat_string(const std::string &a, int n);