Module:Unité : Différence entre versions

De Lagny-sur-Marne Wiki
Aller à : navigation, rechercher
(parseUnit : correction bug si l'unité contient un tiret (notamment si c'est du texte), avec catégorisation temporaire pour améliorer ça)
(retouche de la modification précédente)
 
(3 révisions intermédiaires par le même utilisateur non affichées)
Ligne 15 : Ligne 15 :
 
local addErrorCat = false
 
local addErrorCat = false
  
 +
local supUnicode = { ['0'] = '⁰', ['1'] = '¹', ['2'] = '²', ['3'] = '³', ['4'] = '⁴', ['5'] = '⁵', ['6'] = '⁶', ['7'] = '⁷', ['8'] = '⁸', ['9'] = '⁹',
 +
['+'] = '⁺', ['-'] = '⁻', ['='] = '⁼', ['('] = '⁽', [')'] = '⁾', ['n'] = 'ⁿ' }
 +
local subUnicode = { ['0'] = '₀', ['1'] = '₁', ['2'] = '₂', ['3'] = '₃', ['4'] = '₄', ['5'] = '₅', ['6'] = '₆', ['7'] = '₇', ['8'] = '₈', ['9'] = '₉',
 +
['+'] = '₊', ['-'] = '₋', ['='] = '₌', ['('] = '₍', [')'] = '₎',
 +
['a'] = 'ₐ', ['e'] = 'ₑ', ['o'] = 'ₒ', ['x'] = 'ₓ', ['h'] = 'ₕ', ['k'] = 'ₖ', ['l'] = 'ₗ',
 +
['m'] = 'ₘ', ['n'] = 'ₙ', ['p'] = 'ₚ', ['s'] = 'ₛ', ['t'] = 'ₜ',
 +
}
 
--- Copie de Outils.trim acceptant les nombres.
 
--- Copie de Outils.trim acceptant les nombres.
 
local function trim( texte )
 
local function trim( texte )
 
if type( texte ) == 'string' then
 
if type( texte ) == 'string' then
texte = texte:gsub( '^%s*(.*)%f[%s]%s*$', '%1' )
+
texte = texte:gsub( '^%s*(%S?.-)%s*$', '%1' )
 
if texte ~= '' then
 
if texte ~= '' then
 
return texte
 
return texte
Ligne 40 : Ligne 47 :
 
-- remplacement des espaces insécable par des espace simple
 
-- remplacement des espaces insécable par des espace simple
 
:gsub( '\194\160', ' ' )
 
:gsub( '\194\160', ' ' )
 +
result = mw.ustring.gsub( result, '[ -  ]', ' ' ) -- U+2002 à U+220A et U+202F
 
return result
 
return result
 
else  
 
else  
Ligne 107 : Ligne 115 :
 
if params.noHtml then
 
if params.noHtml then
 
exponent = exponent:gsub('+?%f[%d]0', '' )
 
exponent = exponent:gsub('+?%f[%d]0', '' )
:gsub( '[%d-]', { ['-'] = '⁻', ['1'] = '¹', ['2'] = '²', ['3'] = '³', ['4'] = '⁴', ['5'] = '⁵', ['6'] = '⁶', ['7'] = '⁷', ['8'] = '⁸', ['9'] = '⁹', ['0'] = '⁰' } )
+
:gsub( '[%d-]', supUnicode )
 
else
 
else
 
exponent = '<sup>' .. exponent:gsub('^%+?(%-?)0?', { ['-'] = '−' } ) .. '</sup>'
 
exponent = '<sup>' .. exponent:gsub('^%+?(%-?)0?', { ['-'] = '−' } ) .. '</sup>'
Ligne 212 : Ligne 220 :
 
if match then
 
if match then
 
toParse = toParse:sub( match:len() + 1 )
 
toParse = toParse:sub( match:len() + 1 )
 +
 +
-- fraction
 +
match, capture = toParse:match( '^(([+-]?%d* ?/ ?%d+)%s*)' )
 +
if not match then
 +
match, capture = mw.ustring.match( toParse, '^(([¼-¾⅐-⅞])%s*)' )
 +
end
 +
if match then
 +
if capture:match( '^/' ) then
 +
local n = result[1]:match( ' (%d+)$' ) or ''
 +
result[1] = result[1]:sub( 1, -1 - #n )
 +
result.fraction = n .. capture
 +
else
 +
result.fraction = capture
 +
end
 +
toParse = toParse:sub( match:len() + 1 )
 +
end
 
 
 
-- lien avec un deuxième nombre
 
-- lien avec un deuxième nombre
Ligne 244 : Ligne 268 :
 
 
 
-- unités
 
-- unités
toParse = toParse:gsub( '', '.' ):gsub( '²', '2' ):gsub( '³', '3' )
+
if Data.unit[ toParse ] or mw.ustring.match( toParse, '^%a+$' ) or toParse:match( '%b<>' ) then
local unit, exp
+
table.insert( result, toParse )
local unitRegex = '^(%.?(/?[%aà°±]+) ?(%-?%d*)%s*)'
+
toParse = ''
match, unit, exp = mw.ustring.match( toParse, unitRegex )
+
elseif toParse ~= '' then
while match do -- and unit:len() < 5
+
local unit, exp
table.insert( result, unit )
+
toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' )
if exp ~= '-' then
+
repeat
table.insert( result, exp )
+
-- unité contenant un lien
toParse = toParse:sub( match:len() + 1 )
+
match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/%[%]]*%b[][^%s%d/]*) ?(%-?%d*)%s*)' )
else
+
if not match then
break
+
-- unité ne contenant pas de lien
end
+
match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/]+) ?(%-?%d*)%s*)' )
match, unit, exp = mw.ustring.match( toParse, unitRegex )
+
end
 +
if match then
 +
if unit:match( '%-$' ) and exp ~= '' then
 +
unit = unit:gsub( '%-$', '' )
 +
exp = '-' .. exp
 +
elseif exp == '-' then
 +
unit = match
 +
exp = ''
 +
end
 +
table.insert( result, unit )
 +
table.insert( result, exp )
 +
toParse = toParse:sub( match:len() + 1 )
 +
end
 +
until toParse == '' or not match
 
end
 
end
 +
 
if toParse == '' then
 
if toParse == '' then
 
return result
 
return result
Ligne 335 : Ligne 373 :
 
 
 
local wiki = { nombre }
 
local wiki = { nombre }
 +
 +
-- fraction
 +
if args.fraction then
 +
local nom, den = args.fraction:match( '^(.-)/(.+)$' )
 +
if nom then
 +
if nom:match( '^[ %dn()=+-]+$' ) and den:match( '^[ %daeoxhklmnpst()=+-]$' ) then
 +
nom = nom:gsub( '[%dn()=+-]', supUnicode )
 +
den = den:gsub( '[%daeoxhklmnpst()=+-]', subUnicode )
 +
else
 +
nom = '<sup style="font-size: 70%; vertical-align: 0.4em;">' .. nom .. '</sup>'
 +
den = '<sub style="font-size: 70%; vertical-align: 0em;">' .. den .. '</sub>'
 +
end
 +
args.fraction = nom .. '⁄' .. den
 +
end
 +
table.insert( wiki, ' ' .. args.fraction )
 +
end
 
 
 
-- à, et, ou, ×, – (tiret cadratin)
 
-- à, et, ou, ×, – (tiret cadratin)
Ligne 362 : Ligne 416 :
 
local exp = p.parseNombre( args[ 2 * i + 1 ] )
 
local exp = p.parseNombre( args[ 2 * i + 1 ] )
 
local sep = ''
 
local sep = ''
 +
-- gestion des exposants
 +
local expUnit = ''
 
if exp == '' then
 
if exp == '' then
 
if unit:sub( -2 ) == '²' then
 
if unit:sub( -2 ) == '²' then
Ligne 371 : Ligne 427 :
 
end
 
end
 
end
 
end
 +
if #exp > 0 then
 +
expUnit = '<sup>' .. exp:gsub( '^-', '−') .. '</sup>'  -- remplace le tiret par un vrai signe moins
 +
end
 +
-- gestion de la séparation des unités et des unités en dénominateur
 
if units ~= '' then
 
if units ~= '' then
 
if unit:sub( 1, 1 ) == '/' then
 
if unit:sub( 1, 1 ) == '/' then
 +
sep = '/'
 +
unit = unit:sub( 2 )
 
if not par then
 
if not par then
 
par = true
 
par = true
Ligne 385 : Ligne 447 :
 
end
 
end
 
end
 
end
local expUnit = ''
+
-- remplacement de l'unité par son symbole
if #exp > 0 then
+
if Data.unit[ unit ] then
expUnit = '<sup>' .. exp:gsub( '^-', '−') .. '</sup>'  -- remplace le tiret par un vrai signe moins
+
-- unit = Data.unit[ unit ].symbole
 +
-- désactivé car ne gère pas les multiple tel mL
 
end
 
end
 
units = units .. sep .. unit .. expUnit
 
units = units .. sep .. unit .. expUnit
Ligne 446 : Ligne 509 :
 
end
 
end
 
nConverted = nConverted * multiple * c[2] + ( c[3] or 0 )
 
nConverted = nConverted * multiple * c[2] + ( c[3] or 0 )
mw.log( nConverted )
 
 
-- format
 
-- format
 
nConverted = p.formatNum{ nConverted, round = c.round or 6, noHtml = true }
 
nConverted = p.formatNum{ nConverted, round = c.round or 6, noHtml = true }
Ligne 475 : Ligne 537 :
 
table.insert( wiki, ')' )
 
table.insert( wiki, ')' )
 
end
 
end
table.insert( wiki, ' × 10<sup>' .. exposant .. '</sup>' )
+
table.insert( wiki, ' × 10<sup>' .. exposant .. '</sup>' )
 
else
 
else
 
table.insert( wiki, '10<sup>' .. exposant .. '</sup>' )
 
table.insert( wiki, '10<sup>' .. exposant .. '</sup>' )
Ligne 518 : Ligne 580 :
 
end
 
end
 
end
 
end
if args[2] and not args[3] and args[2]:match('[%d./ -]') then
+
if args[2] and not Data.unit[ args[2] ] and not args[3] and mw.text.trim( args[2] ):match('[%d/ -]') then
 
local tempArgs = p.parseUnit( trim( args[2] ) )
 
local tempArgs = p.parseUnit( trim( args[2] ) )
if tempArgs[1] == false then
+
args[2] = false
tempArgs[1] = args[1]
+
if tempArgs[1] ~= false then
else
+
table.insert( tempArgs, 1, false )
table.insert( tempArgs, 1, args[1] )
+
end
 +
for k, v in pairs( tempArgs ) do
 +
if args[k] and v then
 +
addErrorCat = true
 +
end
 +
args[k] = args[k] or v
 
end
 
end
args = tempArgs
 
 
end
 
end
 
end
 
end

Version actuelle datée du 29 mai 2017 à 22:44

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

local p = {}

-- local Delink = require( 'Module:Delink' ) -- chargé uniquement si nécessaire

-- Chargement de la base de données des nom d'unités avec gestion d'erreur.
local moduleData = 'Module:Unité/Data'
local dataSuccess, Data = pcall ( mw.loadData, moduleData )
if dataSuccess and type( Data ) == 'table' then
	dataSuccess = type( Data.unit ) == 'table' 
		and type( Data.prefix ) == 'table'
		and type( Data.exposant ) == 'table'
end

local errorCat = '[[Catégorie:Page incorrectement traitée par le Module:Unité]]'
local addErrorCat = false

local supUnicode = { ['0'] = '⁰', ['1'] = '¹', ['2'] = '²', ['3'] = '³', ['4'] = '⁴', ['5'] = '⁵', ['6'] = '⁶', ['7'] = '⁷', ['8'] = '⁸', ['9'] = '⁹', 
	['+'] = '⁺', ['-'] = '⁻', ['='] = '⁼', ['('] = '⁽', [')'] = '⁾', ['n'] = 'ⁿ' }
local subUnicode = { ['0'] = '₀', ['1'] = '₁', ['2'] = '₂', ['3'] = '₃', ['4'] = '₄', ['5'] = '₅', ['6'] = '₆', ['7'] = '₇', ['8'] = '₈', ['9'] = '₉', 
	['+'] = '₊', ['-'] = '₋', ['='] = '₌', ['('] = '₍', [')'] = '₎', 
	['a'] = 'ₐ', ['e'] = 'ₑ', ['o'] = 'ₒ', ['x'] = 'ₓ', ['h'] = 'ₕ', ['k'] = 'ₖ', ['l'] = 'ₗ',
	['m'] = 'ₘ', ['n'] = 'ₙ', ['p'] = 'ₚ', ['s'] = 'ₛ', ['t'] = 'ₜ',
	}
--- Copie de Outils.trim acceptant les nombres.
local function trim( texte )
	if type( texte ) == 'string' then
		texte = texte:gsub( '^%s*(%S?.-)%s*$', '%1' )
		if texte ~= '' then
			return texte
		end
	elseif type( texte ) == 'number' then
		return tostring( texte )
	end
end

function p.sanitizeNum( nombre )
	if type( nombre ) == 'number' then
		return tostring( nombre )
	elseif trim( nombre ) then
		local result = nombre
			-- trim
			:gsub( '^%s*(.*)%f[%s]%s*$', '%1' )
			-- remplacement des signes moins ou demi-cadratin par un tiret
			: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', ' ' )
		result = mw.ustring.gsub( result, '[ -  ]', ' ' ) -- U+2002 à U+220A et U+202F
		return result
	else 
		return ''
	end
end

---
-- parseNum transforme si possible une chaine formatée en un chaine interprétable par tonumber()
-- retourne une chaine pour éviter les arrondi éventuels de lua.
-- si "nombre" est une chaine non reconnue comme un nombre par la fonction, retourne "nombre".
-- si "nombre" n'est pas un number ou une chaine retourne une chaine vide.
function p.parseNombre( nombre )
	local result
	if type( nombre ) == 'number' then
		return tostring( nombre )
	else
		-- remplacement des signes moins ou demi-cadratin par un tiret
		result = p.sanitizeNum( nombre )
		if result == '' then
			return ''
		elseif not result:match( '^%-?[%d., ]*%d$' ) then
			return nombre
		end		
	end
	
	-- suppression espaces
	result = result:gsub( ' ', '' )
	
	-- gestion des points et des virgules
	if result:match( '[.,]' ) then
		if result:match( '%d%.%d%d%d%.%d' ) then
			-- type 12.345.678
			result = result:gsub( '%.', '' ):gsub( ',', '.' )
		elseif result:match( '%d,%d%d%d,%d' ) -- type 1,234,567 ou 1.234,567,8
			or result:match( '%d,%d%d%d%.%d' )  -- format anglo-saxon type 1,234.5  
			or result:match( '%d%.%d%d%d,%d' ) -- type 1.123,56 (utilisé en exemple pour sépararer les décimales avec l'ancien modèle unité ou formatnum)
		then
			result = result:gsub( ',', '' )
		else
			result = result:gsub( ',', '.' )
		end
	end
	
	return result
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 )
	local params = {}
	if type( num ) == 'table' then
		params = num
		num = params[1]
	end
	if type( num ) == 'number' then
		num = tostring( num )
	elseif type( num ) ~= 'string' or num == '' then
		return num
	end
	
	-- séparation exposant
	local n, exponent = num:match( '^([-%d.]+)e([+-]?%d+)$' )
	if exponent then
		num = n
		if params.noHtml then
			exponent = exponent:gsub('+?%f[%d]0', '' )
				:gsub( '[%d-]', supUnicode )
		else
			exponent = '<sup>' .. exponent:gsub('^%+?(%-?)0?', { ['-'] = '−' } ) .. '</sup>'
		end
		exponent = ' ×10' .. exponent
	else
		exponent = ''
	end
	
	-- arrondi
	decimals = tonumber( params.decimals )
	round = tonumber( params.round ) or decimals
	if round then
		local mult = 10 ^ round 
		num = tostring( math.floor( num * mult + 0.5 ) / mult )
	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 == '' then
		entier = '0'
	elseif entier:len() > 3 then
		local ini = math.fmod( entier:len() - 1, 3 ) + 1
		entier = ( entier:sub( 1, ini ) or '') .. entier:sub( ini + 1 ):gsub( '(%d%d%d)', '\194\160%1' )
	end
	if fraction ~= '' or ( decimals and decimals > 0 ) then
		if decimals and decimals > #fraction then
			fraction = fraction .. string.rep( '0', decimals - #fraction )
		end
		if #fraction > 4 then
			fraction = ',' .. fraction:gsub( '(%d%d%d)', '%1\194\160' ):gsub( '\194\160$', '' )
		else
			fraction = ',' .. fraction
		end
	end

	return moins .. entier .. fraction .. exponent
end

---
-- formatNombre transforme un nombre formaté ou non en chaine formatée suivant les convention du français.
-- si la chaine n'est pas reconnu comme un nombre, elle n'est pas modifiée.
function p.formatNombre( num, round, decimals )
	return p.formatNum{ p.parseNombre( num ), round, decimals }
end

--- formatNombres transforme tous les nombres d'une chaine en nombre formaté suivant les conventions du français.
function p.formatNombres( nombres, round, decimals )
	if type( nombres ) == 'number' then
		return p.formatNum( nombres, round, decimals )
	elseif type( nombres ) == 'string' then
		-- retire les chiffres des strip marker
		local strip, i = {}, 0
		nombres = nombres:gsub( 
			'UNIQ%-%-%a+%-%x%x%x%x%x%x%x%x%-QINU',
			function ( marker )
				i = i + 1
				strip[ tostring( i ) ] = marker
				return 'UNIQ^' .. i .. '¤QINU'
			end
		)
		--  formatage proprement dit
		nombres = p.sanitizeNum( nombres )
		nombres =  nombres:gsub( 
			'%-?%f[%d.,^][%d., ]+%f[%D]', 
			function ( n )
				return p.formatNombre( n, round, decimals )
			end
		)
		-- réintroduction des strip marker
		nombres = nombres:gsub( 'UNIQ^(%d+)¤QINU', strip )
		return nombres
	else 
		return ''
	end
end

function p.parseUnit( texte )
	local toParse = p.sanitizeNum( texte )
	if toParse ~= '' then
		local result
		local specificArgs = {
			['à'] = 'à',
			et = 'et',
			ou = 'ou',
			['–'] = '–', -- demi cadratin
			['±'] = '±', ['+-'] = '±', ['+/-'] = '±',
			['+'] = '+',
			['−'] = '−', ['-'] = '−', -- signe moins et tiret
			['×'] = '×', x = '×', ['*'] = '×',
			['××'] = '××', xx = '××', ['**'] = '××',
		}
		
		-- valeur numérique
		local match, capture = toParse:match( '^((%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
		result = { capture or false }
		if match then
			toParse = toParse:sub( match:len() + 1 )
			
			-- fraction
			match, capture = toParse:match( '^(([+-]?%d* ?/ ?%d+)%s*)' )
			if not match then
				match, capture = mw.ustring.match( toParse, '^(([¼-¾⅐-⅞])%s*)' )
			end
			if match then
				if capture:match( '^/' ) then
					local n = result[1]:match( ' (%d+)$' ) or ''
					result[1] = result[1]:sub( 1, -1 - #n )
					result.fraction = n .. capture
				else
					result.fraction = capture
				end
				toParse = toParse:sub( match:len() + 1 )
			end
			
			-- lien avec un deuxième nombre
			local match2, conj, num = mw.ustring.match( toParse, '^(([àetouM+/−x*×±–-]+) ?(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
			if match2 and specificArgs[ conj ] 
				and not ( specificArgs[ conj ] == '×' and mw.ustring.match( toParse, '^[×x] ?10 ?e') ) then
				result[ specificArgs[ conj ] ] = num
				toParse = toParse:sub( match2:len() + 1 )
			end
			if result['+'] or result['×'] then
				match2, conj, num = mw.ustring.match( toParse, '^(([x*×−-]) ?(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
				if match2 then
					if specificArgs[ conj ] == '×' then
						result['××'] = num
					else
						result['−'] = num
					end
					toParse = toParse:sub( match2:len() + 1 )
				end
			end
		end
		
		-- 10 exposant   ( \195\151 = ×, signe multiplié)
		match, capture = toParse:match( '^(%s*e(%-?%d+)%s*)' )
		if not match then
			match, capture = toParse:match( '^(%s*[x\195]\151?10e(%-?%d+)%s*)' )
		end
		if match then
			result.e = capture
			toParse = toParse:sub( match:len() + 1 )
		end
		
		-- unités
		if Data.unit[ toParse ] or mw.ustring.match( toParse, '^%a+$' ) or toParse:match( '%b<>' ) then
			table.insert( result, toParse )
			toParse = ''
		elseif toParse ~= '' then
			local unit, exp
			toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' )
			repeat
				-- unité contenant un lien
				match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/%[%]]*%b[][^%s%d/]*) ?(%-?%d*)%s*)' )
				if not match then
					-- unité ne contenant pas de lien
					match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/]+) ?(%-?%d*)%s*)' )
				end
				if match then
					if unit:match( '%-$' ) and exp ~= '' then
						unit = unit:gsub( '%-$', '' )
						exp = '-' .. exp
					elseif exp == '-' then
						unit = match
						exp = ''
					end
					table.insert( result, unit )
					table.insert( result, exp )
					toParse = toParse:sub( match:len() + 1 )
				end
			until toParse == '' or not match
		end
		
		if toParse == '' then
			return result
		else
			-- une partie de la chaine n'a pas pu être décodée, on retourne la chaine originale
			addErrorCat = true
			return { texte }
		end
	else
		return { }
	end
end

---
-- nomUtnit retourne le nom français du code d'une unité et de son exposant.
-- si le code de l'unité n'est pas reconnu retourne 1 et false, de façon à ajouter false en première position d'une table. 
function p.nomUnit( unit, exposant )
	if not dataSuccess or type( unit ) ~= 'string' then
		return 1, false
	end
	
	-- nettoyage des liens et balise HTML
	unit = unit:gsub( '^/' , '' )
	if unit:match( '%[' ) then
		local Delink = require( 'Module:Delink' )
		unit = Delink._delink{ unit }
	end
	if unit:match( '<' ) then
		unit = unit:gsub( '%b<>', '' )
	end
	
	-- récupère le nom de l'unité
	local unitTab = Data.unit[ unit ]
	local unitPrefix = { nom = '' }
	if not unitTab then
		unitTab = Data.unit[ unit:sub( 2 ) ]
		unitPrefix = Data.prefix[ unit:sub( 1, 1 ) ]
		if not ( unitTab and unitPrefix ) then
			-- pour µ, Ki, Mi, Gi... qui sont codé sur deux octets
			unitTab = Data.unit[ unit:sub( 3 ) ]
			unitPrefix = Data.prefix[ unit:sub( 1, 2 ) ]
			if not ( unitTab and unitPrefix ) then
				unitTab = false
			end
		end
	end

	-- récupère le nom de l'exposant
	if trim( exposant ) then 
		local exp = tonumber( exposant )
		exp = exp and Data.exposant[ math.abs( exp ) ]
		exposant = exp or ' puissance ' .. exposant
	else 
		exposant = ''
	end
	
	-- assemble les deux partie
	if type( unitTab ) == 'table' and type( unitTab.nom ) == 'string' then
		return unitPrefix.nom .. unitTab.nom .. exposant 
	elseif unit:match( '[/%d]' ) then
		-- ce n'est pas du texte simple, on anule l'infobule
		return 1, false
	else
		return unit .. exposant
	end
end

function p._unite( args )
	-- remplacement de certains caractères, pour simplifier les pattern
	local nombre = p.sanitizeNum( args[1] )
	if nombre == '' then
		nombre = nil
	else	
		-- formatage du nombre
		nombre = p.formatNombres( nombre, args.arrondi, args['décimales'] )
	end
	
	local wiki = { nombre }
	
	-- fraction
	if args.fraction then
		local nom, den = args.fraction:match( '^(.-)/(.+)$' )
		if nom then
			if nom:match( '^[ %dn()=+-]+$' ) and den:match( '^[ %daeoxhklmnpst()=+-]$' ) then
				nom = nom:gsub( '[%dn()=+-]', supUnicode )
				den = den:gsub( '[%daeoxhklmnpst()=+-]', subUnicode )
			else
				nom = '<sup style="font-size: 70%; vertical-align: 0.4em;">' .. nom .. '</sup>'
				den = '<sub style="font-size: 70%; vertical-align: 0em;">' .. den .. '</sub>'
			end
			args.fraction = nom .. '⁄' .. den
		end
		table.insert( wiki, ' ' .. args.fraction )
	end
	
	-- à, et, ou, ×, – (tiret cadratin)
	local specificArgs = { '–', 'à', 'et', 'ou', '×', '××', '±'	}
	for _, name in ipairs( specificArgs ) do
		local v = trim( args[ name ] )
		if v then
			v = p.formatNombres( v )
			if name == '–' and nombre and nombre:match( '^[^−]' ) and v:match( '^[^−]' ) then
				-- pas d'espace pour le tiret cadratin entre deux nombres positifs
				table.insert( wiki, '–' )
			elseif name == '××' then
				table.insert( wiki, ' × ' )
			else
				table.insert( wiki, ' ' .. name .. ' ' )
			end
			table.insert( wiki, v )
		end
	end
	
	-- unités
	local i = 1
	local unit = trim( args[ 2 * i ] )
	local units = ''
	local nomUnits, par = {}, false
	while unit do
		local exp = p.parseNombre( args[ 2 * i + 1 ] )
		local sep = ''
		-- gestion des exposants
		local expUnit = ''
		if exp == '' then
			if unit:sub( -2 ) == '²' then
				exp = '2'
				unit = unit:sub( 1, -3 )
			elseif unit:sub( -2 ) == '³' then
				exp = '3'
				unit = unit:sub( 1, -3 )
			end
		end
		if #exp > 0 then
			expUnit = '<sup>' .. exp:gsub( '^-', '−') .. '</sup>'  -- remplace le tiret par un vrai signe moins
		end
		-- gestion de la séparation des unités et des unités en dénominateur
		if units ~= '' then
			if unit:sub( 1, 1 ) == '/' then
				sep = '/'
				unit = unit:sub( 2 )
				if not par then
					par = true
					table.insert( nomUnits, 'par' )
				end
			else
				sep = '\194\160'  -- point médian désactivé : '⋅\194\160'  
				if exp:match( '^-' ) and not par then
					par = true
					table.insert( nomUnits, 'par' )
				end
			end
		end
		-- remplacement de l'unité par son symbole
		if Data.unit[ unit ] then
			-- unit = Data.unit[ unit ].symbole
			-- désactivé car ne gère pas les multiple tel mL
		end
		units = units .. sep .. unit .. expUnit
		table.insert( nomUnits, p.nomUnit( unit, exp ) )
		i = i + 1
		unit = trim( args[ 2 * i ] )
	end
	
	-- conversion
	local unitNameString = nomUnits[1] and table.concat( nomUnits, ' ' ) or ''
	unitNameString = mw.ustring.gsub( unitNameString, '(%a)s%f[%A]', '%1' )
	local multiple = 1
	local convertTable = Data.convert[ unitNameString ]
	if not convertTable and #unitNameString > 5 then
		-- gesion des multiples (Kilo, méga, mili...)
		local prefix = Data.prefix[ unitNameString:sub( 1, 4 ) ] or Data.prefix[ unitNameString:sub( 1, 5 ) ]
		local _, par = unitNameString:find( ' par ' )
		local prefix2
		if par then
			prefix2 = Data.prefix[ unitNameString:sub( par + 1, 4 ) ] or Data.prefix[ unitNameString:sub( par + 1, 5 ) ]
		end
		if prefix and Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ) ] then
			convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ) ]
			multiple = 10 ^ prefix.puissance
		elseif prefix2 and  Data.convert[ unitNameString:gsub( ' par ' .. prefix2.nom, '' ) ] then
			convertTable = Data.convert[ unitNameString:gsub( ' par ' .. prefix2.nom, '' ) ]
			multiple = 1 / 10 ^ prefix2.puissance
		elseif prefix and prefix2 and Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ] then 
			convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ]
			multiple = 10 ^ prefix.puissance / 10 ^ prefix2.puissance
		end
	end
	if convertTable then
		if type( convertTable[1] ) ~= 'table' then
			convertTable = { convertTable }
		end
		for i, v in ipairs( wiki ) do
			local n = tonumber( p.parseNombre( v ) )
			if n then
				n = n * 10 ^ ( tonumber( p.parseNombre( args.e ) ) or 0 )
				local converted = {}
				for _, c in ipairs( convertTable ) do
					local nConverted = n
					if c.inverse then
						nConverted = 1 / n
					end
					if c.M then
						-- M = masse molaire
						local M = tonumber( args.M )
						if not M then
							break
						end
						if c.M == '*' then
							nConverted = nConverted * M
						elseif c.M == '/' then
							nConverted = nConverted / M
						end
					end
					nConverted = nConverted * multiple * c[2] + ( c[3] or 0 )
					-- format
					nConverted = p.formatNum{ nConverted, round = c.round or 6, noHtml = true }
					table.insert( converted, nConverted .. ' '.. c[1] )
				end
				wiki[ i ] = '<span title="' .. table.concat( converted, ' ou ' ) ..'">' .. v ..'</span>'
			end
		end
	end
	
	-- incertitude avec + et − séparés
	if trim( args['+'] ) then
		local approximation = '+' .. p.formatNombre( args['+'] ) .. ''
		if trim( args['−'] ) then
			approximation = approximation .. '<br> −' .. p.formatNombre( args['−'] )
		end
		table.insert( wiki, '<span style="display:inline-block; vertical-align:top; line-height:1em; font-size:80%; text-align:left;">' )
		table.insert( wiki, approximation .. '</span>' )
	end
	
	-- puissance de 10
	local exposant = trim( args.e )
	if exposant then
		exposant = p.formatNombre( exposant )
		if nombre then
			if trim( args['±'] ) and not nombre:match( '^%(' ) then
				table.insert( wiki, 1, '(' )
				table.insert( wiki, ')' )
			end
			table.insert( wiki, ' × 10<sup>' .. exposant .. '</sup>' )
		else
			table.insert( wiki, '10<sup>' .. exposant .. '</sup>' )
		end
	end
	
	-- ajoute une abbréviation si le nom de l'unité est différent de l'unité (en retirant les espace qui peuvent être devenus insécables)
	if units and nomUnits[1] and mw.ustring.gsub( table.concat( nomUnits ), '[ \194\160]', '' ) ~= mw.ustring.gsub( units, '[ \194\160]', '' ) then
		units = string.format( ' <abbr class=abbr title="%s">%s</abbr>', table.concat( nomUnits, ' ' ), units )
	elseif units ~= '' and units ~= '°' then
		units = ' ' .. units
	end
	table.insert( wiki, units )
	
	if #wiki > 0 then
		local result = table.concat( wiki )
		if result:match( ' ' ) then
			return '<span class="nowrap">' .. result .. '</span>'
		else
			return result
		end
	end
end

function p.unite( frame )
	local args
	if type( frame ) == 'table' then
		if type( frame.getParent ) == 'function' then
			args = frame:getParent().args;
		else
			args = frame
		end
	end
	if args then
		if trim( args[1] ) then
			if args[1]:match('[^%d,. -]') then
				local tempArgs = p.parseUnit( trim( args[1] ) )
				if not ( args[2] and tempArgs[2] ) then
					for k, v in pairs( tempArgs ) do
						args[k] = v
					end
				end
			end
			if args[2] and not Data.unit[ args[2] ] and not args[3] and mw.text.trim( args[2] ):match('[%d/ -]') then
				local tempArgs = p.parseUnit( trim( args[2] ) )
				args[2] = false
				if tempArgs[1] ~= false then
					table.insert( tempArgs, 1, false )
				end
				for k, v in pairs( tempArgs ) do
					if args[k] and v then
						addErrorCat = true
					end
					args[k] = args[k] or v
				end
			end
		end
		-- args alias
		args['x'] = args['×'] -- lettre x → signe multiplié
		if args['+'] then
			args['−'] = args['−'] or args['-'] -- tiret → signe moins
		else
			args['–'] = args['–'] or args['-'] -- tiret → demi-cadratin
		end
		local cat = ''
		if addErrorCat then
			cat = errorCat
		end
		return p._unite( args ) .. cat
	end
end

return p