local p = local yesno = require('Module:Yesno')
-- Escape a string to add to a Lua patternlocal function escPattern(s) return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')end
-- Get the value of a prefixed argumentfunction getPrefixedArg(args, prefix, num, optNum) return args[prefix .. num] or ((num
-- Get the prefixed parameters to processlocal function getPrefixes(args) local prefixes = local i = 1 local cur = getPrefixedArg(args, 'prefix', i, true)
repeat table.insert(prefixes, cur or ) i = i + 1 cur = getPrefixedArg(args, 'prefix', i, true) until not cur return prefixesend
-- Get the extra parameters to processlocal function getExtras(args) local extras = local i = 1 local cur = getPrefixedArg(args, 'extra', i, true)
while cur do local numMatch = cur:match('^(.+)%(i%)$') if numMatch then table.insert(extras,) else table.insert(extras,) end i = i + 1 cur = getPrefixedArg(args, 'extra', i, true) end return extrasend
-- Perform wikitext parameter substitutionfunction substParams(code, num, args) args['i'] = num local processed = code:gsub("", args) -- Substitute parameters return processedend
-- Return the function that processes each iterationfunction makeProcessor(frame, parent) local template = frame.args.call if template then return function(num, args) args['i'] = num return frame:expandTemplate end end -- Note: The difference between angle brackets and their entities is lost local code = mw.text.unstripNoWiki(frame.args[1]):gsub('<', '<'):gsub('>', '>') local expand = yesno(frame.args.expand or true) if expand then local processingFrame = parent:newChild return function(num, args) return processingFrame:preprocess(substParams(code, num, args)) end end
return function(num, args) return substParams(code, num, args) endend
function numPairs(args, parentArgs, prefixes, optNum) local start = tonumber(args.start or 1) if yesno(args.sparse or false) then local nums = local seenNums =
for k, _ in pairs(parentArgs) do local prefixMatch = false local j = 1
-- Check every prefix for a match while prefixes[j] and not prefixMatch do local numStr = tostring(k):match('^' .. escPattern(prefixes[j]) .. '(%d*)$')
if numStr ~= nil then local num = tonumber(numStr) or (optNum and 1 or nil)
if num ~= nil and num >= start and not seenNums[num] then table.insert(nums, num) seenNums[num] = true end
prefixMatch = true end
j = j + 1 end end table.sort(nums) -- Iterate over each found number function sequenceIter(a, i) i = i + 1 local v = a[i] if v then return i, v end end return sequenceIter, nums, 0 end -- Iterate each number and check for any matches local function prefixIter(a, i) i = i + 1 local j = 1 local prefixMatch = false -- Check every prefix for a match while prefixes[j] and not prefixMatch do prefixMatch = getPrefixedArg(a, prefixes[j], i, optNum) ~= nil j = j + 1 end if prefixMatch then return i, i end end return prefixIter, parentArgs, start - 1end
function p.map(frame) local result = local parent = frame:getParent
local sep = frame.args.call and frame.args[1] or frame.args[2] or frame.args.sep or local conj = frame.args.conj or sep local prefixes = getPrefixes(frame.args) local extras = getExtras(frame.args) local process = makeProcessor(frame, parent) local optNum = yesno(frame.args.optnum or true) for _, num in numPairs(frame.args, parent.args, prefixes, optNum) do local args = -- Pass extra registered arguments for _, extraInfo in ipairs(extras) do local extra, numbered = unpack(extraInfo) if numbered then args[extra] = getPrefixedArg(parent.args, extra, num, optNum) else args[extra] = parent.args[extra] end end -- Pass current parameters without their numeric suffix for _, prefix in ipairs(prefixes) do args[prefix ~= '' and prefix or '1'] = getPrefixedArg(parent.args, prefix, num, optNum) end table.insert(result, process(num, args)) end return mw.text.listToText(result, sep, conj)end
return p