protot/3rdparty/luatables/luatables.cc

707 lines
16 KiB
C++

/*
* LuaTables++
* Copyright (c) 2013-2014 Martin Felis <martin@fyxs.org>.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "luatables.h"
#include <assert.h>
#include <iostream>
#include <cstdlib>
#include <vector>
#include <sstream>
#include <cmath>
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
#include <stdio.h> /* defines FILENAME_MAX */
#if defined(WIN32) || defined (_WIN32)
#include <direct.h>
#define get_current_dir _getcwd
#define DIRECTORY_SEPARATOR "\\"
#elif defined(linux) || defined (__linux) || defined(__linux__) || defined(__APPLE__)
#include <unistd.h>
#define get_current_dir getcwd
#define DIRECTORY_SEPARATOR "/"
#else
#error Platform not supported!
#endif
using namespace std;
std::string get_file_directory (const char* filename) {
string name (filename);
string result = name.substr(0, name.find_last_of (DIRECTORY_SEPARATOR) + 1);
if (result == "")
result = "./";
#if defined (WIN32) || defined (_WIN32)
else if (result.substr(1,2) != ":\\")
result = string(".\\") + result;
#else
else if (result.substr(0,string(DIRECTORY_SEPARATOR).size()) != DIRECTORY_SEPARATOR && result[0] != '.')
result = string("./") + result;
#endif
return result;
}
// char encoded serialize function that is available in plaintext in
// utils/serialize.lua. Converted using lua auto.lua serialize.lua
#include "utils/serialize.lua.h"
//
// Lua Helper Functions
//
void bail(lua_State *L, const char *msg){
std::cerr << msg << lua_tostring(L, -1) << endl;
abort();
}
void stack_print (const char *file, int line, lua_State *L) {
cout << file << ":" << line << ": stack size: " << lua_gettop(L) << endl;;
for (int i = 1; i < lua_gettop(L) + 1; i++) {
cout << file << ":" << line << ": ";
cout << i << ": ";
if (lua_istable (L, i))
cout << " table: " << lua_topointer (L, i) << endl;
else if (lua_isnumber (L, i))
cout << " number: " << lua_tonumber (L, i) << endl;
else if (lua_isuserdata (L, i)) {
void* userdata = (void*) lua_touserdata (L, i);
cout << " userdata (" << userdata << ")" << endl;
} else if (lua_isstring (L, i))
cout << " string: " << lua_tostring(L, i) << endl;
else if (lua_isfunction (L, i))
cout << " function" << endl;
else if (lua_isnil (L, i))
cout << " nil" << endl;
else
cout << " unknown: " << lua_typename (L, lua_type (L, i)) << endl;
}
}
void l_push_LuaKey (lua_State *L, const LuaKey &key) {
if (key.type == LuaKey::Integer)
lua_pushnumber (L, key.int_value);
else
lua_pushstring(L, key.string_value.c_str());
}
bool query_key_stack (lua_State *L, std::vector<LuaKey> key_stack) {
for (int i = key_stack.size() - 1; i >= 0; i--) {
// get the global value when the result of a lua expression was not
// pushed onto the stack via the return statement.
if (lua_gettop(L) == 0) {
lua_getglobal (L, key_stack[key_stack.size() - 1].string_value.c_str());
if (lua_isnil(L, -1)) {
return false;
}
continue;
}
l_push_LuaKey (L, key_stack[i]);
lua_gettable (L, -2);
// return if key is not found
if (lua_isnil(L, -1)) {
return false;
}
}
return true;
}
void create_key_stack (lua_State *L, std::vector<LuaKey> key_stack) {
for (int i = key_stack.size() - 1; i > 0; i--) {
// get the global value when the result of a lua expression was not
// pushed onto the stack via the return statement.
if (lua_gettop(L) == 0) {
lua_getglobal (L, key_stack[key_stack.size() - 1].string_value.c_str());
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setglobal(L, key_stack[key_stack.size() - 1].string_value.c_str());
}
continue;
}
l_push_LuaKey (L, key_stack[i]);
lua_pushvalue (L, -1);
lua_gettable (L, -3);
if (lua_isnil(L, -1)) {
// parent, key, nil
lua_pop(L, 1); // parent, key
lua_newtable(L); // parent, key, table
lua_insert(L, -2); // parent, table, key
lua_pushvalue(L, -2); // parent, table, key, table
lua_settable (L, -4); // parent, table
}
}
}
//
// LuaTableNode
//
std::vector<LuaKey> LuaTableNode::getKeyStack() {
std::vector<LuaKey> result;
const LuaTableNode *node_ptr = this;
do {
result.push_back (node_ptr->key);
node_ptr = node_ptr->parent;
} while (node_ptr != NULL);
return result;
}
std::string LuaTableNode::keyStackToString() {
std::vector<LuaKey> key_stack = getKeyStack();
ostringstream result_stream ("");
for (int i = key_stack.size() - 1; i >= 0; i--) {
if (key_stack[i].type == LuaKey::String)
result_stream << "[\"" << key_stack[i].string_value << "\"]";
else
result_stream << "[" << key_stack[i].int_value << "]";
}
return result_stream.str();
}
bool LuaTableNode::stackQueryValue() {
luaTable->pushRef();
lua_State *L = luaTable->L;
stackTop = lua_gettop(L);
std::vector<LuaKey> key_stack = getKeyStack();
return query_key_stack (L, key_stack);
}
void LuaTableNode::stackCreateValue() {
luaTable->pushRef();
lua_State *L = luaTable->L;
stackTop = lua_gettop(L);
std::vector<LuaKey> key_stack = getKeyStack();
create_key_stack (L, key_stack);
}
LuaTable LuaTableNode::stackQueryTable() {
luaTable->pushRef();
lua_State *L = luaTable->L;
stackTop = lua_gettop(L);
std::vector<LuaKey> key_stack = getKeyStack();
if (!query_key_stack (L, key_stack)) {
std::cerr << "Error: could not query table " << key << "." << std::endl;
abort();
}
return LuaTable::fromLuaState (L);
}
LuaTable LuaTableNode::stackCreateLuaTable() {
luaTable->pushRef();
lua_State *L = luaTable->L;
stackTop = lua_gettop(L);
std::vector<LuaKey> key_stack = getKeyStack();
create_key_stack (L, key_stack);
// create new table for the CustomType
lua_newtable(luaTable->L); // parent, CustomTable
// add table of CustomType to the parent
stackPushKey(); // parent, CustomTable, key
lua_pushvalue(luaTable->L, -2); // parent, CustomTable, key, CustomTable
lua_settable(luaTable->L, -4);
LuaTable result;
result.luaStateRef = luaTable->luaStateRef->acquire();
lua_pushvalue (result.luaStateRef->L, -1);
result.luaRef = luaL_ref (result.luaStateRef->L, LUA_REGISTRYINDEX);
lua_pop (luaTable->luaStateRef->L, 2);
return result;
}
void LuaTableNode::stackPushKey() {
l_push_LuaKey (luaTable->L, key);
}
void LuaTableNode::stackRestore() {
lua_pop (luaTable->L, lua_gettop(luaTable->L) - stackTop);
luaTable->popRef();
}
bool LuaTableNode::exists() {
bool result = true;
if (!stackQueryValue())
result = false;
stackRestore();
return result;
}
void LuaTableNode::remove() {
if (stackQueryValue()) {
lua_pop(luaTable->L, 1);
if (lua_gettop(luaTable->L) != 0) {
l_push_LuaKey (luaTable->L, key);
lua_pushnil (luaTable->L);
lua_settable (luaTable->L, -3);
} else {
lua_pushnil (luaTable->L);
lua_setglobal (luaTable->L, key.string_value.c_str());
}
}
stackRestore();
}
size_t LuaTableNode::length() {
size_t result = 0;
if (stackQueryValue()) {
#if LUA_VERSION_NUM == 501
result = lua_objlen(luaTable->L, -1);
#elif LUA_VERSION_NUM >= 502
result = lua_rawlen(luaTable->L, -1);
#endif
}
stackRestore();
return result;
}
std::vector<LuaKey> LuaTableNode::keys() {
std::vector<LuaKey> result;
if (stackQueryValue()) {
// loop over all keys
lua_pushnil(luaTable->L);
while (lua_next(luaTable->L, -2) != 0) {
if (lua_isnumber(luaTable->L, -2)) {
double number = lua_tonumber (luaTable->L, -2);
double frac;
if (modf (number, &frac) == 0) {
LuaKey key (static_cast<int>(number));
result.push_back (key);
}
} else if (lua_isstring (luaTable->L, -2)) {
LuaKey key (lua_tostring(luaTable->L, -2));
result.push_back (key);
} else {
cerr << "Warning: invalid LuaKey type for key " << lua_typename(luaTable->L, lua_type(luaTable->L, -2)) << "!" << endl;
}
lua_pop(luaTable->L, 1);
}
}
stackRestore();
return result;
}
template<> bool LuaTableNode::getDefault<bool>(const bool &default_value) {
bool result = default_value;
if (stackQueryValue()) {
result = lua_toboolean (luaTable->L, -1);
}
stackRestore();
return result;
}
template<> float LuaTableNode::getDefault<float>(const float &default_value) {
float result = default_value;
if (stackQueryValue()) {
result = static_cast<float>(lua_tonumber (luaTable->L, -1));
}
stackRestore();
return result;
}
template<> double LuaTableNode::getDefault<double>(const double &default_value) {
double result = default_value;
if (stackQueryValue()) {
result = lua_tonumber (luaTable->L, -1);
}
stackRestore();
return result;
}
template<> std::string LuaTableNode::getDefault<std::string>(const std::string &default_value) {
std::string result = default_value;
if (stackQueryValue() && lua_isstring(luaTable->L, -1)) {
result = lua_tostring (luaTable->L, -1);
}
stackRestore();
return result;
}
template<> void LuaTableNode::set<bool>(const bool &value) {
stackCreateValue();
l_push_LuaKey (luaTable->L, key);
lua_pushboolean(luaTable->L, value);
// stack: parent, key, value
lua_settable (luaTable->L, -3);
stackRestore();
}
template<> void LuaTableNode::set<float>(const float &value) {
stackCreateValue();
l_push_LuaKey (luaTable->L, key);
lua_pushnumber(luaTable->L, static_cast<double>(value));
// stack: parent, key, value
lua_settable (luaTable->L, -3);
stackRestore();
}
template<> void LuaTableNode::set<double>(const double &value) {
stackCreateValue();
l_push_LuaKey (luaTable->L, key);
lua_pushnumber(luaTable->L, value);
// stack: parent, key, value
lua_settable (luaTable->L, -3);
stackRestore();
}
template<> void LuaTableNode::set<std::string>(const std::string &value) {
stackCreateValue();
l_push_LuaKey (luaTable->L, key);
lua_pushstring(luaTable->L, value.c_str());
// stack: parent, key, value
lua_settable (luaTable->L, -3);
stackRestore();
}
//
// LuaTable
//
LuaTable::LuaTable (const LuaTable &other) :
filename (other.filename),
referencesGlobal (other.referencesGlobal) {
if (other.luaStateRef) {
luaStateRef = other.luaStateRef->acquire();
if (!referencesGlobal) {
lua_rawgeti(luaStateRef->L, LUA_REGISTRYINDEX, other.luaRef);
luaRef = luaL_ref (luaStateRef->L, LUA_REGISTRYINDEX);
}
}
}
LuaTable& LuaTable::operator= (const LuaTable &other) {
if (&other != this) {
if (luaStateRef) {
// cleanup any existing reference
luaL_unref (luaStateRef->L, LUA_REGISTRYINDEX, luaRef);
// if this is the last, delete the Lua state
int ref_count = luaStateRef->release();
if (ref_count == 0) {
if (luaStateRef->freeOnZeroRefs) {
lua_close (luaStateRef->L);
}
delete luaStateRef;
luaStateRef = NULL;
}
}
filename = other.filename;
luaStateRef = other.luaStateRef;
referencesGlobal = other.referencesGlobal;
if (other.luaStateRef) {
luaStateRef = other.luaStateRef->acquire();
if (!referencesGlobal) {
lua_rawgeti(luaStateRef->L, LUA_REGISTRYINDEX, other.luaRef);
luaRef = luaL_ref (luaStateRef->L, LUA_REGISTRYINDEX);
}
}
}
return *this;
}
LuaTable::~LuaTable() {
if (luaRef != -1) {
luaL_unref (luaStateRef->L, LUA_REGISTRYINDEX, luaRef);
}
if (luaStateRef) {
int ref_count = luaStateRef->release();
if (ref_count == 0) {
if (luaStateRef->freeOnZeroRefs) {
lua_close (luaStateRef->L);
}
delete luaStateRef;
luaStateRef = NULL;
}
}
}
int LuaTable::length() {
pushRef();
if ((lua_gettop(L) == 0) || (lua_type (L, -1) != LUA_TTABLE)) {
cerr << "Error: cannot query table length. No table on stack!" << endl;
abort();
}
size_t result = 0;
#if LUA_VERSION_NUM == 501
result = lua_objlen(L, -1);
#elif LUA_VERSION_NUM >= 502
result = lua_rawlen(L, -1);
#endif
popRef();
return result;
}
void LuaTable::pushRef() {
assert (luaStateRef);
assert (luaStateRef->L);
if (!referencesGlobal) {
lua_rawgeti(luaStateRef->L, LUA_REGISTRYINDEX, luaRef);
}
L = luaStateRef->L;
}
void LuaTable::popRef() {
if (!referencesGlobal) {
lua_pop (luaStateRef->L, 1);
}
L = NULL;
}
LuaTable LuaTable::fromFile (const char* _filename) {
LuaTable result;
result.filename = _filename;
result.luaStateRef = new LuaStateRef();
result.luaStateRef->L = luaL_newstate();
result.luaStateRef->count = 1;
luaL_openlibs (result.luaStateRef->L);
// Add the directory of _filename to package.path
result.addSearchPath(get_file_directory (_filename).c_str());
// run the file we
if (luaL_dofile (result.luaStateRef->L, _filename)) {
bail (result.luaStateRef->L, "Error running file: ");
}
result.luaRef = luaL_ref (result.luaStateRef->L, LUA_REGISTRYINDEX);
return result;
}
LuaTable LuaTable::fromLuaExpression (const char* lua_expr) {
LuaTable result;
result.luaStateRef = new LuaStateRef();
result.luaStateRef->L = luaL_newstate();
result.luaStateRef->count = 1;
luaL_openlibs (result.luaStateRef->L);
if (luaL_loadstring (result.luaStateRef->L, lua_expr)) {
bail (result.luaStateRef->L, "Error compiling expression!");
}
if (lua_pcall (result.luaStateRef->L, 0, LUA_MULTRET, 0)) {
bail (result.luaStateRef->L, "Error running expression!");
}
if (lua_gettop(result.luaStateRef->L) != 0) {
result.luaRef = luaL_ref (result.luaStateRef->L, LUA_REGISTRYINDEX);
} else {
result.referencesGlobal = true;
}
return result;
}
LuaTable LuaTable::fromLuaState (lua_State* L) {
LuaTable result;
result.luaStateRef = new LuaStateRef();
result.luaStateRef->L = L;
result.luaStateRef->count = 1;
result.luaStateRef->freeOnZeroRefs = false;
lua_pushvalue (result.luaStateRef->L, -1);
result.luaRef = luaL_ref (result.luaStateRef->L, LUA_REGISTRYINDEX);
return result;
}
void LuaTable::addSearchPath(const char* path) {
if (luaStateRef->L == NULL) {
cerr << "Error: Cannot add search path: Lua state is not initialized!" << endl;
abort();
}
lua_getglobal(luaStateRef->L, "package");
lua_getfield (luaStateRef->L, -1, "path");
if (lua_type(luaStateRef->L, -1) != LUA_TSTRING) {
cerr << "Error: could not get package.path!" << endl;
abort();
}
string package_path = lua_tostring (luaStateRef->L, -1);
package_path = package_path + string (";") + string(path) + "?.lua;";
lua_pushstring(luaStateRef->L, package_path.c_str());
lua_setfield (luaStateRef->L, -3, "path");
lua_pop(luaStateRef->L, 2);
}
std::string LuaTable::serialize() {
pushRef();
std::string result;
int current_top = lua_gettop(L);
if (lua_gettop(L) != 0) {
if (luaL_loadstring(L, serialize_lua)) {
bail (L, "Error loading serialization function: ");
}
if (lua_pcall(L, 0, 0, 0)) {
bail (L, "Error compiling serialization function: " );
}
lua_getglobal (L, "serialize");
assert (lua_isfunction (L, -1));
lua_pushvalue (L, -2);
if (lua_pcall (L, 1, 1, 0)) {
bail (L, "Error while serializing: ");
}
result = string("return ") + lua_tostring (L, -1);
} else {
cerr << "Cannot serialize global Lua state!" << endl;
abort();
}
lua_pop (L, lua_gettop(L) - current_top);
popRef();
return result;
}
std::string LuaTable::orderedSerialize() {
pushRef();
std::string result;
int current_top = lua_gettop(L);
if (lua_gettop(L) != 0) {
if (luaL_loadstring(L, serialize_lua)) {
bail (L, "Error loading serialization function: ");
}
if (lua_pcall(L, 0, 0, 0)) {
bail (L, "Error compiling serialization function: " );
}
lua_getglobal (L, "serialize");
assert (lua_isfunction (L, -1));
lua_pushvalue (L, -2);
lua_pushstring (L, "");
lua_pushboolean (L, true);
if (lua_pcall (L, 3, 1, 0)) {
bail (L, "Error while serializing: ");
}
result = string("return ") + lua_tostring (L, -1);
} else {
cerr << "Cannot serialize global Lua state!" << endl;
abort();
}
lua_pop (L, lua_gettop(L) - current_top);
popRef();
return result;
}