diff --git a/luprex/core/cpp/globaldb.cpp b/luprex/core/cpp/globaldb.cpp index a6dbb281..c72c30c4 100644 --- a/luprex/core/cpp/globaldb.cpp +++ b/luprex/core/cpp/globaldb.cpp @@ -4,10 +4,10 @@ LuaDefine(globaldb_enable, "c") { LuaVar globaldb; LuaStack LS(L, globaldb); - LS.getfield(globaldb, LuaRegistry, "globaldb"); + LS.rawget(globaldb, LuaRegistry, "globaldb"); if (!LS.istable(globaldb)) { LS.newtable(globaldb); - LS.setfield(LuaRegistry, "globaldb", globaldb); + LS.rawset(LuaRegistry, "globaldb", globaldb); } return LS.result(); } @@ -26,7 +26,7 @@ LuaDefine(globaldb_global, "f") { LuaStack LS(L, globalname, globaltab, globaldb); // Get a pointer to the globaldb. - LS.getfield(globaldb, LuaRegistry, "globaldb"); + LS.rawget(globaldb, LuaRegistry, "globaldb"); if (!LS.istable(globaldb)) { luaL_error(L, "globaldb is not enabled"); } @@ -44,6 +44,6 @@ LuaDefine(globaldb_global, "f") { // Create a new globaltab and store it in the globaldb. LS.newtable(globaltab); LS.rawset(globaldb, globalname, globaltab); - LS.setfield(globaltab, "__global", globalname); + LS.rawset(globaltab, "__global", globalname); return LS.result(); } diff --git a/luprex/core/cpp/gui.cpp b/luprex/core/cpp/gui.cpp index fe4a6c75..f8588ff2 100644 --- a/luprex/core/cpp/gui.cpp +++ b/luprex/core/cpp/gui.cpp @@ -17,7 +17,7 @@ LuaDefine(gui_create, "c") { return LS.result(); } -LuaDefine(gui_add_menu_item, "c") { +LuaDefine(gui_menu_item, "c") { LuaArg lgui, lid; LuaStack LS(L, lgui, lid); Gui *gui = LS.ckuserdata(lgui); diff --git a/luprex/core/cpp/luaconsole.cpp b/luprex/core/cpp/luaconsole.cpp index 88a78ce6..54956aec 100644 --- a/luprex/core/cpp/luaconsole.cpp +++ b/luprex/core/cpp/luaconsole.cpp @@ -1,6 +1,12 @@ #include #include "luaconsole.hpp" +#include "util.hpp" + + +static bool is_single_letter(const std::string &s) { + return ((s.size() == 1) && (s[0] >= 'a') && (s[0] <= 'z')); +} LuaConsole::LuaConsole() { lua_state_ = lua_open(); @@ -52,14 +58,16 @@ void LuaConsole::add(std::string line) { // Try to interpret it as a special command. if (lines_ == 1) { split_words(); - if ((words_.size() >= 1)&&(words_[0].size() == 1)) { - action_ = DO_COMMAND; - return; + if (words_.size() >= 1) { + if (is_single_letter(words_[0]) || (util::validinteger(words_[0]))) { + action_ = DO_COMMAND; + return; + } } } words_.clear(); - // Strip the leading punctuation from lua commands. + // Translate lua expression with leading '=' to 'return' std::string partial; if (raw_input_[0] == '=') { partial = std::string("return ") + raw_input_.substr(1); @@ -67,7 +75,7 @@ void LuaConsole::add(std::string line) { partial = raw_input_; } - // Analyze lua expressions. + // Try to parse the lua expression int top = lua_gettop(lua_state_); int status = luaL_loadbuffer(lua_state_, partial.c_str(), partial.size(), "=stdin"); if (status == LUA_ERRSYNTAX) diff --git a/luprex/core/cpp/luastack.cpp b/luprex/core/cpp/luastack.cpp index e215246e..1a05561b 100644 --- a/luprex/core/cpp/luastack.cpp +++ b/luprex/core/cpp/luastack.cpp @@ -189,33 +189,17 @@ int LuaStack::next(LuaSlot tab, LuaSlot key, LuaSlot value) const { return ret; } -bool LuaStack::isemptytable(LuaSlot tab) const { - if (lua_istable(L_, tab)) { - lua_pushnil(L_); - if (lua_next(L_, tab) == 0) { - return true; - } - lua_pop(L_, 2); - } - return false; -} - void LuaStack::newtable(LuaSlot target) const { lua_newtable(L_); lua_replace(L_, target); } void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const { - int top = lua_gettop(L_); checkstring(classname); - // Special case: if the classname is _G, return global env. - lua_pushstring(L_, "_G"); - int eqlg = lua_equal(L_, -1, classname.index()); - lua_settop(L_, top); - if (eqlg) { - set(classtab, LuaGlobals); - return; + // Special case: if the classname is _G, detect and error. + if (equal(classname, "_G")) { + luaL_error(L_, "_G is explicitly not allowed as a class name"); } // Get the classtab from the global environment. @@ -236,6 +220,19 @@ void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const { setfield(classtab, "__class", classname); } +void LuaStack::makesubtable(LuaSlot sub, LuaSlot tab, const char *name) const { + rawget(sub, tab, name); + if (istable(sub)) { + return; + } else if (isnil(sub)) { + newtable(sub); + rawset(tab, name, sub); + return; + } else { + luaL_error(L_, "%s is not a table", name); + } +} + void LuaStack::setlightuserdata(LuaSlot target, void *p) const { lua_pushlightuserdata(L_, p); lua_replace(L_, target); diff --git a/luprex/core/cpp/luastack.hpp b/luprex/core/cpp/luastack.hpp index 9b236da7..e3e7bb24 100644 --- a/luprex/core/cpp/luastack.hpp +++ b/luprex/core/cpp/luastack.hpp @@ -426,7 +426,7 @@ public: void newtable(LuaSlot target) const; - void makeclass(LuaSlot tab, LuaSlot name) const; + void makesubtable(LuaSlot sub, LuaSlot tab, const char *name) const; void setlightuserdata(LuaSlot target, void *p) const; @@ -456,11 +456,26 @@ public: void clearuserdata(LuaSlot target) { clear_tagged_pointer(target); } int next(LuaSlot tab, LuaSlot key, LuaSlot value) const; - bool isemptytable(LuaSlot s) const; - bool equal(LuaSlot v1, LuaSlot v2) { + void makeclass(LuaSlot tab, LuaSlot name) const; + + void makeclass(LuaSlot tab, const char *name) const { + push_any_value(name); + LuaSpecial classname(lua_gettop(L_)); + makeclass(tab, classname); + lua_pop(L_, 1); + } + + bool equal(LuaSlot v1, LuaSlot v2) const { return lua_equal(L_, v1, v2); } + + bool equal(LuaSlot v1, const char *name) const { + push_any_value(name); + bool result = lua_equal(L_, v1, -1); + lua_pop(L_, 1); + return result; + } template void set(T1 &target, T2 value) const { diff --git a/luprex/core/cpp/source.cpp b/luprex/core/cpp/source.cpp index ec54734f..296133ff 100644 --- a/luprex/core/cpp/source.cpp +++ b/luprex/core/cpp/source.cpp @@ -40,6 +40,16 @@ LuaDefine(source_makeclass, "f") { return LS.result(); } +LuaDefine(source_maketangible, "f") { + LuaArg classname; + LuaRet classtab; + LuaVar subtab; + LuaStack LS(L, classname, classtab, subtab); + LS.makeclass(classtab, classname); + LS.makesubtable(subtab, classtab, "action"); + return LS.result(); +} + // Load the builtins. static void load_builtin(lua_State *L, const char *name, lua_CFunction func) { @@ -189,18 +199,22 @@ static void source_clear_globals(lua_State *L) { // Restore the lua builtins from the backup snapshot. // static void source_restore_builtins(lua_State *L) { - LuaVar snapshot, key, value, skey, svalue, subglobal; - LuaStack LS(L, snapshot, key, value, skey, svalue, subglobal); + LuaVar snapshot, key, value, skey, svalue, target; + LuaStack LS(L, snapshot, key, value, skey, svalue, target); LS.getfield(snapshot, LuaRegistry, "source_snapshot_builtins"); LS.setfield(LuaGlobals, "_G", LuaGlobals); LS.set(key, LuaNil); while (LS.next(snapshot, key, value) != 0) { LS.checktable(value); - LS.makeclass(subglobal, key); + if (LS.equal(key, "_G")) { + LS.set(target, LuaGlobals); + } else { + LS.makeclass(target, key); + } LS.set(skey, LuaNil); while (LS.next(value, skey, svalue) != 0) { - LS.rawset(subglobal, skey, svalue); + LS.rawset(target, skey, svalue); } } LS.result(); diff --git a/luprex/core/cpp/textgame.cpp b/luprex/core/cpp/textgame.cpp index 71fb971e..355d15f5 100644 --- a/luprex/core/cpp/textgame.cpp +++ b/luprex/core/cpp/textgame.cpp @@ -109,13 +109,18 @@ void TextGame::do_menu_command(const StringVec &cmd) { void TextGame::do_choose_command(const StringVec &cmd) { int64_t index; - if (cmd.size() == 2) { - index = util::strtoint(cmd[1], -1); + if (cmd.size() == 1) { + index = util::strtoint(cmd[0], -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; + const Gui::EltVec &elts = gui_.elts(); + if ((index < 0) || (index >= int(elts.size()))) { + std::cerr << "No menu item #" << index << std::endl; + return; + } + std::cerr << "Choosing menu item: " << elts[index].id() << std::endl; } void TextGame::do_snapshot_command(const StringVec &cmd) { @@ -143,14 +148,13 @@ void TextGame::do_quit_command(const StringVec &cmd) { } 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; - case 'q': do_quit_command(words); break; - case 's': do_snapshot_command(words); break; - case 'r': do_rollback_command(words); break; - default: + if (words[0] == "v") do_view_command(words); + else if (words[0] == "m") do_menu_command(words); + else if (words[0] == "q") do_quit_command(words); + else if (words[0] == "s") do_snapshot_command(words); + else if (words[0] == "r") do_rollback_command(words); + else if (util::validinteger(words[0])) do_choose_command(words); + else { std::cerr << "Unknown command: " << words[0] << std::endl; } } diff --git a/luprex/core/cpp/util.cpp b/luprex/core/cpp/util.cpp index 3f902cbb..3757d957 100644 --- a/luprex/core/cpp/util.cpp +++ b/luprex/core/cpp/util.cpp @@ -26,6 +26,12 @@ std::string toupper(std::string input) { return input; } +bool validinteger(const std::string &value) { + char *endptr; + strtoll(value.c_str(), &endptr, 10); + return (endptr == value.c_str() + value.size()); +} + int64_t strtoint(const std::string &value, int64_t errval) { char *endptr; int64_t result = strtoll(value.c_str(), &endptr, 10); diff --git a/luprex/core/cpp/util.hpp b/luprex/core/cpp/util.hpp index 70bbafec..347dc02e 100644 --- a/luprex/core/cpp/util.hpp +++ b/luprex/core/cpp/util.hpp @@ -18,6 +18,9 @@ using stringset = std::set; std::string tolower(std::string input); std::string toupper(std::string input); +// Return true if the string can be parsed as an integer. +bool validinteger(const std::string &value); + // String to integer. Returns errval if the number is not parseable. int64_t strtoint(const std::string &value, int64_t errval); diff --git a/luprex/core/cpp/world.cpp b/luprex/core/cpp/world.cpp index 55b77394..ee610bff 100644 --- a/luprex/core/cpp/world.cpp +++ b/luprex/core/cpp/world.cpp @@ -50,7 +50,7 @@ void Tangible::be_a_player() { LuaVar classtab, mt, place, tangibles; LuaStack LS(world_->state(), classtab, mt, place, tangibles); - LS.call(classtab, source_makeclass, "player"); + LS.makeclass(classtab, "player"); LS.getfield(tangibles, LuaRegistry, "tangibles"); LS.rawget(place, tangibles, anim_queue_.get_id()); LS.getmetatable(mt, place); diff --git a/luprex/core/lua/control.lst b/luprex/core/lua/control.lst index ac08a981..a8cd0af9 100644 --- a/luprex/core/lua/control.lst +++ b/luprex/core/lua/control.lst @@ -6,4 +6,4 @@ inspect.lua ut-table.lua ut-globaldb.lua - +player.lua diff --git a/luprex/core/lua/player.lua b/luprex/core/lua/player.lua new file mode 100644 index 00000000..ebbd4881 --- /dev/null +++ b/luprex/core/lua/player.lua @@ -0,0 +1,24 @@ +maketangible('player') + +function player.interface(actor, place, gui) + gui:menu_item("North") + gui:menu_item("South") + gui:menu_item("East") + gui:menu_item("West") +end + +function player.action.north(actor, place, gui) + print("Moving north") +end + +function player.action.south(actor, place, gui) + print("Moving south") +end + +function player.action.east(actor, place, gui) + print("Moving east") +end + +function player.action.west(actor, place, gui) + print("Moving west") +end diff --git a/luprex/luajit/src/lua.h b/luprex/luajit/src/lua.h index 9e97f651..a3125c22 100644 --- a/luprex/luajit/src/lua.h +++ b/luprex/luajit/src/lua.h @@ -47,11 +47,6 @@ #define LUA_ERRMEM 4 #define LUA_ERRERR 5 -/* Additional thread status codes for luprex */ -#define LUA_ERRPARTIAL 15 -#define LUA_ERREOF 16 - - typedef struct lua_State lua_State; typedef int (*lua_CFunction) (lua_State *L);