Module:table/indexPairs
Itsura
- This module lacks a documentation subpage. Please create it.
- Useful links: root page • root page’s subpages • links • transclusions • testcases • sandbox
local table_get_unprotected_metatable = "Module:table/getUnprotectedMetatable"
local error = error
local next = next
local pairs = pairs
local pcall = pcall
local rawget = rawget
local type = type
local function get_unprotected_metatable(...)
get_unprotected_metatable = require(table_get_unprotected_metatable)
return get_unprotected_metatable(...)
end
local current = {}
local index_pairs
-- Takes a table, and returns:
-- * The value at __index in its metatable.
-- * A boolean stating whether it has a __pairs metamethod.
-- * The three iterator values returned by calling pairs() on it.
local function get_iter(t)
local mt = get_unprotected_metatable(t)
if mt == nil then
return nil, false, next, t, nil
-- If the metatable is protected, it's not possible to get the __index or
-- __pairs metamethods, so call pairs() on `t` and return the result. If
-- __pairs is index_pairs, set current[t] so that it knows to return next()
-- instead of getting into an infinite loop.
elseif not mt then
current[t] = true
-- Use pcall(), so that `t` can always be unset from `current`.
local success, iter, state, init = pcall(pairs, t)
current[t] = nil
-- If there was an error, raise it.
if not success then
error(iter)
end
return nil, true, iter, state, init
end
-- If there's a __pairs metamethod that isn't index_pairs(), use it.
local pairs_mm = rawget(mt, "__pairs")
if not (pairs_mm == nil or pairs_mm == index_pairs) then
return nil, true, pairs_mm(t)
end
local parent = rawget(mt, "__index")
if type(parent) == "function" then
parent = nil
end
return parent, false, next, t, nil
end
function index_pairs(t)
if current[t] then
return next, t, nil
end
local parent, is_pairs_mm, iter, state, k = get_iter(t)
-- If there is no parent, just return the iterator.
if parent == nil then
return iter, state, k
end
local v, seen_k, seen_t, catch_values
return function()
while true do
if is_pairs_mm then
return catch_values(iter(state, k))
end
while true do
k, v = iter(state, k)
if k == nil then
break
-- If a repeated key is found, skip and iterate again.
elseif not seen_k then
seen_k = {[k] = true}
return k, v
elseif not seen_k[k] then
seen_k[k] = true
return k, v
end
end
if parent == nil then
return
elseif seen_t == nil then
seen_t = {}
end
seen_t[t] = true
t, parent, is_pairs_mm, iter, state, k = parent, get_iter(parent)
if seen_t[t] then
error("loop in gettable")
elseif is_pairs_mm then
-- Skip keys which have already been seen, but the return is
-- arbitrary, so allow repeats if the metamethod generates them.
function catch_values(new_k, ...)
if seen_k[new_k] then
return catch_values(iter(state, new_k))
end
k = new_k
return new_k, ...
end
end
end
end
end
return index_pairs