Module:Params: Difference between revisions
Jump to navigation
Jump to search
The directives ‘with_pattern_isep’, ‘with_plain_isep’, ‘with_pattern_psep’ and ‘with_plain_psep’ have been renamed respectively to ‘splitter_pattern’, ‘splitter_string’, ‘setter_pattern’, and ‘setter_string’
(Created page with " --- --- --- LOCAL ENVIRONMENT --- --- ________________________________ --- --- --- --Abstract utilities -- ---------------------------- -- 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 natura...") |
(The directives ‘with_pattern_isep’, ‘with_plain_isep’, ‘with_pattern_psep’ and ‘with_plain_psep’ have been renamed respectively to ‘splitter_pattern’, ‘splitter_string’, ‘setter_pattern’, and ‘setter_string’) |
||
| Line 11: | Line 11: | ||
-- Helper function for `string.gsub()` (for managing zero-padded numbers) | -- Helper function for `string.gsub()` (for managing zero-padded numbers) | ||
function zero_padded(str) | local function zero_padded (str) | ||
return ('%03d%s'):format(#str, str) | return ('%03d%s'):format(#str, str) | ||
end | end | ||
| Line 17: | Line 17: | ||
-- Helper function for `table.sort()` (for natural sorting) | -- Helper function for `table.sort()` (for natural sorting) | ||
function natural_sort(var1, var2) | local function natural_sort (var1, var2) | ||
return tostring(var1):gsub('%d+', zero_padded) < | return tostring(var1):gsub('%d+', zero_padded) < | ||
tostring(var2):gsub('%d+', zero_padded) | tostring(var2):gsub('%d+', zero_padded) | ||
| Line 24: | Line 24: | ||
-- Return a copy or a reference to a table | -- Return a copy or a reference to a table | ||
local function copy_or_ref_table(src, refonly) | local function copy_or_ref_table (src, refonly) | ||
if refonly then return src end | if refonly then return src end | ||
newtab = {} | newtab = {} | ||
| Line 32: | Line 32: | ||
-- Remove | -- Remove some numeric elements from a table, shifting everything to the left | ||
function | local function remove_numeric_keys (tbl, idx, len) | ||
local cache = {} | local cache = {} | ||
local tmp = idx + len - 1 | local tmp = idx + len - 1 | ||
| Line 47: | Line 47: | ||
-- Make a reduced copy of a table (shifting in both directions if necessary) | -- Make a reduced copy of a table (shifting in both directions if necessary) | ||
function copy_table_reduced(tbl, idx, len) | local function copy_table_reduced (tbl, idx, len) | ||
local ret = {} | local ret = {} | ||
local tmp = idx + len - 1 | local tmp = idx + len - 1 | ||
| Line 75: | Line 75: | ||
-- Make an expanded copy of a table (shifting in both directions if necessary) | -- Make an expanded copy of a table (shifting in both directions if necessary) | ||
function copy_table_expanded(tbl, idx, len) | --[[ | ||
local function copy_table_expanded (tbl, idx, len) | |||
local ret = {} | local ret = {} | ||
local tmp = idx + len - 1 | local tmp = idx + len - 1 | ||
| Line 100: | Line 101: | ||
return ret | return ret | ||
end | end | ||
]]-- | |||
-- Move a key from a table to another, but only if under a different name and | -- Move a key from a table to another, but only if under a different name and | ||
-- always parsing | -- always parsing numeric strings as numbers | ||
function steal_if_renamed(val, src, skey, dest, dkey) | local function steal_if_renamed (val, src, skey, dest, dkey) | ||
local realkey = tonumber(dkey) or dkey:match'^%s*(.-)%s*$' | local realkey = tonumber(dkey) or dkey:match'^%s*(.-)%s*$' | ||
if skey ~= realkey then | if skey ~= realkey then | ||
| Line 121: | Line 123: | ||
local mkeywords = { | local mkeywords = { | ||
['or'] = 0, | ['or'] = 0, | ||
pattern = 1, | |||
plain = 2, | plain = 2, | ||
strict = 3 | strict = 3 | ||
| Line 171: | Line 173: | ||
f = 'footer', | f = 'footer', | ||
n = 'ifngiven' | n = 'ifngiven' | ||
} | |||
-- Possible trimming modes for the `parsing` modifier | |||
local trim_parse_opts = { | |||
trim_none = { false, false }, | |||
trim_positional = { false, true }, | |||
trim_named = { true, false }, | |||
trim_all = { true, true } | |||
} | |||
-- Possible string modes for the iteration separator in the `parsing` and | |||
-- `reinterpreting` modifiers | |||
local isep_parse_opts = { | |||
splitter_pattern = false, | |||
splitter_string = true | |||
} | |||
-- Possible string modes for the key-value separator in the `parsing` and | |||
-- `reinterpreting` modifiers | |||
local psep_parse_opts = { | |||
setter_pattern = false, | |||
setter_string = true | |||
} | } | ||
| Line 182: | Line 209: | ||
-- Maximum number of | -- Hard-coded name of the module (to avoid going through `frame:getTitle()`) | ||
local modulename = 'Module:Params' | |||
-- The functions listed here declare that they don't need the `frame.args` | |||
-- metatable to be copied into a regular table; if they are modifiers they also | |||
-- guarantee that they will make their own (modified) copy available | |||
local refpipe = { | |||
call_for_each_group = true, | |||
coins = true, | |||
count = true, | |||
for_each = true, | |||
list = true, | |||
list_values = true, | |||
value_of = true | |||
} | |||
-- The functions listed here declare that they don't need the | |||
-- `frame:getParent().args` metatable to be copied into a regular table; if | |||
-- they are modifiers they also guarantee that they will make their own | |||
-- (modified) copy available | |||
local refparams = { | |||
call_for_each_group = true, | |||
combining_by_calling = true, | |||
concat_and_call = true, | |||
concat_and_invoke = true, | |||
concat_and_magic = true, | |||
count = true, | |||
--inserting = true, | |||
grouping_by_calling = true, | |||
value_of = true, | |||
with_name_matching = true | |||
} | |||
-- Maximum number of numeric parameters that can be filled, if missing (we | |||
-- chose an arbitrary number for this constant; you can discuss about its | -- chose an arbitrary number for this constant; you can discuss about its | ||
-- optimal value at Module talk:Params) | -- optimal value at Module talk:Params) | ||
| Line 192: | Line 255: | ||
-- Functions that can only be invoked in first position | -- Functions and modifiers that can only be invoked in first position | ||
local static_iface = {} | local static_iface = {} | ||
-- Create a new context | -- Create a new context | ||
local function context_new() | local function context_new (frame) | ||
local ctx = {} | local ctx = {} | ||
ctx. | ctx.frame = frame | ||
ctx.oparams = frame.args | |||
ctx.firstposonly = static_iface | |||
ctx.iterfunc = pairs | ctx.iterfunc = pairs | ||
ctx. | ctx.sorttype = 0 | ||
ctx.n_parents = 0 | |||
ctx.n_children = 0 | |||
ctx.n_available = maxfill | ctx.n_available = maxfill | ||
return ctx | return ctx | ||
| Line 208: | Line 275: | ||
-- Move to the next action within the user-given list | -- Move to the next action within the user-given list | ||
local function context_iterate(ctx, n_forward) | local function context_iterate (ctx, n_forward) | ||
local nextfn | local nextfn | ||
if ctx.pipe[n_forward] ~= nil then | if ctx.pipe[n_forward] ~= nil then | ||
nextfn = ctx.pipe[n_forward]:match'^%s*(.*%S)' | nextfn = ctx.pipe[n_forward]:match'^%s*(.*%S)' | ||
end | end | ||
if nextfn == nil then error( | if nextfn == nil then error(modulename .. | ||
': You must specify a function to call', 0) end | ': You must specify a function to call', 0) end | ||
if library[nextfn] == nil then | if library[nextfn] == nil then | ||
if ctx.firstposonly[nextfn] == nil then error( | if ctx.firstposonly[nextfn] == nil then error(modulename .. | ||
': The function ‘' .. nextfn .. '’ does not exist', 0) | ': The function ‘' .. nextfn .. '’ does not exist', 0) | ||
else error( | else error(modulename .. ': The ‘' .. nextfn .. | ||
'’ directive can only appear in first position', 0) | '’ directive can only appear in first position', 0) | ||
end | end | ||
end | end | ||
remove_numeric_keys(ctx.pipe, 1, n_forward) | |||
return library[nextfn] | return library[nextfn] | ||
end | end | ||
| Line 228: | Line 295: | ||
-- Main loop | -- Main loop | ||
local function main_loop(ctx, start_with) | local function main_loop (ctx, start_with) | ||
local fn = start_with | local fn = start_with | ||
repeat fn = fn(ctx) until not fn | repeat fn = fn(ctx) until not fn | ||
if ctx.n_parents > 0 then error(modulename .. | |||
': One or more ‘merging_substack’ directives are missing', 0) end | |||
if ctx.n_children > 0 then error(modulename .. | |||
', For some of the snapshots either the ‘flushing’ directive is missing or a group has not been properly closed with ‘merging_substack’', 0) end | |||
end | |||
-- Add a new stack of parameters to `ctx.children` | |||
local function push_cloned_stack (ctx, tbl) | |||
local newparams = {} | |||
local currsnap = ctx.n_children + 1 | |||
if ctx.children == nil then ctx.children = { newparams } | |||
else ctx.children[currsnap] = newparams end | |||
for key, val in pairs(tbl) do newparams[key] = val end | |||
ctx.n_children = currsnap | |||
end | end | ||
-- Parse the arguments of the `mapping_*` and `renaming_*` class of modifiers | -- Parse optional user arguments of type `...|[let]|[...][number of additional | ||
function | -- parameters]|[parameter 1]|[parameter 2]|[...]` | ||
local style | local function load_child_opts (src, start_from, append_after) | ||
local shf | local names | ||
local tmp | |||
local tbl = {} | |||
local pin = start_from | |||
if src[pin] ~= nil and src[pin]:match'^%s*let%s*$' then | |||
names = {} | |||
repeat | |||
tmp = src[pin + 1] or '' | |||
names[tonumber(tmp) or tmp:match'^%s*(.-)%s*$' or ''] = | |||
src[pin + 2] | |||
pin = pin + 3 | |||
until src[pin] == nil or not src[pin]:match'^%s*let%s*$' | |||
end | |||
tmp = tonumber(src[pin]) | |||
if tmp ~= nil then | |||
if tmp < 0 then tmp = -1 end | |||
local shf = append_after - pin | |||
for idx = pin + 1, pin + tmp do tbl[idx + shf] = src[idx] end | |||
pin = pin + tmp + 1 | |||
end | |||
if names ~= nil then | |||
for key, val in pairs(names) do tbl[key] = val end | |||
end | |||
return tbl, pin | |||
end | |||
-- Load the optional arguments of some of the `mapping_*` and `renaming_*` | |||
-- class of modifiers | |||
local function load_callback_opts (src, n_skip, default_style) | |||
local style | |||
local shf | |||
local tmp = src[n_skip + 1] | local tmp = src[n_skip + 1] | ||
if tmp ~= nil then style = mapping_styles[tmp:match'^%s*(.-)%s*$'] end | if tmp ~= nil then style = mapping_styles[tmp:match'^%s*(.-)%s*$'] end | ||
| Line 244: | Line 357: | ||
shf = n_skip - 1 | shf = n_skip - 1 | ||
else shf = n_skip end | else shf = n_skip end | ||
local n_exist = style[3] | local n_exist = style[3] | ||
local karg = style[4] | local karg = style[4] | ||
| Line 264: | Line 374: | ||
else n_exist = math.max(n_exist, varg) end | else n_exist = math.max(n_exist, varg) end | ||
end | end | ||
local dest, nargs = load_child_opts(src, style[2] + shf, n_exist) | |||
tmp = style[1] | |||
if (tmp == 3 or tmp == 2) and dest[karg] ~= nil then | |||
tmp = style[1] | |||
if (tmp == 3 or tmp == 2) and dest[karg] ~= nil then | |||
tmp = tmp - 2 end | tmp = tmp - 2 end | ||
if (tmp == 3 or tmp == 1) and dest[varg] ~= nil then | if (tmp == 3 or tmp == 1) and dest[varg] ~= nil then | ||
tmp = tmp - 1 end | tmp = tmp - 1 end | ||
return nargs, tmp, karg, varg | return dest, nargs, tmp, karg, varg | ||
end | |||
-- Parse the arguments of some of the `mapping_*` and `renaming_*` class of | |||
-- modifiers | |||
local function load_replace_args (opts, fname) | |||
if opts[1] == nil then error(modulename .. | |||
', ‘' .. fname .. '’: No pattern string was given', 0) end | |||
if opts[2] == nil then error(modulename .. | |||
', ‘' .. fname .. '’: No replacement string was given', 0) end | |||
local ptn = opts[1] | |||
local repl = opts[2] | |||
local argc = 3 | |||
local nmax = tonumber(opts[3]) | |||
if nmax ~= nil or (opts[3] or ''):match'^%s*$' ~= nil then argc = 4 end | |||
local flg = opts[argc] | |||
if flg ~= nil then flg = mkeywords[flg:match'^%s*(.-)%s*$'] end | |||
if flg == 0 then flg = nil elseif flg ~= nil then argc = argc + 1 end | |||
return ptn, repl, nmax, flg == 3, argc, (nmax ~= nil and nmax < 1) or | |||
(flg == 3 and ptn == repl) | |||
end | end | ||
-- Parse the arguments of the `with_*_matching` class of modifiers | -- Parse the arguments of the `with_*_matching` class of modifiers | ||
local function | local function load_pattern_args (opts, fname) | ||
local state = 0 | local state = 0 | ||
local cnt = 1 | local cnt = 1 | ||
local keyw | local keyw | ||
local nptns = 0 | local nptns = 0 | ||
for _, val in ipairs( | local ptns = {} | ||
for _, val in ipairs(opts) do | |||
if state == 0 then | if state == 0 then | ||
nptns = nptns + 1 | nptns = nptns + 1 | ||
| Line 316: | Line 429: | ||
cnt = cnt + 1 | cnt = cnt + 1 | ||
end | end | ||
if state == 0 then error( | if state == 0 then error(modulename .. ', ‘' .. fname .. | ||
'’: No pattern was given', 0) end | '’: No pattern was given', 0) end | ||
return cnt | return ptns, nptns, cnt | ||
end | |||
-- Load the optional arguments of the `parsing` and `reinterpreting` modifiers | |||
local function load_parse_opts (opts, start_from) | |||
local argc = start_from | |||
local tmp | |||
local optslots = { true, true, true } | |||
local noptslots = 3 | |||
local trimn = true | |||
local trimu = false | |||
local iplain = true | |||
local pplain = true | |||
local isp = '|' | |||
local psp = '=' | |||
repeat | |||
noptslots = noptslots - 1 | |||
tmp = opts[argc] | |||
if tmp == nil then break end | |||
tmp = tmp:match'^%s*(.-)%s*$' | |||
if optslots[1] ~= nil and trim_parse_opts[tmp] ~= nil then | |||
tmp = trim_parse_opts[tmp] | |||
trimn = tmp[1] | |||
trimu = tmp[2] | |||
optslots[1] = nil | |||
elseif optslots[2] ~= nil and isep_parse_opts[tmp] ~= nil then | |||
argc = argc + 1 | |||
iplain = isep_parse_opts[tmp] | |||
isp = opts[argc] | |||
optslots[2] = nil | |||
elseif optslots[3] ~= nil and psep_parse_opts[tmp] ~= nil then | |||
argc = argc + 1 | |||
pplain = psep_parse_opts[tmp] | |||
psp = opts[argc] | |||
optslots[3] = nil | |||
else break end | |||
argc = argc + 1 | |||
until noptslots < 1 | |||
return isp, iplain, psp, pplain, trimn, trimu, argc | |||
end | end | ||
-- Map parameters' values using a custom callback and a referenced table | -- Map parameters' values using a custom callback and a referenced table | ||
function | local value_maps = { | ||
[0] = function (tbl, margs, karg, varg, fn) | |||
for key in pairs(tbl) do tbl[key] = fn() end | |||
end, | |||
[1] = function (tbl, margs, karg, varg, fn) | |||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
margs[varg] = val | margs[varg] = val | ||
tbl[key] = fn() | tbl[key] = fn() | ||
end | end | ||
end, | |||
for key | [2] = function (tbl, margs, karg, varg, fn) | ||
for key in pairs(tbl) do | |||
margs[karg] = key | margs[karg] = key | ||
tbl[key] = fn() | tbl[key] = fn() | ||
end | end | ||
end, | |||
for key in pairs(tbl) do | [3] = function (tbl, margs, karg, varg, fn) | ||
for key, val in pairs(tbl) do | |||
margs[karg] = key | margs[karg] = key | ||
margs[varg] = val | |||
tbl[key] = fn() | tbl[key] = fn() | ||
end | end | ||
end | end | ||
} | |||
-- | -- Private table for `map_names()` | ||
function | local name_thieves_maps = { | ||
[0] = function (cache, tbl, rargs, karg, varg, fn) | |||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
steal_if_renamed(val, tbl, key, cache, fn()) | steal_if_renamed(val, tbl, key, cache, fn()) | ||
end | end | ||
end, | |||
[1] = function (cache, tbl, rargs, karg, varg, fn) | |||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
rargs[varg] = val | rargs[varg] = val | ||
steal_if_renamed(val, tbl, key, cache, fn()) | steal_if_renamed(val, tbl, key, cache, fn()) | ||
end | end | ||
end, | |||
[2] = function (cache, tbl, rargs, karg, varg, fn) | |||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
rargs[ | rargs[karg] = key | ||
steal_if_renamed(val, tbl, key, cache, fn()) | steal_if_renamed(val, tbl, key, cache, fn()) | ||
end | end | ||
end, | |||
[3] = function (cache, tbl, rargs, karg, varg, fn) | |||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
rargs[karg] = key | |||
rargs[varg] = val | |||
steal_if_renamed(val, tbl, key, cache, fn()) | steal_if_renamed(val, tbl, key, cache, fn()) | ||
end | end | ||
end | end | ||
} | |||
-- Map parameters' names using a custom callback and a referenced table | |||
local function map_names (tbl, rargs, karg, varg, looptype, fn) | |||
local cache = {} | |||
name_thieves_maps[looptype](cache, tbl, rargs, karg, varg, fn) | |||
for key, val in pairs(cache) do tbl[key] = val end | for key, val in pairs(cache) do tbl[key] = val end | ||
end | end | ||
-- | -- Return a new table that contains `src` regrouped according to the numeric | ||
-- keys | -- suffixes in its keys | ||
-- | local function make_groups (src) | ||
local | -- NOTE: `src` might be the original metatable! | ||
local | local tmp | ||
local | local prefix | ||
local gid | |||
local groups = {} | |||
-- | for key, val in pairs(src) do | ||
-- `key` must only be a string or a number... | |||
gid = tonumber(key) | |||
if | if gid == nil then | ||
prefix, gid = key:match'^%s*(.-)%s*(%-?%d*)%s*$' | |||
gid = tonumber(gid) or '' | |||
else prefix = '' end | |||
if groups[gid] == nil then groups[gid] = {} end | |||
tmp = tonumber(prefix) | |||
if tmp ~= nil then | |||
if tmp < 1 then prefix = tmp - 1 else prefix = tmp end | |||
end | |||
groups[gid][prefix] = val | |||
end | |||
return groups | |||
end | |||
-- Populate a table by parsing a parameter string | |||
local function parse_parameter_string (tbl, str, isp, ipl, psp, ppl, trn, tru) | |||
local key | |||
local val | |||
local spos1 | |||
local spos2 | |||
local pos1 | |||
local pos2 | |||
local pos3 = 0 | |||
local idx = 1 | |||
local lenplone = #str + 1 | |||
if isp == nil or isp == '' then | |||
if psp == nil or psp == '' then | |||
if tru then tbl[idx] = str:match'^%s*(.-)%s*$' | |||
else tbl[idx] = str end | |||
return tbl | |||
end | end | ||
spos1, spos2 = str:find(psp, 1, ppl) | |||
if | if spos1 == nil then | ||
key = idx | |||
if tru then val = str:match'^%s*(.-)%s*$' | |||
else val = str end | |||
idx = idx + 1 | |||
else | |||
key = str:sub(1, spos1 - 1) | |||
key = tonumber(key) or key:match'^%s*(.-)%s*$' | |||
val = str:sub(spos2 + 1) | |||
if trn then val = val:match'^%s*(.-)%s*$' end | |||
end | end | ||
tbl[key] = val | |||
return tbl | |||
end | end | ||
if psp == nil or psp == '' then | |||
return | repeat | ||
pos1 = pos3 + 1 | |||
pos2, pos3 = str:find(isp, pos1, ipl) | |||
val = str:sub(pos1, (pos2 or lenplone) - 1) | |||
if tru then val = val:match'^%s*(.-)%s*$' end | |||
tbl[idx] = val | |||
idx = idx + 1 | |||
until pos2 == nil | |||
return tbl | |||
end | |||
repeat | |||
pos1 = pos3 + 1 | |||
pos2, pos3 = str:find(isp, pos1, ipl) | |||
val = str:sub(pos1, (pos2 or lenplone) - 1) | |||
spos1, spos2 = val:find(psp, 1, ppl) | |||
if spos1 == nil then | |||
key = idx | |||
if tru then val = val:match'^%s*(.-)%s*$' end | |||
idx = idx + 1 | |||
else | |||
key = val:sub(1, spos1 - 1) | |||
key = tonumber(key) or key:match'^%s*(.-)%s*$' | |||
val = val:sub(spos2 + 1) | |||
if trn then val = val:match'^%s*(.-)%s*$' end | |||
end | |||
tbl[key] = val | |||
until pos2 == nil | |||
return tbl | |||
end | end | ||
-- Flush the parameters by calling a custom function for each value (after this | -- Concatenate the numeric keys from the table of parameters to the numeric | ||
-- keys from the table of options; non-numeric keys from the table of options | |||
-- will prevail over colliding non-numeric keys from the table of parameters | |||
local function concat_params (ctx) | |||
local tbl = ctx.params | |||
local nmax = table.maxn(ctx.pipe) | |||
local retval = {} | |||
if ctx.subset == 1 then | |||
-- We need only the sequence | |||
for key, val in ipairs(tbl) do retval[key + nmax] = val end | |||
else | |||
if ctx.subset == -1 then | |||
for key in ipairs(tbl) do tbl[key] = nil end | |||
end | |||
for key, val in pairs(tbl) do | |||
if type(key) == 'number' and key > 0 then | |||
retval[key + nmax] = val | |||
else retval[key] = val end | |||
end | |||
end | |||
for key, val in pairs(ctx.pipe) do retval[key] = val end | |||
return retval | |||
end | |||
-- Flush the parameters by calling a custom function for each value (after this | |||
-- function has been invoked `ctx.params` will be no longer usable) | -- function has been invoked `ctx.params` will be no longer usable) | ||
local function flush_params(ctx, fn) | local function flush_params (ctx, fn) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
if ctx.subset == 1 then | if ctx.subset == 1 then | ||
| Line 411: | Line 664: | ||
for key, val in ipairs(tbl) do tbl[key] = nil end | for key, val in ipairs(tbl) do tbl[key] = nil end | ||
end | end | ||
if ctx. | if ctx.sorttype > 0 then | ||
local nums = {} | local nums = {} | ||
local words = {} | local words = {} | ||
local | local nn = 0 | ||
local | local nw = 0 | ||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
if type(key) == 'number' then | if type(key) == 'number' then | ||
nn = nn + 1 | |||
nums[ | nums[nn] = key | ||
else | else | ||
nw = nw + 1 | |||
words[ | words[nw] = key | ||
end | end | ||
end | end | ||
table.sort(nums) | table.sort(nums) | ||
table.sort(words, natural_sort) | table.sort(words, natural_sort) | ||
for idx = 1, | if ctx.sorttype == 2 then | ||
for idx = 1, | for idx = 1, nw do fn(words[idx], tbl[words[idx]]) end | ||
for idx = 1, nn do fn(nums[idx], tbl[nums[idx]]) end | |||
return | |||
end | |||
for idx = 1, nn do fn(nums[idx], tbl[nums[idx]]) end | |||
for idx = 1, nw do fn(words[idx], tbl[words[idx]]) end | |||
return | return | ||
end | end | ||
| Line 447: | Line 705: | ||
-- Syntax: #invoke:params|sequential|pipe to | -- Syntax: #invoke:params|sequential|pipe to | ||
library.sequential = function(ctx) | library.sequential = function (ctx) | ||
if ctx.subset == -1 then error( | if ctx.subset == -1 then error(modulename .. | ||
': The two directives ‘non-sequential’ and ‘sequential’ are in contradiction with each other', 0) end | ': The two directives ‘non-sequential’ and ‘sequential’ are in contradiction with each other', 0) end | ||
if ctx. | if ctx.sorttype > 0 then error(modulename .. | ||
': The ‘all_sorted’ | ': The ‘all_sorted’ and ‘reassorted’ directives are redundant when followed by ‘sequential’', 0) end | ||
ctx.iterfunc = ipairs | ctx.iterfunc = ipairs | ||
ctx.subset = 1 | ctx.subset = 1 | ||
| Line 459: | Line 717: | ||
-- Syntax: #invoke:params|non-sequential|pipe to | -- Syntax: #invoke:params|non-sequential|pipe to | ||
library['non-sequential'] = function(ctx) | library['non-sequential'] = function (ctx) | ||
if ctx.subset == 1 then error( | if ctx.subset == 1 then error(modulename .. | ||
': The two directives ‘sequential’ and ‘non-sequential’ are in contradiction with each other', 0) end | ': The two directives ‘sequential’ and ‘non-sequential’ are in contradiction with each other', 0) end | ||
ctx.iterfunc = pairs | ctx.iterfunc = pairs | ||
| Line 468: | Line 726: | ||
-- Syntax: #invoke:params| | -- Syntax: #invoke:params|all_sorted|pipe to | ||
library.all_sorted = function(ctx) | library.all_sorted = function (ctx) | ||
if ctx.subset == 1 then error( | if ctx.subset == 1 then error(modulename .. | ||
': The ‘all_sorted’ directive is redundant after ‘sequential’', 0) end | ': The ‘all_sorted’ directive is redundant after ‘sequential’', 0) end | ||
ctx. | if ctx.sorttype == 2 then error(modulename .. | ||
': The two directives ‘reassorted’ and ‘sequential’ are in contradiction with each other', 0) end | |||
ctx.sorttype = 1 | |||
return context_iterate(ctx, 1) | |||
end | |||
-- Syntax: #invoke:params|reassorted|pipe to | |||
library.reassorted = function (ctx) | |||
if ctx.subset == 1 then error(modulename .. | |||
': The ‘reassorted’ directive is redundant after ‘sequential’', 0) end | |||
if ctx.sorttype == 1 then error(modulename .. | |||
': The two directives ‘sequential’ and ‘reassorted’ are in contradiction with each other', 0) end | |||
ctx.sorttype = 2 | |||
return context_iterate(ctx, 1) | return context_iterate(ctx, 1) | ||
end | end | ||
| Line 478: | Line 749: | ||
-- Syntax: #invoke:params|setting|directives|...|pipe to | -- Syntax: #invoke:params|setting|directives|...|pipe to | ||
library.setting = function(ctx) | library.setting = function (ctx) | ||
local opts = ctx.pipe | local opts = ctx.pipe | ||
local cmd = opts[1] | local cmd = opts[1] | ||
| Line 484: | Line 755: | ||
cmd = cmd:gsub('%s+', ''):gsub('/+', '/'):match'^/*(.*[^/])' | cmd = cmd:gsub('%s+', ''):gsub('/+', '/'):match'^/*(.*[^/])' | ||
end | end | ||
if cmd == nil then error( | if cmd == nil then error(modulename .. | ||
', ‘setting’: No directive was given', 0) end | ', ‘setting’: No directive was given', 0) end | ||
local sep = string.byte('/') | local sep = string.byte('/') | ||
| Line 501: | Line 772: | ||
else | else | ||
vname = memoryslots[string.char(chr)] | vname = memoryslots[string.char(chr)] | ||
if vname == nil then error( | if vname == nil then error(modulename .. | ||
', ‘setting’: Unknown slot | ', ‘setting’: Unknown slot ‘' .. | ||
string.char(chr) .. ' | string.char(chr) .. '’', 0) end | ||
table.insert(dest, vname) | table.insert(dest, vname) | ||
end | end | ||
| Line 513: | Line 784: | ||
-- Syntax: #invoke:params|squeezing|pipe to | -- Syntax: #invoke:params|squeezing|pipe to | ||
library.squeezing = function(ctx) | library.squeezing = function (ctx) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
local store = {} | local store = {} | ||
| Line 533: | Line 804: | ||
-- Syntax: #invoke:params|filling_the_gaps|pipe to | -- Syntax: #invoke:params|filling_the_gaps|pipe to | ||
library.filling_the_gaps = function(ctx) | library.filling_the_gaps = function (ctx) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
local nmin = 1 | local nmin = 1 | ||
| Line 552: | Line 823: | ||
if nmax ~= nil and nmax - nmin > nnums then | if nmax ~= nil and nmax - nmin > nnums then | ||
ctx.n_available = ctx.n_available + nmin + nnums - nmax | ctx.n_available = ctx.n_available + nmin + nnums - nmax | ||
if ctx.n_available < 0 then error( | if ctx.n_available < 0 then error(modulename .. | ||
', ‘filling_the_gaps’: It is possible to fill at most ' .. | ', ‘filling_the_gaps’: It is possible to fill at most ' .. | ||
tostring(maxfill) .. ' parameters', 0) end | tostring(maxfill) .. ' parameters', 0) end | ||
| Line 563: | Line 834: | ||
-- Syntax: #invoke:params|clearing|pipe to | -- Syntax: #invoke:params|clearing|pipe to | ||
library.clearing = function(ctx) | library.clearing = function (ctx) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
local | local numerics = {} | ||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
if type(key) == 'number' then | if type(key) == 'number' then | ||
numerics[key] = val | |||
tbl[key] = nil | tbl[key] = nil | ||
end | end | ||
end | end | ||
for key, val in ipairs( | for key, val in ipairs(numerics) do tbl[key] = val end | ||
return context_iterate(ctx, 1) | return context_iterate(ctx, 1) | ||
end | end | ||
| Line 578: | Line 849: | ||
-- Syntax: #invoke:params|cutting|left cut|right cut|pipe to | -- Syntax: #invoke:params|cutting|left cut|right cut|pipe to | ||
library.cutting = function(ctx) | library.cutting = function (ctx) | ||
local lcut = tonumber(ctx.pipe[1]) | local lcut = tonumber(ctx.pipe[1]) | ||
if lcut == nil then error( | if lcut == nil then error(modulename .. | ||
', ‘cutting’: Left cut must be a number', 0) end | ', ‘cutting’: Left cut must be a number', 0) end | ||
local rcut = tonumber(ctx.pipe[2]) | local rcut = tonumber(ctx.pipe[2]) | ||
if rcut == nil then error( | if rcut == nil then error(modulename .. | ||
', ‘cutting’: Right cut must be a number', 0) end | ', ‘cutting’: Right cut must be a number', 0) end | ||
local tbl = ctx.params | local tbl = ctx.params | ||
| Line 613: | Line 884: | ||
-- Syntax: #invoke:params|cropping|left crop|right crop|pipe to | -- Syntax: #invoke:params|cropping|left crop|right crop|pipe to | ||
library.cropping = function(ctx) | library.cropping = function (ctx) | ||
local lcut = tonumber(ctx.pipe[1]) | local lcut = tonumber(ctx.pipe[1]) | ||
if lcut == nil then error( | if lcut == nil then error(modulename .. | ||
', ‘cropping’: Left crop must be a number', 0) end | ', ‘cropping’: Left crop must be a number', 0) end | ||
local rcut = tonumber(ctx.pipe[2]) | local rcut = tonumber(ctx.pipe[2]) | ||
if rcut == nil then error( | if rcut == nil then error(modulename .. | ||
', ‘cropping’: Right crop must be a number', 0) end | ', ‘cropping’: Right crop must be a number', 0) end | ||
local tbl = ctx.params | local tbl = ctx.params | ||
| Line 657: | Line 928: | ||
-- Syntax: #invoke:params|purging|start offset|length|pipe to | -- Syntax: #invoke:params|purging|start offset|length|pipe to | ||
library.purging = function(ctx) | library.purging = function (ctx) | ||
local idx = tonumber(ctx.pipe[1]) | local idx = tonumber(ctx.pipe[1]) | ||
if idx == nil then error( | if idx == nil then error(modulename .. | ||
', ‘purging’: Start offset must be a number', 0) end | ', ‘purging’: Start offset must be a number', 0) end | ||
local len = tonumber(ctx.pipe[2]) | local len = tonumber(ctx.pipe[2]) | ||
if len == nil then error( | if len == nil then error(modulename .. | ||
', ‘purging’: Length must be a number', 0) end | ', ‘purging’: Length must be a number', 0) end | ||
local tbl = ctx.params | local tbl = ctx.params | ||
| Line 676: | Line 947: | ||
-- Syntax: #invoke:params|backpurging|start offset|length|pipe to | -- Syntax: #invoke:params|backpurging|start offset|length|pipe to | ||
library.backpurging = function(ctx) | library.backpurging = function (ctx) | ||
local last = tonumber(ctx.pipe[1]) | local last = tonumber(ctx.pipe[1]) | ||
if last == nil then error( | if last == nil then error(modulename .. | ||
', ‘backpurging’: Start offset must be a number', 0) end | ', ‘backpurging’: Start offset must be a number', 0) end | ||
local len = tonumber(ctx.pipe[2]) | local len = tonumber(ctx.pipe[2]) | ||
if len == nil then error( | if len == nil then error(modulename .. | ||
', ‘backpurging’: Length must be a number', 0) end | ', ‘backpurging’: Length must be a number', 0) end | ||
local idx | local idx | ||
| Line 703: | Line 974: | ||
-- Syntax: #invoke:params|rotating|pipe to | -- Syntax: #invoke:params|rotating|pipe to | ||
library.rotating = function(ctx) | library.rotating = function (ctx) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
local | local numerics = {} | ||
local nmax = 0 | local nmax = 0 | ||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
if type(key) == 'number' then | if type(key) == 'number' then | ||
numerics[key] = val | |||
tbl[key] = nil | tbl[key] = nil | ||
if key > nmax then nmax = key end | if key > nmax then nmax = key end | ||
end | end | ||
end | end | ||
for key, val in pairs( | for key, val in pairs(numerics) do tbl[nmax - key + 1] = val end | ||
return context_iterate(ctx, 1) | return context_iterate(ctx, 1) | ||
end | end | ||
| Line 721: | Line 992: | ||
-- Syntax: #invoke:params|pivoting|pipe to | -- Syntax: #invoke:params|pivoting|pipe to | ||
--[[ | --[[ | ||
library.pivoting = function(ctx) | library.pivoting = function (ctx) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
local shift = #tbl + 1 | local shift = #tbl + 1 | ||
if shift < 2 then return library.rotating(ctx) end | if shift < 2 then return library.rotating(ctx) end | ||
local | local numerics = {} | ||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
if type(key) == 'number' then | if type(key) == 'number' then | ||
numerics[key] = val | |||
tbl[key] = nil | tbl[key] = nil | ||
end | end | ||
end | end | ||
for key, val in pairs( | for key, val in pairs(numerics) do tbl[shift - key] = val end | ||
return context_iterate(ctx, 1) | return context_iterate(ctx, 1) | ||
end | end | ||
| Line 740: | Line 1,011: | ||
-- Syntax: #invoke:params|mirroring|pipe to | -- Syntax: #invoke:params|mirroring|pipe to | ||
--[[ | --[[ | ||
library.mirroring = function(ctx) | library.mirroring = function (ctx) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
local | local numerics = {} | ||
local nmax | local nmax | ||
local nmin | local nmin | ||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
if type(key) == 'number' then | if type(key) == 'number' then | ||
numerics[key] = val | |||
tbl[key] = nil | tbl[key] = nil | ||
if nmax == nil then | if nmax == nil then | ||
| Line 756: | Line 1,027: | ||
end | end | ||
end | end | ||
for key, val in pairs( | for key, val in pairs(numerics) do tbl[nmax + nmin - key] = val end | ||
return context_iterate(ctx, 1) | return context_iterate(ctx, 1) | ||
end | end | ||
| Line 764: | Line 1,035: | ||
-- Syntax: #invoke:params|swapping|pipe to | -- Syntax: #invoke:params|swapping|pipe to | ||
--[[ | --[[ | ||
library.swapping = function(ctx) | library.swapping = function (ctx) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
local cache = {} | local cache = {} | ||
| Line 787: | Line 1,058: | ||
-- Syntax: #invoke:params|sorting_sequential_values|[criterion]|pipe to | -- Syntax: #invoke:params|sorting_sequential_values|[criterion]|pipe to | ||
library.sorting_sequential_values = function(ctx) | library.sorting_sequential_values = function (ctx) | ||
local sortfn | local sortfn | ||
if ctx.pipe[1] ~= nil then sortfn = sortfunctions[ctx.pipe[1]] end | if ctx.pipe[1] ~= nil then sortfn = sortfunctions[ctx.pipe[1]] end | ||
| Line 799: | Line 1,070: | ||
-- Syntax: #invoke:params|inserting|position|how many|...|pipe to | -- Syntax: #invoke:params|inserting|position|how many|...|pipe to | ||
--[[ | --[[ | ||
library.inserting = function(ctx) | library.inserting = function (ctx) | ||
-- NOTE: `ctx.params` might be the original metatable! As a modifier, | -- NOTE: `ctx.params` might be the original metatable! As a modifier, | ||
-- this function MUST create a copy of it before returning | -- this function MUST create a copy of it before returning | ||
local idx = tonumber(ctx.pipe[1]) | local idx = tonumber(ctx.pipe[1]) | ||
if idx == nil then error( | if idx == nil then error(modulename .. | ||
', ‘inserting’: Position must be a number', 0) end | ', ‘inserting’: Position must be a number', 0) end | ||
local len = tonumber(ctx.pipe[2]) | local len = tonumber(ctx.pipe[2]) | ||
if len == nil or len < 1 then error( | if len == nil or len < 1 then error(modulename .. | ||
', ‘inserting’: The amount must be a number greater than zero', 0) end | ', ‘inserting’: The amount must be a number greater than zero', 0) end | ||
local opts = ctx.pipe | local opts = ctx.pipe | ||
| Line 818: | Line 1,089: | ||
-- Syntax: #invoke:params|imposing|name|value|pipe to | -- Syntax: #invoke:params|imposing|name|value|pipe to | ||
library.imposing = function(ctx) | library.imposing = function (ctx) | ||
if ctx.pipe[1] == nil then error( | if ctx.pipe[1] == nil then error(modulename .. | ||
', ‘imposing’: Missing parameter name to impose', 0) end | ', ‘imposing’: Missing parameter name to impose', 0) end | ||
local key = ctx.pipe[1]:match'^%s*(.-)%s*$' | local key = ctx.pipe[1]:match'^%s*(.-)%s*$' | ||
| Line 827: | Line 1,098: | ||
-- Syntax: #invoke:params|discarding|name|[how many]|pipe to | -- Syntax: #invoke:params|providing|name|value|pipe to | ||
library.discarding = function(ctx) | library.providing = function (ctx) | ||
if ctx.pipe[1] == nil then error( | if ctx.pipe[1] == nil then error(modulename .. | ||
', ‘providing’: Missing parameter name to provide', 0) end | |||
local key = ctx.pipe[1]:match'^%s*(.-)%s*$' | |||
key = tonumber(key) or key | |||
if ctx.params[key] == nil then ctx.params[key] = ctx.pipe[2] end | |||
return context_iterate(ctx, 3) | |||
end | |||
-- Syntax: #invoke:params|discarding|name|[how many]|pipe to | |||
library.discarding = function (ctx) | |||
if ctx.pipe[1] == nil then error(modulename .. | |||
', ‘discarding’: Missing parameter name to discard', 0) end | ', ‘discarding’: Missing parameter name to discard', 0) end | ||
local key = ctx.pipe[1] | local key = ctx.pipe[1] | ||
| Line 838: | Line 1,120: | ||
end | end | ||
key = tonumber(key) | key = tonumber(key) | ||
if key == nil then error( | if key == nil then error(modulename .. | ||
', ‘discarding’: A range was provided, but the initial parameter name is not | ', ‘discarding’: A range was provided, but the initial parameter name is not numeric', 0) end | ||
if len < 1 then error( | if len < 1 then error(modulename .. | ||
', ‘discarding’: A range can only be a number greater than zero', 0) end | ', ‘discarding’: A range can only be a number greater than zero', 0) end | ||
for idx = key, key + len - 1 do ctx.params[idx] = nil end | for idx = key, key + len - 1 do ctx.params[idx] = nil end | ||
| Line 847: | Line 1,129: | ||
-- Syntax: #invoke:params|with_name_matching| | -- Syntax: #invoke:params|excluding_non-numeric_names|pipe to | ||
-- |[ | library['excluding_non-numeric_names'] = function (ctx) | ||
local tmp = ctx.params | |||
for key, val in pairs(tmp) do | |||
if type(key) ~= 'number' then tmp[key] = nil end | |||
end | |||
return context_iterate(ctx, 1) | |||
end | |||
-- Syntax: #invoke:params|excluding_numeric_names|pipe to | |||
library.excluding_numeric_names = function (ctx) | |||
local tmp = ctx.params | |||
for key, val in pairs(tmp) do | |||
if type(key) == 'number' then tmp[key] = nil end | |||
end | |||
return context_iterate(ctx, 1) | |||
end | |||
-- Syntax: #invoke:params|with_name_matching|target 1|[plain flag 1]|[or] | |||
-- |[target 2]|[plain flag 2]|[or]|[...]|[target N]|[plain flag | |||
-- N]|pipe to | -- N]|pipe to | ||
library.with_name_matching = function(ctx) | library.with_name_matching = function (ctx) | ||
-- NOTE: `ctx.params` might be the original metatable! As a modifier, | |||
-- this function MUST create a copy of it before returning | |||
local targets, nptns, argc = load_pattern_args(ctx.pipe, targets, | |||
'with_name_matching') | |||
local tmp | |||
local ptn | |||
local tbl = ctx.params | local tbl = ctx.params | ||
local | local newparams = {} | ||
for idx = 1, nptns do | |||
ptn = targets[idx] | |||
if ptn[3] then | |||
tmp = tonumber(ptn[1]) or ptn[1] | |||
for | newparams[tmp] = tbl[tmp] | ||
else | |||
if | for key, val in pairs(tbl) do | ||
if tostring(key):find(ptn[1], 1, ptn[2]) then | |||
newparams[key] = val | |||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
ctx.params = newparams | |||
return context_iterate(ctx, argc) | return context_iterate(ctx, argc) | ||
end | end | ||
-- Syntax: #invoke:params|with_name_not_matching| | -- Syntax: #invoke:params|with_name_not_matching|target 1|[plain flag 1] | ||
-- |[and]|[ | -- |[and]|[target 2]|[plain flag 2]|[and]|[...]|[target N]|[plain | ||
-- flag N]|pipe to | -- flag N]|pipe to | ||
library.with_name_not_matching = function(ctx) | library.with_name_not_matching = function (ctx) | ||
local targets, nptns, argc = load_pattern_args(ctx.pipe, targets, | |||
'with_name_not_matching') | |||
local tbl = ctx.params | local tbl = ctx.params | ||
local | if nptns == 1 and targets[1][3] then | ||
local tmp = targets[1][1] | |||
tbl[tonumber(tmp) or tmp] = nil | |||
return context_iterate(ctx, argc) | |||
end | |||
local yesmatch | local yesmatch | ||
local ptn | |||
for key in pairs(tbl) do | for key in pairs(tbl) do | ||
yesmatch = true | yesmatch = true | ||
for | for idx = 1, nptns do | ||
ptn = targets[idx] | |||
if ptn[3] then | if ptn[3] then | ||
if key ~= ptn[1] then | if tostring(key) ~= ptn[1] then | ||
yesmatch = false | yesmatch = false | ||
break | break | ||
end | end | ||
elseif not | elseif not tostring(key):find(ptn[1], 1, ptn[2]) then | ||
yesmatch = false | yesmatch = false | ||
break | break | ||
| Line 902: | Line 1,213: | ||
-- Syntax: #invoke:params|with_value_matching| | -- Syntax: #invoke:params|with_value_matching|target 1|[plain flag 1]|[or] | ||
-- |[ | -- |[target 2]|[plain flag 2]|[or]|[...]|[target N]|[plain flag | ||
-- N]|pipe to | -- N]|pipe to | ||
library.with_value_matching = function(ctx) | library.with_value_matching = function (ctx) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
local | local targets, nptns, argc = load_pattern_args(ctx.pipe, targets, | ||
'with_value_matching') | |||
local nomatch | local nomatch | ||
local ptn | |||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
nomatch = true | nomatch = true | ||
for | for idx = 1, nptns do | ||
ptn = targets[idx] | |||
if ptn[3] then | if ptn[3] then | ||
if val == ptn[1] then | if val == ptn[1] then | ||
| Line 918: | Line 1,231: | ||
break | break | ||
end | end | ||
elseif | elseif val:find(ptn[1], 1, ptn[2]) then | ||
nomatch = false | nomatch = false | ||
break | break | ||
| Line 929: | Line 1,242: | ||
-- Syntax: #invoke:params|with_value_not_matching| | -- Syntax: #invoke:params|with_value_not_matching|target 1|[plain flag 1] | ||
-- |[and]|[ | -- |[and]|[target 2]|[plain flag 2]|[and]|[...]|[target N]|[plain | ||
-- flag N]|pipe to | -- flag N]|pipe to | ||
library.with_value_not_matching = function(ctx) | library.with_value_not_matching = function (ctx) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
local | local targets, nptns, argc = load_pattern_args(ctx.pipe, targets, | ||
'with_value_not_matching') | 'with_value_not_matching') | ||
local yesmatch | local yesmatch | ||
local ptn | |||
for key, val in pairs(tbl) do | for key, val in pairs(tbl) do | ||
yesmatch = true | yesmatch = true | ||
for | for idx = 1, nptns do | ||
ptn = targets[idx] | |||
if ptn[3] then | if ptn[3] then | ||
if val ~= ptn[1] then | if val ~= ptn[1] then | ||
| Line 946: | Line 1,260: | ||
break | break | ||
end | end | ||
elseif not | elseif not val:find(ptn[1], 1, ptn[2]) then | ||
yesmatch = false | yesmatch = false | ||
break | break | ||
| Line 958: | Line 1,272: | ||
-- Syntax: #invoke:params|trimming_values|pipe to | -- Syntax: #invoke:params|trimming_values|pipe to | ||
library.trimming_values = function(ctx) | library.trimming_values = function (ctx) | ||
local tbl = ctx.params | local tbl = ctx.params | ||
for key, val in pairs(tbl) do tbl[key] = val:match'^%s*(.-)%s*$' end | for key, val in pairs(tbl) do tbl[key] = val:match'^%s*(.-)%s*$' end | ||
| Line 968: | Line 1,282: | ||
-- style]|[let]|[...][number of additional parameters]|[parameter | -- style]|[let]|[...][number of additional parameters]|[parameter | ||
-- 1]|[parameter 2]|[...]|[parameter N]|pipe to | -- 1]|[parameter 2]|[...]|[parameter N]|pipe to | ||
library.mapping_by_calling = function(ctx) | 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( | if tname == nil then error(modulename .. | ||
', ‘mapping_by_calling’: No template name was provided', 0) end | ', ‘mapping_by_calling’: No template name was provided', 0) end | ||
local margs | local margs, argc, looptype, karg, varg = load_callback_opts(opts, 1, | ||
mapping_styles.values_only) | mapping_styles.values_only) | ||
local model = { title = tname, args = margs } | local model = { title = tname, args = margs } | ||
value_maps[looptype](ctx.params, margs, karg, varg, function () | |||
return ctx.frame:expandTemplate(model) | return ctx.frame:expandTemplate(model) | ||
end) | end) | ||
| Line 988: | Line 1,301: | ||
-- name|[call style]|[let]|[...]|[number of additional | -- name|[call style]|[let]|[...]|[number of additional | ||
-- arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to | -- arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to | ||
library.mapping_by_invoking = function(ctx) | 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( | if mname == nil then error(modulename .. | ||
', ‘mapping_by_invoking’: No module name was provided', 0) end | ', ‘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( | if fname == nil then error(modulename .. | ||
', ‘mapping_by_invoking’: No function name was provided', 0) end | ', ‘mapping_by_invoking’: No function name was provided', 0) end | ||
local margs | local margs, argc, looptype, karg, varg = load_callback_opts(opts, 2, | ||
mapping_styles.values_only) | mapping_styles.values_only) | ||
local model = { title = 'Module:' .. mname, args = margs } | local model = { title = 'Module:' .. mname, args = margs } | ||
local mfunc = require(model.title)[fname] | local mfunc = require(model.title)[fname] | ||
if mfunc == nil then error( | if mfunc == nil then error(modulename .. | ||
', ‘mapping_by_invoking’: The function ‘' .. fname .. | ', ‘mapping_by_invoking’: The function ‘' .. fname .. | ||
'’ does not exist', 0) end | '’ does not exist', 0) end | ||
value_maps[looptype](ctx.params, margs, karg, varg, function () | |||
return mfunc(ctx.frame:newChild(model)) | return tostring(mfunc(ctx.frame:newChild(model))) | ||
end) | end) | ||
return context_iterate(ctx, argc) | return context_iterate(ctx, argc) | ||
| Line 1,016: | Line 1,328: | ||
-- style]|[let]|[...][number of additional arguments]|[argument | -- style]|[let]|[...][number of additional arguments]|[argument | ||
-- 1]|[argument 2]|[...]|[argument N]|pipe to | -- 1]|[argument 2]|[...]|[argument N]|pipe to | ||
library.mapping_by_magic = function(ctx) | library.mapping_by_magic = function (ctx) | ||
local opts = ctx.pipe | local opts = ctx.pipe | ||
local magic | local magic | ||
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end | if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end | ||
if magic == nil then error( | if magic == nil then error(modulename .. | ||
', ‘mapping_by_magic’: No parser function was provided', 0) end | ', ‘mapping_by_magic’: No parser function was provided', 0) end | ||
local margs | local margs, argc, looptype, karg, varg = load_callback_opts(opts, 1, | ||
mapping_styles.values_only) | mapping_styles.values_only) | ||
value_maps[looptype](ctx.params, margs, karg, varg, function () | |||
return ctx.frame:callParserFunction(magic, margs) | return ctx.frame:callParserFunction(magic, margs) | ||
end) | end) | ||
return context_iterate(ctx, argc) | |||
end | |||
-- Syntax: #invoke:params|mapping_by_replacing|target|replace|[count]|[plain | |||
-- flag]|pipe to | |||
library.mapping_by_replacing = function (ctx) | |||
local ptn, repl, nmax, is_strict, argc, die = | |||
load_replace_args(ctx.pipe, 'mapping_by_replacing') | |||
if die then return context_iterate(ctx, argc) end | |||
local tbl = ctx.params | |||
if is_strict then | |||
for key, val in pairs(tbl) do | |||
if val == ptn then tbl[key] = repl end | |||
end | |||
else | |||
if flg == 2 then | |||
-- Copied from Module:String's `str._escapePattern()` | |||
ptn = ptn:gsub('[%(%)%.%%%+%-%*%?%[%^%$%]]', '%%%0') | |||
end | |||
for key, val in pairs(tbl) do | |||
tbl[key] = val:gsub(ptn, repl, nmax) | |||
end | |||
end | |||
return context_iterate(ctx, argc) | return context_iterate(ctx, argc) | ||
end | end | ||
| Line 1,035: | Line 1,370: | ||
-- style]|[let]|[...][number of additional parameters]|[parameter | -- style]|[let]|[...][number of additional parameters]|[parameter | ||
-- 1]|[parameter 2]|[...]|[parameter N]|pipe to | -- 1]|[parameter 2]|[...]|[parameter N]|pipe to | ||
library.renaming_by_calling = function(ctx) | library.renaming_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( | if tname == nil then error(modulename .. | ||
', ‘renaming_by_calling’: No template name was provided', 0) end | ', ‘renaming_by_calling’: No template name was provided', 0) end | ||
local rargs | local rargs, argc, looptype, karg, varg = load_callback_opts(opts, 1, | ||
mapping_styles.names_only) | mapping_styles.names_only) | ||
local model = { title = tname, args = rargs } | local model = { title = tname, args = rargs } | ||
map_names(ctx.params, rargs, karg, varg, looptype, function() | map_names(ctx.params, rargs, karg, varg, looptype, function () | ||
return ctx.frame:expandTemplate(model) | return ctx.frame:expandTemplate(model) | ||
end) | end) | ||
| Line 1,055: | Line 1,389: | ||
-- name|[call style]|[let]|[...]|[number of additional | -- name|[call style]|[let]|[...]|[number of additional | ||
-- arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to | -- arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to | ||
library.renaming_by_invoking = function(ctx) | library.renaming_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( | if mname == nil then error(modulename .. | ||
', ‘renaming_by_invoking’: No module name was provided', 0) end | ', ‘renaming_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( | if fname == nil then error(modulename .. | ||
', ‘renaming_by_invoking’: No function name was provided', 0) end | ', ‘renaming_by_invoking’: No function name was provided', 0) end | ||
local rargs | local rargs, argc, looptype, karg, varg = load_callback_opts(opts, 2, | ||
mapping_styles.names_only) | mapping_styles.names_only) | ||
local model = { title = 'Module:' .. mname, args = rargs } | local model = { title = 'Module:' .. mname, args = rargs } | ||
local mfunc = require(model.title)[fname] | local mfunc = require(model.title)[fname] | ||
if mfunc == nil then error( | if mfunc == nil then error(modulename .. | ||
', ‘renaming_by_invoking’: The function ‘' .. fname .. | ', ‘renaming_by_invoking’: The function ‘' .. fname .. | ||
'’ does not exist', 0) end | '’ does not exist', 0) end | ||
map_names(ctx.params, rargs, karg, varg, looptype, function() | map_names(ctx.params, rargs, karg, varg, looptype, function () | ||
local tmp = mfunc(ctx.frame:newChild(model)) | |||
return tonumber(tmp) or tostring(tmp) | |||
end) | end) | ||
return context_iterate(ctx, argc) | return context_iterate(ctx, argc) | ||
| Line 1,083: | Line 1,417: | ||
-- style]|[let]|[...][number of additional arguments]|[argument | -- style]|[let]|[...][number of additional arguments]|[argument | ||
-- 1]|[argument 2]|[...]|[argument N]|pipe to | -- 1]|[argument 2]|[...]|[argument N]|pipe to | ||
library.renaming_by_magic = function(ctx) | library.renaming_by_magic = function (ctx) | ||
local opts = ctx.pipe | local opts = ctx.pipe | ||
local magic | local magic | ||
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end | if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end | ||
if magic == nil then error( | if magic == nil then error(modulename .. | ||
', ‘renaming_by_magic’: No parser function was provided', 0) end | ', ‘renaming_by_magic’: No parser function was provided', 0) end | ||
local rargs | local rargs, argc, looptype, karg, varg = load_callback_opts(opts, 1, | ||
mapping_styles.names_only) | mapping_styles.names_only) | ||
map_names(ctx.params, rargs, karg, varg, looptype, function() | map_names(ctx.params, rargs, karg, varg, looptype, function () | ||
return ctx.frame:callParserFunction(magic, rargs) | return ctx.frame:callParserFunction(magic, rargs) | ||
end) | end) | ||
| Line 1,099: | Line 1,432: | ||
-- Syntax: #invoke:params|renaming_by_replacing|target|replace|[count]|[plain | |||
-- flag]|pipe to | |||
library.renaming_by_replacing = function (ctx) | |||
local ptn, repl, nmax, is_strict, argc, die = | |||
load_replace_args(ctx.pipe, 'renaming_by_replacing') | |||
if die then return context_iterate(ctx, argc) end | |||
local tbl = ctx.params | |||
if is_strict then | |||
local key = tonumber(ptn) or ptn:match'^%s*(.-)%s*$' | |||
local val = tbl[key] | |||
tbl[key] = nil | |||
tbl[tonumber(repl) or repl:match'^%s*(.-)%s*$'] = val | |||
else | |||
if flg == 2 then | |||
-- Copied from Module:String's `str._escapePattern()` | |||
ptn = ptn:gsub('[%(%)%.%%%+%-%*%?%[%^%$%]]', '%%%0') | |||
end | |||
local cache = {} | |||
for key, val in pairs(tbl) do | |||
steal_if_renamed(val, tbl, key, cache, | |||
tostring(key):gsub(ptn, repl, nmax)) | |||
end | |||
for key, val in pairs(cache) do tbl[key] = val end | |||
end | |||
return context_iterate(ctx, argc) | |||
end | |||
-- Syntax: #invoke:params|grouping_by_calling|template | |||
-- name|[let]|[...]|[number of additional arguments]|[argument | |||
-- 1]|[argument 2]|[...]|[argument N]|pipe to | |||
library.grouping_by_calling = function (ctx) | |||
-- NOTE: `ctx.params` might be the original metatable! As a modifier, | |||
-- this function MUST create a copy of it before returning | |||
local opts = ctx.pipe | |||
local tmp | |||
if opts[1] ~= nil then tmp = opts[1]:match'^%s*(.*%S)' end | |||
if tmp == nil then error(modulename .. | |||
', ‘grouping_by_calling’: No template name was provided', 0) end | |||
local model = { title = tmp } | |||
local tmp, argc = load_child_opts(opts, 2, 0) | |||
local gargs = {} | |||
for key, val in pairs(tmp) do | |||
if type(key) == 'number' and key < 1 then gargs[key - 1] = val | |||
else gargs[key] = val end | |||
end | |||
local groups = make_groups(ctx.params) | |||
for gid, group in pairs(groups) do | |||
for key, val in pairs(gargs) do group[key] = val end | |||
group[0] = gid | |||
model.args = group | |||
groups[gid] = ctx.frame:expandTemplate(model) | |||
end | |||
ctx.params = groups | |||
return context_iterate(ctx, argc) | |||
end | |||
-- Syntax: #invoke:params|parsing|string to parse|[trim flag]|[iteration | |||
-- delimiter setter]|[...]|[key-value delimiter setter]|[...]|pipe to | |||
library.parsing = function (ctx) | |||
local opts = ctx.pipe | |||
if opts[1] == nil then error(modulename .. | |||
', ‘parsing’: No string to parse was provided', 0) end | |||
local isep, iplain, psep, pplain, trimnamed, trimunnamed, argc = | |||
load_parse_opts(opts, 2) | |||
parse_parameter_string(ctx.params, opts[1], isep, iplain, psep, pplain, | |||
trimnamed, trimunnamed) | |||
return context_iterate(ctx, argc) | |||
end | |||
-- Syntax: #invoke:params|reinterpreting|parameter to reinterpret|[trim | |||
-- flag]|[iteration delimiter setter]|[...]|[key-value delimiter | |||
-- setter]|[...]|pipe to | |||
library.reinterpreting = function (ctx) | |||
local opts = ctx.pipe | |||
if opts[1] == nil then error(modulename .. | |||
', ‘reinterpreting’: No parameter to reinterpret was provided', 0) end | |||
local isep, iplain, psep, pplain, trimnamed, trimunnamed, argc = | |||
load_parse_opts(opts, 2) | |||
local tbl = ctx.params | |||
local tmp = tonumber(opts[1]) or opts[1]:match'^%s*(.-)%s*$' | |||
local str = tbl[tmp] | |||
if str ~= nil then | |||
tbl[tmp] = nil | |||
parse_parameter_string(tbl, str, isep, iplain, psep, pplain, | |||
trimnamed, trimunnamed) | |||
end | |||
return context_iterate(ctx, argc) | |||
end | |||
-- Syntax: #invoke:params|combining_by_calling|template name|new parameter | |||
-- name|pipe to | |||
library.combining_by_calling = function (ctx) | |||
-- NOTE: `ctx.params` might be the original metatable! As a modifier, | |||
-- this function MUST create a copy of it before returning | |||
local tname = ctx.pipe[1] | |||
if tname ~= nil then tname = tname:match'^%s*(.*%S)' | |||
else error(modulename .. | |||
', ‘combining_by_calling’: No template name was provided', 0) end | |||
local merge_into = ctx.pipe[2] | |||
if merge_into == nil then error(modulename .. | |||
', ‘combining_by_calling’: No parameter name was provided', 0) end | |||
merge_into = tonumber(merge_into) or merge_into:match'^%s*(.-)%s*$' | |||
ctx.params = { | |||
[merge_into] = ctx.frame:expandTemplate{ | |||
title = tname, | |||
args = ctx.params | |||
} | |||
} | |||
return context_iterate(ctx, 3) | |||
end | |||
-- Syntax: #invoke:params|snapshotting|pipe to | |||
library.snapshotting = function (ctx) | |||
push_cloned_stack(ctx, ctx.params) | |||
return context_iterate(ctx, 1) | |||
end | |||
-- Syntax: #invoke:params|remembering|pipe to | |||
library.remembering = function (ctx) | |||
push_cloned_stack(ctx, ctx.oparams) | |||
return context_iterate(ctx, 1) | |||
end | |||
-- Syntax: #invoke:params|entering_substack|[new]|pipe to | |||
library.entering_substack = function (ctx) | |||
local tbl = ctx.params | |||
local ncurrparent = ctx.n_parents + 1 | |||
if ctx.parents == nil then ctx.parents = { tbl } | |||
else ctx.parents[ncurrparent] = tbl end | |||
ctx.n_parents = ncurrparent | |||
if ctx.pipe[1] ~= nil and ctx.pipe[1]:match'^%s*new%s*$' then | |||
ctx.params = {} | |||
return context_iterate(ctx, 2) | |||
end | |||
local currsnap = ctx.n_children | |||
if currsnap > 0 then | |||
ctx.params = ctx.children[currsnap] | |||
ctx.children[currsnap] = nil | |||
ctx.n_children = currsnap - 1 | |||
else | |||
local newparams = {} | |||
for key, val in pairs(tbl) do newparams[key] = val end | |||
ctx.params = newparams | |||
end | |||
return context_iterate(ctx, 1) | |||
end | |||
-- Syntax: #invoke:params|pulling|parameter name|pipe to | |||
library.pulling = function (ctx) | |||
local opts = ctx.pipe | |||
if opts[1] == nil then error(modulename .. | |||
', ‘pulling’: No parameter to pull was provided', 0) end | |||
local parent | |||
local tmp = ctx.n_parents | |||
if tmp < 1 then parent = ctx.oparams else parent = ctx.parents[tmp] end | |||
tmp = tonumber(opts[1]) or opts[1]:match'^%s*(.-)%s*$' | |||
if parent[tmp] ~= nil then ctx.params[tmp] = parent[tmp] end | |||
return context_iterate(ctx, 2) | |||
end | |||
-- Syntax: #invoke:params|detaching_substack|pipe to | |||
library.detaching_substack = function (ctx) | |||
local ncurrparent = ctx.n_parents | |||
if ncurrparent < 1 then error(modulename .. | |||
', ‘detaching_substack’: No substack has been created', 0) end | |||
local parent = ctx.parents[ncurrparent] | |||
for key in pairs(ctx.params) do parent[key] = nil end | |||
return context_iterate(ctx, 1) | |||
end | |||
-- Syntax: #invoke:params|leaving_substack|pipe to | |||
library.leaving_substack = function (ctx) | |||
local ncurrparent = ctx.n_parents | |||
if ncurrparent < 1 then error(modulename .. | |||
', ‘leaving_substack’: No substack has been created', 0) end | |||
local currsnap = ctx.n_children + 1 | |||
if ctx.children == nil then ctx.children = { ctx.params } | |||
else ctx.children[currsnap] = ctx.params end | |||
ctx.params = ctx.parents[ncurrparent] | |||
ctx.parents[ncurrparent] = nil | |||
ctx.n_parents = ncurrparent - 1 | |||
ctx.n_children = currsnap | |||
return context_iterate(ctx, 1) | |||
end | |||
-- Syntax: #invoke:params|merging_substack|pipe to | |||
library.merging_substack = function (ctx) | |||
local ncurrparent = ctx.n_parents | |||
if ncurrparent < 1 then error(modulename .. | |||
', ‘merging_substack’: No substack has been created', 0) end | |||
local parent = ctx.parents[ncurrparent] | |||
local child = ctx.params | |||
ctx.params = parent | |||
ctx.parents[ncurrparent] = nil | |||
ctx.n_parents = ncurrparent - 1 | |||
for key, val in pairs(child) do parent[key] = val end | |||
return context_iterate(ctx, 1) | |||
end | |||
-- Syntax: #invoke:params|flushing|pipe to | |||
library.flushing = function (ctx) | |||
if ctx.n_children < 1 then error(modulename .. | |||
', ‘flushing’: There are no substacks to flush', 0) end | |||
local parent = ctx.params | |||
local currsnap = ctx.n_children | |||
for key, val in pairs(ctx.children[currsnap]) do parent[key] = val end | |||
ctx.children[currsnap] = nil | |||
ctx.n_children = currsnap - 1 | |||
return context_iterate(ctx, 1) | |||
end | |||
--[[ Functions ]]-- | |||
----------------------------- | |||
-- Syntax: #invoke:params|count | |||
library.count = function (ctx) | |||
-- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables! | |||
local retval = 0 | |||
for _ in ctx.iterfunc(ctx.params) do retval = retval + 1 end | |||
if ctx.subset == -1 then retval = retval - #ctx.params end | |||
ctx.text = retval | |||
return false | |||
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]|[...] | |||
library.concat_and_call = function (ctx) | |||
-- NOTE: `ctx.params` might be the original metatable! | |||
local opts = ctx.pipe | |||
local tname | |||
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end | |||
if tname == nil then error(modulename .. | |||
', ‘concat_and_call’: No template name was provided', 0) end | |||
remove_numeric_keys(opts, 1, 1) | |||
ctx.text = ctx.frame:expandTemplate{ | |||
title = tname, | |||
args = concat_params(ctx) | |||
} | |||
return false | |||
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]|[...] | |||
library.concat_and_invoke = function (ctx) | |||
-- NOTE: `ctx.params` might be the original metatable! | |||
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(modulename .. | |||
', ‘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(modulename .. | |||
', ‘concat_and_invoke’: No function name was provided', 0) end | |||
remove_numeric_keys(opts, 1, 2) | |||
local mfunc = require('Module:' .. mname)[fname] | |||
if mfunc == nil then error(modulename .. | |||
', ‘concat_and_invoke’: The function ‘' .. fname .. | |||
'’ does not exist', 0) end | |||
ctx.text = mfunc(ctx.frame:newChild{ | |||
title = 'Module:' .. fname, | |||
args = concat_params(ctx) | |||
}) | |||
return false | |||
end | |||
-- Syntax: #invoke:args|concat_and_magic|parser function|[prepend 1]|[prepend | |||
-- Syntax: #invoke: | -- 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named item n= | ||
library. | -- value n]|[...] | ||
-- NOTE: | library.concat_and_magic = function (ctx) | ||
local | -- NOTE: `ctx.params` might be the original metatable! | ||
local opts = ctx.pipe | |||
if | local magic | ||
ctx.text = | if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end | ||
if magic == nil then error(modulename .. | |||
', ‘concat_and_magic’: No parser function was provided', 0) end | |||
remove_numeric_keys(opts, 1, 1) | |||
ctx.text = ctx.frame:callParserFunction(magic, concat_params(ctx)) | |||
return false | return false | ||
end | end | ||
-- Syntax: #invoke: | -- Syntax: #invoke:params|value_of|parameter name | ||
library.value_of = function (ctx) | |||
-- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables! | |||
library. | |||
-- NOTE: `ctx.params` might be the original | |||
local opts = ctx.pipe | local opts = ctx.pipe | ||
local | local kstr | ||
if opts[1] ~= nil then | if opts[1] ~= nil then kstr = opts[1]:match'^%s*(.*%S)' end | ||
if | if kstr == nil then error(modulename .. | ||
', | ', ‘value_of’: No parameter name was provided', 0) end | ||
local knum = tonumber(kstr) | |||
ctx. | local len = #ctx.params -- No worries: unused when in first position | ||
local val = ctx.params[knum or kstr] | |||
if val ~= nil and ( | |||
ctx.subset ~= -1 or knum == nil or knum > len or knum < 1 | |||
) and ( | |||
ctx.subset ~= 1 or (knum ~= nil and knum <= len and knum > 0) | |||
) then | |||
ctx.text = (ctx.header or '') .. val .. (ctx.footer or '') | |||
return false | |||
end | |||
ctx.text = ctx.ifngiven or '' | |||
return false | return false | ||
end | end | ||
-- Syntax: #invoke: | -- Syntax: #invoke:params|list | ||
library.list = function (ctx) | |||
-- NOTE: `ctx.pipe` might be the original metatable! | |||
library. | local kvs = ctx.pairsep or '' | ||
-- NOTE: `ctx. | local pps = ctx.itersep or '' | ||
local | local ret = {} | ||
local | local nss = 0 | ||
local | flush_params( | ||
ctx, | |||
function (key, val) | |||
ret[nss + 1] = pps | |||
ret[nss + 2] = key | |||
if | ret[nss + 3] = kvs | ||
ret[nss + 4] = val | |||
nss = nss + 4 | |||
end | |||
) | |||
if nss > 0 then | |||
if nss > 4 and ctx.lastsep ~= nil then | |||
ctx.text = | ret[nss - 3] = ctx.lastsep | ||
end | |||
ret[1] = ctx.header or '' | |||
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end | |||
ctx.text = table.concat(ret) | |||
return false | |||
end | |||
ctx.text = ctx.ifngiven or '' | |||
return false | return false | ||
end | end | ||
-- Syntax: #invoke: | -- Syntax: #invoke:params|list_values | ||
library.list_values = function (ctx) | |||
-- NOTE: `ctx.pipe` might be the original metatable! | |||
library. | -- NOTE: `library.coins()` and `library.unique_coins()` rely on us | ||
-- NOTE: `ctx. | local pps = ctx.itersep or '' | ||
local | local ret = {} | ||
local | local nss = 0 | ||
flush_params( | |||
ctx, | |||
function (key, val) | |||
ret[nss + 1] = pps | |||
ctx.text = ctx. | 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 | |||
ctx.text = table.concat(ret) | |||
return false | |||
end | |||
ctx.text = ctx.ifngiven or '' | |||
return false | return false | ||
end | end | ||
-- Syntax: #invoke:params| | -- Syntax: #invoke:params|coins|[first coin = value 1]|[second coin = value | ||
library. | -- 2]|[...]|[last coin = value N] | ||
-- NOTE: `ctx.pipe | library.coins = function (ctx) | ||
-- NOTE: `ctx.pipe` might be the original metatable! | |||
local opts = ctx.pipe | local opts = ctx.pipe | ||
local | local tbl = ctx.params | ||
for key, val in pairs(tbl) do tbl[key] = opts[tonumber(val) or val] end | |||
return library.list_values(ctx) | |||
end | |||
-- Syntax: #invoke:params|unique_coins|[first coin = value 1]|[second coin = | |||
-- value 2]|[...]|[last coin = value N] | |||
library.unique_coins = function (ctx) | |||
local opts = ctx.pipe | |||
local tbl = ctx.params | |||
local tmp | |||
for key, val in pairs(tbl) do | |||
tmp = tonumber(val) or val | |||
tbl[key] = opts[tmp] | |||
opts[tmp] = nil | |||
end | |||
-- Syntax: #invoke:params| | |||
library. | |||
local | |||
local | |||
end | end | ||
return library.list_values(ctx) | |||
end | end | ||
-- Syntax: #invoke:params|for_each|wikitext | -- Syntax: #invoke:params|for_each|wikitext | ||
library.for_each = function(ctx) | library.for_each = function (ctx) | ||
-- NOTE: `ctx.pipe` might be the original metatable! | -- NOTE: `ctx.pipe` might be the original metatable! | ||
local txt = ctx.pipe[1] or '' | local txt = ctx.pipe[1] or '' | ||
| Line 1,269: | Line 1,853: | ||
flush_params( | flush_params( | ||
ctx, | ctx, | ||
function(key, val) | function (key, val) | ||
ret[nss + 1] = pps | ret[nss + 1] = pps | ||
ret[nss + 2] = txt:gsub('%$#', key):gsub('%$@', val) | ret[nss + 2] = txt:gsub('%$#', key):gsub('%$@', val) | ||
| Line 1,292: | Line 1,876: | ||
-- |[...]|[append n]|[named param 1=value 1]|[...]|[named param | -- |[...]|[append n]|[named param 1=value 1]|[...]|[named param | ||
-- n=value n]|[...] | -- n=value n]|[...] | ||
library.call_for_each = function(ctx) | library.call_for_each = 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( | if tname == nil then error(modulename .. | ||
', ‘call_for_each’: No template name was provided', 0) end | ', ‘call_for_each’: No template name was provided', 0) end | ||
local model = { title = tname, args = opts } | local model = { title = tname, args = opts } | ||
| Line 1,305: | Line 1,889: | ||
flush_params( | flush_params( | ||
ctx, | ctx, | ||
function(key, val) | function (key, val) | ||
opts[1] = key | opts[1] = key | ||
opts[2] = val | opts[2] = val | ||
| Line 1,330: | Line 1,914: | ||
-- 1]|[append 2]|[...]|[append n]|[named param 1=value 1]|[...] | -- 1]|[append 2]|[...]|[append n]|[named param 1=value 1]|[...] | ||
-- |[named param n=value n]|[...] | -- |[named param n=value n]|[...] | ||
library.invoke_for_each = function(ctx) | library.invoke_for_each = 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( | if mname == nil then error(modulename .. | ||
', ‘invoke_for_each’: No module name was provided', 0) end | ', ‘invoke_for_each’: 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( | if fname == nil then error(modulename .. | ||
', ‘invoke_for_each’: No function name was provided', 0) end | ', ‘invoke_for_each’: No function name was provided', 0) end | ||
local model = { title = 'Module:' .. mname, args = opts } | local model = { title = 'Module:' .. mname, args = opts } | ||
| Line 1,347: | Line 1,931: | ||
flush_params( | flush_params( | ||
ctx, | ctx, | ||
function(key, val) | function (key, val) | ||
opts[1] = key | opts[1] = key | ||
opts[2] = val | opts[2] = val | ||
| Line 1,372: | Line 1,956: | ||
-- |[...]|[append n]|[named param 1=value 1]|[...]|[named param | -- |[...]|[append n]|[named param 1=value 1]|[...]|[named param | ||
-- n=value n]|[...] | -- n=value n]|[...] | ||
library.magic_for_each = function(ctx) | library.magic_for_each = function (ctx) | ||
local opts = ctx.pipe | local opts = ctx.pipe | ||
local magic | local magic | ||
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end | if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end | ||
if magic == nil then error( | if magic == nil then error(modulename .. | ||
', ‘magic_for_each’: No parser function was provided', 0) end | ', ‘magic_for_each’: No parser function was provided', 0) end | ||
local ccs = ctx.itersep or '' | local ccs = ctx.itersep or '' | ||
| Line 1,384: | Line 1,968: | ||
flush_params( | flush_params( | ||
ctx, | ctx, | ||
function(key, val) | function (key, val) | ||
opts[1] = key | opts[1] = key | ||
opts[2] = val | opts[2] = val | ||
| Line 1,410: | Line 1,994: | ||
-- 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param | -- 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param | ||
-- n=value n]|[...] | -- n=value n]|[...] | ||
library.call_for_each_value = function(ctx) | library.call_for_each_value = 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( | if tname == nil then error(modulename .. | ||
', ‘call_for_each_value’: No template name was provided', 0) end | ', ‘call_for_each_value’: No template name was provided', 0) end | ||
local model = { title = tname, args = opts } | local model = { title = tname, args = opts } | ||
| Line 1,422: | Line 2,006: | ||
flush_params( | flush_params( | ||
ctx, | ctx, | ||
function(key, val) | function (key, val) | ||
opts[1] = val | opts[1] = val | ||
ret[nss + 1] = ccs | ret[nss + 1] = ccs | ||
| Line 1,446: | Line 2,030: | ||
-- 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param | -- 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param | ||
-- n=value n]|[...] | -- n=value n]|[...] | ||
library.invoke_for_each_value = function(ctx) | library.invoke_for_each_value = 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( | if mname == nil then error(modulename .. | ||
', ‘invoke_for_each_value’: No module name was provided', 0) end | ', ‘invoke_for_each_value’: 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( | if fname == nil then error(modulename .. | ||
', ‘invoke_for_each_value’: No function name was provided', 0) end | ', ‘invoke_for_each_value’: No function name was provided', 0) end | ||
local model = { title = 'Module:' .. mname, args = opts } | local model = { title = 'Module:' .. mname, args = opts } | ||
| Line 1,461: | Line 2,045: | ||
local ret = {} | local ret = {} | ||
local nss = 0 | local nss = 0 | ||
remove_numeric_keys(opts, 1, 1) | |||
flush_params( | flush_params( | ||
ctx, | ctx, | ||
function(key, val) | function (key, val) | ||
opts[1] = val | opts[1] = val | ||
ret[nss + 1] = ccs | ret[nss + 1] = ccs | ||
| Line 1,488: | Line 2,072: | ||
-- |[append 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named | -- |[append 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named | ||
-- param n=value n]|[...] | -- param n=value n]|[...] | ||
library.magic_for_each_value = function(ctx) | library.magic_for_each_value = function (ctx) | ||
local opts = ctx.pipe | local opts = ctx.pipe | ||
local magic | local magic | ||
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end | if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end | ||
if magic == nil then error( | if magic == nil then error(modulename .. | ||
', ‘magic_for_each_value’: No parser function was provided', 0) end | ', ‘magic_for_each_value’: No parser function was provided', 0) end | ||
local ccs = ctx.itersep or '' | local ccs = ctx.itersep or '' | ||
| Line 1,499: | Line 2,083: | ||
flush_params( | flush_params( | ||
ctx, | ctx, | ||
function(key, val) | function (key, val) | ||
opts[1] = val | opts[1] = val | ||
ret[nss + 1] = ccs | ret[nss + 1] = ccs | ||
| Line 1,524: | Line 2,108: | ||
-- 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param | -- 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param | ||
-- n=value n]|[...] | -- n=value n]|[...] | ||
library.call_for_each_group = function(ctx) | library.call_for_each_group = function (ctx) | ||
-- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables! | -- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables! | ||
local opts = ctx.pipe | local opts = ctx.pipe | ||
local tmp | local tmp | ||
if opts[1] ~= nil then tmp = opts[1]:match'^%s*(.*%S)' end | if opts[1] ~= nil then tmp = opts[1]:match'^%s*(.*%S)' end | ||
if tmp == nil then error( | if tmp == nil then error(modulename .. | ||
', ‘call_for_each_group’: No template name was provided', 0) end | ', ‘call_for_each_group’: No template name was provided', 0) end | ||
local model = { title = tmp } | local model = { title = tmp } | ||
local ccs = ctx.itersep or '' | local ccs = ctx.itersep or '' | ||
local nss = 0 | local nss = 0 | ||
local ret = {} | local ret = {} | ||
opts = {} | opts = {} | ||
| Line 1,544: | Line 2,125: | ||
end | end | ||
ctx.pipe = opts | ctx.pipe = opts | ||
ctx.params = make_groups(ctx.params) | |||
flush_params( | flush_params( | ||
ctx, | ctx, | ||
function(gid, group) | function (gid, group) | ||
for key, val in pairs(opts) do group[key] = val end | for key, val in pairs(opts) do group[key] = val end | ||
group[0] = gid | group[0] = gid | ||
| Line 1,593: | Line 2,164: | ||
-- Syntax: #invoke:params|new|pipe to | -- Syntax: #invoke:params|new|pipe to | ||
static_iface.new = function (frame) | |||
static_iface.new = function(frame) | local ctx = context_new(frame:getParent()) | ||
local ctx = context_new( | |||
ctx.pipe = copy_or_ref_table(frame.args, false) | ctx.pipe = copy_or_ref_table(frame.args, false) | ||
ctx.params = {} | ctx.params = {} | ||
| Line 1,602: | Line 2,171: | ||
return ctx.text | return ctx.text | ||
end | end | ||
| Line 1,608: | Line 2,176: | ||
--[[ First-position-only functions ]]-- | --[[ First-position-only functions ]]-- | ||
--------------------------------------- | --------------------------------------- | ||
-- Syntax: #invoke:params|self | -- Syntax: #invoke:params|self | ||
static_iface.self = function(frame) | static_iface.self = function (frame) | ||
return frame:getParent():getTitle() | return frame:getParent():getTitle() | ||
end | end | ||
| Line 1,622: | Line 2,189: | ||
return setmetatable({}, { | |||
return setmetatable( | __index = function (_, query) | ||
__index = function( | local fname = query:match'^%s*(.*%S)' | ||
if fname == nil then error(modulename .. | |||
local fname = | |||
if fname == nil then error( | |||
': You must specify a function to call', 0) end | ': You must specify a function to call', 0) end | ||
if library[fname] == nil then error( | local func = static_iface[fname] | ||
if func ~= nil then return func end | |||
func = library[fname] | |||
if func == nil then error(modulename .. | |||
': The function ‘' .. fname .. '’ does not exist', 0) end | ': The function ‘' .. fname .. '’ does not exist', 0) end | ||
return function(frame) | return function (frame) | ||
local | local ctx = context_new(frame:getParent()) | ||
ctx.pipe = copy_or_ref_table(frame.args, | |||
refpipe[fname]) | |||
ctx.params = copy_or_ref_table(ctx.oparams, | |||
refparams[fname]) | |||
ctx.pipe = copy_or_ref_table(frame.args, refpipe[fname]) | |||
ctx.params = copy_or_ref_table(ctx. | |||
main_loop(ctx, func) | main_loop(ctx, func) | ||
return ctx.text | return ctx.text | ||