Moduuli:ParametrisoituTeksti

Tässä ohjeessa kuvataan toiminnallisuutta jonka kehitys on vielä kesken. Sivu on tarkoitettu lähinnä kehityksen apuvälineeksi, ei yleiseen käyttöön.

Kirjaston avulla voi tehdä helposti tekstejä, joissa ehdollisia kohtia. Kirjaston avulla voi varmistaa, että lopputulos on järkevä rippumatta siitä mikä yhdistelmä parametreja on annettu.

Moduulin funktiot muokkaa

muotoile muokkaa

muotoile(rakenne)

Pääfunktio, joka prosessoi annetun elementtirakenteen ja palauttaa sen tuottaman tekstin. Tämä on lyhyt tapa tehdä peräkkäin uusi_muotoilija, prosessoi ja tostring(muotoilija)

uusi_muotoilija muokkaa

uusi_muotoilija()

Luo uuden muotoiluolion. Tätä ei tarvitse kutsua suoraan, jos käytetään muotoile-funktiota.

Rakenne-elementit muokkaa

Tekstin rakenne kuvataan alla luetelluilla elementeillä. Kun elementti evaluoidaan, se voi tuottaa pätkän tekstiä tai tyhjän arvon, mikä vaikuttaa sen sisältävien elementtien tuottamaan tulokseen.

Jotta koodista saa selkeämmän rakenne-elementit kannattaa tuoda moduulin nimiavaruuteen:

local pt = require("Moduuli:ParametrisoituTeksti")
local ryhma, muuttuja, luettelo, tai = pt.ryhma, pt.muuttuja, pt.luettelo, pt.tai

muuttuja muokkaa

muuttuja(m)

Yksinkertaisin elementti, jolla merkitään ehdolliset kohdat eli ne kohdat, joilla joko on arvo tai ei ole arvoa.

Parametrit:

  1. Lua-muuttuja, joka sisältää merkkijonon tai arvon nil. Tyhjä merkkijono ja nil luetaan tyhjiksi.
local m1 = "eka"
local m2 = ""
local m3 = nil

ryhma("(", muuttuja(m1), ")") -- Tulos: "(eka)"
ryhma("(", muuttuja(m2), ")") -- Tulos: ""
ryhma("(", muuttuja(m3), ")") -- Tulos: ""

ryhma muokkaa

ryhma(prefiksi, elementti1, [erotin1, elementti2, [erotin2, elementti3...]], suffiksi)

Tärkein elementti, jolla merkitään ehdollista tekstiä.

Parametrit:

  • Vaihtuva määrä parametreja.
    • Ensimmäinen parametri on aina prefiksi ja viimeinen suffiksi. Prefiksi ja suffiksi tulostetaan, jos yksikin elementti on ei-tyhjä.
    • Elementit: tässä luetelluilla rakenne-elementeillä merkittyjä alirakenteita.
    • Erottimet: Merkkijonoja, jotka lisätään tulostukseen, jos niiden molemmilla puolilla olevilla muuttujilla on arvo. Erotinta voi edeltää numeroarvo, joka kertoo erottimen presedenssin toisiin erottiimiin nähden (oletusarvo on 0).

Esimerkkejä:

local m1 = "na"
local t1 = nil

ryhma("''", muuttuja(m1), "''-taivutus")  -- tulos: "''na''-taivutus"
ryhma("''", muuttuja(t1), "''-taivutus")  -- tulos: ""
local m1 = "eka"
local m2 = "toka"
local m3 = "kolkki"
local t1 = nil

ryhma("(", muuttuja(m1), ", ", muuttuja(m2), "; ", muuttuja(m3), ")")  -- tulos: "(eka, toka; kolkki)"
ryhma("(", muuttuja(m1), ", ", muuttuja(m2), "; ", muuttuja(t1), ")")  -- tulos: "(eka, toka)"
ryhma("(", muuttuja(t1), ", ", muuttuja(m2), "; ", muuttuja(m3), ")")  -- tulos: "(toka; kolkki)"
ryhma("(", muuttuja(t1), ", ", muuttuja(m2), "; ", muuttuja(t1), ")")  -- tulos: "(toka)"
ryhma("(", muuttuja(t1), ", ", muuttuja(t1), "; ", muuttuja(t1), ")")  -- tulos: ""

-- Tässä tulokseen tulee puolipiste (;), koska sen sille on annettu suurempi presedenssi (1) kuin pilkulle (0).
ryhma("(", muuttuja(m1), ", ", muuttuja(t1), 1, "; ", muuttuja(m3), ")")  -- tulos: "(eka; kolkki)"

luettelo muokkaa

luettelo(taulukko, erotin, viimeisen_erotin)

Erottimella erotellun luettelon tekemiseen.

Parametrit:

  1. taulukko lueteltavista elementeistä
  2. erotin, jolla arvot erotellaan
  3. toisiksi viimeisen ja viimeisen arvon välinen erotin, jos eri kuin erotin
local m1 = { "eka", "toka", "kolkki" }

luettelo(m1, ", ", " tai ") -- Tulos: "eka, toka tai kolkki"
local m1 = { "A", "B", nil, "D" }
local m2 = { "1", nil, "3", "4" }

luettelo(ryhma("", m1, " ", ryhma("(", m2, ")"), ""), ", ", " tai ") -- Tulos: "A (1), B, (3) tai D (4)"

Luetteloita voi olla myös sisäkkäin. Tällöin muuttujien pitää olla vastaavan syvyisiä puita.

  • yksittäisessä luettelossa taulukko on muotoa { "1", "2", "3" },
  • kun luettelo on toisen sisällä, taulukko on muotoa { { "1a", "1b" }, { "2a" }, { "3a", "3b" } },
  • jne.

tai muokkaa

tai(elementti1, [elementti2, [elementti3...]]])

Lausekkeen tulos on ensimmäisen ei tyhjän rakenne-elementin arvo.

Parametrit:

  • Mielivaltainen määrä muita rakenne-elementtejä.
local m1 = nil
local m2 = "toka"
local m3 = "kolkki"

tai(muuttuja(m1), muuttuja(m2)) -- Tulos: "toka" 
tai(muuttuja(m2), muuttuja(m3)) -- Tulos: "toka"


funktio muokkaa

funktio(fun, param1, [param2, [param3...]]])

Tällä elementillä voi tehdä monimutkaisempia ehtoja, joita ei muilla elementeillä voi tehdä.

Parametrit:

  • fun: funktio muotoa f(ctx, [param1, [param2, [param3...]]]), jossa ctx on viittaus muotoilija-olioon.
  • muut parametrit: funktiolle mahdollisesti annettavat parametrit

Funktiossa pääse käsiksi parametrien arvoihin muotoilijaolion metodeilla hae_arvo ja hae_arvo_tai_nil.

Tekstiä lisätään tulostukseen lisäämällä muotoilijaolion out-kenttään.

Funktion pitää palauttaa true, jos tekstiä lisättiin, muuten false.

Huomaa, että se ovatko funktion parametrit elementtejä riippuu funktiosta.

Esim.

local function _sukuteksti(ctx, suku)
   local lyh = ctx:hae_arvo_tai_nil(suku)
   
   local txt = mw.getCurrentFrame():expandTemplate{ ['title'] = 'suku-teksti', ['args'] = { lyh } }
   if txt then
      table.insert(ctx.out, txt)
      return true
   end
   
   return false
end

-----
local m1 = "f"

funktio(_sukuteksti, m1) -- Palauttaa mallinekutsun {{sukuteksti|f}} arvon.

Sanarivimallineissa usein esiintyviä toimintoja on koottuu moduuuliin LibSanarivi.

Huomaa, että alirakenteita voi myös nimetä käyttäen Luan funktioita apuna. Tässä ei kuitenkaan ole kyse funktioelementistä.

function p.tailuettelo(...)
   return luettelo({...}, ", ", " tai ")
end

Muotoilu-olion metodit muokkaa

hae_arvo muokkaa

hae_arvo(m)

Palauttaa annetun parametrin arvon. Tätä käytetään funktio-elementissä. Arvoa ei pidä lukea suoraan ilman tätä metodia, koska metodi palauttaa oikean arvon myös silloin kun funktio on luettelon sisällä.

hae_arvo_tai_nil muokkaa

hae_arvo_tai_nil(m)

Sama kuin hae_arvo, mutta palauttaa nil myös, kun arvo on tyhjä merkkijono. Tämä on yleensä se, mitä halutaan.

prosessoi muokkaa

prosessoi(rakenne)

Lisää annetun elementtirakenteen tuottaman tekstin tulostukseen. Palauttaa true, jos tekstiä tulostettiin, muuten false.

Tätä voi kutsua funktio-elementissä, kun sille annetaan parametrina elementtirakenne.

Esimerkki tai-elementin toteutuksesta:

local function _tai(ctx, ...)
   local params = {...}
   local ret
   
   for i = 1, #params do
      ret = ctx:prosessoi(params[i])
      if ret then
          return true
      end
   end
   return false
end

-- Käyttö:
funktio(_tai, luettelo(mon1, mon2, mon3, ", ", " ja "), muuttuja("ei monikkoa")) -- TODO tarkista

--- Moduuli tekstin muotoiluun.
local p = {}

function p:new (args)
   local o = {
      args = args,
      -- Taulukko, johon "tulostetaan".
      out = {},   
      -- Nykyinen sijainti luetteloissa. Esim. {2, 3} tarkoittaa toisen luettelon
      -- kolmatta alkiota. Jokainen luettelo lisää uuden tason.
      pos = {} }
   
   setmetatable(o, self)
   self.__index = self
   
   return o
end

function p:uusi_muotoilija(args)
	return p:new(args)
end

--- Muuttaa lopputuloksen merkkjonoksi.
function p:__tostring ()
   return table.concat(self.out, "")
end

--- Palauttaa muuttujan sisäisen pos-jäsenen osoittaman arvon muuttujasta.
--
-- Esim.
--   Jos var = "a" ja pos = { }, palauttaa "a".
--   Jos var = { { "a1", "a2" }, { "b1" } } ja pos = { 2, 1 }, palauttaa "b1".
-- @param var: muuttuja
-- @return     arvo. Jos arvoa ei ole tai se on tyhjä, palauttaa nil.
--             Jos osoite osoittaa syvemmälle kuin muuttujassa on kerroksia,
--             antaa virheen "odotettiin taulukkoarvoa". Jos osoite osoitaa
--             olemassa olevan kerroksen arvojen ulkopuolelle, palautta nil.
function p:hae_arvo(var)
   -- Arvon osoite muuttujassa.
   local pos = self.pos 
   local cur = var
   
   if cur == nil then
      return nil
   end
   
   for d = 1, #pos do
      i = pos[d]
      if cur == nil or ((type(cur) == "table" and i > #cur)) then
	 return nil
      end
      cur = cur[i]
   end

   if d ~= #pos and cur == nil then
      error("odotettiin taulukkoarvoa kohdassa: [" .. table.concat(pos, ", ") .. "] löytyi: " .. var)
   end

   return cur
end

--- Sama kuin hae_arvo, mutta palauttaa nil, jos arvo on "".
--
-- @param var: muuttujan arvo
-- @return:    kuten hae_arvo, mutta palauttaa nil jos arvo on tyhjä ("").
function p:hae_arvo_tai_nil(var)
   local val = self:hae_arvo(var)
   if val and val ~= "" then
      return val
   end
   return nil
end

--- Lisää muttujan tulostukseen, jos se ei ole tyhjä.
--
-- @param ctx: konteksti
-- @param var: muuttujan arvo
-- @return:    true, jos muuttuja lisättiin tulostukseen, muuten false.
local function _muuttuja(ctx, var)
   local out = ctx.out
   local val = ctx:hae_arvo(var)
   if val and val ~= "" then
      table.insert(out, val)
      return true
   else
      return false
   end
end


--- Ryhmä-toiminnon toteutus.
-- 
-- @param ctx: konteksti
-- @param ...: Taulukko, jonka ensimmäinen arvo on prefiksi ja viimeinen arvo suffiksi.
--             2:sta eteenpäin joka toinen arvo on alirakenne tai muuttuja ja
--             joka toinen erotin. Erotinta voi kuitenkin edeltää numero, joka
--             kertoo sen presedenssin suhteessa toisiin erottimiin.
-- @return:    true, jos yksikin alilauseke palautti true, muuten false.
local function _ryhma(ctx, ...)
   local out    = ctx.out
   local kaava  = {...}
   local prefix_pos         -- Prefiksin indeksi out-taulukossa.
   local subval             -- Alilausekkeen palauttama tulos (bool).
   local retval = false     -- Oma palautusarvo. Asetetaan trueksi, kun yksikin subval palauttaa true.
   local sep                -- Edellinen erotin.
   local sep_prec = 0       -- Edellisen erottimen presedenssi.
   local sep_pos  = 0       -- Edellisen erottimen paikka out-taulukossa.
   local odotus   = "arvo"  -- Arvo, jota odotetaan seuraavaksi ["arvo", "erotin"].
   local prec     = 0       -- Seuraavan erottimen presedenssi. Asetetaan, jos erotinta edeltää numero.
   local cur                -- Lausekkeen kohta, jossa ollaan.

   -- Lisätään varaus prefiksille.
   table.insert(out, "")
   prefix_pos = #out

   for i = 2, #kaava - 1 do
      cur = kaava[i]

      if odotus == "arvo" then            -- Alilauseke tai muuttuja.
	 subval = ctx:prosessoi(cur)

	 -- Asetetaan trueksi ja pidetään truen, jos yksikin alilauseke on true.
	 retval = subval or retval 

	 if subval and sep_pos > 0 then
	    out[sep_pos] = sep            -- Asetetaan edellinen erotin.
	    sep_prec = 0
	    sep_pos = 0
	 end
	 odotus = "erotin"
      elseif cur == tonumber(cur) then    -- Presedenssiarvo.
	 prec = cur
      else                                -- Erotin.
	 -- Asetetaan varaus erottimelle, jos aiemmin on ollut ainakin yksi arvo.
	 if retval and sep_prec <= prec then
	    table.insert(out, "")
	    sep_pos = #out
	    sep = cur
	    sep_prec = prec
	 end
	 prec = 0
	 odotus = "arvo"
      end
   end

   if retval == true then
      out[prefix_pos] = kaava[1]        -- prefiksi
      table.insert(out, kaava[#kaava])  -- suffiksi
   end

   return retval
end


--- Luettelotoiminnon toteutus.
--
-- @param ctx:    konteksti
-- @param sub:    toistettava alilauseke
-- @param sep:    yleinen erotin
-- @param sepn:   viimeistä edeltävä erotin
-- @return:       true, jos yksikin alkio palautti true.
local function _luettelo(ctx, sub, sep, sepn)
   local out  = ctx.out
   local pos  = ctx.pos
   
   -- Nykyisen ja edellisen erottimen kohta out-taulukossa.
   local sep_pos, prev_sep_pos

   local retval = false   -- Oma paluuarvo.
   local ret              -- Alilausekkeiden paluuarvo.

   -- Käytetään yleistä erotinta, jos erillistä viimeistä ei ole annettu.
   sepn = sepn or sep

   table.insert(pos, 1)   
   ret = ctx:prosessoi(sub)
   while ret do
      retval = true

      prev_sep_pos = sep_pos
      table.insert(out, sep)
      sep_pos = #out
      
      pos[#pos] = pos[#pos] + 1
      ret = ctx:prosessoi(sub)
   end
   table.remove(pos)      

   if sep_pos then
      out[sep_pos] = ""
      if prev_sep_pos then
	 out[prev_sep_pos] = sepn
      end
   end

   return retval
end


--- Tai-toiminnon toteutus.
--
-- @param ctx: konteksti
-- @param ...: taulukko vaihtoehdoista
-- @return:       true, jos yksikin vaihtoehdoista palautti true,
--                false, muuten
local function _tai(ctx, ...)
   local params = {...}
   local ret
   
   for i = 1, #params do
      ret = ctx:prosessoi(params[i])
      if ret then
	 return true
      end
   end
   return false
end


--- Tutkii kaavan uloimman osan ja ohjaa sen oikealle käsittelijälle.
--
-- @param kaava: lauseke
function p:prosessoi(kaava)
   if type(kaava) == "table" and kaava.funk ~= nil then
      return kaava.funk(self, unpack(kaava.params))
   else
      return _muuttuja(self, kaava)
   end
end

--- Peruskäyttöä yksinkertaistava funktio, joka muuttaa annetun rakenteen tekstiksi.
-- 
-- @param kaava: lauseke
-- @return:      tulostettava teksti
function p.muotoile(kaava)
	local m = p:uusi_muotoilija()
	m:prosessoi(kaava)

   return tostring(m)
end


---------------
-- Toiminnot --
---------------


--- Muuttuja (korvaava)
-- @param var: muuttujan arvo
function p.muuttuja(var)
   return { ["funk"] = _muuttuja, ["params"] = { var } }
end


-- Tulostaa luettelon erottimilla erotettuna.
--
-- @param rak:   toistettava rakenne
-- @param sep:   yleinen erotin
-- @param sepn:  (valinnainen) viimeistä edeltävä erotin
function p.luettelo(rak, sep, sepn)
   return { ["funk"] = _luettelo, ["params"] = { rak, sep, sepn } }
end


--- Tulostaa parametrit prefiksillä ja suffiksillä ympäröitynä ja erottimilla erotettuna.
function p.ryhma(...)
   return { ["funk"] = _ryhma, ["params"] = {...} }
end


--- Kokeilee vuorollaan jokaista annettua vaihtoehtoa kunnes joku palauttaa true.
function p.tai(...)
   return { ["funk"] = _tai, ["params"] = {...} }
end

--- Muu funktio.
function p.funktio(funk, ...)
   return { ["funk"] = funk, ["params"] = {...} }
end

return p