Hlavní navigace

Kooperace mezi jazykem Lua a nativním (céčkovým) kódem

Pavel Tišnovský 4. 8. 2015

V předchozím článku o programovacím jazyku Lua jsme se mj. seznámili s možností transpřekladu skriptů naprogramovaných v Lue do jazyka C. Zdrojový kód v céčku je pak možné běžným způsobem přeložit. Mnohem častěji se v praxi využívá kooperace mezi nativním (céčkovým) kódem a skripty napsanými v Lue.

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. Komunikace C → Lua

   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. Komunikace Lua → C

   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

10. Odkazy na Internetu

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:

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

  1. LuaJIT – Just in Time překladač pro programovací jazyk Lua
    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 (2)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-2/
  3. 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/
  4. 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/
  5. 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/
  6. 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/
  7. 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/
  8. 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/
  9. 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/
  10. 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/
  11. 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/
  12. 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/
  13. The Lua VM, on the Web
    https://kripken.github.io/lu­a.vm.js/lua.vm.js.html
  14. Lua.vm.js REPL
    https://kripken.github.io/lu­a.vm.js/repl.html
  15. lua2js
    https://www.npmjs.com/package/lua2js
  16. lua2js na GitHubu
    https://github.com/basicer/lua2js-dist
  17. Seriál o programovacím jazyku Lua
    http://www.root.cz/serialy/pro­gramovaci-jazyk-lua/
  18. Source-to-source compiler
    https://en.wikipedia.org/wiki/Source-to-source_compiler
  19. JavaScript is Assembly Language for the Web: Sematic Markup is Dead! Clean vs. Machine-coded HTML
    http://www.hanselman.com/blog/Ja­vaScriptIsAssemblyLanguage­ForTheWebSematicMarkupIsDe­adCleanVsMachinecodedHTML­.aspx
  20. JavaScript is Web Assembly Language and that's OK.
    http://www.hanselman.com/blog/Ja­vaScriptIsWebAssemblyLangu­ageAndThatsOK.aspx
  21. Dart
    https://www.dartlang.org/
  22. CoffeeScript
    http://coffeescript.org/
  23. TypeScript
    http://www.typescriptlang.org/
  24. Lua (programming language)
    http://en.wikipedia.org/wi­ki/Lua_(programming_langu­age)
  25. Static single assignment form (SSA)
    http://en.wikipedia.org/wi­ki/Static_single_assignmen­t_form
  26. Wikipedia: Mezijazyk
    http://cs.wikipedia.org/wi­ki/Mezijazyk
  27. LuaJIT 2.0 SSA IRhttp://wiki.luajit.org/SSA-IR-2.0
  28. The LuaJIT Project
    http://luajit.org/index.html
  29. LuaJIT FAQ
    http://luajit.org/faq.html
  30. LuaJIT Performance Comparison
    http://luajit.org/performance.html
  31. LuaJIT 2.0 intellectual property disclosure and research opportunities
    http://article.gmane.org/gma­ne.comp.lang.lua.general/58908
  32. LuaJIT Wiki
    http://wiki.luajit.org/Home
  33. LuaJIT 2.0 Bytecode Instructions
    http://wiki.luajit.org/Bytecode-2.0
  34. Programming in Lua (first edition)
    http://www.lua.org/pil/contents.html
  35. Lua 5.2 sources
    http://www.lua.org/source/5.2/
  36. Tcl Plugin Version 3
    http://www.tcl.tk/software/plugin/
  37. JavaScript: The Web Assembly Language?
    http://www.informit.com/ar­ticles/article.aspx?p=1856657
  38. asm.js
    http://asmjs.org/
  39. List of languages that compile to JS
    https://github.com/jashke­nas/coffeescript/wiki/List-of-languages-that-compile-to-JS
  40. REPL
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  41. The LLVM Compiler Infrastructure
    http://llvm.org/ProjectsWithLLVM/
  42. clang: a C language family frontend for LLVM
    http://clang.llvm.org/
  43. emscripten
    http://kripken.github.io/emscripten-site/
  44. LLVM Backend („Fastcomp“)
    http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html#llvm-backend
  45. Emscripten – Fastcomp na GitHubu
    https://github.com/kripken/emscripten-fastcomp
  46. Clang (pro Emscripten) na GitHubu
    https://github.com/kripken/emscripten-fastcomp-clang
  47. Why not use JavaScript?
    https://ckknight.github.i­o/gorillascript/
  48. Lambda the Ultimate: Coroutines in Lua,
    http://lambda-the-ultimate.org/node/438
  49. Coroutines Tutorial,
    http://lua-users.org/wiki/CoroutinesTutorial
  50. Lua Coroutines Versus Python Generators,
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators
Našli jste v článku chybu?
Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Vitalia.cz: Žloutenka v Brně: Nakaženo bylo 400 lidí

Žloutenka v Brně: Nakaženo bylo 400 lidí

Podnikatel.cz: Dárky v podnikání. Jak je uplatnit v daních?

Dárky v podnikání. Jak je uplatnit v daních?

DigiZone.cz: Další dva kanály nabídnou HbbTV

Další dva kanály nabídnou HbbTV

Vitalia.cz: Jak koupit Mikuláše a nenaletět

Jak koupit Mikuláše a nenaletět

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

Měšec.cz: Finančním poradcům hrozí vracení provizí

Finančním poradcům hrozí vracení provizí

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

Podnikatel.cz: Na poslední chvíli šokuje vyjímkami v EET

Na poslední chvíli šokuje vyjímkami v EET

DigiZone.cz: Flix TV má set-top box s HEVC

Flix TV má set-top box s HEVC

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Vitalia.cz: Chtějí si léčit kvasinky. Lék je jen v Německu

Chtějí si léčit kvasinky. Lék je jen v Německu

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Lupa.cz: Google měl výpadek, nejel Gmail ani YouTube

Google měl výpadek, nejel Gmail ani YouTube

Podnikatel.cz: 1. den EET? Problémy s pokladnami

1. den EET? Problémy s pokladnami

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Vitalia.cz: Tesco: Chudá rodina si koupí levné polské kuře

Tesco: Chudá rodina si koupí levné polské kuře

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Vitalia.cz: 9 největších mýtů o mase

9 největších mýtů o mase