Module:Params: Difference between revisions

From All Skies Encyclopaedia
imported>Grufo
(Make the module transparent when invoking other modules or calling other templates)
imported>Grufo
m (Comments only)
Line 1: Line 1:
--- ---
--[[
--- LOCAL ENVIRONMENT ---

--- ________________________________ ---
/*\
--- ---
|*|
|*| LOCAL ENVIRONMENT
|*| ________________________________
\*/

]]--




Line 503: Line 498:




--- ---
--[[
--- PUBLIC ENVIRONMENT ---

--- ________________________________ ---
/*\
--- ---
|*|
|*| PUBLIC ENVIRONMENT
|*| ________________________________
\*/

]]--





Revision as of 01:05, 10 July 2023

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

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


-- Default separator between different arguments
local arg_sep = '|'


-- Default separator between keys and values
local keyval_sep = '='


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


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


-- Check if an argument is a positive number or otherwise throw an error
local function positive_number_or_error(input, label)
	if input == nil then return 0 end
	local ret = tonumber(input)
	if ret == nil then error(label .. ' must be a number', 0) end
	if ret < 0 then error(label .. ' cannot be negative', 0) end
	return ret
end


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


-- Move to the next action within the user-given list
local function context_iterate(context, n_forward)
	local nextfn = context.pipe[n_forward]:match'^%s*(.*%S)'
	if nextfn == nil then
		error('You must specify a function to 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(context.pipe, idx) end
	return library[nextfn](context)
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(context)
	local shift = table.maxn(context.pipe) 
	local newargs = {}
	if context.subset == 1 then
		-- We need only the sequence
		for key, val in ipairs(context.params) do
			newargs[key + shift] = val
		end
	else
		for key, val in pairs(context.params) do
			if type(key) == "number" then
				newargs[key + shift] = val
			else
				newargs[key] = val
			end
		end
	end
	for key, val in pairs(context.pipe) do newargs[key] = val end
	return newargs
end



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


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


-- See iface.'non-sequential']()
library['non-sequential'] = function(context)
	if context.subset == 1 then
		error('The two directives `sequential` and `non-sequential` are in contradiction with each other', 0)
	end
	context.iterfunc = pairs
	context.subset = -1
	for key, val in ipairs(context.params) do context.params[key] = nil end
	return context_iterate(context, 1)
end


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


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


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


-- See iface.with_value_not_matching()
library.with_value_not_matching = function(context)
	for key, val in context.iterfunc(context.params) do
		if string.find(val, context.pipe[1], 1, false) then
			context.params[key] = nil
		end
	end
	return context_iterate(context, 2)
end


-- See iface.trimmed()
library.trimmed = function(context)
	local ltrim = positive_number_or_error(context.pipe[1], 'Left trim')
	local rtrim = positive_number_or_error(context.pipe[2], 'Right trim')
	if context.subset == -1 then return context_iterate(context, 3) end
	if ltrim + rtrim > 0 then
		if ltrim + rtrim >= #context.params then
			for key, val in ipairs(context.params) do table.remove(context.params, 1) end
		else
			for _ = 1, ltrim, 1 do
				if table.remove(context.params, 1) == nil then break end
			end
			local last = #context.params - rtrim + 1
			for _ = #context.params, last, -1 do
				if table.remove(context.params, last) == nil then break end
			end
		end
	end
	return context_iterate(context, 3)
end



	--[[ Non-piping piped functions ]]--
	------------------------------------


-- See iface.count()
library.count = function(context)
	local count = 0
	for _ in context.iterfunc(context.params) do count = count + 1 end
	return count
end



-- See iface.list()
library.list = function(context)

	local opts = context.pipe
	local aas
	local kvs
	local las
	local hea
	local sep = ''
	local foo = ''

	if opts[1] ~= nil then aas = opts[1] else aas = arg_sep end
	if opts[2] ~= nil then kvs = opts[2] else kvs = keyval_sep end
	if opts[3] ~= nil then hea = opts[3] else hea = '' end
	if opts[4] ~= nil then las = opts[4] else las = '' end

	local ret = ''

	if context.subset ~= -1 then
		for key, val in ipairs(context.params) do
			ret = ret .. hea .. sep .. key .. kvs .. val
			sep = aas
			hea = ''
			foo = las
			context.params[key] = nil
		end
	end

	if context.subset ~= 1 then
		for key, val in pairs(context.params) do
			ret = ret .. hea .. sep .. key .. kvs .. val
			sep = aas
			hea = ''
			foo = las
		end
	end

	return ret .. foo

end


-- See iface.list_values()
library.list_values = function(context)

	local opts = context.pipe
	local aas
	local las
	local hea
	local sep = ''
	local foo = ''

	if opts[1] ~= nil then aas = opts[1] else aas = arg_sep end
	if opts[2] ~= nil then hea = opts[2] else hea = '' end
	if opts[3] ~= nil then las = opts[3] else las = '' end

	local ret = ''

	if context.subset ~= -1 then
		for key, val in ipairs(context.params) do
			ret = ret .. hea .. sep .. val
			sep = aas
			hea = ''
			foo = las
			context.params[key] = nil
		end
	end

	if context.subset ~= 1 then
		for key, val in pairs(context.params) do
			ret = ret .. hea .. sep .. val
			sep = aas
			hea = ''
			foo = las
		end
	end

	return ret .. foo

end


-- See iface.for_each()
library.for_each = function(context)

	local las
	local hea
	local txt = context.pipe[1]
	local foo = ''

	if txt == nil then return '' end
	if context.pipe[2] ~= nil then hea = context.pipe[2] else hea = '' end
	if context.pipe[3] ~= nil then las = context.pipe[3] else las = '' end

	local ret = ''

	if context.subset ~= -1 then
		for key, val in ipairs(context.params) do
			ret = ret .. hea .. string.gsub(
				string.gsub(txt, '%$#', key),
				'%$@',
				val
			)
			hea = ''
			foo = las
			context.params[key] = nil
		end
	end

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

	return ret .. foo

end


-- See iface.concat_and_call()
library.concat_and_call = function(context)

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

	if tname == nil then error('No template name was provided', 0) end

	table.remove(opts, 1)

	return context.frame:expandTemplate{
		title = tname,
		args = concat_params(context)
	}

end


-- See iface.concat_and_invoke()
library.concat_and_invoke = function(context)

	local opts = context.pipe
	local mname = opts[1]
	local fname = opts[2]

	if mname == nil then error('No module name was provided', 0) 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](context.frame:newChild{
		title = 'Module:' .. fname,
		args = concat_params(context)
	})

end


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

	local opts = context.pipe

	if opts[1] == nil then error('No template name was provided', 0) end

	local ret = ''
	local chd = { title = opts[1], args = opts }

	table.insert(opts, 1, true)

	if context.subset ~= -1 then
		for key, val in ipairs(context.params) do
			opts[1] = key
			opts[2] = val
			ret = ret .. context.frame:expandTemplate(chd)
			context.params[key] = nil
		end
	end

	if context.subset ~= 1 then
		for key, val in pairs(context.params) do
			opts[1] = key
			opts[2] = val
			ret = ret .. context.frame:expandTemplate(chd)
		end
	end

	return ret

end


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

	local opts = context.pipe
	local mname = opts[1]
	local fname = opts[2]

	if mname == nil then error('No module name was provided', 0) end
	if fname == nil then error('No function name was provided', 0) end

	local ret = ''
	local chd = { title = 'Module:' .. mname, args = opts }
	local mfuncs = require(chd.title)

	if context.subset ~= -1 then
		for key, val in ipairs(context.params) do
			opts[1] = key
			opts[2] = val
			ret = ret .. mfuncs[fname](context.frame:newChild(chd))
			context.params[key] = nil
		end
	end

	if context.subset ~= 1 then
		for key, val in pairs(context.params) do
			opts[1] = key
			opts[2] = val
			ret = ret .. mfuncs[fname](context.frame:newChild(chd))
		end
	end

	return ret

end


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

	local opts = context.pipe

	if opts[1] == nil then error('No template name was provided', 0) end

	local ret = ''
	local chd = { title = opts[1], args = opts }

	if context.subset ~= -1 then
		for key, val in ipairs(context.params) do
			opts[1] = val
			ret = ret .. context.frame:expandTemplate(chd)
			context.params[key] = nil
		end
	end

	if context.subset ~= 1 then
		for key, val in pairs(context.params) do
			opts[1] = val
			ret = ret .. context.frame:expandTemplate(chd)
		end
	end

	return ret

end


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

	local opts = context.pipe
	local mname = opts[1]
	local fname = opts[2]

	if mname == nil then error('No module name was provided', 0) end
	if fname == nil then error('No function name was provided', 0) end

	local ret = ''
	local chd = { title = 'Module:' .. mname, args = opts }
	local mfuncs = require(chd.title)

	table.remove(opts, 1)

	if context.subset ~= -1 then
		for key, val in ipairs(context.params) do
			opts[1] = val
			ret = ret .. mfuncs[fname](context.frame:newChild(chd))
			context.params[key] = nil
		end
	end

	if context.subset ~= 1 then
		for key, val in pairs(context.params) do
			opts[1] = val
			ret = ret .. mfuncs[fname](context.frame:newChild(chd))
		end
	end

	return ret

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, true, true)
end


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


-- Syntax:  #invoke:params|with_name_matching|pattern|function name
iface.with_name_matching = function(frame)
	return context_init(frame, library.with_name_matching, true, true)
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, true, true)
end


-- Syntax:  #invoke:params|with_value_matching|pattern|function name
iface.with_value_matching = function(frame)
	return context_init(frame, library.with_value_matching, true, true)
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, true, true)
end


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



	--[[ Non-piping interface functions ]]--
	----------------------------------------


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


-- Syntax:  #invoke:params|list|[argument separator]|[key-value
--            separator]|[header]|[footer]
iface.list = function(frame)
	return context_init(frame, library.list, false, true)
end


-- Syntax:  #invoke:params|list_values|[argument separator]|[header]|[footer]
iface.list_values = function(frame)
	return context_init(frame, library.list_values, false, true)
end


-- Syntax:  #invoke:params|for_each|wikitext|[header]|[footer]
iface.for_each = function(frame)
	return context_init(frame, library.for_each, false, 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, true, false)
end


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


-- Syntax:  #invoke:params|invoke_for_each_value|module name|module function
--            |[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, true, true)
end


return iface