More work on command parser

This commit is contained in:
2021-02-02 16:29:07 -05:00
parent df3ec8ab99
commit 0721d29c72
10 changed files with 239 additions and 106 deletions

View File

@@ -12,12 +12,32 @@ LuaConsole::~LuaConsole() {
}
void LuaConsole::clear() {
expression_ = "";
raw_input_ = "";
lines_ = 0;
lua_expression_ = "";
words_.clear();
syntax_ = "";
action_ = DO_NOTHING;
}
void LuaConsole::split_words() {
words_.clear();
std::string acc;
for (char c : raw_input_) {
if ((c == ' ')||(c == '\n')) {
if (!acc.empty()) {
words_.push_back(acc);
acc = "";
}
} else {
acc += c;
}
}
if (!acc.empty()) {
words_.push_back(acc);
}
}
void LuaConsole::add(std::string line) {
if (action_ != DO_NOTHING) {
clear();
@@ -25,30 +45,42 @@ void LuaConsole::add(std::string line) {
for (int i = 0; i < int(line.size()); i++) {
if (line[i] == '\n') line[i] = ' ';
}
expression_ += line;
expression_ += '\n';
raw_input_ += line;
raw_input_ += '\n';
lines_ += 1;
char cmd = raw_input_[0];
// Convert leading = to 'return'
if (expression_[0] == '=') {
expression_ = std::string("return ") + expression_.substr(1);
// All commands are a single character.
if ((raw_input_[1] != ' ')&&(raw_input_[1] != '\n')) {
action_ = DO_SYNTAX;
syntax_ = "Commands are a single character followed by arguments";
return;
}
// Analyze slash-commands.
if (expression_[0] == '/') {
// Analyze non-lua commands.
if ((cmd != 'r') && (cmd != 'l')) {
lua_expression_ = "";
if (lines_ == 1) {
action_ = DO_SLASH;
syntax_ = "";
action_ = DO_COMMAND;
split_words();
} else {
action_ = DO_SYNTAX;
syntax_ = "slash command has more than one line";
}
return;
}
// Strip the leading punctuation from lua commands.
std::string partial;
if (cmd == 'r') {
partial = std::string("return ") + raw_input_.substr(2);
} else {
partial = raw_input_.substr(2);
}
// Analyze lua expressions.
int top = lua_gettop(lua_state_);
int status = luaL_loadbuffer(lua_state_, expression_.c_str(), expression_.size(), "=stdin");
int status = luaL_loadbuffer(lua_state_, partial.c_str(), partial.size(), "=stdin");
if (status == LUA_ERRSYNTAX)
{
const char *eof = "'<eof>'";
@@ -56,15 +88,14 @@ void LuaConsole::add(std::string line) {
const char *msg = lua_tolstring(lua_state_, -1, &lmsg);
const char *tp = msg + lmsg - (sizeof(eof) - 1);
if (strstr(msg, eof) == tp) {
syntax_ = "";
action_ = DO_NOTHING;
} else {
syntax_ = msg;
action_ = DO_SYNTAX;
syntax_ = msg;
}
} else {
syntax_ = "";
action_ = DO_COMMAND;
action_ = DO_LUA;
lua_expression_ = partial;
}
lua_settop(lua_state_, top);
}
@@ -75,7 +106,7 @@ void LuaConsole::add_stdin() {
}
const int MAXINPUT = 1000;
char buf[MAXINPUT];
fputs(expression_.empty() ? "> " : ">> ", stdout);
fputs(raw_input_.empty() ? "> " : ">> ", stdout);
fflush(stdout);
if (fgets(buf, MAXINPUT, stdin)) {
size_t len = strlen(buf);

View File

@@ -2,20 +2,23 @@
//
// LuaConsole:
//
// Used to parse lines of text that are being interactively typed
// Used to parse commands that are being interactively typed
// in by a user.
//
// Whenever the user types in a line, you should add the line to
// the LuaConsole object. After adding a line, the console will
// recommend one of several actions:
// The command syntax is always a single character command,
// followed by arguments. Only two commands are hardwired:
//
// 1. DO_NOTHING: The input is not complete, so you need to wait.
// 2. DO_COMMAND: Execute a lua command.
// 3. DO_SYNTAX: Print a syntax error message.
// 4. DO_SLASH: Execute a slash-command that bypasses lua.
// l - evaluate lua expression
// r - evaluate lua expression with 'return' prepended
//
// Also: if the first character of the lua form is '=', then
// replace the '=' with 'return '.
// The console expects you to add lines of text one at a time.
// After adding a line, the console will recommend one of these
// actions:
//
// 1. DO_NOTHING: Add more lines of text.
// 2. DO_LUA: Evaluate a lua expression.
// 3. DO_OTHER: Execute a non-lua command.
// 4. DO_SYNTAX: Print a lua syntax error.
//
//////////////////////////////////////////////////////
@@ -29,28 +32,46 @@ class LuaConsole {
public:
enum {
DO_NOTHING, // We need more input text.
DO_COMMAND, // We have a valid lua expression.
DO_COMMAND, // We have a valid non-lua command.
DO_LUA, // We have a valid lua expression.
DO_SYNTAX, // We have a syntax error.
DO_SLASH, // We have a slash-command.
};
using StringVec = std::vector<std::string>;
private:
lua_State *lua_state_;
int action_;
std::string raw_input_;
int lines_;
std::string expression_;
std::string lua_expression_;
StringVec words_;
std::string syntax_;
void split_words();
public:
LuaConsole();
~LuaConsole();
// Get the recommended action.
int action() const { return action_; }
int lines() const { return lines_; }
const std::string &expression() const { return expression_; }
// When action is DO_COMMAND, get the command words.
const StringVec &words() const { return words_; }
// When action is DO_LUA, get the valid lua expression.
std::string lua_expression() const { return lua_expression_; }
// When action is DO_SYNTAX, get the syntax error.
const std::string &syntax() const { return syntax_; }
// Add a line of text that was just read from the console.
void add(std::string line);
// Read a line of text from stdin and add it.
void add_stdin();
// Clear the state.
void clear();
};

View File

@@ -14,6 +14,23 @@
#include "table.hpp"
#include "source.hpp"
// Read control.lst
//
// - trim all lines
// - remove blank lines
// - remove comment lines
//
util::stringvec read_control_lst(const std::string &path) {
util::stringvec lines = util::get_file_lines(path);
util::stringvec result;
for (int i = 0; i < int(lines.size()); i++) {
std::string trimmed = util::trim(lines[i]);
if ((trimmed.size() > 0) && (trimmed[0] != '#')) {
result.push_back(trimmed);
}
}
return result;
}
LuaDefine(source_makeclass, "f") {
LuaArg classname;
@@ -158,8 +175,7 @@ void SourceDB::update() {
}
// Read the list of filenames.
std::string ctrl = "lua/control.lst";
util::stringvec filenames = util::trim_and_uncomment(util::read_lines(ctrl));
util::stringvec filenames = read_control_lst("lua/control.lst");
if (filenames.empty()) {
luaL_error(L, "cannot read source database control.lst");
}

View File

@@ -43,8 +43,8 @@ static void l_message(const char *msg)
}
static void doexp(lua_State *L, const std::string &exp)
{
void TextGame::do_lua(const std::string &exp) {
lua_State *L = viewer_.get_lua_state();
int status = luaL_loadbuffer(L, exp.c_str(), exp.size(), "=stdin");
assert(status == LUA_OK);
globalL = L;
@@ -76,18 +76,66 @@ static void doexp(lua_State *L, const std::string &exp)
}
}
void TextGame::do_view_command(const StringVec &cmd) {
if (cmd.size() != 1) {
std::cerr << "v command (view) takes no arguments" << std::endl;
return;
}
viewer_.update_view_map();
for (auto viewpair : viewer_.get_view_map()) {
int64_t id = viewpair.first;
AnimView &view = viewpair.second;
std::cerr << id << ": " << view.get_graphic() << " " << view.get_xyz() << std::endl;
}
}
void TextGame::do_menu_command(const StringVec &cmd) {
int64_t id;
if (cmd.size() == 1) {
id = 1;
} else if (cmd.size() == 2) {
id = util::strtoint(cmd[1], -1);
} else {
std::cerr << "m command (menu) expects a tangible ID or defaults to 1" << std::endl;
return;
}
std::cerr << "Menu command (id " << id << ") not implemented yet." << std::endl;
}
void TextGame::do_choose_command(const StringVec &cmd) {
int64_t index;
if (cmd.size() == 2) {
index = util::strtoint(cmd[1], -1);
} else {
std::cerr << "c command (choose) expects a menu line number" << std::endl;
return;
}
std::cerr << "Choose command (index " << index << ") not implemented yet." << std::endl;
}
void TextGame::do_command(const StringVec &words) {
switch (words[0][0]) {
case 'v': do_view_command(words); break;
case 'm': do_menu_command(words); break;
case 'c': do_choose_command(words); break;
default:
std::cerr << "Unknown command: " << words[0] << std::endl;
}
}
void TextGame::run()
{
LuaConsole console;
console_.clear();
while (true) {
console.add_stdin();
int action = console.action();
if (action == LuaConsole::DO_COMMAND) {
doexp(viewer_.get_lua_state(), console.expression());
} else if (action == LuaConsole::DO_SLASH) {
std::cerr << "Slash commands not supported yet." << std::endl;
console_.add_stdin();
int action = console_.action();
if (action == LuaConsole::DO_LUA) {
do_lua(console_.lua_expression());
} else if (action == LuaConsole::DO_COMMAND) {
do_command(console_.words());
} else if (action == LuaConsole::DO_SYNTAX) {
std::cerr << console.syntax() << std::endl;
std::cerr << console_.syntax() << std::endl;
}
}
}

View File

@@ -3,10 +3,23 @@
#define TEXTGAME_HPP
#include "viewer.hpp"
#include "luaconsole.hpp"
class TextGame {
private:
using StringVec = LuaConsole::StringVec;
Viewer viewer_;
LuaConsole console_;
int64_t menu_id_;
std::vector<std::string> menu_;
void do_view_command(const StringVec &cmd);
void do_menu_command(const StringVec &cmd);
void do_choose_command(const StringVec &cmd);
void do_lua(const std::string &exp);
void do_command(const StringVec &exp);
public:
void run();
};

View File

@@ -1,6 +1,7 @@
#include <string>
#include <vector>
#include <fstream>
#include <cstdlib>
#include "util.hpp"
#include <sys/types.h>
#include <sys/stat.h>
@@ -11,8 +12,41 @@
namespace util {
// Read a file as lines.
const stringvec read_lines(const std::string &path) {
int64_t strtoint(const std::string &value, int64_t errval) {
char *endptr;
int64_t result = strtoll(value.c_str(), &endptr, 10);
if (endptr == value.c_str() + value.size()) {
return result;
} else {
return errval;
}
}
std::string ltrim(std::string s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
std::not1(std::ptr_fun<int, int>(std::isspace))));
return s;
}
std::string rtrim(std::string s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
return s;
}
std::string trim(std::string s) {
return ltrim(rtrim(s));
}
std::string get_file_contents(const std::string &fn) {
std::ifstream fs(fn);
std::stringstream buffer;
buffer << fs.rdbuf();
return buffer.str();
}
stringvec get_file_lines(const std::string &path) {
stringvec result;
std::ifstream f;
f.open(path);
@@ -26,7 +60,6 @@ const stringvec read_lines(const std::string &path) {
return result;
}
// Get a string encoding the modification time and size of the file.
std::string get_file_fingerprint(const std::string &fn) {
struct stat result;
if(stat(fn.c_str(), &result)==0)
@@ -38,34 +71,13 @@ std::string get_file_fingerprint(const std::string &fn) {
return "";
}
std::string get_file_contents(const std::string &fn) {
std::ifstream fs(fn);
std::stringstream buffer;
buffer << fs.rdbuf();
return buffer.str();
}
// Strip leading and trailing whitespace and comments.
const stringvec trim_and_uncomment(const stringvec &lines) {
stringvec result;
for (int i = 0; i < int(lines.size()); i++) {
std::string trimmed = trim(lines[i]);
if ((trimmed.size() > 0) && (trimmed[0] != '#')) {
result.push_back(trimmed);
}
}
return result;
}
double distance_squared(double x1, double y1, double x2, double y2) {
double dx = x1 - x2;
double dy = y1 - y2;
return dx*dx + dy*dy;
}
// These are Bob Jenkins' Lookup3.
// These hash functions are part of Bob Jenkins' Lookup3.
#define hash_rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
#define hash_mix(a,b,c) { \
@@ -94,9 +106,15 @@ uint32_t hash3(uint32_t a, uint32_t b, uint32_t c) {
double hash_to_float(double lo, double hi, uint32_t a, uint32_t b, uint32_t c) {
double result = hash3(a, b, c); // Lossless.
result *= ((hi-lo) * (1.0 / 0xFFFFFFFF));
result *= (1.0 / 0xFFFFFFFF);
result *= (hi-lo);
result += lo;
return result;
}
std::ostream & operator << (std::ostream &out, const XYZ &xyz) {
out << "(" << xyz.x << "," << xyz.y << "," << xyz.z << ")";
return out;
}
} // namespace util

View File

@@ -5,6 +5,7 @@
#include <set>
#include <algorithm>
#include <sstream>
#include <ostream>
#include <tuple>
#include <utility>
@@ -13,44 +14,40 @@ namespace util {
using stringvec = std::vector<std::string>;
using stringset = std::set<std::string>;
// trim from start
static inline std::string ltrim(std::string s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
std::not1(std::ptr_fun<int, int>(std::isspace))));
return s;
}
// String to integer. Returns errval if the number is not parseable.
int64_t strtoint(const std::string &value, int64_t errval);
// trim from end
static inline std::string rtrim(std::string s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
return s;
}
// Trim strings: left end, right end, both ends.
std::string ltrim(std::string s);
std::string rtrim(std::string s);
std::string trim(std::string s);
// trim from both ends
static inline std::string trim(std::string s) {
return ltrim(rtrim(s));
}
// Read a file as one big string.
std::string get_file_contents(const std::string &path);
const stringvec read_lines(const std::string &path);
const stringvec trim_and_uncomment(const stringvec &lines);
// Read a file as a vector of lines.
stringvec get_file_lines(const std::string &path);
// Get a file's fingerprint - ie, size and modification time.
std::string get_file_fingerprint(const std::string &path);
std::string get_file_contents(const std::string &fn);
// Calculate distance between two points
double distance_squared(double x1, double y1, double x2, double y2);
// Return a pseudorandom number which is a hash function of A,B,C.
uint32_t hash3(uint32_t a, uint32_t b, uint32_t c);
// Returns a floating point value between lo and hi inclusive.
// Return a pseudorandom float between lo and hi inclusive.
double hash_to_float(double lo, double hi, uint32_t a, uint32_t b, uint32_t c);
// An XYZ coordinate, general purpose.
struct XYZ {
float x, y, z;
XYZ() { x=0; y=0; z=0; }
XYZ(float ix, float iy, float iz) { x=ix; y=iy; z=iz; }
bool operator ==(const XYZ &o) { return x==o.x && y == o.y && z==o.z; }
};
std::ostream & operator << (std::ostream &out, const XYZ &xyz);
} // namespace util
#endif // UTIL_HPP

View File

@@ -25,9 +25,3 @@ std::vector<std::string> Viewer::get_menu(int64_t id) {
result.push_back("destroy");
return result;
}
void Viewer::choose_menu_item(int64_t id, const std::string &item) {
}
void Viewer::advance_clock() {
};

View File

@@ -30,19 +30,11 @@ public:
// Get the view map.
//
const AnimViewMap &get_view_map() { return view_map_; }
AnimViewMap &get_view_map() { return view_map_; }
// Get the menu of the specified tangible.
//
std::vector<std::string> get_menu(int64_t id);
// Select the specified menu item.
//
void choose_menu_item(int64_t id, const std::string &item);
// Advance the simulation clock. Called 1/sec.
//
void advance_clock();
};
#endif // VIEWER_HPP

View File

@@ -48,6 +48,9 @@ World::World() {
void Tangible::be_a_player() {
if (id_player_pool_ == nullptr) {
id_player_pool_.reset(new IdPlayerPool(&world_->id_global_pool_));
anim_queue_.add(world_->id_global_pool_.get_one(), "");
anim_queue_.set_graphic("player");
}
}