Lua v roli skriptovacího jazyka pro WWW stránky: projekt Fengari

Autor: Depositphotos
Seznámíme se s projektem nazvaným Fengari. Jde o implementaci virtuálního stroje a současně i interpretru jazyka Lua, kterou je možné využít i na webových stránkách. Díky tomu je možné psát skripty v jazyce Lua ve webových aplikacích.

JavaScript is an assembly language. The JavaScript + HTML generate is like a .NET assembly. The browser can execute it, but no human should really care what's there.
Erik Meijer

Co se dozvíte v článku
  1. Podpora jazyka Lua s využitím transpileru či plnohodnotného virtuálního stroje?
  2. lua.vm.js – dobrý projekt, ale mohl by být i lepší
  3. Projekt Fengari se představuje
  4. Získání minifikované varianty virtuálního stroje jazyka Lua
  5. Skript napsaný v jazyce Lua, který je přímo součástí webové stránky
  6. Skript napsaný v jazyce Lua, který je uložený v samostatném souboru
  7. Přístup k DOMu webové stránky
  8. Vlastnosti JavaScriptu, které je nutné v Lue emulovat
  9. Objekt typu Float32Array a další typovaná pole JavaScriptu
  10. Rozdíly mezi typovými systémy JavaScriptu a jazyka Lua
  11. Přečtení všech atributů a metod objektu js
  12. Globální objekty dostupné programátorům
  13. Kombinace více programovacích jazyků na jediné webové stránce
  14. 3×tisk textu a výpočet faktoriálu: verze v JavaScriptu, Pythonu a jazyce Lua
  15. Vykreslení obrazce na kreslicí plátno s využitím 3D kontextu a OpenGL ES: varianta pro JavaScript
  16. Přepis specifických částí původního skriptu do jazyka Lua
  17. Výsledek přepisu celého programu do jazyka Lua
  18. Porovnání obou variant skriptů řádek po řádku
  19. Odkazy na Internetu

Na stránkách Roota jsme se v úvodním článku Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky (mimochodem: vyšel již před jedenácti lety) seznámili s dvojicí po technologické stránce zajímavých projektů. Mezi projekty, které byly ve zmíněném článku popsány, patří projekt lua2js a lua.vm.js. Cíl obou zmíněných projektů je prakticky totožný: umožnit skriptování na straně WWW klienta (tj. na straně webového prohlížeče) s využitím programovacího jazyka Lua.

Poznámka: jedenáct let v IT – to je celá věčnost. Ovšem ve světě jazyka Lua tomu tak není; zde se za tu dobu prakticky nic nezměnilo. A ve světě webových prohlížečů sice vznikly nové API i celé WebAssembly, ale původní koncept postavený na intepretru jediného jazyka, zůstal též zachován. A roku 2015 byl mimo jiné vydán ECMAScript 6.

Zatímco se však v projektu lua2js používá technologie takzvaného „transcompileru/transpřekladače“ (zjednodušeně řečeno se jedná o překladač zdrojového kódu z jednoho programovacího jazyka do odlišného programovacího jazyka a nikoli do bajtkódu či do nativního strojového kódu), je v rámci projektu lua.vm.js použit zcela odlišný přístup: implementace celého virtuálního stroje programovacího jazyka Lua v JavaScriptu. V obou projektech se tak vlastně využívá praktická aplikace dnes již zcela vážně přijímané analogie tvrdící, že jazyk JavaScript pro webové aplikace je prakticky totéž co assembler pro aplikace nativní (slovo analogie je zdůrazněno naschvál :-).

Díky tomu, že programovací jazyk Lua je stále relativně jednoduchý, a to jak z pohledu jeho syntaxe a sémantiky, tak i z pohledu implementace interpretru, nebude větším překvapením zjištění, že kromě obou výše zmíněných projektů později vznikl další projekt, který je pravděpodobně nejpropracovanější a je ve své současné podobě vhodný i pro produkční nasazení.

Podpora jazyka Lua s využitím transpileru či plnohodnotného virtuálního stroje?

Již v úvodní kapitole bylo naznačeno, jakým způsobem můžeme do webových stránek zaintegrovat skripty naprogramované v jazyce Lua. Jednou z nabízených možností je využití transpileru (transpřekladače), který převede zdrojový kód z jazyka Lua do více či méně čitelného zdrojového kódu v JavaScriptu. Na první pohled se může jednat o dobré řešení, protože vlastní převod (transpřeklad) se provede buď jednou (při nasazování projektu) nebo po příchodu na webovou stránku (taktéž jen jednou, tentokrát ovšem na straně uživatele). Ovšem na druhou stranu to znamená, že ztratíme přímou vazbu na původní zdrojový kód, takže například chybová hlášení atd. budou odkazovat na transpilovaný kód. Tento nedostatek lze do jisté míry vyřešit použitím takzvaných source map, které (poněkud zjednodušeně řečeno) obsahují mapování mezi řádky původního zdrojového kódu a řádky napsané v JavaScriptu.

Poznámka: transpilerů existuje celá řada a s mnoha z nich jsme se již na stránkách Roota setkali. Není bez zajímavosti, že mnoho transpilerů má jako cílový jazyk právě JavaScript, což souvisí s dominancí tohoto jazyka ve webových browserech.

Alternativní cestou je implementace celého virtuálního stroje jazyka Lua přímo ve webovém prohlížeči. Tento virtuální stroj bude – naprosto stejně, jako původní virtuální stroj jazyka Lua napsaný v céčku – překládat zdrojové kódy do bajtkódu jazyka Lua a ty budou následně vykonávány. To je řešení, které je do značné míry transparentní, ovšem platíme za něj většími nároky na strojový čas – je totiž rozdíl spustit skript psaný v JavaScriptu či přeložený do WebAssembly a spustit skript ve virtuálním stroji jazyka Lua, který běží ve virtuálním stroji JavaScriptu (nebo ve WebAssembly). Nicméně virtuální stroj jazyka Lua je tak malý a interně rychlý, že to může být cena, kterou jsou někteří vývojáři ochotni zaplatit.

Poznámka: tímto směrem se vydaly i projekty PyScript, Pyodide, MicroPython i Brython. A jak již jejich název napovídá, jedná se o projekty, jejichž cílem je umožnění běhu aplikací naprogramovaných v Pythonu v prostředí webových prohlížečů, které se staly (ať již se nám to líbí nebo nelíbí) základem pro mnoho aplikací s GUI (nebo by se mělo psát spíše WUI?).

lua.vm.js – dobrý projekt, ale mohl by být i lepší

Na chvíli se ještě zastavme u projektu lua.vm.js. Jedná se o implementaci interpretru a vlastně i celého virtuálního stroje jazyka Lua spustitelnou v ekosystému JavaScriptu. Autoři lua.vm.js se však nesnažili o znovuobjevení kola a o implementaci vlastního virtuálního stroje, ale použili infrastrukturu Clang+LLVM+Emscripten pro překlad původního virtuálního stroje (ten je naprogramován v ANSI C) do JavaScriptu. Co to znamená v praxi? Běh skriptů je přibližně o polovinu pomalejší, než v případě použití nativního Lua VM (a ještě pomalejší v porovnání s LuaJITem), ovšem programátoři získávají stoprocentní kompatibilitu s původní implementací programovacího jazyka Lua.

Rychlost skriptů nemusí být kritická (záleží ovšem na konkrétní aplikaci). Důležitá je i velikost implementace celého virtuálního stroje. Ta přesahuje půl megabajtu, což je již poměrně hodně vysoké číslo:

$ ls -sh lua.vm.js
 
576K lua.vm.js
Poznámka: jedná se o minifikovanou verzi. Velikost lze pochopitelně snížit zazipováním.

A nakonec – tento projekt se již dále nevyvíjí, i když je stále dostupný (zaarchivovaný) GitHub repositář – viz též https://github.com/daurni­mator/lua.vm.js/tree/master. Pokud tedy budeme chtít psát skripty pro webové aplikace v jazyce Lua, je nutné se poohlédnout jinde. Ideovým nástupcem lua.vm.js (a nutno říci, že nástupcem plně funkčním a využitelným i na produkci) je projekt Fengari, kterému je věnován celý zbytek dnešního článku.

Poznámka: na obou projektech pracuje resp. pracoval stejný vývojář; dnes jeden ze sponzorů projektu Fengari.

Projekt Fengari se představuje

„It has a sane coercion system (I’m looking at you JS!). Nobody’s adding X new concepts to it each version (still looking at you JS), which makes it a stable and reliable language.“

Projekt Fengari (v originále φεγγάρι) má stejný cíl, jako výše zmíněný lua.vm.js, tj. umožnit běh skriptů napsaných v jazyce Lua ve webovém prohlížeči, a to bez nutnosti transpřekladu do JavaScriptu. Fengari tento problém řeší reimplementací interpretru, virtuálního stroje i základních knihoven v JavaScriptu. Díky pečlivému přepisu (a ne otrockému překladu s využitím Emscriptenu) je výsledná implementace mnohem menší, než v případě lua.vm.js, o čemž se ostatně můžeme snadno přesvědčit:

$ ls -sh fengari-web.js
 
216K fengari-web.js
Poznámka: opět se jedná o minifikovanou verzi, takže porovnání s lua.vm.js je z tohoto pohledu férové. A opět platí, že tento skript lze zmenšit jeho zazipováním.

Projekt Fengari umožňuje jak přímý zápis skriptů do webových stránek (což je ovšem vhodné spíše pro několikařádkové miniskripty), tak i specifikaci externích skriptů, které jsou do webové stránky načítány s využitím značky <script> s uvedením MIME typu application/lua. Navíc je možné na jediné stránce kombinovat skripty psané v jazyce Lua se skripty napsanými v JavaScriptu (a popřípadě i s dalšími jazyky – viz další text).

Z praktického pohledu je nejdůležitější vlastností projektu Fengari velmi jednoduchý přístup k DOMu i k API webových prohlížečů. Stručně řečeno – prakticky cokoli, co lze naprogramovat v JavaScriptu, lze realizovat i v programovacím jazyce Lua, i když některé jazykové konstrukce musely být v jazyce Lua nahrazeny speciálními funkcemi (příkladem je funkce new nahrazující operátor se stejným jménem v JavaScriptu).

Získání minifikované varianty virtuálního stroje jazyka Lua

Pro otestování vlastností nabízených projektem Fengari je zapotřebí získat jeho variantu určenou pro běh na webové stránce. Získání této varianty je snadné. Postačuje přejít na stránku s GitHub repositářem tohoto projektu, která je dostupná na adrese https://github.com/fengari-lua/fengari-web. Soubor fengari-web.js je zde již předpřipraven, a to konkrétně jako artefakt vzniklý v rámci procesu vydání (release) tohoto projektu. Viz též seznam vydání, jenž je zobrazen na adrese https://github.com/fengari-lua/fengari-web/releases.

Zde bychom měli vidět i vydání verze 0.1.4. Příslušný soubor fengari-web.js se stáhne například pomocí nástroj wget:

$ wget https://github.com/fengari-lua/fengari-web/releases/download/v0.1.4/fengari-web.js 

Ověříme si velikost získaného souboru:

$ ls -sh fengari-web.js
 
216K fengari-web.js

Velikost by měla být přesně rovna 219632 bajtům. Ještě pro jistotu zkontrolujeme otisk:

$ sha512sum fengari-web.js
 
26a4cc8d07889b760b98448fab9f639b7c3220bef57229e6c2293bb52e7ca68999422ab16ef1c43673bf2ab879f5ec9e1ccaa4c7c79fcd3412ccd745339f8fe1  fengari-web.js

Skript napsaný v jazyce Lua, který je přímo součástí webové stránky

Praktickou část dnešního článku začneme tradičně, konkrétně příkladem typu „Hello World!“. Přímo do kódu webové stránky umístíme skript naprogramovaný v jazyce Lua, který po svém spuštění vypíše do konzole webového prohlížeče zprávu. Povšimněte si, že tento skript musí mít MIME typ nastavený na application/lua:

<script type="application/lua">
    print("Hello World!")
</script>

Webová stránka navíc musí (ideálně už v rámci zpracování hlavičky) načíst i virtuální stroj jazyka Lua:

<script type="text/javascript" src="fengari-web.js"></script>

Celý první demonstrační příklad vypadá následovně:

<!doctype html>
<html>
    <head>
        <title>Hello #1</title>
        <script type="text/javascript" src="fengari-web.js"></script>
    </head>
    <body>
        <script type="application/lua">
            print("Hello World!")
        </script>
    </body>
</html>
Poznámka: funkcionalitu je možné si ověřit lokálně (VM Lua musí být v takovém případě uložena ve stejném adresáři, v jakém je i kód webové stránky) nebo na adrese https://tisnik.github.io/tem­p/hello1.htm

. Stránka je sice prázdná, ovšem v konzoli webového prohlížeče (Ctrl+Shift+K) by se měla objevit zpráva „Hello world“.

Skript napsaný v jazyce Lua, který je uložený v samostatném souboru

Ve druhém demonstračním příkladu bude skript uložen ve zvláštním souboru; bude tedy oddělen od webové stránky. Samotný skript je až triviálně jednoduchý, protože obsahuje jediný řádek:

print("Hello World!")
Poznámka: funkce print slouží v jazyce Lua k výpis zprávy do konzoli, zatímco v JavaScriptu slouží pro tisk stránky na tiskárnu! (kdo to vymyslel…).

Ve zdrojovém kódu webové stránky je uveden pouze odkaz na skript. Povšimněte si, že je nastaven jeho MIME typ na application/lua:

<!doctype html>
<html>
    <head>
        <title>Hello #2</title>
        <script type="text/javascript" src="fengari-web.js"></script>
    </head>
    <body>
        <script src="hello2.lua" type="application/lua"></script>
    </body>
</html>

Tuto stránku je sice možné načíst do webového prohlížeče i z lokálního souboru, ale moderní prohlížeče mohou omezit načtení skriptu. V takových případech je nutné spustit v adresáři se stránkou i skriptem webový server, například takto:

$ python -m http.server

Ve webovém prohlížeči přejděte na adresu http://localhost:8000/hello2.htm.

I tento demonstrační příklad je dostupný na webu, konkrétně na adrese https://tisnik.github.io/tem­p/hello2.htm.

Přístup k DOMu webové stránky

Výpis zpráv na konzoli je sice užitečný, například při ladění atd., ovšem pro tvorbu moderních webových aplikací potřebujeme manipulovat s DOMem (Document Object Model) webové stránky. To nám umožní modifikovat obsah stránky (například vykreslit nové prvky), reagovat na vzniklé události (kliknutí tlačítkem myši na ovládací prvek atd.), ale například i provádět vykreslování na kreslicí plátno (canvas) atd. V dalším demonstračním příkladu obsahuje webová stránka kromě dalších prvků i odstavec (div) s identifikátorem output. Tento odstavec původně obsahuje text „xyzzy“:

<!doctype html>
<html>
    <head>
        <title>Hello #3</title>
        <script type="text/javascript" src="fengari-web.js"></script>
    </head>
    <body>
        <div id="output">xyzzy</div>
        <script src="hello3.lua" type="application/lua" async></script>
    </body>
</html>

Skript napsaný v jazyce Lua získá referenci na odstavec a následně změní jeho obsah na text „Hello, world!“. Povšimněte si, že je nutné naimportovat balíček js, který slouží jako vrstva mezi jazykem Lua a objekty tvořícími DOM. Dále stojí za povšimnutí syntaxe volání metody getElementById objektu document. V jazyce Lua se namísto tečky zapisuje dvojtečka:

print("Hello World!")
 
local js = require("js")
 
local document = js.global.document
 
local div = document:getElementById("output")
div.innerText = "Hello, world!"

Tento demonstrační příklad si můžete otestovat po přechodu na adresu https://tisnik.github.io/tem­p/hello3.htm.

Poznámka: ve skutečnosti je require volání funkce s jediným parametrem typu řetězec a proto je možné vynechat kulaté závorky (ovšem já jsem na ně z nějakého důvodu zvyklý, tak je v kódu příkladů ponechám).

Vlastnosti JavaScriptu, které je nutné v Lue emulovat

JavaScript se od programovacího jazyka Lua v několika ohledech odlišuje. Má poněkud odlišný typový systém, specifické „speciální“ hodnoty, ale i jazykové konstrukce, které nemají v programovacím jazyce Lua přesnou alternativu. Z tohoto důvodu je nutné, aby běhový systém programovacího jazyka Lua tyto chybějící konstrukce, hodnoty atd. vhodným způsobem doplnit. Nejdříve je nutné naimportovat knihovnu js, což již známe:

js = require("js")

nebo jen:

js = require "js"

Od této chvíle budou dostupné tyto hodnoty nebo funkce:

Hodnota/funkce Stručný popis
null obdoba hodnoty null v JavaScriptu
global globální kontext, odpovídá referenci document
new() funkce, která nahrazuje konstruktor new
of() vrací iterátor, což umožňuje simulovat smyčku for..of
instanceof() funkce, která nahrazuje operátor instanceof
typeof() funkce, která nahrazuje operátor typeof

Objekt typu Float32Array a další typovaná pole JavaScriptu

V JavaScriptu se poměrně často setkáme s využitím typovaných polí (což je možná poněkud paradoxní s ohledem na typový systém tohoto jazyka). Jedná se o pole, jejichž prvky mají shodný datový typ a prvky jsou uloženy v kompaktním formátu kompatibilním s datovými typy zpracovávanými samotným mikroprocesorem. Typovaná pole mají následující jména:

Int8Array
Uint8Array
Uint8ClampedArray
Int16Array
Uint16Array
Int32Array
Uint32Array
Float16Array
Float32Array
Float64Array
BigInt64Array
BigUint64Array

Mohlo by se zdát, že konstrukce takového pole v jazyce Lua bude přímočará – pouze použijeme jméno datového typu jako jméno funkce, které předáme běžnou tabulku jazyka Lua. Snadno ovšem zjistíme, že tak jednoduché to není:

<!doctype html>
<html>
    <head>
        <title>Array #1</title>
        <script type="text/javascript" src="fengari-web.js"></script>
    </head>
    <body>
        <script type="application/lua">
            local Float32Array = js.global.Float32Array
            local array = Float32Array({1, 2, 3, 4})
            print(array)
        </script>
    </body>
</html>

Po načtení webové stránky do prohlížeče se do konzole prohlížeče vypíše chybové hlášení:

uncaught exception TypeError: calling a builtin typed array constructor without new is forbidden

Toto hlášení mj. říká, že je nutné použít jazykovou konstrukci new. Ta sice v Lue neexistuje, ovšem namísto konstrukce new můžeme zavolat funkci js.new, které se v prvním parametru předá třída, jejíž instance se má vytvořit, a ve druhém či dalších parametrech se předávají parametry původního konstruktoru.

<!doctype html>
<html>
    <head>
        <title>Array #2</title>
        <script type="text/javascript" src="fengari-web.js"></script>
    </head>
    <body>
        <script type="application/lua">
            local Float32Array = js.global.Float32Array
            local array = js.new(Float32Array, {1, 2, 3, 4})
            print(array)
        </script>
    </body>
</html>

Nyní sice skript nezhavaruje, ovšem pole, které získáme, bude obsahovat hodnoty:

NaN,NaN,NaN,NaN

Je tomu tak z toho důvodu, že konstruktoru nelze přímo předat tabulku z jazyka Lua. Ovšem dosáhli jsme alespoň toho, že je pole inicializováno na kapacitu čtyř prvků. To nám umožní explicitní nastavení jednotlivých prvků. Tím, že se zde střetává jazyk, který indexuje prvky od nuly, s jazykem, který prvky indexuje od jedničky, je inicializace prvků pole realizována počítanou programovou smyčkou, která přiřazuje hodnoty do JavaScriptového pole (indexy 0..n-1) z tabulky jazyka Lua (indexy 1..n):

<!doctype html>
<html>
    <head>
        <title>Array #2</title>
        <script type="text/javascript" src="fengari-web.js"></script>
    </head>
    <body>
        <script type="application/lua">
            local Float32Array = js.global.Float32Array
            local data = {1, 2, 3, 4}
            local array = js.new(Float32Array, data)
            for i = 1, #data do
                array[i - 1] = data[i]
            end
            print(array)
        </script>
    </body>
</html>

Výsledek:

1,2,3,4

Jen pro úplnost lze příklad zjednodušit, a to tak, že se konstruktoru Float32Array předá jen počet prvků pole:

<!doctype html>
<html>
    <head>
        <title>Array #2</title>
        <script type="text/javascript" src="fengari-web.js"></script>
    </head>
    <body>
        <script type="application/lua">
            local Float32Array = js.global.Float32Array
            local data = {1, 2, 3, 4}
            local array = js.new(Float32Array, #data)
            for i = 1, #data do
                array[i - 1] = data[i]
            end
            print(array)
        </script>
    </body>
</html>

Rozdíly mezi typovými systémy JavaScriptu a jazyka Lua

Kromě syntaktických rozdílů mezi JavaScriptem a jazykem Lua se tyto programovací jazyky odlišují především typovým systémem, který je stejný či podobný jen zdánlivě. Oba jazyky pracují s numerickými hodnotami, které mají (v případě Fengari) naprosto stejný formát – jedná se numerické hodnoty s plovoucí řádovou čárkou typu double (a tedy i speciálními hodnotami kladného nekonečna, záporného nekonečna a NaN). Ovšem zde veškerá zdánlivá podobnost končí. V jazyku Lua se používá jediný strukturovaný datový typ, který se nazývá tabulka (table), zatímco v JavaScriptu se rozlišují objekty, pole, mapy a množiny:

Datová struktura JavaScript Lua
objekt object tabulka
pole array tabulka
slovník Map tabulka
množina Set ano, tabulka
Poznámka: ve skutečnosti je práce s objekty v jazyce Lua složitější, protože se zde setkáme s takzvanými metatabulkami a metametodami. Složitější i v JavaScriptu s prototypy popř. s pomocnou konstrukcí class.

Odlišuje se i sémantika porovnávání hodnot. V JavaScriptu se rozlišuje mezi operátory == a ===, zatímco pravidla pro porovnávání jsou v jazyce Lua jednodušší. A zapomenout nesmíme ani na způsob reprezentace neexistujících hodnot (získávaných z pole, mapy resp. tabulky). V JavaScriptu se pracuje s hodnotou undefined (což je odlišné od null – jedná se o jiný typ, i když použití operátoru == naznačuje opak), zatímco v jazyce Lua si vystačíme s hodnotou nil.

Přečtení všech atributů a metod objektu js

Pro zajímavost se podívejme na jednoduchý skript sloužící pro výpis všech atributů a metod objektu js, který je z virtuálního stroje jazyka Lua dostupný. Samotný skript je snadno pochopitelný – můžeme zde vidět ruční konstrukci HTML tabulky obsahující název příslušného atributu/metody a hodnotu. Pro jednoduchost je výpis řetězce do HTML stránky realizován v samostatné funkci nazvané write:

<html>
    <head>
        <title>js properties</title>
        <script type="text/javascript" src="fengari-web.js"></script>
    </head>
    <body>
        <script type="text/lua">
        function write(what)
            js.global.document:write(what)
        end
 
        write("<table border>")
        for key, value in pairs(js) do
            write("<tr><td>")
            write(key)
            write("</td><td>")
            write(value)
            write("</td></tr>")
        end
        write("</table>")
        </script>
    </body>
</html>
Poznámka: tuto stránku si můžete zobrazit na adrese https://tisnik.github.io/tem­p/objects1.htm.

Globální objekty dostupné programátorům

Předchozí demonstrační příklad je možné rozšířit takovým způsobem, aby skript po svém spuštění vypsal všechny konstanty, atributy i metody a funkce dostupné přes objekt js.global. Pro ukázku možností základní knihovny jazyka Lua jsou názvy symbolů abecedně setříděny a navíc jsou vypsány pouze ty hodnoty, které jsou typu boolean, string nebo number (typ se samozřejmě vyhodnocuje až v době běhu skriptu). Získání tabulky všech symbolů a na ně navázaných hodnot zajišťuje funkce getKeys(), výpis je proveden v hlavní části aplikace v programové smyčce typu for-each:

<html>
    <head>
        <title>js properties</title>
        <script type="text/javascript" src="fengari-web.js"></script>
    </head>
    <body>
        <script type="text/lua">
        function write(what)
            js.global.document:write(what)
        end
 
        function getKeys(tbl)
            local keys = {}
            for key, _ in pairs(tbl) do
                table.insert(keys, key)
            end
            table.sort(keys)
            return keys
        end
 
        write("<table border>")
        local keys = getKeys(js.global)
        for _, key in ipairs(keys) do
           write("<tr><td>")
           write(key)
           write("</td><td>")
           local value = js.global[key]
           local valueType = type(value)
           write(valueType)
           write("</td><td>")
           if valueType == "boolean" or valueType == "string" or valueType == "number" then
               write(value)
           end
           write("</td></tr>")
        end
        write("</table>")
        </script>
    </body>
</html>
Poznámka: tuto stránku si můžete zobrazit na adrese https://tisnik.github.io/tem­p/objects2.htm.

Kombinace více programovacích jazyků na jediné webové stránce

V předchozích kapitolách jsme se primárně zabývali ukázkami spouštění skriptů naprogramovaných v jazyce Lua. Ovšem zajímavé bude zjistit jak (a zda vůbec) je možné na jediné webové stránce zkombinovat skripty vytvořené v různých programovacích jazycích. Teoreticky to možné je, protože značka <script> podporuje atribut type, do něhož lze zapsat MIME typ rozpoznávaný jednotlivými virtuálními stroji. Nyní nám zbývá si tuto teorii ověřit. Kromě jazyka Lua, jehož virtuální stroj je realizován projektem Fengari, použijeme JavaScript (nativní podpora přímo v prohlížeči) a taktéž programovací jazyk Python (zde lze využít více realizací virtuálního stroje; pro jednoduchost použijeme Brython).

Poznámka: projektem Brython jsme se zabývali v článku Brython aneb použití jazyka Python ve skriptech přímo v prohlížeči.

3×tisk textu a výpočet faktoriálu: verze v JavaScriptu, Pythonu a jazyce Lua

Pokusme se na jediné webové stránce spustit šest skriptů vytvořených ve třech programovacích jazycích, konkrétně v JavaScriptu, v Pythonu a taktéž v jazyce Lua. První tři skripty budou zapsány přímo v HTML kódu, zatímco další tři skripty budou načítány z webového serveru (opět nebude funkční načítání přímo ze souborů). Pro JavaScript pochopitelně není nutné dodávat žádnou další podporu. Pro Python budeme načítat VM Brythonu a pro Luu pak Fengari.

Zdrojový kód této webové stránky vypadá následovně:

<!doctype html>
<html>
    <head>
        <title>Rosetta</title>
        <script type="text/javascript" src="fengari-web.js"></script>
        <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.15.0/brython.min.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            console.log("JavaScript: Hello World!");
        </script>
 
        <script type="application/lua">
            print("Lua: Hello World!")
        </script>
 
        <script type="text/python">
            print("Python: Hello World!")
        </script>
 
        <script src="factorial.js"  type="text/javascript"></script>
        <script src="factorial.lua" type="application/lua"></script>
        <script src="factorial.py"  type="text/python"></script>
    </body>
</html>

Ještě nám zbývá doplnit trojici skriptů factorial.js, factorial.lua a factorial.py. Začněme variantu faktoriálu realizovanou v JavaScriptu:

function factorial(n) {
    if (n < 0) return undefined;
    let result = 1;
    for (let i = 2; i <= n; i++) {
        result *= i;
    }
    return result;
}
 
console.log("10!=", factorial(10));

Sémanticky shodný skript, ovšem přepsaný do jazyka Lua:

function factorial(n)
    if n < 0 then
        return nil
    end
 
    local result = 1
    for i = 2, n do
        result = result * i
    end
 
    return result
end
 
print("10!=", factorial(10))

A konečně třetí sémanticky shodný skript, ovšem vytvořený v Pythonu:

def factorial(n):
    if n < 0:
        return None
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result
 
print("10!=", factorial(10))
Poznámka: tuto stránku i funkci všech skriptů si můžete ověřit na adrese https://tisnik.github.io/tem­p/rosetta.htm.

Vykreslení obrazce na kreslicí plátno s využitím 3D kontextu a OpenGL ES: varianta pro JavaScript

V závěrečné části dnešního článku si ukážeme, jakým způsobem je možné přepsat delší skript z JavaScriptu do jazyka Lua. Tento skript po spuštění na webové stránce vykreslí trojúhelník přes OpenGL ES. V původním zdrojovém kódu (JavaScript) se používají víceřádkové řetězce, volání funkcí a metod, pracuje se zde s poli typu Float32Array atd. Původní JavaScriptová varianta je dostupná na adrese https://tisnik.github.io/tem­p/triangle1.html. Skript, který provede vykreslení trojúhelníka, vypadá následovně:

const vertexShaderSource = `
    attribute vec2 position;
 
    void main() {
        gl_Position = vec4(position, 0, 1);
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
 
    void main() {
        gl_FragColor = vec4(0.7, 0.7, 0.0, 1);
    }
`
 
function print(message) {
    console.log(message);
}
 
// reference na objekt představující kreslicí plátno umístěné na webové stránce
const canvas = document.getElementById("web_gl_canvas");
if (!canvas) {
    throw new Error("Canvas with id 'web_gl_canvas' not found");
}
print("Canvas object retrieved");
 
// vytvoření a získání kontextu WebGL (3D grafika)
const gl = canvas.getContext("webgl");
if (!gl) {
    throw new Error("WebGL is not supported or context creation failed");
}
print("WebGL context created");
 
// vytvoření vertex shaderu
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
if (!vertexShader) {
    throw new Error("Failed to create vertex shader.");
}
print("Vertex shader created");
 
// překlad vertex shaderu
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(vertexShader);
    gl.deleteShader(vertexShader);
    throw new Error(`Vertex shader compile failed: ${log}`);
}
print("Vertex shader compiled");
 
// vytvoření fragment shaderu
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
if (!fragmentShader) {
    throw new Error("Failed to create fragment shader.");
}
print("Fragment shader created");
 
// překlad fragment shaderu
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(fragmentShader);
    gl.deleteShader(fragmentShader);
    throw new Error(`fragment shader compile failed: ${log}`);
}
print("Fragment shader compiled");
 
// registrace obou shaderů
const program = gl.createProgram();
if (!program) {
    throw new Error("Failed to create program.");
}
print("Program created");
 
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
print("Shaders attached to the program");
 
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    const log = gl.getProgramInfoLog(program);
    gl.deleteProgram(program);
    throw new Error(`Program link failed: ${log}`);
}
print("Program linked");
 
// souřadnice vrcholů trojúhelníku
const vertexes = [
   // x   y  
   -0.9, -0.8,
    0.9, -0.8,
    0.0,  0.7,
];
 
// buffer, do kterého se překopírují souřadnice bodu
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
    throw new Error("Failed to create buffer.");
}
print("Vertex buffer created");
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
 
const data = new Float32Array(vertexes);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
print("Vertex buffer filled-in by vertexes");
 
// navázání na parametr předávaný do vertex shaderu
const p = gl.getAttribLocation(program, "position");
if (p === -1) {
    throw new Error("Attribute 'position' not found in shader program");
}
print("Attribute 'position' found in shader program")
 
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(p,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       2*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
// transformace souřadnic: z normalizovaných souřadnic na souřadnice na plátnu
gl.viewport(0, 0, canvas.width, canvas.height);
 
// nastavení barvy pro vymazání barvového bufferu
gl.clearColor(0, 0, 0, 1);
 
// vymazání barvového bufferu
gl.clear(gl.COLOR_BUFFER_BIT);
 
// spuštění programu
gl.useProgram(program);
 
// vykreslení trojúhelníku
gl.drawArrays(gl.TRIANGLES, 0, 3);

Přepis specifických částí původního skriptu do jazyka Lua

Podívejme se, jakým způsobem se musí převést specifické části původního skriptu do jazyka Lua.

Začneme víceřádkovými řetězci (v nich jsou uloženy zdrojové kódy shaderů). Víceřádkové řetězce se v jazyce Lua zapisují následovně:

local vertexShaderSource = [[
    attribute vec2 position;
 
    void main() {
        gl_Position = vec4(position, 0, 1);
    }
]]

Dále musíme získat referenci na objekt document představující webovou stránku a taktéž referenci na třídu Float32Array:

local js = require("js")
 
-- reference na objekt představující kreslicí plátno umístěné na webové stránce
local document = js.global.document
local Float32Array = js.global.Float32Array

Při volání metod objektu document nesmíme zapomenout na dvojtečku namísto tečky. Taktéž si povšimněte, jak se zapisují testy na hodnotu false nebo nil:

local canvas = document:getElementById("web_gl_canvas")
if not canvas then
    error("Canvas with id 'web_gl_canvas' not found")
end
 
-- vytvoření a získání kontextu WebGL (3D grafika)
local gl = canvas:getContext("webgl")
if not gl then
    error("WebGL is not supported or context creation failed")
end

Souřadnice vrcholů vykreslovaného trojúhelníku budou uloženy v běžné tabulce:

-- souřadnice vrcholů trojúhelníku
local vertexes = {
   -- x   y
   -0.9, -0.8,
    0.9, -0.8,
    0.0,  0.7,
}

Převod do JavaScriptového pole už známe:

local data = js.new(Float32Array, vertexes)
for i = 1, #vertexes do
    data[i - 1] = vertexes[i]
end

Dále už běžným způsobem vytvoříme buffer:

-- buffer, do kterého se překopírují souřadnice bodu
local vertexBuffer = gl:createBuffer()
if not vertexBuffer then
    error("Failed to create buffer.")
end
log("Vertex buffer created")
 
gl:bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
 

Poslední důležitou operací je přesun dat z pole data typu Float32Array do bufferu:

gl:bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
Poznámka: ostatní operace již nejsou ničím speciální – jedná se přepis stylem 1:1.

Výsledek přepisu celého programu do jazyka Lua

Podívejme se nyní na to, jak vypadá celý skript pro vykreslení objektu na kreslicí plátno, po přepisu z jazyka JavaScript do jazyka Lua:

AI agenti

local vertexShaderSource = [[
    attribute vec2 position;
 
    void main() {
        gl_Position = vec4(position, 0, 1);
    }
]]
 
local fragmentShaderSource = [[
    precision mediump float;
 
    void main() {
        gl_FragColor = vec4(0.7, 0.7, 0.0, 1);
    }
]]
 
local function log(message)
    print(message)
end
 
local js = require("js")
 
local document = js.global.document
local Float32Array = js.global.Float32Array
 
local function log(msg)
    js.global.console:log(msg)
end
 
-- reference na objekt představující kreslicí plátno umístěné na webové stránce
local canvas = document:getElementById("web_gl_canvas")
if not canvas then
    error("Canvas with id 'web_gl_canvas' not found")
end
log("Canvas object retrieved")
 
-- vytvoření a získání kontextu WebGL (3D grafika)
local gl = canvas:getContext("webgl")
if not gl then
    error("WebGL is not supported or context creation failed")
end
log("WebGL context created")
 
-- vytvoření fragment shaderu
local vertexShader = gl:createShader(gl.VERTEX_SHADER)
if not vertexShader then
    error("Failed to create vertex shader.")
end
log("Vertex shader created")
 
-- překlad vertex shaderu
gl:shaderSource(vertexShader, vertexShaderSource)
gl:compileShader(vertexShader)
if not gl:getShaderParameter(vertexShader, gl.COMPILE_STATUS) then
    local logText = gl:getShaderInfoLog(vertexShader)
    gl:deleteShader(vertexShader)
    error("Vertex shader compile failed: " .. logText)
end
log("Vertex shader compiled")
 
-- vytvoření fragment shaderu
local fragmentShader = gl:createShader(gl.FRAGMENT_SHADER)
if not fragmentShader then
    error("Failed to create fragment shader.")
end
log("Fragment shader created")
 
-- překlad fragment shaderu
gl:shaderSource(fragmentShader, fragmentShaderSource)
gl:compileShader(fragmentShader)
if not gl:getShaderParameter(fragmentShader, gl.COMPILE_STATUS) then
    local logText = gl:getShaderInfoLog(fragmentShader)
    gl:deleteShader(fragmentShader)
    error("Fragment shader compile failed: " .. logText)
end
log("Fragment shader compiled")
 
-- registrace obou shaderů
local program = gl:createProgram()
if not program then
    error("Failed to create program.")
end
log("Program created")
 
gl:attachShader(program, vertexShader)
gl:attachShader(program, fragmentShader)
log("Shaders attached to the program")
 
gl:linkProgram(program)
if not gl:getProgramParameter(program, gl.LINK_STATUS) then
    local logText = gl:getProgramInfoLog(program)
    gl:deleteProgram(program)
    error("Program link failed: " .. logText)
end
log("Program linked")
 
-- souřadnice vrcholů trojúhelníku
local vertexes = {
   -- x   y
   -0.9, -0.8,
    0.9, -0.8,
    0.0,  0.7,
}
 
-- buffer, do kterého se překopírují souřadnice bodu
local vertexBuffer = gl:createBuffer()
if not vertexBuffer then
    error("Failed to create buffer.")
end
log("Vertex buffer created")
 
gl:bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
 
local data = js.new(Float32Array, vertexes)
for i = 1, #vertexes do
    data[i - 1] = vertexes[i]
end
 
gl:bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
log("Vertex buffer filled-in by vertexes")
 
-- navázání na parametr předávaný do vertex shaderu
local p = gl:getAttribLocation(program, "position")
if p == -1 then
    error("Attribute 'position' not found in shader program")
end
log("Attribute 'position' found in shader program")
 
-- seznam předávaný do vertex shaderu
gl:vertexAttribPointer(
    p,
    2,
    gl.FLOAT,
    false,
    2 * 4,
    0
)
gl:enableVertexAttribArray(p)
 
-- transformace souřadnic: z normalizovaných souřadnic na souřadnice na plátnu
gl:viewport(0, 0, canvas.width, canvas.height)
 
-- nastavení barvy pro vymazání barvového bufferu
gl:clearColor(0, 0, 0, 1)
 
-- vymazání barvového bufferu
gl:clear(gl.COLOR_BUFFER_BIT)
 
-- spuštění programu
gl:useProgram(program)
 
-- vykreslení trojúhelníku
gl:drawArrays(gl.TRIANGLES, 0, 3)

Výsledný program je plně funkční, o čemž se můžete přesvědčit na adrese https://tisnik.github.io/tem­p/triangle.htm.

Porovnání obou variant skriptů řádek po řádku

Zajímavé může být i porovnání obou variant skriptů (JavaScript a Lua) řádek po řádku. Skripty nejsou stejně dlouhé, protože varianta psaná v jazyce Lua vyžaduje určité přípravné práce a taktéž je složitější naplnění bufferu s hodnotami vrcholů, ovšem všechny ostatní řádky mají prakticky totožný význam v obou jazycích:

const vertexShaderSource = `                                               local vertexShaderSource = [[
    attribute vec2 position;                                                   attribute vec2 position;
    void main() {                                                              void main() {
        gl_Position = vec4(position, 0, 1);                                        gl_Position = vec4(position, 0, 1);
    }                                                                          }
`                                                                          ]]
 
const fragmentShaderSource = `                                             local fragmentShaderSource = [[
    precision mediump float;                                                   precision mediump float;
    void main() {                                                              void main() {
        gl_FragColor = vec4(0.7, 0.7, 0.0, 1);                                     gl_FragColor = vec4(0.7, 0.7, 0.0, 1);
    }                                                                          }
`                                                                          ]]
 
function print(message) {                                                  local function log(message)
    console.log(message);                                                      print(message)
}                                                                          end
                                                                           local js = require("js")
                                                                           local document = js.global.document
                                                                           local Float32Array = js.global.Float32Array
                                                                           local function log(msg)
                                                                               js.global.console:log(msg)
                                                                           end
 
// reference na objekt představující kreslicí plátno umístěné              -- reference na objekt představující kreslicí plátno umístěné
// na webové stránce                                                       -- na webové stránce
const canvas = document.getElementById("web_gl_canvas");                   local canvas = document:getElementById("web_gl_canvas")
if (!canvas) {                                                             if not canvas then
    throw new Error("Canvas with id 'web_gl_canvas' not found");               error("Canvas with id 'web_gl_canvas' not found")
}                                                                          end
print("Canvas object retrieved");                                          log("Canvas object retrieved")
 
// vytvoření a získání kontextu WebGL (3D grafika)                         -- vytvoření a získání kontextu WebGL (3D grafika)
const gl = canvas.getContext("webgl");                                     local gl = canvas:getContext("webgl")
if (!gl) {                                                                 if not gl then
    throw new Error("WebGL is not supported or context creation failed");      error("WebGL is not supported or context creation failed")
}                                                                          end
print("WebGL context created");                                            log("WebGL context created")
 
// vytvoření vertex shaderu                                                -- vytvoření fragment shaderu
const vertexShader = gl.createShader(gl.VERTEX_SHADER);                    local vertexShader = gl:createShader(gl.VERTEX_SHADER)
if (!vertexShader) {                                                       if not vertexShader then
    throw new Error("Failed to create vertex shader.");                        error("Failed to create vertex shader.")
}                                                                          end
print("Vertex shader created");                                            log("Vertex shader created")
 
// překlad vertex shaderu                                                  -- překlad vertex shaderu
gl.shaderSource(vertexShader, vertexShaderSource);                         gl:shaderSource(vertexShader, vertexShaderSource)
gl.compileShader(vertexShader);                                            gl:compileShader(vertexShader)
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {             if not gl:getShaderParameter(vertexShader, gl.COMPILE_STATUS) then
    const log = gl.getShaderInfoLog(vertexShader);                             local logText = gl:getShaderInfoLog(vertexShader)
    gl.deleteShader(vertexShader);                                             gl:deleteShader(vertexShader)
    throw new Error(`Vertex shader compile failed: ${log}`);                   error("Vertex shader compile failed: " .. logText)
}                                                                          end
print("Vertex shader compiled");                                           log("Vertex shader compiled")
 
// vytvoření fragment shaderu                                              -- vytvoření fragment shaderu
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);                local fragmentShader = gl:createShader(gl.FRAGMENT_SHADER)
if (!fragmentShader) {                                                     if not fragmentShader then
    throw new Error("Failed to create fragment shader.");                      error("Failed to create fragment shader.")
}                                                                          end
print("Fragment shader created");                                          log("Fragment shader created")
 
// překlad fragment shaderu                                                -- překlad fragment shaderu
gl.shaderSource(fragmentShader, fragmentShaderSource);                     gl:shaderSource(fragmentShader, fragmentShaderSource)
gl.compileShader(fragmentShader);                                          gl:compileShader(fragmentShader)
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {           if not gl:getShaderParameter(fragmentShader, gl.COMPILE_STATUS) then
    const log = gl.getShaderInfoLog(fragmentShader);                           local logText = gl:getShaderInfoLog(fragmentShader)
    gl.deleteShader(fragmentShader);                                           gl:deleteShader(fragmentShader)
    throw new Error(`fragment shader compile failed: ${log}`);                 error("Fragment shader compile failed: " .. logText)
}                                                                          end
print("Fragment shader compiled");                                         log("Fragment shader compiled")
 
// registrace obou shaderů                                                 -- registrace obou shaderů
const program = gl.createProgram();                                        local program = gl:createProgram()
if (!program) {                                                            if not program then
    throw new Error("Failed to create program.");                              error("Failed to create program.")
}                                                                          end
print("Program created");                                                  log("Program created")
gl.attachShader(program, vertexShader);                                    gl:attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader);                                  gl:attachShader(program, fragmentShader)
print("Shaders attached to the program");                                  log("Shaders attached to the program")
 
gl.linkProgram(program);                                                   gl:linkProgram(program)
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {                    if not gl:getProgramParameter(program, gl.LINK_STATUS) then
    const log = gl.getProgramInfoLog(program);                                 local logText = gl:getProgramInfoLog(program)
    gl.deleteProgram(program);                                                 gl:deleteProgram(program)
    throw new Error(`Program link failed: ${log}`);                            error("Program link failed: " .. logText)
}                                                                          end
print("Program linked");                                                   log("Program linked")
 
// souřadnice vrcholů trojúhelníku                                         -- souřadnice vrcholů trojúhelníku
const vertexes = [                                                         local vertexes = {
   // x   y                                                                   -- x   y
   -0.9, -0.8,                                                                -0.9, -0.8,
    0.9, -0.8,                                                                 0.9, -0.8,
    0.0,  0.7,                                                                 0.0,  0.7,
];                                                                         }
 
// buffer, do kterého se překopírují souřadnice bodu                       -- buffer, do kterého se překopírují souřadnice bodu
const vertexBuffer = gl.createBuffer();                                    local vertexBuffer = gl:createBuffer()
if (!vertexBuffer) {                                                       if not vertexBuffer then
    throw new Error("Failed to create buffer.");                               error("Failed to create buffer.")
}                                                                          end
print("Vertex buffer created");                                            log("Vertex buffer created")
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);                              gl:bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
 
const data = new Float32Array(vertexes);                                   local data = js.new(Float32Array, vertexes)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);                      for i = 1, #vertexes do
print("Vertex buffer filled-in by vertexes");                                  data[i - 1] = vertexes[i]
                                                                           end
                                                                           gl:bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
                                                                           log("Vertex buffer filled-in by vertexes")
 
// navázání na parametr předávaný do vertex shaderu                        -- navázání na parametr předávaný do vertex shaderu
const p = gl.getAttribLocation(program, "position");                       local p = gl:getAttribLocation(program, "position")
if (p === -1) {                                                            if p == -1 then
    throw new Error("Attribute 'position' not found in shader program");       error("Attribute 'position' not found in shader program")
}                                                                          end
print("Attribute 'position' found in shader program")                      log("Attribute 'position' found in shader program")
 
// seznam předávaný do vertex shaderu                                      -- seznam předávaný do vertex shaderu
gl.vertexAttribPointer(p,                                                   gl:vertexAttribPointer(p,
                       2,                                                      2,
                       gl.FLOAT,                                               gl.FLOAT,
                       gl.FALSE,                                               false,
                       2*Float32Array.BYTES_PER_ELEMENT,                       2 * 4,
                       0                                                       0
);                                                                         )
gl.enableVertexAttribArray(p);                                             gl:enableVertexAttribArray(p)
 
// transformace souřadnic:                                                 -- transformace souřadnic:
// z normalizovaných souřadnic na souřadnice na plátnu                     -- z normalizovaných souřadnic na souřadnice na plátnu
gl.viewport(0, 0, canvas.width, canvas.height);                            gl:viewport(0, 0, canvas.width, canvas.height)
 
// nastavení barvy pro vymazání barvového bufferu                          -- nastavení barvy pro vymazání barvového bufferu
gl.clearColor(0, 0, 0, 1);                                                 gl:clearColor(0, 0, 0, 1)
 
// vymazání barvového bufferu                                              -- vymazání barvového bufferu
gl.clear(gl.COLOR_BUFFER_BIT);                                             gl:clear(gl.COLOR_BUFFER_BIT)
 
// spuštění programu                                                       -- spuštění programu
gl.useProgram(program);                                                    gl:useProgram(program)
 
// vykreslení trojúhelníku                                                 -- vykreslení trojúhelníku
gl.drawArrays(gl.TRIANGLES, 0, 3);                                         gl:drawArrays(gl.TRIANGLES, 0, 3)

Odkazy na Internetu

  1. Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lua-v-roli-skriptovaciho-jazyka-pro-www-stranky/
  2. Fengari φεγγάρι – Lua for the Browser
    https://fengari.io/
  3. Lua in the Browser: WASM vs Pure JS Ports
    https://salivity.github.i­o/lua/article/lua-in-the-browser-wasm-vs-pure-js-ports
  4. Why we rewrote Lua in JS
    https://hackernoon.com/why-we-rewrote-lua-in-js-a66529a8278d
  5. GitHub repositář projektu Fengari
    https://github.com/fengari-lua/fengari
  6. Lua in the Browser: WASM vs Pure JS Ports
    https://salivity.github.i­o/lua/article/lua-in-the-browser-wasm-vs-pure-js-ports
  7. Why we rewrote Lua in JS
    https://hackernoon.com/why-we-rewrote-lua-in-js-a66529a8278d
  8. The Lua VM, on the Web
    https://kripken.github.io/lu­a.vm.js/lua.vm.js.html
  9. Lua.vm.js REPL
    https://kripken.github.io/lu­a.vm.js/repl.html
  10. lua2js
    https://www.npmjs.com/package/lua2js
  11. lua2js na GitHubu
    https://github.com/basicer/lua2js-dist
  12. Seriál o programovacím jazyku Lua
    http://www.root.cz/serialy/pro­gramovaci-jazyk-lua/
  13. Source-to-source compiler
    https://en.wikipedia.org/wiki/Source-to-source_compiler
  14. 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
  15. JavaScript is Web Assembly Language and that's OK.
    http://www.hanselman.com/blog/Ja­vaScriptIsWebAssemblyLangu­ageAndThatsOK.aspx
  16. Dart
    https://www.dartlang.org/
  17. CoffeeScript
    http://coffeescript.org/
  18. TypeScript
    http://www.typescriptlang.org/
  19. Lua (programming language)
    http://en.wikipedia.org/wi­ki/Lua_(programming_langu­age)
  20. Static single assignment form (SSA)
    http://en.wikipedia.org/wi­ki/Static_single_assignmen­t_form
  21. LuaJIT 2.0 SSA IR http://wiki.luajit.org/SSA-IR-2.0
  22. The LuaJIT Project
    http://luajit.org/index.html
  23. LuaJIT FAQ
    http://luajit.org/faq.html
  24. LuaJIT Performance Comparison
    http://luajit.org/performance.html
  25. LuaJIT 2.0 intellectual property disclosure and research opportunities
    http://article.gmane.org/gma­ne.comp.lang.lua.general/58908
  26. LuaJIT Wiki
    http://wiki.luajit.org/Home
  27. LuaJIT 2.0 Bytecode Instructions
    http://wiki.luajit.org/Bytecode-2.0
  28. Programming in Lua (first edition)
    http://www.lua.org/pil/contents.html
  29. Lua 5.2 sources
    http://www.lua.org/source/5.2/
  30. Tcl Plugin Version 3
    http://www.tcl.tk/software/plugin/
  31. JavaScript: The Web Assembly Language?
    http://www.informit.com/ar­ticles/article.aspx?p=1856657
  32. asm.js
    http://asmjs.org/
  33. List of languages that compile to JS
    https://github.com/jashke­nas/coffeescript/wiki/List-of-languages-that-compile-to-JS
  34. REPL
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  35. The LLVM Compiler Infrastructure
    http://llvm.org/ProjectsWithLLVM/
  36. clang: a C language family frontend for LLVM
    http://clang.llvm.org/
  37. emscripten
    http://kripken.github.io/emscripten-site/
  38. LLVM Backend („Fastcomp“)
    http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html#llvm-backend
  39. Emscripten – Fastcomp na GitHubu
    https://github.com/kripken/emscripten-fastcomp
  40. Clang (pro Emscripten) na GitHubu
    https://github.com/kripken/emscripten-fastcomp-clang
  41. Why not use JavaScript?
    https://ckknight.github.i­o/gorillascript/
  42. Roblox
    https://en.wikipedia.org/wiki/Roblox
  43. Category:Lua (programming language)-scriptable game engines
    https://en.wikipedia.org/wi­ki/Category:Lua_(programmin­g_language)-scriptable_game_engines
  44. Goodbye Lua (shrnutí následujícího článku)
    https://www.reddit.com/r/lu­a/comments/4ld6ao/goodbye_lu­a/
  45. Goodbye, Lua
    https://realmensch.org/2016/05/28/go­odbye-lua/
  46. 6th Edition – ECMAScript 2015
    https://en.wikipedia.org/wi­ki/ECMAScript#6th_Edition_%E2%80%93_EC­MAScript_2015
  47. JavaScript Versions
    https://www.w3schools.com/js/js_ver­sions.asp
  48. ECMAScript version history
    https://en.wikipedia.org/wi­ki/ECMAScript_version_his­tory
  49. WebAssembly
    https://en.wikipedia.org/wi­ki/WebAssembly
  50. Lua version history
    https://www.lua.org/versions.html
  51. Brython aneb použití jazyka Python ve skriptech přímo v prohlížeči
    https://www.root.cz/clanky/brython-aneb-pouziti-jazyka-python-ve-skriptech-primo-v-prohlizeci/
  52. Float32Array
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Re­ference/Global_Objects/Flo­at32Array
  53. JavaScript typed arrays
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Gu­ide/Typed_arrays
  54. Equality (==)
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Re­ference/Operators/Equality
  55. Strict equality (===)
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Re­ference/a/Strict_equality

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.