132 lines
3.7 KiB
C++
132 lines
3.7 KiB
C++
|
|
#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;
|
||
|
|
}
|