]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/Script.hh
new lua scripting for scene loading
[chaz/yoink] / src / Moof / Script.hh
diff --git a/src/Moof/Script.hh b/src/Moof/Script.hh
new file mode 100644 (file)
index 0000000..b48f5f4
--- /dev/null
@@ -0,0 +1,670 @@
+
+/*******************************************************************************
+
+ Copyright (c) 2009, Charles McGarvey
+ All rights reserved.
+ Redistribution   and   use  in  source  and  binary  forms,  with  or  without
+ modification, are permitted provided that the following conditions are met:
+   * Redistributions  of  source  code  must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+   * Redistributions  in binary form must reproduce the above copyright notice,
+     this  list of conditions and the following disclaimer in the documentation
+     and/or other materials provided with the distribution.
+ THIS  SOFTWARE  IS  PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND  ANY  EXPRESS  OR  IMPLIED  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED.  IN  NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR  ANY  DIRECT,  INDIRECT,  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES  (INCLUDING,  BUT  NOT  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES;  LOSS  OF  USE,  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*******************************************************************************/
+
+#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 does not try to make the boundary invisible.  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 <list>
+#include <string>
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <lua.hpp>
+
+#include <Moof/Exception.hh>
+
+
+namespace Mf {
+
+
+class Script;
+typedef boost::shared_ptr<Script> ScriptP;
+
+
+struct Script
+{
+       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 STATUS
+       {
+               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 PSEUDO_INDEX
+       {
+               REGISTRY                = LUA_REGISTRYINDEX,
+               ENVIRONMENT             = LUA_ENVIRONINDEX,
+               GLOBALS                 = LUA_GLOBALSINDEX
+       };
+
+       /**
+        * 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
+        * 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
+        * negative index on the stack (the value of which will change as things are
+        * pushed onto and popped from the stack).
+        */
+
+       struct Value
+       {
+               /**
+                * You have direct access to the index of the value on the stack being
+                * represented.
+                */
+
+               int index;
+
+
+               /**
+                * 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.
+                */
+               Value() :
+                       index(0),
+                       state(0) {}
+
+               Value(lua_State* s, int i) :
+                       index(i),
+                       state(s) {}
+
+               // 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); }
+
+               /**
+                * Get the type of the value.
+                */
+
+               TYPE getType() const
+               {
+                       return (TYPE)lua_type(state, index);
+               }
+
+               /**
+                * Get the name of the type of the value as a string.
+                */
+
+               std::string getTypeName() const
+               {
+                       return std::string(lua_typename(state, (int)getType()));
+               }
+
+               /**
+                * Get a pointer value (for userdata, tables, threads, and functions).
+                */
+
+               const void* getIdentifier() const
+               {
+                       return lua_topointer(state, index);
+               }
+
+
+               bool operator == (const Value& rhs) const
+               {
+                       return (bool)lua_equal(state, index, rhs.index);
+               }
+               bool operator != (const Value& rhs) const
+               {
+                       return !(*this == rhs);
+               }
+               bool operator < (const Value& rhs) const
+               {
+                       return (bool)lua_lessthan(state, index, rhs.index);
+               }
+               bool operator <= (const Value& rhs) const
+               {
+                       return *this < rhs || *this == rhs;
+               }
+               bool operator > (const Value& rhs) const
+               {
+                       return !(*this <= rhs);
+               }
+               bool operator >= (const Value& rhs) const
+               {
+                       return !(*this < rhs);
+               }
+               operator bool () const
+               {
+                       return (bool)lua_toboolean(state, index);
+               }
+
+               Value& operator = (const Value& rhs)
+               {
+                       rhs.pushCopy();
+                       replaceWithTop();
+                       return *this;
+               }
+
+
+               /**
+                * 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
+               {
+                       value = (T)lua_tointeger(state, index);
+               }
+
+               void get(bool& value) const
+               {
+                       value = (bool)lua_toboolean(state, index);
+               }
+
+               void get(float& value) const
+               {
+                       value = (float)lua_tonumber(state, index);
+               }
+               void get(double& value) const
+               {
+                       value = (double)lua_tonumber(state, index);
+               }
+
+               void get(std::string& value) const
+               {
+                       size_t size;
+                       const char* str = lua_tolstring(state, index, &size);
+                       value.assign(str, size);
+               }
+
+
+               void set(std::string& value)
+               {
+               }
+
+               //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);
+               }
+
+               /**
+                * Replace this value with the value at the top of the stack.
+                */
+
+               void replaceWithTop()
+               {
+                       lua_replace(state, index);
+               }
+
+               void remove()
+               {
+                       lua_remove(state, index);
+               }
+
+               /**
+                * Inserts the top-most value on the stack at position index, shifting other
+                * values as needed.
+                */
+
+               void insertTopHere()
+               {
+                       lua_insert(state, index);
+               }
+
+               
+               void pushMetatable() const
+               {
+                       lua_getmetatable(state, index);
+               }
+
+               void pushField() const
+               {
+                       lua_gettable(state, index);
+               }
+
+               void pushField(const std::string& name) const
+               {
+                       lua_getfield(state, index, name.c_str());
+               }
+
+
+       private:
+
+               lua_State* state;
+       };
+
+
+       Script() :
+               state_(luaL_newstate())
+       {
+               lua_pushlightuserdata(state_, this);
+               lua_setfield(state_, LUA_REGISTRYINDEX, "_script_obj");
+       }
+
+       ~Script()
+       {
+               if (isMainThread_) lua_close(state_);
+       }
+
+
+       static ScriptP alloc()
+       {
+               return ScriptP(new Script);
+       }
+
+
+       void importStandardLibraries()
+       {
+               luaL_openlibs(state_);
+       }
+
+       void importFunction(const std::string& name, const Function& function)
+       {
+               push(function);
+               lua_setglobal(state_, name.c_str());
+       }
+
+
+       STATUS doString(const std::string& commands)
+       {
+               return (STATUS)luaL_dostring(state_, commands.c_str());
+       }
+
+       STATUS doFile(const std::string& file)
+       {
+               return (STATUS)luaL_dofile(state_, file.c_str());
+       }
+
+
+       /**
+        * Thread-handling methods.
+        */
+
+       Script pushNewThread()
+       {
+               return Script(state_);
+       }
+
+       void pushThread()
+       {
+               lua_pushthread(state_);
+       }
+
+       STATUS resume(int nargs)
+       {
+               return (STATUS)lua_resume(state_, nargs);
+       }
+
+       STATUS getStatus() const
+       {
+               return (STATUS)lua_status(state_);
+       }
+
+       int yield(int results)
+       {
+               return lua_yield(state_, results);
+       }
+
+       bool isMainThread() const
+       {
+               return isMainThread_;
+       }
+
+
+       /**
+        * Get significant values.
+        */
+
+       Value getGlobalTable() const
+       {
+               return Value(state_, GLOBALS);
+       }
+
+       Value getRegistryTable() const
+       {
+               return Value(state_, REGISTRY);
+       }
+
+       Value getEnvironmentTable() const
+       {
+               return Value(state_, ENVIRONMENT);
+       }
+
+       Value getTop() const
+       {
+               return Value(state_, lua_gettop(state_));
+       }
+
+       /**
+        * Get the size of the stack; this is also the index of the top-most value.
+        */
+
+       int getSize() const
+       {
+               return lua_gettop(state_);
+       }
+
+       void setSize(int size)
+       {
+               lua_settop(state_, size);
+       }
+
+       void clear()
+       {
+               setSize(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(state_, extra);
+       }
+
+
+       /**
+        * Concatenates the top-most n values on the stack.
+        */
+
+       void concat(int n)
+       {
+               lua_concat(state_, n);
+       }
+
+
+       /**
+        * Push some values onto the stack.
+        */
+
+       template <typename T>
+       void push(T value)
+       {
+               lua_pushinteger(state_, lua_Integer(value));
+       }
+
+       void push(bool value)
+       {
+               lua_pushboolean(state_, int(value));
+       }
+
+       void push(float value)
+       {
+               lua_pushnumber(state_, (lua_Number)value);
+       }
+       void push(double value)
+       {
+               lua_pushnumber(state_, (lua_Number)value);
+       }
+
+       void push(const std::string& value)
+       {
+               lua_pushlstring(state_, value.c_str(), value.length());
+       }
+       void push(const char* value, size_t length)
+       {
+               lua_pushlstring(state_, value, length);
+       }
+
+       void push(const Function& function)
+       {
+               functions_.push_back(function);
+
+               lua_pushlightuserdata(state_, (void*)&functions_.back());
+               lua_pushcclosure(state_, dispatchCall, 1);
+       }
+
+       void push(void* data)
+       {
+               lua_pushlightuserdata(state_, data);
+       }
+
+       void pushNil()
+       {
+               lua_pushnil(state_);
+       }
+
+       void pushFromThread(Script& thread, int n)
+       {
+               lua_xmove(thread.state_, state_, n);
+       }
+
+       STATUS pushCode(const std::string& filename)
+       {
+               return (STATUS)luaL_loadfile(state_, filename.c_str());
+       }
+
+       STATUS pushCode(const std::string& name, const char* buffer, size_t size)
+       {
+               return (STATUS)luaL_loadbuffer(state_, buffer, size, name.c_str());
+       }
+
+       void* pushNewData(size_t size)
+       {
+               return lua_newuserdata(state_, size);
+       }
+
+       void pushNewTable()
+       {
+               lua_newtable(state_);
+       }
+
+
+       /**
+        * 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).
+        */
+
+       STATUS call(int nargs, int nresults = LUA_MULTRET)
+       {
+               return (STATUS)lua_pcall(state_, nargs, nresults, 0);
+       }
+
+
+       /**
+        * Pops n values from the top of the stack.
+        */
+
+       void pop(int n)
+       {
+               lua_pop(state_, n);
+       }
+
+
+       /**
+        * Index into the stack to get a Value.
+        */
+
+       Value operator [] (int index) const
+       {
+               return Value(state_, index);
+       }
+
+
+       /**
+        * Getting and setting fields of a table.
+        */
+
+       void get(const std::string& field,  int index = GLOBALS) const
+       {
+               lua_getfield(state_, index, field.c_str());
+       }
+
+       void set(const std::string& field, int index = GLOBALS)
+       {
+               lua_setfield(state_, index, field.c_str());
+       }
+
+
+       /**
+        * Control over the garbage collection process.
+        */
+
+       void collectAll()
+       {
+               lua_gc(state_, LUA_GCCOLLECT, 0);
+       }
+
+       void stopCollector()
+       {
+               lua_gc(state_, LUA_GCSTOP, 0);
+       }
+
+       void restartCollector()
+       {
+               lua_gc(state_, LUA_GCRESTART, 0);
+       }
+
+       int getUsedMemory() const
+       {
+               // in kilobytes
+               return lua_gc(state_, LUA_GCCOUNT, 0);
+       }
+
+       void collectStep(int step)
+       {
+               lua_gc(state_, LUA_GCSTEP, step);
+       }
+
+       void tuneCollector(int pause, int step)
+       {
+               lua_gc(state_, LUA_GCSETPAUSE, pause);
+               lua_gc(state_, 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) {}
+
+       static int dispatchCall(lua_State* state)
+       {
+               const Function* function = (const Function*)lua_touserdata(state,
+                               lua_upvalueindex(1));
+
+               lua_getfield(state, LUA_REGISTRYINDEX, "_script_obj");
+               Script* script = (Script*)lua_touserdata(state, -1);
+               lua_pop(state, 1);
+
+               return (*function)(*script);
+       }
+
+       lua_State*                      state_;
+       bool                            isMainThread_;
+       std::list<Function>     functions_;
+};
+
+
+} // namespace Mf
+
+#endif // _MOOF_SCRIPT_HH_
+
+/** vim: set ts=4 sw=4 tw=80: *************************************************/
+
This page took 0.030299 seconds and 4 git commands to generate.