First version of new slash-command parser.
This commit is contained in:
@@ -5,6 +5,9 @@
|
||||
#include "driver-windows.cpp"
|
||||
#endif
|
||||
|
||||
#include "slash-parser.hpp"
|
||||
#include "slash-parser.cpp"
|
||||
|
||||
#define POLLVEC_SIZE (DRV_MAX_CHAN + 1)
|
||||
#define MAX_BIO_BUFFER (128 * 1024)
|
||||
|
||||
@@ -191,15 +194,13 @@ class Driver {
|
||||
}
|
||||
}
|
||||
|
||||
void handle_lua_source() {
|
||||
if (engw.get_rescan_lua_source(&engw)) {
|
||||
void inject_lua_source() {
|
||||
drvutil::ostringstream oss;
|
||||
std::string err = drvutil::package_lua_source(".", &oss);
|
||||
if_error_print_and_exit(err);
|
||||
std::string_view ossv = oss.view();
|
||||
engw.play_access(&engw, AccessKind::INVOKE_LUA_SOURCE, 0, ossv.size(), ossv.data(), nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void channel_printbuffer() {
|
||||
if (engw.get_have_prints(&engw)) {
|
||||
@@ -213,6 +214,20 @@ class Driver {
|
||||
}
|
||||
}
|
||||
|
||||
void handle_slash_command(const std::string &cmd)
|
||||
{
|
||||
SlashCommandParser parser(cmd);
|
||||
if (parser.Parse("/quit", "")) {
|
||||
exit(0);
|
||||
}
|
||||
else if (parser.Parse("/cpl", "")) {
|
||||
inject_lua_source();
|
||||
}
|
||||
else {
|
||||
readline_device_.printline(parser.Error());
|
||||
}
|
||||
}
|
||||
|
||||
void add_console_command(std::string_view addition)
|
||||
{
|
||||
std::string cmd = console_command_ + std::string(addition);
|
||||
@@ -229,7 +244,7 @@ class Driver {
|
||||
} else if (message == "white space") {
|
||||
// no need to do anything.
|
||||
} else if (message == "slash command") {
|
||||
readline_device_.printline("slash command.");
|
||||
handle_slash_command(cmd);
|
||||
} else if (message.empty()) {
|
||||
engw.play_access(&engw, AccessKind::INVOKE_LUA_EXPR,
|
||||
0, cmd.size(), cmd.c_str(), nullptr, nullptr);
|
||||
@@ -689,7 +704,9 @@ class Driver {
|
||||
|
||||
// Main loop.
|
||||
while (!engw.get_stop_driver(&engw)) {
|
||||
handle_lua_source();
|
||||
if (engw.get_rescan_lua_source(&engw)) {
|
||||
inject_lua_source();
|
||||
}
|
||||
handle_new_outgoing_sockets();
|
||||
handle_socket_input_output();
|
||||
handle_console_input();
|
||||
|
||||
150
luprex/ext/slash-parser.cpp
Normal file
150
luprex/ext/slash-parser.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef DONT_INCLUDE_SLASH_PARSER_HPP
|
||||
#include "slash-parser.hpp"
|
||||
#endif
|
||||
#include <charconv>
|
||||
|
||||
|
||||
|
||||
void SlashCommandParser::skip_white()
|
||||
{
|
||||
while ((cursor_ < linelen_) && (ascii_isspace(line_[cursor_])))
|
||||
{
|
||||
cursor_++;
|
||||
}
|
||||
}
|
||||
|
||||
std::string SlashCommandParser::read_word()
|
||||
{
|
||||
int start = cursor_;
|
||||
while ((cursor_ < linelen_) && (!ascii_isspace(line_[cursor_])))
|
||||
{
|
||||
cursor_++;
|
||||
}
|
||||
std::string result = line_.substr(start, cursor_ - start);
|
||||
skip_white();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
SlashCommandParser::SlashCommandParser(std::string_view Line)
|
||||
{
|
||||
line_ = Line;
|
||||
linelen_ = int(Line.size());
|
||||
cursor_ = 0;
|
||||
command_ = read_word();
|
||||
if ((command_.size() < 2) || (command_[0] != '/'))
|
||||
{
|
||||
error_ = "Not a slash-command";
|
||||
return;
|
||||
}
|
||||
if (!ascii_isalpha(command_[1]))
|
||||
{
|
||||
error_ = "Slash command must start with ascii alpha";
|
||||
return;
|
||||
}
|
||||
for (int i = 1; i < int(command_.size()); i++)
|
||||
{
|
||||
if (!ascii_isalnum(command_[i]))
|
||||
{
|
||||
error_ = "Slash-command must be alphanumeric";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SlashCommandParser::Parse(std::string_view Command, std::string_view ArgTypes)
|
||||
{
|
||||
if (Command != std::string_view(command_)) return false;
|
||||
|
||||
args_.clear();
|
||||
for (int i = 0; i < int(ArgTypes.size()); i++)
|
||||
{
|
||||
switch(ArgTypes[i])
|
||||
{
|
||||
case 's':
|
||||
{
|
||||
std::string word = read_word();
|
||||
if (word.empty())
|
||||
{
|
||||
error_ = "Expected string arg";
|
||||
return false;
|
||||
}
|
||||
args_.emplace_back(word);
|
||||
break;
|
||||
}
|
||||
case 'i':
|
||||
{
|
||||
std::string word = read_word();
|
||||
const char *p = word.c_str();
|
||||
const char *last = p + word.size();
|
||||
if ((p < last) && (*p == '+')) p++;
|
||||
int64_t result;
|
||||
auto r = std::from_chars(p, last, result, 10);
|
||||
if ((r.ec != std::errc()) || (r.ptr != last)) {
|
||||
error_ = "Expected int arg";
|
||||
return false;
|
||||
}
|
||||
args_.emplace_back(result);
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
{
|
||||
error_ = "Double args not implemented yet.";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
error_ = "Invalid argtypes ";
|
||||
error_ += ArgTypes;
|
||||
error_ += " parsing ";
|
||||
error_ += Command;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor_ != linelen_)
|
||||
{
|
||||
error_ = "Too many arguments for command: ";
|
||||
error_ += Command;
|
||||
return false;
|
||||
}
|
||||
|
||||
error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string SlashCommandParser::Error() const
|
||||
{
|
||||
if (!error_.empty())
|
||||
{
|
||||
return error_;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::string("Unrecognized command: ") + command_;
|
||||
}
|
||||
}
|
||||
|
||||
std::string SlashCommandParser::StringArg(int n) const
|
||||
{
|
||||
if ((n < 0) || (n >= int(args_.size()))) return "";
|
||||
|
||||
return args_[n].s_;
|
||||
}
|
||||
|
||||
int64_t SlashCommandParser::IntArg(int n) const
|
||||
{
|
||||
if ((n < 0) || (n >= int(args_.size()))) return 0;
|
||||
|
||||
return args_[n].i_;
|
||||
}
|
||||
|
||||
double SlashCommandParser::DoubleArg(int n) const
|
||||
{
|
||||
if ((n < 0) || (n >= int(args_.size()))) return 0.0;
|
||||
|
||||
return args_[n].d_;
|
||||
}
|
||||
114
luprex/ext/slash-parser.hpp
Normal file
114
luprex/ext/slash-parser.hpp
Normal file
@@ -0,0 +1,114 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <string>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
#include <map>
|
||||
#include <cstdint>
|
||||
|
||||
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<Value> 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();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user