1 module luad.table; 2 3 import luad.c.all; 4 5 import luad.base; 6 import luad.stack; 7 import luad.conversions.structs; 8 9 /// Represents a Lua table. 10 struct LuaTable 11 { 12 /// LuaTable sub-types $(DPREF base, LuaObject) through this reference. 13 LuaObject object; 14 15 alias object this; 16 17 package this(lua_State* L, int idx) 18 { 19 LuaObject.checkType(L, idx, LUA_TTABLE, "LuaTable"); 20 object = LuaObject(L, idx); 21 } 22 23 /** 24 * Lookup a value in this table or in a sub-table of this table. 25 * Params: 26 * T = type of value 27 * args = list of keys, where all keys but the last one should result in a table 28 * Returns: 29 * $(D t[k]) where $(D t) is the table for the second-to-last parameter, and $(D k) is the last parameter 30 * 31 * Examples: 32 * ---------------------- 33 auto execute = lua.get!LuaFunction("os", "execute"); 34 execute(`echo hello, world!`); 35 * ---------------------- 36 */ 37 T get(T, U...)(U args) @trusted 38 { 39 this.push(); 40 41 foreach(key; args) 42 { 43 pushValue(this.state, key); 44 lua_gettable(this.state, -2); 45 } 46 47 auto ret = getValue!T(this.state, -1); 48 lua_pop(this.state, args.length + 1); 49 return ret; 50 } 51 52 /** 53 * Read a string value in this table without making a copy of the string. 54 * The read string is passed to $(D dg), and should not be escaped. 55 * If the value for $(D key) is not a string, $(D dg) is not called. 56 * Params: 57 * key = lookup _key 58 * dg = delegate to receive string 59 * Returns: 60 * $(D true) if the value for $(D key) was a string and passed to $(D dg), $(D false) otherwise 61 * Examples: 62 -------------------- 63 t[2] = "two"; 64 t.readString(2, str => assert(str == "two")); 65 -------------------- 66 */ 67 bool readString(T)(T key, scope void delegate(in char[] str) dg) @trusted 68 { 69 this.push(); 70 scope(exit) lua_pop(this.state, 1); 71 72 pushValue(this.state, key); 73 74 lua_gettable(this.state, -2); 75 scope(exit) lua_pop(this.state, 1); 76 77 if(lua_isstring(this.state, -1) == 0) 78 return false; 79 80 size_t len; 81 const(char)* cstr = lua_tolstring(this.state, -1, &len); 82 83 dg(cstr[0 .. len]); 84 return true; 85 } 86 87 /** 88 * Same as calling $(D get!LuaObject) with the same arguments. 89 * Examples: 90 * --------------------- 91 auto luapath = lua["package", "path"]; 92 writefln("LUA_PATH:\n%s", luapath); 93 * --------------------- 94 * See_Also: 95 * $(MREF LuaTable.get) 96 */ 97 LuaObject opIndex(T...)(T args) 98 { 99 return get!LuaObject(args); 100 } 101 102 /** 103 * Set a key-value pair in this table. 104 * Params: 105 * key = key to _set 106 * value = value for $(D key) 107 */ 108 void set(T, U)(T key, U value) @trusted 109 { 110 this.push(); 111 scope(success) lua_pop(this.state, 1); 112 113 pushValue(this.state, key); 114 pushValue(this.state, value); 115 lua_settable(this.state, -3); 116 } 117 118 /** 119 * Set a key-value pair this table or in a sub-table of this table. 120 * Params: 121 * value = value to set 122 * args = list of keys, where all keys but the last one should result in a table 123 * Returns: 124 * $(D t[k] = value), where $(D t) is the table for the second-to-last parameter in args, 125 * and $(D k) is the last parameter in args 126 * 127 * Examples: 128 * ---------------------- 129 lua["string", "empty"] = (in char[] s){ return s.length == 0; }; 130 lua.doString(`assert(string.empty(""))`); 131 * ---------------------- 132 */ 133 void opIndexAssign(T, U...)(T value, U args) @trusted 134 { 135 this.push(); 136 scope(success) lua_pop(this.state, 1); 137 138 foreach(i, arg; args) 139 { 140 static if(i != args.length - 1) 141 { 142 pushValue(this.state, arg); 143 lua_gettable(this.state, -2); 144 } 145 } 146 147 pushValue(this.state, args[$-1]); 148 pushValue(this.state, value); 149 lua_settable(this.state, -3); 150 151 lua_pop(this.state, args.length - 1); 152 } 153 154 /** 155 * Create struct of type $(D T) and fill its members with fields from this table. 156 * 157 * Struct fields that are not present in this table are left at their default value. 158 * 159 * Params: 160 * T = any struct type 161 * 162 * Returns: 163 * Newly created struct 164 */ 165 T toStruct(T)() @trusted if (is(T == struct)) 166 { 167 push(); 168 return popValue!T(this.state); 169 } 170 171 /** 172 * Fill a struct's members with fields from this table. 173 * Params: 174 * s = struct to fill 175 */ 176 void copyTo(T)(ref T s) @trusted if (is(T == struct)) 177 { 178 push(); 179 fillStruct(this.state, -1, s); 180 lua_pop(L, 1); 181 } 182 183 /** 184 * Set the metatable for this table. 185 * Params: 186 * meta = new metatable 187 */ 188 void setMetaTable(ref LuaTable meta) @trusted 189 in{ assert(this.state == meta.state); } 190 body 191 { 192 this.push(); 193 meta.push(); 194 lua_setmetatable(this.state, -2); 195 lua_pop(this.state, 1); 196 } 197 198 /** 199 * Get the metatable for this table. 200 * Returns: 201 * A reference to the metatable for this table. The reference is nil if this table has no metatable. 202 */ 203 LuaTable getMetaTable() @trusted 204 { 205 this.push(); 206 scope(success) lua_pop(this.state, 1); 207 208 return lua_getmetatable(this.state, -1) == 0? LuaTable() : popValue!LuaTable(this.state); 209 } 210 211 /** 212 * Iterate over the values in this table. 213 */ 214 int opApply(T)(int delegate(ref T value) dg) @trusted 215 { 216 this.push(); 217 lua_pushnil(this.state); 218 while(lua_next(this.state, -2) != 0) 219 { 220 auto value = popValue!T(this.state); 221 int result = dg(value); 222 if(result != 0) 223 { 224 lua_pop(this.state, 2); 225 return result; 226 } 227 } 228 lua_pop(this.state, 1); 229 return 0; 230 } 231 232 /** 233 * Iterate over the key-value pairs in this table. 234 */ 235 int opApply(T, U)(int delegate(ref U key, ref T value) dg) @trusted 236 { 237 this.push(); 238 lua_pushnil(this.state); 239 while(lua_next(this.state, -2) != 0) 240 { 241 auto value = popValue!T(this.state); 242 auto key = getValue!U(this.state, -1); 243 244 int result = dg(key, value); 245 if(result != 0) 246 { 247 lua_pop(this.state, 2); 248 return result; 249 } 250 } 251 lua_pop(this.state, 1); 252 return 0; 253 } 254 } 255 256 unittest 257 { 258 lua_State* L = luaL_newstate(); 259 scope(success) 260 { 261 assert(lua_gettop(L) == 0); 262 lua_close(L); 263 } 264 265 lua_newtable(L); 266 auto t = popValue!LuaTable(L); 267 268 assert(t.type == LuaType.Table); 269 270 t.set("foo", "bar"); 271 assert(t.get!string("foo") == "bar"); 272 273 t.set("foo", nil); 274 assert(t.get!LuaObject("foo").isNil); 275 276 t.set("foo", ["outer": ["inner": "hi!"]]); 277 auto s = t.get!(string)("foo", "outer", "inner"); 278 assert(s == "hi!"); 279 280 auto o = t["foo", "outer"]; 281 assert(o.type == LuaType.Table); 282 283 t["foo", "outer", "inner"] = "hello!"; 284 auto s2 = t.get!(string)("foo", "outer", "inner"); 285 assert(s2 == "hello!"); 286 287 // readString 288 t[2] = "two"; 289 bool success = t.readString(2, (in char[] str) { 290 assert(str == "two"); 291 }); 292 assert(success); 293 294 t[2] = true; 295 success = t.readString(2, (in char[] str) { assert(false); }); 296 assert(!success); 297 298 // metatable 299 pushValue(L, ["__index": (LuaObject self, string key){ 300 return key; 301 }]); 302 auto meta = popValue!LuaTable(L); 303 304 lua_newtable(L); 305 auto t2 = popValue!LuaTable(L); 306 307 t2.setMetaTable(meta); 308 309 auto test = t2.get!string("foobar"); 310 assert(test == "foobar"); 311 312 assert(t2.getMetaTable() == meta); 313 314 // opApply 315 auto input = [1, 2, 3]; 316 pushValue(L, input); 317 auto applyTest = popValue!LuaTable(L); 318 319 int i = 0; 320 foreach(int v; applyTest) 321 { 322 assert(input[i++] == v); 323 } 324 325 auto inputWithKeys = ["one": 1, "two": 2, "three": 3]; 326 pushValue(L, inputWithKeys); 327 auto applyTestKeys = popValue!LuaTable(L); 328 329 foreach(string key, int value; applyTestKeys) 330 { 331 assert(inputWithKeys[key] == value); 332 } 333 }