Lua Group 23 Miikka Koskinen Joose Sainio
Johdanto Lua on kevyt skriptikieli, joka on pääasiallisesti suunniteltu muiden kielien tueksi laitteistoläheiseen ohjelmointiin. Luan kehittäminen aloitettiin 1993 Brasiliassa. Kielen kehittäjinä toimivat Roberto Ierusalimschy, Luiz Henrique de Figueiredo ja Waldemar Celes. Luaa alettiin kehittää, koska ainoa sulauttamiseen ei ollut muita kieliä kuin Tcl ja sen käyttäminen oli hyvin rajallista ja se toimi vain Unix ympäristöissä. Lua pohjautuu C:en, Lispiin sekä muutamaan muuhun kieleen. Lua on monen paradigman kieli. Pääasialliset paradigmat jotka Luassa vaikuttavat ovat skriptaus, imperatiivinen sekä funktionaalinen. Vaikka Lua ei ole oliopohjainen kieli voi siinä käyttää olioita, mutta se vaatii hieman enemmä vaivaa. Yleisesti Lua on dynaamisesti tyypitetty kieli eikä muuttujilla itsellään ole tyyppiä, mutta muuttujien arvoilla on. Kieli tarjoaa arvoille seuraavat perustyypit: nil, boolean, number, string, function, userdata, table ja thread. Nil tyypillä on ainoastaan yksi arvo nil, ja sen tarkoituksena on erota kaikista muiden tyyppien arvoista. Tyyppi number esittää sekä kokonaislukuja että liukulukuja. Merkkijonojen tyyppi string ei ota huomioon tekstin koodausta, vaan se pitää huomioida kirjoitettavassa skriptissä. Lua pystyy kutsumaan funktioita jotka on kirjoitettu Lua:lla tai C:llä, molempien kutsuminen onnistuu function tyypillä. Funktioita voidaan kutsua halutulla määrällä parametreja ja niistä voidaan palauttaa kerralla monta arvoa, esim: function f(a, b) return a, b end f(1, 2) a=1, b=2 f(1) a=1, b=nil Userdata tyyppisiä arvoja ovat C:ssä käytetyt muuttujat joita ei voida suoraan tulkita skriptissä. Tablet ovat taulukoita joita voidaan indeksoida kaikilla lua:n tukemilla arvoilla paitsi nil. Taulukot ovat luan ainoa menetelmä säilöä tietoa ja sitä voidaan käyttää taulukoihin, jonoihin, puihin ym. Taulukko voi sisältää mitä tahansa arvoja paitsi nil ja NaN. Thread tyyppi voi sisältää säikeitä joita voidaan ajaa vuorotellen, vaikka käyttöjärjestelmä itsessään ei tukisi moniajoa. Luassa näitä kutsutaan coroutineiksi. Coroutine voidaan käynnistää antamalla sille resume käsky ja sieltä voidaan poistua missä vaiheessa tahansa yield käskyllä, jolloin palataan takaisin suorittamaan koodia resume käskyn kohdalta. Yield voidaan antaa myös säikeen ulkopuolisessa funktiossa, jota säie kutsuu. Tyyppien käyttäytymistä voidaan muuttaa metamethodien avulla. Metamethodit voivat olla uusia metodeja tyypeille, kuten vähennys ja lisäys. Jokaiselle tyypille voidaan määritellä metatable,
joka listaa tyypin kanssa käytettävät metamethodit. Samalle tyypille voidaan määritellä vain yksi metatable, joten saman tyypin eri arvoilla ei voi olla eri taulukkoa. Lua koodin sisältä voidaan normaalitilanteessa muuttaa ainoastaan taulujen metatableja. Muiden tyyppien metatablet voidaan vaihtaa käyttäen C API:a tai debug kirjastoa. Lua sisältää automaattisen roskien keruun, joka tarkoittaa että ohjelmoinnissa ei tarvitse erikseen kiinnittää huomiota muistin varaamiseen uusille objekteille tai muistin vapauttamiseen kuolleista objekteista. Roskienkeruu vapauttaa muistin kun objekti ei enää ole saavutettavissa Lua koodissa. Luassa käytetään incremental mark and sweep tyyppistä roskien keruuta, jonka syklejä voidaan säädellä kahdella arvolla: garbage collector pause (%) ja garbage collector step multiplier (%). Pause arvolla voidaan säädellä kauanko odotetaan ennen kuin kerääjä aloittaa sen syklin. Esimerkiksi arvolla 200 kerääjä odottaa muistin käytön tuplaantuvan ennen kuin aloittaa syklinsä. Step multiplierillä voidaan säädellä roskien keruun nopeutta. Tauluille ja userdatalle voidaan määritellä metamethodi, joka toimii yhdessä automaattisen roskienkeruun kanssa. Lua koodi käynnistetään aina jonkin toisen ohjelman sisältä. Myös virheenkäsittely hoidetaan isäntäohjelmassa. Mikäli koodia ajetaan tulkilla, toimii tulkki tässä tapauksessa isäntäohjelmana. Jos on erityisesti tarvetta napata poikkeus Lua koodin sisällä, se onnistuu kutsumalla panemalla funktio ajoon suojattuun tilaan pcall ja xpcall käskyillä. Syntaksi Luan syntaksi on yleisesti ottaen vapaamuotoista ja yksinkertaista verrattuna suurimpaan osaan muita ohjelmointikieliä. Koodilohot erotellaan toisistaan avainsanalla end, joka merkitsee lohkon loppua. Luassa lauseet ovat eroteltu joko rivinvaihdolla tai käyttäen ;. Lauseita voi jatkaa vapaasti seuraavalle riville sulkujen avulla, tai yleisesti jos ympäristö, jossa skriptiä ajetaan tulkitsee ettei yhdellä rivillä oleva lause ole täydellinen se yrittää jatkaa lausetta seuraavan rivin avulla. Monen rivin merkkijonot merkitään kaksilla hakasulkeilla merkkijonon molemmin puolin [[ multiline string ]]. Kommentteja merkitään kahdella viivalla. Monen rivin kommentteja merkitään samalla kuin monen rivin merkkijonoja. Luassa on neljä erilaista silmukkarakennetta. While ja repeat silmukka, jotka ovat käytännössä sama asia, muuten kuin hieman että ne ovat hieman erilaisia syntaksiltaan. condition = true while condition do statements end repeat statements until condition
For silmukoita Luassa on kaksi erillaista. Numeroitu for silmukka, joka toimiii samoin kuin C++:n for silmukka. Toinen for silmukka on geneerisempi, jonka avulla voi iteroida listoja ja taulukoita alkio kerrallaan. Numeroitu: for i = first, last, delta do Delta voi olla negatiivinen, minkä avulla voidaan käydä statements arvoja läpi takaperin. example: print(i) end Geneerinen: for key, value in pairs(_g) do print(key, value) end Kyseinen silmukka on samanlainen kuin pythonissa: for key, value in dict.items(): print(key, value) Joka käy läpi pythonin tapauksessa dictionaryn ja luassa taulukon läpi avain ja arvo kerrallaan. Vastaava koodi C++:na: for (auto i: map) { std::cout << i.first << i.second << std::endl; } Koska Lua on dynaamisesti tyypitetty kieli ei niiden tyyppiä spesifioida kun muuttuja luodaan. Muuttujat ovat perusarvoltaan globaaleja, mutta ne voidaan määrittää lohkon lokaaleiksi muuttujiksi avainsanalla local. Käytännössä tämä tarkoittaa että jos esimerkiksi if lohkossa luodaan väliaikais muuttujaksi tarkoitettu muuttuja, mutta sitä ei merkitä avainsanalla local, sitä voidaan käyttää vielä kun if lohkosta poistutaan. Tämä saattaa tuntua C++ ohjelmoijalle melko vastenmieliseltä ja kaikkea pyhää vastaan olevalta. Kaikkein mielenkiintoisin Luan muuttujista ovat taulukot. Taulukot ovat perustana kaikille käyttäjän luomille tietotyypeille. Taulukoiden avaimina ja arvoina voi olla mitä tahansa tietotyyppiä. Koska taulukoita yleisesti indeksoidaan, on Luassa erillinen syntaksi merkkijonoilla indeksoitujen arvojen saamiseen. Sekä table[ key ] että table.key palauttavat taulukossa indeksillä key olevan arvon. Jos Luan taulukoita haluaa käyttää kuten C:n taulukkona, onnistuu se sijoittamalla arvot taulukkoon ilman että spesifioi indeksiä taulukkoa luodessa, ja sen jälkeen indeksoimalla kokonaisluvuilla. Huomioitavaa kuitenkin on että Lua aloittaa indeksoinnin automaattisesti ykkösestä, eikä nollasta ja negatiiviset indeksit ovat laillisia indeksejä, eivätkä aloita taulukon indeksointia lopusta.
Mikä erottaa Luan muista kielistä Yksi Luan vahvoista myyntivalteista on sen tulkin pieni koko (~23000 riviä koodia) ja hyvä portattavuus eri alustoille sen ANSI C pohjaisuuden ansiosta. Näiden vuoksi edes mikrokontrollerit eivät tunnu mahdottomilta vaihtoehdoilta Lua koodin ajamiseen. Lua eroaa laajemmista skriptikielistä kuten Python, erityisesti siinä että kielessä on panostettu enemmän integroitavuuteen. Kielen peruskirjastot eivät sisällä niin paljon ominaisuuksia, jonka vuoksi kieli sopiikin paremmin esimerkiksi peleihin/pelimoottoreihin integroitavaksi kieleksi kuin skriptikieleksi, jolla olisi tarkoitus tehdä kokonaisia sovelluksia. Luan tulkissa on se erikoisuus että se ei käytä tavukoodia, vaan hyvin läheisesti assemblyä muistuttavaa koodia. Sulauttaminen muita kieliä käyttäviin projekteihin Tärkeä osa Luaa on sen upottaminen muita kieliä käyttäviin projekteihin. Erityisesti C ja C++ pohjaisiin ohjelmistoihin. Luahan kuuluu C kirjasto, joka on tarkoitettu kielen upottamiseen C koodiin. Rajapinta toimii erityisen pinon avulla, johon kootaan Lua funktioiden suorittamiseen tarvittavat tiedot. Rajapinnan perustana on lua_state tyyppinen struct, joka tarvitaan jokaiseen kirjaston kutsuun, paitsi lua_open, joka luo kyseisen structin. Kyseinen struct sisältää kaikki Lua kääntäjän tarvitsemat tiedot, kuten globaalit muuttujat ja itse pinon. Kutsuttaeksi funktioita tiedot täytyy koota pinoon tietyssä järjestyksessä. Ensimmäiseksi itse funktio runko laitetaan pinoon. Sen päälle kootaan funktion parametrit siinä järjestyksessä kun ne ovat funktiossa määritelty, eli ensimmäinen parametri funktion rungon päälle, sitten toinen jne. Tämän jälkeen voidaan funktiota kutsua, Lua poistaa pinosta kaikki parametrit ja funktio koodin ja laittaa funktion tulokset pinoon. int main(void) { //luodaan Lua state struct lua_state *L = lual_newstate(); //Muutetaan merkkijono ajettavaksi koodiksi, olisi myös //mahdollista ottaa funktio valmiista tiedostosta if (lual_dostring(l, "function foo (x,y) return x+y end")) { //Jos operaatio epäonnistuu tuhotaan lua_state ja lopetetaan //ohjelman suoritus lua_close(l); return 1; }
//Työnnetään nimellä foo oleva arvo globaalista ympäristöstä //pinoon lua_getglobal(l, "foo"); //Työnnetään kokonaisluvut 5 ja 3 pinoon lua_pushinteger(l, 5); lua_pushinteger(l, 3); } lua_call(l, 2, 1); //kutsutaan funktiota joka ottaa kaksi //parametriä ja palauttaa yhden arvon //Tulostetaan pinon päälimmäisenä oleva arvo kokonailukuna printf("result: %d\n", lua_tointeger(l, 1)); lua_close(l); //suljetaan lua state return 0; Lähteet: http://www.lua.org/manual/5.2/manual.html https://en.wikipedia.org/wiki/lua_(programming_language) http://www.lua.org/about.html