Module:Params

---                                       ---	---     LOCAL ENVIRONMENT                  --- ---   ________________________________    ---	---                                        ---

-- Special user-given keywords (functions and modifiers MUST avoid these names) local mkeywords = { --	['pattern'] = false, ['plain'] = true, ['or'] = 0 }

-- Set directives local memoryslots = { i = 'itersep', p = 'pairsep', h = 'header', f = 'footer', n = 'ifngiven' }

-- The private table of functions local library = {}

-- Return a copy or a reference to a table local function copy_or_ref_table(src, refonly) if refonly then return src end newtab = {} for key, val in pairs(src) do newtab[key] = val end return newtab end

-- Prepare the context local function context_init(frame, funcname, refpipe, refparams) local ctx = {} ctx.iterfunc = pairs ctx.pipe = copy_or_ref_table(frame.args, refpipe) ctx.frame = frame:getParent ctx.params = copy_or_ref_table(ctx.frame.args, refparams) return funcname(ctx) end

-- Move to the next action within the user-given list local function context_iterate(ctx, n_forward) local nextfn if ctx.pipe[n_forward] ~= nil then nextfn = ctx.pipe[n_forward]:match'^%s*(.*%S)' end if nextfn == nil then error('No function name was given', 0) end if library[nextfn] == nil then error('The function "' .. nextfn .. '" does not exist', 0) end for idx = n_forward, 1, -1 do table.remove(ctx.pipe, idx) end return library[nextfn](ctx) end

-- Concatenate the numerical keys from the table of parameters to the numerical -- keys from the table of options; non-numerical keys from the table of options -- will prevail over colliding non-numerical keys from the table of parameters local function concat_params(ctx) local shift = table.maxn(ctx.pipe) local newargs = {} if ctx.subset == 1 then -- We need only the sequence for key, val in ipairs(ctx.params) do			newargs[key + shift] = val end else if ctx.subset == -1 then for key, val in ipairs(ctx.params) do				ctx.params[key] = nil end end for key, val in pairs(ctx.params) do			if type(key) == 'number' then newargs[key + shift] = val else newargs[key] = val end end end for key, val in pairs(ctx.pipe) do newargs[key] = val end return newargs end

local function do_for_each_param(ctx, fn) local tbl = ctx.params if ctx.subset == 1 then for key, val in ipairs(tbl) do fn(key, val) end return end if ctx.subset == -1 then for key, val in ipairs(tbl) do tbl[key] = nil end end if ctx.dosort then local nums = {} local words = {} for key, val in pairs(tbl) do			if type(key) == 'number' then nums[#nums + 1] = key else words[#words + 1] = key end

end table.sort(nums) table.sort(words) for idx = 1, #nums do fn(nums[idx], tbl[nums[idx]]) end for idx = 1, #words do fn(words[idx], tbl[words[idx]]) end return end if ctx.subset ~= -1 then for key, val in ipairs(tbl) do			fn(key, val) tbl[key] = nil end end for key, val in pairs(tbl) do fn(key, val) end end

-- Parse the arguments of the `with_*_matching` class of modifiers local function parse_match_args(opts, ptns, fname) local state = 0 local cnt = 1 local keyw for _, val in ipairs(opts) do		if state == 0 then ptns[#ptns + 1] = { val, false } state = -1 else keyw = val:match'^%s*(.*%S)' if keyw == nil or mkeywords[keyw] == nil then break else state = mkeywords[keyw] if state ~= 0 then ptns[#ptns][2] = state end end end cnt = cnt + 1 end if state == 0 then error(fname .. ': No pattern was given', 0) end return cnt end

-- Piped modifiers --

-- See iface.sequential library.sequential = function(ctx) if ctx.subset == -1 then error('The two directives "non-sequential" and "sequential" are in contradiction with each other', 0) end if ctx.dosort then error('The "all_sorted" directive is redundant when followed by "sequential"', 0) end ctx.iterfunc = ipairs ctx.subset = 1 return context_iterate(ctx, 1) end

-- See iface['non-sequential'] library['non-sequential'] = function(ctx) if ctx.subset == 1 then error('The two directives "sequential" and "non-sequential" are in contradiction with each other', 0) end ctx.iterfunc = pairs ctx.subset = -1 return context_iterate(ctx, 1) end

-- See iface.all_sorted library.all_sorted = function(ctx) if ctx.subset == 1 then error('The "all_sorted" directive is redundant after "sequential"', 0) end ctx.dosort = true return context_iterate(ctx, 1) end

-- See iface.setting library.setting = function(ctx) local opts = ctx.pipe local cmd if opts[1] ~= nil then cmd = opts[1]:gsub('%s+', ''):gsub('/+', '/'):match'^/*(.*[^/])' end if cmd == nil then error('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('setting: Unknown slot "' ..				string.char(chr) .. '"', 0) end table.insert(dest, vname) end end for key, val in ipairs(dest) do ctx[val] = opts[argc] end return context_iterate(ctx, argc + 1) end

-- See iface.squeezing library.squeezing = function(ctx) local tbl = ctx.params local store = {} local indices = {} for key, val in pairs(tbl) do		if type(key) == 'number' then indices[#indices + 1] = key store[key] = val tbl[key] = nil end end table.sort(indices) for idx = 1, #indices do tbl[idx] = store[indices[idx]] end return context_iterate(ctx, 1) end

-- See iface.cutting library.cutting = function(ctx) local lcut = tonumber(ctx.pipe[1]) if lcut == nil then error('cutting: Left cut must be a number', 0) end local rcut = tonumber(ctx.pipe[2]) if rcut == nil then error('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, val in ipairs(tbl) do tbl[key] = nil end tot = len else for idx = len - rcut + 1, len, 1 do tbl[idx] = nil end for idx = 1, lcut, 1 do tbl[idx] = nil end end for key, val in pairs(tbl) do			if type(key) == 'number' and key > 0 then if key > len then cache[key - tot] = val else cache[key - lcut] = val end tbl[key] = nil end end for key, val in pairs(cache) do tbl[key] = val end end return context_iterate(ctx, 3) end

-- See iface.with_name_matching library.with_name_matching = function(ctx) local tbl = ctx.params local patterns = {} local argc = parse_match_args(ctx.pipe, patterns, 'with_name_matching') local nomatch for key in pairs(tbl) do		nomatch = true for _, ptn in ipairs(patterns) do			if string.find(key, ptn[1], 1, ptn[2]) then nomatch = false break end end if nomatch then tbl[key] = nil end end return context_iterate(ctx, argc) end

-- See iface.with_name_not_matching library.with_name_not_matching = function(ctx) local tbl = ctx.params local patterns = {} local argc = parse_match_args(ctx.pipe, patterns,		'with_name_not_matching') local yesmatch for key in pairs(tbl) do		yesmatch = true for _, ptn in ipairs(patterns) do			if not string.find(key, ptn[1], 1, ptn[2]) then yesmatch = false break end end if yesmatch then tbl[key] = nil end end return context_iterate(ctx, argc) end

-- See iface.with_value_matching library.with_value_matching = function(ctx) local tbl = ctx.params local patterns = {} local argc = parse_match_args(ctx.pipe, patterns,		'with_value_matching') local nomatch for key, val in pairs(tbl) do		nomatch = true for _, ptn in ipairs(patterns) do			if string.find(val, ptn[1], 1, ptn[2]) then nomatch = false break end end if nomatch then tbl[key] = nil end end return context_iterate(ctx, argc) end

-- See iface.with_value_not_matching library.with_value_not_matching = function(ctx) local tbl = ctx.params local patterns = {} local argc = parse_match_args(ctx.pipe, patterns,		'with_value_not_matching') local yesmatch for key, val in pairs(tbl) do		yesmatch = true for _, ptn in ipairs(patterns) do			if not string.find(val, ptn[1], 1, ptn[2]) then yesmatch = false break end end if yesmatch then tbl[key] = nil end end return context_iterate(ctx, argc) end

-- See iface.trimming_values library.trimming_values = function(ctx) local tbl = ctx.params for key, val in pairs(tbl) do tbl[key] = val:match'^%s*(.-)%s*$' end return context_iterate(ctx, 1) end

-- Piped functions --

-- See iface.count library.count = function(ctx) local count = 0 for _ in ctx.iterfunc(ctx.params) do count = count + 1 end if ctx.subset == -1 then count = count - #ctx.params end return count end

-- See iface.concat_and_call library.concat_and_call = function(ctx) local opts = ctx.pipe local tname if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end if tname == nil then error('concat_and_call: No template name was provided', 0) end table.remove(opts, 1) return ctx.frame:expandTemplate{ title = tname, args = concat_params(ctx) } end

-- See iface.concat_and_invoke library.concat_and_invoke = function(ctx) local opts = ctx.pipe local mname local fname if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end if mname == nil then error('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('concat_and_invoke: No function name was provided', 0) end table.remove(opts, 2) table.remove(opts, 1) return require('Module:' .. mname)[fname](ctx.frame:newChild{		title = 'Module:' .. fname,		args = concat_params(ctx)	}) end

-- See iface.value_of library.value_of = function(ctx) local opts = ctx.pipe local keystr if opts[1] ~= nil then keystr = opts[1]:match'^%s*(.*%S)' end if keystr == nil then error('value_of: No parameter name was provided', 0) end local keynum = tonumber(keystr) if (		ctx.subset == -1 and keynum ~= nil and #ctx.params >= keynum	) or (		ctx.subset == 1 and (keynum == nil or #ctx.params < keynum)	) then return (ctx.ifngiven or '') end local val = ctx.params[keynum or keystr] if val == nil then return (ctx.ifngiven or '') end return (ctx.header or '') .. val .. (ctx.footer or '') end

-- See iface.list library.list = function(ctx) local pps = ctx.itersep or '' local kvs = ctx.pairsep or '' local sep = ctx.header or '' local las = ctx.footer or '' local foo = ctx.ifngiven or '' local ret = '' do_for_each_param(		ctx,		function(key, val)			ret = ret .. sep .. key .. kvs .. val			sep = pps			foo = las		end	) return ret .. foo end

-- See iface.list_values library.list_values = function(ctx) local pps = ctx.itersep or '' local sep = ctx.header or '' local las = ctx.footer or '' local foo = ctx.ifngiven or '' local ret = '' do_for_each_param(		ctx,		function(key, val)			ret = ret .. sep .. val			sep = pps			foo = las		end	) return ret .. foo end

-- See iface.for_each library.for_each = function(ctx) local pps = ctx.itersep or '' local las = ctx.footer or '' local sep = ctx.header or '' local foo = ctx.ifngiven or '' local txt = ctx.pipe[1] or '' local ret = '' do_for_each_param(		ctx,		function(key, val)			ret = ret .. sep .. string.gsub( string.gsub(txt, '%$#', key), '%$@',				val )			sep = pps			foo = las		end	) return ret .. foo end

-- See iface.call_for_each library.call_for_each = function(ctx)

local opts = ctx.pipe local tname

if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end if tname == nil then error('call_for_each: No template name was provided', 0) end

local ccs = ctx.itersep or '' local sep = ctx.header or '' local las = ctx.footer or '' local foo = ctx.ifngiven or '' local model = { title = tname, args = opts } local ret = ''

table.insert(opts, 1, true)

do_for_each_param(		ctx,		function(key, val)			opts[1] = key			opts[2] = val			ret = ret .. sep .. ctx.frame:expandTemplate(model)			sep = ccs			foo = las		end	)

return ret .. foo

end

-- See iface.invoke_for_each library.invoke_for_each = function(ctx)

local opts = ctx.pipe local mname local fname

if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end if mname == nil then error('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('invoke_for_each: No function name was provided', 0) end

local ccs = ctx.itersep or '' local sep = ctx.header or '' local las = ctx.footer or '' local foo = ctx.ifngiven or '' local model = { title = 'Module:' .. mname, args = opts } local mfunc = require(model.title)[fname] local ret = ''

do_for_each_param(		ctx,		function(key, val)			opts[1] = key			opts[2] = val			ret = ret .. sep .. mfunc(ctx.frame:newChild(model))			sep = ccs			foo = las		end	)

return ret .. foo

end

-- See iface.magic_for_each library.magic_for_each = function(ctx)

local opts = ctx.pipe local magic

if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end if magic == nil then error('magic_for_each: No parser function was provided', 0) end

local ccs = ctx.itersep or '' local sep = ctx.header or '' local las = ctx.footer or '' local foo = ctx.ifngiven or '' local ret = ''

table.insert(opts, 1, true)

do_for_each_param(		ctx,		function(key, val)			opts[1] = key			opts[2] = val			ret = ret .. sep .. ctx.frame:callParserFunction(magic, opts)			sep = ccs			foo = las		end	)

return ret .. foo

end

-- See iface.call_for_each_value library.call_for_each_value = function(ctx)

local opts = ctx.pipe local tname

if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end if tname == nil then error('call_for_each_value: No template name was provided', 0) end

local ccs = ctx.itersep or '' local sep = ctx.header or '' local las = ctx.footer or '' local foo = ctx.ifngiven or '' local model = { title = tname, args = opts } local ret = ''

do_for_each_param(		ctx,		function(key, val)			opts[1] = val			ret = ret .. sep .. ctx.frame:expandTemplate(model)			sep = ccs			foo = las		end	)

return ret .. foo

end

-- See iface.invoke_for_each_value library.invoke_for_each_value = function(ctx)

local opts = ctx.pipe local mname local fname

if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end if mname == nil then error('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('invoke_for_each_value: No function name was provided', 0) end

local ccs = ctx.itersep or '' local sep = ctx.header or '' local las = ctx.footer or '' local foo = ctx.ifngiven or '' local model = { title = 'Module:' .. mname, args = opts } local mfunc = require(model.title)[fname] local ret = ''

table.remove(opts, 1)

do_for_each_param(		ctx,		function(key, val)			opts[1] = val			ret = ret .. sep .. mfunc(ctx.frame:newChild(model))			sep = ccs			foo = las		end	)

return ret .. foo

end

-- See iface.magic_for_each_value library.magic_for_each_value = function(ctx)

local opts = ctx.pipe local magic

if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end if magic == nil then error('magic_for_each_value: No parser function was provided', 0) end

local ccs = ctx.itersep or '' local sep = ctx.header or '' local las = ctx.footer or '' local foo = ctx.ifngiven or '' local ret = ''

do_for_each_param(		ctx,		function(key, val)			opts[1] = val			ret = ret .. sep .. ctx.frame:callParserFunction(magic, opts)			sep = ccs			foo = las		end	)

return ret .. foo

end

---                                       ---	---     PUBLIC ENVIRONMENT                 --- ---   ________________________________    ---	---                                        ---

-- The public table of functions local iface = {}

-- Non-piped modifiers --

-- Syntax: #invoke:params|sequential|function name iface.sequential = function(frame) return context_init(frame, library.sequential, false, false) end

-- Syntax: #invoke:params|non-sequential|function name iface['non-sequential'] = function(frame) return context_init(frame, library['non-sequential'], false, false) end

-- Syntax: #invoke:params|sort|function name iface.all_sorted = function(frame) return context_init(frame, library.all_sorted, false, false) end

-- Syntax: #invoke:params|setting|directives|...|function name iface.setting = function(frame) return context_init(frame, library.setting, false, false) end

-- Syntax: #invoke:params|squeezing|function name iface.squeezing = function(frame) return context_init(frame, library.squeezing, false, false) end

-- Syntax: #invoke:params|cutting|left cut|right cut|function name iface.cutting = function(frame) return context_init(frame, library.cutting, false, false) end

-- Syntax: #invoke:params|with_name_matching|pattern 1|[plain flag 1]|[or] --           |[pattern 2]|[plain flag 2]|[or]|[...]|[pattern N]|[plain flag --           N]|function name iface.with_name_matching = function(frame) return context_init(frame, library.with_name_matching, false, false) end

-- Syntax: #invoke:params|with_name_not_matching|pattern 1|[plain flag 1] --           |[and]|[pattern 2]|[plain flag 2]|[and]|[...]|[pattern N]|[plain --           flag N]|function name iface.with_name_not_matching = function(frame) return context_init(frame, library.with_name_not_matching, false,		false) end

-- Syntax: #invoke:params|with_value_matching|pattern 1|[plain flag 1]|[or] --           |[pattern 2]|[plain flag 2]|[or]|[...]|[pattern N]|[plain flag --           N]|function name iface.with_value_matching = function(frame) return context_init(frame, library.with_value_matching, false, false) end

-- Syntax: #invoke:params|with_value_not_matching|pattern 1|[plain flag 1] --           |[and]|[pattern 2]|[plain flag 2]|[and]|[...]|[pattern N]|[plain --           flag N]|function name iface.with_value_not_matching = function(frame) return context_init(frame, library.with_value_not_matching, false,		false) end

-- Syntax: #invoke:params|trimming_values|function name iface.trimming_values = function(frame) return context_init(frame, library.trimming_values, false, false) end

-- Non-piped functions --

-- Syntax: #invoke:params|count iface.count = function(frame) return context_init(frame, library.count, true, true) end

-- Syntax: #invoke:args|concat_and_call|template name|[prepend 1]|[prepend 2] --           |[...]|[item n]|[named item 1=value 1]|[...]|[named item n=value --           n]|[...] iface.concat_and_call = function(frame) return context_init(frame, library.concat_and_call, false, true) end

-- Syntax: #invoke:args|concat_and_invoke|module name|function name|[prepend --           1]|[prepend 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named --           item n=value n]|[...] iface.concat_and_invoke = function(frame) return context_init(frame, library.concat_and_invoke, false, true) end

-- Syntax: #invoke:params|value_of|parameter name iface.value_of = function(frame) return context_init(frame, library.value_of, true, true) end

-- Syntax: #invoke:params|list iface.list = function(frame) return context_init(frame, library.list, true, false) end

-- Syntax: #invoke:params|list_values iface.list_values = function(frame) return context_init(frame, library.list_values, true, false) end

-- Syntax: #invoke:params|for_each|wikitext iface.for_each = function(frame) return context_init(frame, library.for_each, true, false) end

-- Syntax: #invoke:params|call_for_each|template name|[append 1]|[append 2] --           |[...]|[append n]|[named param 1=value 1]|[...]|[named param --           n=value n]|[...] iface.call_for_each = function(frame) return context_init(frame, library.call_for_each, false, false) end

-- Syntax: #invoke:params|invoke_for_each|module name|module function|[append --           1]|[append 2]|[...]|[append n]|[named param 1=value 1]|[...] --           |[named param n=value n]|[...] iface.invoke_for_each = function(frame) return context_init(frame, library.invoke_for_each, false, false) end

-- Syntax: #invoke:params|magic_for_each|parser function|[append 1]|[append 2] --           |[...]|[append n]|[named param 1=value 1]|[...]|[named param --           n=value n]|[...] iface.magic_for_each = function(frame) return context_init(frame, library.magic_for_each, false, false) end

-- Syntax: #invoke:params|call_for_each_value|template name|[append 1]|[append --           2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param --           n=value n]|[...] iface.call_for_each_value = function(frame) return context_init(frame, library.call_for_each_value, false, false) end

-- Syntax: #invoke:params|invoke_for_each_value|module name|[append 1]|[append --           2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param --           n=value n]|[...] iface.invoke_for_each_value = function(frame) return context_init(frame, library.invoke_for_each_value, false, false) end

-- Syntax: #invoke:params|magic_for_each_value|parser function|[append 1] --           |[append 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named --           param n=value n]|[...] iface.magic_for_each_value = function(frame) return context_init(frame, library.magic_for_each_value, false, false) end

return iface