//////////////////////////////////////////////////////////////////////////////////// // // A slash-command parser. The intended use is as follows: // // SlashCommandParser parser(Command); // // if parser.Parse("/command1", "syntax1") { ... handle command1 ... } // else if parser.Parse("/command2", "syntax2") { ... handle command2 ... } // else if parser.Parse("/command3", "syntax3") { ... handle command3 ... } // else { ... print an error message ... } // //////////////////////////////////////////////////////////////////////////////////// #pragma once #include #include #include #include #include class SlashCommandParser { public: struct Value { std::string s_; int64_t i_; double d_; Value(const std::string &s) : s_(s), i_(0), d_(0) {} Value(int64_t i) : s_(), i_(i), d_(0) {} Value(double d) : s_(), i_(0), d_(d) {} }; std::vector args_; private: int cursor_; std::string command_; std::string line_; int linelen_; std::string error_; static bool ascii_isupper(char c) { return (c >= 'A') && (c <= 'Z'); } static bool ascii_islower(char c) { return (c >= 'a') && (c <= 'z'); } static bool ascii_isdigit(char c) { return (c >= '0') && (c <= '9'); } static bool ascii_isalpha(char c) { return ascii_isupper(c) || ascii_islower(c); } static bool ascii_isalnum(char c) { return ascii_isalpha(c) || ascii_isdigit(c); } static bool ascii_isspace(char c) { return (c==0)||(c==' ')||(c=='\t')||(c=='\r')||(c=='\n')||(c=='\f')||(c=='\v'); } void skip_white(); std::string read_word(); void not_enough_args(const std::string &cmd); public: // Initialize the slash-command parser with the full command line. // // The command line is stored. The first keyword (the slash-command // itself) is extracted and stored. If the command-line doesn't // start with a slash-command, an error message is stored. // // After initializing the parser, you should call Parse once // for every known command. // SlashCommandParser(std::string_view Line); // Parse: Try to parse a slash-command. // // The intent is that you should call 'Parse' once for every // known command. Hopefully, one of these 'Parse' commands // will return true. If not, then the command couldn't be parsed. // // You must pass in the name of a slash-command (eg, "/compile") // followed by the argument types that the command accepts. // // The argument types are represented as a string containing the // characters i (for int64), s (for string), and d (for double). // For example, if ArgTypes is "sid", then the command expects a // string, then an int64, then a double. // // If the command matches, and the arguments parse correctly, // returns true, stores the parsed arguments, and clears the // stored error message. // // If the command matches, but the syntax is wrong, returns false // and stores the error message. // // If the command doesn't match, returns false. // bool Parse(std::string_view Command, std::string_view ArgTypes); // Error: Declare that parsing has failed, and get an error message. // // You call this if you tried calling Parse on every known command, // and none of the Parse calls returned true. Returns an error // message indicating what went wrong. // std::string Error() const; // Get the Nth argument. // // After Parse returns true, the parsed arguments are stored in the // SlashCommandParser. You can use these functions to fetch the // parsed arguments. // // For example, if you call Parse("/hello", "sid") and it returns // true, you should then call StringArg(0), then IntArg(1), then // DoubleArg(2) to get all the arguments. // std::string StringArg(int n) const; int64_t IntArg(int n) const; double DoubleArg(int n) const; };