Diferencia entre revisiones de «Módulo:Argumentos»

De Hispanopedia
imported>ColdMindBRS777
Sin resumen de edición
Supremo (discusión | contribs.)
m 1 revisión importada
 
(No se muestran 15 ediciones intermedias de 10 usuarios)
Línea 1: Línea 1:
-- This module provides easy processing of arguments passed to Scribunto from
-- #invoke. It is intended for use by other Lua modules, and should not be
-- called from #invoke directly.
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local z = {}
local z = {}


-- Generate four different tidyVal functions, so that we don't have to check the
function z.obtenerArgumentos(frame)
-- options every time we call it.
if frame.args[1] then
return frame.args
end


local function tidyValDefault(key, val)
return frame:getParent().args
if type(val) == 'string' then
val = val:match('^%s*(.-)%s*$')
if val == '' then
return nil
else
return val
end
else
return val
end
end
end


local function tidyValTrimOnly(key, val)
function z.obtenerArgumentosConValor(frame)
if type(val) == 'string' then
if frame == mw.getCurrentFrame() then
return val:match('^%s*(.-)%s*$')
argumentos = frame:getParent().args
else
else
return val
argumentos = frame.args or frame
end
end
return require('Módulo:Tablas').copiarElementosConValor(argumentos)
end
end


local function tidyValRemoveBlanksOnly(key, val)
-- Obtiene los argumentos con valores de la plantilla en minúsculas y con las
if type(val) == 'string' then
-- tildes removidas, en caso de que las tenga de forma que sea más sencillo
if val:find('%S') then
-- trabajar con las distintas variantes en los módulos.
return val
--
else
-- Nota: En caso de que haya parámetros duplicados tras la normalización solo
return nil
-- se mantendrá el último valor procesado por la función.
--
-- Parámetros de entrada:
--    frame: El marco utilizado por el módulo
--
-- Parámetros de salida:
--    argumentosNormalizados: los argumentos con valor y nombre normalizado
--    argumentosDuplicados: si la plantilla tiene varias veces el mismo
--        argumento tras la normalización o no
function z.obtenerArgumentosConValorNormalizados(frame)
local argumentos = z.obtenerArgumentosConValor(frame)
local argumentosNormalizados = {}
local nombreNormalizado
local argumentosDuplicados = false
for nombre, valor in pairs(argumentos) do
nombreNormalizado = nombre
nombreNormalizado = mw.ustring.lower( nombreNormalizado )
nombreNormalizado = string.gsub(nombreNormalizado, "[á]", "a")
nombreNormalizado = string.gsub(nombreNormalizado, "[é]", "e")
nombreNormalizado = string.gsub(nombreNormalizado, "[í]", "i")
nombreNormalizado = string.gsub(nombreNormalizado, "[ó]", "o")
nombreNormalizado = string.gsub(nombreNormalizado, "[úü]", "u")
if argumentosNormalizados[nombreNormalizado] then
argumentosDuplicados = true
end
end
else
argumentosNormalizados[nombreNormalizado] = valor
return val
end
end
return argumentosNormalizados, argumentosDuplicados
end
end


local function tidyValNoChange(key, val)
--[[
return val
@name obtenerTablaDeArgumentos
end
@global args
 
@param frame
local function matchesTitle(given, title)
@return table
local tp = type( given )
@descr Obtiene una tabla de argumentos tomando los parámetros recibidos
return (tp == 'string' or tp == 'number') and mw.title.new( given ).prefixedText == title
tando desde la plantilla como desde la invocación de un módulo.
end
En caso de duplicado tiene preferencia el valor de la invocación.
 
Por ejemplo:
local translate_mt = { __index = function(t, k) return k end }
con la plantilla: {{Plantilla |campo=valor |key=clave }}
 
y la invocación: {{#invoke:Módulo |key=value }}
function z.obtenerArgumento(frame, options)
se obtiene: { ['campo'] = 'valor', ['key'] = 'value' }
checkType('obtenerArgumento', 1, frame, 'table', true)
--]]
checkType('obtenerArgumento', 2, options, 'table', true)
function z.obtenerTablaDeArgumentos(frame)
frame = frame or {}
-- global args
options = options or {}
args = {}
 
local function paramMerge(orig, copy)
--[[
local data = {}
-- Set up argument translation.
for key, val in pairs(orig) do
--]]
data[key] = val
options.translate = options.translate or {}
end
if getmetatable(options.translate) == nil then
for key, val in pairs(copy) do
setmetatable(options.translate, translate_mt)
data[key] = val
end
if options.backtranslate == nil then
options.backtranslate = {}
for k,v in pairs(options.translate) do
options.backtranslate[v] = k
end
end
return data
end
end
if options.backtranslate and getmetatable(options.backtranslate) == nil then
if frame then
setmetatable(options.backtranslate, {
-- parentArgs = frame:getParent().args or {}
__index = function(t, k)
if type(frame.getParent) == 'function' then
if options.translate[k] ~= k then
return nil
else
return k
end
end
})
end
 
 
--[[
-- Get the argument tables. If we were passed a valid frame object, get the
-- frame arguments (fargs) and the parent frame arguments (pargs), depending
-- on the options set and on the parent frame's availability. If we weren't
-- passed a valid frame object, we are being called from another Lua module
-- or from the debug console, so assume that we were passed a table of args
-- directly, and assign it to a new variable (luaArgs).
--]]
local fargs, pargs, luaArgs
if type(frame.args) == 'table' and type(frame.getParent) == 'function' then
if options.wrappers then
--[[
-- The wrappers option makes Module:Arguments look up arguments in
-- either the frame argument table or the parent argument table, but
-- not both. This means that users can use either the #invoke syntax
-- or a wrapper template without the loss of performance associated
-- with looking arguments up in both the frame and the parent frame.
-- Module:Arguments will look up arguments in the parent frame
-- if it finds the parent frame's title in options.wrapper;
-- otherwise it will look up arguments in the frame object passed
-- to getArgs.
--]]
local parent = frame:getParent()
local parent = frame:getParent()
if not parent then
if parent then
fargs = frame.args
args = paramMerge(args, parent.args)
else
local title = parent:getTitle():gsub('/sandbox$', '')
local found = false
if matchesTitle(options.wrappers, title) then
found = true
elseif type(options.wrappers) == 'table' then
for _,v in pairs(options.wrappers) do
if matchesTitle(v, title) then
found = true
break
end
end
end
 
-- We test for false specifically here so that nil (the default) acts like true.
if found or options.frameOnly == false then
pargs = parent.args
end
if not found or options.parentOnly == false then
fargs = frame.args
end
end
else
-- options.wrapper isn't set, so check the other options.
if not options.parentOnly then
fargs = frame.args
end
if not options.frameOnly then
local parent = frame:getParent()
pargs = parent and parent.args or nil
end
end
end
end
if options.parentFirst then
-- invokeArgs = frame.args or frame or {}
fargs, pargs = pargs, fargs
if type(frame.args) == 'table' then
args = paramMerge(args, frame.args)
elseif type(frame) == 'table' then
args = paramMerge(args, frame)
end
end
else
luaArgs = frame
end
end
return args
end


-- Set the order of precedence of the argument tables. If the variables are
--[[
-- nil, nothing will be added to the table, which is how we avoid clashes
@name obtenerValorDeArgumentos
-- between the frame/parent args and the Lua args.
@global args
local argTables = {fargs}
@param list
argTables[#argTables + 1] = pargs
@return string or nil
argTables[#argTables + 1] = luaArgs
@descr Obtiene el primer argumento válido desde una tabla de parámetros.
 
Esta tabla de parámetros es una lista que contiene los nombres
--[[
de los argumentos u otras tablas con las funciones para obtenerlos.
-- Generate the tidyVal function. If it has been specified by the user, we
Parámetros:
-- use that; if not, we choose one of four functions depending on the
con los argumentos: { ['campo'] = 'valor', ['key'] = 'value' }
-- options chosen. This is so that we don't have to call the options table
y usando la llamada: obtenerValorDeArgumentos{'dato', 'campo', 'key'}
-- every time the function is called.
se obtiene el valor: 'valor'
--]]
pues 'dato' no es un argumento y 'campo' es el primero encontrado
local tidyVal = options.valueFunc
Funciones:
if tidyVal then
también se puede llamar con una función de la forma
if type(tidyVal) ~= 'function' then
obtenerValorDeArgumentos{'dato', { obtenerDato, '1', '2' }}
error(
de forma que si el argumento 'dato' no existe se llama a la función
"bad value assigned to option 'valueFunc'"
obtenerDato('1', '2')
.. '(function expected, got '
--]]
.. type(tidyVal)
function z.obtenerValorDeArgumentos(list)
.. ')',
-- global args
2
local lang = mw.language.getContentLanguage()
)
local err, key
end
if type(list) == 'number' then
elseif options.trim ~= false then
key = args[list]
if options.removeBlanks ~= false then
elseif type(list) == 'string' then
tidyVal = tidyValDefault
key = args[list] or args[lang:ucfirst(list)]
else
elseif type(list) == 'table' then
tidyVal = tidyValTrimOnly
for num, val in ipairs(list) do
end
if type(val) == 'string' or type(val) == 'number' then
else
key = z.obtenerValorDeArgumentos(val)
if options.removeBlanks ~= false then
elseif type(val) == 'function' then
tidyVal = tidyValRemoveBlanksOnly
err, key = pcall(val)
else
if err ~= true then
tidyVal = tidyValNoChange
key = nil
end
end
end
elseif type(val) == 'table' then
 
if val[1] and type(val[1]) == 'function' then
--[[
err, key = pcall(val[1], unpack(val, 2))
-- Set up the args, metaArgs and nilArgs tables. args will be the one
if err ~= true then
-- accessed from functions, and metaArgs will hold the actual arguments. Nil
key = nil
-- arguments are memoized in nilArgs, and the metatable connects all of them
-- together.
--]]
local args, metaArgs, nilArgs, metatable = {}, {}, {}, {}
setmetatable(args, metatable)
 
local function mergeArgs(tables)
--[[
-- Accepts multiple tables as input and merges their keys and values
-- into one table. If a value is already present it is not overwritten;
-- tables listed earlier have precedence. We are also memoizing nil
-- values, which can be overwritten if they are 's' (soft).
--]]
for _, t in ipairs(tables) do
for key, val in pairs(t) do
if metaArgs[key] == nil and nilArgs[key] ~= 'h' then
local tidiedVal = tidyVal(key, val)
if tidiedVal == nil then
nilArgs[key] = 's'
else
metaArgs[key] = tidiedVal
end
end
end
end
end
end
end
if key ~= nil and key ~= '' then
end
return key -- break
 
--[[
-- Define metatable behaviour. Arguments are memoized in the metaArgs table,
-- and are only fetched from the argument tables once. Fetching arguments
-- from the argument tables is the most resource-intensive step in this
-- module, so we try and avoid it where possible. For this reason, nil
-- arguments are also memoized, in the nilArgs table. Also, we keep a record
-- in the metatable of when pairs and ipairs have been called, so we do not
-- run pairs and ipairs on the argument tables more than once. We also do
-- not run ipairs on fargs and pargs if pairs has already been run, as all
-- the arguments will already have been copied over.
--]]
 
metatable.__index = function (t, key)
--[[
-- Fetches an argument when the args table is indexed. First we check
-- to see if the value is memoized, and if not we try and fetch it from
-- the argument tables. When we check memoization, we need to check
-- metaArgs before nilArgs, as both can be non-nil at the same time.
-- If the argument is not present in metaArgs, we also check whether
-- pairs has been run yet. If pairs has already been run, we return nil.
-- This is because all the arguments will have already been copied into
-- metaArgs by the mergeArgs function, meaning that any other arguments
-- must be nil.
--]]
if type(key) == 'string' then
key = options.translate[key]
end
local val = metaArgs[key]
if val ~= nil then
return val
elseif metatable.donePairs or nilArgs[key] then
return nil
end
for _, argTable in ipairs(argTables) do
local argTableVal = tidyVal(key, argTable[key])
if argTableVal ~= nil then
metaArgs[key] = argTableVal
return argTableVal
end
end
nilArgs[key] = 'h'
return nil
end
 
metatable.__newindex = function (t, key, val)
-- This function is called when a module tries to add a new value to the
-- args table, or tries to change an existing value.
if type(key) == 'string' then
key = options.translate[key]
end
if options.readOnly then
error(
'could not write to argument table key "'
.. tostring(key)
.. '"; the table is read-only',
2
)
elseif options.noOverwrite and args[key] ~= nil then
error(
'could not write to argument table key "'
.. tostring(key)
.. '"; overwriting existing arguments is not permitted',
2
)
elseif val == nil then
--[[
-- If the argument is to be overwritten with nil, we need to erase
-- the value in metaArgs, so that __index, __pairs and __ipairs do
-- not use a previous existing value, if present; and we also need
-- to memoize the nil in nilArgs, so that the value isn't looked
-- up in the argument tables if it is accessed again.
--]]
metaArgs[key] = nil
nilArgs[key] = 'h'
else
metaArgs[key] = val
end
end
 
local function translatenext(invariant)
local k, v = next(invariant.t, invariant.k)
invariant.k = k
if k == nil then
return nil
elseif type(k) ~= 'string' or not options.backtranslate then
return k, v
else
local backtranslate = options.backtranslate[k]
if backtranslate == nil then
-- Skip this one. This is a tail call, so this won't cause stack overflow
return translatenext(invariant)
else
return backtranslate, v
end
end
end
end
end
end
 
return key
metatable.__pairs = function ()
-- Called when pairs is run on the args table.
if not metatable.donePairs then
mergeArgs(argTables)
metatable.donePairs = true
end
return translatenext, { t = metaArgs }
end
 
local function inext(t, i)
-- This uses our __index metamethod
local v = t[i + 1]
if v ~= nil then
return i + 1, v
end
end
 
metatable.__ipairs = function (t)
-- Called when ipairs is run on the args table.
return inext, t, 0
end
 
return args
end
 
function z.obtenerArgumentosConValor(frame)
    if frame == mw.getCurrentFrame() then
        argumentos = frame:getParent().args
    else
        argumentos = frame.args or frame
    end
   
    return require('Módulo:Tablas').copiarElementosConValor(argumentos)
end
end


return arguments
return z

Revisión actual - 10:49 25 oct 2023

Este módulo provee un procesamiento fácil a los argumentos que pasan de #invoke. Es un metamódulo, pensado para ser usado por otros módulos, y no debería ser llamado directamente desde #invoke. Entre sus características se incluyen:

  • Eliminar espacios en blanco al principio y final de los valores (no implementado todavía)
  • Eliminar parámetros vacíos (no implementado todavía)
  • Los argumentos pueden ser pasados por el marco actual y el marco padre a la vez (no implementado todavía)
  • Los argumentos pueden ser pasados por otro módulo Lua o desde la consola de depuración.
  • La mayoría de las características pueden ser personalizadas.

Uso básico

Para empezar, se debe cargar el módulo. Contiene una función, llamada obtenerArgumentos.

local dameArgs = require('Módulo:Argumentos').obtenerArgumentos

En el escenario más básico, se puede usar obtenerArgumentos dentro de la función principal (usualmente main). La variable args es una tabla que contiene los argumentos de #invoke.

local dameArgs = require('Módulo:Argumentos').obtenerArgumentos
local p = {}

function p.main(marco)
	local args = dameArgs(marco)
	-- El código principal del módulo vas acá
end

return p

Sin embargo, se recomienda usar una función solo para procesar los argumentos de #invoke. Esto significa que si se llama al módulo desde otro módulo Lua, no es necesario tener un objeto marco disponible, mejorando el rendimiento.

local dameArgs = require('Módulo:Argumentos').obtenerArgumentos
local p = {}

function p.main(marco)
	local args = dameArgs(marco)
	return p._main(args)
end

function p._main(args)
	-- El código principal del módulo vas acá
end

return p

Si se quiere múltiples funciones que usen los argumentos, y que también sean accesibles desde #invoke, puede usarse una función envolvente.

local dameArgs = require('Módulo:Argumentos').obtenerArgumentos

local function hazFuncInvoke(fn)
	return function (marco)
		local args = dameArgs(marco)
		return p[fn](args)
	end
end

local p = {}

p.func1 = hazFuncInvoke('_func1')

function p._func1(args)
	-- El código de la primera función va acá
end

p.func2 = hazFuncInvoke('_func2')

function p._func2(args)
	-- El código de la segunda función va acá
end

return p

Opciones

Las siguientes opciones están disponible, y son explicadas en las secciones de abajo.

local args = dameArgs(marco, {
	limpiarEspacios = false,
	removerVacios   = false,
	fnValores       = function (clave, valor)
		-- Código para procesar un argumento
            end,
	soloMarco       = true,
	soloPadre       = true,
	padrePrimero    = true,
	envolventes     = {
		'Plantilla:Una plantilla envolvente',
		'Plantilla:Otra plantilla envolvente'
            },
	soloLectura     = true,
	noSobreescribir = true
})

Eliminar espacios y vacios

local args = dameArgs(marco, {
	limpiarEspacios = false,
	removerVacios   = false
})

Personalización del formato de los argumentos

Algunas veces se desea remover algunos argumentos en blanco pero no otros, o tal vez poner todos los argumentos posicionales en minúscula. Para hacer cosas como estas, se usa la opción fnValores. La entrada a esta opción debe ser una función que toma dos parámetros, clave and value, y devuelve un valor sencillo. Este valor es lo que se obtiene cuando acceda al campo clave en la tabla de args

Ejemplo 1: esta función preserva los espacio en blanco para el primer argumento posicional, pero los elimina de los otros argumentos, y los elimina si quedan vacíos:

local args = dameArgs(marco, {
	fnValores       = function (clave, valor)
		if 1 == clave then
			return valor
		elseif valor then
			valor = mw.text.trim(valor) -- Elimina los espacios al comienzo y final del valor
			if '' ~= valor then         -- Si el valor no quedó vacío
				return valor        -- Lo devuelve
			end
		end
		return nil                          -- En otros casos, devuelve el valor nulo (es decir, no incluir el valor)
	end
})

Ejemplo 2: esta función elimina los argumentos vacíos y convierte todos los argumentos a minúsculas, pero no elimina los espacios del comienzo y final de los parámetros posicionales.

local args = dameArgs(marco, {
	fnValores       = function (clave, valor)
		if not valor then
			return nil
		end
		value = mw.ustring.lower(valor)
		if mw.ustring.find(valor, '%S') then
			return valor
		end
		return nil
	end
})

Nota: las funciones de arriba fallarán si se les pasa una entrada que no sea de tipo string or nil. Esto puede suceder si se usa la función dametArgs en la función principal del módulo, y esa función es llamada desde otro módulo Lua. En este caso, es necesario comprobar el tipo de la entrada. Esto no es un problema cuando se usa una función específicamente para obtener los argumentos de #invoke; por ejemplo, cuando se usa una función para ese caso (usualmente p.main) y otra ser usada por otros módulos (usualmente p._main).

También, es importante destacar que la función fnValores es llamada aproximadamente cada vez que se pide un argumento de la tabla args. Por lo tanto, si se quiere mejorar el rendimiento debería verificarse no estar haciando nada ineficiente en ese código.

Marcos y marcos padre

Los argumentos de la tabla args pueden ser pasados desde el marco actual o del marco padre a la vez. Para enteder qué significa esto, es más fácil dar un ejemplo. Digamos que tenemos un módulo llamado Módulo:EjemploArgs, qu eimprime los primeros dos parámetros posicionales que se le pasen:

Envolventes

La opción envolventes se usa para indicar un número limitado de plantillas que funcionan como envolentes; es decir, cuyo único propósito es llamar al módulo. Si el módulo detecta que es llamado desde una de estas plantillas, solo comprobará los argumentos del marco padre; de lo contrario solo lo hará con el marco pasado a dameArgs. Esto le permite al módulo ser llamado tanto desde #invoke como desde una envolvente sin la pérdida de rendimiento asociada a tener que comprobar ambos marcos (el actual y el padre) por cada argumento.


Escribiendo en la tabla args

Etiquetas ref

Limitaciones conocidas


local z = {}

function z.obtenerArgumentos(frame)
	if frame.args[1] then 
		return frame.args
	end

	return frame:getParent().args
end

function z.obtenerArgumentosConValor(frame)
	if frame == mw.getCurrentFrame() then
		argumentos = frame:getParent().args
	else
		argumentos = frame.args or frame
	end

	return require('Módulo:Tablas').copiarElementosConValor(argumentos)
end

-- Obtiene los argumentos con valores de la plantilla en minúsculas y con las
-- tildes removidas, en caso de que las tenga de forma que sea más sencillo
-- trabajar con las distintas variantes en los módulos.
-- 
-- Nota: En caso de que haya parámetros duplicados tras la normalización solo
-- se mantendrá el último valor procesado por la función.
--
-- Parámetros de entrada:
--    frame: El marco utilizado por el módulo
--
-- Parámetros de salida:
--    argumentosNormalizados: los argumentos con valor y nombre normalizado
--    argumentosDuplicados: si la plantilla tiene varias veces el mismo 
--        argumento tras la normalización o no
function z.obtenerArgumentosConValorNormalizados(frame)
	local argumentos = z.obtenerArgumentosConValor(frame)
	local argumentosNormalizados = {}
	local nombreNormalizado
	local argumentosDuplicados = false
	
	for nombre, valor in pairs(argumentos) do
		
		nombreNormalizado = nombre
		
		nombreNormalizado = mw.ustring.lower( nombreNormalizado )
		nombreNormalizado = string.gsub(nombreNormalizado, "[á]", "a")
		nombreNormalizado = string.gsub(nombreNormalizado, "[é]", "e")
		nombreNormalizado = string.gsub(nombreNormalizado, "[í]", "i")
		nombreNormalizado = string.gsub(nombreNormalizado, "[ó]", "o")
		nombreNormalizado = string.gsub(nombreNormalizado, "[úü]", "u")
		
		if argumentosNormalizados[nombreNormalizado] then
			argumentosDuplicados = true
		end
		argumentosNormalizados[nombreNormalizado] = valor
	end
	
	return argumentosNormalizados, argumentosDuplicados
end

--[[
	@name	obtenerTablaDeArgumentos
	@global	args
	@param	frame
	@return	table
	@descr	Obtiene una tabla de argumentos tomando los parámetros recibidos
			tando desde la plantilla como desde la invocación de un módulo.
			En caso de duplicado tiene preferencia el valor de la invocación.
			Por ejemplo:
				con la plantilla:	{{Plantilla |campo=valor |key=clave }}
				y la invocación:	{{#invoke:Módulo |key=value }}
				se obtiene:			{ ['campo'] = 'valor', ['key'] = 'value' }
--]]
function z.obtenerTablaDeArgumentos(frame)
	-- global args
	args = {}
	local function paramMerge(orig, copy)
		local data = {}
		for key, val in pairs(orig) do
			data[key] = val
		end
		for key, val in pairs(copy) do
			data[key] = val
		end
		return data
	end
	if frame then
		-- parentArgs = frame:getParent().args or {}
		if type(frame.getParent) == 'function' then
			local parent = frame:getParent()
			if parent then
				args = paramMerge(args, parent.args)
			end
		end
		-- invokeArgs = frame.args or frame or {}
		if type(frame.args) == 'table' then
			args = paramMerge(args, frame.args)
		elseif type(frame) == 'table' then
			args = paramMerge(args, frame)
		end
	end
	return args
end

--[[
	@name	obtenerValorDeArgumentos
	@global	args
	@param	list
	@return	string or nil
	@descr	Obtiene el primer argumento válido desde una tabla de parámetros.
			Esta tabla de parámetros es una lista que contiene los nombres
			de los argumentos u otras tablas con las funciones para obtenerlos.
	Parámetros:
		con los argumentos:		{ ['campo'] = 'valor', ['key'] = 'value' }
		y usando la llamada:	obtenerValorDeArgumentos{'dato', 'campo', 'key'}
		se obtiene el valor:	'valor'
		pues 'dato' no es un argumento y 'campo' es el primero encontrado
	Funciones:
		también se puede llamar con una función de la forma
			obtenerValorDeArgumentos{'dato', { obtenerDato, '1', '2' }}
		de forma que si el argumento 'dato' no existe se llama a la función
			obtenerDato('1', '2')
--]]
function z.obtenerValorDeArgumentos(list)
	-- global args
	local lang = mw.language.getContentLanguage()
	local err, key
	if type(list) == 'number' then
		key = args[list]
	elseif type(list) == 'string' then
		key = args[list] or args[lang:ucfirst(list)]
	elseif type(list) == 'table' then
		for num, val in ipairs(list) do
			if type(val) == 'string' or type(val) == 'number' then
				key = z.obtenerValorDeArgumentos(val)
			elseif type(val) == 'function' then
				err, key = pcall(val)
				if err ~= true then
					key = nil
				end
			elseif type(val) == 'table' then
				if val[1] and type(val[1]) == 'function' then
					err, key = pcall(val[1], unpack(val, 2))
					if err ~= true then
						key = nil
					end
				end
			end
			if key ~= nil and key ~= '' then
				return key -- break
			end
		end
	end
	return key
end

return z