Module:Params: Difference between revisions

From All Skies Encyclopaedia
imported>Grufo
(Add two new functions, `squeezing` and `trimming_values`; general code review)
imported>Grufo
(Add one new function, `all_sorted`; general code review)
Line 83: Line 83:
for key, val in pairs(ctx.pipe) do newargs[key] = val end
for key, val in pairs(ctx.pipe) do newargs[key] = val end
return newargs
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
end


Line 94: Line 130:
library.sequential = function(ctx)
library.sequential = function(ctx)
if ctx.subset == -1 then
if ctx.subset == -1 then
error('The two directives `non-sequential` and `sequential` are in contradiction with each other', 0)
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
end
ctx.iterfunc = ipairs
ctx.iterfunc = ipairs
Line 105: Line 144:
library['non-sequential'] = function(ctx)
library['non-sequential'] = function(ctx)
if ctx.subset == 1 then
if ctx.subset == 1 then
error('The two directives `sequential` and `non-sequential` are in contradiction with each other', 0)
error('The two directives "sequential" and "non-sequential" are in contradiction with each other', 0)
end
end
ctx.iterfunc = pairs
ctx.iterfunc = pairs
ctx.subset = -1
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)
return context_iterate(ctx, 1)
end
end
Line 115: Line 164:
-- See iface.setting()
-- See iface.setting()
library.setting = function(ctx)
library.setting = function(ctx)

local opts = ctx.pipe
local opts = ctx.pipe
local cmd

local cmd = (opts[1] or
if opts[1] ~= nil then
''):gsub('%s+', ''):gsub('/+', '/'):match'^/*(.*[^/])'
cmd = opts[1]:gsub('%s+', ''):gsub('/+', '/'):match'^/*(.*[^/])'

if cmd == nil then
error('No directive was given about what variables to set', 0)
end
end
if cmd == nil then error('No directive was given', 0) end

local target = string.byte('/')
local sep = string.byte('/')
local arglen = 2
local argc = 2
local aggregate = {}
local dest = {}
local varname
local vname
local chr
local chr

for idx = 1, #cmd do
for idx = 1, #cmd do
chr = cmd:byte(idx)
chr = cmd:byte(idx)
if chr == target then
if chr == sep then
for key, val in ipairs(aggregate) do
for key, val in ipairs(dest) do
ctx[val] = opts[arglen]
ctx[val] = opts[argc]
aggregate[key] = nil
dest[key] = nil
end
end
arglen = arglen + 1
argc = argc + 1
else
else
varname = memoryslots[string.char(chr)]
vname = memoryslots[string.char(chr)]
if varname == nil then error('Unknown slot "' ..
if vname == nil then error('Unknown slot "' ..
string.char(chr) .. '"', 0) end
string.char(chr) .. '"', 0) end
table.insert(aggregate, varname)
table.insert(dest, vname)
end
end
end
end
for key, val in ipairs(dest) do ctx[val] = opts[argc] end

return context_iterate(ctx, argc + 1)
for key, val in ipairs(aggregate) do ctx[val] = opts[arglen] end

return context_iterate(ctx, arglen + 1)

end
end


Line 242: Line 283:
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
return context_iterate(ctx, 1)
return context_iterate(ctx, 1)
end
end
Line 265: Line 306:


local opts = ctx.pipe
local opts = ctx.pipe
local tname = opts[1]
local tname


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


Line 283: Line 325:


local opts = ctx.pipe
local opts = ctx.pipe
local mname = opts[1]
local mname
local fname


if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if mname == nil then error('No module name was provided', 0) end
if mname == nil then error('No module name was provided', 0) end
if opts[2] == nil then error('No function name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error('No function name was provided', 0) end

local fname = opts[2]:match'^%s*(.*%S)'


table.remove(opts, 2)
table.remove(opts, 2)
Line 304: Line 347:
library.value_of = function(ctx)
library.value_of = function(ctx)
local opts = ctx.pipe
local opts = ctx.pipe
local keystr = (opts[1] or ''):match'^%s*(.*%S)'
local keystr
if keystr == '' then error('No parameter name was provided', 0) end
if opts[1] ~= nil then keystr = opts[1]:match'^%s*(.*%S)' end
if keystr == nil then error('No parameter name was provided', 0) end
local keynum = tonumber(keystr)
local keynum = tonumber(keystr)
if (
if (
Line 320: Line 364:
-- See iface.list()
-- See iface.list()
library.list = function(ctx)
library.list = function(ctx)

local pps = ctx.itersep or ''
local pps = ctx.itersep or ''
local kvs = ctx.pairsep or ''
local kvs = ctx.pairsep or ''
Line 327: Line 370:
local foo = ctx.ifngiven or ''
local foo = ctx.ifngiven or ''
local ret = ''
local ret = ''
do_for_each_param(

ctx,
if ctx.subset == -1 then
function(key, val)
for key, val in ipairs(ctx.params) do ctx.params[key] = nil end
else
for key, val in ipairs(ctx.params) do
ret = ret .. sep .. key .. kvs .. val
ret = ret .. sep .. key .. kvs .. val
sep = pps
sep = pps
foo = las
foo = las
ctx.params[key] = nil
end
end
end
)

if ctx.subset ~= 1 then
for key, val in pairs(ctx.params) do
ret = ret .. sep .. key .. kvs .. val
sep = pps
foo = las
end
end

return ret .. foo
return ret .. foo

end
end


Line 354: Line 384:
-- See iface.list_values()
-- See iface.list_values()
library.list_values = function(ctx)
library.list_values = function(ctx)

local pps = ctx.itersep or ''
local pps = ctx.itersep or ''
local sep = ctx.header or ''
local sep = ctx.header or ''
Line 360: Line 389:
local foo = ctx.ifngiven or ''
local foo = ctx.ifngiven or ''
local ret = ''
local ret = ''
do_for_each_param(

ctx,
if ctx.subset == -1 then
function(key, val)
for key, val in ipairs(ctx.params) do ctx.params[key] = nil end
else
for key, val in ipairs(ctx.params) do
ret = ret .. sep .. val
ret = ret .. sep .. val
sep = pps
sep = pps
foo = las
foo = las
ctx.params[key] = nil
end
end
end
)

if ctx.subset ~= 1 then
for key, val in pairs(ctx.params) do
ret = ret .. sep .. val
sep = pps
foo = las
end
end

return ret .. foo
return ret .. foo

end
end


Line 387: Line 403:
-- See iface.for_each()
-- See iface.for_each()
library.for_each = function(ctx)
library.for_each = function(ctx)

local pps = ctx.itersep or ''
local pps = ctx.itersep or ''
local las = ctx.footer or ''
local las = ctx.footer or ''
Line 394: Line 409:
local txt = ctx.pipe[1]
local txt = ctx.pipe[1]
local ret = ''
local ret = ''
do_for_each_param(

ctx,
if ctx.subset == -1 then
function(key, val)
for key, val in ipairs(ctx.params) do ctx.params[key] = nil end
else
for key, val in ipairs(ctx.params) do
ret = ret .. sep .. string.gsub(
ret = ret .. sep .. string.gsub(
string.gsub(txt, '%$#', key),
string.gsub(txt, '%$#', key),
Line 406: Line 419:
sep = pps
sep = pps
foo = las
foo = las
ctx.params[key] = nil
end
end
end
)

if ctx.subset ~= 1 then
for key, val in pairs(ctx.params) do
ret = ret .. sep .. string.gsub(
string.gsub(txt, '%$#', key),
'%$@',
val
)
sep = pps
foo = las
end
end

return ret .. foo
return ret .. foo

end
end


Line 431: Line 429:


local opts = ctx.pipe
local opts = ctx.pipe
local tname


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


local ccs = ctx.itersep or ''
local ccs = ctx.itersep or ''
Line 438: Line 438:
local las = ctx.footer or ''
local las = ctx.footer or ''
local foo = ctx.ifngiven or ''
local foo = ctx.ifngiven or ''
local model = { title = opts[1]:match'^%s*(.*%S)', args = opts }
local model = { title = tname, args = opts }
local ret = ''
local ret = ''


table.insert(opts, 1, true)
table.insert(opts, 1, true)


do_for_each_param(
if ctx.subset == -1 then
ctx,
for key, val in ipairs(ctx.params) do ctx.params[key] = nil end
function(key, val)
else
for key, val in ipairs(ctx.params) do
opts[1] = key
opts[1] = key
opts[2] = val
opts[2] = val
Line 452: Line 451:
sep = ccs
sep = ccs
foo = las
foo = las
ctx.params[key] = nil
end
end
end
)

if ctx.subset ~= 1 then
for key, val in pairs(ctx.params) do
opts[1] = key
opts[2] = val
ret = ret .. sep .. ctx.frame:expandTemplate(model)
sep = ccs
foo = las
end
end


return ret .. foo
return ret .. foo
Line 475: Line 463:


local opts = ctx.pipe
local opts = ctx.pipe
local mname
local fname


if opts[1] == nil then error('No module name was provided', 0) end
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if opts[2] == nil then error('No function name was provided', 0) end
if mname == nil then error('No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error('No function name was provided', 0) end


local ccs = ctx.itersep or ''
local ccs = ctx.itersep or ''
Line 483: Line 475:
local las = ctx.footer or ''
local las = ctx.footer or ''
local foo = ctx.ifngiven or ''
local foo = ctx.ifngiven or ''
local model = { title = 'Module:' .. opts[1]:match'^%s*(.*%S)', args = opts }
local model = { title = 'Module:' .. mname, args = opts }
local mfunc = require(model.title)[opts[2]:match'^%s*(.*%S)']
local mfunc = require(model.title)[fname]
local ret = ''
local ret = ''


do_for_each_param(
if ctx.subset == -1 then
ctx,
for key, val in ipairs(ctx.params) do ctx.params[key] = nil end
function(key, val)
else
for key, val in ipairs(ctx.params) do
opts[1] = key
opts[1] = key
opts[2] = val
opts[2] = val
Line 496: Line 487:
sep = ccs
sep = ccs
foo = las
foo = las
ctx.params[key] = nil
end
end
end
)

if ctx.subset ~= 1 then
for key, val in pairs(ctx.params) do
opts[1] = key
opts[2] = val
ret = ret .. sep .. mfunc(ctx.frame:newChild(model))
sep = ccs
foo = las
end
end


return ret .. foo
return ret .. foo
Line 519: Line 499:


local opts = ctx.pipe
local opts = ctx.pipe
local magic


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


local ccs = ctx.itersep or ''
local ccs = ctx.itersep or ''
Line 526: Line 508:
local las = ctx.footer or ''
local las = ctx.footer or ''
local foo = ctx.ifngiven or ''
local foo = ctx.ifngiven or ''
local magic = opts[1]:match'^%s*(.*%S)'
local ret = ''
local ret = ''


table.insert(opts, 1, true)
table.insert(opts, 1, true)


do_for_each_param(
if ctx.subset == -1 then
ctx,
for key, val in ipairs(ctx.params) do ctx.params[key] = nil end
function(key, val)
else
for key, val in ipairs(ctx.params) do
opts[1] = key
opts[1] = key
opts[2] = val
opts[2] = val
Line 540: Line 520:
sep = ccs
sep = ccs
foo = las
foo = las
ctx.params[key] = nil
end
end
end
)

if ctx.subset ~= 1 then
for key, val in pairs(ctx.params) do
opts[1] = key
opts[2] = val
ret = ret .. sep .. ctx.frame:callParserFunction(magic, opts)
sep = ccs
foo = las
end
end


return ret .. foo
return ret .. foo
Line 563: Line 532:


local opts = ctx.pipe
local opts = ctx.pipe
local tname


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


local ccs = ctx.itersep or ''
local ccs = ctx.itersep or ''
Line 570: Line 541:
local las = ctx.footer or ''
local las = ctx.footer or ''
local foo = ctx.ifngiven or ''
local foo = ctx.ifngiven or ''
local model = { title = opts[1]:match'^%s*(.*%S)', args = opts }
local model = { title = tname, args = opts }
local ret = ''
local ret = ''


do_for_each_param(
if ctx.subset == -1 then
ctx,
for key, val in ipairs(ctx.params) do ctx.params[key] = nil end
function(key, val)
else
for key, val in ipairs(ctx.params) do
opts[1] = val
opts[1] = val
ret = ret .. sep .. ctx.frame:expandTemplate(model)
ret = ret .. sep .. ctx.frame:expandTemplate(model)
sep = ccs
sep = ccs
foo = las
foo = las
ctx.params[key] = nil
end
end
end
)

if ctx.subset ~= 1 then
for key, val in pairs(ctx.params) do
opts[1] = val
ret = ret .. sep .. ctx.frame:expandTemplate(model)
sep = ccs
foo = las
end
end


return ret .. foo
return ret .. foo
Line 603: Line 563:


local opts = ctx.pipe
local opts = ctx.pipe
local mname
local fname


if opts[1] == nil then error('No module name was provided', 0) end
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if opts[2] == nil then error('No function name was provided', 0) end
if mname == nil then error('No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error('No function name was provided', 0) end


local ccs = ctx.itersep or ''
local ccs = ctx.itersep or ''
Line 611: Line 575:
local las = ctx.footer or ''
local las = ctx.footer or ''
local foo = ctx.ifngiven or ''
local foo = ctx.ifngiven or ''
local model = { title = 'Module:' .. opts[1]:match'^%s*(.*%S)', args = opts }
local model = { title = 'Module:' .. mname, args = opts }
local mfunc = require(model.title)[opts[2]:match'^%s*(.*%S)']
local mfunc = require(model.title)[fname]
local ret = ''
local ret = ''


table.remove(opts, 1)
table.remove(opts, 1)


do_for_each_param(
if ctx.subset == -1 then
ctx,
for key, val in ipairs(ctx.params) do ctx.params[key] = nil end
function(key, val)
else
for key, val in ipairs(ctx.params) do
opts[1] = val
opts[1] = val
ret = ret .. sep .. mfunc(ctx.frame:newChild(model))
ret = ret .. sep .. mfunc(ctx.frame:newChild(model))
sep = ccs
sep = ccs
foo = las
foo = las
ctx.params[key] = nil
end
end
end
)

if ctx.subset ~= 1 then
for key, val in pairs(ctx.params) do
opts[1] = val
ret = ret .. sep .. mfunc(ctx.frame:newChild(model))
sep = ccs
foo = las
end
end


return ret .. foo
return ret .. foo
Line 647: Line 600:


local opts = ctx.pipe
local opts = ctx.pipe
local magic


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


local ccs = ctx.itersep or ''
local ccs = ctx.itersep or ''
Line 654: Line 609:
local las = ctx.footer or ''
local las = ctx.footer or ''
local foo = ctx.ifngiven or ''
local foo = ctx.ifngiven or ''
local magic = opts[1]:match'^%s*(.*%S)'
local ret = ''
local ret = ''


do_for_each_param(
if ctx.subset == -1 then
ctx,
for key, val in ipairs(ctx.params) do ctx.params[key] = nil end
function(key, val)
else
for key, val in ipairs(ctx.params) do
opts[1] = val
opts[1] = val
ret = ret .. sep .. ctx.frame:callParserFunction(magic, opts)
ret = ret .. sep .. ctx.frame:callParserFunction(magic, opts)
sep = ccs
sep = ccs
foo = las
foo = las
ctx.params[key] = nil
end
end
end
)

if ctx.subset ~= 1 then
for key, val in pairs(ctx.params) do
opts[1] = val
ret = ret .. sep .. ctx.frame:callParserFunction(magic, opts)
sep = ccs
foo = las
end
end


return ret .. foo
return ret .. foo
Line 708: Line 651:
iface['non-sequential'] = function(frame)
iface['non-sequential'] = function(frame)
return context_init(frame, library['non-sequential'], false, false)
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
end



Revision as of 05:38, 15 July 2023

Documentation for this module may be created at Module:Params/doc

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


-- 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('End of arguments reached without a function call', 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



	--[[ Piping piped functions ]]--
	--------------------------------


-- 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('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('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('Left cut must be a number', 0) end
	local rcut = tonumber(ctx.pipe[2])
	if rcut == nil then error('Right cut must be a number', 0) end
	local tbl = ctx.params
	local tlen = #tbl
	if lcut < 0 then lcut = tlen + lcut end
	if rcut < 0 then rcut = tlen + rcut end
	if lcut + rcut > 0 then
		if lcut + rcut >= tlen then
			for key, val in ipairs(tbl) do tbl[key] = nil end
		else
			local last = tlen - rcut + 1
			for idx = tlen, last, -1 do tbl[idx] = nil end
			for _ = 1, lcut, 1 do table.remove(tbl, 1) end
		end
	end
	return context_iterate(ctx, 3)
end


-- See iface.with_name_matching()
library.with_name_matching = function(ctx)
	local ptn = ctx.pipe[1]
	local tbl = ctx.params
	for key, val in pairs(tbl) do
		if not string.find(key, ptn, 1, false) then tbl[key] = nil end
	end
	return context_iterate(ctx, 2)
end


-- See iface.with_name_not_matching()
library.with_name_not_matching = function(ctx)
	local ptn = ctx.pipe[1]
	local tbl = ctx.params
	for key, val in pairs(tbl) do
		if string.find(key, ptn, 1, false) then tbl[key] = nil end
	end
	return context_iterate(ctx, 2)
end


-- See iface.with_value_matching()
library.with_value_matching = function(ctx)
	local ptn = ctx.pipe[1]
	local tbl = ctx.params
	for key, val in pairs(tbl) do
		if not string.find(val, ptn, 1, false) then tbl[key] = nil end
	end
	return context_iterate(ctx, 2)
end


-- See iface.with_value_not_matching()
library.with_value_not_matching = function(ctx)
	local ptn = ctx.pipe[1]
	local tbl = ctx.params
	for key, val in pairs(tbl) do
		if string.find(val, ptn, 1, false) then tbl[key] = nil end
	end
	return context_iterate(ctx, 2)
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



	--[[ Non-piping 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('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('No module name was provided', 0) end
	if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
	if fname == nil then error('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('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]
	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('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('No module name was provided', 0) end
	if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
	if fname == nil then error('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('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('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('No module name was provided', 0) end
	if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
	if fname == nil then error('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('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 = {}



	--[[ Piping interface functions ]]--
	------------------------------------


-- 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|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|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|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|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-piping interface 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