diff options
| author | Alexander Gavrilov | 2012-03-29 14:39:13 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-03-29 14:39:13 +0400 |
| commit | 85c91c92d81b4ba226973aa9312c7b01e47554de (patch) | |
| tree | 1a604fc8a244a22c6c240da7a70a3cba1720b0d5 /library/LuaTypes.cpp | |
| parent | f6c6218909fd196a5bd293b7f41ceaf9c7d4267c (diff) | |
| download | dfhack-85c91c92d81b4ba226973aa9312c7b01e47554de.tar.gz dfhack-85c91c92d81b4ba226973aa9312c7b01e47554de.tar.bz2 dfhack-85c91c92d81b4ba226973aa9312c7b01e47554de.tar.xz | |
Implement __pairs and __ipairs for DF objects.
Structs enumerate fields in memory order in pairs().
Containers & biftields enumerate int indexes in ipairs, and
string keys in pairs (i.e. using index-enum for arrays).
Diffstat (limited to 'library/LuaTypes.cpp')
| -rw-r--r-- | library/LuaTypes.cpp | 238 |
1 files changed, 208 insertions, 30 deletions
diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index 76586bf7..089ead0d 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -389,6 +389,43 @@ static void *find_field(lua_State *state, int index, const char *mode) return p; } +static int cur_iter_index(lua_State *state, int len, int fidx, int first_idx = -1) +{ + int rv; + + if (lua_isnil(state, fidx)) + rv = first_idx; + else + { + if (lua_isnumber(state, fidx)) + rv = lua_tointeger(state, fidx); + else + { + lua_pushvalue(state, fidx); + lua_rawget(state, UPVAL_FIELDTABLE); + if (!lua_isnumber(state, -1)) + field_error(state, fidx, "index not found", "iterate"); + rv = lua_tointeger(state, -1); + lua_pop(state, 1); + } + + if (rv < 0 || rv >= len) + field_error(state, fidx, "index out of bounds", "iterate"); + } + + return rv; +} + +static void iter_idx_to_name(lua_State *state, int idx) +{ + lua_pushvalue(state, idx); + lua_rawget(state, UPVAL_FIELDTABLE); + if (lua_isnil(state, -1)) + lua_pop(state, 1); + else + lua_replace(state, idx); +} + static uint8_t *check_method_call(lua_State *state, int min_args, int max_args) { int argc = lua_gettop(state)-1; @@ -593,6 +630,24 @@ static int meta_struct_newindex(lua_State *state) } /** + * Metamethod: iterator for structures. + */ +static int meta_struct_next(lua_State *state) +{ + if (lua_gettop(state) < 2) lua_pushnil(state); + + int len = lua_objlen(state, UPVAL_FIELDTABLE); + int idx = cur_iter_index(state, len+1, 2, 0); + if (idx == len) + return 0; + + lua_rawgeti(state, UPVAL_FIELDTABLE, idx+1); + lua_dup(state); + lua_gettable(state, 1); + return 2; +} + +/** * Metamethod: __index for primitives, i.e. simple object references. * Fields point to identity, or NULL for metafields. */ @@ -723,6 +778,39 @@ static int meta_container_newindex(lua_State *state) } /** + * Metamethod: integer iterator for containers. + */ +static int meta_container_nexti(lua_State *state) +{ + if (lua_gettop(state) < 2) lua_pushnil(state); + + uint8_t *ptr = get_object_addr(state, 1, 2, "iterate"); + + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int len = id->lua_item_count(state, ptr, container_identity::COUNT_LEN); + int idx = cur_iter_index(state, len, 2); + + if (++idx >= len) + return 0; + + lua_pushinteger(state, idx); + id->lua_item_read(state, 2, ptr, idx); + return 2; +} + +/** + * Metamethod: name iterator for containers. + */ +static int meta_container_next(lua_State *state) +{ + if (!meta_container_nexti(state)) + return 0; + + iter_idx_to_name(state, lua_gettop(state)-1); + return 2; +} + +/** * Method: resize container */ static int method_container_resize(lua_State *state) @@ -781,6 +869,17 @@ static int meta_bitfield_len(lua_State *state) return 1; } +static void read_bitfield(lua_State *state, uint8_t *ptr, bitfield_identity *id, int idx) +{ + int size = id->getBits()[idx].size; + + int value = getBitfieldField(ptr, idx, size); + if (size <= 1) + lua_pushboolean(state, value != 0); + else + lua_pushinteger(state, value); +} + /** * Metamethod: __index for bitfields. */ @@ -803,13 +902,7 @@ static int meta_bitfield_index(lua_State *state) } int idx = check_container_index(state, id->getNumBits(), 2, iidx, "read"); - int size = id->getBits()[idx].size; - - int value = getBitfieldField(ptr, idx, size); - if (size <= 1) - lua_pushboolean(state, value != 0); - else - lua_pushinteger(state, value); + read_bitfield(state, ptr, id, idx); return 1; } @@ -847,6 +940,44 @@ static int meta_bitfield_newindex(lua_State *state) } /** + * Metamethod: integer iterator for bitfields. + */ +static int meta_bitfield_nexti(lua_State *state) +{ + if (lua_gettop(state) < 2) lua_pushnil(state); + + uint8_t *ptr = get_object_addr(state, 1, 2, "iterate"); + + auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int len = id->getNumBits(); + int idx = cur_iter_index(state, len, 2); + + if (idx < 0) + idx = 0; + else + idx += std::max(1, (int)id->getBits()[idx].size); + + if (idx >= len) + return 0; + + lua_pushinteger(state, idx); + read_bitfield(state, ptr, id, idx); + return 2; +} + +/** + * Metamethod: name iterator for bitfields. + */ +static int meta_bitfield_next(lua_State *state) +{ + if (!meta_bitfield_nexti(state)) + return 0; + + iter_idx_to_name(state, lua_gettop(state)-1); + return 2; +} + +/** * Metamethod: __index for df.global */ static int meta_global_index(lua_State *state) @@ -906,36 +1037,43 @@ static void AddMethodWrapper(lua_State *state, int meta_idx, int field_idx, /** * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack. */ -static void IndexFields(lua_State *state, struct_identity *pstruct) +static void IndexFields(lua_State *state, int base, struct_identity *pstruct) { - // stack: metatable fieldtable + if (pstruct->getParent()) + IndexFields(state, base, pstruct->getParent()); - int base = lua_gettop(state) - 2; + auto fields = pstruct->getFields(); + if (!fields) + return; - for (struct_identity *p = pstruct; p; p = p->getParent()) + int cnt = lua_objlen(state, base+3); // field iter table + + for (int i = 0; fields[i].mode != struct_field_info::END; ++i) { - auto fields = p->getFields(); - if (!fields) - continue; + // Qualify conflicting field names with the type + std::string name = fields[i].name; - for (int i = 0; fields[i].mode != struct_field_info::END; ++i) + lua_getfield(state, base+2, name.c_str()); + if (!lua_isnil(state, -1)) + name = pstruct->getName() + ("." + name); + lua_pop(state, 1); + + // Handle the field + switch (fields[i].mode) { - switch (fields[i].mode) - { - case struct_field_info::OBJ_METHOD: - AddMethodWrapper(state, base+1, base+2, fields[i].name, - (function_identity_base*)fields[i].type); - break; + case struct_field_info::OBJ_METHOD: + AddMethodWrapper(state, base+1, base+2, name.c_str(), + (function_identity_base*)fields[i].type); + break; - case struct_field_info::CLASS_METHOD: - break; + case struct_field_info::CLASS_METHOD: + break; - default: - lua_pushstring(state,fields[i].name); - lua_pushlightuserdata(state,(void*)&fields[i]); - lua_rawset(state,base+2); - break; - } + default: + AssociateId(state, base+3, ++cnt, name.c_str()); + lua_pushlightuserdata(state, (void*)&fields[i]); + lua_setfield(state, base+2, name.c_str()); + break; } } } @@ -976,8 +1114,20 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, MakeMetatable(state, pstruct, "struct"); // meta, fields - IndexFields(state, pstruct); + // Index the fields + lua_newtable(state); + + IndexFields(state, base, pstruct); + + // Add the iteration metamethods + PushStructMethod(state, base+1, base+3, meta_struct_next); + SetPairsMethod(state, base+1, "__pairs"); + lua_pushnil(state); + SetPairsMethod(state, base+1, "__ipairs"); + + lua_setfield(state, base+1, "_index_table"); + // Add the indexing metamethods SetStructMethod(state, base+1, base+2, reader, "__index"); SetStructMethod(state, base+1, base+2, writer, "__newindex"); @@ -995,8 +1145,21 @@ static void MakePrimitiveMetatable(lua_State *state, type_identity *type) SetPtrMethods(state, base+1, base+2); + // Index the fields + lua_newtable(state); + EnableMetaField(state, base+2, "value", type); + AssociateId(state, base+3, 1, "value"); + + // Add the iteration metamethods + PushStructMethod(state, base+1, base+3, meta_struct_next); + SetPairsMethod(state, base+1, "__pairs"); + lua_pushnil(state); + SetPairsMethod(state, base+1, "__ipairs"); + lua_setfield(state, base+1, "_index_table"); + + // Add the indexing metamethods SetStructMethod(state, base+1, base+2, meta_primitive_index, "__index"); SetStructMethod(state, base+1, base+2, meta_primitive_newindex, "__newindex"); } @@ -1048,7 +1211,15 @@ static void MakeContainerMetatable(lua_State *state, container_identity *type, AddContainerMethodFun(state, base+1, base+2, method_container_erase, "erase", type, item, count); AddContainerMethodFun(state, base+1, base+2, method_container_insert, "insert", type, item, count); + // push the index table AttachEnumKeys(state, base+1, base+2, ienum); + + PushContainerMethod(state, base+1, base+3, meta_container_next, type, item, count); + SetPairsMethod(state, base+1, "__pairs"); + PushContainerMethod(state, base+1, base+3, meta_container_nexti, type, item, count); + SetPairsMethod(state, base+1, "__ipairs"); + + lua_pop(state, 1); } /* @@ -1078,6 +1249,13 @@ void bitfield_identity::build_metatable(lua_State *state) AttachEnumKeys(state, base+1, base+2, this); + PushContainerMethod(state, base+1, base+3, meta_bitfield_next, this, NULL, -1); + SetPairsMethod(state, base+1, "__pairs"); + PushContainerMethod(state, base+1, base+3, meta_bitfield_nexti, this, NULL, -1); + SetPairsMethod(state, base+1, "__ipairs"); + + lua_pop(state, 1); + EnableMetaField(state, base+2, "whole", this); } |
