From a784f12aedfd8564fa004ca4b33352b8c13f17ae Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Fri, 13 Nov 2020 18:17:47 -0500 Subject: [PATCH] ClassDB is now in C++ --- luprex/build.bat | 2 +- luprex/syscpp/lpx-classdb.cpp | 131 ++++++++++++++++++++++++++++++++++ luprex/syscpp/lpx-classdb.hpp | 13 ++++ luprex/syscpp/lpx-table.cpp | 43 +++++++++++ luprex/syscpp/lpx-table.hpp | 10 +++ luprex/syscpp/lua-headers.hpp | 9 +++ luprex/syscpp/main.cpp | 19 +++-- luprex/syscpp/moretable.cpp | 0 luprex/syscpp/util.hpp | 1 + luprex/syslua/classdb.lua | 42 ----------- luprex/syslua/control.lst | 2 - luprex/syslua/inspect.lua | 2 +- luprex/syslua/tableutil.lua | 21 ------ 13 files changed, 217 insertions(+), 78 deletions(-) create mode 100644 luprex/syscpp/lpx-classdb.cpp create mode 100644 luprex/syscpp/lpx-classdb.hpp create mode 100644 luprex/syscpp/lpx-table.cpp create mode 100644 luprex/syscpp/lpx-table.hpp create mode 100644 luprex/syscpp/lua-headers.hpp delete mode 100644 luprex/syscpp/moretable.cpp delete mode 100644 luprex/syslua/classdb.lua delete mode 100644 luprex/syslua/tableutil.lua diff --git a/luprex/build.bat b/luprex/build.bat index 71ac8719..e1f9d63f 100644 --- a/luprex/build.bat +++ b/luprex/build.bat @@ -1 +1 @@ -g++ -std=c++17 -g -o main syscpp/util.cpp syscpp/main.cpp -Iinc -Llib lib/libluajit-dbg.a -Isyscpp +g++ -std=c++17 -g -o main syscpp/util.cpp syscpp/main.cpp syscpp/lpx-table.cpp syscpp/lpx-classdb.cpp -Iinc -Llib lib/libluajit-dbg.a -Isyscpp diff --git a/luprex/syscpp/lpx-classdb.cpp b/luprex/syscpp/lpx-classdb.cpp new file mode 100644 index 00000000..ef1e6d30 --- /dev/null +++ b/luprex/syscpp/lpx-classdb.cpp @@ -0,0 +1,131 @@ +#include "lpx-classdb.hpp" +#include "lpx-table.hpp" + +// Get an entry from the class database. +// +// DB, CLASSNAME +// if classname is already present, and is a table, return it. +// if classname is already present, and not a table, error. +// if classname is not present, create and initialize it. +// +int lpx_classdb_get(lua_State *L) { + const int db = lua_gettop(L) - 1; + const int cname = lua_gettop(L); + + luaL_checktype(L, db, LUA_TTABLE); + luaL_checktype(L, cname, LUA_TSTRING); + lua_pushvalue(L, cname); + lua_rawget(L, db); + if (lua_istable(L, -1)) { + return 1; + } else if (!lua_isnil(L, -1)) { + lua_pushfstring(L, "%s is not a class", lua_tostring(L, cname)); + lua_error(L); + } + lua_pop(L, 1); + lua_newtable(L); + int cls = lua_gettop(L); + lua_pushvalue(L, cname); + lua_pushvalue(L, cls); + lua_rawset(L, db); + lua_pushstring(L, "__index"); + lua_pushvalue(L, cls); + lua_rawset(L, cls); + lua_pushstring(L, "__class"); + lua_pushvalue(L, cname); + lua_rawset(L, cls); + lua_pushstring(L, "action"); + lua_newtable(L); + lua_rawset(L, cls); + + // Remove arguments, leaving new table on stack. + lua_replace(L, db); + lua_settop(L, db); + return 1; +} + +// Curried version of classdb.get where db is stored in an upvalue. +// +static int lpx_classdb_class(lua_State *L) { + const int cname = lua_gettop(L); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, cname); + return lpx_classdb_get(L); +} + +int lpx_classdb_accessor(lua_State *L) { + const int db = lua_gettop(L); + luaL_checktype(L, db, LUA_TTABLE); + lua_pushcclosure(L, lpx_classdb_class, 1); + return 1; +} + +// Reset a class database: +// +// Clear out all classes. Instead of replacing the class tables, +// it simply deletes all keys. That way, if somebody has a pointer +// to a class, the pointer is not invalidated. +// +// Caution: do not reset the global class database! +// +int lpx_classdb_reset(lua_State *L) { + const int db = lua_gettop(L); + luaL_checktype(L, db, LUA_TTABLE); + lua_pushnil(L); + while (lua_next(L, db) != 0) { + int cname = lua_gettop(L) - 1; + int cls = lua_gettop(L); + if (lua_istable(L, cls)) { + lua_pushstring(L, "action"); + lua_rawget(L, cls); + int actn = lua_gettop(L); + if (lua_istable(L, actn)) { + lua_pushvalue(L, actn); + lpx_table_clear(L); + } else { + lua_pop(L, 1); + lua_newtable(L); + } + lua_pushvalue(L, cls); + lpx_table_clear(L); + lua_pushstring(L, "__index"); + lua_pushvalue(L, cls); + lua_rawset(L, cls); + lua_pushstring(L, "__class"); + lua_pushvalue(L, cname); + lua_rawset(L, cls); + lua_pushstring(L, "action"); + lua_pushvalue(L, actn); + lua_rawset(L, cls); + } + lua_settop(L, cname); + } + // Remove arguments, leave nothing on stack. + lua_settop(L, db - 1); + return 0; +} + +// Create the global function 'class'. +// +// This version of 'class' creates classes in the global environment. +// Note that this is lua 5.1 specific. +// +static int lpx_create_global_class(lua_State *L) { + lua_pushstring(L, "class"); + lua_pushvalue(L, LUA_GLOBALSINDEX); + lpx_classdb_accessor(L); + lua_rawset(L, LUA_GLOBALSINDEX); +} + +static const struct luaL_Reg lpx_classdb_lib [] = { + {"get", lpx_classdb_get}, + {"accessor", lpx_classdb_accessor}, + {"reset", lpx_classdb_reset}, + {NULL, NULL} /* sentinel */ +}; + +int luaopen_lpx_classdb(lua_State *L) { + luaL_openlib(L, "classdb", lpx_classdb_lib, 0); + lpx_create_global_class(L); + return 1; +} diff --git a/luprex/syscpp/lpx-classdb.hpp b/luprex/syscpp/lpx-classdb.hpp new file mode 100644 index 00000000..a55bb778 --- /dev/null +++ b/luprex/syscpp/lpx-classdb.hpp @@ -0,0 +1,13 @@ +#ifndef LPX_CLASSDB_HPP +#define LPX_CLASSDB_HPP + +#include "lua-headers.hpp" + +int lpx_classdb_get(lua_State *L); +int lpx_classdb_reset(lua_State *L); +int lpx_classdb_accessor(lua_State *L); +int luaopen_lpx_classdb(lua_State *L); + +#endif // LPX_CLASSDB_HPP + + diff --git a/luprex/syscpp/lpx-table.cpp b/luprex/syscpp/lpx-table.cpp new file mode 100644 index 00000000..587f7d1c --- /dev/null +++ b/luprex/syscpp/lpx-table.cpp @@ -0,0 +1,43 @@ +#include "lpx-table.hpp" + +// Clear the table. Removes metatable and all key-value pairs. +int lpx_table_clear(lua_State *L) { + const int tab = lua_gettop(L); + luaL_checktype(L, tab, LUA_TTABLE); + + // Clear the metatable. + lua_pushnil(L); + lua_setmetatable(L, tab); + + // Clear the elements. + lua_pushnil(L); + while (lua_next(L, tab) != 0) { + lua_pop(L, 1); // Pop the old value. + lua_pushvalue(L, -1); // Clone the key + lua_pushnil(L); // Push the new value. + lua_settable(L, tab); + } + + // Remove the arguments and return nothing. + lua_remove(L, tab); + return 0; +} + +int lpx_table_coerce(lua_State *L) { + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + } + return 1; +} + +static const struct luaL_Reg lpx_table_lib [] = { + {"clear", lpx_table_clear}, + {"coerce", lpx_table_coerce}, + {NULL, NULL} /* sentinel */ +}; + +int luaopen_lpx_table (lua_State *L) { + luaL_openlib(L, "table", lpx_table_lib, 0); + return 1; +} diff --git a/luprex/syscpp/lpx-table.hpp b/luprex/syscpp/lpx-table.hpp new file mode 100644 index 00000000..17fe6935 --- /dev/null +++ b/luprex/syscpp/lpx-table.hpp @@ -0,0 +1,10 @@ +#ifndef LPX_TABLE_HPP +#define LPX_TABLE_HPP + +#include "lua-headers.hpp" + +int lpx_table_clear(lua_State *L); +int lpx_table_coerce(lua_State *L); +int luaopen_lpx_table (lua_State *L); + +#endif // LPX_TABLE_HPP diff --git a/luprex/syscpp/lua-headers.hpp b/luprex/syscpp/lua-headers.hpp new file mode 100644 index 00000000..70f35e71 --- /dev/null +++ b/luprex/syscpp/lua-headers.hpp @@ -0,0 +1,9 @@ +#ifndef LUA_HEADERS_HPP +#define LUA_HEADERS_HPP +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +#include "luajit.h" +} +#endif // LUA_HEADERS_HPP diff --git a/luprex/syscpp/main.cpp b/luprex/syscpp/main.cpp index 06a5b29e..57397757 100644 --- a/luprex/syscpp/main.cpp +++ b/luprex/syscpp/main.cpp @@ -10,17 +10,12 @@ #include #include #include - -extern "C" { -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" -#include "luajit.h" -} - #include #include +#include "lua-headers.hpp" #include "util.hpp" +#include "lpx-table.hpp" +#include "lpx-classdb.hpp" // Add another error status. @@ -223,14 +218,16 @@ static int pmain(lua_State *L) lua_gc(L, LUA_GCSTOP, 0); luaopen_base(L); // luaopen_package(L); // Omitted because we use our own package system. - luaopen_table(L); - // luaopen_io(L); // Not safe for the sandbox. - // luaopen_os(L); // Not safe for the sandbox. + luaopen_table(L); // Standard table operations. + luaopen_lpx_table(L); // Supplemental table operations. + // luaopen_io(L); // Not safe for the sandbox. + // luaopen_os(L); // Not safe for the sandbox. luaopen_string(L); luaopen_math(L); // luaopen_debug(L); // Not safe for the sandbox. luaopen_bit(L); // luaopen_jit(L); // Don't know what it's for. + luaopen_lpx_classdb(L); lua_gc(L, LUA_GCRESTART, -1); loadmain(L); diff --git a/luprex/syscpp/moretable.cpp b/luprex/syscpp/moretable.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/luprex/syscpp/util.hpp b/luprex/syscpp/util.hpp index 0ae58b9c..d0ea96a8 100644 --- a/luprex/syscpp/util.hpp +++ b/luprex/syscpp/util.hpp @@ -1,4 +1,5 @@ #ifndef UTIL_HPP +#define UTIL_HPP #include #include diff --git a/luprex/syslua/classdb.lua b/luprex/syslua/classdb.lua deleted file mode 100644 index 9d489d6e..00000000 --- a/luprex/syslua/classdb.lua +++ /dev/null @@ -1,42 +0,0 @@ - -classdb = table.coerce(classdb) - --- classdb.create: creates a new class database. -function classdb.create() - return {} -end - --- Given a class database, removes all the functions that have --- been inserted into it, but keeps the class and action tables. -function classdb.reset(db) - for name,maybeclass in pairs(db) do - local class = table.coerce(maybeclass) - local action = table.coerce(rawget(class, "action")) - table.rawclear(class) - table.rawclear(action) - rawset(db, name, class) - class.action = action - class.__index = class - class.__class = name - end -end - --- Given a class_db, makes an accessor that fetches classes --- from that class db. -function classdb.accessor(db) - return function(name) - local class = rawget(db, name) - if type(class) ~= "table" then - class = {} - rawset(db, name, class) - class.action = {} - class.__index = class - class.__class = name - end - return class - end -end - --- Make a global class database to use for the super. -classdb.database = {} -class = classdb.accessor(classdb.database) diff --git a/luprex/syslua/control.lst b/luprex/syslua/control.lst index a3d58638..8296aa1c 100644 --- a/luprex/syslua/control.lst +++ b/luprex/syslua/control.lst @@ -3,8 +3,6 @@ # in the order that they're supposed to be loaded. # -tableutil.lua -classdb.lua inspect.lua main.lua diff --git a/luprex/syslua/inspect.lua b/luprex/syslua/inspect.lua index 4d24b6c4..bd5210d4 100644 --- a/luprex/syslua/inspect.lua +++ b/luprex/syslua/inspect.lua @@ -331,5 +331,5 @@ function inspect.inspect(root, options) return table.concat(inspector.buffer) end -setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end }) +_G.inspect = inspect.inspect diff --git a/luprex/syslua/tableutil.lua b/luprex/syslua/tableutil.lua deleted file mode 100644 index 01fc3204..00000000 --- a/luprex/syslua/tableutil.lua +++ /dev/null @@ -1,21 +0,0 @@ - --- Coerce T to be a table. --- If T is a table, return it. If not, return a blank table. -function table.coerce(t) - if type(t) == "table" then - return t - else - return {} - end -end - --- Clear the table out. Deletes all keys, removes any metatable. --- TODO: this doesn't work on tables that have __metatable metamethod. -function table.rawclear(t) - for k in pairs(t) do - rawset(t, k, nil) - end - setmetatable(t, nil) - return t -end -