#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; }