Moduuli:Translitteroija/kaannin

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

Muuttaa muunnossäännöt muuntimen ymmärtämään muotoon.

Muunnostaulukko ja tyhjä taulukko annetaan parametrina populate_conversion_table -funktiolle:

local conversions = {}

compiler.populate_conversion_table(conversions, muunnokset)

return conversions

Muunnossääntötaulukko koostuu riveistä, jotka kertovat mikä merkkijono ja missä ympäristössä munnetaan miksikin.

Jokainen muunnostaulukon rivi on itsessään taulukko, jossa on 4, 2 tai 1 alkio.

Kun merkki tai merkkijono jätetään ennalleen käytetään 1 alkion muotoa:

{merkkijono}

Esim.

{"-"}

Kun merkki muutetaan konteksista riippumatta, käytetään kahden alkion muotoa.

{lähdemerkkjono,   kohdemerkkijono}

Esim.

{"Б",   "B"}

Muuten käytetään 4 alkion muotoa. Neljän alkion muodossa 1. alkio on edeltävä merkkijono, 2. on muutettava merkkijono, 3. seuraava merkkijon ja 4. kohdemerkkijono.

{edeltävä, lähde, seuraava,   kohde}

Esim. ё muunnetaan o:ksi vain jos se sitä edeltää Ж:

   {'Ж', "ё", '',   "o"},

(Tässä kontekstimerkkijonot ja muunnokseen osallistuvat merkkijonot on tyylitelty erilaisilla lainausmerkeillä, mutta niillä ei ole merkitystä säännönkannalta.)

Huom. Jos kaksi sääntöä on ristiriidassa siten, että toisen edeltävä merkkijono sisältyy toisen muunnettavan merkkijonoon, käytetään sitä, jossa kaikki teksti on muunnettavassa merkkijonossa.

Kun sääntötaulukkoon ei tarvitse enää tehdä muutoksia, voi sen hakea funktiolla ??? ja siirtää muunnostaulukon paikalle, jotta sääntötaulukkoa ei tarvitse muuttaa muunnostaulukoksi joka kerta.


--- Muuttaa säännöt taulukoksi ja hakemistoiksi.

local common = require("Moduuli:Translitteroija/yhteinen")

local export = {}


local log = false
local tostring = common.tostring
local get_index_key = common.get_index_key



local function values(str)
    return { string.byte(str, 1, string.len(str)) }
end

local function compare_rows(a, b)
    -- Verrattava kokonaan (input .. suffiksi)
    if a[5] < b[5] then
        return true
    elseif a[5] > b[5] then
        return false
    end

    -- Prefiksi
    if a[2] < b[2] then
        return true
    elseif a[2] > b[2] then
        return false
    end

    return false
end   


--- Tekee taulukon, joka on muuten kuin varsinainen muunnostaulukko, mutta tekstit on
--  tekstinä, ei numeroina. Järjestää taulukon haulle sopivaksi.
local function create_intermediate_table(conversions_in)
    local tmp = {}

    local input_prefix, input_replaced, input_suffix, output_replacing
    local input_prefix_len, input_replaced_len, input_suffix_len, output_replacing_len
    
    for i, row in ipairs(conversions_in) do
        if #row == 1 then
            input_prefix     = ""
            input_replaced   = row[1]
            input_suffix     = ""
            output_replacing = row[1]
        elseif #row == 2 then
            input_prefix     = ""
            input_replaced   = row[1]
            input_suffix     = ""
            output_replacing = row[2]
        elseif #row == 4 then
            input_prefix     = row[1]
            input_replaced   = row[2]
            input_suffix     = row[3]
            output_replacing = row[4]
        else
            error("Virheellinen syöte: " .. tostring(row))
        end

        input_prefix_len     = string.len(input_prefix)
        input_replaced_len   = string.len(input_replaced)
        input_suffix_len     = string.len(input_suffix)
        output_replacing_len = string.len(output_replacing)

        tmp[i] = {
            input_prefix_len,	                                     -- 1
            input_prefix,                                               -- 2	 
            input_replaced_len + input_suffix_len,                      -- 3
            input_replaced_len,                                         -- 4
            input_replaced .. input_suffix,                             -- 5
            output_replacing_len,                                       -- 6
            output_replacing                                            -- 7
        }
    end

    table.sort(tmp, compare_rows)

    return tmp
end


local function create_index(output_table, size_offset, start_offset)
    local index_first = {}
    local index_last = {}   
    local offset
    local size
    local current
    
    for row_num = 1, output_table.pseudo_length do
        offset = (row_num - 1) * output_table.pseudo_row_length + 1

        size = output_table[offset + size_offset - 1]

        current = get_index_key(output_table, offset, start_offset, size)

        if not index_first[current] then
            index_first[current] = row_num
        end

        index_last[current] = row_num
    end

    return index_first, index_last
end

local function get_max_values(intermediate_table)
    local in_max     = 0
    local prefix_max = 0
    local suffix_max = 0   
    local out_max    = 0
    
    for i, row in ipairs(intermediate_table) do
        prefix_max = math.max(prefix_max, row[1])
        in_max     = math.max(in_max, row[3])
        out_max    = math.max(out_max, row[6])
    end

    return in_max, prefix_max, out_max
end

function export.populate_conversion_table(output_table, conversions_in)
    local tmp = create_intermediate_table(conversions_in)
    local in_max, prefix_max, out_max = get_max_values(tmp)

    -- Koska taulukko on oikeasti yksiulotteinen, jaetaan se tasapituisiin 'virtuaalisiin' riveihin.
    -- Kuinka monta solua yhdellä rivillä on.
    local pseudo_row_len = 1 + prefix_max + 2 + in_max + 1 + out_max
    -- Kuinka monta `pseudo_row_len` pituista riviä taulukossa on.
    local pseudo_rows = #tmp

    local row
    local ptr = 1
    local j = 0
    for i, row in ipairs(tmp) do

        output_table[ptr] = row[1]

        local prefix_bytes = values(row[2])
        for j = 1, prefix_max do
            output_table[ptr + 1 + j - 1] = prefix_bytes[j] or 0 -- täytetään tyhjät 0:illa
        end

        output_table[ptr + 1 + prefix_max] = row[3]

        output_table[ptr + 1 + prefix_max + 1] = row[4]      

        local in_bytes = values(row[5])
        for j = 1, in_max do
            output_table[ptr + 1 + prefix_max + 2 + j - 1] = in_bytes[j] or 0 -- täytetään tyhjät 0:illa
        end

        output_table[ptr + 1 + prefix_max + 2 + in_max] = row[6]

        local out_bytes = values(row[7])
        for j = 1, out_max do
            output_table[ptr + 1 + prefix_max + 2 + in_max + 1 + j - 1] = out_bytes[j] or 0
        end

        ptr = ptr + 1 + prefix_max + 2 + in_max + 1 + out_max
    end

    assert ( #output_table / pseudo_row_len == pseudo_rows, "Virheellinen määrä soluja" )
    
    output_table.pseudo_length = pseudo_rows
    output_table.pseudo_row_length = pseudo_row_len

    output_table.in_max = in_max
    output_table.prefix_max = prefix_max
    output_table.out_max = out_max

    output_table.index_first_for_input, output_table.index_last_for_input
        = create_index(output_table, 1 + prefix_max + 1, 1 + prefix_max + 3)

    output_table.index_first_for_prefix, output_table.index_last_for_prefix
        = create_index(output_table, 1, 2)
end

return export