* more consistent API.
*/
+#include <iostream>
#include <list>
+#include <map>
#include <string>
+#include <vector>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <lua.hpp>
-#include <Moof/Exception.hh>
+#include <Moof/Log.hh>
namespace Mf {
typedef boost::shared_ptr<Script> ScriptP;
-struct Script
+class Script
{
+public:
+
typedef boost::function<int(Script&)> Function;
- enum TYPE
+ enum Type
{
NONE = LUA_TNONE,
NIL = LUA_TNIL,
THREAD = LUA_TTHREAD
};
- enum STATUS
+ enum Result
{
SUCCESS = 0,
YIELD = LUA_YIELD,
FILE_ERROR = LUA_ERRFILE
};
- enum PSEUDO_INDEX
+ enum PseudoIndex
{
REGISTRY = LUA_REGISTRYINDEX,
ENVIRONMENT = LUA_ENVIRONINDEX,
};
/**
- * This is the most noticeable abstraction on top of the standard Lua API.
- * A Value object represents a value on the stack. More specifically, it
+ * This is the most prominent abstraction on top of the standard Lua API.
+ * A Slot object represents a value on the stack. More specifically, it
* represents a position on the stack. The distinction is only important
- * when values are moved around on the stack or if the Value represents a
+ * when objects are moved around on the stack or if the Slot represents a
* negative index on the stack (the value of which will change as things are
* pushed onto and popped from the stack).
*/
- struct Value
+ struct Slot
{
/**
* You have direct access to the index of the value on the stack being
/**
- * A default-constructed Value is invalid until a valid Value is
- * assigned to it. The only method that should be called on such a
- * Value is isValid(), otherwise chaos may ensue. In this case, the
- * Value will be invalid even if index is manually changed to a valid
- * index. You have to index the script itself to get a valid Value.
+ * A default-constructed Slot is invalid until a valid Slot is assigned
+ * to it. The only method that should be called on such a Slot is
+ * isValid(), otherwise chaos may ensue. In this case, the Slot will be
+ * invalid even if index is manually changed to a valid index. You have
+ * to index the script itself to get a valid Slot.
*/
- Value() :
- index(0),
- state(0) {}
-
- Value(lua_State* s, int i) :
+ Slot(lua_State* s = 0, int i = 0) :
index(i),
- state(s) {}
+ mState(s) {}
+
+ /**
+ * A copied value presently points to the same value, except the real
+ * index is used. That means that if a value that refers to a frame
+ * referenced from the top of the stack will have its normalized index
+ * copied into the new value object.
+ */
+
+ Slot(const Slot& copy) :
+ index(copy.getRealIndex()),
+ mState(copy.mState) {}
+
// check the type of the value
- bool isBoolean() const { return (bool)lua_isboolean(state, index); }
- bool isFunction() const { return (bool)lua_isfunction(state, index); }
- bool isNil() const { return (bool)lua_isnil(state, index); }
- bool isNone() const { return (bool)lua_isnone(state, index); }
- bool isValid() const { return state != 0 && !isNone(); }
- bool isNoneOrNil() const { return (bool)lua_isnoneornil(state, index); }
- bool isNumber() const { return (bool)lua_isnumber(state, index); }
- bool isString() const { return (bool)lua_isstring(state, index); }
- bool isTable() const { return (bool)lua_istable(state, index); }
- bool isThread() const { return (bool)lua_isthread(state, index); }
- bool isData() const { return (bool)lua_isuserdata(state, index); }
- bool isLightData() const { return (bool)lua_islightuserdata(state, index); }
+ bool isBoolean() const { return (bool)lua_isboolean(mState, index); }
+ bool isFunction() const { return (bool)lua_isfunction(mState, index); }
+ bool isNil() const { return (bool)lua_isnil(mState, index); }
+ bool isNone() const { return (bool)lua_isnone(mState, index); }
+ bool isValid() const { return mState != 0 && !isNone(); }
+ bool isNoneOrNil() const { return (bool)lua_isnoneornil(mState, index); }
+ bool isNumber() const { return (bool)lua_isnumber(mState, index); }
+ bool isString() const { return (bool)lua_isstring(mState, index); }
+ bool isTable() const { return (bool)lua_istable(mState, index); }
+ bool isThread() const { return (bool)lua_isthread(mState, index); }
+ bool isData() const { return (bool)lua_isuserdata(mState, index); }
+ bool isLightData() const { return (bool)lua_islightuserdata(mState, index); }
+
+ /**
+ * Check the value and throw an error if its the wrong type. There's a
+ * little caveat: This method never returns because it does a long jump.
+ * Consequently, constructed C++ objects which exist on the stack
+ * between the current frame and some lua function will not be
+ * destructed. That's not a problem for objects that only exist on the
+ * stack, but any objects that allocate memory on the heap (such as
+ * containers or strings) will leak. Therefore, you should only call
+ * this method after cleaning up such objects. The best thing to do for
+ * defining functions is to simply check all the parameters at the
+ * get-go before any C++ objects are even constructed.
+ */
+
+ void requireType(Type type) const
+ {
+ if (type != getType())
+ {
+ luaL_typerror(mState, index, lua_typename(mState, type));
+ }
+ }
+
+ void throwError(const char* error)
+ {
+ luaL_argerror(mState, index, error);
+ }
+
+
+ Slot& requireBoolean()
+ {
+ if (!isBoolean()) luaL_typerror(mState, index, "boolean");
+ return *this;
+ }
+ Slot& requireNumber()
+ {
+ if (!isNumber()) luaL_typerror(mState, index, "number");
+ return *this;
+ }
+ Slot& requireString()
+ {
+ if (!isString()) luaL_typerror(mState, index, "string");
+ return *this;
+ }
+ Slot& requireTable()
+ {
+ if (!isTable()) luaL_typerror(mState, index, "table");
+ return *this;
+ }
+ Slot& requireFunction()
+ {
+ if (!isFunction()) luaL_typerror(mState, index, "function");
+ return *this;
+ }
+ Slot& requireData()
+ {
+ if (!isData()) luaL_typerror(mState, index, "data");
+ return *this;
+ }
+ Slot& requireNil()
+ {
+ if (!isNil()) luaL_typerror(mState, index, "nil");
+ return *this;
+ }
+ Slot& requireThread()
+ {
+ if (!isThread()) luaL_typerror(mState, index, "thread");
+ return *this;
+ }
+
/**
* Get the type of the value.
*/
- TYPE getType() const
+ Type getType() const
{
- return (TYPE)lua_type(state, index);
+ return (Type)lua_type(mState, index);
}
/**
std::string getTypeName() const
{
- return std::string(lua_typename(state, (int)getType()));
+ return std::string(luaL_typename(mState, index));
}
+
+ /**
+ * Get the length of the value according to the definition given by Lua.
+ */
+
+ size_t getLength() const
+ {
+ return lua_objlen(mState, index);
+ }
+
+ int getRealIndex() const
+ {
+ if (index < 0) return lua_gettop(mState) + 1 + index;
+ else return index;
+ }
+
+
/**
* Get a pointer value (for userdata, tables, threads, and functions).
*/
const void* getIdentifier() const
{
- return lua_topointer(state, index);
+ return lua_topointer(mState, index);
}
- bool operator == (const Value& rhs) const
+ bool operator == (const Slot& rhs) const
{
- return (bool)lua_equal(state, index, rhs.index);
+ return (bool)lua_equal(mState, index, rhs.index);
}
- bool operator != (const Value& rhs) const
+ bool operator != (const Slot& rhs) const
{
return !(*this == rhs);
}
- bool operator < (const Value& rhs) const
+ bool operator < (const Slot& rhs) const
{
- return (bool)lua_lessthan(state, index, rhs.index);
+ return (bool)lua_lessthan(mState, index, rhs.index);
}
- bool operator <= (const Value& rhs) const
+ bool operator <= (const Slot& rhs) const
{
return *this < rhs || *this == rhs;
}
- bool operator > (const Value& rhs) const
+ bool operator > (const Slot& rhs) const
{
return !(*this <= rhs);
}
- bool operator >= (const Value& rhs) const
+ bool operator >= (const Slot& rhs) const
{
return !(*this < rhs);
}
operator bool () const
{
- return (bool)lua_toboolean(state, index);
+ return (bool)lua_toboolean(mState, index);
}
- Value& operator = (const Value& rhs)
+ Slot& operator = (const Slot& rhs)
{
rhs.pushCopy();
replaceWithTop();
}
- /**
- * Get the length of the value according to the definition given by Lua.
- */
-
- size_t getLength() const
- {
- return lua_objlen(state, index);
- }
-
-
/**
* Convert the underlying value to a C++ type.
*/
template <typename T>
- void get(T& value) const
+ bool get(T& value) const
{
- value = (T)lua_tointeger(state, index);
+ if (isNumber())
+ {
+ value = (T)lua_tointeger(mState, index);
+ return true;
+ }
+ return false;
}
- void get(bool& value) const
+ bool get(float& value) const
{
- value = (bool)lua_toboolean(state, index);
+ if (isNumber())
+ {
+ value = (float)lua_tonumber(mState, index);
+ return true;
+ }
+ return false;
}
-
- void get(float& value) const
+ bool get(double& value) const
{
- value = (float)lua_tonumber(state, index);
+ if (isNumber())
+ {
+ value = (double)lua_tonumber(mState, index);
+ return true;
+ }
+ return false;
}
- void get(double& value) const
+
+ bool get(bool& value) const
{
- value = (double)lua_tonumber(state, index);
+ if (isBoolean())
+ {
+ value = (bool)lua_toboolean(mState, index);
+ return true;
+ }
+ return false;
}
- void get(std::string& value) const
+ bool get(std::string& value) const
{
- size_t size;
- const char* str = lua_tolstring(state, index, &size);
- value.assign(str, size);
+ if (isString())
+ {
+ size_t size;
+ const char* str = lua_tolstring(mState, index, &size);
+ value.assign(str, size);
+ return true;
+ }
+ return false;
}
+ template <typename T>
+ bool get(std::vector<T>& array) const
+ {
+ if (!isTable()) return false;
+
+ array.clear();
+
+ Slot value(mState, -1);
+ int realIndex = getRealIndex();
- void set(std::string& value)
+ bool done = false;
+ for (int i = 1; !done; ++i)
+ {
+ lua_rawgeti(mState, realIndex, i);
+
+ T v;
+ if (value.get(v)) array.push_back(v);
+ else done = true;
+
+ lua_pop(mState, 1);
+ }
+
+ return true;
+ }
+
+ template <typename T>
+ bool get(std::map<std::string,T>& dictionary) const
{
+ if (!isTable()) return false;
+
+ dictionary.clear();
+
+ Slot key(mState, -2);
+ Slot value(mState, -1);
+ int realIndex = getRealIndex();
+
+ lua_pushnil(mState);
+ while (lua_next(mState, realIndex) != 0)
+ {
+ std::string k;
+ if (!key.isNumber() && key.get(k))
+ {
+ T v;
+ if (value.get(v)) dictionary[k] = v;
+ }
+ lua_pop(mState, 1);
+ }
+ lua_pop(mState, 1);
+
+ return true;
}
- //template <typename T>
- //void get(const std::string& field, T& value) const
- //{
- ////lua_getfield(state_, field.c_str());
- //pushField(field);
- //get(-1, value);
- //lua_pop(state_, 1);
- //}
-
/**
* Copy the value and push the copy to the stack.
void pushCopy() const
{
- lua_pushvalue(state, index);
+ lua_pushvalue(mState, index);
}
/**
void replaceWithTop()
{
- lua_replace(state, index);
+ lua_replace(mState, index);
}
void remove()
{
- lua_remove(state, index);
+ lua_remove(mState, index);
}
/**
void insertTopHere()
{
- lua_insert(state, index);
+ lua_insert(mState, index);
}
void pushMetatable() const
{
- lua_getmetatable(state, index);
+ lua_getmetatable(mState, index);
}
void pushField() const
{
- lua_gettable(state, index);
+ lua_gettable(mState, index);
}
void pushField(const std::string& name) const
{
- lua_getfield(state, index, name.c_str());
+ lua_getfield(mState, index, name.c_str());
+ }
+
+ void pushField(size_t index) const
+ {
+ lua_pushinteger(mState, lua_Integer(index));
+ pushField();
}
private:
- lua_State* state;
+ lua_State* mState;
};
Script() :
- state_(luaL_newstate())
+ mState(0)
{
- lua_pushlightuserdata(state_, this);
- lua_setfield(state_, LUA_REGISTRYINDEX, "_script_obj");
+ reset();
}
~Script()
{
- if (isMainThread_) lua_close(state_);
+ destroy();
}
return ScriptP(new Script);
}
+ void reset()
+ {
+ if (mState) destroy();
+ mState = luaL_newstate();
+ lua_pushlightuserdata(mState, this);
+ lua_setfield(mState, LUA_REGISTRYINDEX, "Script_hh_Object");
+ }
+
void importStandardLibraries()
{
- luaL_openlibs(state_);
+ luaL_openlibs(mState);
+ }
+
+ void importBaseLibrary()
+ {
+ lua_pushcfunction(mState, luaopen_base);
+ push(LUA_COLIBNAME);
+ call(1, 0);
+ }
+
+ void importPackageLibrary()
+ {
+ lua_pushcfunction(mState, luaopen_package);
+ push(LUA_LOADLIBNAME);
+ call(1, 0);
+ }
+
+ void importStringLibrary()
+ {
+ lua_pushcfunction(mState, luaopen_string);
+ push(LUA_STRLIBNAME);
+ call(1, 0);
+ }
+
+ void importTableLibrary()
+ {
+ lua_pushcfunction(mState, luaopen_table);
+ push(LUA_TABLIBNAME);
+ call(1, 0);
+ }
+
+ void importMathLibrary()
+ {
+ lua_pushcfunction(mState, luaopen_math);
+ push(LUA_MATHLIBNAME);
+ call(1, 0);
+ }
+
+ void importIoLibrary()
+ {
+ lua_pushcfunction(mState, luaopen_io);
+ push(LUA_IOLIBNAME);
+ call(1, 0);
+ }
+
+ void importOsLibrary()
+ {
+ lua_pushcfunction(mState, luaopen_os);
+ push(LUA_OSLIBNAME);
+ call(1, 0);
+ }
+
+ void importDebugLibrary()
+ {
+ lua_pushcfunction(mState, luaopen_debug);
+ push(LUA_DBLIBNAME);
+ call(1, 0);
}
+
void importFunction(const std::string& name, const Function& function)
{
push(function);
- lua_setglobal(state_, name.c_str());
+ lua_setglobal(mState, name.c_str());
}
- STATUS doString(const std::string& commands)
+ Result doString(const std::string& commands)
{
- return (STATUS)luaL_dostring(state_, commands.c_str());
+ return (Result)luaL_dostring(mState, commands.c_str());
}
- STATUS doFile(const std::string& file)
+ Result doFile(const std::string& file)
{
- return (STATUS)luaL_dofile(state_, file.c_str());
+ return (Result)luaL_dofile(mState, file.c_str());
}
Script pushNewThread()
{
- return Script(state_);
+ return Script(mState);
}
void pushThread()
{
- lua_pushthread(state_);
+ lua_pushthread(mState);
}
- STATUS resume(int nargs)
+ Result resume(int nargs)
{
- return (STATUS)lua_resume(state_, nargs);
+ return (Result)lua_resume(mState, nargs);
}
- STATUS getStatus() const
+ Result getStatus() const
{
- return (STATUS)lua_status(state_);
+ return (Result)lua_status(mState);
}
int yield(int results)
{
- return lua_yield(state_, results);
+ return lua_yield(mState, results);
}
bool isMainThread() const
{
- return isMainThread_;
+ return mIsMainThread;
+ }
+
+
+ /**
+ * Throw an error with the value at the top of the stack. This method never
+ * returns because it does a long jump. Consequently, constructed C++
+ * objects which exist on the stack between the current frame and some lua
+ * function will not be destructed. That's not a problem for objects that
+ * only exist on the stack, but any objects that allocate memory on the heap
+ * (such as containers or strings) will leak. Therefore, you should only
+ * call this method after cleaning up such objects.
+ */
+
+ void throwError()
+ {
+ lua_error(mState);
}
* Get significant values.
*/
- Value getGlobalTable() const
+ Slot getGlobalTable() const
{
- return Value(state_, GLOBALS);
+ return Slot(mState, GLOBALS);
}
- Value getRegistryTable() const
+ Slot getRegistryTable() const
{
- return Value(state_, REGISTRY);
+ return Slot(mState, REGISTRY);
}
- Value getEnvironmentTable() const
+ Slot getEnvironmentTable() const
{
- return Value(state_, ENVIRONMENT);
+ return Slot(mState, ENVIRONMENT);
}
- Value getTop() const
+ Slot getTop() const
{
- return Value(state_, lua_gettop(state_));
+ return Slot(mState, lua_gettop(mState));
}
/**
int getSize() const
{
- return lua_gettop(state_);
+ return lua_gettop(mState);
}
void setSize(int size)
{
- lua_settop(state_, size);
+ lua_settop(mState, size);
}
void clear()
bool checkStack(int extra)
{
- return (bool)lua_checkstack(state_, extra);
+ return (bool)lua_checkstack(mState, extra);
}
/**
- * Concatenates the top-most n values on the stack.
+ * Concatenates the top-most n slots on the stack.
*/
- void concat(int n)
+ void concat(int n = 2)
{
- lua_concat(state_, n);
+ lua_concat(mState, n);
}
template <typename T>
void push(T value)
{
- lua_pushinteger(state_, lua_Integer(value));
+ lua_pushinteger(mState, lua_Integer(value));
}
void push(bool value)
{
- lua_pushboolean(state_, int(value));
+ lua_pushboolean(mState, int(value));
}
void push(float value)
{
- lua_pushnumber(state_, (lua_Number)value);
+ lua_pushnumber(mState, (lua_Number)value);
}
void push(double value)
{
- lua_pushnumber(state_, (lua_Number)value);
+ lua_pushnumber(mState, (lua_Number)value);
}
void push(const std::string& value)
{
- lua_pushlstring(state_, value.c_str(), value.length());
+ lua_pushlstring(mState, value.c_str(), value.length());
+ }
+ void push(const char* value)
+ {
+ lua_pushstring(mState, value);
}
void push(const char* value, size_t length)
{
- lua_pushlstring(state_, value, length);
+ lua_pushlstring(mState, value, length);
}
void push(const Function& function)
{
- functions_.push_back(function);
+ mFunctions.push_back(function);
- lua_pushlightuserdata(state_, (void*)&functions_.back());
- lua_pushcclosure(state_, dispatchCall, 1);
+ lua_pushlightuserdata(mState, (void*)&mFunctions.back());
+ lua_pushcclosure(mState, dispatchCall, 1);
}
void push(void* data)
{
- lua_pushlightuserdata(state_, data);
+ lua_pushlightuserdata(mState, data);
}
void pushNil()
{
- lua_pushnil(state_);
+ lua_pushnil(mState);
}
void pushFromThread(Script& thread, int n)
{
- lua_xmove(thread.state_, state_, n);
+ lua_xmove(thread.mState, mState, n);
}
- STATUS pushCode(const std::string& filename)
+ Result pushCode(const std::string& filename)
{
- return (STATUS)luaL_loadfile(state_, filename.c_str());
+ return (Result)luaL_loadfile(mState, filename.c_str());
}
- STATUS pushCode(const std::string& name, const char* buffer, size_t size)
+ Result pushCode(const std::string& name, const char* buffer, size_t size)
{
- return (STATUS)luaL_loadbuffer(state_, buffer, size, name.c_str());
+ return (Result)luaL_loadbuffer(mState, buffer, size, name.c_str());
}
void* pushNewData(size_t size)
{
- return lua_newuserdata(state_, size);
+ return lua_newuserdata(mState, size);
}
void pushNewTable()
{
- lua_newtable(state_);
+ lua_newtable(mState);
}
* is any number of return values, depending on the callee).
*/
- STATUS call(int nargs, int nresults = LUA_MULTRET)
+ Result call(int nargs = 0, int nresults = LUA_MULTRET)
{
- return (STATUS)lua_pcall(state_, nargs, nresults, 0);
+ return (Result)lua_pcall(mState, nargs, nresults, 0);
}
* Pops n values from the top of the stack.
*/
- void pop(int n)
+ void pop(int n = 1)
{
- lua_pop(state_, n);
+ lua_pop(mState, n);
}
/**
- * Index into the stack to get a Value.
+ * Index into the stack to get a Slot.
*/
- Value operator [] (int index) const
+ Slot operator [] (int index) const
{
- return Value(state_, index);
+ return Slot(mState, index);
}
* Getting and setting fields of a table.
*/
- void get(const std::string& field, int index = GLOBALS) const
+ void pushField(const std::string& field, int index = GLOBALS) const
{
- lua_getfield(state_, index, field.c_str());
+ lua_getfield(mState, index, field.c_str());
}
void set(const std::string& field, int index = GLOBALS)
{
- lua_setfield(state_, index, field.c_str());
+ lua_setfield(mState, index, field.c_str());
}
void collectAll()
{
- lua_gc(state_, LUA_GCCOLLECT, 0);
+ lua_gc(mState, LUA_GCCOLLECT, 0);
}
void stopCollector()
{
- lua_gc(state_, LUA_GCSTOP, 0);
+ lua_gc(mState, LUA_GCSTOP, 0);
}
void restartCollector()
{
- lua_gc(state_, LUA_GCRESTART, 0);
+ lua_gc(mState, LUA_GCRESTART, 0);
}
int getUsedMemory() const
{
// in kilobytes
- return lua_gc(state_, LUA_GCCOUNT, 0);
+ return lua_gc(mState, LUA_GCCOUNT, 0);
}
void collectStep(int step)
{
- lua_gc(state_, LUA_GCSTEP, step);
+ lua_gc(mState, LUA_GCSTEP, step);
}
void tuneCollector(int pause, int step)
{
- lua_gc(state_, LUA_GCSETPAUSE, pause);
- lua_gc(state_, LUA_GCSETSTEPMUL, step);
+ lua_gc(mState, LUA_GCSETPAUSE, pause);
+ lua_gc(mState, LUA_GCSETSTEPMUL, step);
}
-
- struct Exception : public Mf::Exception
- {
- explicit Exception(unsigned error) :
- Mf::Exception(error) {}
-
- void raise()
- {
- throw *this;
- }
- };
-
-
private:
Script(lua_State* state) :
- state_(lua_newthread(state)),
- isMainThread_(false) {}
+ mState(lua_newthread(state)),
+ mIsMainThread(false) {}
static int dispatchCall(lua_State* state)
{
const Function* function = (const Function*)lua_touserdata(state,
lua_upvalueindex(1));
- lua_getfield(state, LUA_REGISTRYINDEX, "_script_obj");
+ lua_getfield(state, LUA_REGISTRYINDEX, "Script_hh_Object");
Script* script = (Script*)lua_touserdata(state, -1);
lua_pop(state, 1);
return (*function)(*script);
}
- lua_State* state_;
- bool isMainThread_;
- std::list<Function> functions_;
+ void destroy()
+ {
+ if (mIsMainThread) lua_close(mState);
+ }
+
+ lua_State* mState;
+ bool mIsMainThread;
+ std::list<Function> mFunctions;
};
+inline std::ostream& operator << (std::ostream& stream,
+ const Script::Slot& slot)
+{
+ if (slot.isString())
+ {
+ std::string str;
+ slot.get(str);
+ stream << str;
+ }
+ else if (slot.isBoolean())
+ {
+ if (slot) stream << "true";
+ else stream << "false";
+ }
+ else if (slot.isNil())
+ {
+ stream << "nil";
+ }
+ else
+ {
+ stream << slot.getTypeName() << " (" << slot.getIdentifier() << ")";
+ }
+
+ return stream;
+}
+
+
} // namespace Mf
#endif // _MOOF_SCRIPT_HH_