Lua - Un piccolo grande linguaggio

Nota: Questo articolo è stato scritto e pubblicato più circa nove anni fa, ma ora lo pubblico di nuovo, praticamente senza nessuna modifica, visto che lo trovo oggi ancora molto attuale.

La continua evoluzione dell'hardware non accenna a rallentare. Ben presto computer super veloci con più processori e tanta memoria saranno disponibili a prezzi ragionevoli da diventare prodotti di massa. Scrivere software che sfrutti tutte le loro potenzialità si trasformerebbe in una sfida continua ed aperta se i programmatori continuassero ad usare gli stessi strumenti e le stesse metodologie del passato producendo applicazioni monolitiche.

Il software ludico, ormai da anni, ha aperto la strada in questa direzione proponendo metodologie assai praticabili anche per scrivere altri generi di software. Ricordiamo che i videogiochi sono da considerare delle applicazioni complesse. Essi sono delle vere applicazioni di intelligenza artificiale con un mix di grafica avanzata, animazioni, suoni, interazione con l'hardware, sincronizzazione...

Scrivere software del genere composto da diverse centinaia di migliaia di linee di codice in C/C++, spesso integrato con codice Assembly, senza ricorrere ad una adeguata strategia di sviluppo è vivamente sconsigliato. Il fallimento è sempre in agguato ed addio fama e ricchezza.

A dire il vero, scrivere software complesso oggi è molto più facile che in passato. Prima di tutto abbiamo un hardware economico, estremamente potente ed equipaggiato, strumenti di sviluppo avanzati, poi vaste librerie mature e ben testate, spesso disponibili gratuitamente. OpenGL e SDL sono le prime due che vengono in mente quando si parla di grafica e multimedialità. Quindi serve solo scrivere le parti mancanti dell'applicazione e collegare il tutto tramite "collanti" software. Cioè, un meccanismo produttivo simile a quello che avviene normalmente nelle industrie dove il prodotto finale è un insieme di parti assemblate.

Diversi software di grafica professionale, soprattutto CAD, adoperano questa metodologia di sviluppo da sempre, diversamente non avrebbero mai goduto della ricchezza di funzionalità, la flessibilità e l'estendibilità che li caratterizza.

Nel mondo dei videogiochi, questa è la prassi e, generalmente, il "collante" software tra i vari pezzi è proprio il linguaggio Lua.

Lua (luna in portoghese) è un linguaggio di scripting nato in Brasile ed è del tutto particolare. Lua, rispetto ai noti linguaggi di scripting come PHP, Perl, Python e Ruby, si distingue per la sua estrema compattezza, velocità ed una fortissima enfasi sulla descrizione e la gestione dei dati. In più, dal principio, è stato progettato e realizzato per essere embedded (incastrato) all'interno di applicazioni scritte in C/C++, D, Pascal, Ada... In questo modo è possibile estendere e modificare un'applicazione già completata e distribuita ai clienti senza disporre del codice sorgente e soprattutto utilizzare un linguaggio così piccolo e semplice che si impara in un giorno!

Il prezzo di tutto ciò è solo l'aggiunta alla nostra applicazione finale di un centinaio di kilobyte costituente l'interprete Lua.

Lua non è solo un linguaggio di estensione, ma è anche un linguaggio general purpose per scrivere una grande varietà di applicazioni. Detto in termini sfidanti, potete scrivere in Lua tutto ciò che potete scrivere in Python o in Ruby con un notevole guadagno in termini di velocità di esecuzione delle applicazioni.

Il linguaggio Lua

Lua è un linguaggio di scripting dinamico molto piccolo e flessibile. L'intero interprete è contenuto in un singolo file eseguibile di circa 200 KB. Basta copiare questo file in una directory di sistema o presente nel vostro Path e Lua diventa pronto all'utilizzo senza limiti. La compilazione di Lua da codice sorgente (qualche migliaio di codice C molto pulito) è semplice e veloce su qualsiasi piattaforma che dispone di un compilatore ANSI C.

Dotare invece un'applicazione scritta in C/C++, D oppure Pascal/Object Pascal di un interprete Lua è semplice e conveniente.

La sintassi di Lua è simile a quella del Pascal, ma si avvicina di più ad Ada; cioò conferisce al linguaggio una grande leggibilità ed eleganza. Chiunque conosca un linguaggio di scripting trova Lua più semplice, più piccolo e più immediato di qualunque altro. In effetti Lua è composto da una manciata di parole chiavi e simboli (vedi riquadro). Il fatto che sia un linguaggio così piccolo non dovrebbe indurre il lettore a pensare che con Lua si può fare poco o niente. Ma ancora una volta, con Lua potete fare tutto ciò che si farebbe con Perl, Python o Ruby. Quando poi si tratta di estendere un'applicazione, Lua dimostra la sua indole. È possibile scrivere codice lua secondo il paradigma procedurale, funzionale e ad oggetti. Lua offre realmente tutte le tre possibilità.

/** Quadro
Parole riservate di Lua
     and       break     do        else      elseif
     end       false     for       function  if
     in        local     nil       not       or
     repeat    return    then      true      until     while

Simboli di Lua
   +     -     *     /     %     ^     #
     ==    ~=    <=    >=    <     >     =
     (     )     {     }     [     ]
     ;     :     ,     .     ..    ...
*/

Hello World

Per non intaccare la tradizione, presentiamo la nostra prima applicazione in Lua con il classico saluto al mondo! Con un editor di testo qualsiasi scriviamo:
print "Hello World!"
e lo salviamo nel file di testo "hello.lua"
Ora da riga di comando digitiamo:
lua hello.lua
Et violà che appare il nostro saluto!

Tipi di dati

Oltre ai tipi di dati semplici come i numeri e le stringhe, Lua possiede un unico tipo di dato strutturato che è la "table" (tabella), ossia l'array associativo, offrendo un meccanismo semplice, potente e flessibile per gestire una grande varietà di dati. In effetti, possiamo definire Lua come un linguaggio table oriented, cioè, un linguaggio orientato alla gestione delle tabelle. In qualche modo una specie di SQL ma molto più potente.

Ecco alcuni esempi di dati espressi in Lua:

Cinque = 5 -- numero intero
Pi = 3.14159 -- numero reale
Nome = "Kira Kikona" --
Rosso= {r=1, g=0, b=0} -- tabella
polilinea = {colore="blu", spessore=1, npunti=4,
                 {x=0,   y=0},
                 {x=-10, y=0},
                 {x=-10, y=1},
                 {x=0,   y=1}

t = {}     -- crea una tabella
k = "x"
a[k] = 10        -- inserisce il numero 10 con chiave "x"
a[20] = "ciao!"  -- inserisce la stringa "ciao!" al rango 20

Ora

print(a["x"])    --> 10
k = 20
print(a[k])      --> "ciao!"
a["x"] = a["x"] + 1     -- incrementa di 1 il valore con chiave "x"
print(a["x"])    --> 11

Ecco un esempio che esprime la struttura di un menù in Lua:

T={"menu"= {
  "id"= "file",
  "value"= "File",
  "popup"= {
    "menuitem"= [
      {"value"= "Nuovo", "onclick": "CreateNewDoc()"},
      {"value"= "Apri", "onclick": "OpenDoc()"},
      {"value"= "Chiudi", "onclick": "CloseDoc()"}
    ]
  }
}}

Il seguente esempio illustra come dichiarare ed utilizzare una tabella di tabelle

Libro={} - oppure Libro={{}} o anche non dichiararla proprio
Libro={
{Codice=100001,
Titolo="Linux Programming",
Autore="Neil Matthew & Richards Stones",
Editore="WROX PRESS",
Anno="2001",
Edizione="I",
ISBN="1-874416-68-0"},
{Codice=100002,
Titolo="The Complete FreeBSD",
Autore="Grey Lehey",
Editore="Walnut Creek",
Anno="1997",
Edizione="II",
ISBN="1-57176-227-2"}
}
print(Libro[2].Titolo) -> The Complete FreeBSD

ì Questi semplici esempi mettono in evidenza quanto sia Lua flessibile a rappresentare i dati. Utilizzare Lua come un formato di dati da costituire un database altamente dinamico non è affatto una novità ma fu una delle primarie ragioni della creazione del linguaggio.

Programmare in Lua

È stato accennato in precedenza che Lua potrebbe anche essere utilizzato come linguaggio di scripting general purpose in modo del tutto simile a Python e Ruby. Perciò, Lua è dotato di tutto il necessario per lo sviluppo di applicazioni autonome di piccola e media complessità. Grazie poi alla semplicità con la quale Lua riesce a chiamare funzioni contenute in librerie dinamiche, allora l'unico limite diventa solo la fantasia.

A partire dalla versione 5.1, per gestire efficientemente le applicazioni di una certa complessità, un programma lua potrebbe essere strutturato in moduli e pacchetti (package). Un modulo è una libreria caricabile tramite la parola chiave "require" che ha un unico nome e viene trattata come una tabella.

Così abbiamo tutti i vantaggi di eseguire le stesse operazioni dedicate alle tabelle anche sui moduli. Supponiamo, per esempio, di avere un modulo chiamato "database" che contiene una funzione "init_data". Per utilizzare quella funzione all'interno di un nuovo programma basta chiamarla in questo modo:

require "databse"
database.init_data()

Oppure più elegantemente:

local db = require "database"
db.init_data()

Oppure riferendosi solo la funzione:

require "database"
local init = database.init_data()
init()

Per scaricare il modulo "database" dalla memoria serve l'istruzione:
package.loaded["database"] = nil

Il manuale di Lua, disponibile gratuitamente sul sito dedicato al linguaggio, illustra dettagliatamente come scrivere i moduli lua e come utilizzarli. L'uso oculato dei moduli in Lua permette di scrivere applicazioni sufficientemente complesse e facilmente gestibili. Per concludere questo paragrafo, c'è da ricordare che non c'è un limite al numero dei moduli da chiamare da un'applicazione, ma è anche possibile che un modulo chiami un altro o più moduli.

Programmazione ad oggetti

Oggi, questo paradigma di pensare e scrivere il software è molto diffuso e gode di un notevole successo. Anche se la programmazione ad oggetti ha molti detrattori, la targa "object oriented" aiuta a vendere il software e questo potrebbe andar bene per tutti.

Comunque, Lua non è un linguaggio object oriented nel vero senso della parola, ma le tabelle potrebbero avere in qualche modo un simile comportamento a quello degli oggetti. Cioè, è possibile esprimere l'incapsulamento dei dati e l'ereditarietà (anche quella multipla) tramite meccanismi esprimibili in Lua. Sinceramente, oltre la chiara dimostrazione della flessibilità di Lua ad emulare la programmazione ad oggetti, non trovo nessun vantaggio ad utilizzare questi meccanismi. Ecco un semplice esempio che illustra come chiamare un metodo di un rudimentale oggetto:

conto = {deposito = 1000}
function conto:ritira (somma)
  self.deposito = self.deposito - somma
end
a = conto
a:ritira(100.00)
print(a.deposito) -> 900

Ci sono diversi esempi sul manuale di Lua che illustrano come scrivere codice ad oggetti in questo linguaggio.

Il compilatore lua

Come tutti i linguaggi di scripting, una script lua è eseguibile da codice sorgente e questo costituisce un vero problema per chi vuole distribuire le proprie applicazioni senza permettere all'utente di curiosare nel codice sorgente oppure modificarlo. Una soluzione praticabile ma poco efficace è quella di offuscare il codice.

Lua risolve questo problema fornendo una piccolissima utility chiamata "luac". Non è un vero compilatore come lo intendiamo, cioè che traduce il codice sorgente di un programma in codice eseguibile ma trasforma semplicemente il codice lua in codice criptato rendendolo assolutamente illeggibile.

Il codice lua "compilato" non guadagna e non perde nulla in termini di prestazioni rispetto a quello sorgente.

Estendere un'applicazione in Lua

Ci sono tante cose veramente interessanti da dire su Lua, ma l'essenza del linguaggio rimane quella di scrivere delle script che interagiscono con applicazioni compilate scritte in altri linguaggi, tipicamente in C/C++ ed Object Pascal (Delphi, Kylix e Free Pascal). Pensate quanto sia utile ed interessante per l'utente personalizzare ed estendere un'applicazione senza disporre di codice sorgente. Non solo. Grazie a Lua è possibile scrivere un'applicazione di tipo "kernel" intorno alla quale sviluppare del software specializzato mediante componenti in lua. In effetti, questo è l'utilizzo più comune di Lua. Basta andare sul sito dedicato al linguaggio e vedere quanti progetti sono stati realizzati in questa maniera.

Il progetto Kepler, per esempio (http://www.keplerproject.org/), è una piattaforma per lo sviluppo web scritta in C ma con componenti in lua. Ci sono anche alcuni componenti scritti in Lua che interagiscono con la nota libreria grafica OpenGL e che potrebbero servire come base per nuovi progetti; LuaGL (http://luagl.wikidot.com/) e Doris (http://doris.sourceforge.net/), vedi immagine (doris.bmp), sono dei tipici esempi.

Sul sito dedicato allo sviluppo dei giochi (http://www.gamedev.net/) c'è una lunga introduzione a Lua. Ecco un piccolo esempio scritto in C che chiama una funzione esterna scritta in Lua.

-- funzione lua: somma due numeri
function add ( x, y )
	return x + y
end

/* codice C: programma chiamante */
#include 

extern "C" {
	#include "lua.h"
	#include "lualib.h"
	#include "lauxlib.h"
}

/* comunica con Lua */
lua_State* L;

int luaadd ( int x, int y )
{
	int sum;

	/* nome della funzione */
	lua_getglobal(L, "add");

	/* primo argomento */
	lua_pushnumber(L, x);

	/* secondo argumento */
	lua_pushnumber(L, y);

	/* chiama la funzione con 2 parametri ma un unico risultato */
	lua_call(L, 2, 1);

	/* ecco il risultato */
	sum = (int)lua_tonumber(L, -1);
	lua_pop(L, 1);

	return sum;
}

int main ( int argc, char *argv[] )
{
	int sum;
	
	/* inizializza Lua */
	L = lua_open();

	/* carica la libraria di base */
	lua_baselibopen(L);

	/* carica la script */
	lua_dofile(L, "add.lua");
	
	/* chiama la funzione */
	sum = luaadd( 12, 13 );

	/* print the result */
	printf( "La somma è %d\n", sum );

	/* pulisce lo stack */
	lua_close(L);

	return 0;
}

Compilando il codice C ed eseguendo il programma vedremo stampato sullo schermo: la somma è 25

Per chi programma in Delphi oppure Free Pascal c'è la unit chiamata "lua.pas" che è una traduzione alla lettera degli header file in C. Essendoci una totale corrispondenza tra il codice C e quello in Pascal, è possibile utilizzare qualsiasi codice lua da Delphi e Free Pascal, ma è possibile anche il contrario.

Una testimonianza dell'integrazione tra Delphi e Lua è l'ottimo ambiente di sviluppo chiamato LuaEdit (http://luaedit.luaforge.net/index.html) interamente scritto in Delphi che, come suggerisce il nome, serve per scrivere e testare script in Lua in modo interattivo (vedi immagine "luaedit.bmp").

La sindrome del gigantismo

È un fenomeno che affligge ormai la maggior parte dei linguaggi di programmazione più utilizzati e non risparmia nemmeno quelli di scripting che, di natura e concezione, dovrebbero essere snelli e leggeri. Questi linguaggi nati piccoli e con funzionalità essenziali si sono col tempo ingigantiti fino a diventare dei veri mostri indomabili. Pensate a Java e al C++, due dei linguaggi più utilizzati.

Sono in molti a credere che il C è l'unico dal futuro garantito solo per il fatto che è rimasto piccolo e ben circoscritto malgrado i suoi ben noti problemi.

Per ironia della sorte, è doveroso citare Ada che dal punto di vista ingegneristico è molto più avanzato, armonioso ed elegante di Java e del C++ ma non ha mai avuto successo. Probabilmente, per il fatto che è nato così gigante e complesso, Ada spaventa i neo-arrivati da farli scappare. Oggi Ada è più piccolo e meno complesso di Java e del C++!

Lua sembra finora indenne dal questo nefasto fenomeno. In effetti, l'ultima versione di Lua (la 5.1.2), datata Aprile 2007, è solo poco più grande della prima (la 1.0) datata Luglio 1993. In pratica, durante i suoi quattordici anni di evoluzione, Lua ha subito solo poche migliorie ed estensioni ma molte pulizie del codice.

Grazie a queste promesse, rigorosamente mantenute, Lua oggi è estremamente robusto, veloce e piccolo da essere considerato il linguaggio universale di estensione soprattutto quando si tratta di software ludico. Conclusione Imparare un nuovo linguaggio di programmazione è stato sempre utile, interessante e a volte anche divertente. Ma quando la conoscenza di un nuovo linguaggio ci aiuto anche a migliorare la nostra attività lavorativa, allora diventa un vero investimento. C'è comunque sempre un prezzo da pagare in termini di tempo, danaro, energia e forse anche di salute, prima di diventare realmente produttivi con un nuovo linguaggio. Il tutto diventa poi proporzionale alla complessità del linguaggio e la validità degli ambienti di sviluppo disponibili.

Credo che Lua sia il linguaggio che offre infiniti benefici ma al miglior prezzo. È veramente il caso di ricordare che "piccolo è bello". Almeno in questo caso.

Sito ufficiale di Lua: www.lua.org