Module:Unité

De Lagny-sur-Marne Wiki
Révision datée du 19 avril 2015 à 08:36 par Zebulon84 (discussion) (espaces insécables autour du ×)
Aller à : navigation, rechercher

La documentation pour ce module peut être créée à Module:Unité/doc

local p = { }

--- Copie de Outils.trim acceptant les nombres.
local function trim( texte )
	if type( texte ) == 'string' then
		texte = texte:gsub( '^%s*(.*)%f[%s]%s*$', '%1' )
		if texte ~= '' then
			return texte
		end
	elseif type( texte ) == 'number' then
		return tostring( texte )
	end
end

---
-- parseNum transforme si possible une chaine formater en un chaine interprétable par tonumber()
-- retourne une chaine pour éviter les arrondi éventuels de lua.
-- si nombre n'est pas un nombre ou une chaine retourne une chaine vide.
-- 
function p.parseNombre( nombre )
	if type( nombre ) == 'number' then
		nombre = tostring( nombre )
	elseif type( nombre ) ~= 'string' then
		return ''
	end
	
	-- remplacement des signes moins ou demi-cadratin par un tiret
	nombre = nombre:gsub( '^&minus', '-' )
			:gsub( '^\226\128[\146\147]', '-' )	-- U+2012, U+2013 (voir [[Tiret]])
			:gsub( '^\226\136\147', '-' ) -- U+2212
	
	if nombre:match( '^%-?[%d., \194\160]*%d$' ) then
		-- suppression espaces et espaces insécables
		nombre = nombre:gsub( ' ', '' ):gsub( '\194\160', '' )
		
		if nombre:match( '[.,]' ) then
			if nombre:match( '%d+,%d%d%d,%d%d%d%f[%D]' ) -- type 1,234,567 
				or nombre:match( '%d+,%d%d%d%.%d+' )  -- type 1,234.5
				--or nombre:match( '%d+,%d00$' )  -- type 1,200
			then
				-- format anglo-saxon
				nombre = nombre:gsub( ',', '' )
			elseif nombre:match( '%d+%.%d%d%d,%d' ) or nombre:match( '%d+%.%d%d%d%.%d%d%d%f[%D]' ) then
				-- formant germanique type 1.234,5
				nombre = nombre:gsub( '%.', '' ):gsub( ',', '.' )
			else
				nombre = nombre:gsub( ',', '.' )
			end
		end
	end
	
	return nombre
end

---
-- _formantNum transforme un nombre ou une chaine représentant un nombre en chaine formatée suivant les conventions du français
-- si le paramètre ne représente pas un nombre lua il est retourné sans modification
function p._formatNum( num )
	if type( num ) == 'number' then
		num = tostring( num )
	elseif type( num ) ~= 'string' then
		return num
	end
	
	local moins, entier, fraction = num:match( '^(%-?)(%d+)%.?(%d*)$' )
	if not entier then
		return num
	end
	
	if moins == '-' then
		moins = '−' -- signe moins (U+2212)
	end
	
	if entier:len() > 3 then
		local ini = math.fmod( entier:len(), 3 )
		entier = ( entier:sub( 1, ini ) or '') .. entier:sub( ini + 1 ):gsub( '(%d%d%d)', '\194\160%1' )
	end
	if fraction ~= '' then
		fraction = ',' .. fraction:gsub( '(%d%d%d)', '%1\194\160' ):gsub( '\194\160$', '' )
	end
	
	return moins .. entier .. fraction
end

---
-- formatNum transforme les nombres d'une chaine en chaine formatée suivant les conventions du français
function p.formatNum( num )
	if type( num ) == 'number' then
		return p._formatNum( num )
	elseif type( num ) == 'string' then
		return num:gsub( '%-?%d*%.?%d+', p.formatNombre )
	end
end

function p.formatNombre( nombre )
	return p._formatNum( p.parseNombre( nombre ) )
end

function p.parseUnit( texte )
	if trim( texte ) then
		local toParse = texte
		local result
		-- valeur numérique
		local nombre = toParse:match( '^%-?%f[%d][%d., \194\160]*%d%f[%D]' ) or false
		if nombre then
			toParse = toParse:sub( nombre:len() + 1 )
		end
		result = { nombre }
		
		-- 10 exposant   ( \195\151 = ×, signe multiplié)
		local match, e = toParse:match( '^(%s*[x\195]\151?10e(%-?%d+))' )
		if match then
			table.insert( result, 'e' )
			table.insert( result, e )
			toParse = toParse:sub( match:len() + 1 )
		end
		
		-- unités
		toParse = toParse:gsub( '⋅', '.' )
		local match, unit, exp = mw.ustring.match( toParse, '^(%s*%.?([%a°/]+)(%-?%d*))' )
		while match and unit:len() < 5 do
			table.insert( result, unit )
			table.insert( result, exp )
			toParse = toParse:sub( match:len() + 1 )
			match, unit, exp = mw.ustring.match( toParse, '^(%s*%.?([%a°/]+)(%-?%d*))' )
		end
		if trim( toParse ) then
			-- une partie de la chaine n'a pas pu être décodée, on retourne la chaine originale
			return { texte }
		else 		
			return result
		end 
	else
		return { }
	end
end

function p._unite( args )
	local wiki = { }
	local sep = ''
	local nombre = trim( args[1] )
	local unit = trim( args[2] )
	local exp = trim( args.e )
	
	if nombre then
		nombre = nombre
		-- remplacement des signes moins par des tirets pour facilité les traitements ultérieurs
			:gsub( '%−%f[%d]', '-')  -- U+2212
			:gsub( '&minus;%f[%d]', '-')  -- html &minus;
			:gsub( '\226\128[\146\147]%f[%d]', '-') -- U+2212, U+2213 (tiret numérique et demi-cadratin)
		-- remplacement des espaces insécable par des espace simple
			:gsub( '\194\160', ' ' )
	end
	
	if nombre and not unit and not exp and nombre:match('[^%d,. -]') then
		args = p.parseUnit( nombre )
		nombre = args[1]
		unit = args[2]
	end
	
	-- formatage du nombre
	if nombre then
		nombre = nombre:gsub( '%-?%f[%d][%d., ]+%f[%D]', p.formatNombre )
		table.insert( wiki, nombre )
		sep = ' '
	end
	
	-- puissance de 10
	local i = 1
	if not exp and unit == '10' or unit == 'e' then
		exp = args[3]
		i = 2
		unit = trim( args[4] )
	end
	if exp then
		if #wiki > 0 then
			table.insert( wiki, '\194\160×\194\16010<sup>' .. exp .. '</sup>' )
		else
			table.insert( wiki, '10<sup>' .. exp .. '</sup>' )
		end
		sep = ' '
	end
	
	-- unités
	while unit do
		if unit == '°' then
			sep = ''
		end 
		table.insert( wiki, sep )
		table.insert( wiki, unit )
		exp = trim( args[ 2 * i + 1 ] )
		if exp then
			table.insert( wiki, '<sup>' .. exp:gsub( '%-', '−' ) .. '</sup>' )
		end
		i = i + 1
		unit = trim( args[ 2 * i ] )
		sep = '⋅'
	end
	
	if #wiki > 0 then
		local result = table.concat( wiki ):gsub( '⋅/', '/' ) -- nettoyage cas particulier de parseunit.
		return '<span class="nowrap">' .. result .. '</span>'
	end
end

function p.unite( frame )
	local args
	if type( frame ) == 'table' and type( frame.getParent ) == 'function' then
			args = frame:getParent().args;
	end
	if args then
		return p._unite( args )
	end
end

return p