350 lines
7.4 KiB
D
350 lines
7.4 KiB
D
module luad.table;
|
|
|
|
import luad.c.all;
|
|
|
|
import luad.base;
|
|
import luad.stack;
|
|
import luad.conversions.structs;
|
|
|
|
/// Represents a Lua table.
|
|
struct LuaTable
|
|
{
|
|
/// LuaTable sub-types $(DPREF base, LuaObject) through this reference.
|
|
LuaObject object;
|
|
|
|
alias object this;
|
|
|
|
package this(lua_State* L, int idx)
|
|
{
|
|
LuaObject.checkType(L, idx, LUA_TTABLE, "LuaTable");
|
|
object = LuaObject(L, idx);
|
|
}
|
|
|
|
/**
|
|
* Lookup a value in this table or in a sub-table of this table.
|
|
* Params:
|
|
* T = type of value
|
|
* args = list of keys, where all keys but the last one should result in a table
|
|
* Returns:
|
|
* $(D t[k]) where $(D t) is the table for the second-to-last parameter, and $(D k) is the last parameter
|
|
*
|
|
* Examples:
|
|
* ----------------------
|
|
auto execute = lua.get!LuaFunction("os", "execute");
|
|
execute(`echo hello, world!`);
|
|
* ----------------------
|
|
*/
|
|
T get(T, U...)(U args) @trusted
|
|
{
|
|
this.push();
|
|
|
|
foreach(key; args)
|
|
{
|
|
pushValue(this.state, key);
|
|
lua_gettable(this.state, -2);
|
|
}
|
|
|
|
auto ret = getValue!T(this.state, -1);
|
|
lua_pop(this.state, args.length + 1);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Read a string value in this table without making a copy of the string.
|
|
* The read string is passed to $(D dg), and should not be escaped.
|
|
* If the value for $(D key) is not a string, $(D dg) is not called.
|
|
* Params:
|
|
* key = lookup _key
|
|
* dg = delegate to receive string
|
|
* Returns:
|
|
* $(D true) if the value for $(D key) was a string and passed to $(D dg), $(D false) otherwise
|
|
* Examples:
|
|
--------------------
|
|
t[2] = "two";
|
|
t.readString(2, str => assert(str == "two"));
|
|
--------------------
|
|
*/
|
|
bool readString(T)(T key, scope void delegate(in char[] str) dg) @trusted
|
|
{
|
|
this.push();
|
|
scope(exit) lua_pop(this.state, 1);
|
|
|
|
pushValue(this.state, key);
|
|
|
|
lua_gettable(this.state, -2);
|
|
scope(exit) lua_pop(this.state, 1);
|
|
|
|
if(lua_isstring(this.state, -1) == 0)
|
|
return false;
|
|
|
|
size_t len;
|
|
const(char)* cstr = lua_tolstring(this.state, -1, &len);
|
|
|
|
dg(cstr[0 .. len]);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Same as calling $(D get!LuaObject) with the same arguments.
|
|
* Examples:
|
|
* ---------------------
|
|
auto luapath = lua["package", "path"];
|
|
writefln("LUA_PATH:\n%s", luapath);
|
|
* ---------------------
|
|
* See_Also:
|
|
* $(MREF LuaTable.get)
|
|
*/
|
|
LuaObject opIndex(T...)(T args)
|
|
{
|
|
return get!LuaObject(args);
|
|
}
|
|
|
|
/**
|
|
* Set a key-value pair in this table.
|
|
* Params:
|
|
* key = key to _set
|
|
* value = value for $(D key)
|
|
*/
|
|
void set(T, U)(T key, U value) @trusted
|
|
{
|
|
this.push();
|
|
scope(success) lua_pop(this.state, 1);
|
|
|
|
pushValue(this.state, key);
|
|
pushValue(this.state, value);
|
|
lua_settable(this.state, -3);
|
|
}
|
|
|
|
/**
|
|
* Set a key-value pair this table or in a sub-table of this table.
|
|
* Params:
|
|
* value = value to set
|
|
* args = list of keys, where all keys but the last one should result in a table
|
|
* Returns:
|
|
* $(D t[k] = value), where $(D t) is the table for the second-to-last parameter in args,
|
|
* and $(D k) is the last parameter in args
|
|
*
|
|
* Examples:
|
|
* ----------------------
|
|
lua["string", "empty"] = (in char[] s){ return s.length == 0; };
|
|
lua.doString(`assert(string.empty(""))`);
|
|
* ----------------------
|
|
*/
|
|
void opIndexAssign(T, U...)(T value, U args) @trusted
|
|
{
|
|
this.push();
|
|
scope(success) lua_pop(this.state, 1);
|
|
|
|
foreach(i, arg; args)
|
|
{
|
|
static if(i != args.length - 1)
|
|
{
|
|
pushValue(this.state, arg);
|
|
lua_gettable(this.state, -2);
|
|
}
|
|
}
|
|
|
|
pushValue(this.state, args[$-1]);
|
|
pushValue(this.state, value);
|
|
lua_settable(this.state, -3);
|
|
|
|
lua_pop(this.state, args.length - 1);
|
|
}
|
|
|
|
/**
|
|
* Create struct of type $(D T) and fill its members with fields from this table.
|
|
*
|
|
* Struct fields that are not present in this table are left at their default value.
|
|
*
|
|
* Params:
|
|
* T = any struct type
|
|
*
|
|
* Returns:
|
|
* Newly created struct
|
|
*/
|
|
T toStruct(T)() @trusted if (is(T == struct))
|
|
{
|
|
push();
|
|
return popValue!T(this.state);
|
|
}
|
|
|
|
/**
|
|
* Fill a struct's members with fields from this table.
|
|
* Params:
|
|
* s = struct to fill
|
|
*/
|
|
void copyTo(T)(ref T s) @trusted if (is(T == struct))
|
|
{
|
|
push();
|
|
fillStruct(this.state, -1, s);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
/**
|
|
* Set the metatable for this table.
|
|
* Params:
|
|
* meta = new metatable
|
|
*/
|
|
void setMetaTable(ref LuaTable meta) @trusted
|
|
in{ assert(this.state == meta.state); }
|
|
body
|
|
{
|
|
this.push();
|
|
meta.push();
|
|
lua_setmetatable(this.state, -2);
|
|
lua_pop(this.state, 1);
|
|
}
|
|
|
|
/**
|
|
* Get the metatable for this table.
|
|
* Returns:
|
|
* A reference to the metatable for this table. The reference is nil if this table has no metatable.
|
|
*/
|
|
LuaTable getMetaTable() @trusted
|
|
{
|
|
this.push();
|
|
scope(success) lua_pop(this.state, 1);
|
|
|
|
return lua_getmetatable(this.state, -1) == 0? LuaTable() : popValue!LuaTable(this.state);
|
|
}
|
|
|
|
/**
|
|
* Get the array length of the table.
|
|
*/
|
|
size_t length() @trusted
|
|
{
|
|
this.push();
|
|
size_t len = lua_objlen(this.state, -1);
|
|
lua_pop(this.state, 1);
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* Iterate over the values in this table.
|
|
*/
|
|
int opApply(T)(int delegate(ref T value) dg) @trusted
|
|
{
|
|
this.push();
|
|
lua_pushnil(this.state);
|
|
while(lua_next(this.state, -2) != 0)
|
|
{
|
|
auto value = popValue!T(this.state);
|
|
int result = dg(value);
|
|
if(result != 0)
|
|
{
|
|
lua_pop(this.state, 2);
|
|
return result;
|
|
}
|
|
}
|
|
lua_pop(this.state, 1);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Iterate over the key-value pairs in this table.
|
|
*/
|
|
int opApply(T, U)(int delegate(ref U key, ref T value) dg) @trusted
|
|
{
|
|
this.push();
|
|
lua_pushnil(this.state);
|
|
while(lua_next(this.state, -2) != 0)
|
|
{
|
|
auto value = popValue!T(this.state);
|
|
auto key = getValue!U(this.state, -1);
|
|
|
|
int result = dg(key, value);
|
|
if(result != 0)
|
|
{
|
|
lua_pop(this.state, 2);
|
|
return result;
|
|
}
|
|
}
|
|
lua_pop(this.state, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
lua_State* L = luaL_newstate();
|
|
scope(success)
|
|
{
|
|
assert(lua_gettop(L) == 0);
|
|
lua_close(L);
|
|
}
|
|
|
|
lua_newtable(L);
|
|
auto t = popValue!LuaTable(L);
|
|
|
|
assert(t.type == LuaType.Table);
|
|
|
|
t.set("foo", "bar");
|
|
assert(t.get!string("foo") == "bar");
|
|
|
|
t.set("foo", nil);
|
|
assert(t.get!LuaObject("foo").isNil);
|
|
|
|
t.set("foo", ["outer": ["inner": "hi!"]]);
|
|
auto s = t.get!(string)("foo", "outer", "inner");
|
|
assert(s == "hi!");
|
|
|
|
auto o = t["foo", "outer"];
|
|
assert(o.type == LuaType.Table);
|
|
|
|
t["foo", "outer", "inner"] = "hello!";
|
|
auto s2 = t.get!(string)("foo", "outer", "inner");
|
|
assert(s2 == "hello!");
|
|
|
|
// length
|
|
t.set("array", ["one", "two"]);
|
|
auto a = t.get!LuaTable("array");
|
|
assert(a.length == 2);
|
|
|
|
// readString
|
|
t[2] = "two";
|
|
bool success = t.readString(2, (in char[] str) {
|
|
assert(str == "two");
|
|
});
|
|
assert(success);
|
|
|
|
t[2] = true;
|
|
success = t.readString(2, (in char[] str) { assert(false); });
|
|
assert(!success);
|
|
|
|
// metatable
|
|
pushValue(L, ["__index": (LuaObject self, string key){
|
|
return key;
|
|
}]);
|
|
auto meta = popValue!LuaTable(L);
|
|
|
|
lua_newtable(L);
|
|
auto t2 = popValue!LuaTable(L);
|
|
|
|
t2.setMetaTable(meta);
|
|
|
|
auto test = t2.get!string("foobar");
|
|
assert(test == "foobar");
|
|
|
|
assert(t2.getMetaTable() == meta);
|
|
|
|
// opApply
|
|
auto input = [1, 2, 3];
|
|
pushValue(L, input);
|
|
auto applyTest = popValue!LuaTable(L);
|
|
|
|
int i = 0;
|
|
foreach(int v; applyTest)
|
|
{
|
|
assert(input[i++] == v);
|
|
}
|
|
|
|
auto inputWithKeys = ["one": 1, "two": 2, "three": 3];
|
|
pushValue(L, inputWithKeys);
|
|
auto applyTestKeys = popValue!LuaTable(L);
|
|
|
|
foreach(string key, int value; applyTestKeys)
|
|
{
|
|
assert(inputWithKeys[key] == value);
|
|
}
|
|
}
|