1 module luad.base; 2 3 import luad.c.all; 4 import luad.stack; 5 6 import core.stdc..string : strlen; 7 8 /** 9 * Enumerates all Lua types. 10 */ 11 enum LuaType 12 { 13 ///string 14 String = LUA_TSTRING, 15 ///number 16 Number = LUA_TNUMBER, 17 //table 18 Table = LUA_TTABLE, 19 ///nil 20 Nil = LUA_TNIL, 21 ///boolean 22 Boolean = LUA_TBOOLEAN, 23 ///function 24 Function = LUA_TFUNCTION, 25 ///userdata 26 Userdata = LUA_TUSERDATA, 27 ///ditto 28 LightUserdata = LUA_TLIGHTUSERDATA, 29 ///thread 30 Thread = LUA_TTHREAD 31 } 32 33 package struct Nil{} 34 35 /** 36 * Special value representing the Lua type and value nil. 37 * Examples: 38 * Useful for clearing keys in a table: 39 * -------------------------- 40 lua["n"] = 1.23; 41 assert(lua.get!double("n") == 1.23); 42 43 lua["n"] = nil; 44 assert(lua["n"].type == LuaType.Nil); 45 * -------------------------- 46 */ 47 public Nil nil; 48 49 /** 50 * Represents a reference to a Lua value of any type. 51 * It contains only the bare minimum of functionality which all Lua values support. 52 * For a generic reference type with more functionality, see $(DPREF dynamic,LuaDynamic). 53 */ 54 struct LuaObject 55 { 56 private: 57 int r = LUA_REFNIL; 58 lua_State* L = null; 59 60 package: 61 this(lua_State* L, int idx) 62 { 63 this.L = L; 64 65 lua_pushvalue(L, idx); 66 r = luaL_ref(L, LUA_REGISTRYINDEX); 67 } 68 69 void push() nothrow 70 { 71 lua_rawgeti(L, LUA_REGISTRYINDEX, r); 72 } 73 74 static void checkType(lua_State* L, int idx, int expectedType, const(char)* expectedName) 75 { 76 int t = lua_type(L, idx); 77 if(t != expectedType) 78 { 79 luaL_error(L, "attempt to create %s with %s", expectedName, lua_typename(L, t)); 80 } 81 } 82 83 public: 84 @trusted this(this) 85 { 86 push(); 87 r = luaL_ref(L, LUA_REGISTRYINDEX); 88 } 89 90 @trusted nothrow ~this() 91 { 92 luaL_unref(L, LUA_REGISTRYINDEX, r); 93 } 94 95 /// The underlying $(D lua_State) pointer for interfacing with C. 96 lua_State* state() pure nothrow @safe @property 97 { 98 return L; 99 } 100 101 /** 102 * Release this reference. 103 * 104 * This reference becomes a nil reference. 105 * This is only required when you want to _release the reference before the lifetime 106 * of this $(D LuaObject) has ended. 107 */ 108 void release() pure nothrow @safe 109 { 110 r = LUA_REFNIL; 111 L = null; 112 } 113 114 /** 115 * Type of referenced object. 116 * See_Also: 117 * $(MREF LuaType) 118 */ 119 @property LuaType type() @trusted nothrow 120 { 121 push(); 122 auto result = cast(LuaType)lua_type(state, -1); 123 lua_pop(state, 1); 124 return result; 125 } 126 127 /** 128 * Type name of referenced object. 129 */ 130 @property string typeName() @trusted /+ nothrow +/ 131 { 132 push(); 133 const(char)* cname = luaL_typename(state, -1); // TODO: Doesn't have to use luaL_typename, i.e. no copy 134 auto name = cname[0.. strlen(cname)].idup; 135 lua_pop(state, 1); 136 return name; 137 } 138 139 /// Boolean whether or not the referenced object is nil. 140 @property bool isNil() pure nothrow @safe 141 { 142 return r == LUA_REFNIL; 143 } 144 145 /** 146 * Convert the referenced object into a textual representation. 147 * 148 * The returned string is formatted in the same way the Lua $(D tostring) function formats. 149 * 150 * Returns: 151 * String representation of referenced object 152 */ 153 string toString() @trusted 154 { 155 push(); 156 157 size_t len; 158 const(char)* cstr = luaL_tolstring(state, -1, &len); 159 auto str = cstr[0 .. len].idup; 160 161 lua_pop(state, 2); 162 return str; 163 } 164 165 /** 166 * Attempt _to convert the referenced object _to the specified D type. 167 * Examples: 168 ----------------------- 169 auto results = lua.doString(`return "hello!"`); 170 assert(results[0].to!string() == "hello!"); 171 ----------------------- 172 */ 173 T to(T)() 174 { 175 static void typeMismatch(lua_State* L, int t, int e) 176 { 177 luaL_error(L, "attempt to convert LuaObject with type %s to a %s", lua_typename(L, t), lua_typename(L, e)); 178 } 179 180 push(); 181 return popValue!(T, typeMismatch)(state); 182 } 183 184 /** 185 * Compare this object to another with Lua's equality semantics. 186 * Also returns false if the two objects are in different Lua states. 187 */ 188 bool opEquals(T : LuaObject)(ref T o) @trusted 189 { 190 if(o.state != this.state) 191 return false; 192 193 push(); 194 o.push(); 195 scope(success) lua_pop(state, 2); 196 197 return lua_equal(state, -1, -2); 198 } 199 } 200 201 unittest 202 { 203 lua_State* L = luaL_newstate(); 204 scope(success) lua_close(L); 205 206 lua_pushstring(L, "foobar"); 207 auto o = popValue!LuaObject(L); 208 209 assert(!o.isNil); 210 assert(o.type == LuaType.String); 211 assert(o.typeName == "string"); 212 assert(o.to!string() == "foobar"); 213 214 lua_pushnil(L); 215 auto nilref = popValue!LuaObject(L); 216 217 assert(nilref.isNil); 218 assert(nilref.typeName == "nil"); 219 220 assert(o != nilref); 221 222 auto o2 = o; 223 assert(o2 == o); 224 }