Lompat ke isi

Modul:index

Dari Wikikato

Dokumentasi untuk modul ini dapat dibuat di Modul:index/doc

local export = {}

local math_module = "Module:math"

local error = error
local format = string.format
local tostring = tostring
local type = type

local function to_integer(...)
	to_integer = require(math_module).to_integer
	return to_integer(...)
end

local function input_err(i, funcname, param)
	local idx_type = type(i)
	error(format(
		"bad argument #%d to 'index.%s' (integer expected, got %s)",
		param, funcname, idx_type == "number" and tostring(i) or idx_type
	), 4)
end

local function get_index(i, obj, func, neg1_nil, funcname, param)
	i = to_integer(i) or input_err(i, funcname, param)
	-- A relative index of -1 refers to the final index. For many functions,
	-- it makes no difference if the final index is nil or -1, so if the
	-- `neg1_nil` flag is set and the index is -1, return nil to avoid having
	-- to call `func`.
	if neg1_nil and i == -1 then
		return nil, obj, func
	elseif i < 0 then
		if func ~= nil then
			obj, func = func(obj), nil
		end
		i = obj + i + 1
	end
	return i, obj, func
end

function export.absolute(obj, i, func, i_min, i_max)
	i, obj, func = get_index(i, obj, func, false, "absolute", 2)
	if i_min ~= nil then
		i_min, obj, func = get_index(i_min, obj, func, false, "absolute", 4)
		if i < i_min then
			i = i_min
		end
	end
	if i_max ~= nil then
		i_max, obj, func = get_index(i_max, obj, func, false, "absolute", 5)
		if i_min and i_min > i_max then
			error("bad arguments #4 and #5 to 'index.absolute' (`i_min` cannot be greater than `i_max`)", 2)
		elseif i > i_max then
			i = i_max
		end
	end
	return i
end

function export.range(obj, i, j, func, always_infer_upper, i_min, j_max)
	-- Canonicalize `i`, or otherwise default to 1.
	if i == nil then
		i = 1
	else
		i, obj, func = get_index(i, obj, func, false, "range", 2)
	end
	-- Get the minimum value for `i`. This is 1 by default, because a start
	-- index of 0 is always equivalent to 1 for most functions (e.g. string.byte
	-- and string.sub). However, many module functions have checks like
	-- "while i <= j" or "if i > j" that won't, therefore, handle (0, 0) like
	-- (1, 0), so converting 0 to 1 avoids the need to special-case it. If
	-- `i_min_allow_0` is set, the minimum defaults to 0 instead (including when
	-- the input is nil), which may be needed if 0 represents the position
	-- before the first index.
	if i_min == nil then
		i_min = 1
	else
		i_min, obj, func = get_index(i_min, obj, func, false, "range", 5)
	end
	-- Set `i` as the minimum value if it hasn't been specified.
	if i < i_min then
		i = i_min
	end
	-- Canonicalize `j`, which will default to the end if not given. By default,
	-- this may be implied by returning nil for `j` (even if the input is -1),
	-- to avoid calling `func` unnecessarily.
	if j ~= nil then
		j, obj, func = get_index(j, obj, func, not always_infer_upper, "range", 3)
	end
	-- Get the maximum value for `j` (if any).
	if j_max ~= nil then
		j_max, obj, func = get_index(j_max, obj, func, not always_infer_upper, "range", 6)
	end
	-- If `j` is not set but `j_max` has a value, use that. Otherwise, if
	-- `always_infer_upper` is set then give the length of `obj`.
	if not j then
		j = j_max or always_infer_upper and (not func and obj or func(obj)) or nil
	-- Ensure `j` does not exceed `j_max`.
	elseif j_max and j > j_max then
		j = j_max
	end
	-- The minimum value for `j` is i - 1, which is equivalent to a range of
	-- 0 length (e.g. string.find("", "") returns (1, 0)).
	local j_min = i - 1
	if j and j < j_min then
		j = j_min
	end
	return i, j
end

return export