Moduuli:tavutus/fi

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

Funktiot[muokkaa]

tavutettu_teksti[muokkaa]

tavutettu_teksti(sana)

Tavuttaa sanan suomen kielen tavutussääntöjen mukaisesti.

Parametrit[muokkaa]

sana

Tavutettava sana. Sanassa voi käyttää lisäksi seuraavia merkkejä:

  • 0: Merkitsee katoa D-astevaihtelutyypin sanoissa, esimerkiksi hiki : hi0en. 0:aa ei tulosteta lopputulokseen.
  • .: Tavutusvihje. Käytetään vierasperäisissä sanoissa, joilla on vaihtoehtoinen alkuperäisiin yhdysosiin perustuva tavutus. Esimerkiksi eks.pressiivinen, Man.chester.

Seuraavat eroaa muista rajamerkeistä siinä, että merkin jälkeen voivat taas esiintyä vain ensitavussa esiintyvät diftongit.

  • -: Sanaan kuuluva yhdyssanojen yhdysosien erotin. Esimerkisi kuorma-auto.

  • #: Ýhdyssanan pääosien erotin. Näitä on vain yksi sanassa. Vaikuttaa tavutukseen, mutta poistetaan tulostuksesta. Merkitsee sivupainon paikan yhdyssanoissa. Esim. kolmi+vaihe#kilo+watti+tunti+mittari (parempi esimerkki, tässä +-merkit turhia).
  • +: Toissijainen yhdyssanojen yhdysosien erotin, joka ei kuulu sanaan. Vaikuttaa tavutukseen, mutta poistetaan tulostuksesta. Esimerkiksi erin+omainen.

Paluuarvo[muokkaa]

Palauttaa taulukon, jossa on alkioina vaihtoehtoisten tavutusten tavut taulukkona.

Esim.

{
  {'va', 'paut', 'ta'},
  {'va', 'pa', 'ut', 'ta'}
}

local mallinetyokalut = require("Moduuli:Mallinetyokalut")
local luokitus = require("Moduuli:luokittelu")

local export = {}

local ALL = 99999

-- Asteriskilla merkityt ovat diftongeja kaikissa tavuissa, 1:llä merkityt vain ensimmäisessä.
local diftongit = {
    ['ai'] = ALL,
    ['au'] = ALL,
    ['ei'] = ALL,
    ['eu'] = ALL,
    ['ey'] = ALL, 
    ['ie'] = 1,
    ['iu'] = ALL,
    ['iy'] = ALL, 
    ['oi'] = ALL,
    ['ou'] = ALL,
    ['ui'] = ALL,   
    ['uo'] = 1,
    ['yi'] = ALL,
    ['yö'] = 1,
    ['äi'] = ALL,
    ['äy'] = ALL,
    ['öi'] = ALL,
    ['öy'] = ALL,
}

local astevaihteludiftongit = {
    ['a0i'] = ALL,   -- laki : lain
    ['a0u'] = ALL,   -- maku : maun
    ['e0i'] = ALL,   -- reki : reissä
    ['e0u'] = ALL,
    ['e0y'] = ALL,
    ['i0e'] = 1,     -- ien : ikenet
    ['i0u'] = ALL,
    ['i0y'] = ALL,
    ['o0i'] = ALL,   -- hokea : hoitte
    ['o0u'] = ALL,   -- hoku : houn
    ['u0i'] = ALL,   -- lukea : luitte
    ['u0o'] = 1,     -- ruko : ruon
    ['y0i'] = ALL,   -- rykiä : ryin
    ['y0ö'] = 1,
    ['ä0i'] = ALL,   -- mäki : mäillä
    ['ä0y'] = ALL,   -- täky : täyn
    ['ö0i'] = ALL,
    ['ö0y'] = ALL,
}




local function vokaali(env)
    if not mw.ustring.match(env.input_lower, "^[aeiouyäö]", env.pos) then
        return false
    end

    env.pos = env.pos + 1
    
    return true
end


local function diftongi(env)
    local sub = mw.ustring.sub(env.input_lower, env.pos, env.pos + 1)
    local nxt = mw.ustring.sub(env.input_lower, env.pos + 1, env.pos + 2)
    local scope = diftongit[sub]

    
    if ((diftongit[sub] and scope == ALL)
            or (env.first_syllable and scope == 1))
    -- tarkistetaan, että kaksi seuraavaa merkkiä eivät ole kaksoisvokaali,
    -- esim. "kokaiini" pitää tavuttua "ko.ka.ii.ni"
        and not mw.ustring.match(nxt, "^([aeiouyäö])%1")
    then
        env.pos = env.pos + 2

        return true
    end

    return false
end

--- K:n kadon aiheuttama diftongi.
--  Nämä pitää merkitä erikseen 0:lla, esim. "i0en"
local function astevaihteludiftongi(env)
    local sub = mw.ustring.sub(env.input_lower, env.pos, env.pos + 2)
    local nxt = mw.ustring.sub(env.input_lower, env.pos + 1, env.pos + 2)    
    local scope = astevaihteludiftongit[sub]

    if ((astevaihteludiftongit[sub] and scope == ALL)
            or (env.first_syllable and scope == 1))
        and not mw.ustring.match(nxt, "^([aeiouyäö])%1")    
    then
        env.pos = env.pos + 3

        return true
    end

    return false
end

--- U- tai y-loppuinen diftongi muussa kuin ensitavussa.
local function agglutinointidiftongi(env)
    local sub = mw.ustring.sub(env.input_lower, env.pos, env.pos + 1)
    local nxt = mw.ustring.sub(env.input_lower, env.pos + 1, env.pos + 2)    

    if not env.first_syllable
        and diftongit[sub]
        and mw.ustring.match(sub, "^.[uy]")
        and not mw.ustring.match(nxt, "^([aeiouyäö])%1")    
    then
        env.pos = env.pos + 2

        return true
    end

    return false
end

local function kaksoisvokaali(env)
    if mw.ustring.match(env.input_lower, "^([aeiouyäö])%1", env.pos) then
        env.pos = env.pos + 2

        return true
    end

    return false
end

local function konsonantteja_0_n(env)
    local m = mw.ustring.match(
        env.input_lower,
        "^[*bcdfghjklmnpqrsštvwxzž0]*",
        env.pos
    )

    if m then
        env.pos = env.pos + mw.ustring.len(m)
        
        return true
    end
    
    return false
end

local function is_short(syllable)
    if mw.ustring.match(syllable, "[aeiouyäöAEIOUYÄÖ][aeiouyäö]$") then
        return false
    elseif mw.ustring.match(syllable, "[aeiouyäö]$") then
        return true
    end

    return false
end

--- Palauttaa tavun kuvauksen.
-- 
-- @param text:  tavun sisältö
-- @param pos:   tavun sijainti sanassa
-- @param first: onko sanan ensimmäinen
-- 
local function make_syllable_description(text, pos, first, separator)
    local open_or_closed
    local short_or_long
    
    if is_short(text) then
        short_or_long = "short"
    else
        short_or_long = "long"
    end

    if mw.ustring.match(text, "[aeiouyäöAEIOUYÄÖ]$") then
        open_or_closed = "open"
    else
        open_or_closed = "closed"
    end

    return {
        text = text,
        pos  = pos,
        short_or_long  = short_or_long,
        open_or_closed = open_or_closed,
        first_syllable = first,
        word_separator = separator
    }
end


local function insert_as_one_and_two_syllables(env, type, start, delim_pos)
    
end

--- Lisää `inputin` tekstin väliltä `first`–`last` (inkl.) outputtiin `output`.
local function insert_from(input, output, first, last, first_syllable, separator)
    table.insert(
        output,
        make_syllable_description(
            mw.ustring.sub(input, first, last):gsub("0", ""),
            first,
            first_syllable,
            separator
        )
    )
end



--- Lukee seuraavan umpi- tai avotavun, jossa on U:hun päättyvä diftongi, esim. "kaut", jos tavu ei ole ensitavu.
--  Lisää tavun yhtenä ("kaut", "pau") ja kahtena ("ka-ut", "pa-u").
--  http://www.kielitoimistonohjepankki.fi/ohje/357
--  avotavussa yksi tavu ja umpitavussa kaksi tavua on ensisijainen per SKAM, s. 89:
--  ”(ii) Ensi tavua kauempana olevat u- ja y-loppuiset jonot ovat avotavussa diftongeja, esim. puu.rou.tu.a,
--  vär.väy.dyn. (iii) U- ja Y-loppuiset jonot ovat eritavuisia eli vokaaliyhtymiä, kun niitä seuraa jälkisegmentin 
--  kanssa samassa tavussa oleva konsontantti, siis pa.la.ut.taa, tar.jo.us, töy.ke.yt.tä. Kaikkia kielentaitajia 
--  koskevia ehdottomia sääntöjä nämä eivät kuitenkaan ole, vaan on idiolektikohtaisia vaihteluita.”
local function schrodingerin_tavu(env)
    local start = env.pos

    if konsonantteja_0_n(env)
        and agglutinointidiftongi(env) 
        and konsonantteja_0_n(env)
    
    then
        local uy_pos = mw.ustring.find(env.input_lower, "[uy]", start)

        assert(uy_pos > -1, "Ei u:ta tai y:tä")
        
        if mw.ustring.match(env.input_lower, "^[*bcdfghjklmnpqrsštvwxzž0][aeiouyöä]", env.pos - 1) then
            env.pos = env.pos - 1
        end

        assert(uy_pos < env.pos, "U tai y vikana: " .. mw.ustring.sub(env.input, start) )

        local open_syllable = false
        -- Katsotaan onko viimeinen merkki vokaali.
        if mw.ustring.match(env.input_lower, "^[aeiouyöä]", env.pos - 1) then
            open_syllable = true
        end

        -- Umpitavussa halutaan, että "ka-ut"-tavutus tulee ennen "kaut"-tavutusta.
        -- Avotavussa halutaan, että "kau"-tavutus tulee ennen "ka-u"-tavutusta, joten
        -- vaihdetaan outputtien paikkaa.
        local output1 = env.output1
        local output2 = env.output2
        if open_syllable then
            output1 = env.output2
            output2 = env.output1
        end
        
        -- Lisätään kahtena tavuna...
        insert_from(
            env.input,
            output1,
            start,
            uy_pos - 1,
            env.first_syllable,
            env.word_separator
        )
        
        insert_from(
            env.input,
            output1,
            uy_pos,
            env.pos - 1,
            false
        )

        -- ... ja yhtenä tavuna.
        insert_from(
            env.input,
            output2,
            start,
            env.pos - 1,
            env.first_syllable,
            env.word_separator
        )

        env.first_syllable = false
        
        return true
    end

    env.pos = start   
    return false
end


--- Lukee seuraavan umpi- tai avotavun, jossa on kadon aiheuttama diftongi.
--  Lisää tavun kahtena ("i-en") ja yhtenä ("ien").
--  ”– – astevaihteludiftongit voivat foneettisesti toteutua joko aitoina diftongeina taikka
--  eritavuisina. Tämä on melko vapaa ja ennustamaton vaihtelu.” (SKAM, s. 89–90)
local function astevaihteludiftongitavu(env)
    local start = env.pos

    if konsonantteja_0_n(env)
        and astevaihteludiftongi(env) 
        and konsonantteja_0_n(env)
    
    then

        local k0_pos = mw.ustring.find(env.input_lower, "0", start)

        -- Tarkistetaan, ettei kadon jälkeen tule toista diftongia. "Ru0oissa" pitää tavuttua "ru-ois-sa".
        local after = mw.ustring.sub(env.input_lower, k0_pos + 1, k0_pos + 2)
        if diftongit[after] then
            env.pos = start
            return false
        end

        assert(k0_pos > -1, "Ei 0:aa")
        
        if mw.ustring.match(env.input_lower, "^[*bcdfghjklmnpqrsštvwxzž0][aeiouyöä]", env.pos - 1) then
            env.pos = env.pos - 1
        end

        assert(k0_pos < env.pos, "0 vikana: " .. mw.ustring.sub(env.input, start) )

        -- Lisätään kahtena tavuna...
        insert_from(
            env.input,
            env.output1,
            start,
            k0_pos - 1,
            env.first_syllable,
            env.word_separator
        )
        insert_from(
            env.input,
            env.output1,
            k0_pos,
            env.pos - 1,
            false
        )
        
        -- ... ja yhtenä tavuna.
        insert_from(
            env.input,
            env.output2,
            start,
            env.pos - 1,
            env.first_syllable,
            env.word_separator
        )
        
        env.first_syllable = false
        
        return true
    end

    env.pos = start   
    return false
end

--- Lukee seuraavan umpi- tai avotavun, jossa ei ole u:hun tai y:hyn päättyvää diftongia.
local function tavu(env)
    local start = env.pos

    if konsonantteja_0_n(env)
        and (
            diftongi(env)
                or kaksoisvokaali(env)
                or vokaali(env)
        )
        and konsonantteja_0_n(env)
    
    then
        -- Jos seuraava on vokaali, peruutetaan yksi konsonantti taaksepäin.
        if mw.ustring.match(env.input_lower, "^[*bcdfghjklmnpqrsštvwxzž0][aeiouyöä]", env.pos - 1) then
            env.pos = env.pos - 1
        end

        -- Lisätään sama tavu molempaan outputtiin.
        insert_from(
            env.input,
            env.output1,
            start,
            env.pos - 1,
            env.first_syllable,
            env.word_separator
        )

        insert_from(
            env.input,
            env.output2,
            start,
            env.pos - 1,
            env.first_syllable,
            env.word_separator
        )

        env.first_syllable = false

        return true
    end

    env.pos = start   
    return false
end


local function sanaraja(env)
    local word_sep = mw.ustring.match(env.input, "[-+# ˌ]", env.pos)

    if not word_sep then
        return false
    end

    env.first_syllable = true
    env.word_separator = word_sep
    env.pos = env.pos + 1
    
    return true
end

local function tavuraja(env)
    -- Huom. nollan pitää olla täälläkin, että voi antaa muitakin nollan sisältäviä tekstejä
    -- (joissa nolla ei ole diftongin keskellä).
    local m = mw.ustring.match(env.input, "[.’'ˌ0]", env.pos)

    if not m then
        return false
    end

    env.pos = env.pos + 1
    
    return true
end


--- Tavuttaa syötteen sanan ja palauttaa aina taulukon, jossa on joko yksi tai kaksi tulosta.
--
local function syllabify(input)

    local env = {
        input = input,
        input_lower = mw.ustring.lower(input),
        output1 = {},
        output2 = {},
        alternative = nil,
        pos = 1,
        first_syllable = true,
        word_separator = " "
    }

    local prev_pos = env.pos

    while env.pos <= mw.ustring.len(env.input) do
        if schrodingerin_tavu(env)
            or astevaihteludiftongitavu(env)
            or tavu(env)
            or sanaraja(env)
            or tavuraja(env)
        then
            -- OK
        else
            error("Odottamaton merkki kohdassa " ..env.pos .. ": " .. mw.ustring.sub(env.input, env.pos))
        end
        
        if env.pos == prev_pos then
            break
        end
        
        prev_pos = env.pos
    end

    -- Palautetaan molemmat, jos eroavat.
    for i, v in ipairs(env.output1) do
        if v.text ~= env.output2[i].text then
            return { env.output1, env.output2 }
        end
    end
    
    return { env.output1 }
end




function export.tavuta(input)
    return syllabify(input)
end

--- Palauttaa tavutuksesta pelkän tekstin.
--
--  Jos syötteellä on useita tavutuksia, palauttaa niistä vain kaksi siten, että ensimmäisessä on
--  tavallisempi tavutus ja toisessa harvinaisempi. Esim. jos syöte olisi "vapauden rakkautta ies"
--  tuotettaisiin
--    va-pau-den rak-ka-ut-ta i-es
--    va-pa-u-den rak-kaut-ta ies
--  eikä kaikkia yhdistelmiä.
--    
function export.tavutettu_teksti(input)
    local combinations = export.tavuta(input)

    local out = {}

    for k, syllableInfos in pairs(combinations) do
        out[k] = mallinetyokalut.map(function (syllableInfo) return syllableInfo.text end, syllableInfos)
    end

    return out
end


return export