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

View File

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

View File

@@ -14,6 +14,23 @@
#include "table.hpp" #include "table.hpp"
#include "source.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") { LuaDefine(source_makeclass, "f") {
LuaArg classname; LuaArg classname;
@@ -158,8 +175,7 @@ void SourceDB::update() {
} }
// Read the list of filenames. // Read the list of filenames.
std::string ctrl = "lua/control.lst"; util::stringvec filenames = read_control_lst("lua/control.lst");
util::stringvec filenames = util::trim_and_uncomment(util::read_lines(ctrl));
if (filenames.empty()) { if (filenames.empty()) {
luaL_error(L, "cannot read source database control.lst"); 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"); int status = luaL_loadbuffer(L, exp.c_str(), exp.size(), "=stdin");
assert(status == LUA_OK); assert(status == LUA_OK);
globalL = L; 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() void TextGame::run()
{ {
LuaConsole console; console_.clear();
while (true) { while (true) {
console.add_stdin(); console_.add_stdin();
int action = console.action(); int action = console_.action();
if (action == LuaConsole::DO_COMMAND) { if (action == LuaConsole::DO_LUA) {
doexp(viewer_.get_lua_state(), console.expression()); do_lua(console_.lua_expression());
} else if (action == LuaConsole::DO_SLASH) { } else if (action == LuaConsole::DO_COMMAND) {
std::cerr << "Slash commands not supported yet." << std::endl; do_command(console_.words());
} else if (action == LuaConsole::DO_SYNTAX) { } 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 #define TEXTGAME_HPP
#include "viewer.hpp" #include "viewer.hpp"
#include "luaconsole.hpp"
class TextGame { class TextGame {
private: private:
using StringVec = LuaConsole::StringVec;
Viewer viewer_; 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: public:
void run(); void run();
}; };

View File

@@ -1,6 +1,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <fstream> #include <fstream>
#include <cstdlib>
#include "util.hpp" #include "util.hpp"
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -11,8 +12,41 @@
namespace util { namespace util {
// Read a file as lines. int64_t strtoint(const std::string &value, int64_t errval) {
const stringvec read_lines(const std::string &path) { 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; stringvec result;
std::ifstream f; std::ifstream f;
f.open(path); f.open(path);
@@ -26,7 +60,6 @@ const stringvec read_lines(const std::string &path) {
return result; return result;
} }
// Get a string encoding the modification time and size of the file.
std::string get_file_fingerprint(const std::string &fn) { std::string get_file_fingerprint(const std::string &fn) {
struct stat result; struct stat result;
if(stat(fn.c_str(), &result)==0) if(stat(fn.c_str(), &result)==0)
@@ -38,34 +71,13 @@ std::string get_file_fingerprint(const std::string &fn) {
return ""; 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 distance_squared(double x1, double y1, double x2, double y2) {
double dx = x1 - x2; double dx = x1 - x2;
double dy = y1 - y2; double dy = y1 - y2;
return dx*dx + dy*dy; 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_rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
#define hash_mix(a,b,c) { \ #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 hash_to_float(double lo, double hi, uint32_t a, uint32_t b, uint32_t c) {
double result = hash3(a, b, c); // Lossless. double result = hash3(a, b, c); // Lossless.
result *= ((hi-lo) * (1.0 / 0xFFFFFFFF)); result *= (1.0 / 0xFFFFFFFF);
result *= (hi-lo);
result += lo; result += lo;
return result; return result;
} }
std::ostream & operator << (std::ostream &out, const XYZ &xyz) {
out << "(" << xyz.x << "," << xyz.y << "," << xyz.z << ")";
return out;
}
} // namespace util } // namespace util

View File

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

View File

@@ -25,9 +25,3 @@ std::vector<std::string> Viewer::get_menu(int64_t id) {
result.push_back("destroy"); result.push_back("destroy");
return result; 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. // 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. // Get the menu of the specified tangible.
// //
std::vector<std::string> get_menu(int64_t id); 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 #endif // VIEWER_HPP

View File

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