Obsah
1. Kooperace mezi jazykem Lua a nativním (céčkovým) kódem
2. Způsoby vestavění virtuálního stroje jazyka Lua do nativních aplikací
3. Izolace skriptů běžících v rámci virtuálního stroje jazyka Lua
4. Inicializace a ukončení práce virtuálního stroje jazyka Lua
4.1 První demonstrační příklad – vytištění informací o verzi interpretru i o jeho autorech
4.2 Druhý demonstrační příklad – inicializace a ukončení práce virtuálního stroje jazyka Lua
5. Spuštění skriptů z nativního (céčkového) kódu
5.1 Třetí demonstrační příklad – spuštění skriptu uloženého v řetězci
5.2 Čtvrtý demonstrační příklad – spuštění skriptu uloženého v externím souboru
6.1 Pátý demonstrační příklad – zavolání funkce ze standardní knihovny jazyka Lua z C
6.2 Šestý demonstrační příklad – předání dvou parametrů různých typů volané funkci
6.3 Sedmý demonstrační příklad – přečtení návratové hodnoty Lua funkce
7.1 Osmý demonstrační příklad – zavolání céčkové funkce ze skriptu
8. Alternativní způsob komunikace mezi jazykem Lua a nativním kódem: knihovna FFI
8.1 Devátý demonstrační příklad – základ použití knihovny FFI
8.2 Desátý demonstrační příklad – volání složitější funkce
9. Repositář s dnešními demonstračními příklady
1. Kooperace mezi jazykem Lua a nativním (céčkovým) kódem
Ve článku nazvaném Interpretry, překladače, JIT překladače a transpřekladače programovacího jazyka Lua, který na Rootu vyšel minulý týden, jsme se mj. seznámili i s projektem nazvaným Lua2c. Jedná se o prozatím velmi naivní transpřekladač sloužící k překladu zdrojových textů napsaných v programovacím jazyce Lua do zdrojového kódu programovacího jazyka C. Výsledný céčkový zdrojový kód se může následně přeložit libovolným céčkovým překladačem (gcc, Clang apod.) a výsledkem tohoto překladu (a slinkování) bude čistě nativní aplikace. I pokud pomineme fakt, že výsledný nativní kód je i při provedení optimalizací pomalejší než původní skript napsaný v Lue a spuštěný v LuaJITu, přináší přímý transpřeklad do céčka i další nevýhody – složité ladění (ne již na úrovni původního kódu) a především pak ztrátu většiny vlastností klasického interpretru (mj. i ztrátu možnost okamžitého spuštění napsaného programu bez nutnosti kompilace – a spuštění interpretru Luy či LuaJITu je skutečně „okamžité“, minimálně z pohledu člověka-vývojáře).
2. Způsoby vestavění virtuálního stroje jazyka Lua do nativních aplikací
V praxi se však programovací jazyk Lua používá odlišným způsobem. Namísto transpřekladu do céčka či jiného nízkoúrovňového programovacího jazyka se buď přímo spouští skripty napsané v Lue, a to s využitím originálního interpretru či poněkud vyspělejšího LuaJITu (Lua je tedy použita stejným způsobem jako například BASH, Perl atd.), nebo se virtuální stroj jazyka Lua může přímo vložit do aplikací, což je poměrně jednoduché (pokud situaci zjednoduším, nejedná se o nic jiného, než o přilinkování jedné knihovny). Ostatně právě zde můžeme vidět jeden z hlavních důvodů relativně velké oblíbenosti programovacího jazyka Lua mezi vývojáři komerčních i nekomerčních aplikací: do prakticky libovolného programu je možné zabudovat buď plnohodnotný interpret tohoto jazyka, nebo pouze tu část, která se stará o běh přeloženého bajtkódu. V některých typech aplikací, například (komerčních) počítačových hrách, totiž nemusí být nutné překládat nové zdrojové kódy, ale pouze spouštět bajtkód přeložený přímo výrobcem hry; další aplikace naopak mohou těžit z toho, že jsou uživatelsky skriptovatelné (viz většina moderních „Office“, programy typu CAD či CAM, grafické a textové editory a mnoho dalších typů aplikací).
Obrázek 1: Nativní aplikace (typicky naprogramovaná v C či C++) může být slinkována s knihovnou obsahující virtuální stroj jazyka Lua. Taková aplikace pak může načítat a spouštět skripty napsané v Lue či je dokonce možné spouštět již přeložený bajtkód a vynechat tak fázi překladu.
Na tomto místě je nutné zdůraznit, že se v případě programovacího jazyka Lua nejedná o unikátní vlastnost, protože i mnoho interpretů dalších programovacích jazyků lze vestavět do jiných aplikací – v poslední době se stává populární především JavaScript vedle již zavedeného Pythonu (OpenOffice.org, GIMP), Scheme (opět GIMP), Lispu (AutoCAD, Emacs) či Visual Basicu (VBA) (MS Office a mnohé další aplikace). Ovšem v případě jazyka Lua je její vestavění do libovolné aplikace skutečně snadné – z pohledu programátora (především pokud programuje v céčku či C++), který ve své aplikaci potřebuje použít nějaký skriptovací jazyk, se jedná o pouhých několik programových řádků s následným slinkováním s objektovým kódem uloženým v archivu liblua.a, jak si ostatně ukážeme v navazujících kapitolách. Vložením celého překladače a interpretu jazyka Lua včetně jeho podpůrného běhového prostředí (základní funkce, garbage collector aj.) se zvětší velikost výsledného spustitelného souboru o cca 70 kB, což není nijak závratná hodnota, především při porovnání velikostí interpretů dalších programovacích jazyků (předpokládáme zde statické slinkování, ale i velikost dynamické knihovny se příliš neliší).
Obrázek 2: Lua však může být použita i ve funkci klasického skriptovacího jazyka (podobně jako BASH či Perl). I v takovém případě je však možné volat funkce z nativních knihoven.
3. Izolace skriptů běžících v rámci virtuálního stroje jazyka Lua
Vestavěný interpret jazyka Lua do jisté míry řeší taktéž otázku bezpečnosti skriptů, aby se zabránilo šíření makrovirů, které byly tak „populární“ mezi uživateli jednoho rozšířeného kancelářského balíku. Problém bezpečnosti je řešen především izolací běhového prostředí skriptů od ostatního systému. Pouze přímo programátor aplikace, která má obsahovat překladač a interpret Lua, může (explicitně zapsaným importem příslušné knihovny) skriptům povolit například možnost práce se soubory, spouštění dalších programů přes volání os.execute() apod. Bez importu těchto knihoven je skriptu povoleno se svým okolím komunikovat pouze s využitím volání zaregistrovaných funkcí: skript tedy v reálné aplikaci nemá žádnou možnost, jak přistoupit k souborovému systému, k síťovému rozhraní, nemůže bez povolení ani spustit jiný program, nemůže poškodit paměť své hostitelské nativní aplikace apod. Pro předávání parametrů se navíc používá zvláštní zásobník, ne standardní rámec procesu (na něj se ukládá pouze jeden ukazatel), takže skripty ani nemají možnost manipulovat se zásobníkem procesu pod kterým běží (tím se eliminují prakticky všechny útoky typu stack overflow). Interpret provádí i základní kontrolu korektnosti předaného bajtkódu.
Pro zajímavost: skriptu napsanému v jazyku Lua lze povolit či naopak neumožnit přístup k těmto knihovnám:
# | Knihovna | Význam |
---|---|---|
1 | coroutine | Podpora pro volání koprogramů |
2 | table | Práce s tabulkami a poli |
3 | io | Základní vstupně/výstupní operace |
4 | os | Volání systémových funkcí |
5 | string | Práce s řetězci |
6 | math | Matematické funkce |
7 | debug | Reflexe, přístup k zásobníkovým rámcům atd. |
8 | package | Práce s balíčky apod. |
Pokud se nějaká knihovna nenačte, není nutné ji ani linkovat, což je užitečné zejména u podpory matematických funkcí na mikrořadičích (ušetří se desítky kilobajtů kódu).
4. Inicializace a ukončení práce virtuálního stroje jazyka Lua
V této kapitole budou ukázány dva demonstrační příklady, na nichž si vyzkoušíme základy připojení virtuálního stroje programovacího jazyka Lua s nativní aplikací. Překladač a interpret programovacího jazyka Lua je možné do aplikací vytvářených v céčku či C++ vložit velmi snadno. Lua se instaluje buď překladem ze zdrojových kódů (k řízení překladu je samozřejmě určen soubor Makefile) nebo s využitím balíčkovacího nástroje příslušné Linuxové distribuce (v tomto případě je nutné nainstalovat i variantu nazvanou „Lua devel“, například liblua5.1–0-dev).
V obou případech získáme několik hlavičkových souborů, především pak lua.h, lualib.h a lauxlib.h (pro C++ i lua.hpp) a taktéž archiv nazvaný liblua.a (popř. liblua5.1.a atd.), jenž obsahuje objektové soubory potřebné pro sestavení aplikace s překladačem a interpretem Lua (pokud se nepoužije překladač gcc, může být název archivu s objektovými soubory odlišný, některé překladače například pracují se soubory majícími koncovku .lib).
Ve stále ještě velmi často používané (i když dnes již poněkud zastaralé) verzi Lua 5.1 obsahuje archiv liblua5.1.a následující soubory (výpis je proveden příkazem ar t liblua.a | sort):
lapi.o lauxlib.o lbaselib.o lcode.o ldblib.o ldebug.o ldo.o ldump.o lfunc.o lgc.o linit.o liolib.o llex.o lmathlib.o lmem.o loadlib.o lobject.o lopcodes.o loslib.o lparser.o lstate.o lstring.o lstrlib.o ltable.o ltablib.o ltm.o lundump.o lvm.o lzio.o
Pro knihovnu jazyka Lua ve verzi 5.2 několik objektových souborů přibylo. Nové objektové soubory jsou v následujícím výpisu zvýrazněny:
lapi.o lauxlib.o lbaselib.o lbitlib.o lcode.o lcorolib.o lctype.o ldblib.o ldebug.o ldo.o ldump.o lfunc.o lgc.o linit.o liolib.o llex.o lmathlib.o lmem.o loadlib.o lobject.o lopcodes.o loslib.o lparser.o lstate.o lstring.o lstrlib.o ltable.o ltablib.o ltm.o lundump.o lvm.o lzio.o
4.1 První demonstrační příklad – vytištění informací o verzi interpretru i o jeho autorech
Dnešní první demonstrační příklad je velmi jednoduchý, protože se v něm pracuje pouze s několika konstantami uloženými v hlavičkovém souboru lualib.h, konkrétně s konstantami LUA_VERSION, LUA_RELEASE, LUA_COPYRIGHT a LUA_AUTHORS. Hodnoty těchto konstant jsou součástí přeložené aplikace:
Zdrojový kód:
/* * Prvni demonstracni priklad ukazujici zpusob propojeni * skriptu v Lue a kodu v C. Zde se pouze vytisknou informace * o verzi interpretru i o jeho autorech. Zadne dalsi operace * se s virtualnim strojem neprovadi. */ #include <stdio.h> #include <stdlib.h> /* Zakladni a doplnkove funkce interpretu jazyka Lua */ #include <lualib.h> /* Hlavni funkce konzolove aplikace */ int main(void) { /* vytisteni hlavicky */ puts(LUA_VERSION); puts(LUA_RELEASE); puts(LUA_COPYRIGHT); puts(LUA_AUTHORS); putchar('\n'); /* navratovy kod */ return 0; } /* finito */
Makefile určený pro překlad (cestu k hlavičkovému souboru a verzi VM Lua je zapotřebí upravit):
CC=gcc COPTIONS=-ansi -Wall INCLUDEPATH=/usr/include/lua5.1 LIBS=lua5.1 all: lua_c_1 clean: rm -f *.o rm -f lua_c_1 lua_c_1: lua_c_1.o $(CC) -o $@ $< -l$(LIBS) %.o: %.c $(CC) $(COPTIONS) -c -I$(INCLUDEPATH) $<
Po překladu a spuštění by se měly vypsat tyto informace (pochopitelně s případnou změnou verze):
Lua 5.1 Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio R. Ierusalimschy, L. H. de Figueiredo & W. Celes
Podívejme se ještě příkazem ldd na knihovny používané přeloženou aplikací:
linux-vdso.so.1 => (0x00007fffd87fe000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbe9f85f000) /lib64/ld-linux-x86-64.so.2 (0x00007fbe9fc48000)
(vidíme, že se pro spuštění této aplikace nepoužívají žádné knihovny jazyka Lua!)
4.2 Druhý demonstrační příklad – inicializace a ukončení práce virtuálního stroje jazyka Lua
Ve druhém demonstračním příkladu se objevují nové funkce, zejména pak funkce nazvaná luaL_newstate() a též funkce lua_close(). První z těchto funkcí slouží k inicializaci virtuálního stroje jazyka Lua a taktéž k vytvoření stavu běhového prostředí. Stav je uložen v datové struktuře typu lua_State a většina funkcí, která s virtuálním strojem jazyka Lua komunikuje, má jako jeden z parametrů ukazatel na tuto datovou strukturu (to vlastně znamená, že je možné mít v rámci jedné nativní aplikace větší množství navzájem izolovaných virtuálních strojů jazyka Lua!):
/* * Druhy demonstracni priklad - inicializace virtualniho stroje * ulozeni jeho stavu do lokalni promenne a nasledne ukonceni prace. */ #include <stdio.h> #include <stdlib.h> /* Zakladni a doplnkove funkce interpretu jazyka Lua */ #include <lualib.h> #include <lauxlib.h> /* Hlavni funkce konzolove aplikace */ int main(void) { /* vytisteni hlavicky */ puts(LUA_RELEASE); puts(LUA_COPYRIGHT); puts(LUA_AUTHORS); putchar('\n'); /* vytvoreni objektu, do nejz se uklada stav virtualniho stroje */ lua_State* L = luaL_newstate(); printf("State: %p\n", L); /* odstraneni vsech objektu asociovanych se stavem "Lua" */ lua_close(L); /* navratovy kod */ return 0; } /* finito */
Na standardní výstup se po spuštění druhého demonstračního příkladu vypíšou tyto řádky:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio R. Ierusalimschy, L. H. de Figueiredo & W. Celes State: 0x1f70010
Makefile použitý pro překlad:
CC=gcc COPTIONS=-ansi -Wall INCLUDEPATH=/usr/include/lua5.1 LIBS=lua5.1 all: lua_c_2 clean: rm -f *.o rm -f lua_c_1 lua_c_2: lua_c_2.o $(CC) -o $@ $< -l$(LIBS) %.o: %.c $(CC) $(COPTIONS) -c -I$(INCLUDEPATH) $<
Opět se příkazem ldd podívejme na knihovny používané přeloženou aplikací:
linux-vdso.so.1 => (0x00007fff353fe000) liblua5.1.so.0 => /usr/lib/x86_64-linux-gnu/liblua5.1.so.0 (0x00007fd6c7f2d000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd6c7b68000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fd6c7861000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fd6c765d000) /lib64/ld-linux-x86-64.so.2 (0x00007fd6c817e000)
5. Spuštění skriptů z nativního (céčkového) kódu
V této kapitole si ukážeme dva základní způsoby spuštění skriptů (psaných v jazyce Lua) z nativního kódu. Nejprve bude předvedeno spuštění skriptu uloženého v řetězci, posléze pak spuštění skriptu načteného z externího souboru.
5.1 Třetí demonstrační příklad – spuštění skriptu uloženého v řetězci
V dnešním třetím demonstračním příkladu je ukázán způsob spuštění skriptu napsaného v programovacím jazyce Lua, který je uložen v céčkovém řetězcovém literálu, tj. v řetězcové konstantě umístěné přímo ve výsledném spustitelném souboru v kódovém segmentu (při prohlížení spustitelného souboru binárním editorem lze zdrojový kód skriptu poměrně rychle nalézt). Celý skript je spuštěn s využitím funkce luaL_dostring(), přičemž první parametr představuje objekt s uloženým stavem interpretu a druhý parametr je řetězec se skriptem (přesněji řečeno odkaz na paměť s uloženým řetězcem zakončeným nulou, jak je ostatně v céčku zvykem).
Nejedná se sice o typický způsob ukládání skriptů, už jen kvůli nepřehlednosti zápisu řetězce na mnoha řádcích a problémy s některými znaky se speciálním významem (ty je zapotřebí převést na céčkovou znakovou entitu), v některých případech se však může hodit – například tehdy, když se má celá aplikace spouštět na mikrořadičích bez souborového systému či v případě požadavku, aby byl celý program i se všemi potřebnými daty uložen v jediném souboru, který tak není nutné instalovat, ale lze ho spouštět například přímo ze sítě či přenosného USB disku. Následuje výpis zdrojového kódu prvního demonstračního příkladu:
/* * Treti demonstracni priklad - spusteni skriptu napsaneho * v programovacim jazyce Lua, ktery je ulozeny v Ceckovem * retezcovem literalu. */ #include <stdio.h> #include <stdlib.h> /* Zakladni a doplnkove funkce interpretu jazyka Lua */ #include <lualib.h> #include <lauxlib.h> /* Skript napsany v programovacim jazyce Lua */ const char * SCRIPT = "-- Demonstracni priklad: pouziti uzaveru\n"\ "\n"\ "-- pomocna funkce vracejici uzaver\n"\ "function defPosloupnosti(n)\n"\ "-- pamatovana hodnota, ktera vsak\n"\ "-- neni z okolniho programu dostupna\n"\ "local y = 1\n"\ "-- pocitadlo volani = exponent\n"\ "local index = 0\n"\ "-- anonymni funkce vytiskne pamatovanou\n"\ "-- hodnotu a nakonec ji vynasobi zvolenou konstantou\n"\ "return function()\n"\ "print(index, y)\n"\ "y = y * n\n"\ "index = index + 1\n"\ "end\n"\ "end\n"\ "\n"\ "print('mocniny cisla 2')\n"\ "-- ziskani uzaveru\n"\ "generator = defPosloupnosti(2)\n"\ "\n"\ "-- postupne se budou tisknout\n"\ "-- mocniny cisla 2\n"\ "for i=0, 16 do\n"\ "generator()\n"\ "end\n"\ "\n"\ "print()\n"\ "\n"\ "print('mocniny cisla 3')\n"\ "-- ziskani uzaveru\n"\ "generator = defPosloupnosti(3)\n"\ "\n"\ "-- postupne se budou tisknout\n"\ "-- mocniny cisla 3\n"\ "for i=0, 16 do\n"\ "generator()\n"\ "end\n"\ "\n"\ "print()\n"\ "\n"\ "print('mocniny cisla 10')\n"\ "-- ziskani uzaveru\n"\ "generator = defPosloupnosti(10)\n"\ "\n"\ "-- postupne se budou tisknout\n"\ "-- mocniny cisla 3\n"\ "for i=0, 16 do\n"\ "generator()\n"\ "end\n"; /* Hlavni funkce konzolove aplikace */ int main(void) { /* navratova hodnota ziskana po zavolani skriptu */ int result; /* vytisteni hlavicky */ puts(LUA_RELEASE); puts(LUA_COPYRIGHT); puts(LUA_AUTHORS); putchar('\n'); /* vytvoreni objektu, do nejz se uklada stav virtualniho stroje */ lua_State* L = luaL_newstate(); /* nacteme zakladni knihovnu obsahujici mj. i funkci print() */ luaopen_base(L); /* nacteni retezce interpretem, jeho preklad a nasledne spusteni */ result = luaL_dostring(L, SCRIPT); /* odstraneni vsech objektu asociovanych se stavem "Lua" */ lua_close(L); if (result != 0) { printf("Error # %d\n", result); } /* vypocet navratoveho kodu */ return (result != 0); } /* finito */
Po spuštění třetího příkladu se na standardní výstup vypíšou následující řádky:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio R. Ierusalimschy, L. H. de Figueiredo & W. Celes mocniny cisla 2 0 1 1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024 11 2048 12 4096 13 8192 14 16384 15 32768 16 65536 mocniny cisla 3 0 1 1 3 2 9 3 27 4 81 5 243 6 729 7 2187 8 6561 9 19683 10 59049 11 177147 12 531441 13 1594323 14 4782969 15 14348907 16 43046721 mocniny cisla 10 0 1 1 10 2 100 3 1000 4 10000 5 100000 6 1000000 7 10000000 8 100000000 9 1000000000 10 10000000000 11 100000000000 12 1000000000000 13 10000000000000 14 1e+14 15 1e+15 16 1e+16
Makefile je prakticky shodný s předchozími dvěma příklady.
5.2 Čtvrtý demonstrační příklad – spuštění skriptu uloženého v externím souboru
Ve čtvrtém demonstračním příkladu je ukázán mnohem častěji používaný způsob spuštění skriptu, jehož kód je uložen v externím souboru (a ne v řetězci, jak tomu bylo v příkladu předchozím). Externě uložený skript je buď možné programově načíst do céčkového řetězce, ovšem to je poměrně komplikované (musí se například předem zjišťovat délka řetězce, alokovat paměť pro řetězec atd.). Jednodušší je použít již odladěnou knihovní funkci nazvanou luaL_dofile(), která načtení, překlad i spuštění skriptu provede automaticky. Návratovou hodnotou této funkce se signalizuje, zda skript proběhl korektně nebo zda v průběhu jeho načítání, překladu, spuštění či běhu došlo k nějaké chybě: v případě korektního běhu se vrátí nula, pokud nastane chyba, vrátí se jednička. Chybu je také možné vygenerovat programově, tj. ve skriptu, zavoláním funkce error(), popřípadě z céčkové funkce zavoláním lua_error() (řetězec obsahující chybové hlášení musí být v tomto případě uložen na zásobníku, což si ukážeme v následujících kapitolách). Zdrojový kód druhého demonstračního příkladu má následující tvar:
/* * Ctvrty demonstracni priklad - spusteni skriptu napsaneho * v programovacim jazyce Lua, ktery je ulozeny v externim * souboru. */ #include <stdio.h> #include <stdlib.h> /* Zakladni a doplnkove funkce interpretu jazyka Lua */ #include <lualib.h> #include <lauxlib.h> /* Hlavni funkce konzolove aplikace */ int main(int argc, char **argv) { /* navratova hodnota ziskana po zavolani skriptu */ int result; /* kontrola, zda je zadano jmeno skriptu */ if (argc != 2) { puts("Pouziti: ./lua_c_4 script.lua"); return 1; } /* vytisteni hlavicky */ puts(LUA_RELEASE); puts(LUA_COPYRIGHT); puts(LUA_AUTHORS); putchar('\n'); /* vytvoreni objektu, do nejz se uklada stav virtualniho stroje */ lua_State* L = luaL_newstate(); /* nacteme zakladni knihovnu obsahujici mj. i funkci print() */ luaopen_base(L); /* nacteni externiho skriptu, jeho preklad a nasledne spusteni */ result = luaL_dofile(L, argv[1]); /* odstraneni vsech objektu asociovanych se stavem "Lua" */ lua_close(L); if (result != 0) { printf("Error # %d\n", result); } /* vypocet navratoveho kodu */ return (result != 0); } /* finito */
Skript naprogramovaný v jazyce Lua, jenž je možné využít pro otestování dnešního čtvrtého demonstračního příkladu:
-- Testovaci skript pro ctvrty demonstracni priklad -- pouziti uzaveru -- pomocna funkce vracejici uzaver function defPosloupnosti(n) -- pamatovana hodnota, ktera vsak -- neni z okolniho programu dostupna local y = 1 -- pocitadlo volani = exponent local index = 0 -- anonymni funkce vytiskne pamatovanou -- hodnotu a nakonec ji vynasobi zvolenou konstantou return function() print(index, y) y = y * n index = index + 1 end end print("mocniny cisla 2") -- ziskani uzaveru generator = defPosloupnosti(2) -- postupne se budou tisknout -- mocniny cisla 2 for i=0, 16 do generator() end print() print("mocniny cisla 3") -- ziskani uzaveru generator = defPosloupnosti(3) -- postupne se budou tisknout -- mocniny cisla 3 for i=0, 16 do generator() end print() print("mocniny cisla 10") -- ziskani uzaveru generator = defPosloupnosti(10) -- postupne se budou tisknout -- mocniny cisla 3 for i=0, 16 do generator() end -- finito
Po spuštění by měl standardní výstup vypadat takto:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio R. Ierusalimschy, L. H. de Figueiredo & W. Celes mocniny cisla 2 0 1 1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024 11 2048 12 4096 13 8192 14 16384 15 32768 16 65536 mocniny cisla 3 0 1 1 3 2 9 3 27 4 81 5 243 6 729 7 2187 8 6561 9 19683 10 59049 11 177147 12 531441 13 1594323 14 4782969 15 14348907 16 43046721 mocniny cisla 10 0 1 1 10 2 100 3 1000 4 10000 5 100000 6 1000000 7 10000000 8 100000000 9 1000000000 10 10000000000 11 100000000000 12 1000000000000 13 10000000000000 14 1e+14 15 1e+15 16 1e+16
6. Komunikace C → Lua
V mnoha případech je nutné z programovacího jazyka C či C++ zavolat funkci naprogramovanou v Lue, popř. zavolat nějakou knihovní funkci z Luy. Právě v těchto případech se setkáme s programovým rozhraním C → Lua založeném na zásobníku, přes který se předávají parametry a návratové hodnoty (v Lue lze z funkce vrátit několik návratových hodnot). Základem při volání funkcí naprogramovaných v jazyku Lua je:
- Céčková funkce lua_getglobal() sloužící pro získání reference na volanou funkci (reference se stane součástí stavu virtuálního stoje, není ji zapotřebí nikam ukládat.
- Céčková funkce lua_pushinteger() pro uložení celočíselné hodnoty na zásobník.
- Další funkce se stejným významem, ale odlišným datovým typem:lua_pushnil, lua_pushnumber, lua_pushinteger, lua_pushunsigned, lua_pushlstring, lua_pushstring, lua_pushvfstring, lua_pushfstring, lua_pushcclosure, lua_pushboolean, lua_pushlightuserdata, lua_pushthread,
- Funkce lua_call() či lua_pcall() pro zavolání Lua kódu.
- lua_pop() pro odstranění návratové hodnoty (hodnot) ze zásobníku.
6.1 Pátý demonstrační příklad – zavolání funkce ze standardní knihovny jazyka Lua z C
V pátém demonstračním příkladu je z céčka zavolána funkce print(42). Nejprve je nutné s využitím lua_getglobal() získat referenci na print(), posléze se přes lua_pushinteger() na zásobník uloží hodnota 42, která se má tisknout a následně je funkce zavolána s využitím lua_pcall() (protected call). Parametry lua_pcall(L, 1, 0, 0) znamenají, že se předává jeden argument a neočekává se žádná návratová hodnota:
/* * Paty demonstracni priklad - zavolani funkce ze standardni * knihovny Lua z jazyka C. */ #include <stdio.h> #include <stdlib.h> /* Zakladni a doplnkove funkce interpretu jazyka Lua */ #include <lualib.h> #include <lauxlib.h> /* Hlavni funkce konzolove aplikace */ int main(void) { int result; /* vytisteni hlavicky */ puts(LUA_RELEASE); puts(LUA_COPYRIGHT); puts(LUA_AUTHORS); putchar('\n'); /* vytvoreni objektu, do nejz se uklada stav virtualniho stroje */ lua_State* L = luaL_newstate(); printf("State: %p\n", L); /* nacteni vsech knihoven */ luaL_openlibs(L); /* bude se volat funkce pojmenovana "print" */ lua_getglobal(L, "print"); /* ulozit parametr volane funkce na zasobnik */ lua_pushinteger(L, 42); /* zavolani funkce "print" a predani jednoho parametru */ /* neocekavame pritom zadne vysledne hodnoty */ result = lua_pcall(L, 1, 0, 0); /* kontrola volani funkce */ if (result) { lua_error(L); puts("Chyba pri volani funkce print()!"); } /* odstraneni vsech objektu asociovanych se stavem "Lua" */ lua_close(L); /* navratovy kod */ return 0; } /* finito */
Výstup tohoto demonstračního příkladu vypadá následovně:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio R. Ierusalimschy, L. H. de Figueiredo & W. Celes State: 0x22dd010 42
6.2 Šestý demonstrační příklad – předání dvou parametrů různých typů volané funkci
V dalším příkladu je ukázáno, jak se volané Lua funkci předává větší množství parametrů, zde konkrétně celočíselná hodnota a řetězec. Nejdůležitější je si uvědomit, že se parametry na zásobník ukládají v tom pořadí, v jakém by se předávaly volané funkci ve skriptu. Dále je pak nutné správně označit počet skutečně předávaných parametrů při volání lua_pcall(), protože virtuální stroj Luy nemá žádnou jinou možnost, jak počet parametrů jinak zjistit (existují Lua funkce akceptující proměnný počet parametrů apod.). Podívejme se na kód příkladu, z něhož bude vše patrné:
/* * Sesty demonstracni priklad - zavolani funkce ze standardni * knihovny Lua z jazyka C. */ #include <stdio.h> #include <stdlib.h> /* Zakladni a doplnkove funkce interpretu jazyka Lua */ #include <lualib.h> #include <lauxlib.h> /* Hlavni funkce konzolove aplikace */ int main(void) { int result; /* vytisteni hlavicky */ puts(LUA_RELEASE); puts(LUA_COPYRIGHT); puts(LUA_AUTHORS); putchar('\n'); /* vytvoreni objektu, do nejz se uklada stav virtualniho stroje */ lua_State* L = luaL_newstate(); printf("State: %p\n", L); /* nacteni vsech knihoven */ luaL_openlibs(L); /* bude se volat funkce pojmenovana "print" */ lua_getglobal(L, "print"); /* ulozit prvni parametr volane funkce na zasobnik */ lua_pushinteger(L, 42); /* ulozit druhy parametr volane funkce na zasobnik */ lua_pushstring(L, "Hello world!"); /* zavolani funkce print a predani dvou parametru */ /* neocekavame pritom zadne vysledne hodnoty */ result = lua_pcall(L, 2, 0, 0); /* kontrola volani funkce */ if (result) { lua_error(L); puts("Chyba pri volani funkce print()!"); } /* odstraneni vsech objektu asociovanych se stavem "Lua" */ lua_close(L); /* navratovy kod */ return 0; } /* finito */
Výstup tohoto demonstračního příkladu vypadá následovně:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio R. Ierusalimschy, L. H. de Figueiredo & W. Celes State: 0x2457010 42 Hello world!
6.3 Sedmý demonstrační příklad – přečtení návratové hodnoty Lua funkce
V sedmém demonstračním příkladu se z nativního kódu volá Lua funkce tonumber(), která převede svůj parametr (řetězec) na číslo. Setkáme se zde tedy jak s předáním parametru volané funkci, tak i se zpracováním výsledku (návratové) hodnoty této funkce, což je zohledněno ve volání lua_pcall(L, 1, 1, 0):
/* * Sedmy demonstracni priklad - zavolani funkce ze standardni * knihovny Lua z jazyka C a precteni navratove hodnoty. */ #include <stdio.h> #include <stdlib.h> /* Zakladni a doplnkove funkce interpretu jazyka Lua */ #include <lualib.h> #include <lauxlib.h> /* Hlavni funkce konzolove aplikace */ int main(void) { int result; /* vytisteni hlavicky */ puts(LUA_RELEASE); puts(LUA_COPYRIGHT); puts(LUA_AUTHORS); putchar('\n'); /* vytvoreni objektu, do nejz se uklada stav virtualniho stroje */ lua_State* L = luaL_newstate(); printf("State: %p\n", L); /* nacteni vsech knihoven */ luaL_openlibs(L); /* bude se volat funkce pojmenovana "tonumber" */ lua_getglobal(L, "tonumber"); /* ulozit parametr volane funkce na zasobnik */ lua_pushstring(L, "42"); /* zavolani funkce print a predani parametru */ result = lua_pcall(L, 1, 1, 0); /* kontrola volani funkce */ if (result) { lua_error(L); puts("Chyba pri volani funkce print()!"); } /* ziskani vysledku */ printf("Result is: %d\n", (int)lua_tointeger(L, 0)); lua_pop(L, 1); /* odstraneni vsech objektu asociovanych se stavem "Lua" */ lua_close(L); /* navratovy kod */ return 0; } /* finito */
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio R. Ierusalimschy, L. H. de Figueiredo & W. Celes State: 0x1642010 Result is: 42
7. Komunikace Lua → C
Jednou z nejdůležitějších funkcí API je zajištění předávání parametrů do céčkové funkce a návratu vypočtených hodnot zpět do Lua skriptu. Vzhledem k tomu, že v jazyku Lua se typy parametrů odvozují z hodnot uložených do proměnných, není možné již v době překladu provést stoprocentní typovou kontrolu – tu je nutné ponechat až na dobu běhu (runtime). Pro usnadnění práce s parametry předávanými ze skriptu do céčkové (C++) aplikace je k dispozici několik funkcí, především lua_gettop() (počet předaných parametrů) a sada funkcí pro zjištění skutečného typu i-tého parametru: lua_isnumber(), lua_isstring(), lua_iscfunction() atd.
Kromě toho existují ještě konverzní funkce typu lua_tonumber(), lua_tointeger() či lua_tolstring(), pomocí nichž lze parametry konvertovat. Pro návrat hodnot z céčkové funkce do skriptu je určený zásobník vytvořený interpretem, jelikož v Lua je možné vracet hodnot několik. Pro ukládání hodnot do tohoto zásobníku slouží funkce typu lua_pushstring() a lua_pushnumber(). Nesmíme také zapomenout na to, že céčková funkce musí vrátit (příkazem return) celkový počet parametrů uložených na zásobník.
7.1 Osmý demonstrační příklad – zavolání céčkové funkce ze skriptu
V následujícím demonstračním příkladu si ukážeme, jakým způsobem je možné předávat parametry céčkové funkci, i to, jak lze kontrolovat počet i typ parametrů a způsob předávání návratových hodnot zpět do Lua skriptu. V příkladu je vytvořena funkce nazvaná gcd(), pomocí níž je možné vypočítat největší společný dělitel (greatest common divisor) dvou celých čísel Euklidovým algoritmem, přičemž první číslo by mělo být větší než druhé. Tato funkce je zaregistrována pro možnost jejího zavolání z Lua skriptu. Po svém zavolání si funkce nejprve zkontroluje počet a typ parametrů a pouze tehdy, když jsou funkci předány dva parametry typu číslo, je funkce provedena. Výsledkem výpočtu je dvojice hodnot – největší společný dělitel a počet iterací Euklidova algoritmu, které bylo zapotřebí provést pro nalezení výsledku. Obě hodnoty jsou uloženy na zásobník vytvořený interpretem jazyka Lua; návratová hodnota funkce gcd() pak udává počet uložených výsledků (tj. vlastně hloubku zaplněného zásobníku). Zdrojový kód tohoto demonstračního příkladu má tvar:
/* * Osmy demonstracni priklad - zavolani ceckove funkce * z Lua skriptu. */ #include <stdio.h> #include <stdlib.h> /* Zakladni a doplnkove funkce interpretu jazyka Lua */ #include <lualib.h> #include <lauxlib.h> /* Vypocet nejvetsiho spolecneho delitele */ static int gcd(lua_State* L) { int x, y, iter = 1; /* zjistit pocet parametru pri volani funkce */ if (lua_gettop(L) != 2) { lua_pushstring(L, "incorrect argument"); lua_error(L); } /* kontrola typu obou parametru */ if (!lua_isnumber(L, 1)) { lua_pushstring(L, "incorrect first argument"); lua_error(L); } if (!lua_isnumber(L, 2)) { lua_pushstring(L, "incorrect second argument"); lua_error(L); } /* nacist parametry */ x = lua_tonumber(L, 1); y = lua_tonumber(L, 2); /* vypocet nejvetsiho spolecneho delitele */ while (x % y > 0) { int pom = y; y = x % y; x = pom; iter ++; } /* prvni vysledek */ lua_pushnumber(L, y); /* druhy vysledek */ lua_pushnumber(L, iter); /* ulozit pocet vysledku na zasobniku */ return 2; } /* Hlavni funkce konzolove aplikace */ int main(int argc, char **argv) { /* navratova hodnota ziskana po zavolani skriptu */ int result; /* kontrola, zda je na CLI zadano jmeno skriptu */ if (argc != 2) { puts("Pouziti: ./lua_c_8 script.lua"); return 1; } /* vytisteni hlavicky */ puts(LUA_RELEASE); puts(LUA_COPYRIGHT); puts(LUA_AUTHORS); putchar('\n'); /* vytvoreni objektu, do nejz se uklada stav virtualniho stroje */ lua_State* L = luaL_newstate(); /* nacteme zakladni knihovnu obsahujici mj. i funkci print() */ luaopen_base(L); /* registrace ceckove funkce gcd() pod jmenem "gcd" */ lua_register(L, "gcd", gcd); /* nacteni externiho skriptu, jeho preklad a nasledne spusteni */ result = luaL_dofile(L, argv[1]); /* odstraneni vsech objektu asociovanych se stavem "Lua" */ lua_close(L); if (result != 0) { printf("Error # %d\n", result); } /* vypocet navratoveho kodu */ return (result != 0); } /* finito */
Testovací skript:
-- Testovaci skript pro osmy demonstracni priklad print("i", "j", "gcd(i,j)", "#of iterations") for i=1, 12 do for j=1, i do -- funkce gcd() vraci dvojici hodnot result, iter = gcd(i, j) print(i, j, result, iter) end end -- finito
Podívejme se, co se stane po spuštění tohoto příkladu:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio R. Ierusalimschy, L. H. de Figueiredo & W. Celes i j gcd(i,j) #of iterations 1 1 1 1 2 1 1 1 2 2 2 1 3 1 1 1 3 2 1 2 3 3 3 1 4 1 1 1 4 2 2 1 4 3 1 2 4 4 4 1 5 1 1 1 5 2 1 2 5 3 1 3 5 4 1 2 5 5 5 1 6 1 1 1 6 2 2 1 6 3 3 1 6 4 2 2 6 5 1 2 6 6 6 1 7 1 1 1 7 2 1 2 7 3 1 2 7 4 1 3 7 5 1 3 7 6 1 2 7 7 7 1 8 1 1 1 8 2 2 1 8 3 1 3 8 4 4 1 8 5 1 4 8 6 2 2 8 7 1 2 8 8 8 1 9 1 1 1 9 2 1 2 9 3 3 1 9 4 1 2 9 5 1 3 9 6 3 2 9 7 1 3 9 8 1 2 9 9 9 1 10 1 1 1 10 2 2 1 10 3 1 2 10 4 2 2 10 5 5 1 10 6 2 3 10 7 1 3 10 8 2 2 10 9 1 2 10 10 10 1 11 1 1 1 11 2 1 2 11 3 1 3 11 4 1 3 11 5 1 2 11 6 1 3 11 7 1 4 11 8 1 4 11 9 1 3 11 10 1 2 11 11 11 1 12 1 1 1 12 2 2 1 12 3 3 1 12 4 4 1 12 5 1 3 12 6 6 1 12 7 1 4 12 8 4 2 12 9 3 2 12 10 2 2 12 11 1 2 12 12 12 1
8. Alternativní způsob komunikace mezi jazykem Lua a nativním kódem: knihovna FFI
Způsob kooperace mezi skripty naprogramovanými v jazyce Lua a nativním kódem, který jsme si popsali v předchozích kapitolách, je bezpečný a poměrně snadno pochopitelný. Ovšem s velkou pravděpodobností se nejedná o způsob, který by vývojář preferoval ve chvíli, kdy potřebuje z jazyka Lua volat množství funkcí z nějaké nativní knihovny (příkladem může být knihovna OpenGL s desítkami funkcí). Naštěstí je možné v mnoha aplikacích použít alternativní způsob volání nativních knihoven, a to s využitím knihovny nazvané FFI (Foreign Function Interface), která je součástí LuaJITu. Při použití FFI je pouze nutné specifikovat hlavičky volaných funkcí (a to včetně specifikace datových typů) a následně se tyto funkce již dají jednoduše volat stylem ffi.C.jméno_funkce(parametry), přičemž o případnou konverzi předávaných parametrů se již programátor ve většině případů nemusí starat (připomeňme si, že ještě v Lua 5.2 se používá jen jediný formát numerických hodnot atd.).
Obrázek 3: Způsob využití FFI při volání funkcí uložených v nativních knihovnách.
8.1 Devátý demonstrační příklad – základ použití knihovny FFI
Podívejme se na způsob použití FFI v praxi. Předpokládejme, že potřebujeme volat céčkovou funkci nazvanou rand(), které se nepředávají žádné parametry. Nejprve je nutné explicitně knihovnu FFI načíst, což zajišťuje první řádek skriptu. Následně se musí zapsat hlavička nativní funkce či funkcí, které se budou z Lua skriptu volat (řádek začínající na ffi.cdef. Samotné volání nativní funkce je již jednoduché: ffi.C.rand(), výsledek (návratová hodnota) je uložena do lokální proměnné nazvané random:
-- Prvni demonstracni priklad vyuzivajici knihovnu FFI local ffi = require("ffi") -- Definice ceckovske funkce ze standardni knihovny ffi.cdef[[ int rand(void); ]] for i=0,10 do -- Zavolani ceckovske funkce local random = ffi.C.rand() print(random) end
Tento příklad je nutné spustit LuaJITem!. Příklad výstupu:
1804289383 846930886 1681692777 1714636915 1957747793 424238335 719885386 1649760492 596516649 1189641421 1025202362
Při pokusu o použití klasického interpretru jazyka Lua dostaneme pouze chybové hlášení:
$ lua ffi_example1.lua lua: ffi_example1.lua:3: module 'ffi' not found: no field package.preload['ffi'] no file '/usr/local/share/lua/5.2/ffi.lua' no file '/usr/local/share/lua/5.2/ffi/init.lua' no file '/usr/local/lib/lua/5.2/ffi.lua' no file '/usr/local/lib/lua/5.2/ffi/init.lua' no file '/usr/share/lua/5.2/ffi.lua' no file '/usr/share/lua/5.2/ffi/init.lua' no file './ffi.lua' no file '/usr/local/lib/lua/5.2/ffi.so' no file '/usr/lib/x86_64-linux-gnu/lua/5.2/ffi.so' no file '/usr/lib/lua/5.2/ffi.so' no file '/usr/local/lib/lua/5.2/loadall.so' no file './ffi.so' stack traceback: [C]: in function 'require' ffi_example1.lua:3: in main chunk [C]: in ?
8.2 Desátý demonstrační příklad – volání složitější funkce
V dalším demonstračním příkladu je knihovna FFI použita pro zavolání funkce nazvané atoi(), která je součástí standardní knihovny programovacího jazyka C. Funkce atoi() již akceptuje (čti vyžaduje) parametr typu řetězec, což je v případě programovacího jazyka C datový typ const char *, tedy ukazatel na první znak řetězce (klíčové slovo const zde může pomáhat překladači při optimalizacích a navíc – což je důležitější – nám říká, že funkce řetězec nebude modifikovat). Při volání funkce atoi() ze skriptu napsaného v Lua se automaticky provede konverze mezi Lua stringem a céčkovým řetězcem:
-- Druhy demonstracni priklad vyuzivajici knihovnu FFI local ffi = require("ffi") -- Definice ceckovske funkce ze standardni knihovny ffi.cdef[[ int atoi(const char *); ]] -- Zavolani ceckovske funkce local value = ffi.C.atoi("42") print(value)
Složitějšími případy, konverzemi dat apod. se budeme zabývat příště.
9. Repositář s dnešními demonstračními příklady
Všech deset dnes popsaných demonstračních příkladů bylo uloženo do Git repositáře dostupného na adrese https://github.com/tisnik/luajit-examples. V tabulce zobrazené pod tímto odstavcem naleznete na zdrojové kódy jednotlivých demonstračních příkladů přímé odkazy:
10. Odkazy na Internetu
- LuaJIT – Just in Time překladač pro programovací jazyk Lua
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (2)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-2/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (3)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-3/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (4)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-4/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (5 – tabulky a pole)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-5-tabulky-a-pole/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (6 – překlad programových smyček do mezijazyka LuaJITu)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-6-preklad-programovych-smycek-do-mezijazyka-luajitu/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (7 – dokončení popisu mezijazyka LuaJITu)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-7-dokonceni-popisu-mezijazyka-luajitu/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (8 – základní vlastnosti trasovacího JITu)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-8-zakladni-vlastnosti-trasovaciho-jitu/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (9 – další vlastnosti trasovacího JITu)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-9-dalsi-vlastnosti-trasovaciho-jitu/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (10 – JIT překlad do nativního kódu)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-10-jit-preklad-do-nativniho-kodu/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (11 – JIT překlad do nativního kódu procesorů s architekturami x86 a ARM)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-11-jit-preklad-do-nativniho-kodu-procesoru-s-architekturami-x86-a-arm/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua (12 – překlad operací s reálnými čísly)
http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-12-preklad-operaci-s-realnymi-cisly/ - The Lua VM, on the Web
https://kripken.github.io/lua.vm.js/lua.vm.js.html - Lua.vm.js REPL
https://kripken.github.io/lua.vm.js/repl.html - lua2js
https://www.npmjs.com/package/lua2js - lua2js na GitHubu
https://github.com/basicer/lua2js-dist - Seriál o programovacím jazyku Lua
http://www.root.cz/serialy/programovaci-jazyk-lua/ - Source-to-source compiler
https://en.wikipedia.org/wiki/Source-to-source_compiler - JavaScript is Assembly Language for the Web: Sematic Markup is Dead! Clean vs. Machine-coded HTML
http://www.hanselman.com/blog/JavaScriptIsAssemblyLanguageForTheWebSematicMarkupIsDeadCleanVsMachinecodedHTML.aspx - JavaScript is Web Assembly Language and that's OK.
http://www.hanselman.com/blog/JavaScriptIsWebAssemblyLanguageAndThatsOK.aspx - Dart
https://www.dartlang.org/ - CoffeeScript
http://coffeescript.org/ - TypeScript
http://www.typescriptlang.org/ - Lua (programming language)
http://en.wikipedia.org/wiki/Lua_(programming_language) - Static single assignment form (SSA)
http://en.wikipedia.org/wiki/Static_single_assignment_form - Wikipedia: Mezijazyk
http://cs.wikipedia.org/wiki/Mezijazyk - LuaJIT 2.0 SSA IRhttp://wiki.luajit.org/SSA-IR-2.0
- The LuaJIT Project
http://luajit.org/index.html - LuaJIT FAQ
http://luajit.org/faq.html - LuaJIT Performance Comparison
http://luajit.org/performance.html - LuaJIT 2.0 intellectual property disclosure and research opportunities
http://article.gmane.org/gmane.comp.lang.lua.general/58908 - LuaJIT Wiki
http://wiki.luajit.org/Home - LuaJIT 2.0 Bytecode Instructions
http://wiki.luajit.org/Bytecode-2.0 - Programming in Lua (first edition)
http://www.lua.org/pil/contents.html - Lua 5.2 sources
http://www.lua.org/source/5.2/ - Tcl Plugin Version 3
http://www.tcl.tk/software/plugin/ - JavaScript: The Web Assembly Language?
http://www.informit.com/articles/article.aspx?p=1856657 - asm.js
http://asmjs.org/ - List of languages that compile to JS
https://github.com/jashkenas/coffeescript/wiki/List-of-languages-that-compile-to-JS - REPL
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - The LLVM Compiler Infrastructure
http://llvm.org/ProjectsWithLLVM/ - clang: a C language family frontend for LLVM
http://clang.llvm.org/ - emscripten
http://kripken.github.io/emscripten-site/ - LLVM Backend („Fastcomp“)
http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html#llvm-backend - Emscripten – Fastcomp na GitHubu
https://github.com/kripken/emscripten-fastcomp - Clang (pro Emscripten) na GitHubu
https://github.com/kripken/emscripten-fastcomp-clang - Why not use JavaScript?
https://ckknight.github.io/gorillascript/ - Lambda the Ultimate: Coroutines in Lua,
http://lambda-the-ultimate.org/node/438 - Coroutines Tutorial,
http://lua-users.org/wiki/CoroutinesTutorial - Lua Coroutines Versus Python Generators,
http://lua-users.org/wiki/LuaCoroutinesVersusPythonGenerators