Escolar Documentos
Profissional Documentos
Cultura Documentos
Programação em Lua
Programação em Lua
Abstract
Lua is a scripting language widely used in several areas, from desktop applications, such as Adobe
Photoshop Lightroom, to software for embedded systems. It is the leading language for scripting
games and it is part of Ginga, the standard middleware for the Brazilian Digital TV system. Lua is also
frequently used in security, being the scripting language embedded in tools like Wireshark, snort, and
nmap.
This text presents the Lua language emphasizing its unconventional mechanisms. The goal is to
introduce the language together with some non-conventional programming techniques, such as higherorder functions, coroutines, and APIs between languages. The text assumes some programming
maturity from the reader and some knowledge about the C language, for the discussion about the
LuaC API.
Resumo
Lua uma linguagem de script amplamente usada nas mais diversas reas, desde grandes
aplicativos para desktops, como o Adobe Photoshop Lightroom, at software para sistemas embarcados. Lua a linguagem mais usada atualmente para scripting em jogos, e parte do padro Ginga
para o Sistema Brasileiro de TV Digital. Lua tambm muito usada na rea de segurana, sendo a
linguagem de script embutida em ferramentas como Wireshark, snort e nmap.
Este texto apresenta a linguagem Lua com nfase nos seus mecanismos menos convencionais.
O objetivo introduzir a linguagem e ao mesmo tempo apresentar algumas tcnicas de programao
no convencionais, como o uso de funes de mais alta ordem, co-rotinas e APIs entre linguagens.
Espera-se do leitor alguma maturidade na rea de programao e conhecimento da linguagem C,
para a discusso da API entre Lua e C.
3.1. Introduo
O objetivo deste texto introduzir o leitor programao na linguagem Lua.
Assumimos que o leitor (voc) possui uma certa maturidade em programao
com alguma linguagem qualquer.
1
R. Ierusalimschy
Programar em Lua no muito diferente de programar em outras linguagens dinmicas, mas diferente. Cada linguagem apresenta caractersticas
prprias, e um bom programador sabe explorar as caractersticas particulares
de cada linguagem. Neste texto, vamos procurar enfatizar as particularidades
de Lua, aspectos que tornam a programao em Lua diferente da programao
em outras linguagens dinmicas. Em particular, em Lua, temos como importantes diferenciais o uso de tcnicas de programao funcional, o uso ubquo
de tabelas como estruturas de dados para os mais variados fins, o uso de corotinas e a comunicao com cdigo escrito em C.
Bom, ento programar em Lua no to diferente de programar em outras
linguagens dinmicas. Mas afinal, o que uma linguagem dinmica? Como
ocorre frequentemente em computao, esse termo no possui uma definio
precisa e universalmente aceita. Mas existe um certo consenso de que linguagens dinmicas apresentam as seguintes caractersticas:
Interpretao dinmica: isso significa que a linguagem capaz de executar
trechos de cdigo criados dinamicamente, no mesmo ambiente de execuo do programa. Como exemplos dessa facilidade temos a funo
loadstring em Lua e a funo eval em Scheme/Lisp e Perl.
Tipagem dinmica forte: tipagem dinmica significa que a linguagem faz verificao de tipos em tempo de execuo do programa. Linguagens com
tipagem dinmica em geral no possuem declaraes de tipos no cdigo
e no fazem verificao de tipos em tempo de compilao. Tipagem
forte significa que a linguagem jamais aplica uma operao a um tipo
incorreto.
Gerncia automtica de memria dinmica (coleta de lixo): isso significa que
no precisamos gerenciar memria explicitamente no nosso programa;
em especial, no h necessidade de um comando para liberar memria
aps seu uso.
Em geral, linguagens dinmicas so interpretadas, e no compiladas para
cdigo nativo da mquina; mas essa uma caracterstica das implementaes
dessas linguagens, no das linguagens em si. Obviamente, as caractersticas acima favorecem uma implementao via um interpretador e dificultam a
construo de compiladores.
Dessas caractersticas, a interpretao dinmica a mais exclusiva de linguagens dinmicas. Obviamente, em qualquer linguagem Turing-completa podemos escrever um interpretador para a prpria linguagem, mas os trechos de
cdigo interpretados no sero executados no mesmo ambiente do programa
interpretador. Por exemplo, podemos escrever um interpretador para C em C,
mas os programas interpretados no tero acesso s variveis e funes declaradas no programa compilado onde o interpretador est sendo usado.
Apesar de no ser uma caracterstica exclusiva de linguagens dinmicas, a
gerncia automtica de memria um mecanismo importante dessa lista, por
haver uma enorme diferena entre programarmos em uma linguagem com e
2
Programao em Lua
em uma linguagem sem gerncia automtica de memria. Mesmo na programao em ponto grande (programming in the large) a gerncia automtica de
memria tem um impacto significativo, ao simplificar as interfaces entre componentes. (Como um exerccio, pegue a API de qualquer biblioteca C de porte
razovel e verifique quanto de sua complexidade devida gerncia de memria.)
Na verdade, existe um contnuo entre linguagens estticas e dinmicas. Por
exemplo, Java uma linguagem muito mais dinmica do que C, pois apresenta
gerncia automtica de memria, um certo grau de tipagem dinmica e um mecanismo embrionrio de interpretao dinmica (por meio da carga dinmica
de classes, que por sua vez podem ser criadas dinamicamente). Mesmo entre
as linguagens reconhecidamente dinmicas existem diferenas. Por exemplo,
nem todas as linguagens dinmicas tm gerncia automtica de memria sobre mdulos ou classes.
Lua se destaca de outras linguagens dinmicas por ser uma linguagem de
script. Uma linguagem de script uma linguagem projetada para controlar e
coordenar componentes geralmente escritos em outra linguagem. As primeiras linguagens de script foram as linguagens de shell do Unix, usadas para
conectar e controlar a execuo de programas. Apesar de vrias linguagens
dinmicas poderem ser usadas para script, poucas foram projetadas para essa
finalidade. Lua seguiu um caminho criado por Tcl [Ousterhout 1990], onde a
linguagem estruturada como uma biblioteca C com uma API que permite
tanto cdigo na linguagem chamar funes escritas em C como cdigo C chamar funes escritas na linguagem. Lua se destaca de outras linguagens de
script por sua simplicidade, portabilidade, economia de recursos e desempenho [Ierusalimschy et al. 2007].
http://luaforwindows.luaforge.net/
R. Ierusalimschy
imprimir 2:
print(2^(1/2))
--> 1.4142135623731
Programao em Lua
--> 1
Cada uma das linhas executada como um trecho (chunk, em ingls) separado, mas o valor da varivel global x se mantm aps o primeiro trecho ter
terminado.
R. Ierusalimschy
A palavra reservada local declara uma varivel local, cujo escopo vai
da declarao at o fim do bloco mais interno que contm a declarao.
No exemplo, sum visvel at o fim da funo add.
A expresso #a retorna o comprimento do array a. Como arrays em Lua
comeam no ndice 1, o comprimento tambm o valor do ltimo ndice.
O comando for vai repetir seu corpo com o valor da varivel i variando de 1 at o comprimento do array (#a). A varivel de controle i
declarada pelo prprio comando for e s visvel dentro do seu corpo.
Todas as estruturas de controle tm um terminador explcito. Tanto o
corpo da funo quanto o corpo do for terminam com a palavra reservada end.
Casamento de prefixos
Um problema comum em vrias reas , dada uma lista de palavras, decidir
se uma dada string prefixo de alguma das palavras da lista. Por exemplo, muitos sistemas de linha de comando permitem que entremos com um comando
digitando apenas os primeiros caracteres do nome do comando.
Uma soluo usual para esse problema construir uma tabela de prefixos,
que mapeia todos os prefixos de cada palavra na lista para a palavra completa.
Dada essa tabela, o problema original resolvido com uma simples consulta.
6
Programao em Lua
A funo na Figura 3.1 recebe uma lista (array) de palavras e retorna sua
tabela de prefixos. Nesse cdigo temos novamente vrias novidades:
A expresso {} cria uma tabela vazia, que atribuida varivel local t.
O lao externo usa o gerador ipairs, que percorre todos os ndices
e valores do array dado (list). Os ndices so atribudos primeira
varivel (que nomeamos _, j que no estamos interessados no seu
valor), e os valores segunda varivel (name).
A funo string.sub retorna uma substring de uma dada string (name,
no caso). Assim como arrays, caracteres em strings so indexados a
partir de 1. Em particular, a chamada como feita no exemplo vai retornar
um prefixo de name com comprimento len.
Na condio do if, usamos o fato de que o valor nil, que o valor
de campos no inicializados em uma tabela, equivalente a falso em
qualquer condio. Assim, o que est sendo testado se o campo da
tabela com chave prefix j foi preenchido anteriormente.
No caso do teste dar positivo (isto , a tabela j ter um elemento com a
dada chave), a funo coloca o valor true na posio j ocupada. Como
este valor no uma string, ele serve como uma marca para colises.2
Aps a construo da tabela de prefixos, seu uso bem simples. Dado um
2
Outra opo seria colocar na posio do conflito uma lista com todas as possveis palavras para o dado prefixo.
R. Ierusalimschy
-->
-->
-->
-->
5
7
false
nil
-->
-->
-->
-->
"b"
nil
nil
false
O operador not, entretanto, sempre retorna um booleano. Em particular, a expresso not not x normaliza o valor de x para um booleano correspondente.
8
Programao em Lua
Um exemplo de uso dessas propriedades a expresso a seguir, que calcula o mximo entre x e y:
x >= y and x or y
Voc est convidado a explicar como ela funciona.
R. Ierusalimschy
dessa declarao criar uma funo dinamicamente e atribu-la a uma varivel global.
Programao em Lua
O exemplo a seguir ilustra uma funo que recebe uma string e retorna
como resultados todos os prefixos da string:
function prefixes (s, len)
len = len or 0
if len <= #s then
return string.sub(s, 1, len),
prefixes(s, len + 1)
end
end
print(prefixes("alo"))
-->
al
alo
R. Ierusalimschy
Para desenvolver esse sistema, podemos comear pesquisando quais estruturas de dados se adequam melhor ao problema. Ou podemos subir o nvel
de abstrao e ignorar as estruturas de dados, representando uma regio diretamente por meio de sua funo caracterstica. Mais especificamente, uma
regio pode ser representada por uma funo que, dado um ponto, retorna verdadeiro se e somente se o ponto pertence regio. Como exemplo, a funo
a seguir representa uma regio circular com centro (1.0, 3.0) e raio 4.5:
return function (x, y)
return (x - 1.0)^2 + (y - 3.0)^2 <= 4.5^2
end
Essa funo bastante simples: ela apenas verifica se a distncia do ponto
dado ao centro do crculo menor ou igual ao raio.
Podemos ser mais abstratos e definir uma funo para criar regies circulares:
function circle (cx, cy, r)
return function (x, y)
return (x - cx)^2 + (y - cy)^2 <= r^2
end
end
Essa funo apenas retorna uma funo que representa a regio circular com
centro e raio dados.
Para o funcionamento correto desse tipo de funo, fundamental que a
linguagem oferea visibilidade lxica. No nosso caso, a funo que representa
o crculo deve ser capaz de acessar as variveis locais funo externa (cx,
cy e r) mesmo aps esta ter retornado.
Para adicionarmos novos formatos, como reas retangulares ou triangulares, no h necessidade de nenhuma modificao no sistema; basta escrevermos as funes adequadas. Mas o mais interessante como podemos
modificar e combinar regies. Por exemplo, dada qualquer regio g, a funo
a seguir cria uma nova regio que o complemento de g:
function complement (g)
return function (x, y) return not g(x, y) end
end
Unio, interseo e diferena de duas regies so igualmente simples; vide
Figura 3.2. Alm de operaes sobre conjuntos, tambm podemos definir vrios outros tipos de transformaes grficas. Por exemplo, a funo a seguir
12
Programao em Lua
Esse formato tambm bastante ineficiente, mas aqui nossa nfase a simplicidade.
13
R. Ierusalimschy
Programao em Lua
R. Ierusalimschy
Figura 3.5. Uma implementao para drawPolygon sobre uma interface convencional.
Programao em Lua
--> 0.125
Lembre-se que cada linha em modo interativo tratado como um trecho completo.
Em Lua, a definio de funes uma operao feita em tempo de execuo, no em tempo de compilao. Afinal, ela apenas acar sinttico para
uma atribuio. Assim, quando pr-compilamos um trecho de cdigo contendo
definies, essas definies no so vlidas at executarmos a funo resultante da compilao. Veja o exemplo a seguir:
f = loadstring("function foo (x) print(10*x) end")
print(foo)
--> nil
f()
-- executa trecho
print(foo)
--> function: 0x807ad58
foo(10)
--> 100
17
R. Ierusalimschy
Mais especificamente, a funo loadstring nunca gera nenhum efeito colateral. Seu nico efeito retornar uma nova funo correspondente ao trecho
de cdigo passado como parmetro. Qualquer efeito do trecho s executado
quando (e se) chamamos a funo retornada.
--> 10
20
Note que, nessa sintaxe, s podemos usar como nomes de campos identificadores vlidos na linguagem. Por exemplo, a expresso t.or invlida, pois
or uma palavra reservada em Lua. Obviamente, a sintaxe bsica t["or"]
sempre vlida.
18
Programao em Lua
3.5.1. Construtores
A expresso para criar uma tabela vazia, {}, na verdade um caso particular de um construtor. Construtores oferecem uma sintaxe bastante rica para
a criao e inicializao de tabelas em Lua. Existem basicamente trs tipos de
construtores: um para listas, outro para estruturas e um genrico.
O construtor de listas tem a forma a seguir:
{exp1, exp2, exp3, ...}
Essa expresso cria uma tabela com o valor de exp1 associado ao ndice 1,
exp2 associado ao ndice 2, e assim por diante. O trecho a seguir ilustra um
uso simples desse tipo de construtor:
dia = {"domingo", "segunda", "tera", "quarta",
"quinta", "sexta", "sbado"}
print(dia[5])
--> quinta
O construtor de estruturas tem a forma a seguir:
{nome1 = exp1, nome2 = exp2, nome3 = exp3, ...}
Essa expresso cria uma tabela com o valor da expresso exp1 associado
string "nome1", exp2 associado string "nome2", e assim sucessivamente.
O trecho a seguir ilustra um uso simples desse tipo de construtor:
point = {x = 10.5, y = -15.34}
print(point.x)
--> 10.5
O construtor genrico tem a forma a seguir:
{[e1] = exp1, [e2] = exp2, [e3] = exp3, ...}
Essa expresso cria uma tabela com o valor de exp1 associado ao valor da expresso e1, exp2 associado ao valor da expresso e2, e assim por diante. Em
particular, qualquer construtor de listas pode ser reescrito na forma a seguir:
{ [1] = exp1, [2] = exp2, [3] = exp3, ...}
De forma similar, construtores de estruturas tambm podem ser reescritos
usando-se o construtor genrico:
{ ["nome1"] = exp1, ["nome2"] = exp2, ...}
(Certifique-se que voc entendeu por que essas equivalncias esto corretas.)
Apesar de sua genericidade, o construtor genrico menos usado que
os dois anteriores, pois a maior parte dos usos tpicos se encaixa naqueles
padres. Uma situao particular onde ele til quando chaves do tipo string
19
R. Ierusalimschy
3.5.2. Arrays
Como j comentamos, em Lua representamos arrays diretamente como
tabelas, usando nmeros inteiros positivos como ndices.
O uso de tabelas como arrays traz diversos benefcios. Por exemplo, Lua
manipula arrays esparsos4 de forma bastante eficiente, sem necessidade de
algoritmos especiais. Se temos uma tabela t vazia, uma atribuio como
t[1000000000]=1 insere apenas um elemento na tabela, o par com chave
1000000000 e valor 1. (O mesmo programa em Perl d erro de Out of memory!) Alm disso, todas as operaes oferecidas pela linguagem para tabelas se estendem naturalmente para arrays.
Quando manipulamos arrays, frequentemente necessrio sabermos seu
tamanho. Em algumas linguagens estticas o tamanho de um array parte
do tipo do array (e.g., Pascal); em outras, responsabilidade do programador
saber o tamanho (e.g., C). Em linguagens mais dinmicas, comum existir
um operador para se consultar o tamanho de um dado array. Como j vimos,
Lua oferece o operador de comprimento (o operador prefixado #), que quando
aplicado a um array retorna o seu comprimento. Mas o que o comprimento
de um array em Lua?
4
20
Programao em Lua
Como arrays so na verdade tabelas, o conceito de comprimento (ou tamanho) no claro em todos os casos. Por exemplo, qual o tamanho de um
array esparso? Seu nmero de elementos? Seu ltimo ndice? Para simplificar essas questes, Lua define o comportamento do operador de comprimento
apenas para arrays bem comportados. Se os nicos ndices numricos presentes em uma tabela t so inteiros consecutivos de 1 at algum n, ento esse n
o resultado da expresso #t; se a tabela no tem nenhum ndice numrico, ento o resultado da expresso #t zero. Em todos os outros casos, o resultado
de #t no definido univocamente.
Em particular, arrays com buracos, isto , com elementos com valor nil,
no so bem comportados. Considere o construtor a seguir:
{10, 20, nil, 40}
Para ns, pode parecer bvio que queremos um array de quatro elementos,
onde o terceiro tem valor nil. Mas para Lua, entretanto, no existe um terceiro elemento. No claro se o array termina neste nil e por acaso tem um
outro elemento desrelacionado no ndice 4. O operador de comprimento, se
aplicado ao resultado desse construtor, pode retornar 2 ou 4. O ideal evitar
esses casos, usando o valor false em vez de nil na tabela. Se isso no for
possvel, deve-se usar algum artifcio para indicar o tamanho do array, como
por exemplo armazenar esse tamanho explicitamente:
{10, 20, nil, 40, n = 4}
Para arrays bem comportados, o operador # bastante til. A atribuio a
seguir ilustra uma construo idiomtica muito comum em Lua:
t[#t + 1] = v
Ela anexa o valor v ao final da lista t. De forma anloga, a atribuio a seguir
apaga o ltimo elemento de uma lista:
t[#t] = nil
R. Ierusalimschy
local t = io.read("*all")
local count = {}
for w in string.gmatch(t, "%w+") do
count[w] = (count[w] or 0) + 1
end
local words = {}
for w in pairs(count) do
words[#words + 1] = w
end
table.sort(words, function (a,b)
return count[a] > count[b]
end)
for i=1, (arg[1] or 10) do
print(words[i], count[words[i]])
end
22
Programao em Lua
Algumas pessoas se confundem com essa ideia: afinal, se listas tambm so tabelas,
como orden-las? Realmente listas tambm no tm nenhuma ordem interna, mas suas
chaves, inteiros positivos, tm uma ordenao natural independente da lista. Em outras palavras, do ponto de vista de Lua uma lista no tem ordem, mas definimos que o
elemento associado chave n o n-simo elemento.
23
R. Ierusalimschy
percorrer o arquivo linha a linha, e para cada linha percorrer suas palavras:
local count = {}
for line in io.lines() do
for w in string.gmatch(lines, "%w+") do
count[w] = (count[w] or 0) + 1
end
end
3.5.4. Mdulos
A combinao de tabelas com funes de primeira classe bastante poderosa. Lua no oferece nenhum mecanismo especfico para a construo de
mdulos, pois mdulos em Lua podem ser diretamente implementados como
tabelas.
Quase todas as bibliotecas padro de Lua so implementadas como mdulos via tabelas. Nesse texto j usamos vrias funes dessas bibliotecas. Por
exemplo, a funo string.sub definida na biblioteca de manipulao de
strings. Essa biblioteca exporta todas as suas funes dentro de uma tabela,
armazenada na varivel global string. Quando escrevemos string.sub,
isso nada mais que uma indexao convencional:
print(type(string))
print(type(string.sub))
--> table
--> function
24
Programao em Lua
3.5.5. Objetos
Programao orientada a objetos (OO) em Lua tambm se vale da combinao de tabelas com funes de primeira classe. Em Lua, um objeto meramente uma tabela, contendo campos com seus dados (variveis de instncia)
e operaes (mtodos).
O exemplo a seguir ilustra uma primeira abordagem com um objeto bem
simples:
Rectangle = {x = 0, y = 0, width = 10, height = 20}
function Rectangle.area ()
return Rectangle.width * Rectangle.height
end
Uma dificuldade bvia com esse esquema que o mtodo area s opera
para esse retngulo particular. Para o mtodo ser til para qualquer retngulo,
podemos incluir o objeto como parmetro do mtodo:
function Rectangle.area (self)
return self.width * self.height
end
Agora, para chamarmos o mtodo, temos que passar o objeto como argumento, o que no muito conveniente:
print(Rectangle.area(Rectangle))
25
R. Ierusalimschy
Para resolver esse inconveniente, Lua oferece o operador de dois pontos (colon
operator ). Com esse operador, podemos reescrever a chamada anterior da
seguinte forma:
print(Rectangle:area())
O operador de dois pontos automaticamente insere o receptor do mtodo como
um primeiro argumento adicional na chamada.
De forma anloga, tambm podemos usar o operador de dois pontos na
definio do mtodo, como ilustrado a seguir:
function Rectangle:area ()
return self.width * self.height
end
Nesse caso, o operador insere automaticamente um primeiro argumento adicional na definio do mtodo, com o nome self.
O uso do parmetro self, mais o suporte sinttico do operador de dois pontos, possibilita que um mesmo mtodo possa operar sobre vrios objetos semelhantes. Mas como criamos esses objetos? Em particular, partindo da definio
de Rectangle, como podemos criar outros retngulos?
Uma opo seria copiarmos todos os campos do objeto original nos novos
objetos; isto , clonarmos o objeto original. Essa opo no muito convidativa quando o objeto oferece muitos mtodos. Uma outra opo, bem mais
interessante, exige um novo mecanismo de Lua, chamado delegao, que foi
inspirado na linguagem Self [Ungar et al. 1987].
Delegao permite que uma tabela herde campos de outra tabela. Mais
especificamente, suponha que uma tabela A delega sua indexao para outra tabela B. Se indexamos A com uma chave presente, o valor associado
retornado normalmente. Mas se indexamos A com uma chave ausente, Lua
automaticamente ir procurar essa chave na tabela B.
Para construirmos uma relao de delegao entre A e B precisamos de
uma tabela intermediria, chamada de metatabela de A.7 Para a tabela A delegar suas buscas para B, o campo __index de sua metatabela deve referenciar
B. O exemplo a seguir deve ajudar a clarificar essas relaes:
A = {x = 10}
B = {x = 20, y = 30}
mt = {__index = B}
print(A.x, A.y)
setmetatable(A, mt)
print(A.x, A.y)
7
-- metatabela
--> 10
nil
--> 10
30
26
Programao em Lua
--> 10
30
Na Figura 3.7 juntamos tudo que temos at agora para definir novamente
um objeto Rectangle. Desta vez, esse objeto vai oferecer um mtodo new
para criar novos retngulos, e usar delegao para que esses novos retngulos
possam usar os mtodos definidos no objeto original.
Vamos acompanhar, em detalhes, o que ocorre quando Lua executa as
duas ltimas linhas desse fragmento. Na primeira, o programa cria uma tabela
e a passa como argumento para a funo Rectangle.new. Devido notao
de dois pontos tanto na chamada quanto na sua definio, essa funo recebe
27
R. Ierusalimschy
28
Programao em Lua
--> 20
--> 40
--> 1
--> 2
--> 10
--> fim
29
R. Ierusalimschy
Programao em Lua
Essa funo se vale de uma sutileza das regras de escopo de Lua. Note que a varivel
local a, declarada dentro do lao, ainda visvel na sua condio.
31
R. Ierusalimschy
Programao em Lua
ping = coroutine.wrap(function ()
while true do
print(ping)
transfer(pong)
end
end)
pong = coroutine.wrap(function ()
while true do
print(pong)
transfer(ping)
end
end)
transfer(ping)
R. Ierusalimschy
Programao em Lua
R. Ierusalimschy
#include "lua.h"
#include "lauxlib.h"
int main (int argc, char **argv) {
lua_State *L = luaL_newstate();
if (luaL_loadfile(L, argv[1]) != LUA_OK)
fprintf(stderr, "error: %s\n",
lua_tostring(L, -1));
else if (lua_pcall(L, 0, 0, 0) != LUA_OK)
fprintf(stderr, "error: %s\n",
lua_tostring(L, -1));
else {
lua_getglobal(L, "result");
printf("resultado: %f\n", lua_tonumber(L, -1));
}
lua_close(L);
return 0;
}
Programao em Lua
amos um novo estado Lua ele est completamente vazio, sem nenhuma global definida. Nem mesmo as bibliotecas padro esto abertas. Para abri-las,
precisamos chamar a funo luaL_openlibs, definida em lualib.h. Essa
chamada vai povoar o estado Lua com todas as funes das bibliotecas padro
da linguagem.
A necessidade de chamar luaL_openlibs em separado novamente explicada pela nfase em flexibilidade da API de Lua. Muitos sistemas usam Lua
sem oferecer todas as bibliotecas. Por exemplo, sistemas embarcados raramente podem implementar a biblioteca de entradasada, por no terem um
sistema de arquivos. Outros sistemas podem no oferecer a biblioteca matemtica para economizar espao. Tais sistemas podem usar Lua sem nenhuma
modificao, bastando no usar a funo luaL_openlibs.
A funo luaL_openlibs, assim como todas as funes da biblioteca
padro, no faz nada mgico para estender Lua. Ela apenas usa a API Lua
C. Vamos ver agora, por meio de um exemplo, como estender Lua com uma
nova funo.
Uma funo C, para poder ser chamada por Lua, deve respeitar um protocolo especfico. No nvel de C, a funo deve seguir o prottipo abaixo:
typedef int (*lua_CFunction) (lua_State *L);
Isso , ela deve receber um nico parmetro, que o estado Lua onde ela ir
operar, e retornar um inteiro, que o nmero de valores que ela est retornando
no nvel Lua. No nvel de Lua, ela recebe seus parmetros na pilha e retorna
seus resultados tambm na pilha.
Cada funo tem sua prpria pilha local. Quando a funo chamada, a
sua pilha contm apenas os parmetros para a funo, com o primeiro parmetro na base da pilha. Ao retornar, os valores no topo da pilha so os resultados
da funo, com o ltimo resultado no topo. O inteiro retornado no nvel C indica quantos desses valores na pilha so resultados. Por exemplo, se a funo
retorna zero no nvel C, significa que ela no est retornando nenhum valor no
nvel Lua, independente de quantos valores esto empilhados. Se ela retorna
um no nvel C, apenas o valor no topo da pilha considerado valor de retorno.
Esse esquema facilita a codificao das funes, pois no h necessidade de
se limpar a pilha de outros valores ao retornar.
Como exemplo, na Figura 3.12 temos a implementao da funo de seno,
copiada diretamente da biblioteca matemtica de Lua. A funo math_sin
basicamente um encadeamento de trs funes: luaL_checknumber, sin,
da biblioteca matemtica de C, e lua_pushnumber.
A funo auxiliar luaL_checknumber verifica se o primeiro elemento da
pilha realmente um nmero e retorna seu valor; observe o uso do ndice positivo 1 na sua chamada, para indexar o elemento na base da pilha. Essa funo
auxiliar construda sobre a lua_tonumber, que j vimos anteriormente, mas
usa antes a funo lua_isnumber para se certificar que o parmetro realmente um nmero. Em caso negativo ela chama uma funo de erro que, por
37
R. Ierusalimschy
meio de um long jump, salta diretamente para um tratador de erros, sem retornar. Assim, quando luaL_checknumber retorna sabemos que o parmetro
tem o tipo correto.
A funo lua_pushnumber, como seu nome indica, empilha um nmero;
no nosso caso, esse nmero o resultado da chamada funo sin. Esse
nmero ser empilhado por cima do parmetro original, que permanece na
pilha. A funo math_sin termina retornando 1, o que indica para Lua que
apenas o valor do topo (o seno) deve ser considerado como resultado.
Aps definirmos a funo math_sin, precisamos registr-la em Lua. A
parte principal do trabalho feita pela funo lua_pushcfunction: ela recebe um ponteiro para uma funo C e empilha um valor Lua do tipo function
que, quando chamado, invoca a funo C correspondente. Como qualquer funo em Lua, a funo empilhada por lua_pushcfunction um valor de primeira classe. Podemos por exemplo armazen-la em uma varivel global, para
uso futuro. O trecho de cdigo a seguir ilustra como podemos modificar nosso
programa da Figura 3.11 para registrar nossa funo em uma global sin:
int main (int argc, char **argv) {
lua_State *L = luaL_newstate();
lua_pushcfunction(L, math_sin);
lua_setglobal(L, "sin");
/* ... como antes ... */
Aps a criao do estado Lua, lua_pushcfunction cria a funo no topo da
pilha e lua_setglobal armazena o valor do topo da pilha na global nomeada
(sin). Aps essas chamadas, qualquer programa executado no estado L ter
acesso funo sin.
Programao em Lua
vrios conceitos bsicos novos para podermos chegar a tpicos mais avanados. Tambm no abordei as bibliotecas padro de Lua; algumas so convencionais, como a biblioteca de manipulao de arquivos. Mas outras, como a
biblioteca de strings com suas funes de casamento de padres, merecem
um tratamento mais longo. Espero que o leitor, aps este texto introdutrio,
sinta-se motivado a aprofundar seus conhecimentos sobre Lua.
Referncias bibliogrficas
[ABNT 2007] ABNT (2007). Televiso digital terrestre Codificao de dados
e especificaes de transmisso para radiodifuso digital. Associao Brasileira de Normas Tcnicas. ABNT NBR 15606-2.
[Bentley et al. 1986] Bentley, J., Knuth, D., and McIlroy, D. (1986). Programming pearls: a literate program. Communications of the ACM, 29(6):471
483.
[de Moura and Ierusalimschy 2009] de Moura, A. L. and Ierusalimschy, R.
(2009). Revisiting coroutines. ACM Transactions on Programming Languages and Systems, 31(2):6.16.31.
[de Moura et al. 2004] de Moura, A. L., Rodriguez, N., and Ierusalimschy,
R. (2004). Coroutines in Lua. Journal of Universal Computer Science,
10(7):910925.
[Eckart 1987] Eckart, J. D. (1987). Iteration and abstract data types. SIGPLAN
Notices, 22(4):103110.
[Hudak and Jones 1994] Hudak, P. and Jones, M. P. (1994). Haskell vs. Ada
vs. C++ vs. Awk vs. . . . an experiment in software prototyping productivity.
Technical report, Yale University.
[Ierusalimschy 2006] Ierusalimschy, R. (2006). Programming in Lua. Lua.org,
Rio de Janeiro, Brasil, segunda edio.
[Ierusalimschy et al. 2006] Ierusalimschy, R., de Figueiredo, L. H., and Celes,
W. (2006). Lua 5.1 Reference Manual. Lua.org, Rio de Janeiro, Brasil.
[Ierusalimschy et al. 2007] Ierusalimschy, R., de Figueiredo, L. H., and Celes,
W. (2007). The evolution of Lua. Em Third ACM SIGPLAN Conference on
History of Programming Languages, pginas 2.12.26, San Diego, CA.
[Muhammad and Ierusalimschy 2007] Muhammad, H. and Ierusalimschy, R.
(2007). C APIs in extension and extensible languages. Em XI Brazilian Symposium on Programming Languages, pginas 137150, Natal, RN.
[Ousterhout 1990] Ousterhout, J. (1990). Tcl: an embeddable command language. Em Proc. of the Winter 1990 USENIX Conference. USENIX Association.
[Ungar et al. 1987] Ungar, D. et al. (1987). Self: The power of simplicity. Sigplan Notices, 22(12):227242. (OOPSLA87).
[Wirth 1982] Wirth, N. (1982). Programming in Modula-2. Springer-Verlag.
39