Ir al contenido

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 insert = table.insert
local concat = table.concat

local tobool = require("Módulo:sí-no")
local escapar = require("Módulo:string/escapar")
local find = string.find
local generar_traza = require("Módulo:traza")

function export.obtener_parametros(args, params)
	local args_new = {}

	local requerido = {}
	local destino = {}
	local patron_lista = {}
	local props_lista_posicional

	--Procesamos los parámetros buscando propiedades específicas
	for nombre, propiedades in pairs(params) do
		if type(nombre) == "string" and find(nombre, NO_PERMITIDO) then
			error("Los parámetros no pueden tener los caracteres #<>[]{}|=")
		end

		if propiedades.alias_de then
			if type(propiedades.alias_de) ~= "string" then
				error("Un parámetro solo puede ser alias respecto de uno no posicional")
			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
			if params[propiedades.alias_de].alias_de then
				error("Un parámetro no puede ser alias de otro parámetro que ya es alias")
			end
			destino[nombre] = propiedades.alias_de
			propiedades = params[propiedades.alias_de] -- hereda las propiedades del parámetro al que apunta
		else
			-- Evito el doble conteo
			if propiedades.requerido then
				requerido[nombre] = true
			end
			if propiedades.lista then
				if type(nombre) ~= "number" then
					args_new[nombre] = {} -- si es string, inicializo lista vacía
				elseif nombre == 1 then
					props_lista_posicional = propiedades -- si es numérico, guardo las propiedades para después
					props_lista_posicional.lista = nil -- necesario para que no intente dobleindexar parámetros posicionales
				end
			end
			destino[nombre] = nombre
		end

		if propiedades.lista and type(nombre) == "string" then
			patron_lista[nombre] =  "^" .. escapar(nombre) .. "(%d*)$" -- solo guardo parámetros no posicionales que son listas
		end
	end

	local function obtener_destino_indice_props(x)
		if destino[x] then
			return destino[x], (params[destino[x]].lista and 1 or -1), params[destino[x]]
		end

		if type(x) == "number" then
			if props_lista_posicional then -- si guardé las propiedades de esto, quiere decir que los parámetros posicionales comparten todos las mismas propiedades
				return x, -1, props_lista_posicional
			end
			if destino[1] and patron_lista[destino[1]] then -- patron_lista solo puede contener strings como claves, o sea que es un alias de otra lista
				return destino[1], x, params[destino[1]]
			end
			return x, -1, (params[x] or {})
		end

		for pname, pattern in pairs(patron_lista) do
			local _,_,a = find(x, pattern)
			if a then
				local idx = (a == "") and 1 or tonumber(a)
				assert(idx <= 99, "Desborde de lista")
				return destino[pname], idx, params[destino[pname]]
			end
		end

		return nil, -1, {}
	end

	for name, val in pairs(args) do
		if val ~= nil and val ~= "" and val:find("%S") then
			val = val:gsub("^%s+", ""):gsub("%s+$", "")
			local dest, index, props = obtener_destino_indice_props(name)
			if dest then
				if props.tipo == "bool" then
					val = tobool(val)
				elseif props.tipo == "num" then
					val = tonumber(val)
					assert(type(val) == "number", "Parámetro \""..name.."\": debe especificar un número")
				elseif type(props.tipo) == "function" then
					val = props.tipo(val)
				end

				if index == -1 then
					if args_new[dest] ~= nil then
						generar_traza("rep")
					end
					args_new[dest] = val
				else
					if args_new[dest][index] ~= nil then
						generar_traza("rep")
					end
					args_new[dest][index] = val
				end

				requerido[dest] = nil
			end
		end
	end

	-- Reemplazo los 'nil' por los valores predeterminados
	for name, props in pairs(params) do
		if not props.alias_de and props.por_defecto ~= nil then
			if type(args_new[name]) == "table" then
				if args_new[name][1] == nil then
					args_new[name][1] = props.por_defecto
				end
			elseif args_new[name] == nil then
				args_new[name] = props.por_defecto
			end
			requerido[name] = nil
		end
	end

	-- verifico que no queden parámetros requeridos
	local faltante = {}
	for name, _ in pairs(requerido) do
		insert(faltante, name)
	end
	assert(#faltante == 0, "Faltan parámetros requeridos: " .. concat(faltante, ", "))

	return args_new
end

return export