You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
8.0 KiB

#include "luainterface.h"
LuaInterface g_lua;
void LuaInterface::init()
{
// installs a custom loader that will load scripts correctlyfrom lua require keyword
auto loaders = getGlobal("package")->getField("loaders");
loaders->setTable(loaders->getTableSize() + 1, createCppFunction(&LuaInterface::luaScriptLoader));
// register LuaObject, the class that all other classes will derive from
registerClass("LuaObject");
bindClassMemberGetField("LuaObject", "use_count", &LuaObject::getUseCount);
// register other custom classes and functions
registerFunctions();
}
void LuaInterface::terminate()
{
collectGarbage();
}
LuaValuePtr LuaInterface::loadFunction(const std::string& buffer, const std::string& source)
{
LuaValuePtr func;
// get the function contained the buffer
if(boost::starts_with(buffer, "function")) {
runBuffer(make_string("__func = ", buffer), source);
func = getGlobal("__func");
setGlobal("__func", createNil());
// use the buffer as a function
} else
func = loadBuffer(buffer, source);
return func;
}
LuaValuePtr LuaInterface::evaluateExpression(const std::string& expression, const std::string& source)
{
// run the expression
std::string buffer = make_string("__exp = (", expression, ")");
runBuffer(buffer, source);
// get the expression result
LuaValuePtr res = getGlobal("__exp");
setGlobal("__exp", createNil());
return res;
}
LuaValuePtr LuaInterface::createEnvironment()
{
// creates a new environment table and
// redirect the new environment to the current thread global environment (aka _G)
// this allows to access global variables from _G in the new environment
// and prevents new variables on the new environment to be set on the global environment
auto env = createTable();
auto mt = createTable();
mt->setField("__index", getGlobalEnvironment());
env->setMetatable(mt);
return env;
}
void LuaInterface::registerClass(const std::string& className, const std::string& baseClass)
{
// create the class table (that it is also the class methods table)
auto klass = createTable(className);
// create the class metatable
auto klass_mt = createTable(className + "_mt");
// create the class fieldmethods table
auto klass_fieldmethods = createTable(className + "_fieldmethods");
// set metatable metamethods
klass_mt->setField("__index", &LuaInterface::luaObjectGetEvent);
klass_mt->setField("__newindex", &LuaInterface::luaObjectSetEvent);
klass_mt->setField("__eq", &LuaInterface::luaObjectEqualEvent);
klass_mt->setField("__gc", &LuaInterface::luaObjectCollectEvent);
klass_mt->setField("methods", klass);
klass_mt->setField("fieldmethods", klass_fieldmethods);
// redirect methods and fieldmethods to the base class ones
if(!className.empty() && className != "LuaObject") {
/* the following code what create classes hierarchy for lua, by reproducing the following:
* DerivedClass = { __index = BaseClass }
* DerivedClass_fieldmethods = { __index = BaseClass_methods }
*/
// redirect the class methods to the base methods
auto redirect = createTable();
auto tmp = getGlobal(baseClass);
redirect->setField("__index", tmp);
klass->setMetatable(redirect);
// redirect the class fieldmethods to the base fieldmethods
redirect = createTable();
tmp = getGlobal(baseClass + "_fieldmethods");
redirect->setField("__index", tmp);
klass_fieldmethods->setMetatable(redirect);
}
}
void LuaInterface::registerClassStaticFunction(const std::string& className, const std::string& functionName, const LuaCppFunction& function)
{
registerClassMemberFunction(className, functionName, function);
}
void LuaInterface::registerClassMemberFunction(const std::string& className, const std::string& functionName, const LuaCppFunction& function)
{
auto table = getGlobal(className);
table->setField(functionName, function);
}
void LuaInterface::registerClassMemberField(const std::string& className, const std::string& field, const LuaCppFunction& getFunction, const LuaCppFunction& setFunction)
{
auto fieldmethods = getGlobal(className + "_fieldmethods");
auto methods = getGlobal(className);
auto capitalized = field;
capitalized[0] = std::toupper(field[0]);
if(getFunction) {
auto func = createCppFunction(getFunction);
fieldmethods->setField(make_string("get_", field), func);
methods->setField(make_string("get", capitalized), func);
}
if(setFunction) {
auto func = createCppFunction(setFunction);
fieldmethods->setField(make_string("set_", field), func);
methods->setField(make_string("set", capitalized), func);
}
}
void LuaInterface::registerGlobalFunction(const std::string& functionName, const LuaCppFunction& function)
{
setGlobal(functionName, createCppFunction(function));
}
int LuaInterface::luaScriptLoader(LuaState* lua)
{
// load the script as a function
auto fileName = make_string(lua->popValue()->toString(), + ".lua");
auto scriptMain = lua->loadScript(fileName);
lua->pushValue(scriptMain);
return 1;
}
int LuaInterface::luaObjectGetEvent(LuaState* lua)
{
// metamethod that will retrive fields values (that include functions) from the object
auto key = lua->popValue()->toString();
auto objRef = lua->popValue();
auto obj = safe_luavalue_cast<LuaObjectPtr>(objRef);
// if a get method for this key exists, calls it
auto get_method = objRef->getMetatable()->getField("fieldmethods")->getField("get_" + key);
if(!get_method->isNil()) {
LuaValueList rets = get_method->call(make_string(obj->getLuaTypeName(), " obj.", key), objRef);
assert(rets.size() == 1);
lua->pushValues(rets);
return 1;
}
// if the field for this key exists, returns it
auto field = obj->getField(key);
if(!field->isNil()) {
lua->pushValue(field);
return 1;
}
// if a method for this key exists, returns it
auto method = objRef->getMetatable()->getField("methods")->getField(key);
lua->pushValue(method);
return 1;
}
int LuaInterface::luaObjectSetEvent(LuaState* lua)
{
// this metamethod is called when setting a field of the object by using the keyword '='
auto value = lua->popValue();
auto key = lua->popValue()->toString();
auto objRef = lua->popValue();
auto obj = safe_luavalue_cast<LuaObjectPtr>(objRef);
// check if a set method for this field exists and call it
auto set_method = objRef->getMetatable()->getField("fieldmethods")->getField("set_" + key);
if(!set_method->isNil()) {
set_method->call(make_string(obj->getLuaTypeName(), " obj.", key, "="), objRef, value);
return 0;
}
// no set method exists, then sets the object table
obj->setField(key, value);
return 0;
}
int LuaInterface::luaObjectEqualEvent(LuaState* lua)
{
// metamethod that will check equality of objects
auto objRef2 = lua->popValue();
auto objRef1 = lua->popValue();
bool ret = false;
// check if obj1 == obj2
if(objRef1->isUserdata() && objRef2->isUserdata()) {
LuaObjectPtr* objPtr1 = static_cast<LuaObjectPtr*>(objRef1->toUserdata());
LuaObjectPtr* objPtr2 = static_cast<LuaObjectPtr*>(objRef2->toUserdata());
assert(objPtr1 && objPtr2);
if(*objPtr1 == *objPtr2)
ret = true;
}
lua->pushBoolean(ret);
return 1;
}
int LuaInterface::luaObjectCollectEvent(LuaState* lua)
{
/* this metamethod is called every two lua garbage collections
* for any LuaObject that have no references left in lua environment
* anymore, thus this creates the possibility of holding an object
* existence by lua
*/
// get object pointer
auto objRef = lua->popValue();
auto objPtr = static_cast<LuaObjectPtr*>(objRef->toUserdata());
assert(objPtr);
// reset pointer to decrease object use count
objPtr->reset();
return 0;
}