p = {}
 
local args
 
local function callTemplate(template, targs)
    return mw.getCurrentFrame():expandTemplate{title = template, args = targs}
end
 
local function getArgNums(prefix, suffix)
    -- Returns a table containing the numbers of the arguments that exist
    -- for the specified prefix and suffix.
    local nums = {}
    for k, v in pairs(args) do
        local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)' .. suffix .. '$')
        if num then table.insert(nums, tonumber(num)) end
    end
    table.sort(nums)
    return nums
end
 
local function getConstants()
    local constantArgNums = getArgNums('pc', 'n')
    local constantArgs = {}
    for _, v in ipairs(constantArgNums) do
        local keyArgName = 'pc' .. tostring(v) .. 'n'
        local valArgName = 'pc' .. tostring(v) .. 'v'
        constantArgs[args[keyArgName]] = args[valArgName]
    end
    return constantArgs
end
 
local function getVariableVals()
    local variableVals = {}
    for i, v in ipairs(args) do
        if i ~= 1 then
            variableVals[i - 1] = v
        end
    end
    return variableVals
end
 
local function _main()
    local template = args['call'] or 'void'
    local variableParam = args.pv or 1
    local variableValPrefix = args.prefix or ''
    local variableValPostfix = args.postfix or ''
    local sep = args[1] or ''
    local constantArgs = getConstants()
    local variableVals = getVariableVals()
 
    local result = ''
    for i, v in ipairs(variableVals) do
        v = mw.text.trim(v) -- trim whitespace
        local targs = constantArgs
        targs[variableParam] = variableValPrefix .. v .. variableValPostfix
        result = result .. tostring(callTemplate(template, targs))
        if variableVals[i + 1] then
            result = result .. sep
        end
    end
    return result
end
 
function p.main(frame)
    -- If called via #invoke, use the args passed into the invoking template.
    -- Otherwise, for testing purposes, assume args are being passed directly in.
    if frame == mw.getCurrentFrame() then
        args = frame:getParent().args
        for k, v in pairs(frame.args) do
            args = frame.args
            break
        end
    else
        args = frame
    end
    return _main()
end
 
return p