Pumunta sa nilalaman

Module:table/indexPairs

Mula Wiksiyonaryo


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