Moduuli:Translitteroija/muunnin
Tämän moduulin ohjeistuksen voi tehdä sivulle Moduuli:Translitteroija/muunnin/ohje
--- Translitteroi merkkijonon annetun taulukon mukaan.
local common = require("Moduuli:Translitteroija/yhteinen")
local export = {}
local conversions = {}
local in_max = 0
local out_max = 0
local prefix_max = 0
local pseudo_row_len = 0
local input_buffer
local index_first_for_input
local index_last_for_input
local index_first_for_prefix
local index_last_for_prefix
local tostring = common.tostring
local get_index_key = common.get_index_key
local log = false
local function compare_buffers(buf1, start1, len1, buf2, start2, len2)
local diff
local pos1, pos2
for diff = 1, math.min(len1, len2) do
pos1 = start1 + diff - 1
pos2 = start2 + diff - 1
if pos1 > #buf1 then
return -1
elseif pos2 > #buf2 then
return 1
elseif buf1[pos1] ~= buf2[pos2] then
return buf1[pos1] - buf2[pos2]
end
end
if len1 ~= len2 then
return len1 - len2
end
return 0
end
---
-- @return 0, jos teksti mätsää muunnokseen
-- -1, jos haettava on järjestykessä ensin
-- 1, jos haettava on järjestyksessä jälkeen
local function compare(state, position, forward_length, backward_length, pseudo_index)
local offset = (pseudo_index - 1) * pseudo_row_len + 1
local target_backward_length = conversions[offset + 0]
local target_forward_length = conversions[offset + 1 + prefix_max]
local input_buffer = state.input_buffer
local output_buffer = state.output_buffer
local output_pos = state.output_pos
local prefix_start = position - backward_length
if log then
mw.log(" compare:",
tostring(state.input_buffer, prefix_start, backward_length, true) ..
"|" ..
tostring(state.input_buffer, position, forward_length, true),
"to: index " .. pseudo_index .. ":",
tostring(conversions, offset + 1, target_backward_length, true) ..
"|" ..
tostring(conversions, offset + 1 + prefix_max + 2, target_forward_length, true)
)
end
-- Huom. Vaikka luetut välit eivät vastaisi muunnostaulukon rivin välejä, täytyy tekstiä kuitenkin verrata,
-- että tiedetään kumpaan suuntaan hakua jatketaan
local val = compare_buffers(
input_buffer,
position,
forward_length,
conversions,
offset + 1 + prefix_max + 2,
target_forward_length
)
if val ~= 0 then
return val, "pääteksti eri"
end
val = compare_buffers(
input_buffer,
prefix_start,
backward_length,
conversions,
offset + 1,
target_backward_length
)
if val ~= 0 then
return val, "prefiksit eri"
end
return val, nil
end
local function binary_search(state, start, last, position, forward_length, backward_length)
-- Initialise numbers
local i_start = start or 1
local i_end = last
local i_mid = 0
local fail_reason
while i_start <= i_end do
i_mid = math.floor((i_start + i_end) / 2)
local compare_result
compare_result, fail_reason = compare(state, position, forward_length, backward_length, i_mid)
if compare_result == 0 then
return i_mid
elseif compare_result < 0 then
i_end = i_mid - 1
else
i_start = i_mid + 1
end
end
return nil, fail_reason
end
local function search(state, position, forward_length, backward_length)
local key_for_input = get_index_key(state.input_buffer, 1, position, forward_length)
local first_for_input = index_first_for_input[key_for_input]
local last_for_input = index_last_for_input[key_for_input]
if not first_for_input then
return nil, "ei löydy päähakemistosta"
end
local key_for_prefix = get_index_key(state.input_buffer, 1, position - backward_length, backward_length)
local first_for_prefix = index_first_for_prefix[key_for_prefix]
local last_for_prefix = index_last_for_prefix[key_for_prefix]
if not first_for_prefix then
return nil, "ei löydy prefiksihakemistosta"
end
-- Kavennetaan inputin ja prefiksin väliä siten, että otetaan niiden yhteinen alue.
-- Jos ensimäinen ja viimeinen vaihtaa paikkaa, ovat alueet erillisiä eikä kyseisillä parametereilla ole tulosta.
local first = math.max(first_for_input, first_for_prefix)
local last = math.min(last_for_input, last_for_prefix)
if first > last then
return nil, "erilliset alueet"
end
return binary_search(state, first, last, position, forward_length, backward_length)
end
function export.convert_byte_buffer(input_buffer)
assert ( conversions ~= nil, "Muuttajaa ei ole asetettu" )
local out = {}
local inp = 1
local outp = 1
local state = {
input_buffer = input_buffer,
input_pos = inp,
output_buffer = out,
output_pos = outp
}
-- Löytynyt taulukon ”rivi”.
local matching_pseudo_index
-- Vertailtavat merkkimäärät
local read_in_input, read_in_prefix
-- debuggraukseen
local reason
if log then
mw.log("****************************************")
mw.log("INPUT BUFFER ALUSSA:",
tostring(input_buffer, nil, nil, true))
end
local counter = 100
while inp <= #input_buffer do
state.input_pos = inp
state.output_pos = outp
if log then
mw.log("INPUT BUFFER (" .. prefix_max .. "|" .. in_max .. ") @ " .. inp .. ":",
tostring(input_buffer, inp, in_max, true)
)
end
-- Yritetään löytää taulukosta inputin kohtaa vastaava rivi. Aloitetaan pisimmästä ja lyhennetään
-- sitä kunnes rivi löytyy tai kaikki mahdolliset on käyty läpi. Näin tehdään sekä
-- inputtiin mätsäävälle että outputin prefiksiin mätsäävälle.
read_in_input = math.min(in_max, #input_buffer - inp + 1)
while read_in_input > 0 do
read_in_prefix = math.min(prefix_max, inp - 1)
while read_in_prefix >= 0 do
matching_pseudo_index = nil
matching_pseudo_index, reason = search(
state,
inp,
read_in_input,
read_in_prefix
)
if matching_pseudo_index ~= nil then
read_in_input = -1
break
end
read_in_prefix = read_in_prefix - 1
end
read_in_input = read_in_input - 1
end
if matching_pseudo_index == nil then
error(
"Ei sopivaa riviä taulukossa: " .. tostring(input_buffer, inp, in_max, true) .. "... "
.. "Keskeytetty kohdassa " .. string.char(unpack(out))
)
end
-- Offsetit rivin soluihin.
local row_offset = (matching_pseudo_index - 1) * pseudo_row_len + 1
-- koko pituus - suffiksin offset
local input_advance_len = conversions[row_offset + 1 + prefix_max + 1]
local output_len_offset = row_offset + 1 + prefix_max + 2 + in_max
local output_text_offset = output_len_offset + 1
local output_len = conversions[output_len_offset]
if log then
mw.log(
" ------------------------>",
tostring(
conversions,
output_text_offset,
output_len,
true
)
)
end
local j
for j = 1, output_len do
out[outp] = conversions[output_text_offset + j - 1]
outp = outp + 1
end
inp = inp + input_advance_len
counter = counter - 1
if counter == 0 then
break
end
end
return out
end
function export.convert_string(str)
local padded = " " .. str .. " "
local chars = { string.byte(padded, 1, string.len(padded)) }
local result_table = export.convert_byte_buffer(chars)
local result = string.char(unpack(result_table))
return string.sub(result, 2, -2)
end
function export.set_conversion_table(conversion_table)
conversions = conversion_table
in_max = conversions.in_max
prefix_max = conversions.prefix_max
out_max = conversions.out_max
pseudo_row_len = conversions.pseudo_row_length
index_first_for_input = conversions.index_first_for_input
index_last_for_input = conversions.index_last_for_input
index_first_for_prefix = conversions.index_first_for_prefix
index_last_for_prefix = conversions.index_last_for_prefix
end
return export