Module:Loops

From The Pinched Universe
Revision as of 19:00, 25 June 2024 by Hori (talk | contribs) (Copied from https://www.mediawiki.org/wiki/Module:Loops)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

The documentation for this module is not intended to be hosted on this wiki.

However, you might be able to find it at one of the following locations:

-- <nowiki>
--------------------------------------------------------------------------------
-- Lua module implementing features similar to [[mw:Extension:Loops]].
--
-- @module lööps
-- @alias  loops
-- @author [[User:ExE Boss]]
-- @require [[Module:TableTools]]
--------------------------------------------------------------------------------

local libraryUtil = require("libraryUtil");
local tableTools  = require("Module:TableTools");

local checkType = libraryUtil.checkType;
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg;

local ustring = mw.ustring;
local loops = {};

local function userError(message)
	return '<strong class="error">' .. message .. '</strong>';
end

local function escapePattern(pattern)
	return ustring.gsub(pattern, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1")
end

local function isFrame(frame)
	return type(frame) == "table"
		and type(frame.args) == "table"
		and type(frame.getParent) == "function";
end

--------------------------------------------------------------------------------
-- Preprocesses text escaped using the [[mw:Extension:DynamicPageList3]] method.
--
-- @function loops._preprocess
-- @param {Frame} frame
-- @param {string} msg
-- @return {string}
--------------------------------------------------------------------------------
local function preprocess(frame, msg)
	msg = ustring.gsub(msg, "«", "<");
	msg = ustring.gsub(msg, "»", ">");
	msg = ustring.gsub(msg, "¦", "|");
	msg = ustring.gsub(msg, "²{", "{{");
	msg = ustring.gsub(msg, "}²", "}}");
	return frame:preprocess(msg);
end
loops._preprocess = preprocess;

--------------------------------------------------------------------------------
-- @param {Frame|table} args
-- @return {number}
-- @usage {{#invoke:Loops|numArgs}}
--------------------------------------------------------------------------------
function loops.numArgs(frame)
	checkType("numArgs", 1, frame, "table");
	local args;
	if (isFrame(frame)) then
		args = (frame:getParent() or frame).args;
	else
		args = frame;
	end
	return tableTools.length(args);
end

--------------------------------------------------------------------------------
-- @param {Frame} args
-- @return {string}
--
-- @usage {{#invoke:Loops|forNumArgs|template string}}
-- @usage {{#invoke:Loops|forNumArgs|value pattern|template string}}
-- @usage {{#invoke:Loops|forNumArgs|key pattern|value pattern|template string}}
-- @usage
--  {{#invoke:Loops|forNumArgs
--  | template = template string
--  }}
-- @usage
--  {{#invoke:Loops|forNumArgs
--  | value    = value pattern
--  | template = template string
--  }}
-- @usage
--  {{#invoke:Loops|forNumArgs
--  | key      = key pattern
--  | template = template string
--  }}
-- @usage
--  {{#invoke:Loops|forNumArgs
--  | key      = key pattern
--  | value    = value pattern
--  | template = template string
--  }}
--------------------------------------------------------------------------------
function loops.forNumArgs(frame)
	local frameArgs, parentArgs;
	checkType("numArgs", 1, frame, "table");
	if (isFrame(frame)) then
		frameArgs  = frame.args;
		parentArgs = frame:getParent().args;
	else
		return error("forNumArgs only supports invocation");
	end

	local kPattern, vPattern, template;
	local frameNumArgs = tableTools.length(frameArgs);
	if (frameNumArgs >= 3) then
		kPattern = frameArgs[1];
		vPattern = frameArgs[2];
		template = frameArgs[3];
	elseif (frameNumArgs >= 2) then
		vPattern = frameArgs[1];
		template = frameArgs[2];
	else
		template = frameArgs[1];
	end

	kPattern = frameArgs.key      or kPattern;
	vPattern = frameArgs.value    or vPattern;
	template = frameArgs.template or template;

	checkTypeForNamedArg("forNumArgs", "key",      kPattern, "string", true);
	checkTypeForNamedArg("forNumArgs", "value",    vPattern, "string", true);
	checkTypeForNamedArg("forNumArgs", "template", template, "string", true);

	if (template == nil) then
		return userError("Must supply template parameter to forNumArgs");
	end

	vPattern = vPattern or "$1";
	if (kPattern ~= nil) then
		if (#kPattern > 0) then
			if (kPattern == vPattern) then
				return userError("key pattern must be different from value pattern");
			end
			kPattern = escapePattern(kPattern);
		else
			kPattern = nil;
		end
	elseif (vPattern ~= "$2") then
		kPattern = "%$2";
	end
	if (#vPattern == 0) then
		vPattern = nil;
	else
		vPattern = escapePattern(vPattern);
	end

	local result = {};
	local v, msg;
	for k = 1, tableTools.length(parentArgs) do
		v = parentArgs[k];
		if (v ~= nil) then
			msg = template;
			if (kPattern) then
				msg = ustring.gsub(msg, kPattern, (ustring.gsub(tostring(k), "%%", "%%%%")));
			end
			if (vPattern) then
				msg = ustring.gsub(msg, vPattern, (ustring.gsub(tostring(v), "%%", "%%%%")));
			end
			result[#result + 1] = preprocess(frame, msg);
		end
	end

	return table.concat(result);
end

--------------------------------------------------------------------------------
-- Parses and prints wikitext markup N times
--
-- @param {Frame} args
-- @param[opt] {string} args[1] Pattern. `$1` by default
-- @param {number} args[2] Starting value
-- @param {number} args[3] Number loops to be performed
-- @param {string} args[4] Wikitext markup
-- @return {string}
--------------------------------------------------------------------------------
function loops.loop(frame)
	local frameArgs
	checkType("numArgs", 1, frame, "table")
	if (isFrame(frame)) then
		frameArgs = (frame:getParent() or frame).args
	else
		return userError("loop only supports invocation")
	end
	
	local pattern = frame.args[1] or "$1"
	
	local start = tonumber(frame.args[2])
	local loopsPerformed = tonumber(frame.args[3])
	-- {{#loop}} supports negative values for loopsPerformed
	local fin = loopsPerformed < 0 and start + (loopsPerformed + 1) or (start - 1) + loopsPerformed
	local step = loopsPerformed < 0 and -1 or 1
	
	local template = frame.args[4]
	
	local result = {}
	local msg
	for i = start, fin, step do
		msg = ustring.gsub(template, pattern, i)
		result[#result + 1] = msg
	end
	
	return preprocess(frame, table.concat(result)) -- preprocess at the end
end

return loops;