]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/Script.hh
ray-scene intersection
[chaz/yoink] / src / Moof / Script.hh
index b48f5f440ae04b49f3a6f778cf3e3e3eb4d3ccb1..f67c62c20d27f67a09732650d4cdb4adb70b88c4 100644 (file)
@@ -39,7 +39,9 @@
  */
 
 #include <list>
+#include <map>
 #include <string>
+#include <vector>
 
 #include <boost/bind.hpp>
 #include <boost/function.hpp>
@@ -47,7 +49,7 @@
 
 #include <lua.hpp>
 
-#include <Moof/Exception.hh>
+#include <Moof/Log.hh>
 
 
 namespace Mf {
@@ -57,11 +59,13 @@ class Script;
 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,
@@ -75,7 +79,7 @@ struct Script
                THREAD                  = LUA_TTHREAD
        };
 
-       enum STATUS
+       enum Status
        {
                SUCCESS                 = 0,
                YIELD                   = LUA_YIELD,
@@ -86,7 +90,7 @@ struct Script
                FILE_ERROR              = LUA_ERRFILE
        };
 
-       enum PSEUDO_INDEX
+       enum PseudoIndex
        {
                REGISTRY                = LUA_REGISTRYINDEX,
                ENVIRONMENT             = LUA_ENVIRONINDEX,
@@ -94,7 +98,7 @@ struct Script
        };
 
        /**
-        * This is the most noticeable abstraction on top of the standard Lua API.
+        * This is the most prominent 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
@@ -119,35 +123,112 @@ struct Script
                 * 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) :
+               Value(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.
+                */
+
+               Value(const Value& 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);
+               }
+
+
+               Value& requireBoolean()
+               {
+                       if (!isBoolean()) luaL_typerror(mState, index, "boolean");
+                       return *this;
+               }
+               Value& requireNumber()
+               {
+                       if (!isNumber()) luaL_typerror(mState, index, "number");
+                       return *this;
+               }
+               Value& requireString()
+               {
+                       if (!isString()) luaL_typerror(mState, index, "string");
+                       return *this;
+               }
+               Value& requireTable()
+               {
+                       if (!isTable()) luaL_typerror(mState, index, "table");
+                       return *this;
+               }
+               Value& requireFunction()
+               {
+                       if (!isFunction()) luaL_typerror(mState, index, "function");
+                       return *this;
+               }
+               Value& requireData()
+               {
+                       if (!isData()) luaL_typerror(mState, index, "data");
+                       return *this;
+               }
+               Value& requireNil()
+               {
+                       if (!isNil()) luaL_typerror(mState, index, "nil");
+                       return *this;
+               }
+               Value& 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);
                }
 
                /**
@@ -156,22 +237,39 @@ struct Script
 
                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
                {
-                       return (bool)lua_equal(state, index, rhs.index);
+                       return (bool)lua_equal(mState, index, rhs.index);
                }
                bool operator != (const Value& rhs) const
                {
@@ -179,7 +277,7 @@ struct Script
                }
                bool operator < (const Value& rhs) const
                {
-                       return (bool)lua_lessthan(state, index, rhs.index);
+                       return (bool)lua_lessthan(mState, index, rhs.index);
                }
                bool operator <= (const Value& rhs) const
                {
@@ -195,7 +293,7 @@ struct Script
                }
                operator bool () const
                {
-                       return (bool)lua_toboolean(state, index);
+                       return (bool)lua_toboolean(mState, index);
                }
 
                Value& operator = (const Value& rhs)
@@ -206,61 +304,115 @@ struct Script
                }
 
 
-               /**
-                * 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();
+
+                       Value   value(mState, -1);
+                       int             realIndex = getRealIndex();
+
+                       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);
+                       }
 
-               void set(std::string& value)
+                       return true;
+               }
+
+               template <typename T>
+               bool get(std::map<std::string,T>& dictionary) const
                {
+                       if (!isTable()) return false;
+
+                       dictionary.clear();
+
+                       Value   key(mState, -2);
+                       Value   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.
@@ -268,7 +420,7 @@ struct Script
 
                void pushCopy() const
                {
-                       lua_pushvalue(state, index);
+                       lua_pushvalue(mState, index);
                }
 
                /**
@@ -277,12 +429,12 @@ struct Script
 
                void replaceWithTop()
                {
-                       lua_replace(state, index);
+                       lua_replace(mState, index);
                }
 
                void remove()
                {
-                       lua_remove(state, index);
+                       lua_remove(mState, index);
                }
 
                /**
@@ -292,42 +444,47 @@ struct Script
 
                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();
        }
 
 
@@ -336,27 +493,92 @@ struct Script
                return ScriptP(new Script);
        }
 
+       void reset()
+       {
+               if (mState) destroy();
+               mState = luaL_newstate();
+               lua_pushlightuserdata(mState, this);
+               lua_setfield(mState, LUA_REGISTRYINDEX, "_script_obj");
+       }
+
 
        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)
+       Status doString(const std::string& commands)
        {
-               return (STATUS)luaL_dostring(state_, commands.c_str());
+               return (Status)luaL_dostring(mState, commands.c_str());
        }
 
-       STATUS doFile(const std::string& file)
+       Status doFile(const std::string& file)
        {
-               return (STATUS)luaL_dofile(state_, file.c_str());
+               return (Status)luaL_dofile(mState, file.c_str());
        }
 
 
@@ -366,32 +588,48 @@ struct Script
 
        Script pushNewThread()
        {
-               return Script(state_);
+               return Script(mState);
        }
 
        void pushThread()
        {
-               lua_pushthread(state_);
+               lua_pushthread(mState);
        }
 
-       STATUS resume(int nargs)
+       Status resume(int nargs)
        {
-               return (STATUS)lua_resume(state_, nargs);
+               return (Status)lua_resume(mState, nargs);
        }
 
-       STATUS getStatus() const
+       Status getStatus() const
        {
-               return (STATUS)lua_status(state_);
+               return (Status)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);
        }
 
 
@@ -401,22 +639,22 @@ struct Script
 
        Value getGlobalTable() const
        {
-               return Value(state_, GLOBALS);
+               return Value(mState, GLOBALS);
        }
 
        Value getRegistryTable() const
        {
-               return Value(state_, REGISTRY);
+               return Value(mState, REGISTRY);
        }
 
        Value getEnvironmentTable() const
        {
-               return Value(state_, ENVIRONMENT);
+               return Value(mState, ENVIRONMENT);
        }
 
        Value getTop() const
        {
-               return Value(state_, lua_gettop(state_));
+               return Value(mState, lua_gettop(mState));
        }
 
        /**
@@ -425,12 +663,12 @@ struct Script
 
        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()
@@ -449,7 +687,7 @@ struct Script
 
        bool checkStack(int extra)
        {
-               return (bool)lua_checkstack(state_, extra);
+               return (bool)lua_checkstack(mState, extra);
        }
 
 
@@ -459,7 +697,7 @@ struct Script
 
        void concat(int n)
        {
-               lua_concat(state_, n);
+               lua_concat(mState, n);
        }
 
 
@@ -470,73 +708,77 @@ struct Script
        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)
+       Status pushCode(const std::string& filename)
        {
-               return (STATUS)luaL_loadfile(state_, filename.c_str());
+               return (Status)luaL_loadfile(mState, filename.c_str());
        }
 
-       STATUS pushCode(const std::string& name, const char* buffer, size_t size)
+       Status pushCode(const std::string& name, const char* buffer, size_t size)
        {
-               return (STATUS)luaL_loadbuffer(state_, buffer, size, name.c_str());
+               return (Status)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);
        }
 
 
@@ -547,9 +789,9 @@ struct Script
         * is any number of return values, depending on the callee).
         */
 
-       STATUS call(int nargs, int nresults = LUA_MULTRET)
+       Status call(int nargs, int nresults = LUA_MULTRET)
        {
-               return (STATUS)lua_pcall(state_, nargs, nresults, 0);
+               return (Status)lua_pcall(mState, nargs, nresults, 0);
        }
 
 
@@ -557,9 +799,9 @@ struct Script
         * 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);
        }
 
 
@@ -569,7 +811,7 @@ struct Script
 
        Value operator [] (int index) const
        {
-               return Value(state_, index);
+               return Value(mState, index);
        }
 
 
@@ -579,12 +821,12 @@ struct Script
 
        void get(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());
        }
 
 
@@ -594,55 +836,42 @@ struct Script
 
        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)
        {
@@ -656,9 +885,14 @@ private:
                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;
 };
 
 
This page took 0.044038 seconds and 4 git commands to generate.