Obsah
1. Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky
2. Způsob využití různých programovacích jazyků na WWW stránkách
3. Transcompilery do JavaScriptu
5. Virtuální stroj naprogramovaný v JavaScriptu
8. Příklady použití jazyka Lua na WWW stránkách
9. „Hello World“ v JavaScriptu a v Lue
10. Přečtení všech atributů a metod objektu js
11. Globální objekty dostupné programátorům
12. Změna stylu (barvy) vybraného elementu
13. Použití objektu typu canvas (2D) pro kreslení
1. Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky
V dnešním článku se seznámíme s dvojicí po technologické stránce zajímavých projektů, které sice mají prakticky totožný cíl, ovšem jejich implementace je zcela odlišná. Tyto projekty se jmenují 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. 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 :-).
2. Způsob využití různých programovacích jazyků na WWW stránká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
I přes snahy některých vývojářů a softwarových společností o začlenění dalších skriptovacích jazyků do webových prohlížečů (Tcl, VBScript, Dart v Dartiu apod.) je patrné, že v současnosti je jediným široce akceptovaným skriptovacím jazykem na straně webového prohlížeče pouze JavaScript. To však neznamená, že by se aplikace, které mají být spouštěny na straně klienta, musely psát pouze v JavaScriptu, jenž nemusí zdaleka všem vývojářům vyhovovat, ať již z objektivních či ze subjektivních příčin. V relativně nedávné minulosti proto vzniklo a pořád ještě vzniká mnoho projektů, jejichž cílem je umožnit tvorbu webových aplikací pro prohlížeč v jiných programovacích jazycích. Zdrojové kódy je pak nutné nějakým způsobem zpracovat takovým způsobem, aby je bylo možné ve webovém prohlížeči spustit. Možností je hned několik – lze použít plugin (velmi problematické a dnes značně nepopulární řešení), transcompiler do JavaScriptu či virtuální stroj popř. interpret daného jazyka implementovaný opět v JavaScriptu. Právě posledními dvěma zmíněnými možnostmi se budeme zabývat v navazujících kapitolách.
3. Transcompilery do JavaScriptu
Jednu z dnes velmi populárních technik umožňujících použití prakticky libovolného programovacího jazyka pro tvorbu aplikací běžících na straně webového prohlížeče, představuje použití takzvaných transcompilerů (source-to-source compiler) zajišťujících překlad programu napsaného ve zdrojovém programovacím jazyce do funkčně identického programu napsaného v JavaScriptu. Transpřekladač se většinou spouští jen jednou na vývojářském počítači, samotní klienti již mají k dispozici JavaScriptový kód. Existuje však i druhá možnost, kdy je transpřekladač naprogramován v JavaScriptu a spouštěn přímo ve webovém prohlížeči klientů. Oba přístupy mají své přednosti, ale pochopitelně i nějaké zápory (například tvůrci uzavřených aplikací pravděpodobně budou upřednostňovat první možnost, protože výstupy transcompilerů jsou většinou dosti nečitelné). Z praxe můžeme uvést například následující projekty založené na transpřekladači:
:# | Jazyk | Poznámka |
---|---|---|
1 | CoffeeScript | přidání syntaktického cukru do JavaScriptu |
2 | ClojureScript | překlad aplikací psaných v Clojure do JavaScriptu |
3 | Kaffeine | rozšíření JavaScriptu o nové vlastnosti |
4 | RedScript | jazyk inspirovaný Ruby |
5 | GorillaScript | další rozšíření JavaScriptu |
6 | ghcjs | transpřekladač pro fanoušky programovacího jazyka Haskell |
4. Emscripten
Další alternativní technologii, která mi osobně přijde velmi zajímavá a v budoucnu možná i přelomová, představují transpřekladače provádějící překlad z bajtkódu či mezikódu do JavaScriptu (vstupem zde tedy není zdrojový kód v nějakém lidsky čitelném programovacím jazyku, ale většinou binárně reprezentovaný výsledek předchozího překladu). Příkladem tohoto typu transpřekladače je především Emscripten umožňující překlad kódu z libovolného jazyka podporovaného LLVM (C, C++, Objective C, D, Ada, Fortran atd.) do JavaScriptu. Podívejme se nyní ve stručnosti na kroky, které je zapotřebí provést proto, aby se původní kód napsaný například v Céčku, mohl spustit ve webovém prohlížeči:
- Na vstupu celého procesu je program napsaný v céčku
- Nejprve je proveden překlad pomocí clang do mezikódu LLVM (LLVM Intermediate Representation)
- Následně je zavolán Fastcomp (jádro překladače Emscriptenu) pro překlad mezikódu z předchozího kroku do JavaScriptu
- Výsledný JavaScriptový zdrojový kód je možné využít různými způsoby (node.js na serveru, na WWW stránce atd.)
Poznámka: poslední překlad (do JavaScriptu) generuje kód kompatibilní s asm.js, tj. používá se zde cíleně omezená podmnožina konstrukcí JavaScriptu.
5. Virtuální stroj naprogramovaný v JavaScriptu
Právě projekt Emscripten zmíněný v předchozí kapitole do značné míry usnadnil další způsob zajištění běhu programů napsaných v různých programovacích jazycích ve webovém prohlížeči. Pokud je totiž možné přeložit jakýkoli program napsaný v jazycích C či C++ do JavaScriptu (samozřejmě za předpokladu, že se vhodným způsobem budou emulovat použité knihovní funkce), proč by nebylo možné do JavaScriptu rovnou přeložit celý virtuální stroj používaný daným programovacím jazykem? Samozřejmě to možné je, a to zejména v případech, kdy je překládaný virtuální stroj malý, což je příklad VM pro jazyk Lua, tak i například poněkud většího virtuálního stroje Pythonu (.NET resp. CLR či Java VM už je těžší oříšek). Překladem VM do JavaScriptu získáme poměrně mnoho výhod, zejména pak možnost mít přímo v HTML stránkách původní zdrojové kódy (Lua, Python atd.) a nikoli nečitelný výstup z transpřekladačů. Za tento postup však také zaplatíme, zejména pomalejším během aplikací v porovnání s nativní VM. V praxi se může jednat o výkonnostní propad zhruba na polovinu, což ovšem v mnoha aplikacích vůbec není tak špatný výsledek.
6. Projekt lua2js
Prvním projektem určeným pro použití programovacího jazyka Lua na webových stránkách, kterým se dnes budeme zabývat, je projekt nazvaný jednoduše lua2js. Již název tohoto projektu napovídá, že se jedná o transpřekladač sloužící pro překlad zdrojových kódů z Luy do JavaScriptu. Samotný transpřekladač lua2js je kupodivu napsán také v JavaScriptu, takže se pro jeho instalaci použije správce JavaScriptových balíčků nazvaný npm (ten samozřejmě již musí být nainstalován) a pro spuštění transpřekladače je použita infrastruktura pro node.js. Samotnou instalaci lua2js zajistí příkaz:
npm install lua2js
Použití je snadné:
nodejs lua2js [vstupní_soubor.lua] [výstupní_soubor.js]
Ve skriptech lze používat základní funkce tvořící jádro jazyka Lua, zejména pak pairs() a ipairs(), další funkce a metody jsou dostupné přes objekty poskytované prohlížečem.
Obrázek 1: Část mimifikovaného zdrojového kódu projektu lua2js.
Podívejme se na překlad jednoduchého příkladu:
local k = 0 for i = 1, 10 do for j = 1, 10 do k = k + 1 end end
Po překladu pomocí příkazu:
nodejs lua2js test.lua test.js
Získáme následující zdrojový soubor v JavaScriptu:
var lua_script = (function() { var tmp; var G = lua_newtable2(lua_core); for (var i in lua_libs) { G.str[i] = lua_newtable2(lua_libs[i]); } G.str['arg'] = lua_newtable(); G.str['_G'] = G; G.str['module'] = function (name) { lua_createmodule(G, name, slice(arguments, 1)); }; G.str['require'] = function (name) { lua_require(G, name); }; G.str['package'].str['seeall'] = function (module) { if (!module.metatable) { module.metatable = lua_newtable(); } module.metatable.str['__index'] = G; }; { var _k_1 = 0; var var_2 = 1, stop_2 = 10; for (; var_2 <= stop_2; var_2++) { var _i_2 = var_2; var var_4 = 1, stop_4 = 10; for (; var_4 <= stop_4; var_4++) { var _j_4 = var_4; _k_1 = lua_add(_k_1, 1); } } }; return [G]; })()[0];
7. Projekt lua.vm.js
Mnohem ambicióznějším a vlastně i praktičtějším projektem je projekt nazvaný lua.vm.js. Tento projekt je založen na výše zmíněném Emscriptenu, protože lua.vm.js není nic jiného než virtuální stroj jazyka Lua implementovaný v 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. Navíc je tento projekt vytvořen takovým způsobem, že se do WWW stránek přímo vkládají skripty psané v Lue, což ostatně uvidíme na čtveřici demonstračních příkladů uvedených v následujících kapitolách.
Obrázek 2: O tom, že se JavaScript v současnosti používá skutečně jako „assembler pro webové aplikace“ vypovídá i výsledný kód virtuálního stroje jazyka Lua po překladu do JavaScriptu.
8. Příklady použití jazyka Lua na WWW stránkách
V následujících čtyřech kapitolách si ukážeme použití programovacího jazyka Lua na WWW stránkách. Přitom bude využit výše zmíněný projekt lua.vm.js, což znamená, že přímo v kódu WWW stránky bude umístěn zdrojový kód psaný v jazyce Lua a samozřejmě i link na virtuální stroj Luy přeložený do JavaScriptu. Tento virtuální stroj získá ze stránky příslušný Lua skript a spustí ho prakticky stejným způsobem, jakoby se jednalo o běžný JavaScriptový program.
9. „Hello World“ v JavaScriptu a v Lue
Začněme skutečně tím nejjednodušším příkladem, kterým je (alespoň většinou) program vypisující na výstupní zařízení (obrazovku, plochu prohlížeče atd.) nápis „Hello world“. Implementace takového programu ve WWW stránce v JavaScriptu vypadá následovně (text se schválně odlišuje):
<html> <head> <title>Hello World JS</title> </head> <body> <script type="text/javascript"> document.write("JavaScript says: 'Hello world!'"); </script> </body> </html>
Vidíme zde použití objektu document a jeho metody write pro tisk řetězce přímo do těla HTML stránky (tedy i renderovaného dokumentu).
Obrázek 3: JavaScript spuštěný v prohlížeči.
Funkčně ne sice zcela totožný, ale dosti podobný, příklad naprogramovaný v jazyce Lua bude vypadat následovně:
<html> <head> <title>Hello World Lua</title> <script src="lua.vm.js"></script> </head> <body> <script type="text/lua"> js.global.document:write("Lua says: 'Hello world!'") </script> </body> </html>
Důležité změny:
- Na začátku, ideálně v hlavičce HTML stránky, je nutné načíst virtuální stroj jazyka Lua přeložený do JavaScriptu [řádek číslo 4]
- Skript psaný v jazyku Lua má typ nastavený na „text/lua“ a nikoli „text/javascript“ (což je pochopitelné a současně i nutné, neboť právě tento atribut Lua VM hledá!) [řádek číslo 7]
- Objekt document je dostupný pod js.global.document [řádek číslo 7]
- Metoda objektu se volá s použitím operátoru dvojtečky a ne tečky (vizPIL) [řádek číslo 8].
Obrázek 4: Skript napsaný v jazyce Lua byl zpracován virtuálním strojem implementovaným v JavaScriptu.
10. 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 src="lua.vm.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>
Obrázek 5: Druhý demonstrační příklad po svém spuštění v prohlížeči.
11. 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 src="lua.vm.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>
Obrázek 6: Třetí demonstrační příklad po svém spuštění v prohlížeči.
Obrázek 7: Objekty používané při práci s obsahem HTML stránky.
12. Změna stylu (barvy) vybraného elementu
Jedna z nejpoužívanějších funkcí v JavaScriptu při práci s DOMem HTML stránky je funkce getElementById(). Tuto funkci je samozřejmě možné volat i z programu psaného v programovacím jazyce Lua, o čemž je možné se snadno přesvědčit (jen se nesmí zapomenout na to, že při volání metod objektů se používá dvojtečka). V následujícím příkladu se změní barva nadpisu z výchozí černé na červenou:
<html> <head> <title>SetColor.Lua</title> <script src="lua.vm.js"></script> </head> <body> <h1 id="nadpis">Text with changed color</h1> <script type="text/lua"> local nadpis = js.global.document:getElementById("nadpis") nadpis.style.color="#ff0000" </script> </body> </html>
Obrázek 8: Čtvrtý demonstrační příklad po svém spuštění v prohlížeči.
13. Použití objektu typu canvas (2D) pro kreslení
Vzhledem k tomu, že ve skriptech psaných v programovacím jazyku Lua lze používat všechny objekty dostupné i JavaScriptu, není velkým problémem zařídit i vykreslování přes canvas, resp. přesněji řečeno přes 2D kontext (samozřejmě by bylo možné použít i 3D komplex, ovšem netřeba věci komplikovat více, než je to pro demonstraci nutné). V dnešním posledním demonstračním příkladu se vykreslí část barvové palety s využitím různobarevných čtverců. Pro převod číselných hodnot barvových složek na odpovídající kód je pro jednoduchost použita funkce string:format:
<html> <head> <title>CanvasTest.Lua</title> <script src="lua.vm.js"></script> </head> <body> <canvas id="canvas" width="400" height="400"></canvas> <script type="text/lua"> local canvas = js.global.document:getElementById("canvas") local renderingContext = canvas:getContext("2d"); for i=0,8 do for j=0,8 do local color = string.format("#%02x%02x%02x", i*31, j*31, 80) renderingContext.fillStyle = color renderingContext:fillRect(j*20, i*20, 20, 20) end end </script> </body> </html>
Poznámka: povšimněte si rozdílu mezi použitím tečky (nastavení stylu vykreslování) a dvojtečky (volání metody objektu, zde konkrétně objektu renderingContext).
Obrázek 9: Pátý demonstrační příklad po svém spuštění v prohlížeči.
14. Odkazy na Internetu
- The Lua VM, on the Web
https://kripken.github.io/lua.vm.js/lua.vm.js.html - Lua.vm.js REPL
https://kripken.github.io/lua.vm.js/repl.html - lua2js
https://www.npmjs.com/package/lua2js - lua2js na GitHubu
https://github.com/basicer/lua2js-dist - Seriál o programovacím jazyku Lua
http://www.root.cz/serialy/programovaci-jazyk-lua/ - Source-to-source compiler
https://en.wikipedia.org/wiki/Source-to-source_compiler - JavaScript is Assembly Language for the Web: Sematic Markup is Dead! Clean vs. Machine-coded HTML
http://www.hanselman.com/blog/JavaScriptIsAssemblyLanguageForTheWebSematicMarkupIsDeadCleanVsMachinecodedHTML.aspx - JavaScript is Web Assembly Language and that's OK.
http://www.hanselman.com/blog/JavaScriptIsWebAssemblyLanguageAndThatsOK.aspx - Dart
https://www.dartlang.org/ - CoffeeScript
http://coffeescript.org/ - TypeScript
http://www.typescriptlang.org/ - Lua (programming language)
http://en.wikipedia.org/wiki/Lua_(programming_language) - Static single assignment form (SSA)
http://en.wikipedia.org/wiki/Static_single_assignment_form - LuaJIT 2.0 SSA IRhttp://wiki.luajit.org/SSA-IR-2.0
- The LuaJIT Project
http://luajit.org/index.html - LuaJIT FAQ
http://luajit.org/faq.html - LuaJIT Performance Comparison
http://luajit.org/performance.html - LuaJIT 2.0 intellectual property disclosure and research opportunities
http://article.gmane.org/gmane.comp.lang.lua.general/58908 - LuaJIT Wiki
http://wiki.luajit.org/Home - LuaJIT 2.0 Bytecode Instructions
http://wiki.luajit.org/Bytecode-2.0 - Programming in Lua (first edition)
http://www.lua.org/pil/contents.html - Lua 5.2 sources
http://www.lua.org/source/5.2/ - Tcl Plugin Version 3
http://www.tcl.tk/software/plugin/ - JavaScript: The Web Assembly Language?
http://www.informit.com/articles/article.aspx?p=1856657 - asm.js
http://asmjs.org/ - List of languages that compile to JS
https://github.com/jashkenas/coffeescript/wiki/List-of-languages-that-compile-to-JS - REPL
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - The LLVM Compiler Infrastructure
http://llvm.org/ProjectsWithLLVM/ - clang: a C language family frontend for LLVM
http://clang.llvm.org/ - emscripten
http://kripken.github.io/emscripten-site/ - LLVM Backend („Fastcomp“)
http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html#llvm-backend - Emscripten – Fastcomp na GitHubu
https://github.com/kripken/emscripten-fastcomp - Clang (pro Emscripten) na GitHubu
https://github.com/kripken/emscripten-fastcomp-clang - Why not use JavaScript?
https://ckknight.github.io/gorillascript/