First version of new slash-command parser.

This commit is contained in:
2026-01-07 15:29:03 -05:00
parent c681170c95
commit 1087d18a2e
3 changed files with 291 additions and 10 deletions

View File

@@ -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,14 +194,12 @@ class Driver {
}
}
void handle_lua_source() {
if (engw.get_rescan_lua_source(&engw)) {
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 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() {
@@ -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
View 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
View 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;
};