Programovací jazyk Lua v roli skriptovacího jazyka pro WWW stránky

21. 7. 2015
Doba čtení: 14 minut

Sdílet

JavaScript, jakožto jazyk používaný (nejenom) pro tvorbu skriptů spouštěných v rámci webových prohlížečů, pravděpodobně není nutné představovat. Pro vývojáře však mohou být zajímavé projekty umožňující tvorbu skriptů pro WWW stránky v odlišném jazyku. Dnes se seznámíme se dvěma projekty pro jazyk Lua.

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

4. Emscripten

5. Virtuální stroj naprogramovaný v JavaScriptu

6. Projekt lua2js

7. Projekt lua.vm.js

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í

14. Odkazy na Internetu

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:

  1. Na vstupu celého procesu je program napsaný v céčku
  2. Nejprve je proveden překlad pomocí clang do mezikódu LLVM (LLVM Intermediate Representation)
  3. Následně je zavolán Fastcomp (jádro překladače Emscriptenu) pro překlad mezikódu z předchozího kroku do JavaScriptu
  4. 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:

  1. 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]
  2. 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]
  3. Objekt document je dostupný pod js.global.document [řádek číslo 7]
  4. 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:

bitcoin_skoleni

<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

  1. The Lua VM, on the Web
    https://kripken.github.io/lu­a.vm.js/lua.vm.js.html
  2. Lua.vm.js REPL
    https://kripken.github.io/lu­a.vm.js/repl.html
  3. lua2js
    https://www.npmjs.com/package/lua2js
  4. lua2js na GitHubu
    https://github.com/basicer/lua2js-dist
  5. Seriál o programovacím jazyku Lua
    http://www.root.cz/serialy/pro­gramovaci-jazyk-lua/
  6. Source-to-source compiler
    https://en.wikipedia.org/wiki/Source-to-source_compiler
  7. 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
  8. JavaScript is Web Assembly Language and that's OK.
    http://www.hanselman.com/blog/Ja­vaScriptIsWebAssemblyLangu­ageAndThatsOK.aspx
  9. Dart
    https://www.dartlang.org/
  10. CoffeeScript
    http://coffeescript.org/
  11. TypeScript
    http://www.typescriptlang.org/
  12. Lua (programming language)
    http://en.wikipedia.org/wi­ki/Lua_(programming_langu­age)
  13. Static single assignment form (SSA)
    http://en.wikipedia.org/wi­ki/Static_single_assignmen­t_form
  14. LuaJIT 2.0 SSA IRhttp://wiki.luajit.org/SSA-IR-2.0
  15. The LuaJIT Project
    http://luajit.org/index.html
  16. LuaJIT FAQ
    http://luajit.org/faq.html
  17. LuaJIT Performance Comparison
    http://luajit.org/performance.html
  18. LuaJIT 2.0 intellectual property disclosure and research opportunities
    http://article.gmane.org/gma­ne.comp.lang.lua.general/58908
  19. LuaJIT Wiki
    http://wiki.luajit.org/Home
  20. LuaJIT 2.0 Bytecode Instructions
    http://wiki.luajit.org/Bytecode-2.0
  21. Programming in Lua (first edition)
    http://www.lua.org/pil/contents.html
  22. Lua 5.2 sources
    http://www.lua.org/source/5.2/
  23. Tcl Plugin Version 3
    http://www.tcl.tk/software/plugin/
  24. JavaScript: The Web Assembly Language?
    http://www.informit.com/ar­ticles/article.aspx?p=1856657
  25. asm.js
    http://asmjs.org/
  26. List of languages that compile to JS
    https://github.com/jashke­nas/coffeescript/wiki/List-of-languages-that-compile-to-JS
  27. REPL
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  28. The LLVM Compiler Infrastructure
    http://llvm.org/ProjectsWithLLVM/
  29. clang: a C language family frontend for LLVM
    http://clang.llvm.org/
  30. emscripten
    http://kripken.github.io/emscripten-site/
  31. LLVM Backend („Fastcomp“)
    http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html#llvm-backend
  32. Emscripten – Fastcomp na GitHubu
    https://github.com/kripken/emscripten-fastcomp
  33. Clang (pro Emscripten) na GitHubu
    https://github.com/kripken/emscripten-fastcomp-clang
  34. Why not use JavaScript?
    https://ckknight.github.i­o/gorillascript/

Autor článku

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