Initial commit
This commit is contained in:
349
source/luad/table.d
Normal file
349
source/luad/table.d
Normal file
@ -0,0 +1,349 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user