Pohjoissaamen ortografiamuunnin Nielsenin ortogafiasta nykyortografiaan

Koodannut Mikko Lounela Eino Koposen tavumallin mukaan.

Kyseessä on epätäydellinen luonnostelma,
lopullisen muuntosäännöstön pitäisi valmistua vuoden sisällä.

Muuntimen perusajatus ja toiminta

Muunnin lukee sanoja, jotka on kirjoitettu Nielsenin ortografialla ja tulostaa ne nykyortografian mukaisessa asussa. Muunnos perustuu tavumalliin, jossa tavun ytimen muodostaa vokaalijono. Konsonanttiyhdistelmät toimivat rajoina (tai siirtyminä) ydinten välillä. Ytimiä on kahta typpiä (1 ja 2), ja siirtymiä puolestaan viittä tyyppiä, mikäli sanojen alut ja loput lasketan mukaan (sananvälin symboli seuraavassa on '0'). Siirtymätyypit ovat 0->1, 1->1, 1->2, 2->1, 1->0 ja 2->0. Merkkimuunnoksia tehdään sekä tavun ytimessä että siirtymässä. Sanan alussa tai lopussa oleva siirtymä voi reaalistua paitsi konsonanttiyhdistelmänä, myös tyhjänä merkkijonona. Sanan ensimmäinen tavu on aina tyyppiä yksi. Sanat koostuvat jaksoista, jotka ovat joko tyyppiä 1->2 tai 1->1->2. Jakson ensimmäisen siirtymän muunnoksen määrittäminen vaatii sen, että tässä vaiheessa tiedetään koko jakson rakenne (muunnokset siirtymissä 1->1 ja 1->2 ovat erilaiset).

Muunnin perustuu (ei-deterministiseen) äärelliseen automaattiin. Äärellisistä automaateista ks. viite. Deterministisessä perusautomaatissa on yhdellä hetkellä tieto vain siitä tilasta, missä automaatti on ja siitä syötteestä, minkä se seuraavaksi saa. Perusautomaatin lisäksi muuntimessa on käytetty kurkistusoperaattoria (lookahead), koska ortografiamuunnoksessa tulee eteen tehtäviä, joissa jonkin merkkijonon muuntamiseen tarvittava informaatio saadaan vasta esimerkiksi kaksi tavua edempänä sanassa.

Muunnin on implementoitu Flex-kielellä, joka on alunperin tarkoitettu tietokoneohjelmien leksikaalisten sekvenssien tunnistamiseen. Flex-ohjelma koostuu säännöistä, joissa kussakin määritellään tila, jossa ohjelman suoritus on, tilassa syötteeksi mahdolliset merkkijonot (säännöllisinä lausekkeina - ks. viite), toiminta, joka suoritetaan tietyssä tilassa tietyllä syötteellä ja tila, johon säännön suorittamisesta siirrytään. Säännössä voidaan myös määrätä syötettä seuraava kontekstiehto säännön toteutumiselle (kurkistus, lookahead).

Esimääritykset: tilat ja joukot

Muuntimessa on alkutilan (INITIAL) lisäksi 12 tilaa:

%x C01 V1 V1B V2 V2B C11 C12 C21 C10 C20 FIN FAIL

Tilojen lisäksi Flexissä voi esimääritellä ja nimetä merkkijonoja myöhempää käyttöä varten. Tämä helpottaa sääntöjen kirjoittamista ja lukemista, joten määrittelemme kullekin tilalle kaikki sen mahdolliset syötteet:

Muunnossäännöt

%%

Muuntimessa on muunnossäännöt kullekin tilalle, kullekin tässätilassa mahdolliselle syötteelle. Syötteet määritellään säännöllisinä lausekkeina, ja osa niistä on esimääritelty yllä määrittelyosassa.Syötettä voi seurata kurkistusosa, joka asettaa kontekstiehdon säännön toteutumiselle. Tämän jälkeen määrätään toiminta, joka säännön lauetessa suoritetaan (muuntimessa joko merkkijonon tulostaminen tai ei mitään), ja tila johon (BEGIN-funktiolla) siirrytään. Esimerkiksi sääntö <V1>a printf ("á"); BEGIN(V1B); sanoo, että tilassa V1, mikäli syöte on 'a', tulostetaan merkki 'á' ja siirrytään tilaan V1B. Sääntö <V1B>""/{C12}{V2}{C21}{V1} BEGIN(C12);sanoo, että tilassa V1B ei lueta syötettä (tyhjä jono ""), mutta jos seuraavana on järjestyksessä joukkoihin C12, V2, C21 ja V1 kuuluvat merkkijonot, siirrytään tilaan C12.

Sännöt luetellaan seuraavassa ryhmiteltynä alkutilan mukaan. Flex-ohjelmassa sääntöjen suoritusjärjestys määräytyy siten, että mikäli seuraavaan syötteeseen soveltuu useampi kuin yksi sääntö, muunnin soveltaaa sitä, jonka kattama merkkijono on pisin. Mikäli vaihtoehtoja vielä jää, Flex soveltaa ylinnä säännöstässä olevaa sääntöä.

Säännöt:

  1. Sanan alussa olevat konsonantit tulostetaan sellaisenaan (jos niitä on). Siirrytään vokaalipositioon 1
    <INITIAL>{C01}*		printf ("%s",yytext); BEGIN(V1);
    <INITIAL>""/.		printf ("%s",yytext); BEGIN(V1);
    
  2. Postiossa yksi (tila V1) tehdään muunnos ja siirrytään kurkistustilaan V1B.
    <V1>a	printf ("á"); BEGIN(V1B);
    <V1>â	printf ("a"); BEGIN(V1B);
    <V1>E	printf ("e"); BEGIN(V1B);
    <V1>u	printf ("u"); BEGIN(V1B);
    <V1>ú	printf ("u"); BEGIN(V1B);
    <V1>O	printf ("o"); BEGIN(V1B);
    <V1>oa	printf ("oa"); BEGIN(V1B);
    <V1>æ	printf ("ea"); BEGIN(V1B);
    <V1>i	printf ("i"); BEGIN(V1B);
    
  3. Kurkistustilassa V1B katsotaan ollaanko 121-sekvenssin alussa. Mikäli ollaan, siirrytään välitilaan C12. Tämä sääntö olisi muuten redundantti, mutta koska 121-sekvenssiä halutaan suosia (ja Flex toteuttaa pisimmän säännön ensin), tämä on tarpeellinen.
    <V1B>""/{C12}{V2}{C21}{V1}	BEGIN(C12);
    
  4. Mikäli ei olla 121-jakson alussa, katsotaan ollaanko 112-jakson alussa. Jos ollaan, siirrytään välitilaan C11.
    <V1B>""/{C11}{V1}{C12}{V2}	BEGIN(C11);
    
  5. Mikäli ei olla 121- eikä 112-jakson alussa, ollaan ehkä sanan lopussa. Siinä tapauksessa siirrytään välitilaan C10.
    <V1B>""/{C10}?{FIN}		BEGIN(C10);
    
  6. Muusssa tapauksessa ollaan siirtymässä positioon kaksi.
    <V1B>""/{C12}{V2}		BEGIN(C12);
    
  7. Tilassa C11 tulostetaan se konsonanttiyhdistelmä, joka tulee vastaan, siirrytään tilaan V1.
    <C11>{C11}/{V1}			printf ("%s",yytext); BEGIN(V1);
    
  8. Tilassa C12 tehdään muunnos, siirrytään tilaan V2.
    <C12>bm/{V2}	printf ("pm"); BEGIN(V2);
    <C12>b'm/{V2}	printf ("bm"); BEGIN(V2);
    <C12>cc/{V2}	printf ("hc"); BEGIN(V2);
    <C12>fr/{V2}	printf ("frr"); BEGIN(V2);
    <C12>g'gj/{V2}	printf ("ddj"); BEGIN(V2);
    <C12>i'g/{V2}	printf ("ig"); BEGIN(V2);
    <C12>it/{V2}	printf ("itt"); BEGIN(V2);
    <C12>i't/{V2}	printf ("it"); BEGIN(V2);
    <C12>kk/{V2}	printf ("hk"); BEGIN(V2);
    <C12>l/{V2}	printf ("l"); BEGIN(V2);
    <C12>m/{V2}	printf ("m"); BEGIN(V2);
    <C12>mb/{V2}	printf ("mbb"); BEGIN(V2);
    <C12>pp/{V2}	printf ("hp"); BEGIN(V2);
    <C12>st/{V2}	printf ("stt"); BEGIN(V2);
    <C12>tt/{V2}	printf ("ht"); BEGIN(V2);
    <C12>vv/{V2}	printf ("vv"); BEGIN(V2);
    <C12>v'v/{V2}	printf ("vv"); BEGIN(V2);
    <C12>ll/{V2}	printf ("ll"); BEGIN(V2);
    
  9. Tilassa V2 tehdään muunnos, siirrytään kurkistustilaan V2B
    <V2>a	printf ("á"); BEGIN(V2B);
    <V2>â	printf ("a"); BEGIN(V2B);
    <V2>e	printf ("i"); BEGIN(V2B);
    <V2>è	printf ("e"); BEGIN(V2B);
    <V2>i	printf ("e"); BEGIN(V2B);
    <V2>o	printf ("u"); BEGIN(V2B);
    <V2>ò	printf ("o"); BEGIN(V2B);
    <V2>u	printf ("o"); BEGIN(V2B);
    
  10. Tilassa V2B kurkistetaan eteenpäin tekemättä muunnosta. Mikäli edessä on C21-ryhmään kuuluva konsonantti, siirrytään tilaan C21;
    <V2B>""/{C21}		BEGIN(C21);
    
  11. Tilassa V2B, jos edessä on C20-konsonantti tai sanan loppu, sirrytään tilaan C20.
    <V2B>""/{C20}?{FIN}	BEGIN(C20);
    
  12. Tilassa C21 tehdään muunnos, siirrytään tilaan V1
    <C21>l	printf ("l"); BEGIN(V1);
    <C21>t	printf ("h"); BEGIN(V1);
    <C21>st	printf ("st"); BEGIN(V1);
    <C21>Z	printf ("Z"); BEGIN(V1);
    <C21>g	printf ("g"); BEGIN(V1);
    
  13. Tilassa C20 katsotaan ollanko sanan lopussa, tehdään muunnos, siirrytään tilaan FIN
    <C20>m/{FIN}	printf ("n"); BEGIN(FIN);
    <C20>k/{FIN}	printf ("t"); BEGIN(FIN);
    <C20>st/{FIN}	printf ("s"); BEGIN(FIN);
    <C20>t/{FIN}	printf ("t"); BEGIN(FIN);
    <C20>id/{FIN}	printf ("id"); BEGIN(FIN);
    <C20>""/{FIN}	BEGIN(FIN);
    
  14. Tilassa C10 katsotaan ollanko sanan lopussa, tehdään muunnos, siirrytään tilaan FIN
    <C10>m/{FIN}	printf ("n"); BEGIN(FIN);
    <C10>k/{FIN}	printf ("t"); BEGIN(FIN);
    <C10>st/{FIN}	printf ("s"); BEGIN(FIN);
    <C10>t/{FIN}	printf ("t"); BEGIN(FIN);
    <C10>id/{FIN}	printf ("id"); BEGIN(FIN);
    <C10>""/{FIN}	BEGIN(FIN);
    
  15. Mikäli vastaan tulee merkki, jota ei ole määritelty siirrytään tilaan FAIL, tulostetaan sana loppuun merkittynä epäilyttäväksi.
    <FAIL>.		printf 	("%s",yytext);
    <*>.		printf 	("[!FAIL: %s",yytext); BEGIN(FAIL);
    
  16. Sanan lopussa siirrytään alkutilaan, oltiin missä tilassa tahansa.
    <FAIL>{FIN}	printf ("]%s",yytext); BEGIN(INITIAL);
    <*>{FIN}		printf ("%s",yytext); BEGIN(INITIAL);
    

Loppu

%%
main()
      {
      yylex();
      }