Module:Params

E Vicipaedia

Nemo hanc paginam annexam adhuc scripsit. Interea, si alias linguas intellegis, eandem paginam sermone barbaro legere potes. Vel, si tibi placet, hanc paginam alio sermone scripta ad lingua Latinam vertere potes!


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


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


-- Memory slots
local memoryslots = {
	i = 'itersep',
	l = 'lastsep',
	p = 'pairsep',
	h = 'header',
	f = 'footer',
	n = 'ifngiven'
}


-- Directive that can only be invoked as first arguments
local firstposonly = {
	new = 'modifier',
	self = 'function',
}


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


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


-- Prepare the context
local function context_new(frame)
	local ctx = {}
	ctx.luaname = 'Module:Params'	--[[ or `frame:getTitle()` ]]--
	ctx.iterfunc = pairs
	ctx.frame = frame:getParent()
	return ctx
end


-- Prepare the context
local function context_init(frame, funcname, refpipe, refparams)
	local ctx = context_new(frame)
	ctx.pipe = copy_or_ref_table(frame.args, refpipe)
	ctx.params = copy_or_ref_table(ctx.frame.args, refparams)
	return funcname(ctx)
end


-- Move to the next action within the user-given list
local function context_iterate(ctx, n_forward)
	local nextfn
	if ctx.pipe[n_forward] ~= nil then
		nextfn = ctx.pipe[n_forward]:match'^%s*(.*%S)'
	end
	if nextfn == nil then
		error(ctx.luaname .. ': You must specify a function to call', 0)
	end
	if library[nextfn] == nil then
		if firstposonly[nextfn] == nil then
			error(ctx.luaname .. ': The function ‘' .. nextfn ..
				'’ does not exist', 0)
		else
			error(ctx.luaname .. ': The ‘' .. nextfn .. '’ ' ..
				firstposonly[nextfn] ..
				' can only be invoked in first position', 0)
		end
	end
	for idx = n_forward, 1, -1 do table.remove(ctx.pipe, idx) end
	return library[nextfn](ctx)
end


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


-- Helper function for `string.gsub()` (for managing zero-padded numbers)
function zero_padded(str)
	return ("%03d%s"):format(#str, str)
end


-- Helper function for `table.sort()` (for natural sorting)
function natural_sort(var1, var2)
	return tostring(var1):gsub("%d+", zero_padded) <
		tostring(var2):gsub("%d+", zero_padded)
end


-- Flush the parameters by calling a custom function for each value (after this
-- function has been invoked `ctx.params` will no longer be usable)
local function flush_params(ctx, fn)
	local tbl = ctx.params
	if ctx.subset == 1 then
		for key, val in ipairs(tbl) do fn(key, val) end
		return
	end
	if ctx.subset == -1 then
		for key, val in ipairs(tbl) do tbl[key] = nil end
	end
	if ctx.dosort then
		local nums = {}
		local words = {}
		local nlen = 0
		local wlen = 0
		for key, val in pairs(tbl) do
			if type(key) == 'number' then
				nlen = nlen + 1
				nums[nlen] = key
			else
				wlen = wlen + 1
				words[wlen] = key
			end

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


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


-- Remove numerical arguments (not necessarily sequential ones)
function purge_args(tbl, offset, length)
	local cache = {}
	local pshift
	local nshift
	if offset > 0 then
		pshift = length
		nshift = 0
	elseif offset + length > 1 then
		pshift = offset + length - 1
		nshift = 1 - offset
	else
		pshift = 0
		nshift = length
	end
	for key, val in pairs(tbl) do
		if type(key) == 'number' then
			if key > 0 and key >= offset then
				if key >= offset + length then
					cache[key - pshift] = val
				end
				tbl[key] = nil
			elseif key < 1 and key < offset + length then
				if key < offset then
					cache[key + nshift] = val
				end
				tbl[key] = nil
			end
		end
	end
	for key, val in pairs(cache) do tbl[key] = val end
end



	--[[ Library's modifiers ]]--
	--------------------------------


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


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


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


-- See iface.setting()
library.setting = function(ctx)
	local opts = ctx.pipe
	local cmd
	if opts[1] ~= nil then
		cmd = opts[1]:gsub('%s+', ''):gsub('/+', '/'):match'^/*(.*[^/])'
	end
	if cmd == nil then error(ctx.luaname .. ', ‘setting’: No directive was given', 0) end
	local sep = string.byte('/')
	local argc = 2
	local dest = {}
	local vname
	local chr
	for idx = 1, #cmd do
		chr = cmd:byte(idx)
		if chr == sep then
			for key, val in ipairs(dest) do
				ctx[val] = opts[argc]
				dest[key] = nil
			end
			argc = argc + 1
		else
			vname = memoryslots[string.char(chr)]
			if vname == nil then error(ctx.luaname .. ', ‘setting’: Unknown slot "' ..
				string.char(chr) .. '"', 0) end
			table.insert(dest, vname)
		end
	end
	for key, val in ipairs(dest) do ctx[val] = opts[argc] end
	return context_iterate(ctx, argc + 1)
end


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


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


-- See iface.cropping()
library.cropping = function(ctx)
	local lcut = tonumber(ctx.pipe[1])
	if lcut == nil then error(ctx.luaname .. ', ‘cropping’: Left crop must be a number', 0) end
	local rcut = tonumber(ctx.pipe[2])
	if rcut == nil then error(ctx.luaname .. ', ‘cropping’: Right crop must be a number', 0) end
	local tbl = ctx.params
	local nmin
	local nmax
	for key in pairs(tbl) do
		if type(key) == 'number' then
			if nmin == nil then
				nmin = key
				nmax = key
			elseif key > nmax then nmax = key
			elseif key < nmin then nmin = key
			end
		end
	end
	if nmin ~= nil then
		local len = nmax - nmin + 1
		if lcut < 0 then lcut = len + lcut end
		if rcut < 0 then rcut = len + rcut end
		if lcut + rcut - len > -1 then
			for key in pairs(tbl) do
				if type(key) == 'number' then tbl[key] = nil end
			end
		elseif lcut + rcut > 0 then
			for idx = nmax - rcut + 1, nmax, 1 do tbl[idx] = nil end
			for idx = nmin, nmin + lcut - 1, 1 do tbl[idx] = nil end
			local lshift = nmin + lcut - 1
			if lshift > 0 then
				for idx = lshift + 1, nmax, 1 do
					tbl[idx - lshift] = tbl[idx]
					tbl[idx] = nil
				end
			end
		end
	end
	return context_iterate(ctx, 3)
end


-- See iface.purging()
library.purging = function(ctx)
	local first = tonumber(ctx.pipe[1])
	if first == nil then error(ctx.luaname .. ', ‘purging’: Start offset must be a number', 0) end
	local size = tonumber(ctx.pipe[2])
	if size == nil then error(ctx.luaname .. ', ‘purging’: Length must be a number', 0) end
	local tbl = ctx.params
	if size < 1 then
		size = size + table.maxn(tbl)
		if first > size then return context_iterate(ctx, 3) end
		size = size - first + 1
	end
	purge_args(tbl, first, size)
	return context_iterate(ctx, 3)
end


-- See iface.backpurging()
library.backpurging = function(ctx)
	local last = tonumber(ctx.pipe[1])
	if last == nil then error(ctx.luaname .. ', ‘backpurging’: Start offset must be a number', 0) end
	local size = tonumber(ctx.pipe[2])
	if size == nil then error(ctx.luaname .. ', ‘backpurging’: Length must be a number', 0) end
	local first
	local tbl = ctx.params
	if size > 0 then
		first = last - size + 1
	else
		for key in pairs(tbl) do
			if type(key) == 'number' and (first == nil or
				key < first) then first = key end
		end
		if first == nil then return context_iterate(ctx, 3) end
		first = first - size
		if last < first then return context_iterate(ctx, 3) end
		size = last - first + 1
	end
	purge_args(ctx.params, first, size)
	return context_iterate(ctx, 3)
end


-- See iface.reversing()
library.reversing = function(ctx)
	local tbl = ctx.params
	local 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


-- See iface.mirroring()
library.mirroring = function(ctx)
	local tbl = ctx.params
	local shift = #tbl + 1
	if shift < 2 then return library.reversing(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


-- See iface.swapping()
--[[
library.swapping = function(ctx)
	local tbl = ctx.params
	local cache = {}
	local nsize = 0
	local tmp
	for key in pairs(tbl) do
		if type(key) == 'number' then
			nsize = nsize + 1
			cache[nsize] = key
		end
	end
	table.sort(cache)
	for idx = math.floor(nsize / 2), 1, -1 do
		tmp = tbl[cache[idx] ]
		tbl[cache[idx] ] = tbl[cache[nsize - idx + 1] ]
		tbl[cache[nsize - idx + 1] ] = tmp
	end
	return context_iterate(ctx, 1)
end
]]--


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


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


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


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


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


-- See iface.mapping_by_calling()
library.mapping_by_calling = function(ctx)
	local opts = ctx.pipe
	local tname
	if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
	if tname == nil then error(ctx.luaname .. ', ‘mapping_by_calling’: No template name was provided', 0) end
	local nargs
	local margs = {}
	local tmp = tonumber(opts[2])
	if tmp == nil then
		nargs = 1
	elseif tmp < 1 then
		nargs = 2
	else
		nargs = tmp + 2
		for idx = 3, nargs do margs[idx] = opts[idx] end
	end
	local model = { title = tname, args = margs }
	local tbl = ctx.params
	for key, val in pairs(tbl) do
		margs[1] = key
		margs[2] = val
		tbl[key] = ctx.frame:expandTemplate(model)
	end
	return context_iterate(ctx, nargs + 1)
end


-- See iface.mapping_by_invoking()
library.mapping_by_invoking = function(ctx)
	local opts = ctx.pipe
	local mname
	local fname
	if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
	if mname == nil then error(ctx.luaname .. ', ‘mapping_by_invoking’: No module name was provided', 0) end
	if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
	if fname == nil then error(ctx.luaname .. ', ‘mapping_by_invoking’: No function name was provided', 0) end
	local nargs
	local margs = {}
	local tmp = tonumber(opts[3])
	if tmp == nil then
		nargs = 2
	elseif tmp < 1 then
		nargs = 3
	else
		nargs = tmp + 3
		for idx = 4, nargs do margs[idx - 1] = opts[idx] end
	end
	local model = { title = 'Module:' .. mname, args = margs }
	local mfunc = require(model.title)[fname]
	local tbl = ctx.params
	for key, val in pairs(tbl) do
		margs[1] = key
		margs[2] = val
		tbl[key] = mfunc(ctx.frame:newChild(model))
	end
	return context_iterate(ctx, nargs + 1)
end


-- See iface.mapping_blindly_by_calling()
library.mapping_blindly_by_calling = function(ctx)
	local opts = ctx.pipe
	local tname
	if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
	if tname == nil then error(ctx.luaname .. ', ‘mapping_blindly_by_calling’: No template name was provided', 0) end
	local nargs
	local margs = {}
	local tmp = tonumber(opts[2])
	if tmp == nil then
		nargs = 1
	elseif tmp < 1 then
		nargs = 2
	else
		nargs = tmp + 2
		for idx = 3, nargs do margs[idx - 1] = opts[idx] end
	end
	local model = { title = tname, args = margs }
	local tbl = ctx.params
	for key, val in pairs(tbl) do
		margs[1] = val
		tbl[key] = ctx.frame:expandTemplate(model)
	end
	return context_iterate(ctx, nargs + 1)
end


-- See iface.mapping_blindly_by_invoking()
library.mapping_blindly_by_invoking = function(ctx)
	local opts = ctx.pipe
	local mname
	local fname
	if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
	if mname == nil then error(ctx.luaname .. ', ‘mapping_blindly_by_invoking’: No module name was provided', 0) end
	if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
	if fname == nil then error(ctx.luaname .. ', ‘mapping_blindly_by_invoking’: No function name was provided', 0) end
	local nargs
	local margs = {}
	local tmp = tonumber(opts[3])
	if tmp == nil then
		nargs = 2
	elseif tmp < 1 then
		nargs = 3
	else
		nargs = tmp + 3
		for idx = 4, nargs do margs[idx - 2] = opts[idx] end
	end
	local model = { title = 'Module:' .. mname, args = margs }
	local mfunc = require(model.title)[fname]
	local tbl = ctx.params
	for key, val in pairs(tbl) do
		margs[1] = val
		tbl[key] = mfunc(ctx.frame:newChild(model))
	end
	return context_iterate(ctx, nargs + 1)
end


-- See iface.renaming_blindly_by_calling()
library.renaming_blindly_by_calling = function(ctx)
	local opts = ctx.pipe
	local tname
	if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
	if tname == nil then error(ctx.luaname .. ', ‘renaming_blindly_by_calling’: No template name was provided', 0) end
	local nargs
	local margs = {}
	local tmp = tonumber(opts[2])
	if tmp == nil then
		nargs = 1
	elseif tmp < 1 then
		nargs = 2
	else
		nargs = tmp + 2
		for idx = 3, nargs do margs[idx - 1] = opts[idx] end
	end
	local model = { title = tname, args = margs }
	local tbl = {}
	for key, val in pairs(ctx.params) do
		margs[1] = key
		tmp = ctx.frame:expandTemplate(model)
		tbl[tonumber(tmp) or tmp] = val
	end
	ctx.params = tbl
	return context_iterate(ctx, nargs + 1)
end


-- See iface.renaming_blindly_by_invoking()
library.renaming_blindly_by_invoking = function(ctx)
	local opts = ctx.pipe
	local mname
	local fname
	if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
	if mname == nil then error(ctx.luaname .. ', ‘renaming_blindly_by_invoking’: No module name was provided', 0) end
	if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
	if fname == nil then error(ctx.luaname .. ', ‘renaming_blindly_by_invoking’: No function name was provided', 0) end
	local nargs
	local margs = {}
	local tmp = tonumber(opts[3])
	if tmp == nil then
		nargs = 2
	elseif tmp < 1 then
		nargs = 3
	else
		nargs = tmp + 3
		for idx = 4, nargs do margs[idx - 2] = opts[idx] end
	end
	local model = { title = 'Module:' .. mname, args = margs }
	local mfunc = require(model.title)[fname]
	local tbl = {}
	for key, val in pairs(ctx.params) do
		margs[1] = key
		tmp = mfunc(ctx.frame:newChild(model))
		tbl[tonumber(tmp) or tmp] = val
	end
	ctx.params = tbl
	return context_iterate(ctx, nargs + 1)
end



	--[[ Library's functions ]]--
	------------------------------------


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


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


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


-- See iface.concat_and_magic()
library.concat_and_magic = function(ctx)
	local opts = ctx.pipe
	local magic
	if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
	if magic == nil then error(ctx.luaname .. ', ‘concat_and_magic’: No parser function was provided', 0) end
	table.remove(opts, 1)
	return ctx.frame:callParserFunction(magic, concat_params(ctx))
end


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


-- See iface.list()
library.list = function(ctx)
	local kvs = ctx.pairsep or ''
	local pps = ctx.itersep or ''
	local ret = {}
	local nss = 0
	flush_params(
		ctx,
		function(key, val)
			ret[nss + 1] = pps
			ret[nss + 2] = key
			ret[nss + 3] = kvs
			ret[nss + 4] = val
			nss = nss + 4
		end
	)
	if nss > 0 then
		if nss > 4 and ctx.lastsep ~= nil then
			ret[nss - 3] = ctx.lastsep
		end
		ret[1] = ctx.header or ''
		if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
		return table.concat(ret)
	end
	return ctx.ifngiven or ''
end


-- See iface.list_values()
library.list_values = function(ctx)
	local pps = ctx.itersep or ''
	local ret = {}
	local nss = 0
	flush_params(
		ctx,
		function(key, val)
			ret[nss + 1] = pps
			ret[nss + 2] = val
			nss = nss + 2
		end
	)
	if nss > 0 then
		if nss > 2 and ctx.lastsep ~= nil then
			ret[nss - 1] = ctx.lastsep
		end
		ret[1] = ctx.header or ''
		if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
		return table.concat(ret)
	end
	return ctx.ifngiven or ''
end


-- See iface.for_each()
library.for_each = function(ctx)
	local txt = ctx.pipe[1] or ''
	local pps = ctx.itersep or ''
	local ret = {}
	local nss = 0
	flush_params(
		ctx,
		function(key, val)
			ret[nss + 1] = pps
			ret[nss + 2] = txt:gsub('%$#', key):gsub('%$@', val)
			nss = nss + 2
		end
	)
	if nss > 0 then
		if nss > 2 and ctx.lastsep ~= nil then
			ret[nss - 1] = ctx.lastsep
		end
		ret[1] = ctx.header or ''
		if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
		return table.concat(ret)
	end
	return ctx.ifngiven or ''
end


-- See iface.call_for_each()
library.call_for_each = function(ctx)
	local opts = ctx.pipe
	local tname
	if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
	if tname == nil then error(ctx.luaname .. ', ‘call_for_each’: No template name was provided', 0) end
	local model = { title = tname, args = opts }
	local ccs = ctx.itersep or ''
	local ret = {}
	local nss = 0
	table.insert(opts, 1, true)
	flush_params(
		ctx,
		function(key, val)
			opts[1] = key
			opts[2] = val
			ret[nss + 1] = ccs
			ret[nss + 2] = ctx.frame:expandTemplate(model)
			nss = nss + 2
		end
	)
	if nss > 0 then
		if nss > 2 and ctx.lastsep ~= nil then
			ret[nss - 1] = ctx.lastsep
		end
		ret[1] = ctx.header or ''
		if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
		return table.concat(ret)
	end
	return ctx.ifngiven or ''
end


-- See iface.invoke_for_each()
library.invoke_for_each = function(ctx)
	local opts = ctx.pipe
	local mname
	local fname
	if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
	if mname == nil then error(ctx.luaname .. ', ‘invoke_for_each’: No module name was provided', 0) end
	if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
	if fname == nil then error(ctx.luaname .. ', ‘invoke_for_each’: No function name was provided', 0) end
	local model = { title = 'Module:' .. mname, args = opts }
	local mfunc = require(model.title)[fname]
	local ccs = ctx.itersep or ''
	local ret = {}
	local nss = 0
	flush_params(
		ctx,
		function(key, val)
			opts[1] = key
			opts[2] = val
			ret[nss + 1] = ccs
			ret[nss + 2] = mfunc(ctx.frame:newChild(model))
			nss = nss + 2
		end
	)
	if nss > 0 then
		if nss > 2 and ctx.lastsep ~= nil then
			ret[nss - 1] = ctx.lastsep
		end
		ret[1] = ctx.header or ''
		if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
		return table.concat(ret)
	end
	return ctx.ifngiven or ''
end


-- See iface.magic_for_each()
library.magic_for_each = function(ctx)
	local opts = ctx.pipe
	local magic
	if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
	if magic == nil then error(ctx.luaname .. ', ‘magic_for_each’: No parser function was provided', 0) end
	local ccs = ctx.itersep or ''
	local ret = {}
	local nss = 0
	table.insert(opts, 1, true)
	flush_params(
		ctx,
		function(key, val)
			opts[1] = key
			opts[2] = val
			ret[nss + 1] = ccs
			ret[nss + 2] = ctx.frame:callParserFunction(magic,
				opts)
			nss = nss + 2
		end
	)
	if nss > 0 then
		if nss > 2 and ctx.lastsep ~= nil then
			ret[nss - 1] = ctx.lastsep
		end
		ret[1] = ctx.header or ''
		if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
		return table.concat(ret)
	end
	return ctx.ifngiven or ''
end


-- See iface.call_for_each_value()
library.call_for_each_value = function(ctx)
	local opts = ctx.pipe
	local tname
	if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
	if tname == nil then error(ctx.luaname .. ', ‘call_for_each_value’: No template name was provided', 0) end
	local model = { title = tname, args = opts }
	local ccs = ctx.itersep or ''
	local ret = {}
	local nss = 0
	flush_params(
		ctx,
		function(key, val)
			opts[1] = val
			ret[nss + 1] = ccs
			ret[nss + 2] = ctx.frame:expandTemplate(model)
			nss = nss + 2
		end
	)
	if nss > 0 then
		if nss > 2 and ctx.lastsep ~= nil then
			ret[nss - 1] = ctx.lastsep
		end
		ret[1] = ctx.header or ''
		if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
		return table.concat(ret)
	end
	return ctx.ifngiven or ''
end


-- See iface.invoke_for_each_value()
library.invoke_for_each_value = function(ctx)
	local opts = ctx.pipe
	local mname
	local fname
	if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
	if mname == nil then error(ctx.luaname .. ', ‘invoke_for_each_value’: No module name was provided', 0) end
	if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
	if fname == nil then error(ctx.luaname .. ', ‘invoke_for_each_value’: No function name was provided', 0) end
	local model = { title = 'Module:' .. mname, args = opts }
	local mfunc = require(model.title)[fname]
	local ccs = ctx.itersep or ''
	local ret = {}
	local nss = 0
	table.remove(opts, 1)
	flush_params(
		ctx,
		function(key, val)
			opts[1] = val
			ret[nss + 1] = ccs
			ret[nss + 2] = mfunc(ctx.frame:newChild(model))
			nss = nss + 2
		end
	)
	if nss > 0 then
		if nss > 2 and ctx.lastsep ~= nil then
			ret[nss - 1] = ctx.lastsep
		end
		ret[1] = ctx.header or ''
		if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
		return table.concat(ret)
	end
	return ctx.ifngiven or ''

end


-- See iface.magic_for_each_value()
library.magic_for_each_value = function(ctx)
	local opts = ctx.pipe
	local magic
	if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
	if magic == nil then error(ctx.luaname .. ', ‘magic_for_each_value’: No parser function was provided', 0) end
	local ccs = ctx.itersep or ''
	local ret = {}
	local nss = 0
	flush_params(
		ctx,
		function(key, val)
			opts[1] = val
			ret[nss + 1] = ccs
			ret[nss + 2] = ctx.frame:callParserFunction(magic,
				opts)
			nss = nss + 2
		end
	)
	if nss > 0 then
		if nss > 2 and ctx.lastsep ~= nil then
			ret[nss - 1] = ctx.lastsep
		end
		ret[1] = ctx.header or ''
		if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
		return table.concat(ret)
	end
	return ctx.ifngiven or ''
end



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


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



	--[[ Modifiers ]]--
	------------------------------------


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


-- Syntax:  #invoke:params|mapping_by_calling|template name|[number of
--            additional arguments]|[argument 1]|[argument 2]|[...]|[argument
--            N]|function name
iface.mapping_by_calling = function(frame)
	return context_init(frame, library.mapping_by_calling, false, false)
end


-- Syntax:  #invoke:params|mapping_by_invoking|module name|function
--            name|[number of additional arguments]|[argument 1]|[argument
--            2]|[...]|[argument N]|function name
iface.mapping_by_invoking = function(frame)
	return context_init(frame, library.mapping_by_invoking, false, false)
end


-- Syntax:  #invoke:params|mapping_blindly_by_calling|template
--            name|[number of additional arguments]|[argument 1]|[argument
--            2]|[...]|[argument N]|function name
iface.mapping_blindly_by_calling = function(frame)
	return context_init(frame, library.mapping_blindly_by_calling, false,
		false)
end


-- Syntax:  #invoke:params|mapping_blindly_by_invoking|module
--            name|function name|[number of additional arguments]|[argument
--            1]|[argument 2]|[...]|[argument N]|function name
iface.mapping_blindly_by_invoking = function(frame)
	return context_init(frame, library.mapping_blindly_by_invoking, false,
		false)
end


-- Syntax:  #invoke:params|renaming_blindly_by_calling|template
--            name|[number of additional arguments]|[argument 1]|[argument
--            2]|[...]|[argument N]|function name
iface.renaming_blindly_by_calling = function(frame)
	return context_init(frame, library.renaming_blindly_by_calling, false,
		false)
end


-- Syntax:  #invoke:params|renaming_blindly_by_invoking|module
--            name|function name|[number of additional arguments]|[argument
--            1]|[argument 2]|[...]|[argument N]|function name
iface.renaming_blindly_by_invoking = function(frame)
	return context_init(frame, library.renaming_blindly_by_invoking, false,
		false)
end




	--[[ First position only ]]--
	----------------------------------------


-- Syntax:  #invoke:params|new|function name
--[[
iface.new = function(frame)
	local ctx = context_new(frame)
	ctx.pipe = copy_or_ref_table(frame.args, false)
	ctx.params = {}
	return context_iterate(ctx, 1)
end
]]--




	--[[ Functions ]]--
	----------------------------------------


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


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


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


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


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


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


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


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


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


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


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


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


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


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




	--[[ First position only ]]--
	----------------------------------------


-- Syntax:  #invoke:params|self
iface.self = function(frame)
	return frame:getParent():getTitle()
end


return iface