/* * Baltisot * Copyright (C) 1999-2008 Nicolas "Pixel" Noble * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __BLUA_H__ #define __BLUA_H__ #include struct lua_State; extern "C" { #include #include } #include #include #include #undef isnumber class LuaPrinter : public Base { public: virtual void puts(const char *); }; class Lua; class LuaObject : public Base { public: LuaObject() : wantdestruct(false), pushed(false) {} virtual void push(Lua *) throw (GeneralException); static void * getme(Lua *, int = 1); void pushdestruct(Lua *) throw (GeneralException); protected: virtual void pushmembers(Lua *) = 0; void pushme(Lua *, void * = 0, const String & = "", bool = true); static void pushit(Lua *, const String &, lua_CFunction); static void pushmeta(Lua *, const String &, lua_CFunction); bool wantdestruct, pushed; }; typedef int (*openlualib_t)(lua_State * L); //!Basic LUA engine. /*! This will create an LUA context, and provide mechanisms to interact with it. This is somehow not threadsafe, and is intended to be so. Don't use this class directly if you're not in the main Lua thread. */ class Lua : public Base { public: Lua(); Lua(const Lua &) throw (GeneralException); virtual ~Lua(); void open_base(); void open_table(); void open_io(bool safe = true); void open_string(); void open_math(); void open_debug(); void open_dir(); void open_bit(); void open_jit(); int wrap_open(openlualib_t open) { return open(L); } void declarefunc(const String &, lua_CFunction, int = LUA_GLOBALSINDEX); void call(const String &, int = LUA_GLOBALSINDEX, int = 0, int = 0); void call(int = 0, int = 0) throw (GeneralException); void push() { lua_pushnil(L); } void push(lua_Number n) { lua_pushnumber(L, n); } void push(const String & s) { lua_pushlstring(L, s.to_charp(), s.strlen()); } void push(bool b) { lua_pushboolean(L, b); } void push(const char *, int size = -1); void push(void * p) { lua_pushlightuserdata(L, p); } void push(lua_CFunction f, int n = 0) { lua_pushcclosure(L, f, n); } void pop(int n = 1) { lua_pop(L, n); } int next(int t = -2) { lua_next(L, t); } void copy(int n = -1) { lua_pushvalue(L, n); } void remove(int n = 1) { lua_remove(L, n); } void insert(int n = 1) { lua_insert(L, n); } void replace(int n = 1) { lua_replace(L, n); } void newtable() { lua_newtable(L); } void * newuser(size_t s) { return lua_newuserdata(L, s); } void settable(int = -3, bool raw = false); void gettable(int = -2, bool raw = false); void setvar() { lua_settable(L, LUA_GLOBALSINDEX); } int gettop() { return lua_gettop(L); } void getglobal(const String &) throw (GeneralException); void push_lua_context(); void error(const String &); int status() { return 0; } int type(int i = -1) { return lua_type(L, i); } bool isnil(int i = -1) { return lua_isnil(L, i); } bool isboolean(int i = -1) { return lua_isboolean(L, i); } bool isnumber(int i = -1) { return lua_isnumber(L, i); } bool isstring(int i = -1) { return lua_isstring(L, i); } bool istable(int i = -1) { return lua_istable(L, i); } bool isfunction(int i = -1) { return lua_isfunction(L, i); } bool iscfunction(int i = -1) { return lua_iscfunction(L, i); } bool isuserdata(int i = -1) { return lua_isuserdata(L, i); } bool islightuserdata(int i = -1) { return lua_islightuserdata(L, i); } bool isobject(int i = -1); bool toboolean(int i = -1) { return lua_toboolean(L, i); } lua_Number tonumber(int i = -1) { return lua_tonumber(L, i); } String tostring(int i = -1); lua_CFunction tocfunction(int i = -1) { return lua_tocfunction(L, i); } void * touserdata(int i = -1) { return lua_touserdata(L, i); } Lua * tothread(int i = -1) { return find(lua_tothread(L, i)); } String escape_string(const String &); void load(Handle *, bool docall = true) throw (GeneralException); void load(const String &, bool docall = true) throw (GeneralException); void dump(Handle *, bool strip = true, int listing = 0); void dumpvars(Handle *, const String &, int = -1); Lua * thread(bool saveit = true); Lua * thread(const String &, int nargs = 0, bool saveit = true); Lua * thread(Handle *, int nargs = 0, bool saveit = true); void weaken(); int yield(int nresults = 0) { return lua_yield(L, nresults); } bool resume(int nargs = 0) throw (GeneralException); bool resume(const String &, int nargs = 0); bool resume(Handle *, int nargs = 0); static Lua * find(lua_State *) throw (GeneralException); void showstack(int level = M_INFO); void showerror(); int getmetatable(int i = -1) { return lua_getmetatable(L, i); } int setmetatable(int i = -2) { return lua_setmetatable(L, i); } int sethook(lua_Hook func, int mask, int count) { return lua_sethook(L, func, mask, count); } void do_break() {/* need to be done within LuaJIT with a hook */} bool is_protected() { return _protected; } void openlib(const String & libname, const struct luaL_reg *l, int nup) { luaL_openlib(L, libname.to_charp(), l, nup); } void setgcthreshold(int = 0) { /***TODO***/ } int getgcthreshold() { return 0; /***TODO***/ } int getgccount() { return lua_getgccount(L); } Lua * Father() { return father ? father : this; } void SetPrinter(LuaPrinter * lp) { lprinter = lp; } void puts(const char * msg) { lprinter->puts(msg); } template T * recast(int n = 1) { Base * b; T * r; b = (Base *) LuaObject::getme(this, n); if (!b) { error("Base object required; got null."); } r = dynamic_cast(b); if (!r) { error(String("Object not compatible; expecting ") + typeid(r).name() + " but got *" + typeid(*b).name() + " instead."); } return r; } protected: virtual Lua * spawn_from_thread(lua_State *); Lua(lua_State *); private: void setup_state(lua_State *); lua_State * L; static std::map lualist; bool _protected, _is_thread; void dumpvars_r(Handle *, int, int = 0) throw (GeneralException); Lua * father; LuaPrinter * lprinter; friend class LuaStatics; }; class LuaException : public GeneralException { public: LuaException(String fn) : GeneralException(fn) { } protected: LuaException() { } }; enum Lua_types_t { BLUA_OBJECT = 0x01, BLUA_TABLE = 0x02, BLUA_BOOLEAN = 0x04, BLUA_NUMBER = 0x08, BLUA_STRING = 0x10, BLUA_FUNCTION = 0x20, BLUA_NIL = 0x40, BLUA_USERDATA = 0x80, BLUA_ANY = 0xff, }; #define MAX_TYPE 8 #define MAXARGS 32 struct lua_functypes_t { int number; const char * name; int minargs, maxargs; int argtypes[MAXARGS]; }; #define DECLARE_METHOD(classname, enumvar) static int method_##enumvar(lua_State * L) { \ return LuaHelpers::method_multiplex( \ enumvar, \ L, \ sLua_##classname::classname##_proceed, \ 0, \ classname##_methods, \ true); \ } #define DECLARE_FUNCTION(classname, enumvar) static int function_##enumvar(lua_State * L) { \ return LuaHelpers::method_multiplex( \ enumvar, \ L, \ 0, \ sLua_##classname::classname##_proceed_statics, \ classname##_functions, \ false); \ } #define PUSH_METHOD(classname, enumvar) pushit( \ L, \ classname##_methods[enumvar].name, \ sLua_##classname::method_##enumvar) #define PUSH_METAMETHOD(classname, enumvar) pushmeta( \ L, \ String("__") + classname##_methods[enumvar].name, \ sLua_##classname::method_##enumvar) #define PUSH_FUNCTION(classname, enumvar) L->declarefunc( \ classname##_functions[enumvar].name, \ sLua_##classname::function_##enumvar) #define CHECK_METHODS(classname) { \ int i = 0; \ while (classname##_methods[i].number != -1) { \ if (i != classname##_methods[i].number) { \ throw GeneralException("Data of " #classname "_methods inconsistants!"); \ } \ i++; \ } \ } #define CHECK_FUNCTIONS(classname) { \ int i = 0; \ while (classname##_functions[i].number != -1) { \ if (i != classname##_functions[i].number) { \ throw GeneralException("Data of " #classname "_functions inconsistants!"); \ } \ i++; \ } \ } template class LuaHelpers : public Base { public: static int method_multiplex(int caller, lua_State * __L, int (*proceed)(Lua * L, int n, T * obj, int caller), int (*proceed_static)(Lua * L, int n, int caller), lua_functypes_t * tab, bool method) { Lua * L = Lua::find(__L); int add = method ? 1 : 0; int n = L->gettop() - add; T * obj = 0; int i, j, mask; bool invalid = false, arg_valid; if (method) obj = (T *) LuaObject::getme(L); if ((n < tab[caller].minargs) || (n > tab[caller].maxargs)) { invalid = true; } else { for (i = 0; i < tab[caller].maxargs && !invalid; i++) { if (n >= (i + 1)) { arg_valid = false; for (j = 0; j < MAX_TYPE && !arg_valid; j++) { mask = 1 << j; if (tab[caller].argtypes[i] & mask) { switch(mask) { case BLUA_OBJECT: if (L->istable(i + 1 + add)) { L->push("__obj"); L->gettable(i + 1 + add); arg_valid = L->isuserdata(); L->pop(); } else { arg_valid = L->isnil(i + 1 + add); } break; case BLUA_TABLE: arg_valid = L->istable(i + 1 + add); break; case BLUA_BOOLEAN: arg_valid = L->isboolean(i + 1 + add); break; case BLUA_NUMBER: arg_valid = L->isnumber(i + 1 + add); break; case BLUA_STRING: arg_valid = L->isstring(i + 1 + add); break; case BLUA_FUNCTION: arg_valid = L->isfunction(i + 1 + add); break; case BLUA_NIL: arg_valid = L->isnil(i + 1 + add); break; case BLUA_USERDATA: arg_valid = L->isuserdata(i + 1 + add) || L->islightuserdata(i + 1 + add); break; } } } invalid = !arg_valid; } } } if (invalid) { if (method) { L->error(String("Invalid arguments to method `") + typeid(T).name() + "::" + tab[caller].name + "'"); } else { L->error(String("Invalid arguments to function `") + typeid(T).name() + " " + tab[caller].name + "'"); } } if (method) { return proceed(L, n, obj, caller); } else { return proceed_static(L, n, caller); } } }; template T * lua_recast(Lua * L, int n = 1) { return L->recast(n); } /*******************************\ |** Let's have a sample of use **| \*******************************/ #ifdef THIS_IS_A_SAMPLE_WHICH_DOES_NOT_COMPILE Luacdfile::Luacdfile(cdfile * h) : LuaHandle(h) { } enum cdfile_methods_t { CDFILE_XXX = 0, CDFILE_YYY }; enum cdfile_functions_t { CDFILE_NEWCDFILE = 0, }; struct lua_functypes_t cdutils_methods[] = { { CDFILE_XXX, "xxx", 1, 1, {LUA_OBJECT} }, { CDFILE_YYY, "yyy", 0, 2, {LUA_NUMBER, LUA_NUMBER} }, { -1, 0, 0, 0, 0 } }; struct lua_functypes_t cdfile_functions[] = { { CDFILE_NEWCDFILE, "cdfile", 1, 4, {LUA_OBJECT, LUA_ANY, LUA_NUMBER, LUA_NUMBER} }, { -1, 0, 0, 0, 0 } }; class sLua_cdfile : public Base { public: static int newcdfile(lua_State * L); DECLARE_METHOD(cdfile, CDFILE_XXX); DECLARE_METHOD(cdfile, CDFILE_YYY); DECLARE_FUNCTION(cdfile, CDFILE_NEWCDFILE); private: static int cdfile_proceed(Lua * L, int n, cdfile * obj, int caller); static int cdfile_proceed_statics(Lua * L, int n, int caller); }; void Luacdfile::pushmembers(Lua * L) { { LuaHandle::pushmembers(L); or pushme(L, SomeObject); } PUSH_METHOD(cdfile, CDFILE_XXX); PUSH_METHOD(cdfile, CDFILE_YYY); } void Luacdfile::pushstatics(Lua * L) { CHECK_METHODS(cdfile); CHECK_FUNCTIONS(cdfile); PUSH_FUNCTION(cdfile, CDFILE_NEWCDFILE); } int sLua_cdfile::cdfile_proceed(Lua * L, int n, cdfile * cdfile, int caller) { int r = 0; SomeObj * obj; int arg1 = DEFAULT1, arg2 = DEFAULT2; switch(caller) { case CDFILE_XXX: obj = (SomeObj *) LuaObject::getme(L, 2); cdfile->xxx(obj); break; case CDFILE_YYY: if (n >= 1) arg1 = L->tonumber(2); if (n >= 2) arg2 = L->tonumber(3); L->push((lua_Number) cdfile->yyy(arg1, arg2)); r = 1; break; } return r; } int sLua_cdfile::cdfile_proceed_statics(Lua * L, int n, int caller) { int r = 0; switch(caller) { case CDFILE_NEWCDFILE: /****TODO****/ break; } return r; } #endif #endif