Você está na página 1de 39

Captulo 3

Uma Introduo Programao em Lua


Roberto Ierusalimschy

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].

3.2. Como usar Lua


A linguagem Lua conta com uma nica implementao principal, mantida
pelos autores da linguagem no site www.lua.org, mas essa implementao
conta com diversas distribuies mantidas por desenvolvedores independentes.
Em muitos usos reais de Lua, o interpretador distribuido embutido na aplicao final. Afinal, um dos principais objetivos de Lua exatamente esse tipo
de uso. Nesses casos, detalhes de como usar Lua so dependentes da aplicao. Esses detalhes incluem que editor usar, onde e como armazenar os
programas, como executar um programa, etc. Neste texto, como no estamos
visando nenhuma aplicao particular, vamos usar o interpretador independente (stand alone) de Lua.
Para mquinas Windows, uma tima opo de instalao a distribuio
Lua for Windows (LfW).1 Essa distribuio um pacote completo para Windows, incluindo no apenas o interpretador Lua com suas bibliotecas padro,
1

http://luaforwindows.luaforge.net/

R. Ierusalimschy

mas tambm um editor e vrias bibliotecas extras populares.


Para mquinas Linux no h uma receita pronta, dada a diversidade de
distribuies de Linux. Compilar Lua em uma mquina Linux muito simples
e rpido. Algumas distribuies j vm com Lua instalado por default. Outras
oferecem pacotes prontos: por exemplo, em Ubuntu e Debian, basta instalar
o pacote lua5.1, que o interpretador com as bibliotecas padro. Vrias
bibliotecas externas tambm so oferecidas como pacotes extras. Em qualquer caso, o interpretador independente de Lua um programa de linha de
comando, para ser executado por meio de um terminal.
Para mquinas Mac OS X, existe a opo de compilar diretamente o fonte,
desde que a mquina j tenha as ferramentas de desenvolvimento em C instaladas. O processo simples e rpido como no Linux. Outra opo usar um
gerenciador de pacotes; por exemplo, tanto o MacPorts quanto o Fink oferecem
pacotes prontos para Lua.
Uma vez instalado, muito fcil usarmos o interpretador. Lua no tem o
conceito de uma funo main; qualquer comando passado ao interpretador
imediatamente
executado. O exemplo a seguir um programa completo para

imprimir 2:
print(2^(1/2))

--> 1.4142135623731

O operador ^ o operador de exponenciao em Lua. Lua trabalha sempre


com nmeros reais em ponto flutuante e a exponenciao funciona para expoentes fracionrios (e negativos tambm). Em geral, usamos a notao -->
para indicar o resultado de um comando. Como em Lua dois traos -- iniciam
um comentrio que vai at o final da linha, podemos incluir aquela indicao
no programa.
Ao usar Lua via um terminal, voc tem pelo menos quatro maneiras de
executar esse pequeno programa:
Voc pode usar a opo de linha de comando -e:
$ lua -e "print(2^0.5)"
(Estou assumindo que $ o prompt do terminal.)
Voc pode entrar com o programa em modo interativo:
$ lua
> print(2^0.5)
(O > o prompt do interpretador Lua em modo interativo.)
Voc pode escrever esse programa em um arquivo e execut-lo via linha
de comando:
$ lua nome-do-arquivo

Programao em Lua

Voc pode escrever esse programa em um arquivo e execut-lo via modo


interativo, por meio da funo predefinida dofile:
$ lua
> dofile("nome-do-arquivo")
Podemos dispensar os parnteses em chamadas de funo onde o nico argumento uma string literal. Assim, voc pode reescrever o exemplo anterior
como a seguir:
> dofile"nome-do-arquivo"
Quando voc chama o interpretador, ele cria um estado Lua que persiste
at o fim da sua execuo. Assim, todos os efeitos colaterais de cada comando
se propagam para os prximos comandos, mesmo que eles sejam executados
como programas em separado. Veja o exemplo a seguir:
$ lua
> x = 1
> print(x)

--> 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.

3.3. Alguns Exemplos


primeira vista, Lua uma linguagem imperativa, razoavelmente convencional. Como j discutimos, no vamos perder muito tempo descrevendo
essa parte mais convencional da linguagem. Uma boa parte voc vai aprender apenas vendo os exemplos; se precisar de maiores detalhes, consulte
o manual de referncia [Ierusalimschy et al. 2006] ou o livro Programming in
Lua [Ierusalimschy 2006].
Seguem alguns exemplos de funes simples em Lua, para voc se familiarizar com o bsico da linguagem.

Soma dos elementos de um array


function add (a)
local sum = 0
for i = 1, #a do sum = sum + a[i] end
return sum
end
Algumas observaes sobre o cdigo acima:
Como a linguagem tem tipagem dinmica, no h tipos nas declaraes
de variveis, parmetros, etc.

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.

Soma das linhas de um arquivo


A funo a seguir recebe o nome de um arquivo texto, que deve conter uma
lista de nmeros, e retorna a soma desses nmeros:
function addfile (filename)
local sum = 0
for line in io.lines(filename) do
sum = sum + tonumber(line)
end
return sum
end
Essa funo semelhante do exemplo anterior, com exceo do for. No
exemplo anterior, usamos um for numrico, que iterage sobre uma progresso
aritmtica de nmeros. Neste exemplo, usamos um for genrico, que usa um
gerador (io.lines, no caso, fornecida pela biblioteca padro de Lua) para
gerar os valores da iterao.
Note tambm o uso da funo tonumber, para converter o numeral lido
(uma string) para um nmero. Lua faz esse tipo de converso automaticamente
sempre que uma string usada em uma operao aritmtica, mas consideramos mais educado efetuar a converso explicitamente.

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

function buildPrefixTable (list)


local t = {}
for _, name in ipairs(list) do
for len = 1, #name do
local prefix = string.sub(name, 1, len)
if t[prefix] then
t[prefix] = true
-- colisao
else
t[prefix] = name
end
end
end
return t
end

Figura 3.1. Funo para construir uma tabela de prefixos.

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

prefixo, o cdigo a seguir retorna a palavra completa ou d um erro adequado:


function complete (t, prefix)
local w = t[prefix]
if type(w) == "string" then return w
elseif w == true then error("ambiguous prefix")
else error("invalid prefix")
end
end
A funo predefinida type, quando aplicada a qualquer valor, retorna uma
string com seu tipo. Os valores de retorno possveis so "nil", "number",
"string", "boolean", "table", "function", "thread" e "userdata".
J vimos, pelo menos brevemente, os tipos number, string, table e nil (que o
tipo do valor nil). Iremos abordar os tipos function e thread mais a frente. O
tipo userdata usado para representar objetos externos a Lua (e.g., arquivos).
Isso deixa faltando apenas o tipo boolean.
Como em outras linguagens, o tipo boolean em Lua tem apenas dois valores, true e false. Mas os valores booleanos no tm exclusividade para
testes. Em qualquer teste da linguagem (if, while e mesmo operadores lgicos) os valores nil e false resultam em um teste negativo, e qualquer outro
valor (incluindo true, mas tambm 0, a string vazia, etc.) resulta em um teste
positivo. Antes de sua verso 5.0, Lua nem tinha um tipo booleano. A principal
motivao para a incluso desse tipo na linguagem foi permitir a distino entre variveis com valor falso (false) e variveis no inicializadas (e portanto
com valor nil). Um teste como if not x d positivo nos dois casos, mas um
teste como if x == false s d positivo se o valor de x for false.
Em Lua, assim como em vrias outras linguagens dinmicas, booleanos
no tm exclusividade como resultado de operadores lgicos. O operador or
retorna sempre o valor do primeiro operando que define o valor final da disjuno. A seguir listamos algumas expresses e seus respectivos resultados:
5 or 7
nil or 7
nil or false
false or nil

-->
-->
-->
-->

5
7
false
nil

De forma anloga, o operador and retorna o valor do primeiro operando que


define o valor final da conjuno:
"a" and "b"
nil and "alo"
nil and false
false and 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.

3.4. Programando com Funes


Funes em Lua so valores dinmicos de primeira classe. Em Lua, no
declaramos funes, de forma esttica. Funes so criadas dinamicamente,
durante a execuo de um programa.
Considere o exemplo a seguir:
> (function (a,b) print(a+b) end)(10, 20)
Esse comando cria uma funo para imprimir a soma de seus dois parmetros,
e imediatamente chama essa funo com argumentos 10 e 20. Aps a execuo do comando, no h mais referncias para a funo, e portanto os recursos
usados por ela (memria) sero eventualmente reciclados pelo coletor de lixo.
De modo geral, a sintaxe function (<pars>) <body> end representa
uma expresso que, ao ser executada, cria uma nova funo. Ela equivalente a notao (lambda (<pars) <body>) da linguagem Scheme e a uma
abstrao no -clculo.
Como funes so valores de primeira classe, podemos fazer com elas
tudo que fazemos com outros valores, como nmeros ou strings. Em particular,
podemos armazenar seu valor em uma varivel global:
> foo = function (a,b) print(a+b) end
> foo(10, 20)
Agora, podemos usar a funo armazenada em foo quantas vezes quisermos.
Como bastante comum querermos armazenar funes em variveis globais para uso futuro, Lua oferece um acar sinttico para a atribuio acima.
Podemos escrev-la na forma a seguir:
> function foo (a,b) print(a+b) end
Essa sintaxe exatamente a que estivemos usando at agora para declarar
funes. Ela passa uma aparncia de normalidade linguagem, por ser bastante semelhante declarao de uma funo em outras linguagens. Lembrese, no entanto, que ela nada mais do que acar sinttico. O significado
9

R. Ierusalimschy

dessa declarao criar uma funo dinamicamente e atribu-la a uma varivel global.

3.4.1. Mltiplos Retornos


Outro aspecto pouco convencional de funes em Lua que elas podem
retornar mltipos valores. Considere o exemplo a seguir:
function foo (x) return x, x+1 end
print(foo(3))
--> 3
4
Nesse exemplo, a funo foo retorna dois valores, x e x+1. Na segunda
linha, todos os retornos de foo so passados como argumentos para a funo
print.
Outra forma de se acessar os mltiplos retornos de uma funo com uma
atribuio mltipla:
a, b = foo(10)
Nessa atribuio, a recebe o primeiro valor retornado por foo e b recebe o
segundo.
Em uma atribuio mltipla, Lua no exige que o nmero de valores seja
igual ao nmero de variveis. Se houver mais valores, os valores extras so
descartados; se houver mais variveis, as variveis extras recebem o valor
nil. O mesmo ocorre em uma chamada de funo, em relao aos argumentos fornecidos na chamada e os parmetros esperados pela funo: se
chamamos uma funo com mais argumentos que o necessrio, os argumentos extras so descartados; se houver argumentos faltando, eles assumem o
valor nil.
De maneira geral, em todos os lugares que Lua aceita uma lista de expresses (passagem de parmetros, atribuio mltipla, construo de listas,
retornos mltiplos) podemos usar uma funo com mltiplos retornos como ltimo (ou nico) elemento da lista. Todos os valores retornados se juntam ao
final dos valores das outras expresses. Entretanto, quando uma chamada de
funo no a ltima expresso da lista, apenas um valor de retorno dela
usado, mesmo que ela retorne vrios valores. Por exemplo, na atribuio
a, b, c = foo(10), foo(20)
a recebe o valor 10, b recebe o valor 20 e c recebe 21 (segundo retorno de
foo(20)). O segundo retorno da primeira chamada a foo, 11, descartado.
Essa regra garante um mnimo de ordem na correspondncia entre expresses
e valores. Por exemplo, em uma chamada como f(g(x),y), a regra garante
que o argumento y ser o segundo argumento, mesmo que g(x) retorne mais
de um resultado.
10

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

Na verdade, a funo recebe uma string e um nmero len, e retorna todos os


prefixos da string com tamanho maior ou igual a len. Esse segundo parmetro
tem um valor default de zero; com isso, quando chamamos a funo com apenas um parmetro (a string), recebemos de volta todos os seus prefixos com
tamanho maior ou igual a zero, ou seja, todos.
A primeira linha da funo implementa o valor default para len. Quando
chamamos a funo pela primeira vez, passando apenas a string, o parmetro
len recebe o valor nil. Assim, o resultado do or na primeira atribuio 0,
que se torna o novo valor do parmetro. Nas chamadas recursivas, len recebe
um valor numrico; portanto, o resultado do or o prprio valor de len, e a
atribuio no altera seu valor.
O resto da funo razoavelmente simples. Se o limite de tamanho for
maior que o comprimento da string, no h nada a ser retornado. Caso contrrio, a funo retorna o prefixo com tamanho len, calculado com a funo
string.sub (que j vimos anteriormente), seguido dos prefixos com tamanho maior que len, calculados por meio da chamada recursiva.

3.4.2. Regies Geomtricas


Alm de serem valores de primeira classe, funes em Lua oferecem escopo lxico. Isso permite empregarmos em Lua vrias tcnicas de programao funcional, como por exemplo o uso de funes para representao de
dados. Para darmos uma pequena ilustrao sobre essas tcnicas, vamos desenvolver aqui um exemplo simples.
O exemplo a seguir baseado em [Hudak and Jones 1994]. A ideia desenvolver um sistema para representao de regies geomtricas. Uma regio
um conjunto de pontos, e a nica operao relevante no nosso sistema
pertinncia: dado um ponto e uma regio, saber se o ponto est dentro da
(pertence ) regio. Para desenharmos uma regio, podemos percorrer a rea
de desenho testando a pertinncia de cada pixel. O sistema deve ser capaz
de representar uma ampla gama de regies geomtricas, e deve ser capaz de
representar unio, interseo e complemento de regies genricas.
11

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

function union (g1, g2)


return function (x, y)
return g1(x, y) or g2(x, y)
end
end
function inter (g1, g2)
return function (x, y)
return g1(x, y) and g2(x, y)
end
end
function diff (g1, g2)
return function (x, y)
return g1(x, y) and not g2(x, y)
end
end

Figura 3.2. Unio, interseo e diferena de regies.

translada uma figura:


function translate (g, dx, dy)
return function (x, y)
return g(x + dx, y + dy)
end
end
Observe como todas as funes que definimos necessitam visibilidade lxica,
ao retornarem funes que necessitam acesso a valores externos para funcionarem corretamente.
Como comentamos anteriormente, podemos visualizar as regies geradas
percorrendo a rea de desenho e testando cada pixel. Para ilustrar o processo
de uma forma simples, vamos escrever uma funo para gerar um arquivo PBM
(portable bitmap) com o desenho de uma dada regio.
Arquivos PBM tm um formato bastante simples3 : em sua variante de modo
texto, ele comea com um cabealho de uma linha contendo a sequncia "P1";
em seguida h uma linha com a largura e a altura do desenho, em pixels. Finalmente, h uma sequncia de algarismos com os valores de cada pixel da imagem (1 para preto, 0 para branco), separados ou no por brancos ou quebras
de linha. A funo na Figura 3.3 gera o arquivo para uma regio dada, mapeando os pixels da rea sendo desenhada [1, M] [1, N] para o espao virtual
3

Esse formato tambm bastante ineficiente, mas aqui nossa nfase a simplicidade.

13

R. Ierusalimschy

local M, N = 500, 500


-- tamanho do desenho
function plot (f)
io.write("P1\n", M, " ", N, "\n")
for i = 1, N do
-- percorre as linhas
local y = (N/2 - i)*2/N
for j = 1, M do
-- percorre as colunas
local x = (j - M/2)*2/M
io.write(f(x, y) and "1" or "0")
end
io.write("\n")
end
end

Figura 3.3. Funo para desenhar uma regio no formato PBM.

Figura 3.4. Desenho da diferena de duas regies circulares

de desenho [1, 1] [1, 1]: A iterao externa percorre as linhas do desenho,


que correspondem coordenada vertical; para cada linha a iterao interna
percorre suas colunas, que correspondem coordenada horizontal. Observe o
uso idiomtico dos operadores lgicos para transformar o resultado da funo
caracterstica (um booleano) no caractere apropriado (0 ou 1).
A Figura 3.4 ilustra o resultado do comando a seguir:
plot(diff(circle(0, 0, 1), circle(0.3, 0, 1)))

3.4.3. Desenho de Polgonos


Um exemplo interessante do uso de funes em Lua vem da biblioteca
NCLua, que a interface entre Lua e o middleware Ginga, da TV Digital Brasileira. Uma das funes oferecidas por essa interface a drawPolygon,
para desenhar um polgono em uma rea de desenho [ABNT 2007]. Um dos
problemas de funes para desenhar polgonos que a representao de um
14

Programao em Lua

polgono geralmente requer uma estrutura de dados especfica, que pode no


ser compatvel com a estrutura usada pelo programa. A funo drawPolygon
de NCLua evita essa dificuldade por meio de funes. Quando chamamos essa
funo passamos apenas dois parmetros: a tela (canvas) onde desenhar e o
modo de desenho: aberto, fechado ou preenchido. Essa funo ento retorna
uma nova funo, que adiciona pontos ao polgono. Chamando repetidas vezes essa funo retornada, podemos adicionar todos os pontos do polgono
sem necessidade da criao de uma estrutura de dados particular. Quando
chamamos essa funo sem parmetros, indicamos que no h mais pontos a
inserir.
Para ilustrar o uso dessa funo, o cdigo a seguir desenha um polgono
armazenado em um array de pontos, onde cada ponto uma estrutura com
dois campos, x e y:
local f = drawPolygon(canvas, "close")
for i = 1, #points do
f(points[i].x, points[i].y)
end
f()
Na primeira linha, chamamos drawPolygon, que cria um novo polgono e retorna a funo para adicionar pontos a esse polgono. Em seguida, fazemos
uma iterao sobre o array points para adicionar os pontos ao polgono. Finalmente, chamamos a funo de adio sem argumentos, para indicar que
no h mais pontos a serem inseridos.
Uma facilidade adicional de drawPolygon que cada chamada funo
de adio de pontos retorna a prpria funo. Isso permite uma sintaxe simples
para criarmos polgonos pequenos, via um encadeamento de chamadas. O
cdigo a seguir ilustra esse uso:
drawPolygon(c, "fill")(1.5,2.3)(3.6,4.5)(0.4,9.7)()
No difcil adaptarmos uma funo para desenhar polgonos com uma
interface convencional para essa interface funcional. Por exemplo, suponha
que temos uma funo como a descrita a seguir:
DP(canvas, mode, n, x, y)
Nessa funo, n o nmero de pontos do polgono, x um array com as coordenadas horizontais dos pontos e y um array com suas coordenadas verticais. Com essa funo, podemos implementar a nova interface por meio do cdigo mostrado na Figura 3.5. Quando chamada, nossa funo drawPolygon
cria dois arrays vazios (list_x e list_y) e um contador n, e retorna a funo de adio de pontos, que executa o grosso do trabalho. Se chamada sem
argumentos, ela invoca a primitiva DP para desenhar o polgono acumulado.
Caso contrrio, ela verifica se os argumentos possuem o tipo correto e insere
um novo ponto na sua estrutura de dados. Essa estrutura, que uma particularidade da funo DP, fica completamente escondida do resto do programa.
15

R. Ierusalimschy

function drawPolygon (canvas, mode)


local list_x, list_y, n = {}, {}, 0
return function (x, y)
if x == nil and y == nil then
DP(canvas, mode, n, list_x, list_y)
elseif type(x) ~= "number" then
error("x deve ser um numero")
elseif type(y) ~= "number" then
error("y deve ser um numero")
else
n = n + 1
list_x[n] = x
list_y[n] = y
end
end
end

Figura 3.5. Uma implementao para drawPolygon sobre uma interface convencional.

3.4.4. Trechos de Cdigo


Como discutimos anteriormente, a unidade de execuo de cdigo em Lua
chamada de trecho. Vamos ver agora mais detalhes sobre como Lua trata
trechos de cdigo.
Antes de executar um trecho de cdigo, Lua pr-compila o trecho para um
formato interno. Esse formato uma sequncia de instrues para uma mquina virtual, algo semelhante ao cdigo de mquina para uma CPU convencional. Para essa pr-compilao, Lua trata o trecho exatamente como se ele
fosse o corpo de uma funo annima. Alm disso, o resultado da compilao
uma funo Lua, com todos os direitos de qualquer funo.
Normalmente, Lua executa a funo annima correspondente a um trecho imediatamente aps sua compilao, de modo que essa fase de prcompilao fica transparente para o programador. Mas podemos ter acesso
a esse passo quando necessrio. Em especial, a funo loadstring compila
um trecho de cdigo arbitrrio e retorna a funo resultante, sem execut-la.
Veja o exemplo a seguir:
i = 0
f = loadstring("print(i); i = i + 1")
f()
--> 0
f()
--> 1
O fato de trechos serem compilados como funes permite algumas tcnicas teis. Em particular, trechos podem ter variveis locais. Como Lua oferece
16

Programao em Lua

visibilidade lxica, essas variveis so acessveis para as funes declaradas


dentro do trecho, mas so invisveis fora dele. Em particular, uma funo armazenada em uma varivel local de um trecho s visvel dentro daquele
trecho. Lua inclusive oferece um acar sinttico para declararmos funes
locais. Veja o exemplo a seguir:
local function foo (x)
print(2*x)
end
Essa declarao equivalente ao cdigo a seguir:
local foo
foo = function (x)
print(2*x)
end
Uma outra opo de traduo seria assim:
local foo = function (x)
print(2*x)
end
Mas essa traduo no to conveniente quanto a primeira opo. (Voc sabe
explicar por que? O que ocorre nos dois casos se quisermos definir uma funo
recursiva?)
Como qualquer funo, trechos de cdigo tambm podem retornar valores.
Isso til em algumas situaes particulares. Por exemplo, o interpretador independente de Lua imprime qualquer valor retornado por um trecho executado
em modo interativo. Alm disso, ele substitui um sinal de igual no incio de uma
linha por um return. Assim, podemos imprimir o resultado de uma expresso
em modo interativo iniciando a linha com um sinal de igual:
$ lua
> = 2^-3

--> 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.

3.5. Programando com Tabelas


Lua oferece um nico mecanismo para estruturao de dados, chamado
tabela. Tabelas nada mais so do que arrays associativos, isto , uma estrutura
de dados que associa chaves com valores e permite um rpido acesso ao valor
associado a uma dada chave.
Tabelas so, provavelmente, a caracterstica mais marcante de Lua. Muitas
outras linguagens, em especial linguagens dinmicas, oferecem arrays associativos, mas nenhuma os usa de modo to extensivo quanto Lua. Em Lua
usamos tabelas para implementar estruturas de dados como arrays, estruturas (registros), conjuntos e listas, e tambm para implementar conceitos mais
abstratos como objetos, classes e mdulos.
A semntica bsica de tabelas bastante simples. A expresso {} cria
uma tabela vazia e retorna uma referncia para ela. Uma atribuio t[x]=y
associa o valor y chave x na tabela referenciada por t. E a expresso t[x]
retorna o valor associado chave x na tabela referenciada por t, ou o valor nil
caso a tabela no contenha a chave dada. Analogamente, se atribuimos nil a
uma chave, eliminamos aquela chave da tabela: as chaves de uma tabela so
aquelas com um valor associado diferente de nil.
J vimos alguns usos de tabelas nos exemplos das sees anteriores. Um
dos primeiros exemplos deste texto, de uma funo para somar os elementos
de um array, na verdade usa uma tabela. Lua no tem arrays. O que chamamos de array em Lua meramente uma tabela cujas chaves so nmeros
naturais. Lua usa um algoritmo que garante que tabelas usadas como arrays
so efetivamente armazenadas internamente como arrays. Essa implementao completamente transparente para o programador; no existe nada na
linguagem que dependa dessa implementao, com exceo do desempenho
de certas operaes.
Para manipularmos tabelas como estruturas, Lua oferece um acar sinttico bastante simples: a notao t.x equivalente a t["x"], isto , a tabela t
indexada pela string literal "x". O exemplo a seguir ilustra esse uso:
t = {}
t["x"] = 10; t.y = 20;
print(t.x, t["y"])

--> 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

no so identificadores vlidos, como no exemplo a seguir:


op = {["+"] = "add", ["-"] = "sub",
["*"] = "mul", ["/"] = "div"}
Um construtor tambm pode usar uma mistura desses trs formatos. Por
exemplo, considere o construtor a seguir:
{23, "ho", op = 10, ot = "a", ["or"] = 30}
Ele criar uma tabela com o valor 23 na chave 1, "ho" na chave 2, 10 na
chave "op", "a" na chave "ot" e 30 na chave "or". (Como or uma palavra
reservada em Lua, ela no pode ser usada como um identificador.)
Quando escrevemos uma chamada de funo onde o nico argumento
um construtor, o uso dos parnteses opcional. Isso d um aspecto mais declarativo quando usamos Lua para descrio de dados, como neste fragmento:
character{"merlin",
image = "files/img/merlin.jpg",
strength = 100.5,
category = wizard
}
Essa descrio na verdade uma chamada funo character passando
como argumento uma tabela com a string "merlin" no ndice 1 mais os outros
campos com chaves explcitas.

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

Arrays onde a grande maioria dos elementos tm valor nil ou zero.

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

3.5.3. Palavras mais Frequentes


Vamos ver agora um exemplo de um pequeno programa completo, que
ilustra vrios conceitos que vimos at agora. O objetivo do programa listar as
n palavras mais frequentes em um dado texto [Bentley et al. 1986].
O algoritmo geral desse programa bastante simples. Primeiro percorremos todas as palavras do texto, contando quantas vezes cada uma aparece.
Para isso, mantemos uma tabela que associa cada palavra j vista com o nmero de vezes que ela apareceu. Em seguida, ordenamos o resultado por ordem decrescente de frequncias e listamos os n primeiros elementos da lista.
A Figura 3.6 mostra o cdigo completo do programa. Na primeira linha, lemos o
arquivo de entrada todo de uma vez como uma nica string (com a opo *all
para a funo de leitura io.read), e armazenamos o resultado em t. Isso
21

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

Figura 3.6. Programa para listar as palavras mais frequentes em um texto.

22

Programao em Lua

bastante razovel para arquivos com at algumas dezenas de megabytes. Mais


adiante vamos ver como tratar arquivos realmente grandes.
Em seguida, criamos a tabela count, para armazenar a frequncia de cada
palavra, e percorremos todas as palavras do texto, contando-as. Para esse percorrimento, usamos um gerador baseado em um padro: o lao ser repetido
para cada substring de t que case com o padro "%w+". Esse padro significa uma sequncia de um ou mais caracteres alfanumricos, que nossa
definio de palavra. (Esse padro similar ao padro "\w+" de Perl, por
exemplo; em Lua usamos o caractere % como escape nos padres para evitar
conflito com o significado de \ em strings.) Note o uso idiomtico do conectivo
or no corpo do lao: se a palavra w j tem um contador, o resultado da disjuno esse contador; caso contrrio, o resultado zero. Em qualquer caso,
o valor incrementado e atribudo como a nova contagem associada quela
palavra.
O prximo passo ordenar as palavras. Isso um pouco mais sutil do que
parece. No podemos ordenar diretamente a tabela count, pelo simples fato
de que tabelas no tm ordem; elas apenas mapeiam chaves para valores. Por
isso, para impormos uma ordenao sobre as palavras, precisamos coloc-las
em uma lista.5 Assim, criamos uma nova tabela (chamada words) e inserimos
nela todas as palavras presentes como chave na tabela count.
Para ordenar a lista words, usamos a funo predefinida table.sort. O
segundo parmetro dessa funo a funo usada por sort para comparar os
valores sendo ordenados. Essa funo recebe como parmetros dois valores
sendo ordenados, e retorna verdadeiro se e somente se o valor do primeiro
parmetro deve preceder o valor do segundo parmetro na ordem final. No
nosso caso, os valores so as palavras sendo ordenadas, e a funo ento
consulta a tabela count para comparar qual tem maior frequncia.
Finalmente, no ltimo lao imprimimos as n palavras mais frequentes, que
so as primeiras da lista ordenada, e suas respectivas frequncias. O valor de
n pode ser dado como um argumento na chamada do programa; o interpretador independente armazena esses argumentos em uma tabela global arg.
Quando no fornecido um valor, o programa usa um valor preestabelecido
(10, no exemplo).
Como comentamos anteriormente, a tcnica de ler o arquivo inteiro para
posterior tratamento bastante eficiente para arquivos no muito grandes. Mas
para arquivos grandes (da ordem de centenas de megabytes ou mais) ela pode
se tornar invivel. Nesse caso, podemos modificar o incio do programa para
5

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

Normalmente, um mdulo definido por meio de um trecho de cdigo Lua


armazenado em um arquivo.6 Lua indiferente em relao a como um mdulo
escrito, desde que sua execuo resulte na criao de uma tabela global
contendo os itens exportados pelo mdulo. Por razes que veremos mais adiante, tambm educado o mdulo retornar sua tabela de exportaes. Como
ilustrao, o trecho de cdigo a seguir define um mdulo vector exportando
duas funes, norm1 e norm2.
vector = {}
function vector.norm1 (x, y)
return (x^2 + y^2)^(1/2)
end
function vector.norm2 (x, y)
return math.abs(x) + math.abs(y)
end
return vector
Observe que Lua oferece um acar sinttico para definirmos funes diretamente como campos em tabelas.
6

Tambm podemos definir mdulos para Lua em C.

24

Programao em Lua

Para carregar um mdulo, podemos simplesmente execut-lo, por exemplo


com a funo predefinida dofile. Entretanto, Lua oferece uma funo bem
mais conveniente para a carga de mdulos, chamada require. A funo
require difere de um simples dofile em dois aspectos importantes:
Ela mantm uma lista de mdulos j carregados, de modo que requerer
um mdulo j carregado no o carrega novamente.
Ela usa uma lista de lugares onde procurar o arquivo contendo o mdulo;
com a funo dofile temos que especificar o caminho completo at o
arquivo.
A funo require retorna como resultado o valor retornado pelo trecho
que criou o mdulo. Assim, se o trecho retorna a tabela de exportao, essa
tabela ser retornada quando chamarmos require para carregar o mdulo.
Isso permite o idioma a seguir:
local v = require("vector")
Esse idioma permite usarmos um outro nome para o mdulo no nosso cdigo
(v, no exemplo), alm de evitar o uso de globais.

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

Delegao em Lua uma instncia especfica de um mecanismo mais geral chamado


metamtodos, que justifica o uso de metatabelas. Neste texto vamos discutir apenas
delegao.

26

Programao em Lua

Rectangle = {x = 0, y = 0, width = 10, height = 20}


Rectangle.__index = Rectangle
function Rectangle:new (o)
setmetatable(o, self)
return o
end
function Rectangle:area ()
return self.width * self.height
end
r = Rectangle:new{width = 40, height = 60}
print(r:area())
--> 2400

Figura 3.7. Classe Rectangle usando delegao.

A chamada funo setmetatable estabelece mt como a metatabela de


A. Podemos trocar a metatabela de uma tabela a qualquer hora (chamando a
funo setmetatable), assim como podemos trocar o contedo do campo
__index (via atribuies convencionais). No momento do acesso a um campo
ausente de uma tabela, o valor corrente do campo __index de sua metatabela
corrente ir indicar a quem delegar o acesso. Se a metatabela ou seu campo
__index no existir, o acesso retorna o valor nil, que o comportamento
normal de acesso a um campo ausente.
Nada nos obriga a criarmos uma terceira tabela para ser a metatabela. No
exemplo anterior, tanto A quanto B poderiam ser a metatabela de A. Adotando
a segunda alternativa, o cdigo ficaria assim:
A = {x = 10}
B = {x = 20, y = 30}
B.__index = B
setmetatable(A, B)
print(A.x, A.y)

--> 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

um outro parmetro, self, com o valor de Rectangle. Essa funo apenas


atribui o valor de Rectangle como metatabela do novo objeto e o retorna.
Na prxima linha chamamos o mtodo area nesse novo objeto. Lembrese que r:area() equivalente a r.area(r). Ou seja, o primeiro passo
acessar o campo area na tabela r. Como essa tabela no tem esse campo, o
interpretador consulta sua metatabela para uma alternativa. A metatabela de r
Rectangle, que tambm o valor do seu campo __index. Assim, a funo
Rectangle.area se torna o resultado do acesso r.area. Lua ento chama
essa funo, passando r como argumento extra para o campo self.

3.6. Programando com Co-rotinas


Co-rotinas so um tipo de mecanismo de controle bastante poderoso, mas
pouco convencional. Como sempre, o termo em si no tem um significado
preciso. Vrios mecanismos de controle, muitos no equivalentes entre si, recebem o nome de co-rotinas.
Genericamente, o termo co-rotina se refere a um mecanismo que permite
que um procedimento suspenda temporariamente sua execuo e continue
mais tarde. Esse mecanismo bsico permite diversas variaes:
Co-rotinas podem ser valores de primeira classe, isto , co-rotinas podem ser manipuladas livremente e reinvocadas em qualquer ponto do
programa, ou podem haver restries sobre seu uso.
O controle de fluxo pode ser simtrico ou assimtrico. O controle simtrico tem uma nica primitiva para transferir o controle entre co-rotinas; o
assimtrico tem duas primitivas, uma para suspender a execuo e outra
para reiniciar.
Co-rotinas podem ser suspensas enquanto executando outras funes
(com pilha, ou stackful) ou apenas enquanto executando sua funo principal. [de Moura and Ierusalimschy 2009]
Em particular, Lua oferece um mecanismo de co-rotinas assimtrico, de
primeira classe e com pilha [de Moura et al. 2004]. Nesta seo, vamos apresentar esse mecanismo e mostrar alguns de seus usos.
Co-rotinas, como implementadas por Lua, so bastante similares a linhas
de execuo (threads) cooperativas. Cada co-rotina em Lua representa uma
linha de execuo independente, com sua prpria pilha de chamadas8 . Mas,
ao contrrio de um sistema multithreading convencional, no h preempo em
um sistema de co-rotinas. Uma co-rotina s interrompe sua execuo quando
termina ou quando invoca explicitamente uma primitiva de suspenso (yield).
A funo coroutine.wrap cria uma co-rotina e retorna uma funo que,
8

Por isso classificada como stackful.

28

Programao em Lua

ao ser chamada, executa (resume) a co-rotina.9 O parmetro nico para wrap


uma funo Lua contendo o cdigo do corpo da co-rotina:
co = coroutine.wrap(function () print(20) end)
co()
--> 20
Essa funo pode opcionalmente ter parmetros, cujos valores so dados na
chamada co-rotina:
co = coroutine.wrap(function (x) print(x) end)
co("alo")
--> alo
Como j vimos, o poder de co-rotinas vem da possibilidade de uma corotina suspender sua execuo para continuar posteriormente. Para isso, ela
deve chamar a funo yield, como no exemplo a seguir:
co = coroutine.wrap(function (x)
print(x)
coroutine.yield()
print(2*x)
end)
co(20)

--> 20

Note que, ao ser chamada, a co-rotina nesse exemplo executou apenas at a


chamada a yield. Mas ela no terminou sua execuo, apenas a suspendeu.
Ao ser chamada novamente, ela ir continuar do ponto onde parou:
co()

--> 40

A funo yield tambm pode passar um valor de retorno para o ponto


onde a co-rotina foi invocada:
co = coroutine.wrap(function ()
for i = 1, 10 do coroutine.yield(i) end
return "fim"
end)
print(co())
print(co())
...
print(co())
print(co())

--> 1
--> 2
--> 10
--> fim

O exemplo anterior, apesar de simples e de pouca utilidade, ilustra a es9

Tambm podemos criar uma co-rotina com a funo coroutine.create, que no


vamos tratar neste texto.

29

R. Ierusalimschy

sncia da construo de geradores, que est intimamente ligada construo


de iteradores.

3.6.1. Iteradores e Geradores


Uma questo recorrente em programao a construo de iteradores:
estruturas de controle para percorrer os elementos de uma estrutura de dados em uma determinada ordem. Tradicionalmente, existem duas maneiras de
se estruturar iteradores, chamadas de exportao de dados e importao de
aes [Eckart 1987].
Exportao de dados se baseia em geradores: funes que, cada vez que
so chamadas, retornam um prximo elemento da estrutura de dados (segundo
alguma ordem). Essa a forma mais usada em Java, por exemplo, por meio
da interface Iterator.
Importao de aes usa funes de mais alta ordem: nesse esquema,
uma funo de iterao recebe uma funo como parmetro e a aplica a todos
os elementos da estrutura de dados. Essa a forma mais usada em Ruby,
por exemplo (apesar de Ruby no usar funes de mais alta ordem, mas um
mecanismo especialmente dedicado a esse fim). A vantagem dessa forma de
iteradores que muito mais fcil manter o estado da iterao: considere, por
exemplo, o percorrimento de uma rvore em pr-ordem. A desvantagem que
o lao de iterao no pode ser modificado, o que dificulta algumas tarefas:
considere, por exemplo, o percorrimento de duas estruturas em paralelo.
Existe um problema famoso que ilustra simultaneamente os problemas das
duas abordagens, chamado problema das bordas iguais (same-fringe problem). O problema consiste em, dadas duas rvores, determinar se o percorrimento das folhas das duas rvores produz a mesma sequncia de elementos.
Com exportao de dados, usando geradores, difcil percorrer cada rvore,
pois no temos recurso para manter o estado do percorrimento. Com importao de aes, usando funes de iterao, difcil percorrer as duas rvores
em paralelo.
O uso de co-rotinas elimina essa dicotomia entre as duas abordagens, pois
torna trivial a transformao de uma funo de iterao em um gerador. Para
ilustrar essa transformao, vamos resolver o problema das bordas iguais.
Percorrer as folhas de uma rvore uma tarefa bem simples, se usarmos
recurso. O cdigo na Figura 3.8 implementa um iterador via importao de
aes. Esse iterador recebe a ao como uma funo (f), e aplica essa funo
a cada folha da rvore a seguindo a ordem da esquerda para a direita.
Construir o iterador foi bem fcil; mas, como comentamos, um iterador no
muito til para o nosso problema. No temos como percorrer duas rvores
em paralelo usando esse iterador, j que o lao de percorrimento est embutido
no iterador, e percorre apenas uma nica rvore.
Portanto, vamos tranformar esse iterador em um gerador. Tudo que temos
a fazer especificar yield como a funo de visita e executar a funo de
percorrimento dentro de uma co-rotina:
30

Programao em Lua

function leaves (a, f)


if a ~= nil then
if a.left or a.right then
leaves(a.left, f)
leaves(a.right, f)
else
-- folha
f(a.value)
end
end
end

Figura 3.8. Funo para percorrer as folhas de uma rvore binria.

function gen (a)


return coroutine.wrap(function ()
leaves(a, coroutine.yield)
end)
end
A funo gen, quando chamada, retorna uma funo que cede as folhas da
rvore dada uma a uma, na ordem correta; ou seja, um gerador. Com poucas
linhas de cdigo transformamos um iterador por importao de aes em um
iterador por exportao de dados.
Agora, fica bem fcil resolver o problema das bordas iguais:
function samefringe (a, b)
local gen_a, gen_b = gen(a), gen(b)
repeat
local a, b = gen_a(), gen_b()
if a ~= b then return false end
until a == nil
return true
end
A funo samefringe cria um gerador para cada rvore dada, e percorre as
duas em paralelo comparando os elementos gerados.10

3.6.2. Co-rotinas Simtricas


A forma de co-rotinas usada por Lua, chamada de assimtrica, oferece
duas primitivas para passagem de controle entre co-rotinas: resume, que
10

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

local main = coroutine.wrap(function () end)


local next
current = main
transfer = function (co, val)
if current ~= main then
next = co
return coroutine.yield(val)
else
current = co
while current ~= main do
next = main
val = current(val)
current = next
end
return val
end
end

Figura 3.9. Implementao de transfer em Lua.

invocada implicitamente quando chamamos a co-rotina, e yield. Existe uma


outra forma de implementarmos co-rotinas, chamada de simtrica. Essa segunda forma mais conhecida por alguns programadores, devido a seu uso na
linguagem Modula-2 [Wirth 1982].
Co-rotinas simtricas utilizam uma nica primitiva de controle de fluxo, tradicionalmente chamada de transfer. Essa primitiva simultaneamente suspende a execuo da co-rotina em execuo e transfere o controle para uma
outra co-rotina qualquer, dada como parmetro na chamada a transfer.
A maioria das implementaes de co-rotinas assimtricas no so com pilha (e.g., geradores em Python). Por essa razo, algumas pessoas tendem a
achar que co-rotinas assimtricas so menos expressivas que co-rotinas simtricas. Mas isso no verdade. Em particular, podemos implementar a funo
transfer em Lua, como veremos agora.
Como um transfer simultaneamente suspende uma co-rotina e ativa outra, podemos implement-lo usando uma combinao de um yield, para suspender uma co-rotina, seguido de um resume, para ativar a outra. O cdigo da
Figura 3.9, adaptado de [de Moura and Ierusalimschy 2009], implementa essa
ideia. Na primeira linha, o cdigo cria uma nova co-rotina para representar o
fluxo de execuo principal, de modo a ser possvel voltarmos o controle para
esse fluxo. Em seguida, declara uma varivel local next, que ser usada pela
transfer, e uma varivel global current, que ir sempre conter a co-rotina
32

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)

Figura 3.10. Um pequeno programa com co-rotinas simtricas.

que estiver em execuo. Finalmente, temos a definio de transfer.


Quando a funo transfer chamada pela co-rotina principal, ela entra
em um lao e invoca a co-rotina destino (co). A partir da, qualquer co-rotina
que chame transfer ir colocar o valor da co-rotina destino na varivel next
e ceder o controle, que voltar ao lao. Se a co-rotina destino for a principal,
o lao termina e a co-rotina principal continua sua execuo. Caso contrrio, o
lao repete e invoca a co-rotina destino (current).
A Figura 3.10 apresenta um pequeno programa que ilustra o uso da funo transfer. Apesar de sua simplicidade, o programa apresenta uma tpica
arquitetura produtorconsumidor.

3.7. A API LuaC


Como comentamos anteriormente, um dos pontos fortes de Lua sua facilidade para se comunicar com C. Nessa seo, vamos ver como essa comunicao feita.
Lua foi projetada para se comunicar com C. Isso to importante que Lua
organizada como uma biblioteca em C, no como um programa. O programa
lua, que temos usado ao longo do texto para executar os exemplos, na verdade um pequeno programa com menos de 400 linhas de cdigo que um
cliente da biblioteca Lua. Essa biblioteca exporta pouco menos que 100 funes, que permitem executarmos trechos de cdigo Lua, chamarmos funes,
registrarmos funes C para serem chamadas por Lua, manipularmos tabelas,
e outras operaes bsicas.
Sempre que manipulamos uma API como a de Lua, importante lembrar
que trabalhamos com dois nveis de abstrao simultneos. Por um lado, existe
33

R. Ierusalimschy

o nvel do programa em C, que estamos escrevendo. Por outro lado, existe o


nvel do programa em Lua que est sendo manipulado por esse programa C.
Por exemplo, existe uma funo na API para consultar o valor de uma varivel
global. Quando chamamos essa funo, estamos executando uma chamada
de funo, no nvel C, mas estamos consultando uma global no nvel Lua.
Uma considerao importante na implementao de Lua o tamanho e a
portabilidade do cdigo. Lua comumente usada em plataformas bastante no
convencionais, como consoles de jogos, conversores (set-top box) para TVs,
cameras fotogrficas, etc. Para diminuir seu tamanho e aumentar sua portabilidade, o cdigo de Lua dividido em trs partes: o ncleo, uma biblioteca
auxiliar e as bibliotecas padro.
O ncleo contm toda a parte bsica de Lua, como o pr-compilador, o interpretador, os algoritmos de manipulao de tabela e de coleta de lixo. Porm,
ele no assume nada sobre o sistema operacional. Por exemplo, o ncleo faz
toda sua alocao de memria chamando uma funo externa, que deve ser
fornecida a ele na sua inicializao. Da mesma forma, o ncleo no l arquivos,
mas carrega trechos de cdigo chamando uma funo externa apropriada. A
API do ncleo totalmente definida no arquivo lua.h; todos os nomes definidos por essa API tem o prefixo lua_.
A estrutura do ncleo bastante apropriada para aplicaes embarcadas,
rodando em plataformas no convencionais que muitas vezes nem dispem
de um sistema operacional. Para plataformas mais convencionais, entretanto,
ela demasiadamente detalhista. Para isso existe a biblioteca auxiliar. Essa
biblioteca usa a API do ncleo e funes normais do sistema operacional para
oferecer uma interface de mais alto nvel para o programador. Essa API auxiliar definida no arquivo lauxlib.h; todos os nomes definidos por essa API
tem o prefixo luaL_. Neste texto vamos usar a biblioteca auxiliar sempre que
necessrio, pois assumimos estar programando em uma plataforma convencional.
A comunicao LuaC bi-direcional. Por um lado, Lua pode chamar funes que na verdade esto escritas em C. Por exemplo, todas as bibliotecas
padro de Lua, como para manipulao de strings e para manipulao de arquivos, so escritas em C. Por outro lado, tambm bastante fcil C chamar
funes escritas em Lua. Isso permite que partes de um programa escrito em
C sejam configurveis por meio de cdigo Lua.
Uma distino til entre embutir (embed) e estender uma linguagem de
script. Embutir usar a linguagem como uma biblioteca C dentro de um programa hospedeiro, enquanto estender escrever o programa principal na linguagem dinmica estendida com funes escritas em C. Uma linguagem dinmica poder chamar cdigo escrito em C bastante comum; mesmo em Java
podemos fazer isso, por meio da Java Native Interface. Entretanto, poucas linguagens oferecem suporte adequado para serem embutidas, e portanto induzem o projetista a usar uma arquitetura de extenso, com o programa principal
escrito na linguagem dinmica [Muhammad and Ierusalimschy 2007]. Em Lua,
34

Programao em Lua

podemos escolher qual a melhor arquitetura para cada aplicao particular.


Por exemplo, tanto World of Warcraft quanto Ginga usam Lua de forma embutida, enquanto o Photoshop Lightroom estende Lua.

3.7.1. Embutindo Lua


A comunicao LuaC envolve sempre duas estruturas centrais, que vamos apresentar agora. A primeira o estado Lua, representado por um ponteiro de tipo lua_State *. Como Lua implementada como uma biblioteca,
seu cdigo C no possui nenhuma varivel global (extern ou static). Todo o estado do interpretador armazenado na estrutura dinmica lua_State. Para
qualquer programa usar Lua, ele deve criar pelo menos um estado, por meio
da funo luaL_newstate. O valor retornado por essa funo, um ponteiro
de tipo lua_State *, deve ser passado como parmetro para todas as outras funes da API. A funo lua_close finaliza um estado e libera todos os
recursos alocados por ele, incluindo memria, arquivos abertos e bibliotecas
dinmicas carregadas.
A segunda estrutura central a pilha. A pilha uma estrutura abstrata, que
o cdigo C s acessa por meio de chamadas API. Como o nome implica,
essa estrutura uma pilha de valores Lua. Esses so os nicos valores que
o cdigo C consegue acessar. Para se acessar qualquer outro valor, ele deve
primeiro ser copiado para a pilha.
Para tornar as coisas um pouco mais concretas, a Figura 3.11 mostra um
programa completo usando a biblioteca Lua. Esse programa executa um arquivo Lua e imprime o valor numrico final da varivel global result. Vamos
analis-lo passo a passo, ignorando alguns detalhes por enquanto.
A primeira coisa que o programa faz criar um estado Lua. Em seguida,
chama a funo luaL_loadfile para compilar o arquivo argv[1] nesse
estado. Se no houver erros na compilao, essa funo deixa um valor do tipo
function no topo da pilha e retorna o valor LUA_OK. Caso contrrio, retorna
um cdigo de erro e deixa uma mensagem de erro no topo da pilha. A funo
lua_tostring converte esse valor, uma string em Lua, para uma string em
C, que ento impressa na sada de erros (stderr).
A funo criada pelo compilador representa o trecho de cdigo lido do
arquivo. At aqui esse cdigo foi apenas compilado. Para execut-lo, precisamos chamar a funo criada pelo compilador. Isso feito pela funo
lua_pcall, que chama a funo presente no topo da pilha. Enventuais parmetros para a funo devem tambm ser empilhados. Nesse caso, chamamos
a funo sem parmetros, e isso indicado pelo zero como segundo argumento de lua_pcall. Assim como luaL_loadfile, em caso de erros a
funo lua_pcall tambm deixa uma mensagem no topo da pilha e retorna
um cdigo diferente de LUA_OK. Em caso de sucesso, deixa na pilha o nmero
de resultados pedidos. No nosso exemplo, queremos zero resultados, como
indicado pelo terceiro argumento da chamada lua_pcall.
No ltimo trecho do cdigo imprimimos o valor da varivel result. A fun35

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;
}

Figura 3.11. Um programa para executar um arquivo Lua


e imprimir o valor da varivel global result.

o lua_getglobal copia o valor da global nomeada para o topo da pilha e a


funo lua_tonumber converte esse valor para um nmero do tipo double.
Vrias funes da API usam a pilha de modo normal, adicionando e retirando elementos pelo topo. Por exemplo, a funo luaL_loadfile empilha a funo criada ou a mensagem de erro; lua_pcall desempilha a funo a ser chamada e eventuais argumentos e empilha eventuais resultados; e
lua_getglobal empilha o valor da global nomeada. Outras funes podem
acessar diretamente qualquer elemento dentro da pilha. Fazem parte dessa
categoria todas as funes de projeo, que transformam valores Lua em valores C; no nosso exemplo vimos lua_tostring e lua_tonumber.
Para indicar um elemento na pilha, podemos usar um ndice positivo ou
negativo. ndices positivos contam a partir da base da pilha: o ndice 1 indica o
primeiro elemento que foi empilhado. ndices negativos contam a partir do topo:
o ndice 1 indica o elemento no topo, o ltimo que foi empilhado. No nosso
exemplo, usamos o ndice 1 em todas as chamadas a funes de projeo,
pois queramos sempre referenciar o elemento no topo.

3.7.2. Estendendo Lua


O arquivo Lua executado pelo programa da Figura 3.11 pode ser algo to
simples quanto result=12 ou algo bastante complexo, envolvendo muitas
computaes. Entretanto, ele no pode chamar nenhuma funo: quando cri36

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

static int math_sin (lua_State *L) {


lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
return 1;
}

Figura 3.12. Funo seno, da biblioteca matemtica.

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.

3.8. Comentrios Finais


Neste texto procurei ilustrar algumas das caractersticas mais marcantes da
linguagem Lua. Abordamos funes de primeira classe, tabelas, co-rotinas e a
API LuaC. Procurei tambm enfatizar o poder expressivo desses mecanismos,
mostrando alguns exemplos mais complexos.
Devido a restries de tamanho, no pude abordar vrios outros aspectos
importantes de Lua. Em particular, o tratamento da API LuaC foi bastante introdutrio; por ser um mecanismo no muito convencional, temos que introduzir
38

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

Você também pode gostar