summaryrefslogtreecommitdiff
path: root/library/LuaWrapper.cpp
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-03-28 01:47:52 +0800
committerPetr Mrázek2012-03-28 16:58:51 +0800
commitb76bdad50f03e5083b7dae99c5e1d2d8f80fc72d (patch)
tree30e5c18e16a760a452788e48e5c38be2d7f401b6 /library/LuaWrapper.cpp
parentd2d16271f052cbf01a8c6e9fab5574739b3dd185 (diff)
downloaddfhack-b76bdad50f03e5083b7dae99c5e1d2d8f80fc72d.tar.gz
dfhack-b76bdad50f03e5083b7dae99c5e1d2d8f80fc72d.tar.bz2
dfhack-b76bdad50f03e5083b7dae99c5e1d2d8f80fc72d.tar.xz
Implement recursive transfer of values from lua to c++ structures.
E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data must be represented by raw lua tables. For structs, the entries in the table are assigned to matching fields. For containers, if a 'resize' field is missing or nil, the table is treated like 1-based lua array, and the container is resized to match its # length. Otherwise, the field must be either an explicit number, true or false. If it is true, the size is selected by the highest index in the table. After that, entries are copied using 0-based indices. For pointers, the table must match the target object. If the pointer is null, the object is auto-allocated; this can be controlled using the 'new' field, the value of which will be passed to df.new().
Diffstat (limited to 'library/LuaWrapper.cpp')
-rw-r--r--library/LuaWrapper.cpp115
1 files changed, 106 insertions, 9 deletions
diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp
index 4758f04f..11db1487 100644
--- a/library/LuaWrapper.cpp
+++ b/library/LuaWrapper.cpp
@@ -255,8 +255,8 @@ static void fetch_container_details(lua_State *state, int meta, type_identity **
/**
* Check if type1 and type2 are compatible, possibly using additional metatable data.
*/
-static bool is_type_compatible(lua_State *state, type_identity *type1, int meta1,
- type_identity *type2, int meta2, bool exact_equal)
+bool LuaWrapper::is_type_compatible(lua_State *state, type_identity *type1, int meta1,
+ type_identity *type2, int meta2, bool exact_equal)
{
if (type1 == type2)
return true;
@@ -417,9 +417,9 @@ static bool is_valid_metatable(lua_State *state, int objidx, int metaidx)
/**
* Given a DF object reference or type, safely retrieve its identity pointer.
*/
-static type_identity *get_object_identity(lua_State *state, int objidx,
- const char *ctx, bool allow_type = false,
- bool keep_metatable = false)
+type_identity *LuaWrapper::get_object_identity(lua_State *state, int objidx,
+ const char *ctx, bool allow_type,
+ bool keep_metatable)
{
if (!lua_getmetatable(state, objidx))
luaL_error(state, "Invalid object in %s", ctx);
@@ -608,6 +608,31 @@ static int meta_new(lua_State *state)
return 1;
}
+static void invoke_resize(lua_State *state, int table, lua_Integer size)
+{
+ lua_getfield(state, table, "resize");
+ lua_pushvalue(state, table);
+ lua_pushinteger(state, size);
+ lua_call(state, 2, 0);
+}
+
+static void copy_table(lua_State *state, int dest, int src, int skipkey)
+{
+ lua_pushnil(state);
+
+ while (lua_next(state, src))
+ {
+ if (lua_equal(state, -2, skipkey))
+ lua_pop(state, 1);
+ else
+ {
+ lua_pushvalue(state, -2);
+ lua_swap(state);
+ lua_settable(state, dest);
+ }
+ }
+}
+
/**
* Method: assign data between objects.
*/
@@ -618,11 +643,83 @@ static int meta_assign(lua_State *state)
if (argc != 2)
luaL_error(state, "Usage: target:assign(src) or df.assign(target,src)");
- type_identity *id1, *id2;
- check_type_compatible(state, 1, 2, &id1, &id2, "df.assign()", false, false);
+ if (!lua_istable(state, 2))
+ {
+ type_identity *id1, *id2;
+ check_type_compatible(state, 1, 2, &id1, &id2, "df.assign()", false, false);
+
+ if (!id1->copy(get_object_ref(state, 1), get_object_ref(state, 2)))
+ luaL_error(state, "No copy support for %s", id1->getFullName().c_str());
+ }
+ else
+ {
+ type_identity *id = get_object_identity(state, 1, "df.assign()", false);
+
+ if (id->isContainer())
+ {
+ lua_pushstring(state, "resize");
+ int resize_str = lua_gettop(state);
- if (!id1->copy(get_object_ref(state, 1), get_object_ref(state, 2)))
- luaL_error(state, "No copy support for %s", id1->getFullName().c_str());
+ lua_dup(state);
+ lua_rawget(state, 2);
+
+ if (lua_isnil(state,-1))
+ {
+ /*
+ * nil or missing resize field => 1-based lua array
+ */
+ int size = lua_objlen(state, 2);
+
+ lua_pop(state, 1);
+ invoke_resize(state, 1, size);
+
+ for (int i = 1; i <= size; i++)
+ {
+ lua_pushinteger(state, i-1);
+ lua_rawgeti(state, 2, i);
+ lua_settable(state, 1);
+ }
+ }
+ else
+ {
+ if (lua_isboolean(state, -1))
+ {
+ // resize=false => just assign
+ // resize=true => find the largest index
+ if (lua_toboolean(state, -1))
+ {
+ lua_Integer size = 0;
+
+ lua_pushnil(state);
+ while (lua_next(state, 2))
+ {
+ lua_pop(state, 1);
+ if (lua_isnumber(state,-1))
+ size = std::max(size, lua_tointeger(state,-1)+1);
+ }
+
+ invoke_resize(state, 1, size);
+ }
+ }
+ else
+ {
+ // otherwise, must be an explicit number
+ if (!lua_isnumber(state,-1))
+ luaL_error(state, "Invalid container.resize value in df.assign()");
+
+ invoke_resize(state, 1, lua_tointeger(state, -1));
+ }
+
+ lua_pop(state, 1);
+ copy_table(state, 1, 2, resize_str);
+ }
+ }
+ else
+ {
+ lua_pushstring(state, "new");
+ copy_table(state, 1, 2, lua_gettop(state));
+ }
+ }
return 0;
}