Moduuli:sanarivi-apu

Kohteesta Wikisanakirja
Siirry navigaatioon Siirry hakuun

Tämän moduulin ohjeistuksen voi tehdä sivulle Moduuli:sanarivi-apu/ohje

local p = {}

--- Kutsuu funktiota `func` kaikille taulukon `arr` arvoille ja palauttaa tulokset taulukkona.
-- 
-- @param func: funktio muotoa f(p)
-- @param arr:  taulukko
-- @return:     taulukko
function p.map(func, arr)
   local out = {}
   for i, v in ipairs(arr) do
      out[i] = func(v)
   end
   return out
end

function p.map2(func, arr1, arr2)
   local out = {}
   local v1, v2
   
   for i = 1, math.max(#arr1, #arr2) do
      v1 = arr1[i]
      v2 = arr2[i]
      out[i] = func(v1, v2)
   end
   
   return out
end

--- Palauttaa ensimmäisen ei tyhjän arvon annetusta luettelosta.
-- 
-- Tyhjäksi arvoksi lasketaan nil ja tyhjä merkkijono.
-- Esim. 
--   p.ensimmainen_arvo{"", nil, "arvo1", "arvo2"}
-- palauttaa "arvo1".
-- 
function p.ensimmainen_arvo(params)
   for i = 1, #params do
      local val = params[i]
      if val ~= nil and val ~= "" then
         return val
      end
   end
   
   return nil
end



--- Erottaa pisteillä erotetut numerot nimen lopusta.
-- 
-- Apufunktio numeroidut_parametrit -funktiolle.
-- 
-- Esim. "mon1" -> "mon", 1;
--       "param2.3" -> "param", 2, 3;
--       "y3muoto1" -> "y3muoto", 1.
local function erota_osat(sana)
   -- Etsitään lopusta alkaen, jotta nimi voi sisältää numeroita.
   if type(sana) ~= "string" then
      return  { sana }
   end
   local rev  = sana:reverse()
   local nums = string.match(rev, "^([%d.]*)"):reverse()
   if nums == "" then
      return { sana }
   end
   local name = string.sub(sana, 1, sana:len() - nums:len())
   local ns   = {}
   
   for num in string.gmatch(nums, "([%d]+)") do
      table.insert(ns, tonumber(num))
   end

   return { name, unpack(ns) }
end

--- Muuttaa assosiatiivisen taulukon, jossa avaimet on numeroitu, sisäkkäisiksi taulukoiksi.
--
-- Syötetaulukon numerot erotetaan pisteillä (paitsi ensimmäinen). Numeroimattomat ("a") ja
-- ylemmän tason ylimääräiset arvot ("c1") jätetään pois ilman virheilmoitusta.
-- Esim. { ["a"] = "X",
--         ["a2"] = "A",
--         ["b1"] = "B",
--         ["b2"] = "C",
--         ["c1"] = "X",
--         ["c1.1"] = "D" }
-- 
--    -> { ["a"] = { [2] = "A" },
--         ["b"] = { "B",
--                   "C" },
--         ["c"] = { { "D" } } }
-- @param args:  taulukko, jossa on numeroon päättyviä avaimia
-- @param nimet: taulukkoon ainakin tulevat avaimet. Numeroton arvo tulkitaan 1:seksi.
--               Esim. "mon" = "mon1" -> { "mon" = { [1] = ... } }
function p.numeroidut_parametrit(args, nimet)
   local out = {}
   local cur_node
   local path_
   local cur_p

   if nimet then
      for i, v in ipairs(nimet) do
         if args[v] then
            out[v] = {}
            out[v][1] = args[v]
         end
      end
   end

   for key, val in pairs(args) do
      path_ = erota_osat(key)
      cur_node = out

      if #path_ > 1 then
         -- Etsitään/luodaan polun päätepiste.
         for i = 1, (#path_ - 1) do
            cur_p = path_[i]
            
            -- Jos type on "string", on annettu kaksi eritasoista samannimistä arvoa. Ohitetaan
            -- äänettömästi, koska monissa mallineissa on esim. "mon" ja "mon2".
            if type(cur_node[cur_p]) ~= "table" then
               cur_node[cur_p] = {}
            end
            cur_node = cur_node[cur_p]
         end
         
         cur_p = path_[#path_]
         
         -- Jos ei ole nil, sama kuin yllä. (Avaimet tulee satunnaisessa järjestyksessä.)
         if cur_node[cur_p] == nil then
            cur_node[cur_p] = val
         end
      end
   end

   return out
end



--- vanhentunut POIS -- POIS -- POIS -- POIS -- POIS -- POIS -- POIS -- POIS --

-- Palauttaa parametrisoitu_teksti -funktion rakenne-parametrissa käytettävän
-- erottimen.
function p.uusi_erotin(sep, w)
   local m = { ["string"] = sep, ["prec"] = w }
   
   return m
end

function p.parametrisoitu_teksti(rakenne, args, int)
   local out = {}
   local TYHJA = p.uusi_erotin("", 0)

   --- Apufunktio, jota kutsutaan rekursiivisesti alilausekkeille.
   --  
   -- Huom. rekursion tasosta riippumattaa kaikki tulostus menee suoraan out-taulukkoon.
   -- @param branch: kaavan alilauseke
   -- @return:       true, jos muuttujia asetettiin.
   function apu(branch, int)
      local pref_pos = #out + 1        -- Prefiksin paikka.
      local pref     = branch[1]       -- Alilausekkeen prefiksi. Tulostaan jos sisältöä löytyy.
      local suff     = branch[#branch] -- Alilausekkeen suffiksi.
      local sep      = TYHJA           -- Erotin.
      local sep_pos  = -1              -- Erottimen paikka.
      local asetettu = false           -- Lippu, joka asetetaan kun yksikin muuttuja on asetettu,
      -- jonka jälkeen se on päällä funktion loppuun asti.
      local sep_asetettu               -- Kertoo onko välittömästi edellinen asetettu. Nollataan joka kierroksella.
      local val

      table.insert(out, "") -- prefiksin paikka

      for i = 2, #branch - 1 do
         table.insert(out, "")
         sep_asetettu = false
         if i % 2 == 0 then  -- Muuttuja tai alilauseke.
            val = branch[i]
            
            if type(val) == "table" then       -- Arvo on alilauseke.
               if apu(val, int + 2) then
                  asetettu = true
                  sep_asetettu = true
               end
            elseif args[val] and args[val] ~= "" then -- Arvo on (ei-tyhjä) muuttuja.
               out[#out] = args[val]
               asetettu = true
               sep_asetettu = true                
            end

            -- Jos muuttujan arvo asetettiin ja "jonossa" on erotin, asetetaan
            -- erotin tulostukseen.
            if sep_asetettu and sep_pos > -1 then
               out[sep_pos] = sep.string
               sep = TYHJA
               sep_pos = -1
            end
            
         else  -- Erotin.
            
            -- Jos ensimmäinen erotettava on löytynyt, tallennetaan erotin. 
            -- Se lisätään tulosteeseen kuitenkin vasta, kun toinenkin löytyy.
            -- Jos joku erotin on jo tallennettu, katsotaan kumman presedenssi on suurempi.
            if asetettu and sep.prec <= branch[i].prec then
               sep_pos = #out
               sep = branch[i]
            end
         end
      end

      table.insert(out, "") -- suffiksin paikka

      -- Jos sisältöä saatiin asetetaan mahdollinen prefiksi ja suffiksi.
      if asetettu then
         out[pref_pos] = pref
         out[#out]     = suff
         return true
      else
         return false
      end
   end

   apu(rakenne, 0)

   return table.concat(out, "")
end

return p