Mengenal Bahasa Pemrograman Lua

Di kesempatan kali ini saya akan memperkenalkan bahasa Lua. Bahasa ini sebenarnya sudah ada cukup lama (sejak 1993), tapi kurang terkenal secara umum. Lua lebih sering dijadikan bahasa scripting untuk aplikasi dan sering kali nama Lua tidak disebut sama sekali. Lua artinya “Bulan” (moon) dalam bahasa portugis. Lua diciptakan oleh Roberto Ierusalimschy, Luiz Henrique de Figueiredo, dan Waldemar Celes, anggota dari Computer Graphics Technology Group (Tecgraf) di Pontifical Catholic University of Rio de Janeiro, di Brazil.

Lua bisa dipakai untuk membuat program/aplikasi seperti bahasa lain, tapi kelebihan bahasa ini  adalah dari awal dirancang untuk diembed dalam sebuah aplikasi lain (terutama dalam bahasa sejenis C). Di-embed ini artinya bisa  bisa dipakai untuk sistem plugin atau bisa dipakai untuk mengontrol bagian business/game/app logic sementara bagian yang butuh performance tinggi memakai C atau bahasa lain. Lua juga bisa jadi file konfigurasi yang sangat fleksibel.

Lua dipakai di banyak software misalnya Adobe Lightroom, Nmap, Wireshark, dan lighttpd. Seperti saya sebutkan di awal, sering kali berbagai aplikasi menyebutkan bahasa Lua ini hanya sekilas, dan fitur aplikasinya yang lebih ditekankan dengan nama lain, misalnya dokumentasi Nmap menyebutkan tentang Nmap Scripting Engine (NSE) tapi Lua hanya disebut sekilas di tengah. Lua juga banyak dipakai di berbagai game (misalnya World Of Warcraft, Angry Birds) dan game engine. Interpreter Lua ukurannya sangat kecil sehingga bisa dipakai untuk embedded system/IOT (misalnya NodeMCU dan eLua).

Bahasa Lua

Bahasa Lua menurut saya relatif sederhana, tapi juga sangat fleksibel. Sintaksnya tidak aneh, sedikit mirip BASIC atau pascal. Contohnya seperti ini:

print "hello, berapa umur kamu?"
umur = io.read("*n")
if umur >= 17 then
   print("Halo kakak")
else
   print("Halo adik")
end

Bagi sebagian orang mungkin ini agak aneh mengingat bahasa sekarang sudah tidak lagi memakai kata-kata (“then”, “end”) tapi lebih memakai simbol ({} di C/C++/Java/.NET, indentasi di Python), tapi untuk yang dulu memakai Pascal dan BASIC ini terasa cukup wajar. Sebagian besar syntax Lua mirip dengan bahasa lain, tapi ada beberapa hal yang agak aneh. Di Lua, operator kurang dari, lebih besar, sama dengan, semuanya sama dengan bahasa lain (<, >, =) tapi untuk tidak sama dengan, syntax yang dipakai adalah ~=. Syntax untuk komentar juga agak kurang standar, memakai “–” untuk satu baris, dan memakai –[[ untuk komentar multi baris –]].

Secara umum bahasa Lua cukup mudah dibaca bahkan oleh pemula, tidak seperti bahasa Forth (yang saya bahas sebelumnya) atau bahasa fungsional seperti Scala.

Tipe primitif yang didukung Lua adalah: string, number, dan integer. Struktur data utama di Lua adalah table. Secara umum: tabel memetakan sesuatu ke sesuatu yang lain (seperti Map/Dictionary di bahasa lain). Hampir semua hal di Lua diimplementasikan dengan table. Sebuah array di Lua juga adalah tabel yang memetakan indeks dengan sebuah nilai, dengan nilai index awal default adalah 1. Masalah index ini agak mengganggu bagi saya, karena di bahasa lain biasanya array berbasis 0. Sebuah tabel bisa berisi tabel lain untuk merepresentasikan array multi dimensi.

Sebuah struktur (struct) juga bisa direpresentasikan dalam bentuk tabel (memetakan nama field sebagai string ke nilainya). Di Lua, sebuah fungsi juga bisa masuk ke dalam tabel. Contohnya kita bisa saja membuat tabel yang berisi berbagai operasi matriks yang berisi pemetaan nama operasi, misalnya “multiply” dengan implementasi fungsi perkalian matriks.

Lua juga memiliki konsep metatable, yang bisa digunakan untuk mendefinisikan operasi terhadap sebuah variabel. Dengan setmetatable(variabel, sebuahtabel), kita bisa mendefinisikan berbagai operator dan fungsi yang berlaku untuk variabel tersebut. Jadi jika kita ingin membuat sebuah variabel menjadi sebuah tipe matriks, kita bisa membuatnya dengan setmetatable(variabel1, tabel_operasi_matriks).

Lua tidak memiliki konsep OOP tapi dengan menggunakan berbagai trik table, metatable dan fleksibilitas function di Lua, kita bisa mengimplementasikan Object Oriented Programming. Saya sendiri kurang menyukai ini, kalau saya ingin memprogram dengan paradigma OO, saya akan memilih bahasa lain.

Fitur Lua lain yang menarik adalah coroutine, generalisasi dari subrutin, tapi fitur ini jarang saya pakai. Sebenarnya fitur ini powerful, tapi ketika melakukan embeding Lua ke bahasa lain (terutama C), fitur ini cukup merepotkan karena tidak mudah dipetakan ke C.

Embeding Lua ke bahasa C

Seperti sudah saya sebutkan di atas, fitur Lua adalah sangat cocok untuk diembed ke sebuah aplikasi. Sebenarnya bahasa lain seperti Python dan Ruby juga bisa diembed ke bahasa lain (misalnya C), tapi ada banyak kelebihan Lua:

  • Ukurannya sangat kecil, ratusan kilobyte. Python butuh minimal beberapa megabyte (disk space dan memori)
  • Mendukung multi thread (satu thread menjalankan instance Lua terpisah). Untuk embedding multi thread di Python sangat sulit, dan Python memiliki Global Interpreter Lock
  • Interfacing sangat mudah, baik coding manual atau dengan bantuan SWIG

Implementasi utama Lua adalah dalam bahasa C, walau ada juga Lua untuk Java. Kebanyakan bahasa memiliki build system yang kompleks dan butuh banyak library tambahan. Dalam kasus Lua: kita cuma perlu copy paste semua file .c (kecuali lua.c) ke project kita dan itu sudah cukup. Sebagai catatan: selain implementasi utama Lua, ada juga LuaJIT yang sangat cepat namun memiliki banyak batasan terutama di sistem 64 bit.

Setelah memasukkan semua file C dalam project kita, di bagian program yang butuh Lua, kita perlu menginclude beberapa file:

               
#include "lua.h"       
#include "lualib.h"       
#include "lauxlib.h"

Lalu kita bisa menginstansiasi interpreter Lua. Jika diperlukan, kita bisa membuat banyak instance (misalnya satu di setiap thread). Setelah itu biasanya kita ingin agar library standar Lua diaktifkan, jadi kita memakai luaL_openlibs.

lua_State *L = luaL_newstate(); 
luaL_openlibs(L);

Biasanya skrip Lua akan diletakkan di luar program, supaya bisa diubah dengan mudah tanpa rekompilasi. Untuk meload dari file, kita bisa memakai luaL_loadfile atau luaL_loadstring jika ingin memakai string. Di banyak Game, file lua dienkrip di file (agar tidak mudah dimodifikasi), lalu didekrip dimemor, dan diload menggunakan luaL_loadstring.

 
int err = luaL_loadfile(L, "myscript.lua");
if (err) {
       fprintf(stderr, "Failed loading file: %s\n", lua_tostring(L, -1));
       exit(1);
}

Dalam passing parameter ke Lua dan dari Lua, kita menggunakan konsep stack. Kita bisa memasukkan nilai ke stack dengan lua_pushstring/lua_pushnumber/lua_pushinteger, dan bisa mengambil nilai di stack dengan lua_tointeger, lua_tostring, lua_tonumber dan beberapa fungsi lain. Jadi ketika memanggil fungsi di Lua: kita melakukan push value ke stack, dan untuk mengambil nilai dari lua kita akses hasilnya di stack juga.

Kita bisa mencari fungsi yang diimplementasikan di Lua dengan lua_getglobal(L, "namafungsi"), ini akan mempush reference fungsi ke stack. Setelah itu kita bisa mempush nilai parameter untuk fungsi, memanggil fungsinya, dan mendapatkan hasilnya di stack.

 
lua_getglobal(L, "myfunction"); 
lua_pushinteger(L, 1); //param 1
lua_pushinteger(L, 2);  //param 2
int err = lua_pcall(L, 
    2 /*number of arguments*/, 
    1 /*number of result*/, 
    0 /*indeks ke error handler, jika 0 maka error akan dipush ke stack*/);

Membuat fungsi di C yang bisa dipanggil oleh Lua juga sangat mudah. Misalnya kita ingin membuat fungsi max yang menerima 2 double dari Lua dan mengembalikan angka terbesar, maka kita cukup membuat ini:

 
int max_for_lua(lua_State *L) 
{
  double num1 = lua_tointeger(L, 1); //param 1 dari Lua
  double num2 = lua_tointeger(L, 2); //param 2 dari Lua

  double max = num1>num2?num1:num2; //logika utama

  lua_pushnumber(L, max); //push ke stack Lua
  return 1; //hanya ada satu nilai kembalian
}

Tentunya keberadaan fungsi ini perlu diberitahukan ke Lua agar bisa diakses oleh skrip Lua. Nama fungsi di C dan Lua bisa berbeda, contohnya fungsi max_for_lua di atas bisa didaftarkan menjadi “my_max” seperti ini:

 
lua_pushcfunction(lua_state, max_for_lua);
lua_setglobal(lua_state, "my_max");

Penutup

Sering kali saya menemukan di mana saya butuh eksperimen cepat dan tidak ingin sering mengcompile ulang kode program saya. Contoh penggunaan yang baru-baru ini saya lakukan adalah ketika eksplorasi fitur tertentu di iOS. Jika saya mengandalkan objective C atau Swift saja, tiap kali saya mengubah kode (biasanya hanya logic tertentu), akan butuh beberapa detik sampai beberapa puluh detik sampai bisa menjalankan ulang aplikasinya. Walau hanya menekan tombol “run” di XCode yang terjadi di latar belakang adalah: kodenya harus dicompile, dilink, ditandangani secara digital, dicopy ke device, lalu dijalankan. Dengan memindahkan logic ke Lua, saya hanya perlu me-mount direktori dokumen aplikasi dengan ifuse, mengedit file Lua, lalu merestart aplikasinya.

Menambahkan sebuah scripting ke language ke aplikasi memang butuh waktu tapi di sisi lain juga sangat fleksibel karena skrip bisa dimodifikasi dengan cepat dan tidak seperti kode C, tidak perlu dikompilasi ulang. Jika Anda sudah beberapa kali menambahkan fitur scripting ke sebuah aplikasi, menambahkan ke aplikasi berikutnya tidak butuh waktu lama. Untuk aplikasi kompleks kita bisa memakai Simplified Wrapper and Interface Generator (SWIG) untuk mempercepat membuat interface dari C/C++ ke Lua.

Semoga tulisan singkat ini cukup untuk memperkenalkan bahasa Lua dan mungkin juga membuat Anda tertarik untuk menambahkan Lua ke aplikasi yang Anda buat.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.