Obsah
1. Programovatelný sázecí systém LuaTeX: použití základního rozhraní mezi TeXem a skripty
2. Vytvoření tabulky v dokumentu s využitím programové smyčky
3. Zdrojový kód čtvrtého demonstračního příkladu
4. Volání skriptu uloženého v externím souboru s využitím funkce dofile
5. Zdrojový kód pátého demonstračního příkladu
6. Rozdělení deklarace tabulky na statickou a dynamickou část
7. Zdrojový kód šestého demonstračního příkladu
8. Načítání externích dat (vypočtených v jiném nástroji)
9. Volání funkce deklarované v externím souboru
10. Zdrojový kód sedmého demonstračního příkladu
11. Vytvoření nového příkazu volajícího funkci deklarovanou v jazyku Lua
12. Zdrojový kód osmého demonstračního příkladu
13. Repositář s popsanými demonstračními příklady
1. Programovatelný sázecí systém LuaTeX: použití základního rozhraní mezi TeXem a skripty
V první části seriálu o sázecím systému LuaTeX jsme se seznámili se základními vlastnostmi tohoto nástroje a taktéž s některými odlišnostmi LuaTeXu od klasického TeXu. Posléze jsme si ukázali nově přidaný příkaz \directlua, který slouží k zajištění základního rozhraní mezi přepsaným a upraveným jádrem TeXu a interpretrem skriptovacího jazyka Lua (tento příkaz mohou používat jak uživatelé plainTeXu, tak i LaTeXu). Taktéž jsme se zmínili o příkazu nazvaném luacode, jenž zjednodušuje volání skriptů. Tento příkaz se používá v LaTeXu resp. přesněji řečeno v LuaLaTeXu stylem \begin\{luacode} … \end{luacode} (takže se vlastně jedná o nově definované prostředí, i když s některými omezeními). Dnes si ukážeme některé další možnosti obou zmíněných příkazů, popíšeme si způsob využití externích skriptů a nakonec se seznámíme s tím, jak je možné vhodnou kombinací příkazů \newcommand (popř. \def pro uživatele plainTeXu) a \directlua do LuaTeXu/LuaLaTeXu přidat vlastní příkazy, samozřejmě s možností předání případných parametrů těmto příkazům.

Obrázek 1: Dokument vygenerovaný demonstračním příkladem, s nímž jsme se seznámili minule.
2. Vytvoření tabulky v dokumentu s využitím programové smyčky
Demonstrační příklady popsané minule byly ve skutečnosti velmi jednoduché až primitivní, takže při pohledu na ně možná nemuselo být příliš jasné, v čem vlastně spočívá výhoda kombinace TeXu a skriptovacího jazyka Lua. Podívejme se nyní na poněkud složitější dokument, v němž je umístěna tabulka s druhými mocninami celých čísel od 1 do 16. Celá tabulka je vygenerována skriptem vytvořeným v programovacím jazyce Lua, v němž lze využít funkci nazvanou pow pocházející ze standardní knihovny math [1].
Tabulka je do vstupního bufferu TeXu „vypsána“ funkcí tex.print() (TeX vidí a následně zpracuje už vygenerovanou tabulku), což znamená, že jsme si museli dát pozor na problematický znak zpětného lomítka (o této problematice jsme se taktéž zmiňovali minule). TeXem se zpracuje přibližně tento kód:
\documentclass{article} \usepackage{luacode} \begin{document} \section*{Mocniny dvou} \subsection*{verze 1} \begin{tabular}{|r|r|} \hline $n$ & $2^n$ \\ \hline 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 \\ \hline \end{tabular} \end{document}
V tomto demonstračním příkladu používáme upravené prostředí nazvané luacode* a nikoli pouze luacode. Je tomu tak především z toho důvodu, aby se zamezilo expanzi TeXovských maker ještě předtím, než bude kód v prostředí předán interpretru jazyka Lua.
3. Zdrojový kód čtvrtého demonstračního příkladu
Úplný zdrojový kód v celkovém pořadí již čtvrtého demonstračního příkladu vypadá následovně:
\documentclass{article} \usepackage{luacode} \begin{document} \section*{Mocniny dvou} \subsection*{verze 1} \begin{luacode*} tex.print("\\begin{tabular}{|r|r|}\\hline") tex.print("$n$&$2^n$\\\\") tex.print("\\hline") for n = 1,16 do tex.print(n .. "&" .. math.pow(2, n)) tex.print("\\\\") end tex.print("\\hline") tex.print("\\end{tabular}") \end{luacode*} \end{document}
Základ pro vytvoření obsahu tabulky tvoří čtveřice řádků:
for n = 1,16 do tex.print(n .. "&" .. math.pow(2, n)) tex.print("\\\\") end
Ve skutečnosti se však do interpretru jazyka Lua předá jediný řádek, v němž jsou příkazy odděleny mezerami (to je v jazyku Lua zcela legitimní):
tex.print("\\begin{tabular}{|r|r|}\\hline") tex.print("$n$&$2^n$\\\\") tex.print("\\hline") for n = 1,16 do tex.print(n .. "&" .. math.pow(2, n)) tex.print("\\\\") end tex.print("\\hline") tex.print("\\end{tabular}")

Obrázek 2: Dokument vygenerovaný předchozím demonstračním příkladem.
4. Volání skriptu uloženého v externím souboru s využitím funkce dofile
Ve standardní knihovně programovacího jazyka Lua existuje funkce nazvaná dofile() [2], která dokáže do aktuálně běžícího skriptu načíst obsah jiného zdrojového souboru a následně ho zpracovat (interpretovat). Tato funkce je samozřejmě, podobně jako i další funkce ze základních knihoven, dostupná i v LuaTeXu, čehož můžeme využít pro oddělení dokumentů od skriptů. Dokument bude uložen v jednom souboru (v tomto příkladu se konkrétně bude jmenovat test5.tex), zatímco skript bude uložen ve druhém souboru nazvaném test5.lua. Načtení a okamžitá interpretace skriptu s programovou smyčkou určenou pro výpočet a vygenerování tabulky druhých mocnin pak v dokumentu zajistí jediný řádek:
\directlua{dofile("test5.lua")}
První předností tohoto způsobu oddělení skriptu od dokumentu je fakt, že nedojde ke spojení všech řádků skriptu před jeho předáním interpretru (to může v některých případech způsobit problémy, například při použití jednořádkových komentářů). Dále je toto oddělení výhodné i z praktického hlediska – programátorské textové editory nebudou mít problém ani se zvýrazněním syntaxe dokumentů (TeX/LaTeX) ani s Lua skripty, zatímco při použití jediného souboru je zvýraznění syntaxe s rozpoznáním rozdílných sekcí problematické (popravdě jsem zatím neviděl editor, který by tuto kombinaci bez problémů podporoval).
5. Zdrojový kód pátého demonstračního příkladu
Obsah souboru (přesněji řečeno dokumentu) test5.tex se díky oddělení skriptu do samostatného souboru zkrátil a současně i zpřehlednil:
\documentclass{article} \usepackage{luacode} \begin{document} \section*{Mocniny dvou} \subsection*{verze 2} \directlua{dofile("test5.lua")} \end{document}
Poznámka: řádek \usepackage{luacode} je vlastně v tomto demonstračním příkladu zbytečný, neboť příkaz \directlua je přímo zabudován do LuaTeXu (v předchozím příkladu bylo nutné tento řádek použít).
Samotný skript uložený v souboru nazvaném test5.lua potom vypadá následovně:
tex.print("\\begin{tabular}{|r|r|}\\hline") tex.print("$n$&$2^n$\\\\") tex.print("\\hline") for n = 1,16 do tex.print(n .. "&" .. math.pow(2, n)) tex.print("\\\\") end tex.print("\\hline") tex.print("\\end{tabular}")
Zdvojení zpětných lomítek v tomto skriptu stále nalezneme, a to konkrétně z toho důvodu, že zpětné lomítko má v řetězcových literálech jazyka Lua speciální význam; viz též [3].

Obrázek 3: Dokument vygenerovaný předchozím demonstračním příkladem.
6. Rozdělení deklarace tabulky na statickou a dynamickou část
Při pohledu na demonstrační příklad zmíněný v předchozí kapitole jste si pravděpodobně uvědomili, že se v Lua skriptu zbytečně a k tomu navíc velmi pracně vytváří celá tabulka, včetně všech příkazů určených pro definici jejího začátku s deklarací sloupců, prvním řádkem s popisem obou sloupců apod. Celý skript je kvůli tomu dosti nepřehledný, navíc v něm snadno může dojít k chybě, která se těžko hledá (TeX totiž nahlásí chybu v kódu, který „vidí“ v průběhu tokenizace a expanze maker, tedy po zpracování interpretrem, bez přímé vazby na vstupní dokument).
Ovšem kooperace mezi TeXovským dokumentem a Lua skripty je ve skutečnosti velmi variabilní, takže je například možné dokument i k němu příslušející skript upravit takovým způsobem, že se tabulka deklaruje přímo v TeXovském dokumentu (jak je to obvyklé) a pouze ty řádky vytvářené tabulky, které se mají vypočítat, budou vytvářeny skriptem naprogramovaným v jazyku Lua. Příkaz \directlua{dofile(„jméno_skriptu.lua“)} se nám tedy posune dovnitř definice celé tabulky a samozřejmě díky tomu dojde ke značnému zjednodušení vlastního skriptu, z něhož je odstraněna většina otrocky napsaných příkazů tex.print(). Další výhoda tohoto přístupu spočívá v tom, že kód uložený ve skriptu bude možné využít na více místech (například tehdy, pokud by se data do tabulky načítala z datového souboru, což je téma, kterému se budeme věnovat o několik odstavců níže).
Poznámka: dejte si pozor na to, že tento způsob není možné použít ve chvíli, kdy se kombinuje prostředí tabular s prostředím luacode či luacode* (viz též http://tex.stackexchange.com/questions/253706/is-it-possible-to-view-the-latex-code-after-lua-expansion-is-made).
7. Zdrojový kód šestého demonstračního příkladu
V dokumentu v pořadí již šestého demonstračního příkladu vidíme deklaraci běžné tabulky s jedním řádkem (nadpisem) a voláním příkazu \directlua:
\documentclass{article} \usepackage{luacode} \begin{document} \section*{Mocniny dvou} \subsection*{verze 3} \begin{tabular}{|r|r|} \hline $n$ & $2^n$ \\ \hline \directlua{dofile("test6.lua")} \hline \end{tabular} \end{document}
Poznámka: řádek \usepackage{luacode} je v tomto demonstračním příkladu opět zbytečný a uváděn je pouze pro úplnost.
Skript se nám zjednoduší na pouhé čtyři řádky, které jsou již mnohem čitelnější, než tomu bylo v příkladu předchozím:
for n = 1,16 do tex.print(n .. "&" .. math.pow(2, n)) tex.print("\\\\") end

Obrázek 4: Dokument vygenerovaný předchozím demonstračním příkladem.
8. Načítání externích dat (vypočtených v jiném nástroji)
Samozřejmě není nutné, aby skript naprogramovaný v jazyku Lua prováděl pouze nějaké výpočty. Poměrně často se totiž ocitneme v situaci, kdy jsou již data vypočtena či naměřena s využitím jiného externího nástroje a my „pouze“potřebujeme tato data vhodným způsobem vložit do dokumentu. Jedním z poměrně flexibilních způsobů je právě použití skriptu naprogramovaného v jazyku Lua. Předpokládejme pro jednoduchost, že máme následující datový soubor, jehož obsah potřebujeme vložit do tabulky:
42 0 -100 NaN 6502 123456
Pomoci nám může následující skript, který data postupně načte a vloží do tabulky s očíslováním jednotlivých řádků (tento úkol zajišťuje počitadlo lineno):
local infile = io.open("test6.data") local lineno = 1 if not infile then print("Chyba pri otevirani souboru") else for line in infile:lines() do print(lineno, line) tex.print(lineno .. "&" .. line) tex.print("\\\\") lineno = lineno + 1 end infile:close() end
Poznámka: pokud není zapotřebí provádět alespoň základní detekci, zda soubor se vstupními daty existuje a je dostupný (čitelný), lze skript ještě více zjednodušit:
local lineno = 1 for line in io.lines("test6.data") do print(lineno, line) tex.print(lineno .. "&" .. line) tex.print("\\\\") lineno = lineno + 1 end
Tento skript lze samozřejmě vylepšit a rozšířit. Jedno z možných vylepšení spočívá v detekci speciálních znaků ve vstupním souboru. Tyto znaky, mezi než patří zejména dvojice zpětných lomítek, znak & apod., je nutné například funkcí subs nahradit za odpovídající ekvivalenty, které nebudou při vložení do tabulky způsobovat problémy. Možných rozšíření je celá řada; zmiňme například načítání souborů CSV s detekcí, zda na prvním řádku jsou umístěny popisy sloupců, filtrace dat na základě skriptu, použití externího modulu pro práci se soubory JSON a XML atd.
9. Volání funkce deklarované v externím souboru
V externích souborech s Lua skripty je možné deklarovat různé datové struktury (tj. tabulky fungující jako záznamy, pole i asociativní pole) a taktéž funkce. Pokud například budeme mít v souboru nazvaném test7.lua uloženu deklaraci funkce pro (rekurzivní) výpočet faktoriálu, bude možné takovou funkci nechat zpracovat interpretrem a následně je možné tuto funkci volat:
function factorial(n) if n <= 1 then return 1 else return n * factorial(n-1) end end
Poznámka: interpret při prvním čtení definice funkce provede základní kontrolu a překlad funkce do bajtkódu (viz též [4] a [5]).
10. Zdrojový kód sedmého demonstračního příkladu
Zkusme nyní použít funkci factorial(), jejíž definice je uložena v samostatném souboru nazvaném test7.lua. Tento soubor je nejdříve nutné načíst do interpretru, což se provede jednoduše:
\directlua{dofile("test7.lua")}
Následně se již funkce může volat, například opět při vytváření tabulky:
\begin{tabular}{|r|r|} \hline $n$ & $n!$ \\ \hline 1 & \directlua{tex.print(factorial(1))} \\ 10 & \directlua{tex.print(factorial(10))} \\ 100 & \directlua{tex.print(factorial(100))} \\ \hline \end{tabular}
Úplný zdrojový kód dokumentu vypadá takto:
\documentclass{article} \usepackage{luacode} \directlua{dofile("test7.lua")} \begin{document} \section*{Faktorial} \subsection*{verze 1} \begin{tabular}{|r|r|} \hline $n$ & $n!$ \\ \hline 1 & \directlua{tex.print(factorial(1))} \\ 10 & \directlua{tex.print(factorial(10))} \\ 100 & \directlua{tex.print(factorial(100))} \\ \hline \end{tabular} \end{document}

Obrázek 5: Dokument vygenerovaný předchozím demonstračním příkladem.
11. Vytvoření nového příkazu volajícího funkci deklarovanou v jazyku Lua
V praxi může být výhodnější nevolat funkci naprogramovanou v jazyku Lua nepřímo:
\directlua{tex.print(factorial(42))}
ale použít namísto toho uživatelský příkaz deklarovaný na úrovni plainTeXu či LaTeXu, tedy takto:
\factorial{1}
Podívejme se nyní, jak je možné tohoto stavu dosáhnout. Nejdříve opět musíme předat interpretru jazyka Lua skript, v němž je deklarována funkce, která se má volat:
\directlua{dofile("test8.lua")}
Následně můžeme s využitím příkazu \newcommand (LaTeX) či \def (TeX) vytvořit nový uživatelský příkaz, který bude interně volat naši funkci, samozřejmě s předáním všech potřebných parametrů:
\newcommand*{\factorial}[1]{% \directlua{tex.print(factorial(#1))}% }
V praxi se tento nový příkaz deklarovaný na úrovni (La)TeXu použije následujícím způsobem:
\begin{tabular}{|r|r|} \hline $n$ & $n!$ \\ \hline 1 & \factorial{1} \\ 10 & \factorial{10} \\ 100 & \factorial{100} \\ \hline \end{tabular}
což vlastně odpovídá předchozímu kódu:
\begin{tabular}{|r|r|} \hline $n$ & $n!$ \\ \hline 1 & \directlua{tex.print(factorial(1))} \\ 10 & \directlua{tex.print(factorial(10))} \\ 100 & \directlua{tex.print(factorial(100))} \\ \hline \end{tabular}
První varianta je evidentně mnohem čitelnější než varianta druhá.
12. Zdrojový kód osmého demonstračního příkladu
Nový příkaz deklarovaný na úrovni LaTeXu je použit v osmém demonstračním příkladu, jehož zdrojový kód je zobrazen pod tímto odstavcem:
\documentclass{article} \usepackage{luacode} \directlua{dofile("test8.lua")} \newcommand*{\factorial}[1]{% \directlua{tex.print(factorial(#1))}% } \begin{document} \section*{Faktorial} \subsection*{verze 2} \begin{tabular}{|r|r|} \hline $n$ & $n!$ \\ \hline 1 & \factorial{1} \\ 10 & \factorial{10} \\ 100 & \factorial{100} \\ \hline \end{tabular} \end{document}
Obsah souboru test8.lua:
function factorial(n) if n <= 1 then return 1 else return n * factorial(n-1) end end

Obrázek 6: Dokument vygenerovaný předchozím demonstračním příkladem.
13. Repositář s popsanými demonstračními příklady
Demonstrační příklady popsané v předchozím i v dnešním článku, byly uloženy do veřejného Git repositáře, z něhož si je můžete snadno stáhnout a otestovat:
14. Odkazy na Internetu
- LuaTex
http://www.luatex.org/ - LuaTex: dokumentace
http://www.luatex.org/documentation.html - LuaTex Wiki
http://wiki.luatex.org/index.php/Main_Page - LuaTeX (Wikipedia)
https://en.wikipedia.org/wiki/LuaTeX - Paper o LuaTeXu
https://www.tug.org/TUGboat/tb28–3/tb90hoekwater-luatex.pdf - TeX (Wikibooks)
https://en.wikibooks.org/wiki/TeX - LaTeX (Wikibooks)
https://en.wikibooks.org/wiki/LaTeX - The Latin Modern (LM) Family of Fonts
http://www.gust.org.pl/projects/e-foundry/latin-modern - Sázecí system TeX
https://www.phil.muni.cz/~letty/tex/ - CSTeX – česká a slovenská podpora TeXu
http://petr.olsak.net/cstex.html - Proč nerad používám LaTeX
http://petr.olsak.net/ftp/olsak/bulletin/nolatex.pdf - εχTEX
http://www.extex.org/index.html - PlainTeX (Wikipedia)
https://cs.wikipedia.org/wiki/PlainTeX - What is the difference between \def and \newcommand? (SO)
http://tex.stackexchange.com/questions/655/what-is-the-difference-between-def-and-newcommand#658 - LaTeX/Macros
https://en.wikibooks.org/wiki/LaTeX/Macros - TeX (StackExchange)
http://tex.stackexchange.com/