+++ /dev/null
-
-/*] Copyright (c) 2009-2010, Charles McGarvey [**************************
-**] All rights reserved.
-*
-* vi:ts=4 sw=4 tw=75
-*
-* Distributable under the terms and conditions of the 2-clause BSD license;
-* see the file COPYING for a complete text of the license.
-*
-**************************************************************************/
-
-#ifndef _MOOF_SCRIPT_HH_
-#define _MOOF_SCRIPT_HH_
-
-/**
- * @file Script.hh
- * A thin wrapper over Lua. This is not meant as a complicated binding
- * package between C++ and Lua. It is not meant to obscure the division
- * between C++ and Lua but rather to clarify it and make it more
- * manageable. It does not hide the concept of the Lua stack, but rather
- * provides that mechanism with a certain level of abstraction while also
- * providing a cleaner, more consistent API.
- */
-
-#include <iostream>
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
-
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
-#include <boost/shared_ptr.hpp>
-#include <lua.hpp>
-
-
-namespace Mf {
-
-
-class Script;
-typedef boost::shared_ptr<Script> ScriptP;
-
-
-class Script
-{
-public:
-
- typedef boost::function<int(Script&)> Function;
-
- enum Type
- {
- NONE = LUA_TNONE,
- NIL = LUA_TNIL,
- BOOLEAN = LUA_TBOOLEAN,
- LIGHTUSERDATA = LUA_TLIGHTUSERDATA,
- NUMBER = LUA_TNUMBER,
- STRING = LUA_TSTRING,
- TABLE = LUA_TTABLE,
- FUNCTION = LUA_TFUNCTION,
- USERDATA = LUA_TUSERDATA,
- THREAD = LUA_TTHREAD
- };
-
- enum Result
- {
- SUCCESS = 0,
- YIELD = LUA_YIELD,
- RUNTIME_ERROR = LUA_ERRRUN,
- SYNTAX_ERROR = LUA_ERRSYNTAX,
- MEMORY_ERROR = LUA_ERRMEM,
- HANDLER_ERROR = LUA_ERRERR,
- FILE_ERROR = LUA_ERRFILE
- };
-
- enum PseudoIndex
- {
- REGISTRY = LUA_REGISTRYINDEX,
- ENVIRONMENT = LUA_ENVIRONINDEX,
- GLOBALS = LUA_GLOBALSINDEX
- };
-
- /**
- * 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 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 Slot
- {
- /**
- * You have direct access to the index of the value on the stack
- * being represented.
- */
-
- int index;
-
-
- Slot(const Script& s, int i = 0) :
- index(i),
- mScript(const_cast<Script&>(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.positiveIndex()),
- //mScript(copy.mScript) {}
-
-
- // check the type of the value
- bool isBoolean() const
- { return (bool)lua_isboolean(mScript.mState, index); }
- bool isImportedFunction() const
- { return (bool)lua_iscfunction(mScript.mState, index); }
- bool isFunction() const
- { return (bool)lua_isfunction(mScript.mState, index); }
- bool isNil() const
- { return (bool)lua_isnil(mScript.mState, index); }
- bool isNone() const
- { return (bool)lua_isnone(mScript.mState, index); }
- bool isNoneOrNil() const
- { return (bool)lua_isnoneornil(mScript.mState, index); }
- bool isNumber() const
- { return (bool)lua_isnumber(mScript.mState, index); }
- bool isString() const
- { return (bool)lua_isstring(mScript.mState, index); }
- bool isTable() const
- { return (bool)lua_istable(mScript.mState, index); }
- bool isThread() const
- { return (bool)lua_isthread(mScript.mState, index); }
- bool isData() const
- { return (bool)lua_isuserdata(mScript.mState, index); }
- bool isLightData() const
- { return (bool)lua_islightuserdata(mScript.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 t) const
- {
- if (t != type())
- {
- luaL_typerror(mScript.mState, index,
- lua_typename(mScript.mState, t));
- }
- }
-
- void raise(const char* error)
- {
- luaL_argerror(mScript.mState, index, error);
- }
-
-
- Slot& requireBoolean()
- {
- if (!isBoolean())
- {
- luaL_typerror(mScript.mState, index, "boolean");
- }
- return *this;
- }
- Slot& requireNumber()
- {
- if (!isNumber())
- {
- luaL_typerror(mScript.mState, index, "number");
- }
- return *this;
- }
- Slot& requireString()
- {
- if (!isString())
- {
- luaL_typerror(mScript.mState, index, "string");
- }
- return *this;
- }
- Slot& requireTable()
- {
- if (!isTable())
- {
- luaL_typerror(mScript.mState, index, "table");
- }
- return *this;
- }
- Slot& requireFunction()
- {
- if (!isFunction())
- {
- luaL_typerror(mScript.mState, index, "function");
- }
- return *this;
- }
- Slot& requireData()
- {
- if (!isData())
- {
- luaL_typerror(mScript.mState, index, "data");
- }
- return *this;
- }
- Slot& requireNil()
- {
- if (!isNil())
- {
- luaL_typerror(mScript.mState, index, "nil");
- }
- return *this;
- }
- Slot& requireThread()
- {
- if (!isThread())
- {
- luaL_typerror(mScript.mState, index, "thread");
- }
- return *this;
- }
-
-
- /**
- * Get the type of the value.
- */
-
- Type type() const
- {
- return (Type)lua_type(mScript.mState, index);
- }
-
- /**
- * Get the name of the type of the value as a string.
- */
-
- std::string typeName() const
- {
- return std::string(luaL_typename(mScript.mState, index));
- }
-
-
- /**
- * Get the length of the value according to the definition given by
- * Lua.
- */
-
- size_t length() const
- {
- return lua_objlen(mScript.mState, index);
- }
-
- int positiveIndex() const
- {
- if (index < 0) return index + lua_gettop(mScript.mState) + 1;
- else return index;
- }
-
-
- /**
- * Get a pointer value (for userdata, tables, threads, and
- * functions).
- */
-
- const void* id() const
- {
- return lua_topointer(mScript.mState, index);
- }
-
- bool isIdentical(const Slot& rhs) const
- {
- return &mScript == &(rhs.mScript) && index == rhs.index;
- }
-
- operator bool () const
- {
- return !isNone();
- }
-
-
- bool operator == (const Slot& rhs) const
- {
- return (bool)lua_equal(mScript.mState, index, rhs.index);
- }
- bool operator != (const Slot& rhs) const
- {
- return !(*this == rhs);
- }
-
- bool operator < (const Slot& rhs) const
- {
- return (bool)lua_lessthan(mScript.mState, index, rhs.index);
- }
- bool operator <= (const Slot& rhs) const
- {
- return *this < rhs || *this == rhs;
- }
- bool operator > (const Slot& rhs) const
- {
- return !(*this <= rhs);
- }
- bool operator >= (const Slot& rhs) const
- {
- return !(*this < rhs);
- }
-
-
- /**
- * Convert the underlying value to a C++ type.
- */
-
- template <class T>
- bool get(T& value) const
- {
- if (isNumber())
- {
- value = (T)lua_tointeger(mScript.mState, index);
- return true;
- }
- return false;
- }
-
- bool get(float& value) const
- {
- if (isNumber())
- {
- value = (float)lua_tonumber(mScript.mState, index);
- return true;
- }
- return false;
- }
- bool get(double& value) const
- {
- if (isNumber())
- {
- value = (double)lua_tonumber(mScript.mState, index);
- return true;
- }
- return false;
- }
-
- bool get(bool& value) const
- {
- if (isBoolean())
- {
- value = (bool)lua_toboolean(mScript.mState, index);
- return true;
- }
- return false;
- }
-
- bool get(const char*& value, size_t& size) const
- {
- if (isString())
- {
- value = lua_tolstring(mScript.mState, index, &size);
- return true;
- }
- return false;
- }
-
- bool get(std::string& value) const
- {
- const char* str;
- size_t size;
- if (get(str, size))
- {
- value.assign(str, size);
- return true;
- }
- return false;
- }
-
- bool get(void*& value) const
- {
- if (isData())
- {
- value = lua_touserdata(mScript.mState, index);
- return true;
- }
- return false;
- }
-
- template <class T>
- bool get(std::vector<T>& array) const
- {
- if (!isTable()) return false;
-
- array.clear();
-
- Slot value = mScript[-1];
- int realIndex = positiveIndex();
-
- bool done = false;
- for (int i = 1; !done; ++i)
- {
- lua_rawgeti(mScript.mState, realIndex, i);
-
- T v;
- if (value.get(v)) array.push_back(v);
- else done = true;
-
- mScript.pop();
- }
-
- return true;
- }
-
- template <class T>
- bool get(std::map<std::string,T>& dictionary) const
- {
- if (!isTable()) return false;
-
- dictionary.clear();
-
- Slot key = mScript[-2];
- Slot value = mScript[-1];
- int realIndex = positiveIndex();
-
- mScript.pushNil();
- while (lua_next(mScript.mState, realIndex) != 0)
- {
- std::string k;
- if (!key.isNumber() && key.get(k))
- {
- T v;
- if (value.get(v)) dictionary[k] = v;
- }
- mScript.pop();
- }
- mScript.pop();
-
- return true;
- }
-
- /**
- * Get the value of a field from the table.
- */
-
- template <class T, class V>
- bool get(T& value, V field) const
- {
- bool ret = pushField(field).get(value);
- mScript.pop();
- return ret;
- }
-
-
- template <class T, class V>
- void setField(T field, V value)
- {
- mScript.push(field);
- mScript.push(value);
- setField();
- }
-
- void setField()
- {
- lua_settable(mScript.mState, index);
- }
-
-
- template <class T>
- void setField(const std::string& field, T value)
- {
- setField(field.c_str(), value);
- }
- template <class T>
- void setField(const char* field, T value)
- {
- mScript.push(value);
- lua_setfield(mScript.mState, index, field);
- }
-
-
- /**
- * This set method, as opposed to the others, sets the value of the
- * actual slot. The others set table values.
- */
- template <class T>
- void set(T value)
- {
- mScript.push(value);
- replace();
- }
-
- void set()
- {
- replace();
- }
-
-
- /**
- * Replace this value with the value at the top of the stack.
- */
-
- void replace()
- {
- lua_replace(mScript.mState, index);
- }
-
- void remove()
- {
- lua_remove(mScript.mState, index);
- }
-
- void pop()
- {
- // removes this slot, taking with it everything above it
- mScript.pop(mScript.stackSize() - index + 1);
- }
-
- /**
- * Inserts the top-most value on the stack at position index,
- * shifting other values as needed.
- */
-
- void insertTopHere()
- {
- lua_insert(mScript.mState, index);
- }
-
-
- /**
- * Copy the value and push the copy to the stack.
- */
-
- Slot pushCopy() const
- {
- lua_pushvalue(mScript.mState, index);
- return mScript.top();
- }
-
- Slot pushMetaTable() const
- {
- lua_getmetatable(mScript.mState, index);
- return mScript.top();
- }
-
- Slot pushEnvironment() const
- {
- lua_getfenv(mScript.mState, index);
- return mScript.top();
- }
-
-
- Slot pushField() const
- {
- lua_gettable(mScript.mState, index);
- return mScript.top();
- }
-
- template <class T>
- Slot pushField(T index) const
- {
- mScript.push(index);
- return pushField();
- }
-
- Slot pushField(const std::string& name) const
- {
- return pushField(name.c_str());
- }
- Slot pushField(const char* name) const
- {
- lua_getfield(mScript.mState, index, name);
- return mScript.top();
- }
-
-
- Script& script()
- {
- return mScript;
- }
-
- const Script& script() const
- {
- return mScript;
- }
-
- private:
-
- Script& mScript;
- };
-
-
- Script() :
- mState(0)
- {
- reset();
- }
-
- ~Script()
- {
- destroy();
- }
-
-
- static ScriptP alloc()
- {
- return ScriptP(new Script);
- }
-
- void reset()
- {
- if (mState) destroy();
- mState = luaL_newstate();
- registry().setField("Script_hh_Object", (void*)this);
- }
-
-
- void importStandardLibraries()
- {
- 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(mState, name.c_str());
- }
-
- Result doString(const std::string& commands)
- {
- return (Result)luaL_dostring(mState, commands.c_str());
- }
-
- Result doFile(const std::string& file)
- {
- return (Result)luaL_dofile(mState, file.c_str());
- }
-
-
- /**
- * Thread-handling methods.
- */
-
- Script pushNewThread()
- {
- return Script(mState);
- }
-
- void pushThread()
- {
- lua_pushthread(mState);
- }
-
- Result resume(int nargs)
- {
- return (Result)lua_resume(mState, nargs);
- }
-
- Result getStatus() const
- {
- return (Result)lua_status(mState);
- }
-
- int yield(int results)
- {
- return lua_yield(mState, results);
- }
-
- bool isMainThread() const
- {
- 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 raise()
- {
- lua_error(mState);
- }
-
-
- /**
- * Get significant values.
- */
-
- Slot globals() const
- {
- return Slot(*this, GLOBALS);
- }
-
- Slot registry() const
- {
- return Slot(*this, REGISTRY);
- }
-
- Slot environment() const
- {
- return Slot(*this, ENVIRONMENT);
- }
-
- Slot top() const
- {
- return Slot(*this, stackSize());
- }
-
- /**
- * Get the size of the stack; this is also the index of the top-most
- * value.
- */
-
- int stackSize() const
- {
- return lua_gettop(mState);
- }
-
- void setStackSize(int size)
- {
- lua_settop(mState, size);
- }
-
- void clearStack()
- {
- setStackSize(0);
- }
-
-
- /**
- * Makes sure there is at least extra more places on the stack.
- * Returns false if space couldn't be created. Just like with the
- * regular Lua API, you are responsible to make sure the stack is big
- * enough to hold whatever you want to push on it. This is usually
- * only an issue if you're pushing stuff in a loop.
- */
-
- bool checkStack(int extra)
- {
- return (bool)lua_checkstack(mState, extra);
- }
-
-
- /**
- * Concatenates the top-most n slots on the stack.
- */
-
- void concatenate(int n = 2)
- {
- lua_concat(mState, n);
- }
-
-
- /**
- * Push some values onto the stack.
- */
-
- template <class T>
- Slot push(T value)
- {
- lua_pushinteger(mState, lua_Integer(value));
- return top();
- }
-
- Slot push(bool value)
- {
- lua_pushboolean(mState, int(value));
- return top();
- }
-
- Slot push(float value)
- {
- lua_pushnumber(mState, (lua_Number)value);
- return top();
- }
- Slot push(double value)
- {
- lua_pushnumber(mState, (lua_Number)value);
- return top();
- }
-
- Slot push(const std::string& value)
- {
- lua_pushlstring(mState, value.c_str(), value.length());
- return top();
- }
- Slot push(const char* value)
- {
- lua_pushstring(mState, value);
- return top();
- }
- Slot push(const char* value, size_t length)
- {
- lua_pushlstring(mState, value, length);
- return top();
- }
-
- Slot push(const Function& function)
- {
- mFunctions.push_back(function);
- lua_pushlightuserdata(mState, (void*)&mFunctions.back());
- lua_pushcclosure(mState, dispatchCall, 1);
- return top();
- }
-
- Slot push(void* data)
- {
- lua_pushlightuserdata(mState, data);
- return top();
- }
-
- Slot pushNil()
- {
- lua_pushnil(mState);
- return top();
- }
-
- Slot pushFromThread(Script& thread, int n)
- {
- lua_xmove(thread.mState, mState, n);
- return top();
- }
-
- Slot pushCode(const std::string& file, Result& result)
- {
- result = (Result)luaL_loadfile(mState, file.c_str());
- return top();
- }
-
- Slot pushCode(const std::string& name, const char* buffer,
- size_t size, Result& result)
- {
- result = (Result)luaL_loadbuffer(mState,
- buffer, size, name.c_str());
- return top();
- }
-
- Slot pushNewData(void*& data, size_t size)
- {
- data = lua_newuserdata(mState, size);
- return top();
- }
-
- Slot pushNewTable(int narr = 0, int nrec = 0)
- {
- lua_createtable(mState, narr, nrec);
- return top();
- }
-
-
- /**
- * Call a function on the stack. The correct procedure is to push a
- * function onto the stack followed by nargs arguments. This method
- * will pop them off upon return, leaving up to nresults return values
- * (default is any number of return values, depending on the callee).
- */
-
- Result call(int nargs = 0, int nresults = LUA_MULTRET)
- {
- return (Result)lua_pcall(mState, nargs, nresults, 0);
- }
-
-
- /**
- * Pops n values from the top of the stack.
- */
-
- void pop(int n = 1)
- {
- lua_pop(mState, n);
- }
-
-
- /**
- * Index into the stack to get a Slot.
- */
-
- Slot operator [] (int index) const
- {
- return Slot(*this, index);
- }
-
-
- /**
- * Control over the garbage collection process.
- */
-
- void collectAll()
- {
- lua_gc(mState, LUA_GCCOLLECT, 0);
- }
-
- void stopCollector()
- {
- lua_gc(mState, LUA_GCSTOP, 0);
- }
-
- void restartCollector()
- {
- lua_gc(mState, LUA_GCRESTART, 0);
- }
-
- int getUsedMemory() const
- {
- // in kilobytes
- return lua_gc(mState, LUA_GCCOUNT, 0);
- }
-
- void collectStep(int step)
- {
- lua_gc(mState, LUA_GCSTEP, step);
- }
-
- void tuneCollector(int pause, int step)
- {
- lua_gc(mState, LUA_GCSETPAUSE, pause);
- lua_gc(mState, LUA_GCSETSTEPMUL, step);
- }
-
-
-private:
-
- Script(lua_State* state) :
- 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_hh_Object");
- Script* script = (Script*)lua_touserdata(state, -1);
- lua_pop(state, 1);
-
- return (*function)(*script);
- }
-
- 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())
- {
- bool value;
- slot.get(value);
- if (value) stream << "true";
- else stream << "false";
- }
- else if (slot.isNil())
- {
- stream << "nil";
- }
- else
- {
- stream << slot.typeName() << " (" << slot.id() << ")";
- }
-
- return stream;
-}
-
-
-} // namespace Mf
-
-#endif // _MOOF_SCRIPT_HH_
-