Module:Params: Difference between revisions
From All Skies Encyclopaedia
imported>Grufo m (Comments only, no changes) |
imported>Grufo (The new modifier ‘providing’ has been added to the module) |
||
(14 intermediate revisions by the same user not shown) | |||
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 ( |
return ('%03d%s'):format(#str, str) |
||
end |
end |
||
-- 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( |
return tostring(var1):gsub('%d+', zero_padded) < |
||
tostring(var2):gsub( |
tostring(var2):gsub('%d+', zero_padded) |
||
end |
end |
||
-- 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 33: | Line 33: | ||
-- Remove numerical elements from a table, shifting everything to the left |
-- Remove numerical elements from a table, shifting everything to the left |
||
function remove_numerical_keys(tbl, idx, len) |
local function remove_numerical_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 99: | Line 100: | ||
end |
end |
||
return ret |
return ret |
||
end |
|||
]]-- |
|||
-- Move a key from a table to another, but only if under a different name and |
|||
-- always parsing numerical strings as numbers |
|||
local function steal_if_renamed (val, src, skey, dest, dkey) |
|||
local realkey = tonumber(dkey) or dkey:match'^%s*(.-)%s*$' |
|||
if skey ~= realkey then |
|||
dest[realkey] = val |
|||
src[skey] = nil |
|||
end |
|||
end |
end |
||
Line 110: | Line 123: | ||
local mkeywords = { |
local mkeywords = { |
||
['or'] = 0, |
['or'] = 0, |
||
pattern = 1, |
|||
plain = 2, |
|||
strict = 3 |
|||
} |
} |
||
Line 118: | Line 131: | ||
-- Sort functions (functions and modifiers MUST avoid these names) |
-- Sort functions (functions and modifiers MUST avoid these names) |
||
local sortfunctions = { |
local sortfunctions = { |
||
-- |
--alphabetically = false, -- Simply uncommenting enables the option |
||
naturally = natural_sort |
|||
} |
} |
||
-- Callback styles for the `mapping_*` and `renaming_*` class of modifiers |
|||
-- Functions and modifiers MUST avoid these names too: `let` |
|||
-- (functions and modifiers MUST avoid these names) |
|||
--[[ |
|||
Meanings of the columns: |
|||
col[1] = Loop type (0-3) |
|||
col[2] = Number of module arguments that the style requires (1-3) |
|||
col[3] = Minimum number of sequential parameters passed to the callback |
|||
col[4] = Name of the callback parameter where to place each parameter name |
|||
col[5] = Name of the callback parameter where to place each parameter value |
|||
col[6] = Argument in the modifier's invocation that will override `col[4]` |
|||
col[7] = Argument in the modifier's invocation that will override `col[5]` |
|||
A value of `-1` indicates that no meaningful value is stored (i.e. `nil`) |
|||
--[[ Module's private environment ]]-- |
|||
-------------------------------------- |
|||
]]-- |
|||
local mapping_styles = { |
|||
names_and_values = { 3, 2, 2, 1, 2, -1, -1 }, |
|||
values_and_names = { 3, 2, 2, 2, 1, -1, -1 }, |
|||
values_only = { 1, 2, 1, -1, 1, -1, -1 }, |
|||
names_only = { 2, 2, 1, 1, -1, -1, -1 }, |
|||
names_and_values_as = { 3, 4, 0, -1, -1, 2, 3 }, |
|||
names_only_as = { 2, 3, 0, -1, -1, 2, -1 }, |
|||
values_only_as = { 1, 3, 0, -1, -1, -1, 2 }, |
|||
blindly = { 0, 2, 0, -1, -1, -1, -1 } |
|||
} |
|||
-- Memory slots |
-- Memory slots (functions and modifiers MUST avoid these names) |
||
local memoryslots = { |
local memoryslots = { |
||
i = 'itersep', |
i = 'itersep', |
||
Line 139: | Line 173: | ||
f = 'footer', |
f = 'footer', |
||
n = 'ifngiven' |
n = 'ifngiven' |
||
} |
|||
-- Functions and modifiers MUST avoid these names too: `let` |
|||
--[[ Module's private environment ]]-- |
|||
-------------------------------------- |
|||
-- 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 available their own (modified) copy |
|||
local refpipe = { |
|||
count = true, |
|||
value_of = true, |
|||
list = true, |
|||
list_values = true, |
|||
for_each = true, |
|||
call_for_each_group = true |
|||
} |
|||
-- 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 available their |
|||
-- own (modified) copy |
|||
local refparams = { |
|||
--inserting = true, |
|||
grouping_by_calling = true, |
|||
count = true, |
|||
concat_and_call = true, |
|||
concat_and_invoke = true, |
|||
concat_and_magic = true, |
|||
value_of = true, |
|||
call_for_each_group = true |
|||
} |
} |
||
Line 157: | Line 228: | ||
-- Create a new context |
-- Create a new context |
||
local function context_new() |
local function context_new () |
||
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.sorttype = 0 |
|||
ctx.firstposonly = static_iface |
ctx.firstposonly = static_iface |
||
ctx.n_available = maxfill |
|||
return ctx |
return ctx |
||
end |
end |
||
Line 167: | Line 240: | ||
-- 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 |
||
Line 187: | Line 260: | ||
-- 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 |
||
Line 193: | Line 266: | ||
-- Parse |
-- Parse user arguments of type `...|[let]|[...][number of additional |
||
-- parameters]|[parameter 1]|[parameter 2]|[...]` |
|||
function parse_child_args(dest, n_exist, src, n_skip) |
|||
local function parse_child_args (src, start_from, append_after) |
|||
local retval |
|||
local names |
local names |
||
local |
local tmp |
||
local |
local dest = {} |
||
local pin = |
local pin = start_from |
||
if src[pin]:match'^%s*let%s*$' then |
if src[pin] ~= nil and src[pin]:match'^%s*let%s*$' then |
||
names = {} |
names = {} |
||
repeat |
repeat |
||
tmp = src[pin + 1] or '' |
|||
names[tonumber(tmp) or tmp:match'^%s*(.-)%s*$' or ''] = |
|||
va2 = tonumber(va1) |
|||
src[pin + 2] |
|||
if va2 ~= nil and va2 > 0 then |
|||
names[va2 + n_exist] = src[pin + 2] |
|||
else names[va2 or va1 or ''] = src[pin + 2] end |
|||
pin = pin + 3 |
pin = pin + 3 |
||
until not src[pin]:match'^%s*let%s*$' |
until src[pin] == nil or not src[pin]:match'^%s*let%s*$' |
||
end |
end |
||
tmp = tonumber(src[pin]) |
|||
if |
if tmp ~= nil then |
||
if tmp < 0 then tmp = -1 end |
|||
else |
|||
local shf = append_after - pin |
|||
for idx = pin + 1, pin + |
for idx = pin + 1, pin + tmp do dest[idx + shf] = src[idx] end |
||
pin = pin + tmp + 1 |
|||
end |
end |
||
if names ~= nil then |
if names ~= nil then |
||
for key, val in pairs(names) do dest[key] = val end |
for key, val in pairs(names) do dest[key] = val end |
||
end |
end |
||
return |
return dest, pin |
||
end |
|||
-- Parse the arguments of some of the `mapping_*` and `renaming_*` class of |
|||
-- modifiers |
|||
local function parse_callback_args (src, n_skip, default_style) |
|||
local style |
|||
local shf |
|||
local tmp = src[n_skip + 1] |
|||
if tmp ~= nil then style = mapping_styles[tmp:match'^%s*(.-)%s*$'] end |
|||
if style == nil then |
|||
style = default_style |
|||
shf = n_skip - 1 |
|||
else shf = n_skip end |
|||
local n_exist = style[3] |
|||
local karg = style[4] |
|||
local varg = style[5] |
|||
tmp = style[6] |
|||
if tmp > -1 then |
|||
tmp = src[tmp + shf] |
|||
karg = tonumber(tmp) |
|||
if karg == nil then karg = tmp:match'^%s*(.-)%s*$' |
|||
else n_exist = math.max(n_exist, karg) end |
|||
end |
|||
tmp = style[7] |
|||
if tmp > -1 then |
|||
tmp = src[tmp + shf] |
|||
varg = tonumber(tmp) |
|||
if varg == nil then varg = tmp:match'^%s*(.-)%s*$' |
|||
else n_exist = math.max(n_exist, varg) end |
|||
end |
|||
local dest, nargs = parse_child_args(src, style[2] + shf, n_exist) |
|||
tmp = style[1] |
|||
if (tmp == 3 or tmp == 2) and dest[karg] ~= nil then |
|||
tmp = tmp - 2 end |
|||
if (tmp == 3 or tmp == 1) and dest[varg] ~= nil then |
|||
tmp = tmp - 1 end |
|||
return dest, nargs, tmp, karg, varg |
|||
end |
|||
-- Parse the arguments of some of the `mapping_*` and `renaming_*` class of |
|||
-- modifiers |
|||
local function parse_replace_args (opts, fname) |
|||
if opts[1] == nil then error(ctx.luaname .. |
|||
', ‘' .. fname .. '’: No pattern string was given', 0) end |
|||
if opts[2] == nil then error(ctx.luaname .. |
|||
', ‘' .. 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 parse_pattern_args |
local function parse_pattern_args (ctx, fname) |
||
local state = 0 |
local state = 0 |
||
local cnt = 1 |
local cnt = 1 |
||
local keyw |
local keyw |
||
local nptns = 0 |
local nptns = 0 |
||
local ptns = {} |
|||
for _, val in ipairs(opts) do |
|||
for _, val in ipairs(ctx.pipe) do |
|||
if state == 0 then |
if state == 0 then |
||
nptns = nptns + 1 |
nptns = nptns + 1 |
||
Line 251: | Line 381: | ||
if state == 0 then error(ctx.luaname .. ', ‘' .. fname .. |
if state == 0 then error(ctx.luaname .. ', ‘' .. fname .. |
||
'’: No pattern was given', 0) end |
'’: No pattern was given', 0) end |
||
return cnt |
return ptns, cnt |
||
end |
|||
-- Map parameters' values using a custom callback and a referenced table |
|||
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 |
|||
margs[varg] = val |
|||
tbl[key] = fn() |
|||
end |
|||
end, |
|||
[2] = function (tbl, margs, karg, varg, fn) |
|||
for key in pairs(tbl) do |
|||
margs[karg] = key |
|||
tbl[key] = fn() |
|||
end |
|||
end, |
|||
[3] = function (tbl, margs, karg, varg, fn) |
|||
for key, val in pairs(tbl) do |
|||
margs[karg] = key |
|||
margs[varg] = val |
|||
tbl[key] = fn() |
|||
end |
|||
end |
|||
} |
|||
-- Private table for `map_names()` |
|||
local name_thieves_maps = { |
|||
[0] = function (cache, tbl, rargs, karg, varg, fn) |
|||
for key, val in pairs(tbl) do |
|||
steal_if_renamed(val, tbl, key, cache, fn()) |
|||
end |
|||
end, |
|||
[1] = function (cache, tbl, rargs, karg, varg, fn) |
|||
for key, val in pairs(tbl) do |
|||
rargs[varg] = val |
|||
steal_if_renamed(val, tbl, key, cache, fn()) |
|||
end |
|||
end, |
|||
[2] = function (cache, tbl, rargs, karg, varg, fn) |
|||
for key, val in pairs(tbl) do |
|||
rargs[karg] = key |
|||
steal_if_renamed(val, tbl, key, cache, fn()) |
|||
end |
|||
end, |
|||
[3] = function (cache, tbl, rargs, karg, varg, fn) |
|||
for key, val in pairs(tbl) do |
|||
rargs[karg] = key |
|||
rargs[varg] = val |
|||
steal_if_renamed(val, tbl, key, cache, fn()) |
|||
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 |
|||
end |
|||
-- Return a new table that contains `src` regrouped according to the numerical |
|||
-- suffixes in its keys |
|||
local function make_groups (src) |
|||
-- NOTE: `src` might be the original metatable! |
|||
local tmp |
|||
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 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 |
end |
||
Line 258: | Line 478: | ||
-- keys from the table of options; non-numerical keys from the table of options |
-- 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 |
-- will prevail over colliding non-numerical keys from the table of parameters |
||
local function concat_params(ctx) |
local function concat_params (ctx) |
||
local tbl = ctx.params |
local tbl = ctx.params |
||
local size = table.maxn(ctx.pipe) |
local size = table.maxn(ctx.pipe) |
||
Line 281: | Line 501: | ||
-- Flush the parameters by calling a custom function for each value (after this |
-- 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 290: | Line 510: | ||
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) |
||
if ctx.sorttype == 2 then |
|||
for idx = 1, nlen do fn(nums[idx], tbl[nums[idx]]) end |
|||
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 326: | Line 551: | ||
-- 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(ctx.luaname .. |
if ctx.subset == -1 then error(ctx.luaname .. |
||
': 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(ctx.luaname .. |
||
': 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 338: | Line 563: | ||
-- 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(ctx.luaname .. |
if ctx.subset == 1 then error(ctx.luaname .. |
||
': 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 |
||
Line 347: | Line 572: | ||
-- 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(ctx.luaname .. |
if ctx.subset == 1 then error(ctx.luaname .. |
||
': The ‘all_sorted’ directive is redundant after ‘sequential’', 0) end |
': The ‘all_sorted’ directive is redundant after ‘sequential’', 0) end |
||
if ctx.sorttype == 2 then error(ctx.luaname .. |
|||
ctx.dosort = true |
|||
': 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(ctx.luaname .. |
|||
': The ‘reassorted’ directive is redundant after ‘sequential’', 0) end |
|||
if ctx.sorttype == 1 then error(ctx.luaname .. |
|||
': 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 357: | Line 595: | ||
-- 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 381: | Line 619: | ||
vname = memoryslots[string.char(chr)] |
vname = memoryslots[string.char(chr)] |
||
if vname == nil then error(ctx.luaname .. |
if vname == nil then error(ctx.luaname .. |
||
', ‘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 392: | Line 630: | ||
-- 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 412: | Line 650: | ||
-- 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 430: | Line 668: | ||
end |
end |
||
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 |
|||
if ctx.n_available < 0 then error(ctx.luaname .. |
|||
', ‘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 441: | Line 680: | ||
-- 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 numericals = {} |
local numericals = {} |
||
Line 456: | Line 695: | ||
-- 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(ctx.luaname .. |
if lcut == nil then error(ctx.luaname .. |
||
Line 491: | Line 730: | ||
-- 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(ctx.luaname .. |
if lcut == nil then error(ctx.luaname .. |
||
Line 535: | Line 774: | ||
-- 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(ctx.luaname .. |
if idx == nil then error(ctx.luaname .. |
||
Line 554: | Line 793: | ||
-- 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(ctx.luaname .. |
if last == nil then error(ctx.luaname .. |
||
Line 581: | Line 820: | ||
-- 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 numericals = {} |
local numericals = {} |
||
Line 599: | Line 838: | ||
-- 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 |
||
Line 618: | Line 857: | ||
-- 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 numericals = {} |
local numericals = {} |
||
Line 642: | Line 881: | ||
-- 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 665: | Line 904: | ||
-- 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 677: | Line 916: | ||
-- 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 |
||
Line 696: | Line 935: | ||
-- 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(ctx.luaname .. |
if ctx.pipe[1] == nil then error(ctx.luaname .. |
||
', ‘imposing’: Missing parameter name to impose', 0) end |
', ‘imposing’: Missing parameter name to impose', 0) end |
||
local key = ctx.pipe[1]:match'^%s*(. |
local key = ctx.pipe[1]:match'^%s*(.-)%s*$' |
||
ctx.params[tonumber(key) or key] = ctx.pipe[2] |
ctx.params[tonumber(key) or key] = ctx.pipe[2] |
||
return context_iterate(ctx, 3) |
|||
end |
|||
-- Syntax: #invoke:params|providing|name|value|pipe to |
|||
library.providing = function (ctx) |
|||
if ctx.pipe[1] == nil then error(ctx.luaname .. |
|||
', ‘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) |
return context_iterate(ctx, 3) |
||
end |
end |
||
Line 706: | Line 956: | ||
-- Syntax: #invoke:params|discarding|name|[how many]|pipe to |
-- Syntax: #invoke:params|discarding|name|[how many]|pipe to |
||
library.discarding = function(ctx) |
library.discarding = function (ctx) |
||
if ctx.pipe[1] == nil then error(ctx.luaname .. |
if ctx.pipe[1] == nil then error(ctx.luaname .. |
||
', ‘discarding’: Missing parameter name to |
', ‘discarding’: Missing parameter name to discard', 0) end |
||
local key = ctx.pipe[1] |
local key = ctx.pipe[1] |
||
local len = tonumber(ctx.pipe[2]) |
local len = tonumber(ctx.pipe[2]) |
||
if len == nil then |
if len == nil then |
||
ctx.params[tonumber(key) or key:match'^%s*(. |
ctx.params[tonumber(key) or key:match'^%s*(.-)%s*$'] = nil |
||
return context_iterate(ctx, 2) |
return context_iterate(ctx, 2) |
||
end |
end |
||
Line 725: | Line 975: | ||
-- Syntax: #invoke:params|with_name_matching| |
-- 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) |
||
local tbl = ctx.params |
local tbl = ctx.params |
||
local targets, argc = parse_pattern_args(ctx, targets, |
|||
local patterns = {} |
|||
'with_name_matching') |
|||
local nomatch |
local nomatch |
||
for key in pairs(tbl) do |
for key in pairs(tbl) do |
||
nomatch = true |
nomatch = true |
||
for _, ptn in ipairs( |
for _, ptn in ipairs(targets) do |
||
if not ptn[3] then |
if not ptn[3] then |
||
if string.find(key, ptn[1], 1, ptn[2]) then |
if string.find(key, ptn[1], 1, ptn[2]) then |
||
Line 752: | Line 1,002: | ||
-- 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 tbl = ctx.params |
local tbl = ctx.params |
||
local targets, argc = parse_pattern_args(ctx, targets, |
|||
local patterns = {} |
|||
local argc = parse_pattern_args(patterns, ctx.pipe, |
|||
'with_name_not_matching') |
'with_name_not_matching') |
||
local yesmatch |
local yesmatch |
||
for key in pairs(tbl) do |
for key in pairs(tbl) do |
||
yesmatch = true |
yesmatch = true |
||
for _, ptn in ipairs( |
for _, ptn in ipairs(targets) do |
||
if ptn[3] then |
if ptn[3] then |
||
if key ~= ptn[1] then |
if key ~= ptn[1] then |
||
Line 780: | Line 1,029: | ||
-- 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 targets, argc = parse_pattern_args(ctx, targets, |
|||
local patterns = {} |
|||
local argc = parse_pattern_args(patterns, ctx.pipe, |
|||
'with_value_matching') |
'with_value_matching') |
||
local nomatch |
local nomatch |
||
for key, val in pairs(tbl) do |
for key, val in pairs(tbl) do |
||
nomatch = true |
nomatch = true |
||
for _, ptn in ipairs( |
for _, ptn in ipairs(targets) do |
||
if ptn[3] then |
if ptn[3] then |
||
if val == ptn[1] then |
if val == ptn[1] then |
||
Line 808: | Line 1,056: | ||
-- 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 targets, argc = parse_pattern_args(ctx, targets, |
|||
local patterns = {} |
|||
local argc = parse_pattern_args(patterns, ctx.pipe, |
|||
'with_value_not_matching') |
'with_value_not_matching') |
||
local yesmatch |
local yesmatch |
||
for key, val in pairs(tbl) do |
for key, val in pairs(tbl) do |
||
yesmatch = true |
yesmatch = true |
||
for _, ptn in ipairs( |
for _, ptn in ipairs(targets) do |
||
if ptn[3] then |
if ptn[3] then |
||
if val ~= ptn[1] then |
if val ~= ptn[1] then |
||
Line 837: | Line 1,084: | ||
-- 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 844: | Line 1,091: | ||
-- Syntax: #invoke:params|mapping_by_calling|template name|[ |
-- Syntax: #invoke:params|mapping_by_calling|template name|[call |
||
-- |
-- style]|[let]|[...][number of additional parameters]|[parameter |
||
-- 2]|[...]|[ |
-- 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 |
||
Line 853: | Line 1,100: | ||
if tname == nil then error(ctx.luaname .. |
if tname == nil then error(ctx.luaname .. |
||
', ‘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 = parse_callback_args(opts, 1, |
||
mapping_styles.values_only) |
|||
local nargs = parse_child_args(margs, 2, opts, 1) |
|||
local model = { title = tname, args = margs } |
local model = { title = tname, args = margs } |
||
value_maps[looptype](ctx.params, margs, karg, varg, function () |
|||
local tbl = ctx.params |
|||
return ctx.frame:expandTemplate(model) |
|||
for key, val in pairs(tbl) do |
|||
end) |
|||
margs[1] = key |
|||
return context_iterate(ctx, argc) |
|||
margs[2] = val |
|||
tbl[key] = ctx.frame:expandTemplate(model) |
|||
end |
|||
return context_iterate(ctx, nargs) |
|||
end |
end |
||
-- Syntax: #invoke:params|mapping_by_invoking|module name|function |
-- Syntax: #invoke:params|mapping_by_invoking|module name|function |
||
-- name|[let]|[...]|[number of additional |
-- name|[call style]|[let]|[...]|[number of additional |
||
-- 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 |
||
Line 879: | Line 1,123: | ||
if fname == nil then error(ctx.luaname .. |
if fname == nil then error(ctx.luaname .. |
||
', ‘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 = parse_callback_args(opts, 2, |
||
mapping_styles.values_only) |
|||
local nargs = parse_child_args(margs, 2, opts, 2) |
|||
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(ctx.luaname .. |
|||
local tbl = ctx.params |
|||
', ‘mapping_by_invoking’: The function ‘' .. fname .. |
|||
for key, val in pairs(tbl) do |
|||
'’ does not exist', 0) end |
|||
margs[1] = key |
|||
value_maps[looptype](ctx.params, margs, karg, varg, function () |
|||
margs[2] = val |
|||
return mfunc(ctx.frame:newChild(model)) |
|||
end |
end) |
||
return context_iterate(ctx, |
return context_iterate(ctx, argc) |
||
end |
end |
||
-- Syntax: #invoke:params| |
-- Syntax: #invoke:params|mapping_by_magic|parser function|[call |
||
-- |
-- style]|[let]|[...][number of additional arguments]|[argument |
||
-- 1]|[argument 2]|[...]|[argument N]|pipe to |
-- 1]|[argument 2]|[...]|[argument N]|pipe to |
||
library. |
library.mapping_by_magic = function (ctx) |
||
local opts = ctx.pipe |
local opts = ctx.pipe |
||
local |
local magic |
||
if opts[1] ~= nil then |
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end |
||
if |
if magic == nil then error(ctx.luaname .. |
||
', |
', ‘mapping_by_magic’: No parser function was provided', 0) end |
||
local margs = |
local margs, argc, looptype, karg, varg = parse_callback_args(opts, 1, |
||
mapping_styles.values_only) |
|||
local nargs = parse_child_args(margs, 1, opts, 1) |
|||
value_maps[looptype](ctx.params, margs, karg, varg, function () |
|||
local model = { title = tname, args = margs } |
|||
return ctx.frame:callParserFunction(magic, margs) |
|||
local tbl = ctx.params |
|||
end) |
|||
for key, val in pairs(tbl) do |
|||
return context_iterate(ctx, argc) |
|||
margs[1] = val |
|||
tbl[key] = ctx.frame:expandTemplate(model) |
|||
end |
|||
return context_iterate(ctx, nargs) |
|||
end |
end |
||
-- Syntax: #invoke:params| |
-- Syntax: #invoke:params|mapping_by_replacing|target|replace|[count]|[plain |
||
-- |
-- flag]|pipe to |
||
library.mapping_by_replacing = function (ctx) |
|||
-- arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to |
|||
local ptn, repl, nmax, is_strict, argc, die = |
|||
library.mapping_blindly_by_invoking = function(ctx) |
|||
parse_replace_args(ctx.pipe, 'mapping_by_replacing') |
|||
local opts = ctx.pipe |
|||
if die then return context_iterate(ctx, argc) end |
|||
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 margs = {} |
|||
local nargs = parse_child_args(margs, 1, opts, 2) |
|||
local model = { title = 'Module:' .. mname, args = margs } |
|||
local mfunc = require(model.title)[fname] |
|||
local tbl = ctx.params |
local tbl = ctx.params |
||
if is_strict then |
|||
for key, val in pairs(tbl) do |
|||
for key, val in pairs(tbl) do |
|||
margs[1] = val |
|||
if val == ptn then tbl[key] = repl end |
|||
tbl[key] = mfunc(ctx.frame:newChild(model)) |
|||
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 |
end |
||
return context_iterate(ctx, |
return context_iterate(ctx, argc) |
||
end |
end |
||
-- Syntax: #invoke:params| |
-- Syntax: #invoke:params|renaming_by_calling|template name|[call |
||
-- |
-- style]|[let]|[...][number of additional parameters]|[parameter |
||
-- 1]|[ |
-- 1]|[parameter 2]|[...]|[parameter N]|pipe to |
||
library. |
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(ctx.luaname .. |
if tname == nil then error(ctx.luaname .. |
||
', |
', ‘renaming_by_calling’: No template name was provided', 0) end |
||
local rargs = |
local rargs, argc, looptype, karg, varg = parse_callback_args(opts, 1, |
||
mapping_styles.names_only) |
|||
local nargs = parse_child_args(rargs, 1, opts, 1) |
|||
local model = { title = tname, args = rargs } |
local model = { title = tname, args = rargs } |
||
map_names(ctx.params, rargs, karg, varg, looptype, function () |
|||
local cache = {} |
|||
return ctx.frame:expandTemplate(model) |
|||
local tbl = ctx.params |
|||
end) |
|||
for key, val in pairs(tbl) do |
|||
return context_iterate(ctx, argc) |
|||
rargs[1] = key |
|||
tmp = ctx.frame:expandTemplate(model):match'^%s*(.-)%s*$' |
|||
tmp = tonumber(tmp) or tmp |
|||
if key ~= tmp then |
|||
cache[tmp] = val |
|||
tbl[key] = nil |
|||
end |
|||
end |
|||
for key, val in pairs(cache) do tbl[key] = val end |
|||
return context_iterate(ctx, nargs) |
|||
end |
end |
||
-- Syntax: #invoke:params| |
-- Syntax: #invoke:params|renaming_by_invoking|module name|function |
||
-- name| |
-- 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. |
library.renaming_by_invoking = function (ctx) |
||
local opts = ctx.pipe |
local opts = ctx.pipe |
||
local mname |
local mname |
||
Line 977: | Line 1,207: | ||
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 .. |
||
', |
', ‘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(ctx.luaname .. |
if fname == nil then error(ctx.luaname .. |
||
', |
', ‘renaming_by_invoking’: No function name was provided', 0) end |
||
local rargs = |
local rargs, argc, looptype, karg, varg = parse_callback_args(opts, 2, |
||
mapping_styles.names_only) |
|||
local nargs = parse_child_args(rargs, 1, opts, 2) |
|||
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(ctx.luaname .. |
|||
local cache = {} |
|||
', ‘renaming_by_invoking’: The function ‘' .. fname .. |
|||
'’ does not exist', 0) end |
|||
map_names(ctx.params, rargs, karg, varg, looptype, function () |
|||
return mfunc(ctx.frame:newChild(model)) |
|||
end) |
|||
return context_iterate(ctx, argc) |
|||
end |
|||
-- Syntax: #invoke:params|renaming_by_magic|parser function|[call |
|||
-- style]|[let]|[...][number of additional arguments]|[argument |
|||
-- 1]|[argument 2]|[...]|[argument N]|pipe to |
|||
library.renaming_by_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 .. |
|||
', ‘renaming_by_magic’: No parser function was provided', 0) end |
|||
local rargs, argc, looptype, karg, varg = parse_callback_args(opts, 1, |
|||
mapping_styles.names_only) |
|||
map_names(ctx.params, rargs, karg, varg, looptype, function () |
|||
return ctx.frame:callParserFunction(magic, rargs) |
|||
end) |
|||
return context_iterate(ctx, argc) |
|||
end |
|||
-- 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 = |
|||
parse_replace_args(ctx.pipe, 'renaming_by_replacing') |
|||
if die then return context_iterate(ctx, argc) end |
|||
local tbl = ctx.params |
local tbl = ctx.params |
||
if is_strict then |
|||
for key, val in pairs(tbl) do |
|||
local key = tonumber(ptn) or ptn:match'^%s*(.-)%s*$' |
|||
rargs[1] = key |
|||
local val = tbl[key] |
|||
tmp = mfunc(ctx.frame:newChild(model)):match'^%s*(.-)%s*$' |
|||
tbl[key] = nil |
|||
tmp = tonumber(tmp) or tmp |
|||
tbl[tonumber(repl) or repl:match'^%s*(.-)%s*$'] = val |
|||
if key ~= tmp then |
|||
else |
|||
cache[tmp] = val |
|||
if flg == 2 then |
|||
-- Copied from Module:String's `str._escapePattern()` |
|||
ptn = ptn:gsub('[%(%)%.%%%+%-%*%?%[%^%$%]]', '%%%0') |
|||
end |
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 |
end |
||
return context_iterate(ctx, argc) |
|||
for key, val in pairs(cache) do tbl[key] = val end |
|||
end |
|||
return context_iterate(ctx, nargs) |
|||
-- 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(ctx.luaname .. |
|||
', ‘grouping_by_calling’: No template name was provided', 0) end |
|||
local model = { title = tmp } |
|||
local tmp, argc = parse_child_args(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 |
end |
||
Line 1,007: | Line 1,307: | ||
-- Syntax: #invoke:params|count |
-- Syntax: #invoke:params|count |
||
library.count = function(ctx) |
library.count = 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 retval = 0 |
local retval = 0 |
||
Line 1,020: | Line 1,320: | ||
-- |[...]|[item n]|[named item 1=value 1]|[...]|[named item n=value |
-- |[...]|[item n]|[named item 1=value 1]|[...]|[named item n=value |
||
-- n]|[...] |
-- n]|[...] |
||
library.concat_and_call = function(ctx) |
library.concat_and_call = function (ctx) |
||
-- NOTE: `ctx.params` might be the original metatable! |
-- NOTE: `ctx.params` might be the original metatable! |
||
local opts = ctx.pipe |
local opts = ctx.pipe |
||
Line 1,039: | Line 1,339: | ||
-- 1]|[prepend 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named |
-- 1]|[prepend 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named |
||
-- item n=value n]|[...] |
-- item n=value n]|[...] |
||
library.concat_and_invoke = function(ctx) |
library.concat_and_invoke = function (ctx) |
||
-- NOTE: `ctx.params` might be the original metatable! |
-- NOTE: `ctx.params` might be the original metatable! |
||
local opts = ctx.pipe |
local opts = ctx.pipe |
||
Line 1,051: | Line 1,351: | ||
', ‘concat_and_invoke’: No function name was provided', 0) end |
', ‘concat_and_invoke’: No function name was provided', 0) end |
||
remove_numerical_keys(opts, 1, 2) |
remove_numerical_keys(opts, 1, 2) |
||
local mfunc = require('Module:' .. mname)[fname] |
|||
if mfunc == nil then error(ctx.luaname .. |
|||
', ‘concat_and_invoke’: The function ‘' .. fname .. |
|||
'’ does not exist', 0) end |
|||
ctx.text = mfunc(ctx.frame:newChild{ |
|||
title = 'Module:' .. fname, |
title = 'Module:' .. fname, |
||
args = concat_params(ctx) |
args = concat_params(ctx) |
||
Line 1,062: | Line 1,366: | ||
-- 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named item n= |
-- 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named item n= |
||
-- value n]|[...] |
-- value n]|[...] |
||
library.concat_and_magic = function(ctx) |
library.concat_and_magic = function (ctx) |
||
-- NOTE: `ctx.params` might be the original metatable! |
-- NOTE: `ctx.params` might be the original metatable! |
||
local opts = ctx.pipe |
local opts = ctx.pipe |
||
Line 1,076: | Line 1,380: | ||
-- Syntax: #invoke:params|value_of|parameter name |
-- Syntax: #invoke:params|value_of|parameter name |
||
library.value_of = function(ctx) |
library.value_of = 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 |
||
Line 1,100: | Line 1,404: | ||
-- Syntax: #invoke:params|list |
-- Syntax: #invoke:params|list |
||
library.list = function(ctx) |
library.list = function (ctx) |
||
-- NOTE: `ctx.pipe` might be the original metatable! |
-- NOTE: `ctx.pipe` might be the original metatable! |
||
local kvs = ctx.pairsep or '' |
local kvs = ctx.pairsep or '' |
||
Line 1,108: | Line 1,412: | ||
flush_params( |
flush_params( |
||
ctx, |
ctx, |
||
function(key, val) |
function (key, val) |
||
ret[nss + 1] = pps |
ret[nss + 1] = pps |
||
ret[nss + 2] = key |
ret[nss + 2] = key |
||
Line 1,131: | Line 1,435: | ||
-- Syntax: #invoke:params|list_values |
-- Syntax: #invoke:params|list_values |
||
library.list_values = function(ctx) |
library.list_values = function (ctx) |
||
-- NOTE: `ctx.pipe` might be the original metatable! |
-- NOTE: `ctx.pipe` might be the original metatable! |
||
local pps = ctx.itersep or '' |
local pps = ctx.itersep or '' |
||
Line 1,138: | Line 1,442: | ||
flush_params( |
flush_params( |
||
ctx, |
ctx, |
||
function(key, val) |
function (key, val) |
||
ret[nss + 1] = pps |
ret[nss + 1] = pps |
||
ret[nss + 2] = val |
ret[nss + 2] = val |
||
Line 1,159: | Line 1,463: | ||
-- 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,167: | Line 1,471: | ||
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,190: | Line 1,494: | ||
-- |[...]|[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 |
||
Line 1,203: | Line 1,507: | ||
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,228: | Line 1,532: | ||
-- 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 |
||
Line 1,245: | Line 1,549: | ||
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,270: | Line 1,574: | ||
-- |[...]|[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 |
||
Line 1,282: | Line 1,586: | ||
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,308: | Line 1,612: | ||
-- 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 |
||
Line 1,320: | Line 1,624: | ||
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,344: | Line 1,648: | ||
-- 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 |
||
Line 1,362: | Line 1,666: | ||
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,386: | Line 1,690: | ||
-- |[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 |
||
Line 1,397: | Line 1,701: | ||
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,422: | Line 1,726: | ||
-- 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 |
||
Line 1,432: | Line 1,736: | ||
local ccs = ctx.itersep or '' |
local ccs = ctx.itersep or '' |
||
local nss = 0 |
local nss = 0 |
||
local prefix |
|||
local gid |
|||
local groups = {} |
|||
local ret = {} |
local ret = {} |
||
opts = {} |
opts = {} |
||
Line 1,442: | Line 1,743: | ||
end |
end |
||
ctx.pipe = opts |
ctx.pipe = opts |
||
ctx.params = make_groups(ctx.params) |
|||
prefix, gid = tostring(key):match'^%s*(.-)%s*(%-?%d*)%s*$' |
|||
gid = tonumber(gid) or '' |
|||
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 |
|||
ctx.params = groups |
|||
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,492: | Line 1,783: | ||
-- 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() |
local ctx = context_new() |
||
ctx.frame = frame:getParent() |
ctx.frame = frame:getParent() |
||
Line 1,506: | Line 1,797: | ||
--[[ 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,518: | Line 1,808: | ||
--[[ Public metatable of functions ]]-- |
--[[ Public metatable of functions ]]-- |
||
--------------------------------------- |
--------------------------------------- |
||
return setmetatable(static_iface, { |
return setmetatable(static_iface, { |
||
__index = function(iface, _fname_) |
__index = function (iface, _fname_) |
||
local ctx = context_new() |
local ctx = context_new() |
||
local fname = _fname_:match'^%s*(.*%S)' |
local fname = _fname_:match'^%s*(.*%S)' |
||
Line 1,529: | Line 1,818: | ||
if library[fname] == nil then error(ctx.luaname .. |
if library[fname] == nil then error(ctx.luaname .. |
||
': The function ‘' .. fname .. '’ does not exist', 0) end |
': The function ‘' .. fname .. '’ does not exist', 0) end |
||
local func = library[fname] |
|||
return function(frame) |
|||
return function (frame) |
|||
local func = library[fname] |
|||
local refpipe = { |
|||
count = true, |
|||
value_of = true, |
|||
list = true, |
|||
list_values = true, |
|||
for_each = true, |
|||
call_for_each_group = true |
|||
} |
|||
local refparams = { |
|||
--inserting = true, |
|||
count = true, |
|||
concat_and_call = true, |
|||
concat_and_invoke = true, |
|||
concat_and_magic = true, |
|||
value_of = true, |
|||
call_for_each_group = true |
|||
} |
|||
ctx.frame = frame:getParent() |
ctx.frame = frame:getParent() |
||
ctx.pipe = copy_or_ref_table(frame.args, |
ctx.pipe = copy_or_ref_table(frame.args, |
||
refpipe[fname]) |
|||
ctx.params = copy_or_ref_table(ctx.frame.args, refparams[fname]) |
|||
ctx.params = copy_or_ref_table(ctx.frame.args, |
|||
refparams[fname]) |
|||
main_loop(ctx, func) |
main_loop(ctx, func) |
||
return ctx.text |
return ctx.text |
Latest revision as of 18:56, 1 October 2024
Documentation for this module may be created at Module:Params/doc
--- ---
--- LOCAL ENVIRONMENT ---
--- ________________________________ ---
--- ---
--[[ Abstract utilities ]]--
----------------------------
-- Helper function for `string.gsub()` (for managing zero-padded numbers)
local function zero_padded (str)
return ('%03d%s'):format(#str, str)
end
-- Helper function for `table.sort()` (for natural sorting)
local function natural_sort (var1, var2)
return tostring(var1):gsub('%d+', zero_padded) <
tostring(var2):gsub('%d+', zero_padded)
end
-- 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
-- Remove numerical elements from a table, shifting everything to the left
local function remove_numerical_keys (tbl, idx, len)
local cache = {}
local tmp = idx + len - 1
for key, val in pairs(tbl) do
if type(key) == 'number' and key >= idx then
if key > tmp then cache[key - len] = val end
tbl[key] = nil
end
end
for key, val in pairs(cache) do tbl[key] = val end
end
-- Make a reduced copy of a table (shifting in both directions if necessary)
local function copy_table_reduced (tbl, idx, len)
local ret = {}
local tmp = idx + len - 1
if idx > 0 then
for key, val in pairs(tbl) do
if type(key) ~= 'number' or key < idx then
ret[key] = val
elseif key > tmp then ret[key - len] = val end
end
elseif tmp > 0 then
local nshift = 1 - idx
for key, val in pairs(tbl) do
if type(key) ~= 'number' then ret[key] = val
elseif key > tmp then ret[key - tmp] = val
elseif key < idx then ret[key + nshift] = val end
end
else
for key, val in pairs(tbl) do
if type(key) ~= 'number' or key > tmp then
ret[key] = val
elseif key < idx then ret[key + len] = val end
end
end
return ret
end
-- Make an expanded copy of a table (shifting in both directions if necessary)
--[[
local function copy_table_expanded (tbl, idx, len)
local ret = {}
local tmp = idx + len - 1
if idx > 0 then
for key, val in pairs(tbl) do
if type(key) ~= 'number' or key < idx then
ret[key] = val
else ret[key + len] = val end
end
elseif tmp > 0 then
local nshift = idx - 1
for key, val in pairs(tbl) do
if type(key) ~= 'number' then ret[key] = val
elseif key > 0 then ret[key + tmp] = val
elseif key < 1 then ret[key + nshift] = val end
end
else
for key, val in pairs(tbl) do
if type(key) ~= 'number' or key > tmp then
ret[key] = val
else ret[key - len] = val end
end
end
return ret
end
]]--
-- Move a key from a table to another, but only if under a different name and
-- always parsing numerical strings as numbers
local function steal_if_renamed (val, src, skey, dest, dkey)
local realkey = tonumber(dkey) or dkey:match'^%s*(.-)%s*$'
if skey ~= realkey then
dest[realkey] = val
src[skey] = nil
end
end
--[[ Public strings ]]--
------------------------
-- Special match keywords (functions and modifiers MUST avoid these names)
local mkeywords = {
['or'] = 0,
pattern = 1,
plain = 2,
strict = 3
}
-- Sort functions (functions and modifiers MUST avoid these names)
local sortfunctions = {
--alphabetically = false, -- Simply uncommenting enables the option
naturally = natural_sort
}
-- Callback styles for the `mapping_*` and `renaming_*` class of modifiers
-- (functions and modifiers MUST avoid these names)
--[[
Meanings of the columns:
col[1] = Loop type (0-3)
col[2] = Number of module arguments that the style requires (1-3)
col[3] = Minimum number of sequential parameters passed to the callback
col[4] = Name of the callback parameter where to place each parameter name
col[5] = Name of the callback parameter where to place each parameter value
col[6] = Argument in the modifier's invocation that will override `col[4]`
col[7] = Argument in the modifier's invocation that will override `col[5]`
A value of `-1` indicates that no meaningful value is stored (i.e. `nil`)
]]--
local mapping_styles = {
names_and_values = { 3, 2, 2, 1, 2, -1, -1 },
values_and_names = { 3, 2, 2, 2, 1, -1, -1 },
values_only = { 1, 2, 1, -1, 1, -1, -1 },
names_only = { 2, 2, 1, 1, -1, -1, -1 },
names_and_values_as = { 3, 4, 0, -1, -1, 2, 3 },
names_only_as = { 2, 3, 0, -1, -1, 2, -1 },
values_only_as = { 1, 3, 0, -1, -1, -1, 2 },
blindly = { 0, 2, 0, -1, -1, -1, -1 }
}
-- Memory slots (functions and modifiers MUST avoid these names)
local memoryslots = {
i = 'itersep',
l = 'lastsep',
p = 'pairsep',
h = 'header',
f = 'footer',
n = 'ifngiven'
}
-- Functions and modifiers MUST avoid these names too: `let`
--[[ Module's private environment ]]--
--------------------------------------
-- 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 available their own (modified) copy
local refpipe = {
count = true,
value_of = true,
list = true,
list_values = true,
for_each = true,
call_for_each_group = true
}
-- 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 available their
-- own (modified) copy
local refparams = {
--inserting = true,
grouping_by_calling = true,
count = true,
concat_and_call = true,
concat_and_invoke = true,
concat_and_magic = true,
value_of = true,
call_for_each_group = true
}
-- Maximum number of numerical parameters that can be filled, if missing (we
-- chose an arbitrary number for this constant; you can discuss about its
-- optimal value at Module talk:Params)
local maxfill = 1024
-- The private table of functions
local library = {}
-- Functions that can only be invoked in first position
local static_iface = {}
-- Create a new context
local function context_new ()
local ctx = {}
ctx.luaname = 'Module:Params' --[[ or `frame:getTitle()` ]]--
ctx.iterfunc = pairs
ctx.sorttype = 0
ctx.firstposonly = static_iface
ctx.n_available = maxfill
return 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 ctx.firstposonly[nextfn] == nil then error(ctx.luaname ..
': The function ‘' .. nextfn .. '’ does not exist', 0)
else error(ctx.luaname .. ': The ‘' .. nextfn ..
'’ directive can only appear in first position', 0)
end
end
remove_numerical_keys(ctx.pipe, 1, n_forward)
return library[nextfn]
end
-- Main loop
local function main_loop (ctx, start_with)
local fn = start_with
repeat fn = fn(ctx) until not fn
end
-- Parse user arguments of type `...|[let]|[...][number of additional
-- parameters]|[parameter 1]|[parameter 2]|[...]`
local function parse_child_args (src, start_from, append_after)
local names
local tmp
local dest = {}
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 dest[idx + shf] = src[idx] end
pin = pin + tmp + 1
end
if names ~= nil then
for key, val in pairs(names) do dest[key] = val end
end
return dest, pin
end
-- Parse the arguments of some of the `mapping_*` and `renaming_*` class of
-- modifiers
local function parse_callback_args (src, n_skip, default_style)
local style
local shf
local tmp = src[n_skip + 1]
if tmp ~= nil then style = mapping_styles[tmp:match'^%s*(.-)%s*$'] end
if style == nil then
style = default_style
shf = n_skip - 1
else shf = n_skip end
local n_exist = style[3]
local karg = style[4]
local varg = style[5]
tmp = style[6]
if tmp > -1 then
tmp = src[tmp + shf]
karg = tonumber(tmp)
if karg == nil then karg = tmp:match'^%s*(.-)%s*$'
else n_exist = math.max(n_exist, karg) end
end
tmp = style[7]
if tmp > -1 then
tmp = src[tmp + shf]
varg = tonumber(tmp)
if varg == nil then varg = tmp:match'^%s*(.-)%s*$'
else n_exist = math.max(n_exist, varg) end
end
local dest, nargs = parse_child_args(src, style[2] + shf, n_exist)
tmp = style[1]
if (tmp == 3 or tmp == 2) and dest[karg] ~= nil then
tmp = tmp - 2 end
if (tmp == 3 or tmp == 1) and dest[varg] ~= nil then
tmp = tmp - 1 end
return dest, nargs, tmp, karg, varg
end
-- Parse the arguments of some of the `mapping_*` and `renaming_*` class of
-- modifiers
local function parse_replace_args (opts, fname)
if opts[1] == nil then error(ctx.luaname ..
', ‘' .. fname .. '’: No pattern string was given', 0) end
if opts[2] == nil then error(ctx.luaname ..
', ‘' .. 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
-- Parse the arguments of the `with_*_matching` class of modifiers
local function parse_pattern_args (ctx, fname)
local state = 0
local cnt = 1
local keyw
local nptns = 0
local ptns = {}
for _, val in ipairs(ctx.pipe) do
if state == 0 then
nptns = nptns + 1
ptns[nptns] = { val, false, false }
state = -1
else
keyw = val:match'^%s*(.*%S)'
if keyw == nil or mkeywords[keyw] == nil or (
state > 0 and mkeywords[keyw] > 0
) then break
else
state = mkeywords[keyw]
if state > 1 then ptns[nptns][2] = true end
if state == 3 then ptns[nptns][3] = true end
end
end
cnt = cnt + 1
end
if state == 0 then error(ctx.luaname .. ', ‘' .. fname ..
'’: No pattern was given', 0) end
return ptns, cnt
end
-- Map parameters' values using a custom callback and a referenced table
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
margs[varg] = val
tbl[key] = fn()
end
end,
[2] = function (tbl, margs, karg, varg, fn)
for key in pairs(tbl) do
margs[karg] = key
tbl[key] = fn()
end
end,
[3] = function (tbl, margs, karg, varg, fn)
for key, val in pairs(tbl) do
margs[karg] = key
margs[varg] = val
tbl[key] = fn()
end
end
}
-- Private table for `map_names()`
local name_thieves_maps = {
[0] = function (cache, tbl, rargs, karg, varg, fn)
for key, val in pairs(tbl) do
steal_if_renamed(val, tbl, key, cache, fn())
end
end,
[1] = function (cache, tbl, rargs, karg, varg, fn)
for key, val in pairs(tbl) do
rargs[varg] = val
steal_if_renamed(val, tbl, key, cache, fn())
end
end,
[2] = function (cache, tbl, rargs, karg, varg, fn)
for key, val in pairs(tbl) do
rargs[karg] = key
steal_if_renamed(val, tbl, key, cache, fn())
end
end,
[3] = function (cache, tbl, rargs, karg, varg, fn)
for key, val in pairs(tbl) do
rargs[karg] = key
rargs[varg] = val
steal_if_renamed(val, tbl, key, cache, fn())
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
end
-- Return a new table that contains `src` regrouped according to the numerical
-- suffixes in its keys
local function make_groups (src)
-- NOTE: `src` might be the original metatable!
local tmp
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 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
-- 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 tbl = ctx.params
local size = 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 + size] = val end
else
if ctx.subset == -1 then
for key, val in ipairs(tbl) do tbl[key] = nil end
end
for key, val in pairs(tbl) do
if type(key) == 'number' then retval[key + size] = 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)
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.sorttype > 0 then
local nums = {}
local words = {}
local nn = 0
local nw = 0
for key, val in pairs(tbl) do
if type(key) == 'number' then
nn = nn + 1
nums[nn] = key
else
nw = nw + 1
words[nw] = key
end
end
table.sort(nums)
table.sort(words, natural_sort)
if ctx.sorttype == 2 then
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
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
--[[ Modifiers ]]--
-----------------------------
-- Syntax: #invoke:params|sequential|pipe to
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.sorttype > 0 then error(ctx.luaname ..
': The ‘all_sorted’ and ‘reassorted’ directives are redundant when followed by ‘sequential’', 0) end
ctx.iterfunc = ipairs
ctx.subset = 1
return context_iterate(ctx, 1)
end
-- Syntax: #invoke:params|non-sequential|pipe to
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
-- Syntax: #invoke:params|all_sorted|pipe to
library.all_sorted = function (ctx)
if ctx.subset == 1 then error(ctx.luaname ..
': The ‘all_sorted’ directive is redundant after ‘sequential’', 0) end
if ctx.sorttype == 2 then error(ctx.luaname ..
': 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(ctx.luaname ..
': The ‘reassorted’ directive is redundant after ‘sequential’', 0) end
if ctx.sorttype == 1 then error(ctx.luaname ..
': The two directives ‘sequential’ and ‘reassorted’ are in contradiction with each other', 0) end
ctx.sorttype = 2
return context_iterate(ctx, 1)
end
-- Syntax: #invoke:params|setting|directives|...|pipe to
library.setting = function (ctx)
local opts = ctx.pipe
local cmd = opts[1]
if cmd ~= nil then
cmd = cmd: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
-- Syntax: #invoke:params|squeezing|pipe to
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
-- Syntax: #invoke:params|filling_the_gaps|pipe to
library.filling_the_gaps = function (ctx)
local tbl = ctx.params
local nmin = 1
local nmax = nil
local nnums = -1
local tmp = {}
for key, val in pairs(tbl) do
if type(key) == 'number' then
if nmax == nil then
if key < nmin then nmin = key end
nmax = key
elseif key > nmax then nmax = key
elseif key < nmin then nmin = key end
nnums = nnums + 1
tmp[key] = val
end
end
if nmax ~= nil and nmax - nmin > nnums then
ctx.n_available = ctx.n_available + nmin + nnums - nmax
if ctx.n_available < 0 then error(ctx.luaname ..
', ‘filling_the_gaps’: It is possible to fill at most ' ..
tostring(maxfill) .. ' parameters', 0) end
for idx = nmin, nmax, 1 do tbl[idx] = '' end
for key, val in pairs(tmp) do tbl[key] = val end
end
return context_iterate(ctx, 1)
end
-- Syntax: #invoke:params|clearing|pipe to
library.clearing = function (ctx)
local tbl = ctx.params
local numericals = {}
for key, val in pairs(tbl) do
if type(key) == 'number' then
numericals[key] = val
tbl[key] = nil
end
end
for key, val in ipairs(numericals) do tbl[key] = val end
return context_iterate(ctx, 1)
end
-- Syntax: #invoke:params|cutting|left cut|right cut|pipe to
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
-- Syntax: #invoke:params|cropping|left crop|right crop|pipe to
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 do tbl[idx] = nil end
for idx = nmin, nmin + lcut - 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
-- Syntax: #invoke:params|purging|start offset|length|pipe to
library.purging = function (ctx)
local idx = tonumber(ctx.pipe[1])
if idx == nil then error(ctx.luaname ..
', ‘purging’: Start offset must be a number', 0) end
local len = tonumber(ctx.pipe[2])
if len == nil then error(ctx.luaname ..
', ‘purging’: Length must be a number', 0) end
local tbl = ctx.params
if len < 1 then
len = len + table.maxn(tbl)
if idx > len then return context_iterate(ctx, 3) end
len = len - idx + 1
end
ctx.params = copy_table_reduced(tbl, idx, len)
return context_iterate(ctx, 3)
end
-- Syntax: #invoke:params|backpurging|start offset|length|pipe to
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 len = tonumber(ctx.pipe[2])
if len == nil then error(ctx.luaname ..
', ‘backpurging’: Length must be a number', 0) end
local idx
local tbl = ctx.params
if len > 0 then
idx = last - len + 1
else
for key in pairs(tbl) do
if type(key) == 'number' and (idx == nil or
key < idx) then idx = key end
end
if idx == nil then return context_iterate(ctx, 3) end
idx = idx - len
if last < idx then return context_iterate(ctx, 3) end
len = last - idx + 1
end
ctx.params = copy_table_reduced(ctx.params, idx, len)
return context_iterate(ctx, 3)
end
-- Syntax: #invoke:params|rotating|pipe to
library.rotating = function (ctx)
local tbl = ctx.params
local numericals = {}
local nmax = 0
for key, val in pairs(tbl) do
if type(key) == 'number' then
numericals[key] = val
tbl[key] = nil
if key > nmax then nmax = key end
end
end
for key, val in pairs(numericals) do tbl[nmax - key + 1] = val end
return context_iterate(ctx, 1)
end
-- Syntax: #invoke:params|pivoting|pipe to
--[[
library.pivoting = function (ctx)
local tbl = ctx.params
local shift = #tbl + 1
if shift < 2 then return library.rotating(ctx) end
local numericals = {}
for key, val in pairs(tbl) do
if type(key) == 'number' then
numericals[key] = val
tbl[key] = nil
end
end
for key, val in pairs(numericals) do tbl[shift - key] = val end
return context_iterate(ctx, 1)
end
]]--
-- Syntax: #invoke:params|mirroring|pipe to
--[[
library.mirroring = function (ctx)
local tbl = ctx.params
local numericals = {}
local nmax
local nmin
for key, val in pairs(tbl) do
if type(key) == 'number' then
numericals[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(numericals) do tbl[nmax + nmin - key] = val end
return context_iterate(ctx, 1)
end
]]--
-- Syntax: #invoke:params|swapping|pipe to
--[[
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
]]--
-- Syntax: #invoke:params|sorting_sequential_values|[criterion]|pipe to
library.sorting_sequential_values = function (ctx)
local sortfn
if ctx.pipe[1] ~= nil then sortfn = sortfunctions[ctx.pipe[1]] end
if sortfn then table.sort(ctx.params, sortfn)
else table.sort(ctx.params) end -- i.e. either `false` or `nil`
if sortfn == nil then return context_iterate(ctx, 1) end
return context_iterate(ctx, 2)
end
-- Syntax: #invoke:params|inserting|position|how many|...|pipe to
--[[
library.inserting = function (ctx)
-- NOTE: `ctx.params` might be the original metatable! As a modifier,
-- this function MUST create a copy of it before returning
local idx = tonumber(ctx.pipe[1])
if idx == nil then error(ctx.luaname ..
', ‘inserting’: Position must be a number', 0) end
local len = tonumber(ctx.pipe[2])
if len == nil or len < 1 then error(ctx.luaname ..
', ‘inserting’: The amount must be a number greater than zero', 0) end
local opts = ctx.pipe
local tbl = copy_table_expanded(ctx.params, idx, len)
for key = idx, idx + len - 1 do tbl[key] = opts[key - idx + 3] end
ctx.params = tbl
return context_iterate(ctx, len + 3)
end
]]--
-- Syntax: #invoke:params|imposing|name|value|pipe to
library.imposing = function (ctx)
if ctx.pipe[1] == nil then error(ctx.luaname ..
', ‘imposing’: Missing parameter name to impose', 0) end
local key = ctx.pipe[1]:match'^%s*(.-)%s*$'
ctx.params[tonumber(key) or key] = ctx.pipe[2]
return context_iterate(ctx, 3)
end
-- Syntax: #invoke:params|providing|name|value|pipe to
library.providing = function (ctx)
if ctx.pipe[1] == nil then error(ctx.luaname ..
', ‘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(ctx.luaname ..
', ‘discarding’: Missing parameter name to discard', 0) end
local key = ctx.pipe[1]
local len = tonumber(ctx.pipe[2])
if len == nil then
ctx.params[tonumber(key) or key:match'^%s*(.-)%s*$'] = nil
return context_iterate(ctx, 2)
end
key = tonumber(key)
if key == nil then error(ctx.luaname ..
', ‘discarding’: A range was provided, but the initial parameter name is not numerical', 0) end
if len < 1 then error(ctx.luaname ..
', ‘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
return context_iterate(ctx, 3)
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
library.with_name_matching = function (ctx)
local tbl = ctx.params
local targets, argc = parse_pattern_args(ctx, targets,
'with_name_matching')
local nomatch
for key in pairs(tbl) do
nomatch = true
for _, ptn in ipairs(targets) do
if not ptn[3] then
if string.find(key, ptn[1], 1, ptn[2]) then
nomatch = false
break
end
elseif key == ptn[1] then
nomatch = false
break
end
end
if nomatch then tbl[key] = nil end
end
return context_iterate(ctx, argc)
end
-- Syntax: #invoke:params|with_name_not_matching|target 1|[plain flag 1]
-- |[and]|[target 2]|[plain flag 2]|[and]|[...]|[target N]|[plain
-- flag N]|pipe to
library.with_name_not_matching = function (ctx)
local tbl = ctx.params
local targets, argc = parse_pattern_args(ctx, targets,
'with_name_not_matching')
local yesmatch
for key in pairs(tbl) do
yesmatch = true
for _, ptn in ipairs(targets) do
if ptn[3] then
if key ~= ptn[1] then
yesmatch = false
break
end
elseif 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
-- 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
library.with_value_matching = function (ctx)
local tbl = ctx.params
local targets, argc = parse_pattern_args(ctx, targets,
'with_value_matching')
local nomatch
for key, val in pairs(tbl) do
nomatch = true
for _, ptn in ipairs(targets) do
if ptn[3] then
if val == ptn[1] then
nomatch = false
break
end
elseif 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
-- Syntax: #invoke:params|with_value_not_matching|target 1|[plain flag 1]
-- |[and]|[target 2]|[plain flag 2]|[and]|[...]|[target N]|[plain
-- flag N]|pipe to
library.with_value_not_matching = function (ctx)
local tbl = ctx.params
local targets, argc = parse_pattern_args(ctx, targets,
'with_value_not_matching')
local yesmatch
for key, val in pairs(tbl) do
yesmatch = true
for _, ptn in ipairs(targets) do
if ptn[3] then
if val ~= ptn[1] then
yesmatch = false
break
end
elseif 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
-- Syntax: #invoke:params|trimming_values|pipe to
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
-- Syntax: #invoke:params|mapping_by_calling|template name|[call
-- style]|[let]|[...][number of additional parameters]|[parameter
-- 1]|[parameter 2]|[...]|[parameter N]|pipe to
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 margs, argc, looptype, karg, varg = parse_callback_args(opts, 1,
mapping_styles.values_only)
local model = { title = tname, args = margs }
value_maps[looptype](ctx.params, margs, karg, varg, function ()
return ctx.frame:expandTemplate(model)
end)
return context_iterate(ctx, argc)
end
-- Syntax: #invoke:params|mapping_by_invoking|module name|function
-- name|[call style]|[let]|[...]|[number of additional
-- arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to
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 margs, argc, looptype, karg, varg = parse_callback_args(opts, 2,
mapping_styles.values_only)
local model = { title = 'Module:' .. mname, args = margs }
local mfunc = require(model.title)[fname]
if mfunc == nil then error(ctx.luaname ..
', ‘mapping_by_invoking’: The function ‘' .. fname ..
'’ does not exist', 0) end
value_maps[looptype](ctx.params, margs, karg, varg, function ()
return mfunc(ctx.frame:newChild(model))
end)
return context_iterate(ctx, argc)
end
-- Syntax: #invoke:params|mapping_by_magic|parser function|[call
-- style]|[let]|[...][number of additional arguments]|[argument
-- 1]|[argument 2]|[...]|[argument N]|pipe to
library.mapping_by_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 ..
', ‘mapping_by_magic’: No parser function was provided', 0) end
local margs, argc, looptype, karg, varg = parse_callback_args(opts, 1,
mapping_styles.values_only)
value_maps[looptype](ctx.params, margs, karg, varg, function ()
return ctx.frame:callParserFunction(magic, margs)
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 =
parse_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)
end
-- Syntax: #invoke:params|renaming_by_calling|template name|[call
-- style]|[let]|[...][number of additional parameters]|[parameter
-- 1]|[parameter 2]|[...]|[parameter N]|pipe to
library.renaming_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_by_calling’: No template name was provided', 0) end
local rargs, argc, looptype, karg, varg = parse_callback_args(opts, 1,
mapping_styles.names_only)
local model = { title = tname, args = rargs }
map_names(ctx.params, rargs, karg, varg, looptype, function ()
return ctx.frame:expandTemplate(model)
end)
return context_iterate(ctx, argc)
end
-- Syntax: #invoke:params|renaming_by_invoking|module name|function
-- name|[call style]|[let]|[...]|[number of additional
-- arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to
library.renaming_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_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_by_invoking’: No function name was provided', 0) end
local rargs, argc, looptype, karg, varg = parse_callback_args(opts, 2,
mapping_styles.names_only)
local model = { title = 'Module:' .. mname, args = rargs }
local mfunc = require(model.title)[fname]
if mfunc == nil then error(ctx.luaname ..
', ‘renaming_by_invoking’: The function ‘' .. fname ..
'’ does not exist', 0) end
map_names(ctx.params, rargs, karg, varg, looptype, function ()
return mfunc(ctx.frame:newChild(model))
end)
return context_iterate(ctx, argc)
end
-- Syntax: #invoke:params|renaming_by_magic|parser function|[call
-- style]|[let]|[...][number of additional arguments]|[argument
-- 1]|[argument 2]|[...]|[argument N]|pipe to
library.renaming_by_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 ..
', ‘renaming_by_magic’: No parser function was provided', 0) end
local rargs, argc, looptype, karg, varg = parse_callback_args(opts, 1,
mapping_styles.names_only)
map_names(ctx.params, rargs, karg, varg, looptype, function ()
return ctx.frame:callParserFunction(magic, rargs)
end)
return context_iterate(ctx, argc)
end
-- 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 =
parse_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(ctx.luaname ..
', ‘grouping_by_calling’: No template name was provided', 0) end
local model = { title = tmp }
local tmp, argc = parse_child_args(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
--[[ 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(ctx.luaname ..
', ‘concat_and_call’: No template name was provided', 0) end
remove_numerical_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(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
remove_numerical_keys(opts, 1, 2)
local mfunc = require('Module:' .. mname)[fname]
if mfunc == nil then error(ctx.luaname ..
', ‘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
-- 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named item n=
-- value n]|[...]
library.concat_and_magic = function (ctx)
-- NOTE: `ctx.params` might be the original metatable!
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
remove_numerical_keys(opts, 1, 1)
ctx.text = ctx.frame:callParserFunction(magic, concat_params(ctx))
return false
end
-- Syntax: #invoke:params|value_of|parameter name
library.value_of = function (ctx)
-- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables!
local opts = ctx.pipe
local kstr
if opts[1] ~= nil then kstr = opts[1]:match'^%s*(.*%S)' end
if kstr == nil then error(ctx.luaname ..
', ‘value_of’: No parameter name was provided', 0) end
local knum = tonumber(kstr)
local len = #ctx.params
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
end
-- Syntax: #invoke:params|list
library.list = function (ctx)
-- NOTE: `ctx.pipe` might be the original metatable!
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
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end
-- Syntax: #invoke:params|list_values
library.list_values = function (ctx)
-- NOTE: `ctx.pipe` might be the original metatable!
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
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end
-- Syntax: #invoke:params|for_each|wikitext
library.for_each = function (ctx)
-- NOTE: `ctx.pipe` might be the original metatable!
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
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return 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]|[...]
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
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return 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]|[...]
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
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return 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]|[...]
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
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return 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]|[...]
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
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return 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]|[...]
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
remove_numerical_keys(opts, 1, 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
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return 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]|[...]
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
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end
-- Syntax: #invoke:params|call_for_each_group|template name|[append 1]|[append
-- 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param
-- n=value n]|[...]
library.call_for_each_group = function (ctx)
-- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables!
local opts = ctx.pipe
local tmp
if opts[1] ~= nil then tmp = opts[1]:match'^%s*(.*%S)' end
if tmp == nil then error(ctx.luaname ..
', ‘call_for_each_group’: No template name was provided', 0) end
local model = { title = tmp }
local ccs = ctx.itersep or ''
local nss = 0
local ret = {}
opts = {}
for key, val in pairs(ctx.pipe) do
if type(key) == 'number' then opts[key - 1] = val
else opts[key] = val end
end
ctx.pipe = opts
ctx.params = make_groups(ctx.params)
flush_params(
ctx,
function (gid, group)
for key, val in pairs(opts) do group[key] = val end
group[0] = gid
model.args = group
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
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end
--- ---
--- PUBLIC ENVIRONMENT ---
--- ________________________________ ---
--- ---
--[[ First-position-only modifiers ]]--
---------------------------------------
-- Syntax: #invoke:params|new|pipe to
--[[
static_iface.new = function (frame)
local ctx = context_new()
ctx.frame = frame:getParent()
ctx.pipe = copy_or_ref_table(frame.args, false)
ctx.params = {}
main_loop(ctx, context_iterate(ctx, 1))
return ctx.text
end
]]--
--[[ First-position-only functions ]]--
---------------------------------------
-- Syntax: #invoke:params|self
static_iface.self = function (frame)
return frame:getParent():getTitle()
end
--[[ Public metatable of functions ]]--
---------------------------------------
return setmetatable(static_iface, {
__index = function (iface, _fname_)
local ctx = context_new()
local fname = _fname_:match'^%s*(.*%S)'
if fname == nil then error(ctx.luaname ..
': You must specify a function to call', 0) end
if library[fname] == nil then error(ctx.luaname ..
': The function ‘' .. fname .. '’ does not exist', 0) end
local func = library[fname]
return function (frame)
ctx.frame = frame:getParent()
ctx.pipe = copy_or_ref_table(frame.args,
refpipe[fname])
ctx.params = copy_or_ref_table(ctx.frame.args,
refparams[fname])
main_loop(ctx, func)
return ctx.text
end
end
})