Obsah
1. LuaJIT – Just in Time překladač pro programovací jazyk Lua (4)
2. Volání funkci, předávání parametrů a vracení hodnot z volaných funkcí v IR LuaJITu
3. Demonstrační příklad test19.lua: volání funkce print() s předáním různého počtu parametrů
3.1 Zdrojový kód příkladu test19.lua
3.2 Překlad příkladu test19.lua do mezijazyka LuaJITu
4. Demonstrační příklad test20.lua: překlad a volání uživatelských funkcí
4.1 Zdrojový kód příkladu test20.lua
4.2 Překlad příkladu test20.lua do mezijazyka LuaJITu
5. Demonstrační příklad test21.lua: volání „statických metod“
5.1 Zdrojový kód příkladu test21.lua
5.2 Překlad příkladu test21.lua do mezijazyka LuaJITu
6. Demonstrační příklad test22.lua: volání „metod“
6.1 Zdrojový kód příkladu test22.lua
6.2 Překlad příkladu test22.lua do mezijazyka LuaJITu
7. Demonstrační příklad test23.lua: způsob vracení parametrů z volaných funkcí
7.1 Zdrojový kód příkladu test23.lua
7.2 Překlad příkladu test23.lua do mezijazyka LuaJITu
8. Repositář se zdrojovými kódy všech pěti dnešních demonstračních příkladů
1. LuaJIT – Just in Time překladač pro programovací jazyk Lua (4)
Ve čtvrté části článku o velmi zajímavém a přitom poměrně jednoduchém Just in Time překladači nazvaném LuaJIT se budeme zabývat problematikou volání funkcí v programovacím jazyku Lua, přesněji řečeno způsobem překladu volání funkcí do mezijazyka (IR) využívaného LuaJITem. Taktéž si řekneme a na demonstračních příkladech i prakticky ukážeme, jakým způsobem se do volaných funkcí předávají parametry a jak se naopak z volaných funkcí vrací hodnota či hodnoty do té části programu, z něhož je daná funkce volána. Voláním funkcí jsme se poměrně podrobně zabývali již při vzájemném porovnávání bajtkódů JVM, Lua VM a Python VM, takže si jen stručně připomeňme, že Lua jakožto dynamicky typovaný programovací jazyk vyhodnocuje typy předávaných hodnot až v době běhu aplikace (tj. při volání konkrétní funkce či metody).
Navíc se v programovacím jazyku Lua nemusí při volání funkcí dodržet přesný počet parametrů – nadbytečné parametry nejsou využity a naopak, pokud se při volání uvede menší počet parametrů, než by odpovídalo počtu argumentů volané funkce (uvedené v její hlavičce), jsou tyto argumenty automaticky nastaveny na hodnotu nil. Aby byla situace ještě poněkud komplikovanější, je v Lua možné, aby funkce vracely více než jednu hodnotu a z funkcí lze i s (na)vázanými proměnnými vytvořit uzávěr (closure).
2. Volání funkci, předávání parametrů a vracení hodnot z volaných funkcí v IR LuaJITu
V demonstračních příkladech, které budou popsány v navazujících kapitolách, se při jejich překladu do mezijazyka LuaJITu vyskytuje větší množství nových či nově použitých instrukcí. Tyto instrukce jsou stručně popsány v tabulce pod tímto odstavcem:
# | Instrukce | Operandy | Popis |
---|---|---|---|
1 | GSET | var, str | uložení nové položky do globální tabulky _G, použito při deklaraci funkcí |
2 | GGET | dst, str | přečtení položky z globální tabulky _G, použito před voláním funkcí |
3 | FNEW | dst, func | vytvoření uzávěru (closure) z prototypu funkce |
4 | CALL | base, lit, lit | volání funkce s předáním parametrů (standardní forma volání) |
5 | CALLM | base, lit, lit | volání funkce s předáním parametrů (speciální forma volání) |
5 | RET0 | A, D | návrat z funkce bez předání návratového hodnoty (A a D se ignorují) |
6 | RET1 | A, D | návrat z funkce s předáním jedné návratové hodnoty A (D se ignoruje) |
7 | RET | A, D | návrat z funkce s předáním jedné návratových hodnot A, A+1, … A+D-2 |
8 | TNEW | A, D | vytvoření nové tabulky o velikosti zapsané v D |
9 | TSETS | var, var, str | uložení prvku do tabulky (klíčem je řetězec) |
10 | TGETS | dst, var, str | přečtení prvku z tabulky (klíčem je řetězec) |
3. Demonstrační příklad test19.lua: volání funkce print() s předáním různého počtu parametrů
V dnešním prvním demonstračním příkladu si ukážeme, jakým způsobem je možné volat funkci print(), která je součástí standardní knihovny programovacího jazyka Lua. Tuto funkci lze volat bez parametrů – v tom případě se na standardní výstup jednoduše zapíše nový řádek (provede se odřádkování). Pokud se tato funkce zavolá s jedním parametrem, je na standardní výstup poslána textová reprezentace hodnoty parametru, v případě většího počtu parametrů jsou tyto parametry taktéž zkonvertovány na text a vypsány na stejný řádek standardního výstupu. Díky tomuto chování funkce print() si můžeme ukázat, jak lze volat funkci akceptující proměnný počet parametrů. V tomto příkladu je taktéž vytvořena uživatelská funkce main(), která musí být v době běhu programu vytvořena z prototypu, následně uložena do globální tabulky _G a posléze zavolána.
3.1 Zdrojový kód příkladu test19.lua
Zdrojový kód prvního demonstračního příkladu je velmi jednoduchý:
-- -- LuaJIT: demonstrační příklad číslo 19 -- -- Volání funkci v programovacím jazyce Lua. -- -- -- Spuštění testu. -- function main() print() print(nil) print(42) print(1, 2) print("xyzzy") end main() -- finito
3.2 Překlad příkladu test19.lua do mezijazyka LuaJITu
V sekvenci instrukcí vzniklých přeložením příkladu test19.lua do mezijazyka LuaJITu můžeme najít volání funkce print() realizované instrukcemi GGET a CALL. První z těchto instrukcí načte reference na funkci z globální tabulky _G, druhá instrukce obstará volání:
; Překlad demonstračního příkladu test19.lua ; do IR využívaného virtuálním strojem a JIT ; překladačem LuaJIT. -- BYTECODE -- test19.lua:12-18 ; function main() ; print() 0001 GGET 0 0 ; získání reference na funkci se jménem "print" 0002 CALL 0 1 1 ; volání funkce print() ; print(nil) 0003 GGET 0 0 ; získání reference na funkci se jménem "print" 0004 KPRI 1 0 ; uložení primitivní hodnoty nil do slotu číslo 1 0005 CALL 0 1 2 ; volání funkce print() ; print(42) 0006 GGET 0 0 ; získání reference na funkci se jménem "print" 0007 KSHORT 1 42 ; uložení numerické hodnoty 42 do slotu číslo 1 0008 CALL 0 1 2 ; volání funkce print() ; print(1,2) 0009 GGET 0 0 ; získání reference na funkci se jménem "print" 0010 KSHORT 1 1 ; uložení numerické hodnoty 1 do slotu číslo 1 0011 KSHORT 2 2 ; uložení numerické hodnoty 2 do slotu číslo 2 0012 CALL 0 1 3 ; volání funkce print() ; print("xyzzy") 0013 GGET 0 0 ; získání reference na funkci se jménem "print" 0014 KSTR 1 1 ; uložení reference na řetězec "xyzzy" do slotu číslo 1 0015 CALL 0 1 2 ; volání funkce print() 0016 RET0 0 1 ; návrat z funkce main
Důležitá je i sekvence instrukcí použitá pro vytvoření a registraci funkce main(). Najdeme zde především dvě instrukce FNEW a GSET, kde první instrukce vytvoří uzávěr z prototypu funkce a druhá instrukce uloží tento uzávěr do globální tabulky _G:
-- BYTECODE -- test19.lua:0-27 ; main() ; vytvoření, "registrace" a následné volání funkce main() 0001 FNEW 0 0 ; vytvoření uzávěru tvořeného funkcí "main" s uložením do slotu číslo 0 0002 GSET 0 1 ; uložení uzávěru do globální tabulky _G pod jménem "main" 0003 GGET 0 1 ; získání reference na funkci se jménem "main" 0004 CALL 0 1 1 ; zavolání funkce main() 0005 RET0 0 1 ; návrat z programu ; konec
4. Demonstrační příklad test20.lua: překlad a volání uživatelských funkcí
Dnešní druhý demonstrační příklad, který je nazvaný test20.lua, je, podobně jako příklad předchozí, stále velmi jednoduchý. Jsou v něm implementovány funkce se jmény function1(), function2() a function3(), přičemž první funkce pojmenovaná function1() neočekává žádné argumenty, funkce se jménem function2() očekává jeden argument a konečně funkce function3() očekává dva argumenty, které jsou sečteny (v případě, že argumenty nejsou typu celé či reálné číslo, vznikne při běhu programu chyba, to však při překladu do IR neuvidíme). Všechny tři zmíněné funkce jsou volány z kódu, který je implementován v další trojici funkcí nazvaných callFunction1(), callFunction2() a callFunction3(). Povšimněte si, že se nikde nekontroluje ani počet ani typ skutečně předávaných parametrů, což je pro jazyk Lua a současně i pro několik dalších dynamicky typovaných programovacích jazyků typická vlastnost:
4.1 Zdrojový kód příkladu test20.lua
Podívejme se na zdrojový kód demonstračního příkladu test20.lua:
-- -- LuaJIT: demonstrační příklad číslo 20 -- -- Volání funkci v programovacím jazyce Lua. -- -- -- Funkce bez parametrů. -- function function1() end -- -- Funkce s jedním parametrem. -- function function2(x) return x end -- -- Funkce se dvěma parametry. -- function function3(x, y) if x and y then return x+y else return nil end end -- -- Volání funkce function1(). -- function callFunction1() function1() function1(nil) function1(42) function3(1, 2) function1("xyzzy") end -- -- Volání funkce function2(). -- function callFunction2() function2() function2(nil) function2(42) function3(1, 2) function2("xyzzy") end -- -- Volání funkce function3(). -- function callFunction3() function3() function3(nil) function3(42) function3(1, 2) function3("xyzzy") end -- -- Spuštění testu. -- function main() callFunction1() callFunction2() callFunction3() end main() -- finito
4.2 Překlad příkladu test20.lua do mezijazyka LuaJITu
V přeloženém příkladu test20.lua stojí za povšimnutí několik zajímavostí. Především se zde nepoužívá jediná instrukce RET, ale dvě různé instrukce RET0 a RET1. Instrukce RET0 zajišťuje návrat z volané funkce do funkce volající, ovšem bez předání návratové hodnoty. Naproti tomu instrukce RET1 dokáže do volající funkce předat jednu hodnotu, konkrétně hodnotu uloženou ve slotu, jehož index je uložen přímo v instrukčním slovu:
; Překlad demonstračního příkladu test20.lua ; do IR využívaného virtuálním strojem a JIT ; překladačem LuaJIT. -- BYTECODE -- test20.lua:12-13 ; function function1() ; end 0001 RET0 0 1 ; návrat z funkce -- BYTECODE -- test20.lua:20-22 ; function function2(x) ; return x 0001 RET1 0 2 ; návrat z funkce + předání návratové hodnoty -- BYTECODE -- test20.lua:29-35 ; function function3(x, y) ; if x and y then 0001 ISF 0 ; test, zda není první parametr funkce false či nil 0002 JMP 2 => 0008 ; + podmíněný skok 0003 ISF 1 ; test, zda není druhý parametr funkce false či nil 0004 JMP 2 => 0008 ; + podmíněný skok ; return x+y 0005 ADDVV 2 0 1 ; výpočet návratové hodnoty 0006 RET1 2 2 ; návrat z funkce + předání návratové hodnoty ; else 0007 JMP 2 => 0010 ; nepodmíněný skok ; return nil 0008 => KPRI 2 0 ; návratová hodnota 0009 RET1 2 2 ; návrat z funkce + předání návratové hodnoty 0010 => RET0 0 1 ; návrat z funkce -- BYTECODE -- test20.lua:42-48 ; function callFunction1() ; function1() 0001 GGET 0 0 ; získání reference na funkci se jménem "function1" 0002 CALL 0 1 1 ; volání funkce ; function1(nil) 0003 GGET 0 0 ; získání reference na funkci se jménem "function1" 0004 KPRI 1 0 0005 CALL 0 1 2 ; volání funkce ; function1(42) 0006 GGET 0 0 ; získání reference na funkci se jménem "function1" 0007 KSHORT 1 42 0008 CALL 0 1 2 ; volání funkce ; function1(1, 2) 0009 GGET 0 1 ; získání reference na funkci se jménem "function1" 0010 KSHORT 1 1 0011 KSHORT 2 2 0012 CALL 0 1 3 ; volání funkce ; function1("xyzzy") 0013 GGET 0 0 ; "function1" 0014 KSTR 1 2 ; "xyzzy" 0015 CALL 0 1 2 ; volání funkce ; end 0016 RET0 0 1 ; návrat z funkce -- BYTECODE -- test20.lua:55-61 ; function callFunction2() ; function2() 0001 GGET 0 0 ; získání reference na funkci se jménem "function2" 0002 CALL 0 1 1 ; volání funkce ; function2(nil) 0003 GGET 0 0 ; získání reference na funkci se jménem "function2" 0004 KPRI 1 0 0005 CALL 0 1 2 ; volání funkce ; function2(42) 0006 GGET 0 0 ; získání reference na funkci se jménem "function2" 0007 KSHORT 1 42 0008 CALL 0 1 2 ; volání funkce ; function2(1, 2) 0009 GGET 0 1 ; získání reference na funkci se jménem "function2" 0010 KSHORT 1 1 0011 KSHORT 2 2 0012 CALL 0 1 3 ; volání funkce ; function2("xyzzy") 0013 GGET 0 0 ; získání reference na funkci se jménem "function2" 0014 KSTR 1 2 ; "xyzzy" 0015 CALL 0 1 2 ; volání funkce 0016 RET0 0 1 ; návrat z funkce -- BYTECODE -- test20.lua:68-74 ; function callFunction3() ; function3() 0001 GGET 0 0 ; získání reference na funkci se jménem "function3" 0002 CALL 0 1 1 ; volání funkce ; function3(nil) 0003 GGET 0 0 ; získání reference na funkci se jménem "function3" 0004 KPRI 1 0 0005 CALL 0 1 2 ; volání funkce ; function3(42) 0006 GGET 0 0 ; získání reference na funkci se jménem "function3" 0007 KSHORT 1 42 0008 CALL 0 1 2 ; volání funkce ; function3(1, 2) 0009 GGET 0 0 ; získání reference na funkci se jménem "function3" 0010 KSHORT 1 1 0011 KSHORT 2 2 0012 CALL 0 1 3 ; volání funkce ; function3("xyzzy") 0013 GGET 0 0 ; získání reference na funkci se jménem "function3" 0014 KSTR 1 1 ; "xyzzy" 0015 CALL 0 1 2 ; volání funkce 0016 RET0 0 1 ; návrat z funkce -- BYTECODE -- test20.lua:81-85 ; function main() ; callFunction1() 0001 GGET 0 0 ; získání reference na funkci se jménem "callFunction1" 0002 CALL 0 1 1 ; volání funkce ; callFunction2() 0003 GGET 0 1 ; získání reference na funkci se jménem "callFunction2" 0004 CALL 0 1 1 ; volání funkce ; callFunction3() 0005 GGET 0 2 ; získání reference na funkci se jménem "callFunction3" 0006 CALL 0 1 1 ; volání funkce ; end 0007 RET0 0 1 ; návrat z funkce -- BYTECODE -- test20.lua:0-94 ; main() ; vytvoření, "registrace" a následné volání funkce main() 0001 FNEW 0 0 ; vytvoření uzávěru z prototypu začínajícího na řádku test20.lua:12 0002 GSET 0 1 ; uložení uzávěru do globální tabulky _G pod jménem "function1" 0003 FNEW 0 2 ; vytvoření uzávěru z prototypu začínajícího na řádku test20.lua:20 0004 GSET 0 3 ; uložení uzávěru do globální tabulky _G pod jménem "function2" 0005 FNEW 0 4 ; vytvoření uzávěru z prototypu začínajícího na řádku test20.lua:29 0006 GSET 0 5 ; uložení uzávěru do globální tabulky _G pod jménem "function3" 0007 FNEW 0 6 ; vytvoření uzávěru z prototypu začínajícího na řádku test20.lua:42 0008 GSET 0 7 ; uložení uzávěru do globální tabulky _G pod jménem "callFunction1" 0009 FNEW 0 8 ; vytvoření uzávěru z prototypu začínajícího na řádku test20.lua:55 0010 GSET 0 9 ; uložení uzávěru do globální tabulky _G pod jménem "callFunction2" 0011 FNEW 0 10 ; vytvoření uzávěru z prototypu začínajícího na řádku test20.lua:68 0012 GSET 0 11 ; uložení uzávěru do globální tabulky _G pod jménem "callFunction3" 0013 FNEW 0 12 ; vytvoření uzávěru z prototypu začínajícího na řádku test20.lua:81 0014 GSET 0 13 ; uložení uzávěru do globální tabulky _G pod jménem "main" 0015 GGET 0 13 ; získání reference na funkci se jménem "main" 0016 CALL 0 1 1 ; zavolání funkce main() 0017 RET0 0 1 ; návrat z programu ; konec
5. Demonstrační příklad test21.lua: volání „statických metod“
Díky tomu, že funkce jsou v programovacím jazyku Lua plnohodnotným datovým typem, je možné odkazy/reference na funkce vkládat i do tabulek (což je vlastně jediný strukturovaný datový typ tohoto jazyka). Tímto způsobem je možné v Lue vytvořit objekty, přičemž funkce vložené do tabulek lze chápat jako obdobu „statických metod“. „Statické metody“ jsou běžné funkce, při jejichž deklaraci je mezi jménem tabulky a jménem funkce použita tečka, která je též použita při volání této funkce. Podívejme se na příklad test21.lua, který je přímo odvozen od dnešního druhého příkladu test20.lua až na ten rozdíl, že původní funkce function1(), function2() a function3() jsou uloženy do tabulky nazvané testClass.
5.1 Zdrojový kód příkladu test21.lua
-- -- demonstrační příklad číslo 21. -- -- Volání "statických metod" v programovacím jazyce Lua. -- testClass = {} -- -- Funkce/statická metoda bez parametrů. -- function testClass.function1() end -- -- Funkce/statická metoda s jedním parametrem. -- function testClass.function2(x) return x end -- -- Funkce/statická metoda se dvěma parametry. -- function testClass.function3(x, y) if x and y then return x+y else return nil end end -- -- Volání funkcí/statických metod. -- function callFunction1() testClass.function1() testClass.function1(nil) testClass.function1(42) testClass.function3(1, 2) testClass.function1("xyzzy") end -- -- Volání funkce function2(). -- function callFunction2() testClass.function2() testClass.function2(nil) testClass.function2(42) testClass.function3(1, 2) testClass.function2("xyzzy") end -- -- Volání funkce function3(). -- function callFunction3() testClass.function3() testClass.function3(nil) testClass.function3(42) testClass.function3(1, 2) testClass.function3("xyzzy") end -- -- Spuštění testu. -- function main() callFunction1() callFunction2() callFunction3() end main() -- -- Finito. --
5.2 Překlad příkladu test21.lua do mezijazyka LuaJITu
V sekvenci instrukcí přeloženého demonstračního příkladu test21.lua můžeme mj. vidět následující trojici instrukcí použitou pro zavolání vybrané statické metody:
; testClass.function1() 0001 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0002 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0003 CALL 0 1 1 ; volání funkce/metody
Z této sekvence je patrné, že se nejdříve získá reference na tabulku nazvanou „testClass“ z globální tabulky _G a následně se přečte reference na vybranou funkci. Pro čtení z obou tabulek se používají klíče, které jsou typu řetězec. Tato funkce je následně zavolána.
; Překlad demonstračního příkladu test21.lua ; do IR využívaného virtuálním strojem a JIT ; překladačem LuaJIT. -- BYTECODE -- test21.lua:14-15 ; function testClass.function1() ; end 0001 RET0 0 1 ; návrat z funkce -- BYTECODE -- test21.lua:22-24 ; function testClass.function2() ; return x 0001 RET1 0 2 ; návrat z funkce + předání návratové hodnoty -- BYTECODE -- test21.lua:31-37 ; function testClass.function3(x, y) ; if x and y then 0001 ISF 0 ; test, zda není první parametr funkce false či nil 0002 JMP 2 => 0008 ; + podmíněný skok 0003 ISF 1 ; test, zda není druhý parametr funkce false či nil 0004 JMP 2 => 0008 ; + podmíněný skok ; return x+y 0005 ADDVV 2 0 1 ; výpočet návratové hodnoty 0006 RET1 2 2 ; návrat z funkce + předání návratové hodnoty ; else 0007 JMP 2 => 0010 ; nepodmíněný skok ; return nil 0008 => KPRI 2 0 ; návratová hodnota 0009 RET1 2 2 ; návrat z funkce + předání návratové hodnoty 0010 => RET0 0 1 ; návrat z funkce -- BYTECODE -- test21.lua:44-50 ; function callFunction1() ; testClass.function1() 0001 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0002 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0003 CALL 0 1 1 ; volání funkce/metody ; testClass.function1(nil) 0004 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0005 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0006 KPRI 1 0 ; parametr volané funkce - hodnota nil 0007 CALL 0 1 2 ; volání funkce/metody ; testClass.function1(42) 0008 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0009 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0010 KSHORT 1 42 ; parametr volané funkce - hodnota 42 0011 CALL 0 1 2 ; volání funkce/metody ; testClass.function1(1, 2) 0012 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0013 TGETS 0 0 2 ; získání reference na funkci se jménem "function1" 0014 KSHORT 1 1 ; první parametr volané funkce - hodnota 1 0015 KSHORT 2 2 ; druhý parametr volané funkce - hodnota 2 0016 CALL 0 1 3 ; volání funkce/metody ; testClass.function1("xyzzy") 0017 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0018 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0019 KSTR 1 3 ; první parametr volané funkce - odkaz na řetězec "xyzzy" 0020 CALL 0 1 2 ; volání funkce/metody ; end 0021 RET0 0 1 ; návrat z funkce -- BYTECODE -- test21.lua:57-63 ; function callFunction2() ; testClass.function2() 0001 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0002 TGETS 0 0 1 ; získání reference na funkci se jménem "function2" 0003 CALL 0 1 1 ; volání funkce/metody ; testClass.function2(nil) 0004 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0005 TGETS 0 0 1 ; získání reference na funkci se jménem "function2" 0006 KPRI 1 0 ; parametr volané funkce - hodnota nil 0007 CALL 0 1 2 ; volání funkce/metody ; testClass.function2(42) 0008 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0009 TGETS 0 0 1 ; získání reference na funkci se jménem "function2" 0010 KSHORT 1 42 ; parametr volané funkce - hodnota 42 0011 CALL 0 1 2 ; volání funkce/metody ; testClass.function2(1, 2) 0012 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0013 TGETS 0 0 2 ; získání reference na funkci se jménem "function2" 0014 KSHORT 1 1 ; první parametr volané funkce - hodnota 1 0015 KSHORT 2 2 ; druhý parametr volané funkce - hodnota 2 0016 CALL 0 1 3 ; volání funkce/metody ; testClass.function2("xyzzy") 0017 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0018 TGETS 0 0 1 ; získání reference na funkci se jménem "function2" 0019 KSTR 1 3 ; první parametr volané funkce - odkaz na řetězec "xyzzy" 0020 CALL 0 1 2 ; volání funkce/metody ; end 0021 RET0 0 1 ; návrat z funkce -- BYTECODE -- test21.lua:70-76 ; function callFunction3() ; testClass.function3() 0001 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0002 TGETS 0 0 1 ; získání reference na funkci se jménem "function3" 0003 CALL 0 1 1 ; volání funkce/metody ; testClass.function3(nil) 0004 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0005 TGETS 0 0 1 ; získání reference na funkci se jménem "function3" 0006 KPRI 1 0 ; parametr volané funkce - hodnota nil 0007 CALL 0 1 2 ; volání funkce/metody ; testClass.function3(42) 0008 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0009 TGETS 0 0 1 ; získání reference na funkci se jménem "function3" 0010 KSHORT 1 42 ; parametr volané funkce - hodnota 42 0011 CALL 0 1 2 ; volání funkce/metody ; testClass.function3(1, 2) 0012 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0013 TGETS 0 0 1 ; získání reference na funkci se jménem "function3" 0014 KSHORT 1 1 ; první parametr volané funkce - hodnota 1 0015 KSHORT 2 2 ; druhý parametr volané funkce - hodnota 2 0016 CALL 0 1 3 ; volání funkce/metody ; testClass.function3("xyzzy") 0017 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0018 TGETS 0 0 1 ; získání reference na funkci se jménem "function3" 0019 KSTR 1 2 ; první parametr volané funkce - odkaz na řetězec "xyzzy" 0020 CALL 0 1 2 ; volání funkce/metody ; end 0021 RET0 0 1 ; návrat z funkce -- BYTECODE -- test21.lua:83-87 ; function main() ; callFunction1() 0001 GGET 0 0 ; získání reference na funkci se jménem "callFunction1" 0002 CALL 0 1 1 ; volání funkce ; callFunction2() 0003 GGET 0 1 ; získání reference na funkci se jménem "callFunction2" 0004 CALL 0 1 1 ; volání funkce ; callFunction3() 0005 GGET 0 2 ; získání reference na funkci se jménem "callFunction3" 0006 CALL 0 1 1 ; volání funkce ; end 0007 RET0 0 1 ; návrat z funkce -- BYTECODE -- test21.lua:0-99 ; main() ; vytvoření, "registrace" a následné volání funkce main(), inicializace třídy testClass 0001 TNEW 0 0 ; vytvoření nové tabulky 0002 GSET 0 0 ; uložení tabulky do _G pod jménem "testClass" 0003 GGET 0 0 ; přečtení reference na tabulku "testClass" 0004 FNEW 1 2 ; vytvoření uzávěru z prototypu začínajícího na řádku test21.lua:14 0005 TSETS 1 0 1 ; uložení uzávěru do vybrané tabulky _G pod jménem "function1" 0006 GGET 0 0 ; přečtení reference na tabulku "testClass" 0007 FNEW 1 4 ; vytvoření uzávěru z prototypu začínajícího na řádku test21.lua:22 0008 TSETS 1 0 3 ; uložení uzávěru do vybrané tabulky _G pod jménem "function2" 0009 GGET 0 0 ; přečtení reference na tabulku "testClass" 0010 FNEW 1 6 ; vytvoření uzávěru z prototypu začínajícího na řádku test21.lua:31 0011 TSETS 1 0 5 ; uložení uzávěru do vybrané tabulky _G pod jménem "function3" 0012 FNEW 0 7 ; vytvoření uzávěru z prototypu začínajícího na řádku test21.lua:44 0013 GSET 0 8 ; uložení uzávěru do globální tabulky _G pod jménem "callFunction1" 0014 FNEW 0 9 ; vytvoření uzávěru z prototypu začínajícího na řádku test21.lua:57 0015 GSET 0 10 ; uložení uzávěru do globální tabulky _G pod jménem "callFunction2" 0016 FNEW 0 11 ; vytvoření uzávěru z prototypu začínajícího na řádku test21.lua:70 0017 GSET 0 12 ; uložení uzávěru do globální tabulky _G pod jménem "callFunction3" 0018 FNEW 0 13 ; vytvoření uzávěru z prototypu začínajícího na řádku test21.lua:83 0019 GSET 0 14 ; uložení uzávěru do globální tabulky _G pod jménem "main" 0020 GGET 0 14 ; získání reference na funkci se jménem "main" 0021 CALL 0 1 1 ; zavolání funkce main() 0022 RET0 0 1 ; návrat z programu ; konec
6. Demonstrační příklad test22.lua: volání „metod“
V programovacím jazyku Lua je možné funkce vkládat do tabulek i s využitím dvojtečky namísto tečky. Tento malý syntaktický rozdíl však vede k vytvoření sémanticky odlišného kódu – takto vytvořená funkce totiž bude mít navíc jeden parametr, v němž se předá odkaz na objekt, tedy hodnota, která se v jiných programovacích jazycích označuje klíčovým slovem self nebo this. Odlišné je pak i volání takových funkcí, které lze podle jejich vlastností již nazývat „metodami“.
6.1 Zdrojový kód příkladu test22.lua
-- -- demonstrační příklad číslo 22. -- -- Volání "metod" v programovacím jazyce Lua. -- testClass = {} -- -- Funkce/metoda bez parametrů. -- function testClass:function1() end -- -- Funkce/metoda s jedním parametrem. -- function testClass:function2(x) return x end -- -- Funkce/metoda se dvěma parametry. -- function testClass:function3(x, y) if x and y then return x+y else return nil end end -- -- Volání funkce function1(). -- function callFunction1() testClass:function1() testClass:function1(nil) testClass:function1(42) testClass:function3(1, 2) testClass:function1("xyzzy") end -- -- Volání funkce function2(). -- function callFunction2() testClass:function2() testClass:function2(nil) testClass:function2(42) testClass:function3(1, 2) testClass:function2("xyzzy") end -- -- Volání funkce function3(). -- function callFunction3() testClass:function3() testClass:function3(nil) testClass:function3(42) testClass:function3(1, 2) testClass:function3("xyzzy") end -- -- Spuštění testu. -- function main() callFunction1() callFunction2() callFunction3() end main() -- -- Finito. --
6.2 Překlad příkladu test22.lua do mezijazyka LuaJITu
V předchozím příkladu se pro zavolání statické metody používal tento kód:
; testClass.function1() 0001 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0002 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0003 CALL 0 1 1 ; volání funkce/metody
Pro zavolání metody nestatické musíme zpracovat i parametr this/self:
; testClass.function1() 0001 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0002 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0003 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0004 CALL 0 1 2 ; volání funkce/metody
Tento rozdíl je hned několikrát vidět i v úplném výpisu IR:
; Překlad demonstračního příkladu test22.lua ; do IR využívaného virtuálním strojem a JIT ; překladačem LuaJIT. -- BYTECODE -- test22.lua:14-15 ; function testClass.function1() ; end 0001 RET0 0 1 ; návrat z funkce -- BYTECODE -- test22.lua:22-24 ; function testClass.function2() ; return x 0001 RET1 1 2 ; návrat z funkce + předání návratové hodnoty -- BYTECODE -- test22.lua:31-37 ; function testClass.function3(x, y) ; if x and y then 0001 ISF 1 ; test, zda není první parametr funkce false či nil 0002 JMP 3 => 0008 ; + podmíněný skok 0003 ISF 2 ; test, zda není druhý parametr funkce false či nil 0004 JMP 3 => 0008 ; + podmíněný skok ; return x+y 0005 ADDVV 3 1 2 ; výpočet návratové hodnoty 0006 RET1 3 2 ; návrat z funkce + předání návratové hodnoty ; else 0007 JMP 3 => 0010 ; nepodmíněný skok ; return nil 0008 => KPRI 3 0 ; návratová hodnota 0009 RET1 3 2 ; návrat z funkce + předání návratové hodnoty 0010 => RET0 0 1 ; návrat z funkce -- BYTECODE -- test22.lua:44-50 ; function callFunction1() ; testClass.function1() 0001 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0002 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0003 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0004 CALL 0 1 2 ; volání funkce/metody ; testClass.function1(nil) 0005 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0006 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0007 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0008 KPRI 2 0 ; parametr volané funkce - hodnota nil 0009 CALL 0 1 3 ; volání funkce/metody ; testClass.function1(42) 0010 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0011 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0012 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0013 KSHORT 2 42 ; parametr volané funkce - hodnota 42 0014 CALL 0 1 3 ; volání funkce/metody ; testClass.function1(1, 2) 0015 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0016 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0017 TGETS 0 0 2 ; získání reference na funkci se jménem "function1" 0018 KSHORT 2 1 ; první parametr volané funkce - hodnota 1 0019 KSHORT 3 2 ; druhý parametr volané funkce - hodnota 2 0020 CALL 0 1 4 ; volání funkce/metody ; testClass.function1("xyzzy") 0021 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0022 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0023 TGETS 0 0 1 ; získání reference na funkci se jménem "function1" 0024 KSTR 2 3 ; první parametr volané funkce - odkaz na řetězec "xyzzy" 0025 CALL 0 1 3 ; volání funkce/metody ; end 0026 RET0 0 1 -- BYTECODE -- test22.lua:57-63 ; function callfunction2() ; testClass.function2() 0001 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0002 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0003 TGETS 0 0 1 ; získání reference na funkci se jménem "function2" 0004 CALL 0 1 2 ; volání funkce/metody ; testClass.function2(nil) 0005 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0006 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0007 TGETS 0 0 1 ; získání reference na funkci se jménem "function2" 0008 KPRI 2 0 ; parametr volané funkce - hodnota nil 0009 CALL 0 1 3 ; volání funkce/metody ; testClass.function2(42) 0010 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0011 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0012 TGETS 0 0 1 ; získání reference na funkci se jménem "function2" 0013 KSHORT 2 42 ; parametr volané funkce - hodnota 42 0014 CALL 0 1 3 ; volání funkce/metody ; testClass.function2(1, 2) 0015 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0016 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0017 TGETS 0 0 2 ; získání reference na funkci se jménem "function2" 0018 KSHORT 2 1 ; první parametr volané funkce - hodnota 1 0019 KSHORT 3 2 ; druhý parametr volané funkce - hodnota 2 0020 CALL 0 1 4 ; volání funkce/metody ; testClass.function2("xyzzy") 0021 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0022 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0023 TGETS 0 0 1 ; získání reference na funkci se jménem "function2" 0024 KSTR 2 3 ; první parametr volané funkce - odkaz na řetězec "xyzzy" 0025 CALL 0 1 3 ; volání funkce/metody ; end 0026 RET0 0 1 -- BYTECODE -- test22.lua:70-76 ; function callfunction3() ; testClass.function3() 0001 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0002 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0003 TGETS 0 0 1 ; získání reference na funkci se jménem "function3" 0004 CALL 0 1 2 ; volání funkce/metody ; testClass.function3(nil) 0005 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0006 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0007 TGETS 0 0 1 ; získání reference na funkci se jménem "function3" 0008 KPRI 2 0 ; parametr volané funkce - hodnota nil 0009 CALL 0 1 3 ; volání funkce/metody ; testClass.function3(42) 0010 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0011 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0012 TGETS 0 0 1 ; získání reference na funkci se jménem "function3" 0013 KSHORT 2 42 ; parametr volané funkce - hodnota 42 0014 CALL 0 1 3 ; volání funkce/metody ; testClass.function3(1, 2) 0015 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0016 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0017 TGETS 0 0 1 ; získání reference na funkci se jménem "function3" 0018 KSHORT 2 1 ; první parametr volané funkce - hodnota 1 0019 KSHORT 3 2 ; druhý parametr volané funkce - hodnota 2 0020 CALL 0 1 4 ; volání funkce/metody ; testClass.function3("xyzzy") 0021 GGET 0 0 ; přečtení reference na tabulku "testClass" z globální tabulky _G 0022 MOV 1 0 ; při volání se použije i skrytý parametr this/self 0023 TGETS 0 0 1 ; získání reference na funkci se jménem "function3" 0024 KSTR 2 2 ; první parametr volané funkce - odkaz na řetězec "xyzzy" 0025 CALL 0 1 3 ; volání funkce/metody ; end 0026 RET0 0 1 -- BYTECODE -- test22.lua:83-87 ; function main() ; callFunction1() 0001 GGET 0 0 ; získání reference na funkci se jménem "callFunction1" 0002 CALL 0 1 1 ; volání funkce ; callFunction2() 0003 GGET 0 1 ; získání reference na funkci se jménem "callFunction2" 0004 CALL 0 1 1 ; volání funkce ; callFunction3() 0005 GGET 0 2 ; získání reference na funkci se jménem "callFunction3" 0006 CALL 0 1 1 ; volání funkce ; end 0007 RET0 0 1 ; návrat z funkce -- BYTECODE -- test22.lua:0-99 ; main() ; vytvoření, "registrace" a následné volání funkce main(), inicializace třídy testClass 0001 TNEW 0 0 ; vytvoření nové tabulky 0002 GSET 0 0 ; uložení tabulky do _G pod jménem "testClass" 0003 GGET 0 0 ; přečtení reference na tabulku "testClass" 0004 FNEW 1 2 ; vytvoření uzávěru z prototypu začínajícího na řádku test22.lua:14 0005 TSETS 1 0 1 ; uložení uzávěru do vybrané tabulky _G pod jménem "function1" 0006 GGET 0 0 ; přečtení reference na tabulku "testClass" 0007 FNEW 1 4 ; vytvoření uzávěru z prototypu začínajícího na řádku test22.lua:22 0008 TSETS 1 0 3 ; uložení uzávěru do vybrané tabulky _G pod jménem "function2" 0009 GGET 0 0 ; přečtení reference na tabulku "testClass" 0010 FNEW 1 6 ; vytvoření uzávěru z prototypu začínajícího na řádku test22.lua:31 0011 TSETS 1 0 5 ; uložení uzávěru do vybrané tabulky _G pod jménem "function3" 0012 FNEW 0 7 ; vytvoření uzávěru z prototypu začínajícího na řádku test22.lua:44 0013 GSET 0 8 ; uložení uzávěru do globální tabulky _G pod jménem "callFunction1" 0014 FNEW 0 9 ; vytvoření uzávěru z prototypu začínajícího na řádku test22.lua:57 0015 GSET 0 10 ; uložení uzávěru do globální tabulky _G pod jménem "callFunction2" 0016 FNEW 0 11 ; vytvoření uzávěru z prototypu začínajícího na řádku test22.lua:70 0017 GSET 0 12 ; uložení uzávěru do globální tabulky _G pod jménem "callFunction3" 0018 FNEW 0 13 ; vytvoření uzávěru z prototypu začínajícího na řádku test22.lua:83 0019 GSET 0 14 ; uložení uzávěru do globální tabulky _G pod jménem "main" 0020 GGET 0 14 ; získání reference na funkci se jménem "main" 0021 CALL 0 1 1 ; zavolání funkce main() 0022 RET0 0 1 ; návrat z programu ; konec
7. Demonstrační příklad test23.lua: způsob vracení parametrů z volaných funkcí
V pátém a současně i v dnešním posledním demonstračním příkladu nazvaném test23.lua si vysvětlíme způsob vracení parametrů z volaných funkcí. Taktéž si ukážeme, jak LuaJIT optimalizuje volání typu print(funcX()) v případě, že se z funkce funcX() vrací větší počet hodnot. Technika využívaná LuaJITem se v mnoha ohledech odlišuje od technik, které najdeme v jiných překladačích popř. v odlišných bajtkódech.
7.1 Zdrojový kód příkladu test23.lua
Nejprve si ukažme zdrojový kód pátého demonstračního příkladu, v němž můžeme vidět deklaraci čtyř funkcí nazvaných function1 až function4. Funkce vrací různý počet i typ parametrů. Tyto funkce jsou následně volány stylem print(function1()), což znamená, že hodnota či hodnoty vrácené funkcí jsou ihned předány do další funkce:
-- -- LuaJIT: demonstrační příklad číslo 23 -- -- Volání funkci v programovacím jazyce Lua -- a vraceni hodnot z funkci. -- -- -- Funkce nevracející žádnou hodnotu. -- function function1() end -- -- Funkce vracející jednu hodnotu. -- function function2() return 42 end -- -- Funkce vracející dvě hodnoty -- function function3() return 1,2 end -- -- Další funkce vracející dvě hodnoty -- function function4() return 1,nil end -- -- Spuštění testu. -- function main() print(function1()); print(function2()); print(function3()); print(function4()); end main() -- finito
7.2 Překlad příkladu test23.lua do mezijazyka LuaJITu
V IR dnešního posledního demonstračního příkladu je patrné, jak se překládá příkaz print(function1()) a samozřejmě i všechny podobné příkazy. Využívá se zde dvojice instrukcí CALL+CALLM, přičemž druhá zmíněná instrukce dokáže zpracovat libovolný počet hodnot předaných přes sloty:
0001 GGET 0 0 ; přečtení reference na funkci "print" 0002 GGET 1 1 ; přečtení reference na funkci "function1" 0003 CALL 1 0 1 ; zavolání funkce function1() 0004 CALLM 0 1 0 ; zavolání funkce print() s parametry vrácenými předchozí funkcí
Následuje výpis celého IR:
; Překlad demonstračního příkladu test23.lua ; do IR využívaného virtuálním strojem a JIT ; překladačem LuaJIT. -- BYTECODE -- test23.lua:13-14 ; function function1() ; end 0001 RET0 0 1 ; návrat z funkce -- BYTECODE -- test23.lua:21-23 ; function function2() ; return 42 0001 KSHORT 0 42 ; do slotu 0 uložit konstantu 0002 RET1 0 2 ; návrat z funkce + předání návratové hodnoty -- BYTECODE -- test23.lua:30-32 ; function function3() ; return 1,2 0001 KSHORT 0 1 ; do slotu 0 uložit konstantu 0002 KSHORT 1 2 ; do slotu 1 uložit konstantu 0003 RET 0 3 ; návrat z funkce + předání návratových hodnot -- BYTECODE -- test23.lua:39-41 ; function function4() 0001 KSHORT 0 1 ; do slotu 0 uložit konstantu 0002 KPRI 1 0 ; do slotu 1 uložit konstantu 0003 RET 0 3 ; návrat z funkce + předání návratových hodnot -- BYTECODE -- test23.lua:48-53 ; function main() 0001 GGET 0 0 ; přečtení reference na funkci "print" 0002 GGET 1 1 ; přečtení reference na funkci "function1" 0003 CALL 1 0 1 ; zavolání funkce function1() 0004 CALLM 0 1 0 ; zavolání funkce print() s parametry vrácenými předchozí funkcí 0005 GGET 0 0 ; přečtení reference na funkci "print" 0006 GGET 1 2 ; přečtení reference na funkci "function2" 0007 CALL 1 0 1 ; zavolání funkce function2() 0008 CALLM 0 1 0 ; zavolání funkce print() s parametry vrácenými předchozí funkcí 0009 GGET 0 0 ; přečtení reference na funkci "print" 0010 GGET 1 3 ; přečtení reference na funkci "function3" 0011 CALL 1 0 1 ; zavolání funkce function3() 0012 CALLM 0 1 0 ; zavolání funkce print() s parametry vrácenými předchozí funkcí 0013 GGET 0 0 ; přečtení reference na funkci "print" 0014 GGET 1 4 ; přečtení reference na funkci "function4" 0015 CALL 1 0 1 ; zavolání funkce function4() 0016 CALLM 0 1 0 ; zavolání funkce print() s parametry vrácenými předchozí funkcí 0017 RET0 0 1 ; návrat z funkce -- BYTECODE -- test23.lua:0-62 ; main() ; vytvoření a "registrace" všech funkcí 0001 FNEW 0 0 ; vytvoření uzávěru z prototypu začínajícího na řádku test23.lua:13 0002 GSET 0 1 ; "uložení uzávěru do globální tabulky _G pod jménem function1" 0003 FNEW 0 2 ; vytvoření uzávěru z prototypu začínajícího na řádku test23.lua:21 0004 GSET 0 3 ; uložení uzávěru do globální tabulky _G pod jménem "function2" 0005 FNEW 0 4 ; vytvoření uzávěru z prototypu začínajícího na řádku test23.lua:30 0006 GSET 0 5 ; uložení uzávěru do globální tabulky _G pod jménem "function3" 0007 FNEW 0 6 ; vytvoření uzávěru z prototypu začínajícího na řádku test23.lua:39 0008 GSET 0 7 ; uložení uzávěru do globální tabulky _G pod jménem "function4" 0009 FNEW 0 8 ; vytvoření uzávěru z prototypu začínajícího na řádku test23.lua:48 0010 GSET 0 9 ; uložení uzávěru do globální tabulky _G pod jménem "main" 0011 GGET 0 9 ; získání reference na funkci se jménem "main" 0012 CALL 0 1 1 ; zavolání funkce main() 0013 RET0 0 1 ; návrat z programu ; konec
8. Repositář se zdrojovými kódy všech pěti dnešních demonstračních příkladů
Všech pět dnes popsaných a taktéž „disasemblovaných“ demonstračních příkladů bylo uloženo do Git repositáře umístěného na adrese https://github.com/tisnik/luajit-examples. Odkazy na prozatím poslední verze těchto pěti příkladů naleznete v tabulce umístěné pod tímto odstavcem:
9. Odkazy na Internetu
- Wikipedia: Mezijazyk
http://cs.wikipedia.org/wiki/Mezijazyk - 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 9.1 – Coroutine Basics,
http://www.lua.org/pil/9.1.html - Programming in Lua (first edition)
http://www.lua.org/pil/contents.html - Programming in Lua: 6 – More about Functions
http://www.lua.org/pil/6.html - Lua Lanes
http://kotisivu.dnainternet.net/askok/bin/lanes/ - Programming in Lua: 6.1 – Closures
http://www.lua.org/pil/6.1.html - Programming in Lua: 9.1 – Coroutine Basics
http://www.lua.org/pil/9.1.html - Programming in Lua: Numeric for
http://www.lua.org/pil/4.3.4.html - Programming in Lua: break and return
http://www.lua.org/pil/4.4.html - Programming in Lua: Tables
http://www.lua.org/pil/2.5.html - Programming in Lua: Table Constructors
http://www.lua.org/pil/3.6.html - Programovací jazyk Lua
http://palmknihy.cz/web/kniha/programovaci-jazyk-lua-12651.htm - Lua: Tables Tutorial
http://lua-users.org/wiki/TablesTutorial - Lua: Control Structure Tutorial
http://lua-users.org/wiki/ControlStructureTutorial - Lua Types Tutorial
http://lua-users.org/wiki/LuaTypesTutorial - Goto Statement in Lua
http://lua-users.org/wiki/GotoStatement - Lua 5.2 sources
http://www.lua.org/source/5.2/ - Lua 5.2 sources – lopcodes.h
http://www.lua.org/source/5.2/lopcodes.h.html - Lua 5.2 sources – lopcodes.c
http://www.lua.org/source/5.2/lopcodes.c.html - 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