Módulo:parámetros

De Wikcionario, el diccionario libre

La documentación para este módulo puede ser creada en Módulo:parámetros/doc

--versión modificada de https://en.wiktionary.org/wiki/Module:parameters

local export = {}

local no_permitido = "[#\\<\\>\\[\\]\\{\\}\\|=]"

local traza = require("Módulo:traza")
local escapar = require("Módulo:String/escapar")
local tobool = require("Módulo:sí-no")

local function generar_traza(err)
	traza("parámetros/"..err)
end

local function buscar_huecos(arr)
	local highest = 1
	for num, _ in pairs(arr) do
		if type(num) == "number" and num > 0 and num < math.huge and math.floor(num) == num then
			highest = math.max(highest, num)
		end
	end

	for i = 1, highest-1 do --reviso que no hayan huecos en los valores anteriores al máximo
		if ((type(arr[i]) == "table") and buscar_huecos(arr[i])) or arr[i] == nil then
			return true
		end
	end
	return false
end

function export.obtener_parametros(args, params)
	local args_new = {}
	local requerido = {}
	local patron_lista = {}
	local patron_lista_doble = {}
	
	--Procesamos los parámetros buscando propiedades específicas
	for nombre, propiedades in pairs(params) do
		
		if type(nombre) == "string" and string.find(nombre, no_permitido) then
			error("Los parámetros no pueden tener los caracteres #<>[]{}|=")
		end
		
		if type(nombre) == "number" and (propiedades.lista or propiedades.lista_doble) then
			error("Los parámetros posicionales son listas simples. No se puede especificar que sea una lista simple o una lista doble.")	
		end

		local es_alias = false
		if propiedades.alias_de then
			es_alias = true
			if #propiedades > 1 then
				error("Un parámetro que es alias de otro no puede tener más propiedades especificadas")
			end
			if not params[propiedades.alias_de] then
				error("El parámetro "..nombre.." es alias de un parámetro que no existe ("..propiedades.alias_de..")")
			end
			propiedades = params[propiedades.alias_de]
		else
			if propiedades.requerido then
				requerido[nombre] = true --Lo compruebo sólo en parámetros que no son alias para no contar doble
			end
		end

		if propiedades.lista and propiedades.lista_doble then
			error("Un parámetro no puede ser \"lista\" y \"lista doble\" a la vez")
		end
		
		if propiedades.lista then --si es lista, proceso los items que pueda llegar a tener
			args_new[nombre] = {maxindex = 0}
			
			--Si el parámetro tiene nombre quiere decir que es el prefijo de los items
			--Ej: si lista = "par", entonces deberíamos esperar par1=algo, par2=algo, par3=algo, ...
			if type(nombre) == "string" then
				patron_lista[nombre] =  "^" .. escapar(nombre) .. "(%d*)$"
			elseif not es_alias then
				error("Se esperaba que el parámetro solicitado (lista) fuese string")
			end
		end

		if propiedades.lista_doble then
			args_new[nombre] = {maxindex = 0}
			if type(nombre) == "string" then
				patron_lista_doble[nombre] = "^(%d*)" .. escapar(nombre) .. "(%d*)$"
			elseif not es_alias then
				error("Se esperaba que el parámetro solicitado (lista doble) fuesee string")
			end
			--En un futuro debería ser así:
			--elseif type(nombre) == "number" then
			--	error("Las listas dobles no pueden estar indexadas")
			--else
			--	error("Se esperaba que el parámetro solicitado fuese string")
			--end
		end
	end
	
	if not params[1] then
		params[1] = {}
	end

	-- Proceso los argumentos (lo que recibí de la invocación)
	local max_index
	
	--Para debug, se imprimen los argumentos recibidos
	--str = ""
	--for name, val in pairs(args) do
	--	str = str..";"..tostring(name).."-"..tostring(val)
	--end
	--error(str)
	
	for name, val in pairs(args) do
		local index = nil
		local index1 = nil
		local index2 = nil

		local param = nil
		local destino = nil
		
		if type(name) == "number" then
			if not params[1].alias_de then
				index = name
				param = params[1]
				destino = name
			else
				----
				--- PArche temporal para el módulo pron-graf: la plantilla se llama con parámetros indexados
				-- Pero "fone" es lista doble, por lo tanto complicaría las cosas permitir índices superiores a uno
				-- Por lo tanto, si se quiere acceder a un índice superior a uno y no existe, entonces forzamos a
				-- que sea uno
				---
				destino = params[1].alias_de
				param = params[destino]
				index = 1
				index1 = 1
				index2 = 1
			end
		else --Sino,
			if params[name] then
				destino = params[name].alias_de or name
				param = params[destino]
				index = 1
				index1 = 1
				index2 = 1
			else

				-- Sino, si coincide con alguno de los patrones que guardé antes
				-- busco el número y lo guardo en el índice y actualizo el nombre del parámetro
				for pname, pattern in pairs(patron_lista) do
					local _,_,a = string.find(name, pattern)
					if a then
						index = tonumber(a)
						if index > 99 then
							error("Desborde de lista")
						end
						destino = params[pname].alias_de or pname
						param = params[destino]
						break
					end
				end

				if not destino then
					for pname, pattern in pairs(patron_lista_doble) do
						local _,_,a,b = string.find(name, pattern)
						if a and b then
							index1 = tonumber(a == "" and "1" or a)
							index2 = tonumber(b == "" and "1" or b)
							if index1 > 99 or index2 > 99 then
								error("desborde de lista")
							end
							index = index2
							destino = params[pname].alias_de or pname
							param = params[destino]
							break
						end
					end
				end
			end
		end

		-- Si el argumento no está en la lista de parámetros, se arrojaba un error.
		-- Para mí es muy riesgoso hacerlo, sobre todo si queremos implementar estas cosas
		-- en páginas viejas en las que no sabemos con qué nos podremos encontrar...
		-- Si destino=nil, salteo la iteración

		if destino then
			--Saco los espacios de más
			val = mw.text.trim(val)

			if val == "" then
				val = nil
			end

			-- Convert to proper type if necessary.
			if param.tipo == "binario" or param.tipo == "bool" or param.tipo == "boolean" or param.tipo == "booleano" then
				val = tobool(val, false)
			elseif param.tipo == "num" or param.tipo == "núm" or param.tipo == "number" or param.tipo == "número" then
				val = tonumber(val)
				assert(type(val) == "number", "Parámetro \""..name.."\": debe especificar un número")
			end

			-- Can't use "if val" alone, because val may be a boolean false.
			if val ~= nil then
				requerido[destino] = nil --sacamos el parámetro de la lista de requeridos
				
				-- Store the argument value.
				if param.lista then
					if args_new[destino][index] ~= nil then
						generar_traza("parámetro repetido")
					end

					args_new[destino][index] = val
					args_new[destino].maxindex = math.max(index, args_new[destino].maxindex)

				elseif param.lista_doble then
					if args_new[destino][index1] == nil then
						args_new[destino][index1] = {}
					end
					
					if args_new[destino][index1][index2] ~= nil then
						generar_traza("parámetro repetido")
					end

					args_new[destino][index1][index2] = val
					args_new[destino].maxindex = math.max(index1, index2, args_new[destino].maxindex)
				
				else
					if args_new[destino] ~= nil then
						generar_traza("parámetro repetido")					
					end

					args_new[destino] = val
				end
			end
		end
	end
	
	-- Reemplazo los 'nil' por los valores predeterminados
	for name, param in pairs(params) do
		if param.por_defecto ~= nil then
			if type(args_new[name]) == "table" then
				if args_new[name][1] == nil then
					args_new[name][1] = param.por_defecto
				end
			elseif args_new[name] == nil then
				args_new[name] = param.por_defecto
			end
			requerido[name] = nil
		end
	end
	
	--Si quedan valores no nulos en requerido, lanzo error a menos que esté en el espacio de plantillas
	if mw.title.getCurrentTitle().namespace ~= 10 then
		local lista = {}
		for name, param in pairs(requerido) do
			table.insert(lista, name)
		end
		if #lista > 0 then
			error('Faltan parámetros requeridos: "' .. mw.text.listToText(lista, '", "', '" y "'), 2)
		end
	end
	
	-- Chequeo huecos en listas
	for name, val in pairs(args_new) do
		if type(val) == "table" and buscar_huecos(val) then
			generar_traza("lista con huecos")
		end
	end

	return args_new
end

return export