Module:Params: Difference between revisions
From All Skies Encyclopaedia
imported>Xaosflux m (Changed protection settings for "Module:Params": High-risk template or module: see also talk page ([Edit=Require extended confirmed access] (indefinite) [Move=Require extended confirmed access] (indefinite))) |
imported>Grufo (BREAKING CHANGES: modifiers `mapping_values_by_calling`, `mapping_values_by_invoking`, `mapping_values_blindly_by_calling` and `mapping_values_blindly_by_invoking` have been renamed to `mapping_by_calling`, `mapping_by_invoking`, `mapping_blindly_by_calling` and `mapping_blindly_by_invoking`; NEW MODIFIERS: `cropping`, `purging`, `backpurging`, `reversing`, `renaming_blindly_by_calling`, `renaming_blindly_by_invoking`; SWITCH TO NATURAL SORT ORDER; CODE REVIEW) |
||
Line 13: | Line 13: | ||
-- Memory slots |
|||
-- Set directives |
|||
local memoryslots = { |
local memoryslots = { |
||
i = 'itersep', |
i = 'itersep', |
||
Line 21: | Line 21: | ||
f = 'footer', |
f = 'footer', |
||
n = 'ifngiven' |
n = 'ifngiven' |
||
} |
|||
-- Directive that can only be invoked as first arguments |
|||
local firstposonly = { |
|||
new = 'modifier', |
|||
self = 'function', |
|||
} |
} |
||
Line 38: | Line 45: | ||
-- Prepare the context |
-- Prepare the context |
||
local function |
local function context_new(frame) |
||
local ctx = {} |
local ctx = {} |
||
ctx.luaname = 'Module:Params' --[[ or `frame:getTitle()` ]]-- |
ctx.luaname = 'Module:Params' --[[ or `frame:getTitle()` ]]-- |
||
ctx.iterfunc = pairs |
ctx.iterfunc = pairs |
||
ctx.pipe = copy_or_ref_table(frame.args, refpipe) |
|||
ctx.frame = frame:getParent() |
ctx.frame = frame:getParent() |
||
return ctx |
|||
end |
|||
-- Prepare the context |
|||
local function context_init(frame, funcname, refpipe, refparams) |
|||
local ctx = context_new(frame) |
|||
ctx.pipe = copy_or_ref_table(frame.args, refpipe) |
|||
ctx.params = copy_or_ref_table(ctx.frame.args, refparams) |
ctx.params = copy_or_ref_table(ctx.frame.args, refparams) |
||
return funcname(ctx) |
return funcname(ctx) |
||
Line 59: | Line 73: | ||
end |
end |
||
if library[nextfn] == nil then |
if library[nextfn] == nil then |
||
if firstposonly[nextfn] == nil then |
|||
error(ctx.luaname .. ': The function ‘' .. nextfn .. '’ does not exist', 0) |
|||
error(ctx.luaname .. ': The function ‘' .. nextfn .. |
|||
'’ does not exist', 0) |
|||
else |
|||
error(ctx.luaname .. ': The ‘' .. nextfn .. '’ ' .. |
|||
firstposonly[nextfn] .. |
|||
' can only be invoked in first position', 0) |
|||
end |
|||
end |
end |
||
for idx = n_forward, 1, -1 do table.remove(ctx.pipe, idx) end |
for idx = n_forward, 1, -1 do table.remove(ctx.pipe, idx) end |
||
Line 70: | Line 91: | ||
-- will prevail over colliding non-numerical keys from the table of parameters |
-- will prevail over colliding non-numerical keys from the table of parameters |
||
local function concat_params(ctx) |
local function concat_params(ctx) |
||
local shift = table.maxn(ctx.pipe) |
local shift = table.maxn(ctx.pipe) |
||
local newargs = {} |
local newargs = {} |
||
if ctx.subset == 1 then |
if ctx.subset == 1 then |
||
Line 96: | Line 117: | ||
-- Helper function for `string.gsub()` (for managing zero-padded numbers) |
|||
function zero_padded(str) |
|||
return ("%03d%s"):format(#str, str) |
|||
end |
|||
-- Helper function for `table.sort()` (for natural sorting) |
|||
function natural_sort(var1, var2) |
|||
return tostring(var1):gsub("%d+", zero_padded) < |
|||
tostring(var2):gsub("%d+", zero_padded) |
|||
end |
|||
-- Flush the parameters by calling a custom function for each value (after this |
|||
-- function has been invoked `ctx.params` will no longer be usable) |
|||
local function flush_params(ctx, fn) |
local function flush_params(ctx, fn) |
||
local tbl = ctx.params |
local tbl = ctx.params |
||
Line 121: | Line 157: | ||
end |
end |
||
table.sort(nums) |
table.sort(nums) |
||
table.sort(words) |
table.sort(words, natural_sort) |
||
for idx = 1, nlen do fn(nums[idx], tbl[nums[idx]]) end |
for idx = 1, nlen do fn(nums[idx], tbl[nums[idx]]) end |
||
for idx = 1, wlen do fn(words[idx], tbl[words[idx]]) end |
for idx = 1, wlen do fn(words[idx], tbl[words[idx]]) end |
||
Line 159: | Line 195: | ||
if state == 0 then error(ctx.luaname .. ', ‘' .. fname .. '’: No pattern was given', 0) end |
if state == 0 then error(ctx.luaname .. ', ‘' .. fname .. '’: No pattern was given', 0) end |
||
return cnt |
return cnt |
||
end |
|||
-- Remove numerical arguments (not necessarily sequential ones) |
|||
function purge_args(tbl, offset, length) |
|||
local cache = {} |
|||
local pshift |
|||
local nshift |
|||
if offset > 0 then |
|||
pshift = length |
|||
nshift = 0 |
|||
elseif offset + length > 1 then |
|||
pshift = offset + length - 1 |
|||
nshift = 1 - offset |
|||
else |
|||
pshift = 0 |
|||
nshift = length |
|||
end |
|||
for key, val in pairs(tbl) do |
|||
if type(key) == 'number' then |
|||
if key > 0 and key >= offset then |
|||
if key >= offset + length then |
|||
cache[key - pshift] = val |
|||
end |
|||
tbl[key] = nil |
|||
elseif key < 1 and key < offset + length then |
|||
if key < offset then |
|||
cache[key + nshift] = val |
|||
end |
|||
tbl[key] = nil |
|||
end |
|||
end |
|||
end |
|||
for key, val in pairs(cache) do tbl[key] = val end |
|||
end |
end |
||
Line 261: | Line 331: | ||
local cache = {} |
local cache = {} |
||
if tot >= len then |
if tot >= len then |
||
for key |
for key in ipairs(tbl) do tbl[key] = nil end |
||
tot = len |
tot = len |
||
else |
else |
||
Line 281: | Line 351: | ||
return context_iterate(ctx, 3) |
return context_iterate(ctx, 3) |
||
end |
end |
||
-- See iface.cropping() |
|||
library.cropping = function(ctx) |
|||
local lcut = tonumber(ctx.pipe[1]) |
|||
if lcut == nil then error(ctx.luaname .. ', ‘cropping’: Left crop must be a number', 0) end |
|||
local rcut = tonumber(ctx.pipe[2]) |
|||
if rcut == nil then error(ctx.luaname .. ', ‘cropping’: Right crop must be a number', 0) end |
|||
local tbl = ctx.params |
|||
local nmin |
|||
local nmax |
|||
for key in pairs(tbl) do |
|||
if type(key) == 'number' then |
|||
if nmin == nil then |
|||
nmin = key |
|||
nmax = key |
|||
elseif key > nmax then nmax = key |
|||
elseif key < nmin then nmin = key |
|||
end |
|||
end |
|||
end |
|||
if nmin ~= nil then |
|||
local len = nmax - nmin + 1 |
|||
if lcut < 0 then lcut = len + lcut end |
|||
if rcut < 0 then rcut = len + rcut end |
|||
if lcut + rcut - len > -1 then |
|||
for key in pairs(tbl) do |
|||
if type(key) == 'number' then tbl[key] = nil end |
|||
end |
|||
elseif lcut + rcut > 0 then |
|||
for idx = nmax - rcut + 1, nmax, 1 do tbl[idx] = nil end |
|||
for idx = nmin, nmin + lcut - 1, 1 do tbl[idx] = nil end |
|||
local lshift = nmin + lcut - 1 |
|||
if lshift > 0 then |
|||
for idx = lshift + 1, nmax, 1 do |
|||
tbl[idx - lshift] = tbl[idx] |
|||
tbl[idx] = nil |
|||
end |
|||
end |
|||
end |
|||
end |
|||
return context_iterate(ctx, 3) |
|||
end |
|||
-- See iface.purging() |
|||
library.purging = function(ctx) |
|||
local first = tonumber(ctx.pipe[1]) |
|||
if first == nil then error(ctx.luaname .. ', ‘purging’: Start offset must be a number', 0) end |
|||
local size = tonumber(ctx.pipe[2]) |
|||
if size == nil then error(ctx.luaname .. ', ‘purging’: Length must be a number', 0) end |
|||
local tbl = ctx.params |
|||
if size < 1 then |
|||
size = size + table.maxn(tbl) |
|||
if first > size then return context_iterate(ctx, 3) end |
|||
size = size - first + 1 |
|||
end |
|||
purge_args(tbl, first, size) |
|||
return context_iterate(ctx, 3) |
|||
end |
|||
-- See iface.backpurging() |
|||
library.backpurging = function(ctx) |
|||
local last = tonumber(ctx.pipe[1]) |
|||
if last == nil then error(ctx.luaname .. ', ‘backpurging’: Start offset must be a number', 0) end |
|||
local size = tonumber(ctx.pipe[2]) |
|||
if size == nil then error(ctx.luaname .. ', ‘backpurging’: Length must be a number', 0) end |
|||
local first |
|||
local tbl = ctx.params |
|||
if size > 0 then |
|||
first = last - size + 1 |
|||
else |
|||
for key in pairs(tbl) do |
|||
if type(key) == 'number' and (first == nil or |
|||
key < first) then first = key end |
|||
end |
|||
if first == nil then return context_iterate(ctx, 3) end |
|||
first = first - size |
|||
if last < first then return context_iterate(ctx, 3) end |
|||
size = last - first + 1 |
|||
end |
|||
purge_args(ctx.params, first, size) |
|||
return context_iterate(ctx, 3) |
|||
end |
|||
-- See iface.reversing() |
|||
library.reversing = function(ctx) |
|||
local tbl = ctx.params |
|||
local reversed = {} |
|||
local nmax |
|||
local nmin |
|||
for key, val in pairs(tbl) do |
|||
if type(key) == 'number' then |
|||
reversed[key] = val |
|||
tbl[key] = nil |
|||
if nmax == nil then |
|||
nmax = key |
|||
nmin = key |
|||
elseif key > nmax then nmax = key |
|||
elseif key < nmin then nmin = key |
|||
end |
|||
end |
|||
end |
|||
for key, val in pairs(reversed) do |
|||
tbl[nmax + nmin - key] = val |
|||
end |
|||
return context_iterate(ctx, 1) |
|||
end |
|||
-- See iface.swapping() |
|||
--[[ |
|||
library.swapping = function(ctx) |
|||
local tbl = ctx.params |
|||
local cache = {} |
|||
local nsize = 0 |
|||
local tmp |
|||
for key in pairs(tbl) do |
|||
if type(key) == 'number' then |
|||
nsize = nsize + 1 |
|||
cache[nsize] = key |
|||
end |
|||
end |
|||
table.sort(cache) |
|||
for idx = math.floor(nsize / 2), 1, -1 do |
|||
tmp = tbl[cache[idx] ] |
|||
tbl[cache[idx] ] = tbl[cache[nsize - idx + 1] ] |
|||
tbl[cache[nsize - idx + 1] ] = tmp |
|||
end |
|||
return context_iterate(ctx, 1) |
|||
end |
|||
]]-- |
|||
Line 374: | Line 578: | ||
-- See iface. |
-- See iface.mapping_by_calling() |
||
library. |
library.mapping_by_calling = function(ctx) |
||
local opts = ctx.pipe |
local opts = ctx.pipe |
||
local tname |
local tname |
||
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end |
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end |
||
if tname == nil then error(ctx.luaname .. ', |
if tname == nil then error(ctx.luaname .. ', ‘mapping_by_calling’: No template name was provided', 0) end |
||
local nargs |
local nargs |
||
local margs = {} |
local margs = {} |
||
Line 393: | Line 597: | ||
local model = { title = tname, args = margs } |
local model = { title = tname, args = margs } |
||
local tbl = ctx.params |
local tbl = ctx.params |
||
for key, val in pairs(tbl) do |
|||
if ctx.subset == 1 then |
|||
margs[1] = key |
|||
for key, val in ipairs(tbl) do |
|||
margs[2] = val |
|||
tbl[key] = ctx.frame:expandTemplate(model) |
|||
margs[2] = val |
|||
tbl[key] = ctx.frame:expandTemplate(model) |
|||
end |
|||
elseif ctx.subset == -1 then |
|||
tmp = {} |
|||
for key, val in pairs(tbl) do tmp[key] = true end |
|||
for key, val in ipairs(tmp) do tmp[key] = nil end |
|||
for key in pairs(tmp) do |
|||
margs[1] = key |
|||
margs[2] = tbl[key] |
|||
tbl[key] = ctx.frame:expandTemplate(model) |
|||
end |
|||
else |
|||
for key, val in pairs(tbl) do |
|||
margs[1] = key |
|||
margs[2] = val |
|||
tbl[key] = ctx.frame:expandTemplate(model) |
|||
end |
|||
end |
end |
||
return context_iterate(ctx, nargs + 1) |
return context_iterate(ctx, nargs + 1) |
||
Line 420: | Line 606: | ||
-- See iface. |
-- See iface.mapping_by_invoking() |
||
library. |
library.mapping_by_invoking = function(ctx) |
||
local opts = ctx.pipe |
local opts = ctx.pipe |
||
local mname |
local mname |
||
local fname |
local fname |
||
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end |
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end |
||
if mname == nil then error(ctx.luaname .. ', |
if mname == nil then error(ctx.luaname .. ', ‘mapping_by_invoking’: No module name was provided', 0) end |
||
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end |
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end |
||
if fname == nil then error(ctx.luaname .. ', |
if fname == nil then error(ctx.luaname .. ', ‘mapping_by_invoking’: No function name was provided', 0) end |
||
local nargs |
local nargs |
||
local margs = {} |
local margs = {} |
||
Line 443: | Line 629: | ||
local mfunc = require(model.title)[fname] |
local mfunc = require(model.title)[fname] |
||
local tbl = ctx.params |
local tbl = ctx.params |
||
for key, val in pairs(tbl) do |
|||
if ctx.subset == 1 then |
|||
margs[1] = key |
|||
for key, val in ipairs(tbl) do |
|||
margs[2] = val |
|||
tbl[key] = mfunc(ctx.frame:newChild(model)) |
|||
margs[2] = val |
|||
tbl[key] = mfunc(ctx.frame:newChild(model)) |
|||
end |
|||
elseif ctx.subset == -1 then |
|||
tmp = {} |
|||
for key, val in pairs(tbl) do tmp[key] = true end |
|||
for key, val in ipairs(tmp) do tmp[key] = nil end |
|||
for key in pairs(tmp) do |
|||
margs[1] = key |
|||
margs[2] = tbl[key] |
|||
tbl[key] = mfunc(ctx.frame:newChild(model)) |
|||
end |
|||
else |
|||
for key, val in pairs(tbl) do |
|||
margs[1] = key |
|||
margs[2] = val |
|||
tbl[key] = mfunc(ctx.frame:newChild(model)) |
|||
end |
|||
end |
end |
||
return context_iterate(ctx, nargs + 1) |
return context_iterate(ctx, nargs + 1) |
||
Line 470: | Line 638: | ||
-- See iface. |
-- See iface.mapping_blindly_by_calling() |
||
library. |
library.mapping_blindly_by_calling = function(ctx) |
||
local opts = ctx.pipe |
local opts = ctx.pipe |
||
local tname |
local tname |
||
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end |
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end |
||
if tname == nil then error(ctx.luaname .. ', |
if tname == nil then error(ctx.luaname .. ', ‘mapping_blindly_by_calling’: No template name was provided', 0) end |
||
local nargs |
local nargs |
||
local margs = {} |
local margs = {} |
||
Line 489: | Line 657: | ||
local model = { title = tname, args = margs } |
local model = { title = tname, args = margs } |
||
local tbl = ctx.params |
local tbl = ctx.params |
||
for key, val in pairs(tbl) do |
|||
if ctx.subset == 1 then |
|||
margs[1] = val |
|||
for key, val in ipairs(tbl) do |
|||
tbl[key] = ctx.frame:expandTemplate(model) |
|||
margs[1] = val |
|||
tbl[key] = ctx.frame:expandTemplate(model) |
|||
end |
|||
elseif ctx.subset == -1 then |
|||
tmp = {} |
|||
for key, val in pairs(tbl) do tmp[key] = true end |
|||
for key, val in ipairs(tmp) do tmp[key] = nil end |
|||
for key in pairs(tmp) do |
|||
margs[1] = tbl[key] |
|||
tbl[key] = ctx.frame:expandTemplate(model) |
|||
end |
|||
else |
|||
for key, val in pairs(tbl) do |
|||
margs[1] = val |
|||
tbl[key] = ctx.frame:expandTemplate(model) |
|||
end |
|||
end |
end |
||
return context_iterate(ctx, nargs + 1) |
return context_iterate(ctx, nargs + 1) |
||
Line 513: | Line 665: | ||
-- See iface. |
-- See iface.mapping_blindly_by_invoking() |
||
library. |
library.mapping_blindly_by_invoking = function(ctx) |
||
local opts = ctx.pipe |
local opts = ctx.pipe |
||
local mname |
local mname |
||
local fname |
local fname |
||
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end |
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end |
||
if mname == nil then error(ctx.luaname .. ', |
if mname == nil then error(ctx.luaname .. ', ‘mapping_blindly_by_invoking’: No module name was provided', 0) end |
||
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end |
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end |
||
if fname == nil then error(ctx.luaname .. ', |
if fname == nil then error(ctx.luaname .. ', ‘mapping_blindly_by_invoking’: No function name was provided', 0) end |
||
local nargs |
local nargs |
||
local margs = {} |
local margs = {} |
||
Line 536: | Line 688: | ||
local mfunc = require(model.title)[fname] |
local mfunc = require(model.title)[fname] |
||
local tbl = ctx.params |
local tbl = ctx.params |
||
for key, val in pairs(tbl) do |
|||
if ctx.subset == 1 then |
|||
margs[1] = val |
|||
for key, val in ipairs(tbl) do |
|||
tbl[key] = mfunc(ctx.frame:newChild(model)) |
|||
margs[1] = val |
|||
end |
|||
tbl[key] = mfunc(ctx.frame:newChild(model)) |
|||
return context_iterate(ctx, nargs + 1) |
|||
end |
|||
end |
|||
elseif ctx.subset == -1 then |
|||
tmp = {} |
|||
for key, val in pairs(tbl) do tmp[key] = true end |
|||
-- See iface.renaming_blindly_by_calling() |
|||
for key, val in ipairs(tmp) do tmp[key] = nil end |
|||
library.renaming_blindly_by_calling = function(ctx) |
|||
for key in pairs(tmp) do |
|||
local opts = ctx.pipe |
|||
margs[1] = tbl[key] |
|||
local tname |
|||
tbl[key] = mfunc(ctx.frame:newChild(model)) |
|||
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end |
|||
end |
|||
if tname == nil then error(ctx.luaname .. ', ‘renaming_blindly_by_calling’: No template name was provided', 0) end |
|||
local nargs |
|||
local margs = {} |
|||
local tmp = tonumber(opts[2]) |
|||
if tmp == nil then |
|||
nargs = 1 |
|||
elseif tmp < 1 then |
|||
nargs = 2 |
|||
else |
else |
||
nargs = tmp + 2 |
|||
for key, val in pairs(tbl) do |
|||
for idx = 3, nargs do margs[idx - 1] = opts[idx] end |
|||
end |
|||
tbl[key] = mfunc(ctx.frame:newChild(model)) |
|||
local model = { title = tname, args = margs } |
|||
end |
|||
local tbl = {} |
|||
for key, val in pairs(ctx.params) do |
|||
margs[1] = key |
|||
tbl[ctx.frame:expandTemplate(model)] = val |
|||
end |
|||
ctx.params = tbl |
|||
return context_iterate(ctx, nargs + 1) |
|||
end |
|||
-- See iface.renaming_blindly_by_invoking() |
|||
library.renaming_blindly_by_invoking = function(ctx) |
|||
local opts = ctx.pipe |
|||
local mname |
|||
local fname |
|||
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end |
|||
if mname == nil then error(ctx.luaname .. ', ‘renaming_blindly_by_invoking’: No module name was provided', 0) end |
|||
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end |
|||
if fname == nil then error(ctx.luaname .. ', ‘renaming_blindly_by_invoking’: No function name was provided', 0) end |
|||
local nargs |
|||
local margs = {} |
|||
local tmp = tonumber(opts[3]) |
|||
if tmp == nil then |
|||
nargs = 2 |
|||
elseif tmp < 1 then |
|||
nargs = 3 |
|||
else |
|||
nargs = tmp + 3 |
|||
for idx = 4, nargs do margs[idx - 2] = opts[idx] end |
|||
end |
|||
local model = { title = 'Module:' .. mname, args = margs } |
|||
local mfunc = require(model.title)[fname] |
|||
local tbl = {} |
|||
for key, val in pairs(ctx.params) do |
|||
margs[1] = key |
|||
tbl[mfunc(ctx.frame:newChild(model))] = val |
|||
end |
end |
||
ctx.params = tbl |
|||
return context_iterate(ctx, nargs + 1) |
return context_iterate(ctx, nargs + 1) |
||
end |
end |
||
Line 913: | Line 1,109: | ||
end |
end |
||
return ctx.ifngiven or '' |
return ctx.ifngiven or '' |
||
end |
|||
-- See iface.self() |
|||
library.self = function(ctx) |
|||
return ctx.frame:getTitle() |
|||
end |
end |
||
Line 972: | Line 1,162: | ||
return context_init(frame, library.cutting, false, false) |
return context_init(frame, library.cutting, false, false) |
||
end |
end |
||
-- Syntax: #invoke:params|cropping|left crop|right crop|function name |
|||
iface.cropping = function(frame) |
|||
return context_init(frame, library.cropping, false, false) |
|||
end |
|||
-- Syntax: #invoke:params|purging|start offset|length|function name |
|||
iface.purging = function(frame) |
|||
return context_init(frame, library.purging, false, false) |
|||
end |
|||
-- Syntax: #invoke:params|backpurging|start offset|length|function name |
|||
iface.backpurging = function(frame) |
|||
return context_init(frame, library.backpurging, false, false) |
|||
end |
|||
-- Syntax: #invoke:params|reversing|function name |
|||
iface.reversing = function(frame) |
|||
return context_init(frame, library.reversing, false, false) |
|||
end |
|||
-- Syntax: #invoke:params|swapping|function name |
|||
--[[ |
|||
iface.swapping = function(frame) |
|||
return context_init(frame, library.swapping, false, false) |
|||
end |
|||
]]-- |
|||
Line 1,014: | Line 1,236: | ||
-- Syntax: #invoke:params| |
-- Syntax: #invoke:params|mapping_by_calling|template name|[number of |
||
-- arguments]|[argument 1]|[argument 2]|[...]|[argument |
-- additional arguments]|[argument 1]|[argument 2]|[...]|[argument |
||
-- name |
-- N]|function name |
||
iface. |
iface.mapping_by_calling = function(frame) |
||
return context_init(frame, library. |
return context_init(frame, library.mapping_by_calling, false, false) |
||
end |
end |
||
-- Syntax: #invoke:params| |
-- Syntax: #invoke:params|mapping_by_invoking|module name|function |
||
-- additional arguments]|[argument 1 |
-- name|[number of additional arguments]|[argument 1]|[argument |
||
-- N]|function name |
-- 2]|[...]|[argument N]|function name |
||
iface. |
iface.mapping_by_invoking = function(frame) |
||
return context_init(frame, library. |
return context_init(frame, library.mapping_by_invoking, false, false) |
||
end |
end |
||
-- Syntax: #invoke:params| |
-- Syntax: #invoke:params|mapping_blindly_by_calling|template |
||
-- additional arguments]|[argument 1 |
-- name|[number of additional arguments]|[argument 1]|[argument |
||
-- N]|function name |
-- 2]|[...]|[argument N]|function name |
||
iface. |
iface.mapping_blindly_by_calling = function(frame) |
||
return context_init(frame, library. |
return context_init(frame, library.mapping_blindly_by_calling, false, |
||
false) |
false) |
||
end |
end |
||
-- Syntax: #invoke:params| |
-- Syntax: #invoke:params|mapping_blindly_by_invoking|module |
||
-- |[number of additional arguments]|[argument |
-- name|function name|[number of additional arguments]|[argument |
||
-- |[argument N]|function name |
-- 1]|[argument 2]|[...]|[argument N]|function name |
||
iface. |
iface.mapping_blindly_by_invoking = function(frame) |
||
return context_init(frame, library. |
return context_init(frame, library.mapping_blindly_by_invoking, false, |
||
false) |
false) |
||
end |
end |
||
-- Syntax: #invoke:params|renaming_blindly_by_calling|template |
|||
-- name|[number of additional arguments]|[argument 1]|[argument |
|||
-- 2]|[...]|[argument N]|function name |
|||
iface.renaming_blindly_by_calling = function(frame) |
|||
return context_init(frame, library.renaming_blindly_by_calling, false, |
|||
false) |
|||
end |
|||
-- Syntax: #invoke:params|renaming_blindly_by_invoking|module |
|||
-- name|function name|[number of additional arguments]|[argument |
|||
-- 1]|[argument 2]|[...]|[argument N]|function name |
|||
iface.renaming_blindly_by_invoking = function(frame) |
|||
return context_init(frame, library.renaming_blindly_by_invoking, false, |
|||
false) |
|||
end |
|||
--[[ First position only ]]-- |
|||
---------------------------------------- |
|||
-- Syntax: #invoke:params|new|function name |
|||
--[[ |
|||
iface.new = function(frame) |
|||
local ctx = context_new(frame) |
|||
ctx.pipe = copy_or_ref_table(frame.args, false) |
|||
ctx.params = {} |
|||
return context_iterate(ctx, 1) |
|||
end |
|||
]]-- |
|||
Line 1,154: | Line 1,411: | ||
return context_init(frame, library.magic_for_each_value, false, false) |
return context_init(frame, library.magic_for_each_value, false, false) |
||
end |
end |
||
--[[ First position only ]]-- |
|||
---------------------------------------- |
|||
Revision as of 00:11, 12 May 2024
Documentation for this module may be created at Module:Params/doc
--- ---
--- LOCAL ENVIRONMENT ---
--- ________________________________ ---
--- ---
-- Special user-given keywords (functions and modifiers MUST avoid these names)
local mkeywords = {
-- ['pattern'] = false,
['plain'] = true,
['or'] = 0
}
-- Memory slots
local memoryslots = {
i = 'itersep',
l = 'lastsep',
p = 'pairsep',
h = 'header',
f = 'footer',
n = 'ifngiven'
}
-- Directive that can only be invoked as first arguments
local firstposonly = {
new = 'modifier',
self = 'function',
}
-- The private table of functions
local library = {}
-- Return a copy or a reference to a table
local function copy_or_ref_table(src, refonly)
if refonly then return src end
newtab = {}
for key, val in pairs(src) do newtab[key] = val end
return newtab
end
-- Prepare the context
local function context_new(frame)
local ctx = {}
ctx.luaname = 'Module:Params' --[[ or `frame:getTitle()` ]]--
ctx.iterfunc = pairs
ctx.frame = frame:getParent()
return ctx
end
-- Prepare the context
local function context_init(frame, funcname, refpipe, refparams)
local ctx = context_new(frame)
ctx.pipe = copy_or_ref_table(frame.args, refpipe)
ctx.params = copy_or_ref_table(ctx.frame.args, refparams)
return funcname(ctx)
end
-- Move to the next action within the user-given list
local function context_iterate(ctx, n_forward)
local nextfn
if ctx.pipe[n_forward] ~= nil then
nextfn = ctx.pipe[n_forward]:match'^%s*(.*%S)'
end
if nextfn == nil then
error(ctx.luaname .. ': You must specify a function to call', 0)
end
if library[nextfn] == nil then
if firstposonly[nextfn] == nil then
error(ctx.luaname .. ': The function ‘' .. nextfn ..
'’ does not exist', 0)
else
error(ctx.luaname .. ': The ‘' .. nextfn .. '’ ' ..
firstposonly[nextfn] ..
' can only be invoked in first position', 0)
end
end
for idx = n_forward, 1, -1 do table.remove(ctx.pipe, idx) end
return library[nextfn](ctx)
end
-- Concatenate the numerical keys from the table of parameters to the numerical
-- keys from the table of options; non-numerical keys from the table of options
-- will prevail over colliding non-numerical keys from the table of parameters
local function concat_params(ctx)
local shift = table.maxn(ctx.pipe)
local newargs = {}
if ctx.subset == 1 then
-- We need only the sequence
for key, val in ipairs(ctx.params) do
newargs[key + shift] = val
end
else
if ctx.subset == -1 then
for key, val in ipairs(ctx.params) do
ctx.params[key] = nil
end
end
for key, val in pairs(ctx.params) do
if type(key) == 'number' then
newargs[key + shift] = val
else
newargs[key] = val
end
end
end
for key, val in pairs(ctx.pipe) do newargs[key] = val end
return newargs
end
-- Helper function for `string.gsub()` (for managing zero-padded numbers)
function zero_padded(str)
return ("%03d%s"):format(#str, str)
end
-- Helper function for `table.sort()` (for natural sorting)
function natural_sort(var1, var2)
return tostring(var1):gsub("%d+", zero_padded) <
tostring(var2):gsub("%d+", zero_padded)
end
-- Flush the parameters by calling a custom function for each value (after this
-- function has been invoked `ctx.params` will no longer be usable)
local function flush_params(ctx, fn)
local tbl = ctx.params
if ctx.subset == 1 then
for key, val in ipairs(tbl) do fn(key, val) end
return
end
if ctx.subset == -1 then
for key, val in ipairs(tbl) do tbl[key] = nil end
end
if ctx.dosort then
local nums = {}
local words = {}
local nlen = 0
local wlen = 0
for key, val in pairs(tbl) do
if type(key) == 'number' then
nlen = nlen + 1
nums[nlen] = key
else
wlen = wlen + 1
words[wlen] = key
end
end
table.sort(nums)
table.sort(words, natural_sort)
for idx = 1, nlen do fn(nums[idx], tbl[nums[idx]]) end
for idx = 1, wlen do fn(words[idx], tbl[words[idx]]) end
return
end
if ctx.subset ~= -1 then
for key, val in ipairs(tbl) do
fn(key, val)
tbl[key] = nil
end
end
for key, val in pairs(tbl) do fn(key, val) end
end
-- Parse the arguments of the `with_*_matching` class of modifiers
local function parse_match_args(opts, ptns, fname)
local state = 0
local cnt = 1
local keyw
local nptns = 0
for _, val in ipairs(opts) do
if state == 0 then
nptns = nptns + 1
ptns[nptns] = { val, false }
state = -1
else
keyw = val:match'^%s*(.*%S)'
if keyw == nil or mkeywords[keyw] == nil then break
else
state = mkeywords[keyw]
if state ~= 0 then ptns[nptns][2] = state end
end
end
cnt = cnt + 1
end
if state == 0 then error(ctx.luaname .. ', ‘' .. fname .. '’: No pattern was given', 0) end
return cnt
end
-- Remove numerical arguments (not necessarily sequential ones)
function purge_args(tbl, offset, length)
local cache = {}
local pshift
local nshift
if offset > 0 then
pshift = length
nshift = 0
elseif offset + length > 1 then
pshift = offset + length - 1
nshift = 1 - offset
else
pshift = 0
nshift = length
end
for key, val in pairs(tbl) do
if type(key) == 'number' then
if key > 0 and key >= offset then
if key >= offset + length then
cache[key - pshift] = val
end
tbl[key] = nil
elseif key < 1 and key < offset + length then
if key < offset then
cache[key + nshift] = val
end
tbl[key] = nil
end
end
end
for key, val in pairs(cache) do tbl[key] = val end
end
--[[ Library's modifiers ]]--
--------------------------------
-- See iface.sequential()
library.sequential = function(ctx)
if ctx.subset == -1 then error(ctx.luaname .. ': The two directives ‘non-sequential’ and ‘sequential’ are in contradiction with each other', 0) end
if ctx.dosort then error(ctx.luaname .. ': The ‘all_sorted’ directive is redundant when followed by ‘sequential’', 0) end
ctx.iterfunc = ipairs
ctx.subset = 1
return context_iterate(ctx, 1)
end
-- See iface['non-sequential']()
library['non-sequential'] = function(ctx)
if ctx.subset == 1 then error(ctx.luaname .. ': The two directives ‘sequential’ and ‘non-sequential’ are in contradiction with each other', 0) end
ctx.iterfunc = pairs
ctx.subset = -1
return context_iterate(ctx, 1)
end
-- See iface.all_sorted()
library.all_sorted = function(ctx)
if ctx.subset == 1 then error(ctx.luaname .. ': The ‘all_sorted’ directive is redundant after ‘sequential’', 0) end
ctx.dosort = true
return context_iterate(ctx, 1)
end
-- See iface.setting()
library.setting = function(ctx)
local opts = ctx.pipe
local cmd
if opts[1] ~= nil then
cmd = opts[1]:gsub('%s+', ''):gsub('/+', '/'):match'^/*(.*[^/])'
end
if cmd == nil then error(ctx.luaname .. ', ‘setting’: No directive was given', 0) end
local sep = string.byte('/')
local argc = 2
local dest = {}
local vname
local chr
for idx = 1, #cmd do
chr = cmd:byte(idx)
if chr == sep then
for key, val in ipairs(dest) do
ctx[val] = opts[argc]
dest[key] = nil
end
argc = argc + 1
else
vname = memoryslots[string.char(chr)]
if vname == nil then error(ctx.luaname .. ', ‘setting’: Unknown slot "' ..
string.char(chr) .. '"', 0) end
table.insert(dest, vname)
end
end
for key, val in ipairs(dest) do ctx[val] = opts[argc] end
return context_iterate(ctx, argc + 1)
end
-- See iface.squeezing()
library.squeezing = function(ctx)
local tbl = ctx.params
local store = {}
local indices = {}
local newlen = 0
for key, val in pairs(tbl) do
if type(key) == 'number' then
newlen = newlen + 1
indices[newlen] = key
store[key] = val
tbl[key] = nil
end
end
table.sort(indices)
for idx = 1, newlen do tbl[idx] = store[indices[idx]] end
return context_iterate(ctx, 1)
end
-- See iface.cutting()
library.cutting = function(ctx)
local lcut = tonumber(ctx.pipe[1])
if lcut == nil then error(ctx.luaname .. ', ‘cutting’: Left cut must be a number', 0) end
local rcut = tonumber(ctx.pipe[2])
if rcut == nil then error(ctx.luaname .. ', ‘cutting’: Right cut must be a number', 0) end
local tbl = ctx.params
local len = #tbl
if lcut < 0 then lcut = len + lcut end
if rcut < 0 then rcut = len + rcut end
local tot = lcut + rcut
if tot > 0 then
local cache = {}
if tot >= len then
for key in ipairs(tbl) do tbl[key] = nil end
tot = len
else
for idx = len - rcut + 1, len, 1 do tbl[idx] = nil end
for idx = 1, lcut, 1 do tbl[idx] = nil end
end
for key, val in pairs(tbl) do
if type(key) == 'number' and key > 0 then
if key > len then
cache[key - tot] = val
else
cache[key - lcut] = val
end
tbl[key] = nil
end
end
for key, val in pairs(cache) do tbl[key] = val end
end
return context_iterate(ctx, 3)
end
-- See iface.cropping()
library.cropping = function(ctx)
local lcut = tonumber(ctx.pipe[1])
if lcut == nil then error(ctx.luaname .. ', ‘cropping’: Left crop must be a number', 0) end
local rcut = tonumber(ctx.pipe[2])
if rcut == nil then error(ctx.luaname .. ', ‘cropping’: Right crop must be a number', 0) end
local tbl = ctx.params
local nmin
local nmax
for key in pairs(tbl) do
if type(key) == 'number' then
if nmin == nil then
nmin = key
nmax = key
elseif key > nmax then nmax = key
elseif key < nmin then nmin = key
end
end
end
if nmin ~= nil then
local len = nmax - nmin + 1
if lcut < 0 then lcut = len + lcut end
if rcut < 0 then rcut = len + rcut end
if lcut + rcut - len > -1 then
for key in pairs(tbl) do
if type(key) == 'number' then tbl[key] = nil end
end
elseif lcut + rcut > 0 then
for idx = nmax - rcut + 1, nmax, 1 do tbl[idx] = nil end
for idx = nmin, nmin + lcut - 1, 1 do tbl[idx] = nil end
local lshift = nmin + lcut - 1
if lshift > 0 then
for idx = lshift + 1, nmax, 1 do
tbl[idx - lshift] = tbl[idx]
tbl[idx] = nil
end
end
end
end
return context_iterate(ctx, 3)
end
-- See iface.purging()
library.purging = function(ctx)
local first = tonumber(ctx.pipe[1])
if first == nil then error(ctx.luaname .. ', ‘purging’: Start offset must be a number', 0) end
local size = tonumber(ctx.pipe[2])
if size == nil then error(ctx.luaname .. ', ‘purging’: Length must be a number', 0) end
local tbl = ctx.params
if size < 1 then
size = size + table.maxn(tbl)
if first > size then return context_iterate(ctx, 3) end
size = size - first + 1
end
purge_args(tbl, first, size)
return context_iterate(ctx, 3)
end
-- See iface.backpurging()
library.backpurging = function(ctx)
local last = tonumber(ctx.pipe[1])
if last == nil then error(ctx.luaname .. ', ‘backpurging’: Start offset must be a number', 0) end
local size = tonumber(ctx.pipe[2])
if size == nil then error(ctx.luaname .. ', ‘backpurging’: Length must be a number', 0) end
local first
local tbl = ctx.params
if size > 0 then
first = last - size + 1
else
for key in pairs(tbl) do
if type(key) == 'number' and (first == nil or
key < first) then first = key end
end
if first == nil then return context_iterate(ctx, 3) end
first = first - size
if last < first then return context_iterate(ctx, 3) end
size = last - first + 1
end
purge_args(ctx.params, first, size)
return context_iterate(ctx, 3)
end
-- See iface.reversing()
library.reversing = function(ctx)
local tbl = ctx.params
local reversed = {}
local nmax
local nmin
for key, val in pairs(tbl) do
if type(key) == 'number' then
reversed[key] = val
tbl[key] = nil
if nmax == nil then
nmax = key
nmin = key
elseif key > nmax then nmax = key
elseif key < nmin then nmin = key
end
end
end
for key, val in pairs(reversed) do
tbl[nmax + nmin - key] = val
end
return context_iterate(ctx, 1)
end
-- See iface.swapping()
--[[
library.swapping = function(ctx)
local tbl = ctx.params
local cache = {}
local nsize = 0
local tmp
for key in pairs(tbl) do
if type(key) == 'number' then
nsize = nsize + 1
cache[nsize] = key
end
end
table.sort(cache)
for idx = math.floor(nsize / 2), 1, -1 do
tmp = tbl[cache[idx] ]
tbl[cache[idx] ] = tbl[cache[nsize - idx + 1] ]
tbl[cache[nsize - idx + 1] ] = tmp
end
return context_iterate(ctx, 1)
end
]]--
-- See iface.with_name_matching()
library.with_name_matching = function(ctx)
local tbl = ctx.params
local patterns = {}
local argc = parse_match_args(ctx.pipe, patterns, 'with_name_matching')
local nomatch
for key in pairs(tbl) do
nomatch = true
for _, ptn in ipairs(patterns) do
if string.find(key, ptn[1], 1, ptn[2]) then
nomatch = false
break
end
end
if nomatch then tbl[key] = nil end
end
return context_iterate(ctx, argc)
end
-- See iface.with_name_not_matching()
library.with_name_not_matching = function(ctx)
local tbl = ctx.params
local patterns = {}
local argc = parse_match_args(ctx.pipe, patterns,
'with_name_not_matching')
local yesmatch
for key in pairs(tbl) do
yesmatch = true
for _, ptn in ipairs(patterns) do
if not string.find(key, ptn[1], 1, ptn[2]) then
yesmatch = false
break
end
end
if yesmatch then tbl[key] = nil end
end
return context_iterate(ctx, argc)
end
-- See iface.with_value_matching()
library.with_value_matching = function(ctx)
local tbl = ctx.params
local patterns = {}
local argc = parse_match_args(ctx.pipe, patterns,
'with_value_matching')
local nomatch
for key, val in pairs(tbl) do
nomatch = true
for _, ptn in ipairs(patterns) do
if string.find(val, ptn[1], 1, ptn[2]) then
nomatch = false
break
end
end
if nomatch then tbl[key] = nil end
end
return context_iterate(ctx, argc)
end
-- See iface.with_value_not_matching()
library.with_value_not_matching = function(ctx)
local tbl = ctx.params
local patterns = {}
local argc = parse_match_args(ctx.pipe, patterns,
'with_value_not_matching')
local yesmatch
for key, val in pairs(tbl) do
yesmatch = true
for _, ptn in ipairs(patterns) do
if not string.find(val, ptn[1], 1, ptn[2]) then
yesmatch = false
break
end
end
if yesmatch then tbl[key] = nil end
end
return context_iterate(ctx, argc)
end
-- See iface.trimming_values()
library.trimming_values = function(ctx)
local tbl = ctx.params
for key, val in pairs(tbl) do tbl[key] = val:match'^%s*(.-)%s*$' end
return context_iterate(ctx, 1)
end
-- See iface.mapping_by_calling()
library.mapping_by_calling = function(ctx)
local opts = ctx.pipe
local tname
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
if tname == nil then error(ctx.luaname .. ', ‘mapping_by_calling’: No template name was provided', 0) end
local nargs
local margs = {}
local tmp = tonumber(opts[2])
if tmp == nil then
nargs = 1
elseif tmp < 1 then
nargs = 2
else
nargs = tmp + 2
for idx = 3, nargs do margs[idx] = opts[idx] end
end
local model = { title = tname, args = margs }
local tbl = ctx.params
for key, val in pairs(tbl) do
margs[1] = key
margs[2] = val
tbl[key] = ctx.frame:expandTemplate(model)
end
return context_iterate(ctx, nargs + 1)
end
-- See iface.mapping_by_invoking()
library.mapping_by_invoking = function(ctx)
local opts = ctx.pipe
local mname
local fname
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if mname == nil then error(ctx.luaname .. ', ‘mapping_by_invoking’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname .. ', ‘mapping_by_invoking’: No function name was provided', 0) end
local nargs
local margs = {}
local tmp = tonumber(opts[3])
if tmp == nil then
nargs = 2
elseif tmp < 1 then
nargs = 3
else
nargs = tmp + 3
for idx = 4, nargs do margs[idx - 1] = opts[idx] end
end
local model = { title = 'Module:' .. mname, args = margs }
local mfunc = require(model.title)[fname]
local tbl = ctx.params
for key, val in pairs(tbl) do
margs[1] = key
margs[2] = val
tbl[key] = mfunc(ctx.frame:newChild(model))
end
return context_iterate(ctx, nargs + 1)
end
-- See iface.mapping_blindly_by_calling()
library.mapping_blindly_by_calling = function(ctx)
local opts = ctx.pipe
local tname
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
if tname == nil then error(ctx.luaname .. ', ‘mapping_blindly_by_calling’: No template name was provided', 0) end
local nargs
local margs = {}
local tmp = tonumber(opts[2])
if tmp == nil then
nargs = 1
elseif tmp < 1 then
nargs = 2
else
nargs = tmp + 2
for idx = 3, nargs do margs[idx - 1] = opts[idx] end
end
local model = { title = tname, args = margs }
local tbl = ctx.params
for key, val in pairs(tbl) do
margs[1] = val
tbl[key] = ctx.frame:expandTemplate(model)
end
return context_iterate(ctx, nargs + 1)
end
-- See iface.mapping_blindly_by_invoking()
library.mapping_blindly_by_invoking = function(ctx)
local opts = ctx.pipe
local mname
local fname
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if mname == nil then error(ctx.luaname .. ', ‘mapping_blindly_by_invoking’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname .. ', ‘mapping_blindly_by_invoking’: No function name was provided', 0) end
local nargs
local margs = {}
local tmp = tonumber(opts[3])
if tmp == nil then
nargs = 2
elseif tmp < 1 then
nargs = 3
else
nargs = tmp + 3
for idx = 4, nargs do margs[idx - 2] = opts[idx] end
end
local model = { title = 'Module:' .. mname, args = margs }
local mfunc = require(model.title)[fname]
local tbl = ctx.params
for key, val in pairs(tbl) do
margs[1] = val
tbl[key] = mfunc(ctx.frame:newChild(model))
end
return context_iterate(ctx, nargs + 1)
end
-- See iface.renaming_blindly_by_calling()
library.renaming_blindly_by_calling = function(ctx)
local opts = ctx.pipe
local tname
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
if tname == nil then error(ctx.luaname .. ', ‘renaming_blindly_by_calling’: No template name was provided', 0) end
local nargs
local margs = {}
local tmp = tonumber(opts[2])
if tmp == nil then
nargs = 1
elseif tmp < 1 then
nargs = 2
else
nargs = tmp + 2
for idx = 3, nargs do margs[idx - 1] = opts[idx] end
end
local model = { title = tname, args = margs }
local tbl = {}
for key, val in pairs(ctx.params) do
margs[1] = key
tbl[ctx.frame:expandTemplate(model)] = val
end
ctx.params = tbl
return context_iterate(ctx, nargs + 1)
end
-- See iface.renaming_blindly_by_invoking()
library.renaming_blindly_by_invoking = function(ctx)
local opts = ctx.pipe
local mname
local fname
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if mname == nil then error(ctx.luaname .. ', ‘renaming_blindly_by_invoking’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname .. ', ‘renaming_blindly_by_invoking’: No function name was provided', 0) end
local nargs
local margs = {}
local tmp = tonumber(opts[3])
if tmp == nil then
nargs = 2
elseif tmp < 1 then
nargs = 3
else
nargs = tmp + 3
for idx = 4, nargs do margs[idx - 2] = opts[idx] end
end
local model = { title = 'Module:' .. mname, args = margs }
local mfunc = require(model.title)[fname]
local tbl = {}
for key, val in pairs(ctx.params) do
margs[1] = key
tbl[mfunc(ctx.frame:newChild(model))] = val
end
ctx.params = tbl
return context_iterate(ctx, nargs + 1)
end
--[[ Library's functions ]]--
------------------------------------
-- See iface.count()
library.count = function(ctx)
local count = 0
for _ in ctx.iterfunc(ctx.params) do count = count + 1 end
if ctx.subset == -1 then count = count - #ctx.params end
return count
end
-- See iface.concat_and_call()
library.concat_and_call = function(ctx)
local opts = ctx.pipe
local tname
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
if tname == nil then error(ctx.luaname .. ', ‘concat_and_call’: No template name was provided', 0) end
table.remove(opts, 1)
return ctx.frame:expandTemplate{
title = tname,
args = concat_params(ctx)
}
end
-- See iface.concat_and_invoke()
library.concat_and_invoke = function(ctx)
local opts = ctx.pipe
local mname
local fname
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if mname == nil then error(ctx.luaname .. ', ‘concat_and_invoke’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname .. ', ‘concat_and_invoke’: No function name was provided', 0) end
table.remove(opts, 2)
table.remove(opts, 1)
return require('Module:' .. mname)[fname](ctx.frame:newChild{
title = 'Module:' .. fname,
args = concat_params(ctx)
})
end
-- See iface.concat_and_magic()
library.concat_and_magic = function(ctx)
local opts = ctx.pipe
local magic
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
if magic == nil then error(ctx.luaname .. ', ‘concat_and_magic’: No parser function was provided', 0) end
table.remove(opts, 1)
return ctx.frame:callParserFunction(magic, concat_params(ctx))
end
-- See iface.value_of()
library.value_of = function(ctx)
local opts = ctx.pipe
local keystr
if opts[1] ~= nil then keystr = opts[1]:match'^%s*(.*%S)' end
if keystr == nil then error(ctx.luaname .. ', ‘value_of’: No parameter name was provided', 0) end
local keynum = tonumber(keystr)
local len = #ctx.params
if (
ctx.subset == -1 and keynum ~= nil and len >= keynum
) or (
ctx.subset == 1 and (keynum == nil or len < keynum)
) then return (ctx.ifngiven or '') end
local val = ctx.params[keynum or keystr]
if val == nil then return (ctx.ifngiven or '') end
return (ctx.header or '') .. val .. (ctx.footer or '')
end
-- See iface.list()
library.list = function(ctx)
local kvs = ctx.pairsep or ''
local pps = ctx.itersep or ''
local ret = {}
local nss = 0
flush_params(
ctx,
function(key, val)
ret[nss + 1] = pps
ret[nss + 2] = key
ret[nss + 3] = kvs
ret[nss + 4] = val
nss = nss + 4
end
)
if nss > 0 then
if nss > 4 and ctx.lastsep ~= nil then
ret[nss - 3] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
return table.concat(ret)
end
return ctx.ifngiven or ''
end
-- See iface.list_values()
library.list_values = function(ctx)
local pps = ctx.itersep or ''
local ret = {}
local nss = 0
flush_params(
ctx,
function(key, val)
ret[nss + 1] = pps
ret[nss + 2] = val
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
return table.concat(ret)
end
return ctx.ifngiven or ''
end
-- See iface.for_each()
library.for_each = function(ctx)
local txt = ctx.pipe[1] or ''
local pps = ctx.itersep or ''
local ret = {}
local nss = 0
flush_params(
ctx,
function(key, val)
ret[nss + 1] = pps
ret[nss + 2] = txt:gsub('%$#', key):gsub('%$@', val)
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
return table.concat(ret)
end
return ctx.ifngiven or ''
end
-- See iface.call_for_each()
library.call_for_each = function(ctx)
local opts = ctx.pipe
local tname
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
if tname == nil then error(ctx.luaname .. ', ‘call_for_each’: No template name was provided', 0) end
local model = { title = tname, args = opts }
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
table.insert(opts, 1, true)
flush_params(
ctx,
function(key, val)
opts[1] = key
opts[2] = val
ret[nss + 1] = ccs
ret[nss + 2] = ctx.frame:expandTemplate(model)
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
return table.concat(ret)
end
return ctx.ifngiven or ''
end
-- See iface.invoke_for_each()
library.invoke_for_each = function(ctx)
local opts = ctx.pipe
local mname
local fname
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if mname == nil then error(ctx.luaname .. ', ‘invoke_for_each’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname .. ', ‘invoke_for_each’: No function name was provided', 0) end
local model = { title = 'Module:' .. mname, args = opts }
local mfunc = require(model.title)[fname]
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
flush_params(
ctx,
function(key, val)
opts[1] = key
opts[2] = val
ret[nss + 1] = ccs
ret[nss + 2] = mfunc(ctx.frame:newChild(model))
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
return table.concat(ret)
end
return ctx.ifngiven or ''
end
-- See iface.magic_for_each()
library.magic_for_each = function(ctx)
local opts = ctx.pipe
local magic
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
if magic == nil then error(ctx.luaname .. ', ‘magic_for_each’: No parser function was provided', 0) end
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
table.insert(opts, 1, true)
flush_params(
ctx,
function(key, val)
opts[1] = key
opts[2] = val
ret[nss + 1] = ccs
ret[nss + 2] = ctx.frame:callParserFunction(magic,
opts)
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
return table.concat(ret)
end
return ctx.ifngiven or ''
end
-- See iface.call_for_each_value()
library.call_for_each_value = function(ctx)
local opts = ctx.pipe
local tname
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
if tname == nil then error(ctx.luaname .. ', ‘call_for_each_value’: No template name was provided', 0) end
local model = { title = tname, args = opts }
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
flush_params(
ctx,
function(key, val)
opts[1] = val
ret[nss + 1] = ccs
ret[nss + 2] = ctx.frame:expandTemplate(model)
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
return table.concat(ret)
end
return ctx.ifngiven or ''
end
-- See iface.invoke_for_each_value()
library.invoke_for_each_value = function(ctx)
local opts = ctx.pipe
local mname
local fname
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if mname == nil then error(ctx.luaname .. ', ‘invoke_for_each_value’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname .. ', ‘invoke_for_each_value’: No function name was provided', 0) end
local model = { title = 'Module:' .. mname, args = opts }
local mfunc = require(model.title)[fname]
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
table.remove(opts, 1)
flush_params(
ctx,
function(key, val)
opts[1] = val
ret[nss + 1] = ccs
ret[nss + 2] = mfunc(ctx.frame:newChild(model))
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
return table.concat(ret)
end
return ctx.ifngiven or ''
end
-- See iface.magic_for_each_value()
library.magic_for_each_value = function(ctx)
local opts = ctx.pipe
local magic
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
if magic == nil then error(ctx.luaname .. ', ‘magic_for_each_value’: No parser function was provided', 0) end
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
flush_params(
ctx,
function(key, val)
opts[1] = val
ret[nss + 1] = ccs
ret[nss + 2] = ctx.frame:callParserFunction(magic,
opts)
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
return table.concat(ret)
end
return ctx.ifngiven or ''
end
--- ---
--- PUBLIC ENVIRONMENT ---
--- ________________________________ ---
--- ---
-- The public table of functions
local iface = {}
--[[ Modifiers ]]--
------------------------------------
-- Syntax: #invoke:params|sequential|function name
iface.sequential = function(frame)
return context_init(frame, library.sequential, false, false)
end
-- Syntax: #invoke:params|non-sequential|function name
iface['non-sequential'] = function(frame)
return context_init(frame, library['non-sequential'], false, false)
end
-- Syntax: #invoke:params|sort|function name
iface.all_sorted = function(frame)
return context_init(frame, library.all_sorted, false, false)
end
-- Syntax: #invoke:params|setting|directives|...|function name
iface.setting = function(frame)
return context_init(frame, library.setting, false, false)
end
-- Syntax: #invoke:params|squeezing|function name
iface.squeezing = function(frame)
return context_init(frame, library.squeezing, false, false)
end
-- Syntax: #invoke:params|cutting|left cut|right cut|function name
iface.cutting = function(frame)
return context_init(frame, library.cutting, false, false)
end
-- Syntax: #invoke:params|cropping|left crop|right crop|function name
iface.cropping = function(frame)
return context_init(frame, library.cropping, false, false)
end
-- Syntax: #invoke:params|purging|start offset|length|function name
iface.purging = function(frame)
return context_init(frame, library.purging, false, false)
end
-- Syntax: #invoke:params|backpurging|start offset|length|function name
iface.backpurging = function(frame)
return context_init(frame, library.backpurging, false, false)
end
-- Syntax: #invoke:params|reversing|function name
iface.reversing = function(frame)
return context_init(frame, library.reversing, false, false)
end
-- Syntax: #invoke:params|swapping|function name
--[[
iface.swapping = function(frame)
return context_init(frame, library.swapping, false, false)
end
]]--
-- Syntax: #invoke:params|with_name_matching|pattern 1|[plain flag 1]|[or]
-- |[pattern 2]|[plain flag 2]|[or]|[...]|[pattern N]|[plain flag
-- N]|function name
iface.with_name_matching = function(frame)
return context_init(frame, library.with_name_matching, false, false)
end
-- Syntax: #invoke:params|with_name_not_matching|pattern 1|[plain flag 1]
-- |[and]|[pattern 2]|[plain flag 2]|[and]|[...]|[pattern N]|[plain
-- flag N]|function name
iface.with_name_not_matching = function(frame)
return context_init(frame, library.with_name_not_matching, false,
false)
end
-- Syntax: #invoke:params|with_value_matching|pattern 1|[plain flag 1]|[or]
-- |[pattern 2]|[plain flag 2]|[or]|[...]|[pattern N]|[plain flag
-- N]|function name
iface.with_value_matching = function(frame)
return context_init(frame, library.with_value_matching, false, false)
end
-- Syntax: #invoke:params|with_value_not_matching|pattern 1|[plain flag 1]
-- |[and]|[pattern 2]|[plain flag 2]|[and]|[...]|[pattern N]|[plain
-- flag N]|function name
iface.with_value_not_matching = function(frame)
return context_init(frame, library.with_value_not_matching, false,
false)
end
-- Syntax: #invoke:params|trimming_values|function name
iface.trimming_values = function(frame)
return context_init(frame, library.trimming_values, false, false)
end
-- Syntax: #invoke:params|mapping_by_calling|template name|[number of
-- additional arguments]|[argument 1]|[argument 2]|[...]|[argument
-- N]|function name
iface.mapping_by_calling = function(frame)
return context_init(frame, library.mapping_by_calling, false, false)
end
-- Syntax: #invoke:params|mapping_by_invoking|module name|function
-- name|[number of additional arguments]|[argument 1]|[argument
-- 2]|[...]|[argument N]|function name
iface.mapping_by_invoking = function(frame)
return context_init(frame, library.mapping_by_invoking, false, false)
end
-- Syntax: #invoke:params|mapping_blindly_by_calling|template
-- name|[number of additional arguments]|[argument 1]|[argument
-- 2]|[...]|[argument N]|function name
iface.mapping_blindly_by_calling = function(frame)
return context_init(frame, library.mapping_blindly_by_calling, false,
false)
end
-- Syntax: #invoke:params|mapping_blindly_by_invoking|module
-- name|function name|[number of additional arguments]|[argument
-- 1]|[argument 2]|[...]|[argument N]|function name
iface.mapping_blindly_by_invoking = function(frame)
return context_init(frame, library.mapping_blindly_by_invoking, false,
false)
end
-- Syntax: #invoke:params|renaming_blindly_by_calling|template
-- name|[number of additional arguments]|[argument 1]|[argument
-- 2]|[...]|[argument N]|function name
iface.renaming_blindly_by_calling = function(frame)
return context_init(frame, library.renaming_blindly_by_calling, false,
false)
end
-- Syntax: #invoke:params|renaming_blindly_by_invoking|module
-- name|function name|[number of additional arguments]|[argument
-- 1]|[argument 2]|[...]|[argument N]|function name
iface.renaming_blindly_by_invoking = function(frame)
return context_init(frame, library.renaming_blindly_by_invoking, false,
false)
end
--[[ First position only ]]--
----------------------------------------
-- Syntax: #invoke:params|new|function name
--[[
iface.new = function(frame)
local ctx = context_new(frame)
ctx.pipe = copy_or_ref_table(frame.args, false)
ctx.params = {}
return context_iterate(ctx, 1)
end
]]--
--[[ Functions ]]--
----------------------------------------
-- Syntax: #invoke:params|count
iface.count = function(frame)
return context_init(frame, library.count, true, true)
end
-- Syntax: #invoke:args|concat_and_call|template name|[prepend 1]|[prepend 2]
-- |[...]|[item n]|[named item 1=value 1]|[...]|[named item n=value
-- n]|[...]
iface.concat_and_call = function(frame)
return context_init(frame, library.concat_and_call, false, true)
end
-- Syntax: #invoke:args|concat_and_invoke|module name|function name|[prepend
-- 1]|[prepend 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named
-- item n=value n]|[...]
iface.concat_and_invoke = function(frame)
return context_init(frame, library.concat_and_invoke, false, true)
end
-- Syntax: #invoke:args|concat_and_magic|parser function|[prepend 1]|[prepend
-- 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named item n=
-- value n]|[...]
iface.concat_and_magic = function(frame)
return context_init(frame, library.concat_and_magic, false, true)
end
-- Syntax: #invoke:params|value_of|parameter name
iface.value_of = function(frame)
return context_init(frame, library.value_of, true, true)
end
-- Syntax: #invoke:params|list
iface.list = function(frame)
return context_init(frame, library.list, true, false)
end
-- Syntax: #invoke:params|list_values
iface.list_values = function(frame)
return context_init(frame, library.list_values, true, false)
end
-- Syntax: #invoke:params|for_each|wikitext
iface.for_each = function(frame)
return context_init(frame, library.for_each, true, false)
end
-- Syntax: #invoke:params|call_for_each|template name|[append 1]|[append 2]
-- |[...]|[append n]|[named param 1=value 1]|[...]|[named param
-- n=value n]|[...]
iface.call_for_each = function(frame)
return context_init(frame, library.call_for_each, false, false)
end
-- Syntax: #invoke:params|invoke_for_each|module name|module function|[append
-- 1]|[append 2]|[...]|[append n]|[named param 1=value 1]|[...]
-- |[named param n=value n]|[...]
iface.invoke_for_each = function(frame)
return context_init(frame, library.invoke_for_each, false, false)
end
-- Syntax: #invoke:params|magic_for_each|parser function|[append 1]|[append 2]
-- |[...]|[append n]|[named param 1=value 1]|[...]|[named param
-- n=value n]|[...]
iface.magic_for_each = function(frame)
return context_init(frame, library.magic_for_each, false, false)
end
-- Syntax: #invoke:params|call_for_each_value|template name|[append 1]|[append
-- 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param
-- n=value n]|[...]
iface.call_for_each_value = function(frame)
return context_init(frame, library.call_for_each_value, false, false)
end
-- Syntax: #invoke:params|invoke_for_each_value|module name|[append 1]|[append
-- 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param
-- n=value n]|[...]
iface.invoke_for_each_value = function(frame)
return context_init(frame, library.invoke_for_each_value, false, false)
end
-- Syntax: #invoke:params|magic_for_each_value|parser function|[append 1]
-- |[append 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named
-- param n=value n]|[...]
iface.magic_for_each_value = function(frame)
return context_init(frame, library.magic_for_each_value, false, false)
end
--[[ First position only ]]--
----------------------------------------
-- Syntax: #invoke:params|self
iface.self = function(frame)
return frame:getParent():getTitle()
end
return iface