Lots of work on LuaStack
This commit is contained in:
@@ -1 +1 @@
|
|||||||
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
|
g++ -std=c++17 -g -o main syscpp/util.cpp syscpp/main.cpp syscpp/luastack.cpp syscpp/source.cpp syscpp/table.cpp syscpp/globaldb.cpp -Iinc -Llib lib/libluajit-dbg.a -Isyscpp
|
||||||
|
|||||||
43
luprex/syscpp/globaldb.cpp
Normal file
43
luprex/syscpp/globaldb.cpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include "globaldb.hpp"
|
||||||
|
#include "table.hpp"
|
||||||
|
|
||||||
|
// Get a table from the global database.
|
||||||
|
//
|
||||||
|
// GLOBALNAME
|
||||||
|
// if globalname is already present, and is a table, return it.
|
||||||
|
// if globalname is already present, and not a table, error.
|
||||||
|
// if globalname is not present, create and initialize it.
|
||||||
|
//
|
||||||
|
int lpx_globaldb_global(lua_State *L) {
|
||||||
|
LuaArg globalname;
|
||||||
|
LuaRet globaltab;
|
||||||
|
LuaVar globaldb;
|
||||||
|
LuaStack LS(L, globalname, globaltab, globaldb);
|
||||||
|
|
||||||
|
LS.checktype(globalname, LUA_TSTRING);
|
||||||
|
|
||||||
|
// Get a pointer to the globaldb.
|
||||||
|
LS.getfield(globaldb, LUA_REGISTRYINDEX, "globaldb");
|
||||||
|
if (!LS.istable(globaldb)) {
|
||||||
|
LS.newtable(globaldb);
|
||||||
|
LS.setfield(LUA_REGISTRYINDEX, "globaldb", globaldb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the globaltab from the globaldb, sanity check it.
|
||||||
|
LS.rawget(globaltab, globaldb, globalname);
|
||||||
|
if (LS.istable(globaltab)) {
|
||||||
|
return LS.result();
|
||||||
|
} else if (!LS.isnil(globaltab)) {
|
||||||
|
luaL_error(L, "%s is not a global", LS.tostring(globalname).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new globaltab and store it in the globaldb.
|
||||||
|
LS.newtable(globaltab);
|
||||||
|
LS.rawset(globaldb, globalname, globaltab);
|
||||||
|
LS.setfield(globaltab, "__global", globalname);
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
void luaopen_lpx_globaldb(lua_State *L) {
|
||||||
|
LuaStack::reg(L, 0, "global", LuaArgCheck<1, lpx_globaldb_global>);
|
||||||
|
}
|
||||||
19
luprex/syscpp/globaldb.hpp
Normal file
19
luprex/syscpp/globaldb.hpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// ClassDB - code to manipulate class databases.
|
||||||
|
//
|
||||||
|
// It would have been easier to write this in Lua, but since every
|
||||||
|
// lua module in the system depends on it, it's safer to have it
|
||||||
|
// preloaded before we even open any of the lua files.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GLOBALDB_HPP
|
||||||
|
#define GLOBALDB_HPP
|
||||||
|
|
||||||
|
#include "luastack.hpp"
|
||||||
|
|
||||||
|
int lpx_globaldb_global(lua_State *L);
|
||||||
|
|
||||||
|
void luaopen_lpx_globaldb(lua_State *L);
|
||||||
|
|
||||||
|
#endif // GLOBALDB_HPP
|
||||||
|
|
||||||
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
#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) {
|
|
||||||
LpxArg db, classname;
|
|
||||||
LpxRet classtab;
|
|
||||||
LpxVar action;
|
|
||||||
LpxStackManager LSM(L, db, classname, classtab, action);
|
|
||||||
|
|
||||||
LSM.checknometa(db);
|
|
||||||
luaL_checktype(L, classname, LUA_TSTRING);
|
|
||||||
LSM.rawget(classtab, db, classname);
|
|
||||||
if (lua_istable(L, classtab)) {
|
|
||||||
return LSM.retval();
|
|
||||||
} else if (!lua_isnil(L, classtab)) {
|
|
||||||
luaL_error(L, "%s is not a class", lua_tostring(L, classname));
|
|
||||||
}
|
|
||||||
LSM.newtable(classtab);
|
|
||||||
LSM.rawset(db, classname, classtab);
|
|
||||||
LSM.setfield(classtab, "__index", classtab);
|
|
||||||
LSM.setfield(classtab, "__class", classname);
|
|
||||||
LSM.newtable(action);
|
|
||||||
LSM.setfield(classtab, "action", action);
|
|
||||||
return LSM.retval();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Curried version of classdb.get where db is stored in an upvalue.
|
|
||||||
//
|
|
||||||
static int lpx_classdb_class(lua_State *L) {
|
|
||||||
lua_pushvalue(L, lua_upvalueindex(1));
|
|
||||||
lua_insert(L, -2);
|
|
||||||
return lpx_classdb_get(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
int lpx_classdb_accessor(lua_State *L) {
|
|
||||||
luaL_checktype(L, -1, LUA_TTABLE);
|
|
||||||
lua_pushcclosure(L, LpxArgCheck<1, 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) {
|
|
||||||
LpxArg db;
|
|
||||||
LpxVar classname, classtab, action, key;
|
|
||||||
LpxStackManager LSM(L, db, classname, classtab, action, key);
|
|
||||||
|
|
||||||
luaL_checktype(L, db, LUA_TTABLE);
|
|
||||||
LSM.setnil(classname);
|
|
||||||
while (LSM.next(db, classname, classtab) != 0) {
|
|
||||||
if (lua_istable(L, classtab)) {
|
|
||||||
LSM.setstring(key, "action");
|
|
||||||
LSM.rawget(action, classtab, key);
|
|
||||||
if (lua_istable(L, action)) {
|
|
||||||
lua_pushvalue(L, action);
|
|
||||||
lpx_table_clear(L);
|
|
||||||
} else {
|
|
||||||
LSM.newtable(action);
|
|
||||||
}
|
|
||||||
lua_pushvalue(L, classtab);
|
|
||||||
lpx_table_clear(L);
|
|
||||||
LSM.setfield(classtab, "__index", classtab);
|
|
||||||
LSM.setfield(classtab, "__class", classname);
|
|
||||||
LSM.setfield(classtab, "action", action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LSM.retval();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a classdb. That's just an empty table.
|
|
||||||
int lpx_classdb_create(lua_State *L) {
|
|
||||||
lua_newtable(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void luaopen_lpx_classdb(lua_State *L) {
|
|
||||||
LpxStackManager::reg(L, "classdb", "get", LpxArgCheck<2, lpx_classdb_get>);
|
|
||||||
LpxStackManager::reg(L, "classdb", "accessor", LpxArgCheck<1, lpx_classdb_accessor>);
|
|
||||||
LpxStackManager::reg(L, "classdb", "reset", LpxArgCheck<1, lpx_classdb_reset>);
|
|
||||||
LpxStackManager::reg(L, "classdb", "create", LpxArgCheck<0, lpx_classdb_create>);
|
|
||||||
|
|
||||||
// Insert the global function 'class', which is a closure with an upvalue.
|
|
||||||
lua_pushstring(L, "class");
|
|
||||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
|
||||||
lpx_classdb_accessor(L);
|
|
||||||
lua_rawset(L, LUA_GLOBALSINDEX);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// ClassDB - code to manipulate class databases.
|
|
||||||
//
|
|
||||||
// It would have been easier to write this in Lua, but since every
|
|
||||||
// lua module in the system depends on it, it's safer to have it
|
|
||||||
// preloaded before we even open any of the lua files.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef LPX_CLASSDB_HPP
|
|
||||||
#define LPX_CLASSDB_HPP
|
|
||||||
|
|
||||||
#include "lpx-stack-manager.hpp"
|
|
||||||
|
|
||||||
int lpx_classdb_get(lua_State *L);
|
|
||||||
int lpx_classdb_reset(lua_State *L);
|
|
||||||
int lpx_classdb_accessor(lua_State *L);
|
|
||||||
int lpx_classdb_create(lua_State *L);
|
|
||||||
|
|
||||||
void luaopen_lpx_classdb(lua_State *L);
|
|
||||||
|
|
||||||
#endif // LPX_CLASSDB_HPP
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
#ifndef LPX_STACK_MANAGER_HPP
|
|
||||||
#define LPX_STACK_MANAGER_HPP
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "lua.h"
|
|
||||||
#include "lauxlib.h"
|
|
||||||
#include "lualib.h"
|
|
||||||
#include "luajit.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
// LpxArgCheck
|
|
||||||
//
|
|
||||||
// This is a template that, when given a lua_CFunction that doesn't
|
|
||||||
// verify the number of arguments on the stack, returns a new lua_CFunction
|
|
||||||
// that *does* verify the number of arguments on the stack.
|
|
||||||
//
|
|
||||||
// In general, your lua_CFunctions should not verify the number of
|
|
||||||
// arguments on the stack. Instead, they should assume that the stack
|
|
||||||
// contains arbitrary stuff underneath the arguments. That makes your
|
|
||||||
// lua_CFunctions callable from both C and Lua. But it is desirable to
|
|
||||||
// check arguments (only when calling from lua), so it is useful to use
|
|
||||||
// LpxArgCheck when turning a lua_CFunction into a lua closure.
|
|
||||||
//
|
|
||||||
template<int N, lua_CFunction FN>
|
|
||||||
int LpxArgCheck(lua_State *L) {
|
|
||||||
if (lua_gettop(L) != N) {
|
|
||||||
lua_pushfstring(L, "expected %d arguments, got %d", N, lua_gettop(L));
|
|
||||||
lua_error(L);
|
|
||||||
}
|
|
||||||
FN(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
// LpxSlot
|
|
||||||
//
|
|
||||||
// An LpxSlot contains a lua stack index. It is initialized by the
|
|
||||||
// LpxStackManager and contains the same index until the LpxStackManager
|
|
||||||
// is destroyed. You can convert an LpxSlot into a lua stack index
|
|
||||||
// by simply coercing it to 'int'.
|
|
||||||
//
|
|
||||||
// There are three variants of LpxSlot that you can use: LpxArg (function
|
|
||||||
// argument), LpxRet (function return value), and LpxVar (function local
|
|
||||||
// variable).
|
|
||||||
//
|
|
||||||
class LpxSlot {
|
|
||||||
private:
|
|
||||||
int index_;
|
|
||||||
LpxSlot *next_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline operator int() const {
|
|
||||||
return index_;
|
|
||||||
}
|
|
||||||
|
|
||||||
LpxSlot() {
|
|
||||||
index_ = 0;
|
|
||||||
next_ = 0;
|
|
||||||
}
|
|
||||||
friend class LpxStackManager;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LpxArg : public LpxSlot {};
|
|
||||||
class LpxRet : public LpxSlot {};
|
|
||||||
class LpxVar : public LpxSlot {};
|
|
||||||
|
|
||||||
class LpxStackManager {
|
|
||||||
private:
|
|
||||||
using SSList = LpxSlot*;
|
|
||||||
|
|
||||||
SSList arg_;
|
|
||||||
SSList ret_;
|
|
||||||
SSList var_;
|
|
||||||
|
|
||||||
int argtop_;
|
|
||||||
int gaptop_;
|
|
||||||
int vartop_;
|
|
||||||
int rettop_;
|
|
||||||
int finaltop_;
|
|
||||||
|
|
||||||
int narg_;
|
|
||||||
int ngap_;
|
|
||||||
int nvar_;
|
|
||||||
int nret_;
|
|
||||||
|
|
||||||
lua_State *L_;
|
|
||||||
|
|
||||||
static int sscount(SSList l) {
|
|
||||||
int total = 0;
|
|
||||||
while (l != 0) {
|
|
||||||
total += 1;
|
|
||||||
l = l->next_;
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ck() {
|
|
||||||
if (lua_gettop(L_) != rettop_) {
|
|
||||||
lua_pushstring(L_, "LpxStackManager: stack is not right");
|
|
||||||
lua_error(L_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class... SS>
|
|
||||||
void record(LpxArg &v, SS & ... stackslots)
|
|
||||||
{
|
|
||||||
v.next_ = arg_; arg_ = &v;
|
|
||||||
record(stackslots...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class... SS>
|
|
||||||
void record(LpxRet &v, SS & ... stackslots)
|
|
||||||
{
|
|
||||||
v.next_ = ret_; ret_ = &v;
|
|
||||||
record(stackslots...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class... SS>
|
|
||||||
void record(LpxVar &v, SS & ... stackslots)
|
|
||||||
{
|
|
||||||
v.next_ = var_; var_ = &v;
|
|
||||||
record(stackslots...);
|
|
||||||
}
|
|
||||||
|
|
||||||
void record()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<class... SS>
|
|
||||||
LpxStackManager(lua_State *L, SS & ... stackslots) {
|
|
||||||
L_ = L;
|
|
||||||
arg_ = 0;
|
|
||||||
ret_ = 0;
|
|
||||||
var_ = 0;
|
|
||||||
|
|
||||||
record(stackslots...);
|
|
||||||
|
|
||||||
int narg = sscount(arg_);
|
|
||||||
int nvar = sscount(var_);
|
|
||||||
int nret = sscount(ret_);
|
|
||||||
int ngap = (nret - nvar - narg);
|
|
||||||
if (ngap < 0) ngap = 0;
|
|
||||||
|
|
||||||
argtop_ = lua_gettop(L_);
|
|
||||||
gaptop_ = argtop_ + ngap;
|
|
||||||
vartop_ = gaptop_ + nvar;
|
|
||||||
rettop_ = vartop_ + nret;
|
|
||||||
finaltop_ = argtop_ - narg + nret;
|
|
||||||
|
|
||||||
int i = argtop_;
|
|
||||||
for (LpxSlot *v = arg_; v != 0; v = v->next_) {
|
|
||||||
v->index_ = i;
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
i = vartop_;
|
|
||||||
for (LpxSlot *v = var_; v != 0; v = v->next_) {
|
|
||||||
v->index_ = i;
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
i = rettop_;
|
|
||||||
for (LpxSlot *v = ret_; v != 0; v = v->next_) {
|
|
||||||
v->index_ = i;
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize vars and rets to NIL.
|
|
||||||
// I could conceivably do a Lpx_settop instead.
|
|
||||||
for (int i = argtop_; i < rettop_; i++) {
|
|
||||||
lua_pushnil(L_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~LpxStackManager() {
|
|
||||||
ck();
|
|
||||||
int i = finaltop_;
|
|
||||||
for (LpxSlot *v = ret_; v != 0; v = v->next_) {
|
|
||||||
lua_replace(L_, i);
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
lua_settop(L_, finaltop_);
|
|
||||||
}
|
|
||||||
|
|
||||||
int retval() {
|
|
||||||
return rettop_ - vartop_;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reg(lua_State *L, const char *classname, const char *funcname, lua_CFunction fn) {
|
|
||||||
luaL_Reg reg;
|
|
||||||
reg.name = funcname;
|
|
||||||
reg.func = fn;
|
|
||||||
luaL_register(L, classname, ®);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// The following functions are 'register-machine' variants
|
|
||||||
// of the core lua functions. They do not read from the stack,
|
|
||||||
// or leave results on the stack.
|
|
||||||
|
|
||||||
void setnil(int target) {
|
|
||||||
lua_pushnil(L_);
|
|
||||||
lua_replace(L_, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setstring(int target, const char *str) {
|
|
||||||
lua_pushstring(L_, str);
|
|
||||||
lua_replace(L_, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setlstring(int target, const char *str, int len) {
|
|
||||||
lua_pushlstring(L_, str, len);
|
|
||||||
lua_replace(L_, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setnumber(int target, double value) {
|
|
||||||
lua_pushnumber(L_, value);
|
|
||||||
lua_replace(L_, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
int next(int tab, int key, int value) {
|
|
||||||
lua_pushvalue(L_, key);
|
|
||||||
int ret = lua_next(L_, tab);
|
|
||||||
if (ret != 0) {
|
|
||||||
lua_replace(L_, value);
|
|
||||||
lua_replace(L_, key);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rawget(int target, int tab, int key) {
|
|
||||||
lua_pushvalue(L_, key);
|
|
||||||
lua_rawget(L_, tab);
|
|
||||||
lua_replace(L_, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rawset(int tab, int key, int value) {
|
|
||||||
lua_pushvalue(L_, key);
|
|
||||||
lua_pushvalue(L_, value);
|
|
||||||
lua_rawset(L_, tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setfield(int tab, const char *field, int value) {
|
|
||||||
lua_pushvalue(L_, value);
|
|
||||||
lua_setfield(L_, tab, field);
|
|
||||||
}
|
|
||||||
|
|
||||||
void getfield(int target, int tab, const char *field) {
|
|
||||||
lua_getfield(L_, tab, field);
|
|
||||||
lua_replace(L_, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void newtable(int target) {
|
|
||||||
lua_newtable(L_);
|
|
||||||
lua_replace(L_, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void checknometa(int index) {
|
|
||||||
if (lua_istable(L_, index)) {
|
|
||||||
if (!lua_getmetatable(L_, index)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
luaL_error(L_, "expected simple table with no metatable");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // LPX_STACK_MANAGER_HPP
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#include "lpx-table.hpp"
|
|
||||||
|
|
||||||
// Clear the table. Removes metatable and all key-value pairs.
|
|
||||||
int lpx_table_clear(lua_State *L) {
|
|
||||||
LpxArg tab;
|
|
||||||
LpxStackManager LSM(L, tab);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return LSM.retval();
|
|
||||||
}
|
|
||||||
|
|
||||||
int lpx_table_coerce(lua_State *L) {
|
|
||||||
if (!lua_istable(L, -1)) {
|
|
||||||
lua_pop(L, 1);
|
|
||||||
lua_newtable(L);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void luaopen_lpx_table (lua_State *L) {
|
|
||||||
LpxStackManager::reg(L, "table", "clear", LpxArgCheck<1, lpx_table_clear>);
|
|
||||||
LpxStackManager::reg(L, "table", "coerce", LpxArgCheck<1, lpx_table_coerce>);
|
|
||||||
}
|
|
||||||
92
luprex/syscpp/luastack.cpp
Normal file
92
luprex/syscpp/luastack.cpp
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#include "luastack.hpp"
|
||||||
|
|
||||||
|
LuaSlot LuaRegistry(LUA_REGISTRYINDEX);
|
||||||
|
LuaSlot LuaGlobals(LUA_GLOBALSINDEX);
|
||||||
|
|
||||||
|
void LuaStack::count_slots_finalize(int narg, int nvar, int nret) {
|
||||||
|
narg_ = narg;
|
||||||
|
nret_ = nret;
|
||||||
|
nvar_ = nvar;
|
||||||
|
ngap_ = nret - nvar - narg;
|
||||||
|
if (ngap_ < 0) ngap_ = 0;
|
||||||
|
|
||||||
|
int argtop = lua_gettop(L_);
|
||||||
|
argpos_ = argtop + 1 - narg_;
|
||||||
|
gappos_ = argpos_ + narg_;
|
||||||
|
varpos_ = gappos_ + ngap_;
|
||||||
|
retpos_ = varpos_ + nvar_;
|
||||||
|
|
||||||
|
rettop_ = retpos_ + nret_ - 1;
|
||||||
|
finaltop_ = argpos_ + nret_ - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaStack::clear_frame() {
|
||||||
|
lua_settop(L_, varpos_ - 1);
|
||||||
|
for (int i = 0; i < nvar_ + nret_; i++) {
|
||||||
|
lua_pushnil(L_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int LuaStack::result() {
|
||||||
|
lua_settop(L_, rettop_);
|
||||||
|
int i = finaltop_;
|
||||||
|
for (int j = 0; j < nret_; j++) {
|
||||||
|
lua_replace(L_, i);
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
lua_settop(L_, finaltop_);
|
||||||
|
return nret_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaStack::reg(lua_State *L, const char *classname, const char *funcname, lua_CFunction fn) {
|
||||||
|
int top = lua_gettop(L);
|
||||||
|
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||||
|
luaL_Reg reg;
|
||||||
|
reg.name = funcname;
|
||||||
|
reg.func = fn;
|
||||||
|
luaL_register(L, classname, ®);
|
||||||
|
lua_settop(L, top);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaStack::setnil(LuaSlot target) const {
|
||||||
|
lua_pushnil(L_);
|
||||||
|
lua_replace(L_, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaStack::setboolean(LuaSlot target, bool b) const {
|
||||||
|
lua_pushboolean(L_, b ? 1 : 0);
|
||||||
|
lua_replace(L_, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaStack::setboolean(LuaSlot target, int b) const {
|
||||||
|
lua_pushboolean(L_, b);
|
||||||
|
lua_replace(L_, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaStack::setstring(LuaSlot target, const char *str) const {
|
||||||
|
lua_pushstring(L_, str);
|
||||||
|
lua_replace(L_, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaStack::setstring(LuaSlot target, const std::string &str) const {
|
||||||
|
lua_pushlstring(L_, str.c_str(), str.size());
|
||||||
|
lua_replace(L_, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaStack::setnumber(LuaSlot target, double value) const {
|
||||||
|
lua_pushnumber(L_, value);
|
||||||
|
lua_replace(L_, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LuaStack::tostring(LuaSlot s) {
|
||||||
|
size_t len;
|
||||||
|
const char *str = lua_tolstring(L_, s, &len);
|
||||||
|
return std::string(str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LuaStack::checkstring(LuaSlot s) {
|
||||||
|
size_t len;
|
||||||
|
const char *str = luaL_checklstring(L_, s, &len);
|
||||||
|
return std::string(str, len);
|
||||||
|
}
|
||||||
|
|
||||||
264
luprex/syscpp/luastack.hpp
Normal file
264
luprex/syscpp/luastack.hpp
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#ifndef LUASTACK_HPP
|
||||||
|
#define LUASTACK_HPP
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "lua.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "lualib.h"
|
||||||
|
#include "luajit.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// LuaArgCheck
|
||||||
|
//
|
||||||
|
// This is a template that, when given a lua_CFunction that doesn't
|
||||||
|
// verify the number of arguments on the stack, returns a new lua_CFunction
|
||||||
|
// that *does* verify the number of arguments on the stack.
|
||||||
|
//
|
||||||
|
// In general, your lua_CFunctions should not verify the number of
|
||||||
|
// arguments on the stack. Instead, they should assume that the stack
|
||||||
|
// contains arbitrary stuff underneath the arguments. That makes your
|
||||||
|
// lua_CFunctions callable from both C and Lua. But it is desirable to
|
||||||
|
// check arguments (only when calling from lua), so it is useful to use
|
||||||
|
// LuaArgCheck when turning a lua_CFunction into a lua closure.
|
||||||
|
//
|
||||||
|
template<int N, lua_CFunction FN>
|
||||||
|
int LuaArgCheck(lua_State *L) {
|
||||||
|
if (lua_gettop(L) != N) {
|
||||||
|
lua_pushfstring(L, "expected %d arguments, got %d", N, lua_gettop(L));
|
||||||
|
lua_error(L);
|
||||||
|
}
|
||||||
|
FN(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LuaSlot
|
||||||
|
//
|
||||||
|
// An LuaSlot contains a lua stack index. It is initialized by the
|
||||||
|
// LuaStack and contains the same index until the LuaStack
|
||||||
|
// is destroyed. You can convert an LuaSlot into a lua stack index
|
||||||
|
// by simply coercing it to 'int'.
|
||||||
|
//
|
||||||
|
// There are three variants of LuaSlot that you can use: LuaArg (function
|
||||||
|
// argument), LuaRet (function return value), and LuaVar (function local
|
||||||
|
// variable).
|
||||||
|
//
|
||||||
|
class LuaSlot {
|
||||||
|
protected:
|
||||||
|
int index_;
|
||||||
|
private:
|
||||||
|
inline operator int() const {
|
||||||
|
return index_;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
LuaSlot(int n) {
|
||||||
|
index_ = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaSlot() {
|
||||||
|
index_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index() const {
|
||||||
|
return index_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class LuaStack;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LuaArg : public LuaSlot {};
|
||||||
|
class LuaRet : public LuaSlot {};
|
||||||
|
class LuaVar : public LuaSlot {};
|
||||||
|
|
||||||
|
extern LuaSlot LuaRegistry;
|
||||||
|
extern LuaSlot LuaGlobals;
|
||||||
|
|
||||||
|
class LuaUpvalue : public LuaSlot {
|
||||||
|
public:
|
||||||
|
LuaUpvalue(int n) {
|
||||||
|
index_ = lua_upvalueindex(n);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LuaStack {
|
||||||
|
private:
|
||||||
|
int narg_;
|
||||||
|
int ngap_;
|
||||||
|
int nvar_;
|
||||||
|
int nret_;
|
||||||
|
|
||||||
|
int argpos_;
|
||||||
|
int gappos_;
|
||||||
|
int varpos_;
|
||||||
|
int retpos_;
|
||||||
|
|
||||||
|
int rettop_;
|
||||||
|
int finaltop_;
|
||||||
|
|
||||||
|
lua_State *L_;
|
||||||
|
|
||||||
|
template<int NARG, int NVAR, int NRET, class... SS>
|
||||||
|
void count_slots(LuaArg &v, SS & ... stackslots)
|
||||||
|
{
|
||||||
|
count_slots<NARG+1, NRET, NVAR>(stackslots...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int NARG, int NVAR, int NRET, class... SS>
|
||||||
|
void count_slots(LuaVar &v, SS & ... stackslots)
|
||||||
|
{
|
||||||
|
count_slots<NARG, NVAR+1, NRET>(stackslots...);
|
||||||
|
}
|
||||||
|
template<int NARG, int NVAR, int NRET, class... SS>
|
||||||
|
void count_slots(LuaRet &v, SS & ... stackslots)
|
||||||
|
{
|
||||||
|
count_slots<NARG, NVAR, NRET+1>(stackslots...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int NARG, int NVAR, int NRET>
|
||||||
|
void count_slots() {
|
||||||
|
count_slots_finalize(NARG, NVAR, NRET);
|
||||||
|
}
|
||||||
|
|
||||||
|
void count_slots_finalize(int narg, int nvar, int nret);
|
||||||
|
|
||||||
|
template<class... SS>
|
||||||
|
void assign_slots(int argp, int varp, int retp, LuaArg &v, SS & ... stackslots) {
|
||||||
|
v.index_ = argp;
|
||||||
|
assign_slots(argp + 1, varp, retp, stackslots...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... SS>
|
||||||
|
void assign_slots(int argp, int varp, int retp, LuaVar &v, SS & ... stackslots) {
|
||||||
|
v.index_ = varp;
|
||||||
|
assign_slots(argp, varp+1, retp, stackslots...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... SS>
|
||||||
|
void assign_slots(int argp, int varp, int retp, LuaRet &v, SS & ... stackslots) {
|
||||||
|
v.index_ = retp;
|
||||||
|
assign_slots(argp, varp, retp+1, stackslots...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void assign_slots(int argp, int varp, int retp) {}
|
||||||
|
|
||||||
|
void clear_frame();
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<class... SS>
|
||||||
|
LuaStack(lua_State *L, SS & ... stackslots) {
|
||||||
|
L_ = L;
|
||||||
|
count_slots<0, 0, 0>(stackslots...);
|
||||||
|
assign_slots(argpos_, varpos_, retpos_, stackslots...);
|
||||||
|
clear_frame();
|
||||||
|
}
|
||||||
|
|
||||||
|
~LuaStack() {};
|
||||||
|
|
||||||
|
int result();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void reg(lua_State *L, const char *classname, const char *funcname, lua_CFunction fn);
|
||||||
|
|
||||||
|
void setnil(LuaSlot target) const;
|
||||||
|
void setboolean(LuaSlot target, bool b) const;
|
||||||
|
void setboolean(LuaSlot target, int b) const;
|
||||||
|
void setstring(LuaSlot target, const char *str) const;
|
||||||
|
void setstring(LuaSlot target, const std::string &str) const;
|
||||||
|
void setnumber(LuaSlot target, double value) const;
|
||||||
|
|
||||||
|
bool isboolean(LuaSlot s) { return lua_isboolean(L_, s); }
|
||||||
|
bool iscfunction(LuaSlot s) { return lua_iscfunction(L_, s); }
|
||||||
|
bool isfunction(LuaSlot s) { return lua_isfunction(L_, s); }
|
||||||
|
bool islightuserdata(LuaSlot s) { return lua_islightuserdata(L_, s); }
|
||||||
|
bool isnil(LuaSlot s) { return lua_isnil(L_, s); }
|
||||||
|
bool isnumber(LuaSlot s) { return lua_isnumber(L_, s); }
|
||||||
|
bool isstring(LuaSlot s) { return lua_isstring(L_, s); }
|
||||||
|
bool istable(LuaSlot s) { return lua_istable(L_, s); }
|
||||||
|
bool isthread(LuaSlot s) { return lua_isthread(L_, s); }
|
||||||
|
bool isuserdata(LuaSlot s) { return lua_isuserdata(L_, s); }
|
||||||
|
|
||||||
|
bool toboolean(LuaSlot s) { return lua_toboolean(L_, s); }
|
||||||
|
int tointeger(LuaSlot s) { return lua_tointeger(L_, s); }
|
||||||
|
double tonumber(LuaSlot s) { return lua_tonumber(L_, s); }
|
||||||
|
std::string tostring(LuaSlot s);
|
||||||
|
lua_State *tothread(LuaSlot s) { return lua_tothread(L_, s); }
|
||||||
|
|
||||||
|
int checkint(LuaSlot s) { return luaL_checkint(L_, s); }
|
||||||
|
long checklong(LuaSlot s) { return luaL_checklong(L_, s); }
|
||||||
|
double checknumber(LuaSlot s) { return luaL_checknumber(L_, s); }
|
||||||
|
std::string checkstring(LuaSlot s);
|
||||||
|
void checktype(LuaSlot s, int t) { return luaL_checktype(L_, s, t); }
|
||||||
|
|
||||||
|
void clearmetatable(LuaSlot tab) {
|
||||||
|
lua_pushnil(L_);
|
||||||
|
lua_setmetatable(L_, tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setmetatable(LuaSlot tab, LuaSlot mt) {
|
||||||
|
lua_pushvalue(L_, mt);
|
||||||
|
lua_setmetatable(L_, tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rawget(LuaSlot target, LuaSlot tab, LuaSlot key) const {
|
||||||
|
lua_pushvalue(L_, key);
|
||||||
|
lua_rawget(L_, tab);
|
||||||
|
lua_replace(L_, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rawset(LuaSlot tab, LuaSlot key, LuaSlot value) const {
|
||||||
|
lua_pushvalue(L_, key);
|
||||||
|
lua_pushvalue(L_, value);
|
||||||
|
lua_rawset(L_, tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int next(LuaSlot tab, LuaSlot key, LuaSlot value) const {
|
||||||
|
lua_pushvalue(L_, key);
|
||||||
|
int ret = lua_next(L_, tab);
|
||||||
|
if (ret != 0) {
|
||||||
|
lua_replace(L_, value);
|
||||||
|
lua_replace(L_, key);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setfield(LuaSlot tab, const char *field, LuaSlot value) const {
|
||||||
|
lua_pushvalue(L_, value);
|
||||||
|
lua_setfield(L_, tab, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setfield(LuaSlot tab, const char *field, const char *value) const {
|
||||||
|
lua_pushstring(L_, value);
|
||||||
|
lua_setfield(L_, tab, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setfield(LuaSlot tab, const char *field, const std::string &value) const {
|
||||||
|
lua_pushlstring(L_, value.c_str(), value.size());
|
||||||
|
lua_setfield(L_, tab, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getfield(LuaSlot target, LuaSlot tab, const char *field) const {
|
||||||
|
lua_getfield(L_, tab, field);
|
||||||
|
lua_replace(L_, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void newtable(LuaSlot target) const {
|
||||||
|
lua_newtable(L_);
|
||||||
|
lua_replace(L_, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checknometa(LuaSlot index) const {
|
||||||
|
if (lua_istable(L_, index)) {
|
||||||
|
if (!lua_getmetatable(L_, index)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
luaL_error(L_, "expected simple table with no metatable");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LUASTACK_HPP
|
||||||
@@ -12,10 +12,11 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "lpx-stack-manager.hpp"
|
#include "luastack.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include "lpx-table.hpp"
|
#include "table.hpp"
|
||||||
#include "lpx-classdb.hpp"
|
#include "globaldb.hpp"
|
||||||
|
#include "source.hpp"
|
||||||
|
|
||||||
|
|
||||||
// Add another error status.
|
// Add another error status.
|
||||||
@@ -174,7 +175,7 @@ static void dotty(lua_State *L)
|
|||||||
report(L, status);
|
report(L, status);
|
||||||
if (status == LUA_OK && lua_gettop(L) > 0)
|
if (status == LUA_OK && lua_gettop(L) > 0)
|
||||||
{ /* any result to print? */
|
{ /* any result to print? */
|
||||||
lua_getglobal(L, "iprint");
|
lua_getglobal(L, "pprint");
|
||||||
if (lua_isnil(L, -1)) {
|
if (lua_isnil(L, -1)) {
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
lua_getglobal(L, "print");
|
lua_getglobal(L, "print");
|
||||||
@@ -193,26 +194,6 @@ static void dotty(lua_State *L)
|
|||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void loadmain(lua_State *L)
|
|
||||||
{
|
|
||||||
util::stringvec filenames = util::trim_and_uncomment(util::read_lines("syslua/control.lst"));
|
|
||||||
if (filenames.empty()) {
|
|
||||||
lua_pushfstring(L, "Cannot read syslua/control.lst");
|
|
||||||
lua_error(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &fn : filenames) {
|
|
||||||
std::string full = "syslua/" + fn;
|
|
||||||
int status = luaL_loadfilex(L, full.c_str(), NULL);
|
|
||||||
if (status == LUA_OK) {
|
|
||||||
status = docall(L, 0, 0);
|
|
||||||
}
|
|
||||||
if (status != LUA_OK) {
|
|
||||||
lua_error(L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pmain(lua_State *L)
|
static int pmain(lua_State *L)
|
||||||
{
|
{
|
||||||
globalL = L;
|
globalL = L;
|
||||||
@@ -231,11 +212,10 @@ static int pmain(lua_State *L)
|
|||||||
// luaopen_debug(L); // Not safe for the sandbox.
|
// luaopen_debug(L); // Not safe for the sandbox.
|
||||||
luaopen_bit(L);
|
luaopen_bit(L);
|
||||||
// luaopen_jit(L); // Don't know what it's for.
|
// luaopen_jit(L); // Don't know what it's for.
|
||||||
luaopen_lpx_classdb(L);
|
luaopen_lpx_globaldb(L);
|
||||||
|
luaopen_lpx_source(L);
|
||||||
lua_gc(L, LUA_GCRESTART, -1);
|
lua_gc(L, LUA_GCRESTART, -1);
|
||||||
|
|
||||||
loadmain(L);
|
|
||||||
|
|
||||||
dotty(L);
|
dotty(L);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
225
luprex/syscpp/source.cpp
Normal file
225
luprex/syscpp/source.cpp
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "util.hpp"
|
||||||
|
#include "table.hpp"
|
||||||
|
#ifndef WIN32
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#include "luastack.hpp"
|
||||||
|
#ifdef WIN32
|
||||||
|
#define stat _stat
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// The source database is a lua table that maps filenames
|
||||||
|
// to file info. A file info is a lua table containing:
|
||||||
|
//
|
||||||
|
// name: filename as a string
|
||||||
|
// mtime: file modification time as human-readable string
|
||||||
|
// code: the entire contents of the source file as a string
|
||||||
|
// error: a syntax error message, or nil
|
||||||
|
// hash: 128-bit hash of the code
|
||||||
|
// closure: a lua closure, the compiled file
|
||||||
|
// sequence: the position of the file in control.lst
|
||||||
|
//
|
||||||
|
// Operations on a source DB:
|
||||||
|
//
|
||||||
|
// source.peek()
|
||||||
|
// - peek at the source database, for inspection purposes.
|
||||||
|
// source.read()
|
||||||
|
// - return a new source, reusing info from old.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static std::string get_mtime(const std::string &fn) {
|
||||||
|
struct stat result;
|
||||||
|
if(stat(fn.c_str(), &result)==0)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << result.st_mtime;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string get_code(const std::string &fn) {
|
||||||
|
std::ifstream fs(fn);
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << fs.rdbuf();
|
||||||
|
return buffer.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
int lpx_source_updatefile(lua_State *L) {
|
||||||
|
LuaArg source, fn;
|
||||||
|
LuaRet info;
|
||||||
|
LuaVar mtime, null;
|
||||||
|
LuaStack LS(L, source, fn, info, mtime, null);
|
||||||
|
|
||||||
|
// Get the existing info table from the source DB.
|
||||||
|
if (LS.istable(source)) {
|
||||||
|
LS.rawget(info, source, fn);
|
||||||
|
if (!LS.istable(info)) {
|
||||||
|
LS.newtable(info);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LS.newtable(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the file modification is wrong, update
|
||||||
|
// these fields: code, mtime, closure, error
|
||||||
|
// Otherwise, update nothing.
|
||||||
|
std::string cfn = LS.tostring(fn);
|
||||||
|
LS.getfield(mtime, info, "mtime");
|
||||||
|
std::string old_mtime;
|
||||||
|
if (LS.isstring(mtime)) {
|
||||||
|
old_mtime = LS.tostring(mtime);
|
||||||
|
}
|
||||||
|
std::cerr << "Probing " << cfn << std::endl;
|
||||||
|
std::string new_mtime = get_mtime("syslua/" + cfn);
|
||||||
|
LS.setnil(null);
|
||||||
|
if ((old_mtime == "") || (old_mtime != new_mtime)) {
|
||||||
|
std::cerr << "Rereading " << cfn << std::endl;
|
||||||
|
std::string ccode = get_code("syslua/" + cfn);
|
||||||
|
LS.setfield(info, "name", fn);
|
||||||
|
LS.setfield(info, "mtime", new_mtime);
|
||||||
|
LS.setfield(info, "code", ccode);
|
||||||
|
if ((new_mtime == "")||(ccode == "")) {
|
||||||
|
LS.setfield(info, "error", "cannot read source file");
|
||||||
|
LS.setfield(info, "closure", null);
|
||||||
|
} else if (luaL_loadbuffer(L, ccode.c_str(), ccode.size(), cfn.c_str()) == 0) {
|
||||||
|
lua_setfield(L, info.index(), "closure");
|
||||||
|
LS.setfield(info, "error", null);
|
||||||
|
} else {
|
||||||
|
lua_setfield(L, info.index(), "error");
|
||||||
|
LS.setfield(info, "closure", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
int lpx_source_updateall(lua_State *L) {
|
||||||
|
LuaArg source;
|
||||||
|
LuaRet newdb;
|
||||||
|
LuaVar info, fn, seq;
|
||||||
|
LuaStack LS(L, source, newdb, info, fn, seq);
|
||||||
|
|
||||||
|
// Read the list of filenames.
|
||||||
|
std::string ctrl = "syslua/control.lst";
|
||||||
|
util::stringvec filenames = util::trim_and_uncomment(util::read_lines(ctrl));
|
||||||
|
if (filenames.empty()) {
|
||||||
|
luaL_error(L, "cannot read source database control.lst");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the files one by one.
|
||||||
|
LS.newtable(newdb);
|
||||||
|
for (int i = 0; i < filenames.size(); i++) {
|
||||||
|
LS.setstring(fn, filenames[i]);
|
||||||
|
|
||||||
|
// Call source_updatefile to get the updated info for one file.
|
||||||
|
lua_pushvalue(L, source.index());
|
||||||
|
lua_pushvalue(L, fn.index());
|
||||||
|
lpx_source_updatefile(L);
|
||||||
|
lua_replace(L, info.index());
|
||||||
|
|
||||||
|
// Insert the sequence number and put finalized info into the new database.
|
||||||
|
LS.setnumber(seq, i + 1);
|
||||||
|
LS.setfield(info, "sequence", seq);
|
||||||
|
LS.rawset(newdb, fn, info);
|
||||||
|
}
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a class from the class database.
|
||||||
|
//
|
||||||
|
// 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_source_class(lua_State *L) {
|
||||||
|
LuaArg classname;
|
||||||
|
LuaRet classtab;
|
||||||
|
LuaVar classdb, action;
|
||||||
|
LuaStack LS(L, classname, classtab, classdb, action);
|
||||||
|
|
||||||
|
LS.checktype(classname, LUA_TSTRING);
|
||||||
|
|
||||||
|
// Get a pointer to the classdb.
|
||||||
|
LS.getfield(classdb, LUA_REGISTRYINDEX, "classdb");
|
||||||
|
if (!LS.istable(classdb)) {
|
||||||
|
LS.newtable(classdb);
|
||||||
|
LS.setfield(LUA_REGISTRYINDEX, "classdb", classdb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the classtab from the classdb, sanity check it.
|
||||||
|
LS.rawget(classtab, classdb, classname);
|
||||||
|
if (LS.istable(classtab)) {
|
||||||
|
return LS.result();
|
||||||
|
} else if (!LS.isnil(classtab)) {
|
||||||
|
luaL_error(L, "%s is not a class", LS.tostring(classname).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new classtab and store it in the classdb.
|
||||||
|
LS.newtable(classtab);
|
||||||
|
LS.rawset(classdb, classname, classtab);
|
||||||
|
LS.setfield(classtab, "__index", classtab);
|
||||||
|
LS.setfield(classtab, "__class", classname);
|
||||||
|
LS.newtable(action);
|
||||||
|
LS.setfield(classtab, "action", action);
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
int lpx_source_resetclasses(lua_State *L) {
|
||||||
|
LuaVar classdb, classname, classtab, action, key;
|
||||||
|
LuaStack LS(L, classname, classtab, classdb, action, key);
|
||||||
|
|
||||||
|
// Get a pointer to the classdb.
|
||||||
|
LS.getfield(classdb, LUA_REGISTRYINDEX, "classdb");
|
||||||
|
if (!LS.istable(classdb)) {
|
||||||
|
LS.newtable(classdb);
|
||||||
|
LS.setfield(LUA_REGISTRYINDEX, "classdb", classdb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the classdb, clearing it.
|
||||||
|
LS.setnil(classname);
|
||||||
|
while (LS.next(classdb, classname, classtab) != 0) {
|
||||||
|
if (LS.istable(classtab)) {
|
||||||
|
LS.setstring(key, "action");
|
||||||
|
LS.rawget(action, classtab, key);
|
||||||
|
if (LS.istable(action)) {
|
||||||
|
lua_pushvalue(L, action.index());
|
||||||
|
lpx_table_clear(L);
|
||||||
|
} else {
|
||||||
|
LS.newtable(action);
|
||||||
|
}
|
||||||
|
lua_pushvalue(L, classtab.index());
|
||||||
|
lpx_table_clear(L);
|
||||||
|
LS.setfield(classtab, "__index", classtab);
|
||||||
|
LS.setfield(classtab, "__class", classname);
|
||||||
|
LS.setfield(classtab, "action", action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
void luaopen_lpx_source(lua_State *L) {
|
||||||
|
LuaStack::reg(L, "source", "updatefile", LuaArgCheck<2, lpx_source_updatefile>);
|
||||||
|
LuaStack::reg(L, "source", "updateall", LuaArgCheck<1, lpx_source_updateall>);
|
||||||
|
LuaStack::reg(L, "source", "class", LuaArgCheck<1, lpx_source_class>);
|
||||||
|
LuaStack::reg(L, "source", "resetclasses", LuaArgCheck<0, lpx_source_resetclasses>);
|
||||||
|
LuaStack::reg(L, 0 , "class", LuaArgCheck<1, lpx_source_class>);
|
||||||
|
}
|
||||||
21
luprex/syscpp/source.hpp
Normal file
21
luprex/syscpp/source.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// ClassDB - code to manipulate class databases.
|
||||||
|
//
|
||||||
|
// It would have been easier to write this in Lua, but since every
|
||||||
|
// lua module in the system depends on it, it's safer to have it
|
||||||
|
// preloaded before we even open any of the lua files.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SOURCE_HPP
|
||||||
|
#define SOURCE_HPP
|
||||||
|
|
||||||
|
#include "luastack.hpp"
|
||||||
|
|
||||||
|
int lpx_source_update(lua_State *L);
|
||||||
|
int lpx_source_class(lua_State *L);
|
||||||
|
int lpx_source_resetclasses(lua_State *L);
|
||||||
|
|
||||||
|
void luaopen_lpx_source(lua_State *L);
|
||||||
|
|
||||||
|
#endif // SOURCE_HPP
|
||||||
|
|
||||||
|
|
||||||
34
luprex/syscpp/table.cpp
Normal file
34
luprex/syscpp/table.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include "table.hpp"
|
||||||
|
|
||||||
|
// Clear the table. Removes metatable and all key-value pairs.
|
||||||
|
int lpx_table_clear(lua_State *L) {
|
||||||
|
LuaArg tab;
|
||||||
|
LuaStack LS(L, tab);
|
||||||
|
|
||||||
|
LS.checktype(tab, LUA_TTABLE);
|
||||||
|
|
||||||
|
LS.clearmetatable(tab);
|
||||||
|
|
||||||
|
lua_pushnil(L);
|
||||||
|
while (lua_next(L, tab.index()) != 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.index());
|
||||||
|
}
|
||||||
|
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
int lpx_table_coerce(lua_State *L) {
|
||||||
|
if (!lua_istable(L, -1)) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
lua_newtable(L);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void luaopen_lpx_table (lua_State *L) {
|
||||||
|
LuaStack::reg(L, "table", "clear", LuaArgCheck<1, lpx_table_clear>);
|
||||||
|
LuaStack::reg(L, "table", "coerce", LuaArgCheck<1, lpx_table_coerce>);
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
#ifndef LPX_TABLE_HPP
|
#ifndef TABLE_HPP
|
||||||
#define LPX_TABLE_HPP
|
#define TABLE_HPP
|
||||||
|
|
||||||
#include "lpx-stack-manager.hpp"
|
#include "luastack.hpp"
|
||||||
|
|
||||||
int lpx_table_clear(lua_State *L);
|
int lpx_table_clear(lua_State *L);
|
||||||
int lpx_table_coerce(lua_State *L);
|
int lpx_table_coerce(lua_State *L);
|
||||||
|
|
||||||
void luaopen_lpx_table (lua_State *L);
|
void luaopen_lpx_table (lua_State *L);
|
||||||
|
|
||||||
#endif // LPX_TABLE_HPP
|
#endif // TABLE_HPP
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
#define UTIL_HPP
|
#define UTIL_HPP
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <set>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
namespace util {
|
namespace util {
|
||||||
|
|
||||||
using stringvec = std::vector<std::string>;
|
using stringvec = std::vector<std::string>;
|
||||||
|
using stringset = std::set<std::string>;
|
||||||
|
|
||||||
// trim from start
|
// trim from start
|
||||||
static inline std::string ltrim(std::string s) {
|
static inline std::string ltrim(std::string s) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
inspect.lua
|
inspect.lua
|
||||||
globaldb.lua
|
|
||||||
model.lua
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
local globaldb=class('globaldb')
|
|
||||||
|
|
||||||
function globaldb.get(db, key)
|
|
||||||
local result = db[key]
|
|
||||||
if type(result) == 'table' then
|
|
||||||
return result
|
|
||||||
else
|
|
||||||
result = {}
|
|
||||||
db[key] = result
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
function globaldb.accessor(db)
|
|
||||||
return function(key)
|
|
||||||
return globaldb.get(db, key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function globaldb.create()
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
@@ -333,7 +333,7 @@ function inspect.inspect(root, options)
|
|||||||
return table.concat(inspector.buffer)
|
return table.concat(inspector.buffer)
|
||||||
end
|
end
|
||||||
|
|
||||||
function inspect.iprint(...)
|
function inspect.pprint(...)
|
||||||
local n = select("#", ...)
|
local n = select("#", ...)
|
||||||
for i = 1,n do
|
for i = 1,n do
|
||||||
local v = select(i, ...)
|
local v = select(i, ...)
|
||||||
@@ -341,4 +341,4 @@ function inspect.iprint(...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
_G.iprint = inspect.iprint
|
_G.pprint = inspect.pprint
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
local model=class('model')
|
|
||||||
local globaldb=class('globaldb')
|
|
||||||
|
|
||||||
-- The global environment contents for a model. A list of the builtin
|
|
||||||
-- functions and classes that get installed in the global environment of
|
|
||||||
-- a world model.
|
|
||||||
--
|
|
||||||
-- This barely counts as 'sandboxing' - it's more just a mechanism to
|
|
||||||
-- keep the user from unintentionally thinking that some functionality is
|
|
||||||
-- available when it's not. For true sandboxing, you need to not import
|
|
||||||
-- functions into the lua interpreter at all.
|
|
||||||
--
|
|
||||||
-- Omitted from this list: debug, dofile, getfenv, io, jit,
|
|
||||||
-- load, loadfile, loadstring, module, newproxy, os, package,
|
|
||||||
-- pcall, require, xpcall
|
|
||||||
--
|
|
||||||
|
|
||||||
model.global_contents = { "assert", "bit", "error", "getmetatable", "inspect",
|
|
||||||
"ipairs", "math", "next", "pairs", "print", "rawequal", "rawget", "rawset",
|
|
||||||
"select", "setmetatable", "tonumber", "tostring", "type", "unpack" }
|
|
||||||
|
|
||||||
-- make a world model.
|
|
||||||
--
|
|
||||||
function model.make()
|
|
||||||
genv = {}
|
|
||||||
genv._G = genv
|
|
||||||
meta = {}
|
|
||||||
meta.class_db = classdb.create()
|
|
||||||
meta.global_db = globaldb.create()
|
|
||||||
meta.source_db = {}
|
|
||||||
meta.tangible_db = {}
|
|
||||||
meta.__newindex = function() error("world model global environment is read-only") end
|
|
||||||
-- meta.__metatable = false
|
|
||||||
genv.class = classdb.accessor(meta.class_db)
|
|
||||||
genv.global = make_global_accessor(meta.global_db)
|
|
||||||
for i,name in ipairs(world.global_contents) do
|
|
||||||
genv[name] = _G[name]
|
|
||||||
end
|
|
||||||
setmetatable(genv, meta)
|
|
||||||
return genv
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user