Spring til indhold

Modul:Formattal

Page extended-protected
Fra Wikipedia, den frie encyklopædi
Version fra 26. nov. 2017, 16:08 af Jhertel (diskussion | bidrag) Jhertel (diskussion | bidrag) (Fjerner version 9308127 af Jhertel (diskussion) Forkert modul!)

local p = {}
p.angivFejlMedRoedStjerneOgFejlbeskrivelse = false
local fejlkategorikode = "[[Kategori:Sider med tal hvis format ikke kendes af formattal]]"


--[[
  formattal(frame)
  
  Formaterer et ikke-negativt tal med højst to decimaler på dansk. Det 
  oprindelige tal kan enten bruge punktum som decimaladskiller og komma som 
  tusindadskiller ("123,456,789.12"), eller komma som decimaladskiller og 
  punktum som tusindadskiller ("123.456.789,12").
  
  Uanset hvordan tallet var formateret i input, formateres det i output på dansk
  vis med punktum som tusindadskiller og komma som decimaladskiller.
  
  Eksempler:
    Opfattes som 6-cifret heltal, da tallet ikke må have mere end to decimaler:
      formattal("123456") = "123.456"
      formattal("123,456") = "123.456" 
      formattal("123.456") = "123.456"
    Opfattes som 5-cifret tal med 2 decimaler:
      formattal("123,45) = "123,45"
      formattal("123.45") = "123,45"
    Giver fejl for ukendt format (negativt tal):
      formattal("-123") = "-123"
    Giver fejl for ukendt format (tusindadskiller og decimaltegn er ens):
      formattal("123.456.7") = "123.456.7" 
    Giver fejl for ukendt format (bruger forskellige tusindadskillere eller
    har for mange decimaler):
      formattal("123.456,789") = "123.456,789"

  Alle HTML-kommentarer og visse blanktegn i tallet ignores; andre uvedkommende
  tegn vil give fejl.

  Ved fejl returneres tallet uændret samtidig med at artiklen placeres i 
  fejlkategorien "Sider med tal hvis format ikke kendes af formattal".
]]
function p.formattal (frame)
    local originaltInput = (frame.args[1] or "")
	
	-- Fjern HTML-kommentarer og ydre blanktegn.
    local inputUdenEventuelleHtmlKommentarerOgBlanktegn 
		= p.fjernHtmlkommentarerOgYdreBlanktegn(originaltInput)

	-- Opdel i inputtal (fx "-1.234.553,43" eller "") 
	-- og inputrest (fx "<ref>…</ref>" eller "").
	local inputtal, inputrest 
		= p.opdelInputITalOgRest(inputUdenEventuelleHtmlKommentarerOgBlanktegn)

	-- Opdel tallet (inputtal) i enkeltdele.
	-- Dette kan fejle, og så vil fejltekstEllerNil være ikke-nil og indeholde
	-- en fejlbeskrivelse.
    local eventueltFortegnEllerTom, heltalsdelUdenTusindadskillere, 
    	decimalerEllerNil, fejltekstEllerNil = p.opdelTal(inputtal)

	-- Returner output (enten formateret tal eller fejl).
	if not fejltekstEllerNil then
    	-- Succes. Tallet er fuldt fortolket og opdelt i enkeltdele.
    	local outputtal
    		= p.danOutputtal(
    			eventueltFortegnEllerTom, 
				heltalsdelUdenTusindadskillere, 
				decimalerEllerNil) 

		return outputtal .. inputrest

	else
    	-- Fejlsituation.
    	local fejltekst = fejltekstEllerNil -- Den kan ikke længere være nil.
        return p.danSamletFejlreturstreng(inputtal, inputrest, fejltekst, 
        	p.angivFejlMedRoedStjerneOgFejlbeskrivelse)
    end

end

function p.opdelInputITalOgRest(s)

	-- Opdel i inputtal (fx "-1.234.553,43") og inputrest (fx "<ref>…</ref>" eller "").
	-- Mønsteret er designet, så det ikke kan fejle.
	local inputtal, inputrest = s:match("^([+-]?[%d,.]*)(.*)$")
	
	return inputtal, inputrest
end


function p.fjernHtmlkommentarerOgYdreBlanktegn(s)
	
	-- Fjern HTML-kommentarer
    local sUdenEventuelleHtmlKommentarer 
    	= string.gsub(s, "<!%-%-.-%-%->", "") 

    -- Fjern blanktegn i enderne
    local sUdenEventuelleHtmlKommentarerOgBlanktegn 
    	= mw.text.trim(sUdenEventuelleHtmlKommentarer)
    	
    return sUdenEventuelleHtmlKommentarerOgBlanktegn
end

function p.danOutputtal(eventueltFortegnEllerTom, 
		heltalsdelUdenTusindadskillere, decimalerEllerNil)

	local heltalsdelMedTusindadskillere = p.indsaetTusindadskillere(heltalsdelUdenTusindadskillere)
	local decimaldel = p.danDecimaldel(decimalerEllerNil) -- "" eller ",12"
	local samletTal
		= eventueltFortegnEllerTom .. heltalsdelMedTusindadskillere ..  decimaldel

    return samletTal
end

function p.danDecimaldel(decimalerEllerNil)

    if decimalerEllerNil then
		return "," .. decimalerEllerNil
	else
		return ""
	end
	
end

function p.fejlangivelseskode(fejltekst, angivFejlMedRoedStjerneOgFejlbeskrivelse)
	
	if angivFejlMedRoedStjerneOgFejlbeskrivelse then
		return '<sup><span style="color:red" title="Skabelonen Formattal kan ikke konvertere dette tal: ' .. fejltekst .. '">*</span></sup>'
	else
		return ""
	end
	
end

function p.danSamletFejlreturstreng(inputtal, inputrest, fejltekst, 
	angivFejlMedRoedStjerneOgFejlbeskrivelse)
     
    return 
    	inputtal 
    	.. p.fejlangivelseskode(fejltekst, angivFejlMedRoedStjerneOgFejlbeskrivelse)
    	.. inputrest 
    	.. fejlkategorikode

end


--[[
	Returnerer 
		eventueltFortegnEllerTom, heltalsdelUdenTusindadskillere, decimaler, fejltekstEllerNil.

	Hvis fejltekstEllerNil ikke er nil, gik det godt. Ellers indeholder fejltekstEllerNil en 
	beskrivelse af fejlen.

	Decimaler er nil, hvis der ingen decimaler er.
]]
function p.opdelTal(inputtal)

	local fejltekstEllerNil = nil
	local heltalsdelUdenTusindadskillere = nil

    -- Fjern et evt. fortegn på første position og husk det.
    local eventueltFortegnEllerTom, inputtaldelUdenEventueltFortegn
    	= p.opsplitIFortegnOgRest(inputtal)

	-- Prøv at matche et kommatal med 1 eller 2 decimaler.
    local heltalsdelMedEventuelleTusindadskillere, decimaladskiller, decimaler
    	= inputtaldelUdenEventueltFortegn:match("^([%d,.]-)([,.])(%d%d?)$")

	-- Afgør om der var et match.
	-- Hvis ikke der er et match, vil både heltalsdel, decimaladskiller 
	-- og decimaler være nil.
	local derVarEtMatch = (heltalsdelMedEventuelleTusindadskillere ~= nil)

    if derVarEtMatch then
    	-- Der var et match: Tallet er et kommatal med 1 eller 2 decimaler.
    	
    	-- Bestemt tusindadskillertegnet.
    	-- Bemærk at decimaladskiller logisk kun kan være "." eller ","
    	-- pga. mønsteret ovenfor.
    	local tusindadskillertegnIInput = p.modsatAdskillertegn(decimaladskiller)

    	-- Fjern alle tusindadskillertegn fra heltalsdel.
        heltalsdelUdenTusindadskillere 
        	= p.removeAll(heltalsdelMedEventuelleTusindadskillere, "[" .. tusindadskillertegnIInput .. "]")

    else
    	-- Der var ikke et match. Hvis tallet er på korrekt form, er det et heltal.

	    local heltalsdelMedEventuellePunktumtusindadskillere
    		= inputtaldelUdenEventueltFortegn:match("^([%d.]+)$")
    	
    	if heltalsdelMedEventuellePunktumtusindadskillere then
    		heltalsdelUdenTusindadskillere 
    			= p.removeAll(heltalsdelMedEventuellePunktumtusindadskillere, "%.")
    	else
		    local heltalsdelMedEventuelleKommatusindadskillere
	    		= inputtaldelUdenEventueltFortegn:match("^([%d,]+)$")

			if heltalsdelMedEventuelleKommatusindadskillere then
	    		heltalsdelUdenTusindadskillere 
	    			= p.removeAll(heltalsdelMedEventuelleKommatusindadskillere, ",")
			else
				-- Angiv fejl.
				heltalsdelUdenTusindadskillere = nil  
				
				if inputtal == "" then
					fejltekstEllerNil = "Tallet er tomt."
				else
					fejltekstEllerNil = "Kunne ikke fortolke tallet '" .. inputtal .. "'."
				end
			end
		end
    end

	if not fejltekstEllerNil then
		-- Håndter situationer som ".12" = 0,12.
		if heltalsdelUdenTusindadskillere == "" then
			heltalsdelUdenTusindadskillere = "0"
		end
	
		-- Tjek for ugyldige tusindadskillere.
	    if heltalsdelUdenTusindadskillere:match("%D") then
	    	-- Fejlsituation: Heltalsdelen uden tusindadskillere indeholder
	    	-- mindst ét ikke-ciffer (%D), som kun kan være en forkert
	    	-- tusindadskiller.
	    	fejltekstEllerNil = "Heltalsdelen '" .. heltalsdelUdenTusindadskillere .. "' indeholder ugyldige tusindadskillere; hele tallet er '" .. inputtal .. "'."
	    	eventueltFortegnEllerTom = ""
	    	heltalsdelUdenTusindadskillere = nil
	    	decimaler = nil
		end
	end
	
	return eventueltFortegnEllerTom, heltalsdelUdenTusindadskillere, decimaler, fejltekstEllerNil
end


function p.removeAll(s, pattern)
	return s:gsub(pattern, "")
end


--[[
	Returner det modsatte adskillertegn til det givne.
	
	adskillertegn:
		Enten "." eller ",".

	Returværdi:
		"." hvis adskillertegn er ",".
		"," hvis adskillertegn er ".".
]]
function p.modsatAdskillertegn(adskillertegn)
    if adskillertegn == "." then
    	return ","
    else
        return "."
    end
end

function p.opsplitIFortegnOgRest(s)

    local eventueltFortegnEllerTom, reststrengUdenFortegn = s:match("^([+-]?)(.*)$")

	return eventueltFortegnEllerTom, reststrengUdenFortegn

end

--[[
	p.indsaetTusindadskillere(heltalsStreng)
	
	Hjælpefunktion. 
	
	Indsæt tusindadskillere i en given streng af cifre.

	Eksempler:	
		p.indsaetTusindadskillere("") == ""
		p.indsaetTusindadskillere("1") == "1"
		p.indsaetTusindadskillere("12") == "12"
		p.indsaetTusindadskillere("123") == "123"
		p.indsaetTusindadskillere("1234") == "1.234"
		p.indsaetTusindadskillere("123456789") == "123.456.789"
]]
function p.indsaetTusindadskillere(heltalsstreng)
	local akkumulator = heltalsstreng -- Heltal uden tusindadskillere, fx "123456789".
	local antalTusindAdskillereIndsat

    repeat  
        akkumulator, antalTusindAdskillereIndsat 
        	= string.gsub(akkumulator, "^(%d+)(%d%d%d)", '%1.%2')
    until antalTusindAdskillereIndsat == 0

    return akkumulator  -- Heltal med tusindadskillere, fx "123.456.789".
end

return p