Você está na página 1de 65

LabLua www.lua.inf.puc-rio.

br

Lua

Conceitos Bsicos e API C a

Agosto de 2008

Sumrio a
1 Lua 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 4 4 4 6 7 8 9 10 11 12 12 12 12 13 13 15 15 16 16 17 17

Introduo . . . . . . . . ca Primeiros Passos . . . . Tipos . . . . . . . . . . Operadores Relacionais Operadores Lgicos . . . o Variveis . . . . . . . . . a Criando Tabelas . . . . Tamanho de Tabelas . . Estruturas de Controle . 1.9.1 If . . . . . . . . . 1.9.2 While . . . . . . 1.9.3 For numrico . . e 1.9.4 For genrico . . . e 1.10 Saindo de um bloco . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

2 Funes co 2.1 Declarando e Chamando uma Funo ca 2.2 Nmero varivel de parmetros . . . . u a a 2.3 Retornando um unico valor . . . . . . 2.4 Valores de Primeira Classe . . . . . . . 2.5 Fechos . . . . . . . . . . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

3 Manipulando Cadeias 19 3.1 Concatenando Cadeias . . . . . . . . . . . . . . . . . . . . . . . . 20 3.2 Busca e Substituio . . . . . . . . . . . . . . . . . . . . . . . . . 20 ca 4 Tabelas 4.1 Manipulando tabelas 4.2 Descrio de Dados . ca 4.3 Meta-mtodos . . . . e 4.4 Programao OO em ca 4.5 Herana . . . . . . . c 22 22 24 26 28 29

. . . . . . . . . Lua . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

5 Mdulos o 5.1 Denindo um Mdulo . . o 5.2 Entendendo require . . . 5.3 Instalando Mdulos . . . . o 5.4 Entendendo um rockspec

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

31 32 34 34 35

6 API C 37 6.1 Primeiros Passos . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 6.2 Funes Bsicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 co a 6.3 Manipulando a Pilha . . . . . . . . . . . . . . . . . . . . . . . . . 41 7 Usando Lua em uma Aplicao C ca 44 7.1 Um arquivo de congurao simples . . . . . . . . . . . . . . . . 44 ca 7.2 Incrementado o arquivo de congurao . . . . . . . . . . . . . . 45 ca 7.3 Usando funes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 co 8 Usando Funes C em Lua co 8.1 Denindo Funes com Estado co 8.1.1 Usando registry . . . . . 8.1.2 Usando o ambiente . . . 8.1.3 Usando upvalues . . . . 9 Denindo Novos Tipos de 9.1 Usando meta-tabelas . . 9.2 Usando um modelo OO 9.3 Meta-mtodos . . . . . . e 9.4 Criando Interfaces . . . 9.5 Coleta de Lixo . . . . . 51 52 53 53 55 56 60 61 62 63 65

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

Dados em . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

C . . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

Cap tulo 1

Lua
1.1 Introduo ca

Lua uma linguagem de programao criada em 1993 no Tecgraf, um laboratrio e ca o de pesquisa e desenvolvimento da PUC-Rio. Lua uma linguagem de script e dinmica, semelhante a outras linguagens de script como Python, Ruby e PHP. a Lua destaca-se pela sua simplicidade, portabilidade, rapidez e pela facilidade com que podemos embutir um interpretador Lua em uma aplicao C. Alm ca e disso, Lua a unica linguagem criada em um pa em desenvolvimento a ganhar e s relevncia global. a Outra caracter stica importante de Lua o seu tamanho pequeno. O ncleo e u da linguagem somado `s suas bibliotecas padres ocupa menos de 200k. a o Lua uma linguagem de propsito geral, que pode ser utilizada tanto para e o escrever pequenos scripts, com algumas poucas linhas de cdigo, como sistemas o complexos, com dezenas de milhares de linhas de cdigo. o Atualmente a linguagem utilizada principalmente em jogos, como World of e Warcraft, The Sims e Sim City. Contudo, Lua no uma linguagem espec a e ca para jogos, sendo utilizada tambm em muitas outras aplicaes, como o softe co ware Adobe Photoshop Lightroom e o middleware Ginga do Sistema Brasileiro de TV Digital. Um outro uso de Lua no desenvolvimento de aplicaes web, como o Publie co que!, um gerenciador de contedo para web desenvolvido pela Fbrica Digital, u a e Sputnik, um wiki extens dispon como software livre. vel vel

1.2

Primeiros Passos

Um pedao de cdigo Lua chamado de trecho. Um trecho pode ser desde uma c o e simples linha, digitada no modo interativo, como um programa inteiro. Lua compila trechos de cdigo em instrues para uma mquina virtual de maneira o co a muito rpida (Trechos com mais de 1M de cdigo Lua podem ser compilados a o em menos de um segundo).

Para executar um trecho de cdigo Lua, podemos usar um interpretador da o linguagem, que chamaremos de lua (notem que ao nos referirmos ` linguagem, a usamos a letra L maiscula e ao falarmos do interpretador a letra l minscula). u u Caso o programa no tenha sido compilado, o interpretador lua ir compila a a lo primeiro para em seguida execut-lo. (Caso voc deseje apenas compilar um a e programa, utilize o compilador luac.) Variveis em Lua, assim como em C e Java, possuem escopo lxico. Quando a e uma varivel declarada, o padro que ela seja global. Para indicar que uma a e a e varivel local ao bloco do programa na qual ela foi declarada, devemos usar a e a palavra-chave local. Alm de ser uma boa prtica de programao declarar e a ca as variveis como locais, ao fazer isso o programador tambm pode melhorar a e o desempenho do programa, uma vez que em Lua o acesso a variveis locais a e bastante mais rpido do que o acesso a variveis locais. a a Vamos apresentar um pouco da sintaxe de Lua atravs do programa a seguir e que l um nmero da entrada e retorna verdadeiro se o nmero par e falso e u u e caso contrrio. O exemplo no se prope a apresentar a melhor soluo, mas a a a o ca apresentar diferentes aspectos da linguagem. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 function odd (n) if n == 0 then return false else return even (n-1) end end function even (n) if n == 0 then return true else return odd (n-1) end end local n = io.read ("*number") print (even (n))

Em Lua, funes so declaradas usando a palavra-chave function. A funo co a ca odd retorna true se um nmero u e mpar e false caso contrrio, ao passo que a a funo even faz o oposto. ca Essas duas funes foram escritas de maneira recursiva para enfatizar que co Lua implementa chamadas nais prprias. Isso quer dizer que quando o ultimo o comando executado por uma funo uma chamada a uma outra funo (ou a ca e ca ela mesma), a pilha de execuo no cresce. Ou seja, podemos escrever funes ca a co que utilizam chamadas nais sem nos preocuparmos com o tamanho da pilha de execuo, que permanecer constante. ca a

Na linha 17, declaramos uma varivel local n (notem o uso da palavra-chave a local), que ir receber um nmero digitado pelo usurio. Para ler um nmero a u a u da entrada, utilizamos aqui a funo read da biblioteca padro de entrada e ca a sa io. da Escreva o programa anterior em um arquivo chamado par.lua e em seguida tente execut-lo usando o interpretador lua atravs do seguinte comando: a e lua par.lua Uma soluo mais simples para o problema de determinar se um nmero ca u e par pode ser obtida atravs do uso do operador mdulo de Lua %, que retorna e o o resto de uma diviso: a 1 2 local n = io.read ("*number") print (n % 2 == 0)

1.3

Tipos

Em Lua, no declaramos o tipo de uma varivel. O tipo da varivel detera a a e minado dinamicamente, dependendo do valor que ela est armazenando. Sendo a assim, uma varivel pode armazenar valores de qualquer um dos tipos bsicos a a de Lua. Existem oito tipos bsicos em Lua: nil, boolean, number, string, function, a userdata, thread e table. O tipo nil possui nil como seu unico valor, de modo que nenhum outro valor igual a nil. Em geral, usamos esse tipo para indicar a ausncia de um valor. e e O tipo boolean representa os valores booleanos true e false. Uma expresso a ser considerada falsa caso seja igual a nil ou a false, sendo verdadeira caso a contrrio (inclusive quando ela for 0). a Ao contrrio da maioria das linguagens, Lua possui somente um tipo numrico a e bsico, number, que ponto utuante por padro. No h um tipo espec a e a a a co para nmeros inteiros, mas o programador no precisa se preocupar com isso, u a pois Lua pode representar qualquer nmero inteiro de 32 bits corretamente e u faz isso de maneira transparente. O tipo string representa cadeias de caracteres, as quais podem ser delimitadas por aspas duplas ou simples. Se uma cadeia comea com aspas simples, ela c deve terminar com aspas simples. De maneira anloga, se uma cadeia comea a c com aspas duplas, ela deve terminar com aspas duplas. Para escrever uma cadeia que se estende por vrias linhas, deve-se usar a notao [[ para abrir a a ca cadeia e ]] para fechar a cadeia. Cadeias de caracteres so imutveis em Lua, toda vez que voc altera uma a a e cadeia, voc est na verdade criando uma nova cadeia. e a Function o tipo que representa funes. Em Lua, funes so valores de e co co a primeira classe, isso quer dizer que um valor do tipo funo no requer nenhum ca a tratamento especial. Dessa forma, a mesma varivel que armazena um valor a numrico, pode tambm armazenar uma funo. Dado que em Lua funes so e e ca co a valores de primeira classe, temos que a sintaxe: 6

function foo (n) ... end E apenas um aucar sinttico para: c a foo = function (n) ... end Ou seja, estamos armazenando na varivel foo um valor do tipo function. a O tipo userdata usado para armazenar dados C em variveis Lua. Valores e a deste tipo somente podem ser criados ou modicados atravs da API de Lua e com C. Valores do tipo thread representam uxos de execuo independentes. O tipo ca thread (no confundir com processos leves, threads, do sistema operacional) d a a suporte ao uso de co-rotinas, que sero abordadas mais adiante. a Por m, o tipo table representa uma tabela Lua. Tabelas so o unico mea canismo de estruturao de dados de Lua. Com tabelas podemos representar ca arrays, tabelas de s mbolos, conjuntos, grafos, registros, etc. Podemos usar qualquer valor para indexar uma tabela, exceto nil. Usamos colchetes para indexar elementos da tabela. Seja t uma tabela, t[1] representa o elemento na posio 1 da tabela e t[x] o valor associado com a ca chave x. Neste ultimo caso, podemos usar tambm o aucar sinttico t.x. e c a A seguir, ilustramos como uma varivel Lua pode armazenar qualquer um a dos tipos bsicos. Para saber qual o tipo corrente de uma varivel, usamos a a e a funo type. ca -- Dois traos seguidos indicam um comentrio c a local a = 3 print (type (a)) -- imprime "number" a = "bola" print (type (a)) -- imprime "string" a = true print (type (a)) -- imprime "boolean" a = print -- "a" agora a fun~o "print" e ca a (type (a)) -- imprime "function" Dentre os tipos bsicos de Lua, temos que valores do tipo function, userdata, a thread e table representam objetos. Dessa forma, uma varivel que representa a um valor deste tipo no armazena o valor em si, mas apenas uma referncia a e para ele.

1.4

Operadores Relacionais

Lua possui os seguintes operadores relacionais:

<

>

<=

>=

==

~=

Os cinco primeiros operadores so bastante comuns em outras linguagens e a possuem o signicado usual. O operador relacional = signica a negao da ca igualdade. Todos os operadores relacionais retornam true ou false. Quando comparamos valores que so objetos (valores do tipo function, usera data, thread e table), temos que dois valores sero iguais somente se eles reprea sentam o mesmo objeto. Como o exemplo abaixo ilustra: local tab1 = {} local tab2 = {} tab1.x = 33 tab2.x = 33 -- cria uma tabela

-- associa valor 33 com chave "x"

print (tab1 == tab2) -- imprime "false" tab2 = tab1 print (tab1 == tab2) -- imprime "true" tab2.x = 20 print (tab1.x)

-- imprime 20, pois tab1 e tab2 se referem ao mesmo valor

1.5

Operadores Lgicos o

Lua possui os operadores lgicos and, or e not. Os operadores and e or usam o avaliao de curto circuito, de modo que o segundo operando s avaliado se ca oe necessrio. a O resultado do operador not sempre um valor booleano. Ao passo que e or retorna o primeiro operando que no nil ou false. Um idioma comum em a e Lua usar operador or para atribuir um valor padro a uma varivel, como e a a ilustrado a abaixo: function initx (v) x = v or 100 end Seja initx uma funo que inicializa a varivel x com o valor v, a expresso ca a a x = v or 100 indica que quando v for um valor falso, ento a varivel x ser a a a inicializada com o valor 100. O operador and por sua vez, retorna seu primeiro argumento se ele falso e e o seu segundo argumento caso contrrio. A seguir, temos alguns exemplos a envolvendo os operadores lgicos: o -- Usaremos "-->" para indicar a sada do comando print (34 or nil) --> 34 8

print print print print print

(not 34) --> (true and 0) --> (not not 0) --> (false or "bola") --> (n and "33" or "34")

false 0 true bola

E poss denir em Lua um operador ternrio, semelhante ao operador ? vel a de C, usando os operadores lgicos and e or. Uma expresso do tipo x ? y : z, o a onde y no um valor falso (i.e., y diferente de nil e de false), pode ser a e e representada em Lua como: x and y or z Usando novamente o exemplo envolvendo nmeros pares, podemos inicializar u uma varivel s com um determinado valor se n um nmero par e com outro a e u valor caso contrrio. a s = (n % 2 == 0) and "par" or "mpar"

1.6

Variveis a

Para declarar uma varivel em Lua, basta escrever o seu nome (que deve comear a c com uma letra ou um sublinhado). Caso nenhum valor seja atribu ` varivel do a a quando ela for declarada, seu valor ser nil. a Para atribuir um valor a uma varivel, use o operador de atribuio =, como a ca ilustrado a seguir: x = 1 b, c = "bola", 3 y print (b, y) -- x recebe 1 -- b recebe o valor "bola" e c o valor 3 -- o valor de y nil e --> bola nil

Ao fazer uma atribuio, todos os valores do lado direito so avaliados em um ca a primeiro momento, e depois estes valores so atribu a dos `s variveis do lado a a esquerdo da atribuio. ca E poss vel termos uma atribuio onde o nmero de variveis no lado esca u a querdo diferente do nmero de valores no lado direito. Caso existam mais e u valores do que variveis, os valores extra so descartados. Caso contrrio, as a a a variveis extra recebem nil. O exemplo a seguir ilustra esses caso: a a, b, sobrei = 1, 2 print (a, b, sobrei) --> 1 2 nil

x, y = "bola", "casa", "sobrei" print (x, y) --> bola x, y = y, x print (x, y)

casa

-- faz a troca de valores (o lado direito avaliado antes do esquerdo) e --> casa bola 9

Como mencionamos anteriormente, variveis so globais por padro, de a a a modo que devemos usar a palavra-chave local para declarar uma varivel como a local. O escopo de uma varivel local determinado pelo bloco no qual ela foi a e declarada. Uma varivel local declarada fora de qualquer bloco vis a e vel em todo o arquivo. Um bloco pode ser criado com as palavras chaves do, para abrir um bloco, e end, para fechar um bloco, ou atravs do uso de uma estrutura de e controle (if, while, etc). A seguir, temos um exemplo que ilustra o escopo de variveis em Lua: a local x = 33 local y print (x, y) if x > 10 then local x = 5 y = 9 print (x, y) else x = 2 print (x, y) end print (x, y)

--> 33

nil

-- alterando um "x" local --> 5 9

-- alterando o "x" mais externo --> 2 nil --> 33 9

E comum em Lua usar variveis locais para armazenar valores de algumas a variveis globais, uma vez que o acesso a variveis locais mais rpido em Lua. a a e a Seja sqrt uma funo global que calcula a raiz quadrada de um nmero, um ca u programa Lua que usa esta funo teria uma declarao: ca ca local sqrt = sqrt A varivel local sqrt somente vis aps a linha da sua declarao, uma vez a e vel o ca que todos os valores do lado direito de uma atribuio so primeiro avaliados e ca a depois atribu dos. Todas as variveis globais de Lua, incluindo funes como print, esto ara co a a mazenadas na tabela G, de modo que quando tentamos acessar uma varivel que no local, o interpretador ir automaticamente procurar pela varivel na a e a a tabela de globais G, ou seja, print e G.print representam a mesma funo. ca

1.7

Criando Tabelas

Existem vrias formas de criar uma tabela em Lua usando o operador {}. Para a criar uma tabela t vazia, tudo que devemos fazer t = {}. e E muito comum, contudo, querermos iniciar uma tabela com alguns valores, ao invs de criarmos uma tabela completamente vazia. O programa a seguir e cria uma tabela inicializando-a como um array: -- Cria e inicializa a tabela t local t = {4, "lua", false} 10

Aps a execuo do trecho de cdigo acima, t[1] possui o valor 4, t[2] a cadeia o ca o lua e t[3] o valor false. Outra maneira de inicializar uma tabela como um array associativo, nesse e caso devemos fornecer dois valores para cada posio da tabela, um indicando ca a chave e o outro o valor associado com aquela chave: local t = {x=100, y=200, w=50} print (t ["y"], t.w) --> 200

50

Aps a execuo do trecho de cdigo acima, t uma tabela que possui trs o ca o e e chaves: x, com o valor associado 100; y, com o valor associado 200; e w, que possui 50 como valor associado. Para inserir um novo valor em uma tabela, basta fazer uma atribuio: ca t[100] = true t["a"] = "A" t.curso = "Lua" Outra maneira de inserir valores em uma tabela que est sendo utilizada a como array atravs da funo table.insert, que insere um valor em uma dee e ca terminada posio do array (deslocando os demais elementos) ou no nal do ca array, se nenhuma posio especicada. O exemplo a seguir ilustra o uso de ca e table.insert: local t = {10, 20, 30} table.insert (t, 2, 15) -- Agora temos t[1] = 10, table.insert (t, 35) print (t[1], t[3], t[5])

-- Insere 15 na posi~o 2 ca t[2] = 15, t[3] = 20 e t[4] = 30 -- Insere 35 no final da tabela --> 10 20 35

1.8

Tamanho de Tabelas

Se uma tabela estiver sendo utilizada como um array poss saber o nmero e vel u de elementos do array usando o operador de tamanho #. O operador # retorna um ndice n do array tal que o valor na posio n do ca array diferente de nil e o valor na posio n + 1 nil. Em virtude disto, evite e ca e usar o operador # com arrays que possam conter valores nil no meio deles. O exemplo a seguir ilustra o funcionamento do operador #. local t = {"a", "b", "c"} print (#t) --> 3 table.insert (t, "d") print (#t) --> 4 t [6] = "8" -- Temos um valor nil no meio do array (t[5]). O operador de tamanho n~o tem um a -- comportamento bem definido neste caso

11

print (#t) --> ?? t [5] = "e" -- Agora o array n~o tem "buracos" e o operador # retorna o valor esperado a print (#t) --> 6

1.9
1.9.1

Estruturas de Controle
If

A estrutura de controle bsica de Lua o if. Caso a condio do if seja vera e ca dadeira, a parte then executada, caso contrrio, a parte else executada. A e a e seguir temos um exemplo da utilizao do if. ca local x = 44 if x > 30 then print ("maior que 30") elseif x > 20 then print ("maior que 20") elseif x > 10 then print ("maior que 10") else print ("que x pequeno") end

1.9.2

While

O while uma das estruturas de repetio que Lua possui. Caso a condio do e ca ca lao seja verdadeira, o seu corpo executado e a condio ser ento reavaliada, c e ca a a caso contrrio o lao termina. O exemplo a seguir ilustra o uso de while: a c local i while i print i = i end = 0 < 10 do (i) + 1

1.9.3

For numrico e

Em Lua, existem dois tipos de for : o numrico e o genrico. O for numrico e e e possui a seguinte sintaxe: for var=exp1, exp2, exp3 do corpo_do_lao c end Onde exp1 o valor inicial de var e corpo do lao ser executado enquanto e c a var for menor ou igual exp2, no caso de um passo positivo, ou maior ou igual 12

a exp2, no case de um passo negativo. A expresso exp3 representa o passo do a lao. Quando exp3 no especicada, assume-se que o seu valor 1. A seguir, c a e e temos um exemplo que usa o for numrico: e for i=1, 10 do print (i) end for i=10, 1, -1 do print (i) end A varivel var vis somente no corpo do lao e no deve ser alterada. a e vel c a Caso queira sair do lao antes do seu nal, voc deve usar o comando break. c e

1.9.4

For genrico e

O for genrico usado para percorrer os valores retornados por uma funo e e ca iteradora. Os principais iteradores fornecidos por Lua so: pairs, para percorrer a as chaves de uma tabela; ipairs, para percorrer os ndices de um array; e io.lines, para percorrer as linhas de um arquivo. Seja a um array, podemos imprimir todos os seus valores com o seguinte for genrico: e for i, v in ipairs (a) do print (v) end -- i guarda o ndice, v o valor

Para imprimir todas as chaves de uma tabela t (em uma ordem qualquer), usamos o seguinte trecho de cdigo: o for k, v in pairs (t) do print (k, v) end Exerc cio 1.9.4 Dado um arquivo de texto onde as linhas mpares do arquivo possuem o nome de uma pessoa e as linhas pares o sobrenome correspondente ao nome da linha anterior, escreva um programa Lua que l todas as linhas do arquivo, armazena e as informaes em uma tabela, onde cada elemento da tabela possui uma chave co nome e uma chave sobrenome, e imprime a tabela resultante. Utilize o for genrico e a funo iteradora io.lines (nomedoarquivo) para ler e ca as linhas do arquivo. -- k guarda a chave, v o valor

1.10

Saindo de um bloco

Para sair de um bloco de programa, usamos os comando break e return. O comando break muito utilizado em laos, pois permite terminar a execuo do e c ca lao. c

13

O comando return permite retornar valores de uma funo ou simplesmente ca terminar a execuo de uma funo. Em Lua, funes podem retornar mltiplos ca ca co u valores, de modo que o comando return pode receber mltiplos argumentos. u Ao usar um comando return ou break existe a restrio de que este comando ca deve ser o ultimo comando do bloco. O exemplo a seguir um cdigo Lua que e o no compila por este motivo: a i = 5 while i < 10 do break -- Erro! break deve ser o ltimo comando do bloco u i = i + 1 -- Atribui~o o ltimo comando do bloco "while" ca e u end Existem duas solues para este problema. Reordenar seu cdigo, de modo co o que o comando break ou return seja o ultimo comando do bloco, ou criar um novo bloco do-end ao redor do comando, como ilustrado a seguir: i = 5 while i < 10 do do break -- Ok! break agora o ltimo comando do bloco e u end i = i + 1 -- Atribui~o o ltimo comando do bloco "while" ca e u end

14

Cap tulo 2

Funes co
2.1 Declarando e Chamando uma Funo ca

Uma funo pode ser declarada da seguinte forma: ca function nomedafun~o (arg_1, arg_2, ..., arg_n) ca corpoDaFun~o ca end a ca Onde as variveis arg N indicam os parmetros da funo. Em Lua, uma a funo pode ter desde nenhum parmetro at centenas deles. Da mesma forma, ca a e uma funo pode no retornar nenhum valor ou retornar vrios. ca a a Vamos denir uma funao foo, que recebe dois parmetros e retorna a soma c a e produto deles: function foo (a, b) local x = a or 1 -- x recebe o valor padr~o 1 quando a um valor falso a e local y = b or 1 -- y recebe o valor padr~o 1 quando b um valor falso a e return x + y, x * y end Embora a funo foo possua dois parmetros, poss cham-la com um ca a e vel a nmero diferente de parmetros. No caso de chamarmos foo com mais parmetros u a a do a que funo espera, os valores extra so descartados. Caso a funo seja ca a ca chamada com um nmero menor de parmetros, o valor dos parmetros que u a a no foram fornecidos nil. O exemplo a seguir ilustra estas situaes: a e co s, p = foo (3, 4) print (s, p) s, p = foo (2) print (s, p) -- a 3 e b 4 e e --> 7 12 -- b nil e y inicializado com 1 e e --> 3 2

15

s, p = foo (2, 5, 8) print (s, p) print (foo ())

-- 8 descartado e --> 7 10 --> 2 1

2.2

N mero varivel de parmetros u a a

Funes com um nmero varivel de parmetros podem ser declaradas usando co u a a trs pontos (...). Para acessar ento os parmetros que foram chamados, usamos e a a a notao {...}, para criar um array onde o primeiro parmetro da funo est ca a ca a na posio 1, o segundo na posio 2 e assim por diante. O exemplo a seguir ca ca mostra uma funo que recebe cadeias de caracteres e imprime aquelas com ca tamanho maior do que 3. function maior3 (...) for i, v in ipairs {...} do if #v > 3 then print (v) end end end -- percorre a lista de par^metros a -- # o operador de tamanho para cadeias e arrays e

maior3 ("bola", "sol", "lua", "balao")

-- Imprime "bola" e "balao"

2.3

Retornando um unico valor

Em Lua, uma funo pode no retornar nenhum valor, retornar apenas um valor ca a ou retornar mltiplos valores. Porm, nem sempre iremos obter todos os valores u e retornados por uma funo. ca Quando estamos avaliando uma lista de expresses, iremos obter somente o o primeiro valor de retorno de cada membro da lista, com exceo da ultima ca expresso, que pode retornar mltiplos valores normalmente. O uso abaixo da a u funo foo denida anteriormente ilustra este caso: ca a, b, c, d = foo (1, 2), foo (3, 4), foo (5, 6) print (a, b, c, d) --> 3 7 11 30 Podemos notar que a chamada f oo(1, 2) retornou apenas o valor 3, sendo que o mesmo aconteceu com a chamada f oo(3, 4), que retornou apenas o valor 7. A chamada f oo(5, 6), como a ultima expresso da lista de expresses, retornou e a o dois valores, 11 e 30. ` As vezes ns tambm queremos que uma chamada de funo retorne somente o e ca um valor, no importando se a chamada de funo faz parte de uma lista de a ca expresses ou no. Nesse caso, devemos colocar parnteses ao redor da chamada o a e de funo, isso limita o nmero de valores de retorno da funo a no mximo 1, ca u ca a como mostra o exemplo a seguir: 16

a, b = foo (5, 10) print (a, b) a, b = (foo (5, 10)) print (a, b) a, b, c = foo (1, 2), print (a, b, c)

--> 15

50

--> 15 nil (foo (3, 4)) --> 3 7 nil

2.4

Valores de Primeira Classe

Funes em Lua so valores de primeira classe, o que quer dizer que podeco a mos passar funes como argumentos para outras funes, bem como retornar co co funes. co Para ilustrar o uso de funes como valores de primeira classe, vamos denir co uma funo map, que recebe uma tabela e uma funo e aplica a funo sobre ca ca ca todos os elementos da tabela. Abaixo, temos uma denio da funo map: ca ca function map (f, t) for k, v in pairs (t) do t [k] = f (v) end end Considerando que os valores armazenados na tabela so nmeros, caso se a u deseje incrementar todos os elementos da tabela em 1 unidade, podemos fazer a seguinte chamada: function inc (v) return v + 1 end map (inc, t) Onde t uma tabela Lua. Uma maneira mais compacta de fazer a chamada e anterior denindo a funao de incremento no momento em que fazemos a e c chamada ` funo map: a ca map (function (v) return v + 1 end, t)

2.5

Fechos

E comum querermos associar variveis externas com uma funo que vai ser a ca denida, criando um fecho (closure). Vamos tomar como exemplo a funo generateinc denida a seguir. Essa ca funo usada para denirmos uma nova funo de incremento, baseada em ca e ca um valor inicial e um valor de incremento. A funo generateinc retorna ento ca a uma funo de incremento, a qual possui referncias para as variveis externas ca e a n e s.

17

local i = 3 function generateinc (init, step) local n = init or 0 local s = step or 1 return function () n = n + s -- referencia as variveis externas n e s a return n end end local inc = generateinc () print (inc ()) --> 1 local inc_ = generateinc (i) print (inc (), inc_ ()) --> 2 i = 10 print (inc (), inc_ ())

--> 3

local inc10 = generateinc (5, i) print (inc (), inc_ (), inc10 ())

--> 4

15

Quando a funo de incremento retornada, ela possui um estado associado, ca e no qual ela ir guardar o valor das variveis n e s. Usando generateinc, criamos a a uma funo inc, que ir funcionar como um contador de passo 1, comeando a ca a c partir de 0. e Em seguida, criamos inc , que tambm possui passo 1, mas que incrementa valores a partir de 3. Por ultimo, criamos a funo inc10, cujo passo 10 e cujo ca e valor inicial do contador 5. e

18

Cap tulo 3

Manipulando Cadeias
Em Lua, cadeias de caracteres so imutveis, isto , toda vez que tentamos a a e alterar uma cadeia de caracteres, no estamos realmente modicando a cadeia a original, mas sim criando uma nova cadeia. Cada cadeia em Lua unica, de e modo que a comparao de duas cadeias muito rpida (uma mera comparao ca e a ca de ponteiros C). A manipulao de cadeias em Lua feita atravs da biblioteca padro string. ca e e a Ao usar as funes da biblioteca string, lembre-se que em Lua o primeiro caracco tere da cadeia est no a ndice 1. Podemos acessar partes de uma cadeia usando a funo string.sub, como ca mostrado a seguir: s = "bolada" print (string.sub print (string.sub print (string.sub print (string.sub print (string.sub (s, (s, (s, (s, (s, 1, 3)) 1)) 5, 6)) 5)) -4)) --> --> --> --> --> bol bolada da da lada

O primeiro parmetro de string.sub uma cadeia e o segundo parmetro indica a e a a posio inicial da subcadeia que queremos obter. string.sub possui ainda um ca terceiro parmetro, que opcional e indica a posio nal da subcadeia. a e ca Caso o primeiro parmetro de string.sub seja negativo, a indexao da cadeia a ca se dar a partir do seu nal. a O tamanho de uma cadeia pode ser obtido atravs do operador # ou da e funo string.len: ca a = "boi" b = "formiga" print (#a, #b) print (string.len (a)) print (#string.sub (b, 3))

--> 3 --> 3 --> 5

19

3.1

Concatenando Cadeias

Para concatenar duas cadeias, podemos usar o operador de concatenao .. , ca como ilustrado a seguir: a = "seg" .. "unda" b = "feira" print (a .. "-" .. b)

--> segunda-feira

Como cada cadeia em Lua unica, devemos tomar um pouco de cuidado ao e escrevermos um programa que pode criar milhares de novas cadeias. Seja t uma tabela Lua que representa um array onde os elementos so cadeias a de caracteres. Queremos escrever um programa que cria uma cadeia com todos os elementos de t separados por v rgulas e depois imprime a cadeia resultante. Uma maneira simples de escrever esse programa mostrada a seguir: e local s = "" for i, v in ipairs (t) do s = s .. v .. "," end print (string.sub (s, 1, #s - 1)) Caso t tenha poucos elementos, o desempenho do programa anterior ser saa tisfatrio. Porm, caso t possua alguns milhares de elementos, a execuo do o e ca programa anterior ir demorar mais do que o esperado. Isso se deve ao fato a de que a cada iterao do lao estamos criando uma nova cadeia que ser simca c a plesmente descartada na prxima iterao, podendo ser recolhida depois pelo o ca coletor de lixo. Como em Lua cada cadeia unica, a criao de milhares de novas cadeias (e e ca a interao desse processo com o coletor de lixo) um pouco custoso, de forma ca e que bom evitarmos fazer isso quando existem solues melhores. e co Uma maneira mais eciente de concatenar vrias cadeias atravs da funo a e e ca table.concat. A funo table.concat recebe uma tabela como primeiro parmetro ca a e uma cadeia como segundo parmetro, que ser usada para separar os elementos a a da tabela. O programa abaixo realiza a mesma tarefa que o programa anterior: local s = table.concat (t, ",") print (s) Mesmo quando as cadeias que desejamos concatenar no esto armazenadas em a a uma tabela, em geral mais eciente inserir os elementos na tabela e depois e usar table.concat do que criar milhares de cadeias intermedirias. a

3.2

Busca e Substituio ca

Operaes de busca e substituio envolvendo cadeias de caracteres so bastante co ca a comuns. Em Lua, podemos buscar uma subcadeia usando a funo string.nd. ca O primeiro parmetro de string.nd a cadeia na qual ser feita a busca e o a e a 20

segundo parmetro indica um padro de busca. No explicaremos aqui como a a a construir um padro de busca usando caracter a sticas de expresses regulares, o iremos usar somente padres simples. Para maiores detalhes sobre a biblioteca o de cadeias de Lua e os padres de busca/substituio que ela suporta, por favor o ca consulte o manual de referncia da linguagem. e O programa a seguir imprime todas as subcadeias que se encontram entre v rgulas. O terceiro parmetro de string.nd opcional e indica a posio inicial a e ca da busca. Quando uma busca bem-sucedida, as posies de in e co cio e m do padro na cadeia original so retornadas, caso contrrio, o valor de retorno a a a e nil: local s = "boi,bola,balao" local i = 1 local j = string.find (s, ",") while j do print (string.sub (s, i, j-1)) i = j + 1 j = string.find (s, ",", i) end print (string.sub (s, i)) Para obter uma nova cadeia a partir da cadeia s do exemplo anterior, com todas as v rgulas sendo substitu das por espaos, podemos usar a funo string.gsub: c ca local s = "boi,bola,balao" local news = (string.gsub (s, ",", " ")) print (news) --> boi bola balao" O segundo parmetro de string.gsub a cadeia a ser substitu e o terceiro ina e da dica a cadeia de substituio. A funo string.gsub retorna tambm um segundo ca ca e resultado, o nmero de substituies que foram realizadas. u co O quarto parmetro de string.gsub opcional e serve para especicar o a e nmero mximo de substituies que devem ser feitas. A seguir, usamos novau a co mente string.gsub, indicando que somente 1 substituio deve ser realizada: ca local s = "boi,bola,balao" local news = (string.gsub (s, ",", " ", 1)) print (news) --> boi bola,balao"

21

Cap tulo 4

Tabelas
Tabelas so o unico mecanismo de estruturao de dados em Lua. Tabelas a ca implementam diretamente arrays associativos e arrays numricos, e podem ser e usadas para implementar diversas outras estruturas de dados, como conjuntos e grafos. Alm disso, tabelas so usadas para representar registros, objetos e mdulos e a o Lua.

4.1

Manipulando tabelas

A manipulao de tabelas feita principalmente atravs da biblioteca padro ca e e a de Lua table. Para inserir elementos em uma tabela, usamos a funo insert da ca biblioteca table, como ilustrado a seguir: local t = {} for i=1, 3 do table.insert (t, i) end Quando passamos somente dois parmetros para insert, Lua insere o segundo a parmetro na posio n + 1 da tabela, onde n o tamanho da tabela de acordo a ca e com o operador #. Caso se queira inserir o elemento 4 na posio 2 da tabela, devemos fazer a ca seguinte chamada: table.insert (t, 2, 4) Nesse caso, os elementos da tabela nas posies 2 e 3 vo ser deslocados: co a print print print print (t[1]) (t[2]) (t[3]) (t[4]) --> --> --> --> 1 4 2 3 22

Contudo, no precisamos necessariamente usar table.insert para inserir elea mentos em uma tabela, podemos fazer isso de maneira direta, com uma simples atribuio: ca local t = {} for i=1, 3 do t [i] = i end Ou ainda: local t = {} for i=1, 3 do t [#t+1] = i end Nos exemplos anteriores, zemos com que o primeiro ndice do array esteja na posio 1 da tabela. Em Lua, o primeiro elemento de um array pode estar ca no ndice 0, 1, 33 ou qualquer outro ndice. Contudo, ao usarmos funes das co bibliotecas padres de Lua, assume-se que o primeiro elemento do array est no o a ndice 1, de modo que uma boa prtica fazer com que o primeiro elemento do e a seu array esteja no ndice 1. Da mesma forma que temos uma funo insert, tambm existe uma funo ca e ca remove, que remove o elemento em um dado ndice da tabela ou o ultimo ele mento, caso nenhum ndice seja fornecido: local t = {10, 20, 30} table.remove (t) print (t[1], t[2], t[3]) table.insert (t, 40) table.remove (t, 2) print (t[1], t[2], t[3]) -- remove o ltimo elemento u --> 10 20 nil -- insere elemento no fim -- remove o segundo elemento, deslocando os elementos seguintes --> 10 40 nil

Mais uma vez, podemos remover os elementos diretamente, atribuindo nil a uma determinada posio: ca local t = {10, 20, 30} t [3] = nil print (t[1], t[2], t[3]) t [#t+1] = 40 t [2] = nil print (t[1], t[2], t[3]) -- remove o ltimo elemento u --> 10 20 nil -- insere elemento no fim -- remove o segundo elemento, n~o desloca os elementos restantes a --> 10 nil 40

Ao remover elementos desta forma, devemos tomar cuidado com os buracos que cam na tabela, i.e., valores nil que esto no meio da tabela. a Para ver as outras funes de manipulao de tabelas, consulte o manual de co ca referncia de Lua, dispon em http://www.lua.org/manual/5.1/. e vel

23

4.2

Descrio de Dados ca

Desde o princ pio, Lua foi usada como uma linguagem de descrio de dados. ca Para ilustrar este uso de tabelas, vamos criar um registro com informaes sobre co diversos pa ses: entry { name = "Brasil", capital = "Braslia", netdomain = "br", population = 186757608 } entry { name = "Estados Unidos", capital = "Washington DC", netdomain = "us", population = 304385000 } entry { name = "Argentina", capital = "Buenos Aires", netdomain = "ar", population = 40677348 } Em Lua, podemos chamar uma funo sem colocar parnteses, dado que o ca e argumento da funo uma tabela ou cadeia de caracteres. Vamos ento usar ca e a essa caracter stica da linguagem e denir uma funo entry. ca A funo entry ir receber um argumento e inseri-lo em uma tabela, contendo ca a o registro de todos os pa ses: function entry (e) table.insert (t, e) end Supondo que as informaes sobre os pa estejam no arquivo paises.lua, co ses vamos usar a funo dole, que executa o cdigo de um arquivo Lua. O nosso ca o programa principal ca: function entry (e) table.insert (t, e) end local t = {} dofile ("paises.lua")

-- executa arquivo "paises.lua"

24

Ao executarmos o programa cima, cada entrada do arquivo paises.lua ser a interpretada como uma chamada de funo que simplesmente insere o argumento ca fornecido em uma tabela. Podemos, por exemplo, imprimir o nome dos pa cuja populao maior ses ca e do que 100 milhes de habitantes, como ilustrado a seguir, onde temos uma o a varivel chamada . Em Lua, quando no estamos interessados no valor de uma a varivel, comum indicar isso nomeando-a simplesmente como . a e for _, v in pairs (t) do if v.population > 100000000 then print (v.name) end end A denio da funo entry um ponto essencial aqui. Essa funo no ca ca e ca a precisa necessariamente inserir o argumento em uma tabela, ela pode realizar qualquer ao, como imprimir o nome da capital de um pa ca s. function entry (e) print (e.capital) end dofile ("paises.lua") Cuidados ao usar dole Devemos ter um cuidado especial ao usar a funo dole. Em geral, s devemos ca o usar dole quando temos certeza que iremos executar um arquivo Lua que no a produzir erros, caso contrrio, todo o nosso programa Lua ir falhar. a a a Uma abordagem que permite tratar erros quando executando o cdigo de o um arquivo Lua atravs do uso da funo loadle. A funo loadle executa e e ca ca em modo protegido, de modo que quando no h erros, o trecho de cdigo Lua a a o compilado e retornado como uma funo. Caso contrrio, o valor de retorno e ca a nil mais uma mensagem de erro. e Caso deseje utilizar loadle, o trecho de cdigo do exemplo anterior que usa o dole seria substitu por: do local f, s = loadfile ("paises.lua") -- compila o arquivo "paises.lua" if not f then print ("Erro ao compilar paises.lua\n", s) return end f () -- Executa fun~o retornada ca -- executa arquivo "paises.lua", imprimindo o nome das capitais

25

4.3

Meta-mtodos e

Cada tabela pode ter associada a ela uma meta-tabela. Meta-tabelas permitem modicar o comportamento de um tabela, indicando qual ao deve ser tomada ca quando uma operao envolvendo uma tabela realizada. ca e Para saber se uma tabela possui uma meta-tabela associada podemos chamar a funo getmetatable: ca local t = {} print (getmetatable (t)) --> nil

Inicialmente, uma tabela no possui nenhuma meta-tabela associada. Para a fazer isso, podemos usar a funo setmetatable: ca local meta = {} local t = {} setmetatable (t, meta) print (meta, getmetatable (t))

--> table: 0x806f118

table: 0x806f118

Temos agora que meta a meta-tabela de t. e E poss fazer algumas operaes envolvendo tabelas, tais como +, e <, vel co cujo signicado dado atravs de meta-tabelas. Considere o seguinte trecho de e e cdigo Lua: o local t1, t2 = {}, {} t1.x = 20 t1.y = 30 t2.x = 10 t2.y = 5 t1 = t1 + t2 Ao tentarmos execut-lo, iremos obter um erro, pois as tabelas t1 e t2 no a a possuem uma meta-tabela associada indicando qual o comportamento da tae bela quando a operao + realizada. Como as tabelas no possuem uma ca e a operao de soma espec ca ca, Lua tenta realizar a operao de soma usual, a ca qual ir falhar. a Vamos ento denir uma meta-tabela e denir o seu campo add, de modo a a realizar corretamente a operao + envolvendo tabelas: ca local t1, t2 = {}, {} local meta = {} setmetatable (t1, meta) setmetatable (t1, meta) meta.__add = function (a, b) local c = {} setmetatable (c, getmetatable (a)) c.x = a.x + b.x c.y = a.y + b.y return c 26

end t1.x = 20 t1.y = 30 t2.x = 10 t2.y = 5 t1 = t1 + t2 print (t1.x, t1.y)

--> 30

35

Agora, quando a operao + realizada, Lua verica se a tabela t1 possui ca e uma meta-tabela associada. Em caso armativo, Lua procurar por um campo a add nessa meta-tabela, chamando-o, caso ele exista, para realizar a operao ca +. Outra operao que pode ter seu comportamento modicado com o uso de ca meta-mtodos print, atravs da denio do campo tostring da meta-tabela. e e e ca No exemplo anterior, poder amos denir esse campo da seguinte forma, onde .. o operador de Lua que concatena cadeias de caracteres: e meta.__tostring = function (a) return "x = " .. a.x .. ", y = " .. a.y end t1.x = 20 t1.y = 30 t2.x = 10 t2.y = 5 t1 = t1 + t2 print (t1) --> x = 30, y = 35 Para tratar elementos no existentes em tabelas podemos usar os metaa mtodos index e newindex. O meta-mtodo index chamado sempre que tene e e tamos acessar um campo ausente de uma tabela, ao passo que o meta-mtodo e newindex chamado sempre que atribu e mos um valor a um campo ausente de uma tabela. Tomando como base o exemplo anterior, vamos denir um meta-mtodo e index : meta.__index = function (t, k) -- t a tabela que estamos acessando e print ("Acessando elemento n~o inicializado " .. k) a return 0 end t1 = {x=20, y=5} t2 = {x=30}

-- n~o definimos um valor para o campo "y" de t2 a

setmetatable (t1, meta) setmetatable (t2, meta) t1 = t1 + t2 print (t1) --> Acessando elemento n~o inicializado y a -- Quando tentamos acessar t2.y, o meta-mtodo index retorna 0 e --> x = 50, y = 5 27

Note que sempre que criamos uma nova tabela precisamos dizer quem a sua e meta-tabela, caso contrrio a tabela no ter nenhuma meta-tabela associada. a a a Se no meta-mtodo add, por exemplo, no tivssemos atribu a c a mesma e a e do meta-tabela de a, a funo print no usaria o meta-mtodo tostring denido ca a e anteriormente.

4.4

Programao OO em Lua ca

Lua no uma linguagem orientada a objetos, mas prov mecanismos que tora e e nam poss a programao orientada a objetos (POO) (h, inclusive, mais de vel ca a uma maneira de programar orientado a objetos em Lua). A idia central da POO em Lua o uso de prottipos. Podemos entender e e o uma meta-tabela como um prottipo de uma classe. Toda vez que fazemos uma o chamada a um objeto, caso o objeto no implemente aquele mtodo, o campo a e index da sua meta-tabela ser avaliado. Em geral, iremos utilizar um modelo a OO onde o campo index da meta-tabela ser a prpria meta-tabela. Quando o a o campo index uma tabela, ao invs de uma funo, Lua tenta acessar o chave e e ca ausente nessa tabela. Dessa forma, quando um mtodo no for implementado e a por um objeto, iremos procurar pela implementao dele na sua meta-tabela. ca Para mostrar como podemos fazer isso, vamos denir uma classe Rectangle, para representar retngulos. Podemos denir uma estrutura bsica para a nossa a a classe e um construtor da seguinte forma: Rectangle = { width = 0, height = 0 } function Rectangle.new (self, o) o = o or {} setmetatable (o, self) self.__index = self return o end Primeiro, denimos que Rectangle possui dois campos: width e height, ambos inicializados com 0; em seguida, denimos o mtodo new, que dever criar novos e a retngulos. Este mtodo possui dois parmetros: o primeiro parmetro, self, a e a a deve ser um objeto Rectangle; ao passo que o segundo parmetro contm a a e denio de um retngulo (os valores dos seus campos width e height). Caso o ca a segundo parmetro no seja fornecido, um objeto vazio criado. a a e Em seguida, estabelecemos self como a meta-tabela de o e atribu mos self a self. index, de modo que quando tentamos acessar um campo ausente no objeto o, iremos procurar pelo campo em self. No caso de no fornecemos o parmetro a a o para o mtodo new, ao tentarmos acessar o campo width do novo objeto, e iremos na verdade acessar o campo width da sua meta-tabela, que no caso 0. e 28

Podemos evitar o uso expl cito do parmetro self usando a notao Rectana ca gle:new, ao invs de Rectangle.new. Vamos denir mais um mtodo, que calcula e e a rea de um retngulo, j fazendo uso da nova notao. a a a ca function Rectangle:area () return self.width * self.height end Ao usarmos a notao de dois pontos, Lua automaticamente cria um varivel ca a self que referencia o objeto que realizou a chamada ao mtodo. e A seguir, temos um exemplo de uso de objetos Rectangle. Note que as notaes de ponto e de dois pontos tambm se aplicam para a chamada de co e mtodos e podem ser usadas livremente, mtodos declarados usando : podem e e ser chamados com a outra notao e vice-versa. ca local ret1 = Rectangle:new {height=6, width=4} local ret2 = Rectangle.new (Rectangle) print (ret1:area ()) print (ret1.area (ret1)) print (ret2:area ()) --> 24 --> 24 --> 0

4.5

Herana c

Com o modelo de objetos que usamos anteriormente, muito fcil estender/modicar e a a classe Rectangle. Suponha que queremos denir uma nova classe, Cuboid, para representar um paralelep pedo retangular. Nesse caso, cada objeto ter agora a um campo depth, representando a profundidade do objeto, e teremos tambm e que redenir o mtodo que calcula a rea e adicionar um mtodo para calcular e a e o volume do cubide. Temos a seguir a denio de Cuboid : o ca Cuboid = Rectangle:new ({depth=0}) function Cuboid:area () return 2 * (self.width * self.height + self.width * self.depth + self.depth * self.height) end function Cuboid:volume () return self.width * self.height * self.depth end local print local print print c1 = Cuboid:new () (c1:area ()) --> 0 c2 = Cuboid:new {width=1, height=2, depth=5} (c2:area ()) --> 34 (c2:volume ()) --> 10 29

Ao criarmos c1, usaremos todos os valores padres que denimos para width, o height e depth. Ao tentarmos acessar o campo depth do objeto c1, estamos na verdade acessando o campo depth de Cuboid, j que c1 no deniu um campo a a depth. Ao tentarmos acessar os campos height e width de c1, estamos na verdade acessando os campos width e height de Rectangle, j que nem c1 nem Cuboid a deniram estes campos. O objeto c2, por sua vez, dene valores para os campos width, height e depth. Devemos notar que quando chamamos Cuboid:new, na verdade estamos chamando Rectangle:new, uma vez que Cuboid no deniu um mtodo new. a e Tanto c1 como c2 esto usando o mtodo area da classe Cuboid para calcular a e a sua rea. a

30

Cap tulo 5

Mdulos o
O sistema de mdulos de Lua foi padronizado com o surgimento da verso 5.1, o a em 2006. Embora mdulos j existissem e fossem usados h algum tempo pelos o a a desenvolvedores, no existia ainda uma pol a tica bem denida para mdulos. o Segundo a denio da segunda edio de Programming in Lua, um mdulo ca ca o uma biblioteca que pode ser carregada usando-se require e que dene um e unico nome global que armazena uma tabela. Todas as funes e variveis do co a mdulo esto contidas nesta tabela, de modo que podemos dizer que um mdulo o a o Lua nada mais do que uma tabela. Sendo assim, mdulos em Lua so valores e o a de primeira classe tambm e nenhum mecanismo especial precisa ser adicionado e a ` linguagem para que ela d suporte a mdulos. e o Ao fazer require de um mdulo, tambm espera-se que o mdulo retorne o e o a tabela com as suas funes e variveis exportadas. Dessa forma o seguinte co a trecho de cdigo carrega um mdulo e guarda uma referncia para a tabela o o e correspondente em uma varivel local: a local math = require math Podemos tambm armazenar as funes que um mdulo exporta em variveis e co o a locais: require math --carrega mdulo math o local sin = math.sin local sqrt = math.sqrt Os seguintes mdulos fazem parte da biblioteca padro de Lua: o a coroutine: possui as operaes relacionadas com co-rotinas; co string: contm funes que manipulam cadeias de caracteres; e co table: manipulao de tabelas; ca math: mdulo com as funes matemticas; o co a io: biblioteca de entrada e sa (E/S); da 31

package: biblioteca de mdulos. Duas funes dessa biblioteca require o co e module so exportadas diretamente no ambiente global de Lua; a os: implementa facilidades do sistema operacional; debug: biblioteca de depurao. ca O interpretador lua importa automaticamente todos os mdulos da biblioo teca padro, de modo que no preciso fazer require math, por exemplo, para a a e poder usar a biblioteca matemtica. a

5.1

Denindo um Mdulo o

Assim como require uma funo indispensvel para quem deseja usar um e ca a mdulo, a funo module foi criada para facilitar o trabalho dos desenvolveo ca dores de mdulos. Embora seja poss o vel denir mdulos sem usar module, o e prefer vel que se use a funo module, pois ela simplica a denio de um ca ca mdulo e uma soluo padro fornecida pela linguagem. o e ca a A maneira mais simples de declarar um mdulo Lua (e tambm a mais usual) o e colocando a seguinte declarao no comeo do seu mdulo: e ca c o module (..., package.seeall) O primeiro parmetro de module o nome do mdulo. Podemos ento esa e o a crever explicitamente o nome do mdulo ou simplesmente colocar ..., indicando o que o mdulo ter o mesmo nome do arquivo no qual ele est sendo denido. o a a Nesse caso, se temos um arquivo bola.lua, o nome do nosso mdulo ser bola. o a Ao criarmos um mdulo usando module, estamos denindo um novo amo biente para as variveis do mdulo e suas funes. Esse ambiente no contm a o co a e nenhuma biblioteca padro ou funo de Lua. Dessa forma, se quisermos usar a ca uma funo de Lua como print, por exemplo, devemos declarar uma varivel ca a local com o valor dessa funo antes de chamar module. ca Uma maneira fcil de importar todas as funes e bibliotecas padres de Lua a co o fornecendo package.seeall como segundo parmetro para module. e a Na gura 5.1 temos a denio de um mdulo simples, que representa uma ca o frao armazenando o seu numerador e o seu denominador. Supondo que a ca denio foi salva em um arquivo fraction.lua, o nome do nosso mdulo ca o ser fraction. Temos a seguir um trecho de cdigo Lua que importa o mdulo a o o fraction e usa as suas funcionalidades (o arquivo com o cdigo abaixo deve estar o no mesmo diretrio onde est o arquivo fraction.lua): o a require fraction local x = fraction.new (1, 2) local y = fraction.new (5, 3) fraction.print (x) 32 --> 1 / 2

module (..., package.seeall) function new (n, d) return {n=n, d=d} end -- Usamos "local" para declarar uma fun~o que n~o exportada pelo mdulo ca a e o -- Calcula o maior divisor comum local function gcd (a, b) if b == 0 then return a else return gcd (b, a % b) end end -- Calcula o menor mltiplo comum u local function lcd (a, b) return (a * b) / gcd (a, b) end function add (f1, f2) local d = lcd (f1.d, f2.d) local n = (f1.n * d / f1.d) + (f2.n * d / f2.d) return new (n, d) end function sub (f1, f2) return add (f1, new (-f2.n, f2.d)) end function mul (f1, f2) return new (f1.n * f2.n, f1.d * f2.d) end function div (f1, f2) return mul (f1, new (f2.d, f2.n)) end function print (f) -- Temos que referenciar a fun~o "print" global explicitamente, caso ca -- contrrio estaramos acessando recursivamente a fun~o "print" do mdulo a ca o _G.print (f.n .. / .. f.d) end Figura 5.1: Denio do mdulo fraction ca o 33

fraction.print (y) fraction.print (fraction.add (x, y)) local z = fraction.sub (y, x) fraction.print (z) fraction.print (fraction.mul (y, z))

--> 5 / 3 --> 13 / 6

-->

7 / 6

--> 35 / 18

5.2

Entendendo require

Quando chamada, a funo require segue alguns passos para tentar encontrar ca o mdulo desejado. o Primeiro, require tenta achar um mdulo Lua com o nome fornecido, caso o no encontre, tentar ento achar um mdulo C correspondente. a a a o Quando procura por um mdulo Lua, require usa a varivel de ambiente o a LUA PATH ou um caminho pr-denido durante a compilao de Lua. Em e ca sistemas Unix, uma seqncia padro usada por require quando importando ue a um mdulo primeiro procur-lo no diretrio atual e depois nos diretrios o e a o o /usr/local/share/lua/5.1/ e /usr/local/share/lua/5.1/nomedomodulo/init.lua. Usando a varivel LUA PATH, poder a amos especicar esta seqncia de busca ue da seguinte forma, onde os pontos de interrogao so trocados pelo nome do ca a mdulo que se est procurando o a ?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua Caso a funo require no encontre um mdulo Lua, ela ir procurar por ca a o a um arquivo .so (ou .dll no caso do Windows) no diretrio atual e no dio retrio /usr/local/lib/lua/5.1. Neste caso, a varivel usada por require o a LUA CPATH, a qual poderia ser congurada da seguinte forma: e ?.so;/usr/local/lib/lua/5.1/?.so Quando um mdulo carregado, uma entrada correspondente atualizada o e e na tabela package.loaded, de modo que ao tentarmos carregar um mdulo de o novamente, a entrada armazenada em package.loaded retornada. Para forar e c que um mdulo seja carregado novamente, podemos fazer: o package.loaded[meuModulo] = nil require meuModulo

5.3

Instalando Mdulos o

Uma maneira bsica de instalar um mdulo simplesmente copiando o arquivo a o e .lua, .so ou .dll para um diretrio onde o interpretador de Lua poder eno a contr-lo. a 34

Outra abordagem modicar as variveis LUA PATH e LUA CPATH de e a modo que elas apontem para o diretrio onde o seu mdulo se encontra. o o Uma abordagem mais recente e que facilita a vida de usurios e desenvolvedoa res de mdulos Lua utilizar LuaRocks1 . LuaRocks um sistema de instalao o e e ca e gerenciamento de mdulos Lua. o LuaRocks baseado no conceito de rocks. Podemos entender um rock como e um mdulo Lua. Associado a um rock temos o seu rockspec, que um aro e quivo que contm informaoes sobre a instalao do mdulo, documentao e e c ca o ca dependncias do mesmo. e Para instalar um mdulo usando LuaRocks, tudo que temos que fazer : o e luarocks install nomedomodulo Em seguida, para utilizar o mdulo instalado dessa forma em um programa Lua, o devemos usar luarocks.require: require luarocks.require E depois utilizar require normalmente.

5.4

Entendendo um rockspec

Para um usurio de mdulos, um arquivo rockspec pode ser visto como uma a o caixa preta. Em geral, um usurio de mdulos no precisar entender ou modia o a a car um rockspec. Quando for necessrio fornecer o valor de alguma varivel de a a congurao, de forma que ela reita o local correto no qual uma determinada ca biblioteca foi instalada no seu sistema, prefer e vel que se faa esta operao c ca via linha de comando (tente luarocks help e veja as opes dispon co veis), do que atravs da modicao de um rockspec. e ca Para um desenvolvedor de mdulos, por outro lado, fundamental que ele o e entenda um rockspec, de forma a poder escrever a especicao do seu mdulo, ca o facilitando a vida de pessoas que desejam utiliz-lo. a Na gura 5.2, podemos ver o exemplo do rockspec de um mdulo Lua. O o nome do pacote Shake e a verso 1.0.1 (o suxo 1 indica apenas a verso do e a e a rockspec, no do pacote em si). Em seguida, temos a indicao de onde o cdigo a ca o fonte do mdulo est dispon o a vel. LuaRocks ir ento procurar pelo mdulo no a a o endereo fornecido, fazer o download, compilar os arquivos e instalar o pacote. c A varivel description possui um descrio do mdulo, indicando tambm a ca o e qual a sua licena. A seguir, temos a varivel dependencies, que lista as e c a dependncias de um mdulo. Shake possui trs dependncias: a verso 5.1 de e o e e a Lua (ou alguma verso mais nova); a verso 0.1.2 de leg; e a verso 1.3.0 de a a a lualesystem. Tirando a dependncia de Lua 5.1, todas as outras dependncias, e e que possuem rocks no repositrio de LuaRocks, sero instaladas por LuaRocks, o a que ir, se necessrio, instalar tambm as dependncias de leg e lualesystem. a a e e
1 Para uma referncia mais completa sobre LuaRocks, por favor consulte a pgina da fere a ramenta em http://www.luarocks.org/

35

package = "Shake" version = "1.0.1-1" source = { url = "http://luaforge.net/frs/download.php/3009/shake-1.0.1.tar.gz" } description = { summary = "A Simple Lua Test Engine", detailed = [[ Shake is a simple and transparent test engine for Lua that assumes that tests only use standard assert and print calls. Shake uses Leg and LPeg to preprocess test files and extract a lot more information than what is usually available when tests use standard Lua assertions. ]], license = "MIT/X11", homepage = "http://shake.luaforge.net/" } dependencies = { "lua >= 5.1", "leg >= 0.1.2", "luafilesystem >= 1.3.0", } build = { type = "make", variables = { LUA_DIR = "$(LUADIR)", SYS_BINDIR = "$(BINDIR)" } } Figura 5.2: Rockspec do mdulo Shake o Caso o mdulo que voc queira instalar seja uma ligao (binding) para uma o e ca biblioteca C externa, voc deve primeiro instalar a biblioteca C antes de usar o e LuaRocks para instalar o mdulo que depende dela. o A ultima parte do rockspec contm a descrio da varivel build, que ir e ca a a dizer ao LuaRocks como o mdulo deve ser instalado. O campo type indica o que ferramenta devemos usar para construir o mdulo: make, cmake ou module o (no caso de module, LuaRocks usar seu prprio sistema para instalao de a o ca mdulos). o Caso deseje obter uma explicao mais detalhada sobre os demais campos ca do rockspec, por favor consulte o site do LuaRocks.

36

Cap tulo 6

API C
Atualmente, muito comum desenvolvermos partes de uma aplicao utilizando e ca uma linguagem de script e outra parte utilizando uma linguagem de mais baixo n que possua um melhor desempenho. vel Em 90% dos casos, a linguagem de mais baixo n escolhida C, de modo vel e que vrias linguagens de script como Python, Perl e Ruby possuem alguma a forma de se comunicar com C. Lua no diferente e disponibiliza uma API para que o cdigo Lua possa se a e o comunicar com cdigo C e vice-versa. o Lua uma linguagem extens pois podemos denir funes em C e use vel co a las em programas Lua. Ao mesmo tempo, Lua tambm uma linguagem de e e extenso, uma vez que poss embutir Lua em uma aplicao C. a e vel ca O modelo para se comunicar com cdigo C oferecido por Lua baseado em o e uma pilha virtual, onde tanto o cdigo C como o cdigo Lua podem colocar e o o retirar valores na pilha. As funes que manipulam a pilha virtual possuem como parmetro uma co a estrutura lua State, que armazena informaes sobre o estado do programa Lua. co Nenhuma informao sobre o estado armazenada em variveis globais, de modo ca e a que podemos ter vrios lua State independentes. a

6.1

Primeiros Passos

Para termos acesso ao conjunto de funes que fazem a comunicao entre Lua e co ca C devemos incluir alguns arquivos de cabealho em nosso programa C. O arquivo c lua.h onde esto as funes bsicas fornecidas por Lua para manipular a pilha e a co a virtual. O nome das funes relacionadas com esse arquivo comea com lua . co c O arquivo lauxlib.h dene algumas funes de mais alto n co vel usando a API bsica fornecida por lua.h. As funes dispon a co veis em lauxlib.h no a acessam nenhum valor interno de Lua, elas so implementadas usando somente a as funcionalidades providas pela API bsica de comunicao com C. O nome a ca das funes relacionadas com lauxlib.h possuem o prexo luaL . co

37

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

#include #include #include #include #include

<stdio.h> <string.h> "lua.h" "lauxlib.h" "lualib.h"

int main () { char buff [256]; int error; lua_State *L = luaL_newstate (); /* cria um novo estado Lua */ luaL_openlibs (L); /* d acesso a todas as bibliotecas padr~es */ a o while (fgets (buff, sizeof (buff), stdin) != NULL) { error = luaL_loadbuffer (L, buff, strlen (buff), "line") || lua_pcall (L, 0, 0, 0); if (error) { fprintf (stderr, "%s", lua_tostring (L, -1)); lua_pop (L, 1); /* retira a mensagem de erro da pilha */ } } lua_close (L); return 0; } Figura 6.1: Embarcando o interpretador Lua em um programa C

O arquivo lualib.h, por sua vez, d acesso `s bibliotecas padres de Lua a a o (math, table, io, etc) e deve ser inclu caso se queira ter acesso a elas. do O exemplo apresentado na gura 6.1 aparece no livro Programming in Lua e ilustra como poss embarcar o interpretador Lua em uma aplicao C. e vel ca Na linha 10, um novo estado Lua criado atravs da funo luaL newstate. e e ca Essa a unica funo da API C de Lua que no recebe como parmetro um e ca a a lua State. Em seguida, na linha 11, inicializamos todas as bibliotecas padres. E o poss tambm inicializar cada biblioteca individualmente atravs de funes vel e e co espec cas, como luaopen math e luaopen table, por exemplo. O lao das linhas 13 20 l um texto da entrada padro e o passa para c e a o interpretador Lua. A funo luaL loadbuer, usada na linha 14, recebe um ca estado Lua e um buer de dados e tenta compilar o buer de dados como um trecho de cdigo Lua (assim como a funo loadle vista anteriormente). O o ca terceiro parmetro de luaL loadbuer o tamanho do buer e o quarto o nome a e e que queremos dar ao trecho de cdigo, de modo que esse nome ir aparecer em o a mensagens de erro e ser util tambm para propsitos de depurao. a e o ca

38

Caso nenhum erro ocorra durante a execuo de luaL loadbuer, o trecho ca de cdigo Lua compilado ser colocado no topo da pilha e o valor 0 ser retoro a a nado. Caso contrrio, um valor diferente de zero ser retornado, junto com uma a a mensagem de erro. a a Supondo que nenhum erro ocorreu, a funo lua pcall, na linha 15, ser ento ca chamada. lua pcall executa uma funo em modo protegido. De acordo com o ca protocolo da pilha virtual de Lua, devemos primeiro colocar na pilha a funo ca que queremos executar e em seguida os seus argumentos (o primeiro argumento colocado primeiro, depois o segundo e assim por diante). e a O segundo parmetro de lua pcall serve ento para indicar quantos argua mentos a funo possui. No nosso caso, temos um trecho de cdigo Lua que foi ca o ca retornado como uma funo por luaL loadbuer, sendo que a funo retornada ca no possui nenhum parmetro. a a O terceiro parmetro de lua pcall indica o nmero de valores de retorno a u da funo. Os valores de retorno da funo sero deixados na pilha. Como a ca ca a nossa funo no retorna nada, fornecemos um valor 0 tambm para o terceiro ca a e parmetro de lua pcall. a O quarto e ultimo parmetro o a e ndice na pilha virtual de uma funo que ca possa tratar um poss vel erro durante a execuo da funo. Ao fornecermos ca ca um valor 0 para este parmetro, faremos com que uma poss mensagem de a vel erro seja deixada na pilha. Na linha 17, caso tenha ocorrido um erro, iremos imprimir a mensagem de erro que foi deixada na pilha. Existem duas maneiras de se acessar a pilha, a partir do topo ou a partir da sua base. Para acessarmos a pilha a partir do topo, usamos ndices negativos: 1 o topo da pilha, 2 o e e ndice do elemento logo abaixo e assim por diante. Para acessarmos a base da pilha, usamos ndices positivos: 1 o elemento mais em baixo na pilha, 2 o e e ndice do elemento logo acima e assim por diante. A funo lua tostring, usada na linha 17, est acessando o elemento no topo ca a da pilha (a mensagem de erro) e convertendo-o para uma cadeia de caracteres. lua tostring espera que o valor no topo da pilha seja uma cadeia de caracteres ou um nmero. Essa funo retorna um ponteiro para uma cadeia dentro do u ca estado Lua. E importante tomar cuidado com poss veis usos desse ponteiro, pois como Lua realiza coleta de lixo automtica, no temos garantias de que o a a ponteiro retornado ainda ser vlido aps tirarmos o valor da pilha. Em virtude a a o disso, sempre bom copiarmos a cadeia para a qual o ponteiro retornado por e Lua aponta, ao invs de utilizar o ponteiro diretamente. e A linha 18 apenas retira a mensagem de erro da pilha. O segundo parmetro a de lua pop indica quantos valores queremos retirar da pilha. Por m, na linha 22, fechamos o nosso estado Lua, liberando toda a memria o ` dinmica usada pelo estado. As vezes no necessrio chamar essa funo, uma a a e a ca vez que algumas plataformas liberam a memria alocada dinamicamente por o um processo quando ele termina. Tente compilar o programa acima e fornecer algumas linhas de cdigo Lua o para o interpretador. Em um ambiente Unix, o programa acima pode ser compilado da seguinte forma: 39

gcc interpretador.c -llua -lm -ldl Onde -lm necessrio para fazer a ligao das funes da biblioteca matemtica e a ca co a de C e -ldl para possibilitar o carregamento dinmico de mdulos. a o O exemplo acima assume que o seu compilador sabe onde procurar os arquivos cabealho de Lua e a sua biblioteca dinmica. Caso este no seja o caso, c a a voc deve indicar o caminho explicitamente, como mostrado a seguir: e gcc -I/home/user/lua/include/ interpretador.c -L/home/user/lua/lib/ -llua -lm -ldl

6.2

Funes Bsicas co a

Como a API C de Lua consiste basicamente na manipulao de uma pilha ca virtual, estaremos todo o tempo realizando operaes como colocar elementos co na pilha, desempilhar elementos e vericar o tipo de um determinado elemento. Para a tarefa bsica de empilhar elementos na pilha, Lua oferece um conjunto a de funes, cada uma especializada em empilhar um valor de um determinado co tipo na pilha. A seguir, temos uma lista dessas funes: co void void void void void void lua_pushnil lua_pushboolean lua_pushinteger lua_pushnumber lua_pushlstring lua_pushstring (lua_State (lua_State (lua_State (lua_State (lua_State (lua_State *L); *L, int bool); *L, lua_Integer n); *L, lua_Number n); *L, const char *s, size_t len); *L, const char *s);

A diferena entre as funes lua pushlstring e lua pushstring que a primeira c co e espera que o tamanho da cadeia seja indicado, pois a cadeia pode conter \0s dentro dela ou no ser terminada por \0. J a funo lua pushstring, espera que a a ca a cadeia seja terminada por \0. Quando colocamos uma cadeia na pilha, Lua faz uma cpia da cadeia. Dessa o forma, podemos modicar a cadeia ou liberar a memria para a qual a varivel o a s aponta, sem alterar com isso a cadeia que colocamos na pilha. Quando queremos saber o tipo de um elemento da pilha, podemos usar as funes lua is*, que possuem a seguinte assinatura: co int lua_is* (lua_State *L, int index); Onde index indica o ndice do elemento na pilha. Essas funes sempre retornam co 0 ou 1. As funes lua isnumber e lua isstring merecem uma nota especial. A co primeira retorna 1 caso o elemento no ndice index seja uma cadeia que pode ser convertida para um nmero, ao passo que a segunda sempre retorna 1 caso u o elemento correspondente seja um nmero, uma vez que um nmero sempre u u pode ser convertido para uma cadeia. E poss tambm converter elementos da pilha, atravs das funes lua to*. vel e e co A seguir, listamos algumas delas:

40

int lua_toboolean (lua_State *L, int index); lua_Integer lua_tointeger (lua_State *L, int index); lua_Number lua_tonumber (lua_State *L, int index); const char *lua_tolstring (lua_State *L, int index, size_t *len); const char *lua_tostring (lua_State *L, int index); e A funo lua toboolean sempre retorna 0 ou 1, sendo que 0 retornado somente ca quando o valor Lua correspondente false ou nil. e u a Devemos tomar cuidado ao usar a funo lua tointeger quando o nmero no ca for inteiro. Nesse caso, o nmero ser truncado de alguma maneira no especiu a a cada. Tanto lua tointeger como lua tonumber retornam 0 quando o elemento na posio index da pilha no pode ser convertido para um nmero. ca a u a As funes lua tolstring e lua tostring so bastante similares, sendo que a co primeira tambm indica na varivel len, caso ela no seja NULL, o tamanho e a a da cadeia retornada. Sendo assim, lua tostring apenas uma forma abreviada e de usarmos lua tolstring, passando sempre NULL como o terceiro parmetro. a Caso o elemento no ndice index no possa ser convertido para uma cadeia, o a valor NULL ser retornado. a Devemos lembrar que as funes de converso apenas retornam um valor, co a elas no retiram o valor da pilha. Para isso, devemos fazer uma chamada ` a a funo lua pop. ca Uma variao das funes lua to* so as funes luaL check*. A diferena ca co a co c entre elas que as funes luaL check* tambm vericam se um dado valor pode e co e ser convertido. Podemos entender as funes luaL check* como uma juno de co ca lua is* e lua to*. Seu uso, porm, mais restrito, pois as funes luaL check* e e co so usadas apenas na vericao dos argumentos de uma funo, no operando, a ca ca a portanto, sobre ndices da pilha.

6.3

Manipulando a Pilha

A API C de Lua tambm disponibiliza funes que manipulam a pilha e permie co tem realizar operaes como copiar elementos, saber quantos elementos a pilha co possui e modicar o valor de um dado elemento da pilha. Um conjunto dessas funes apresentado a seguir: co e int void void void void void void lua_gettop (lua_State *L); lua_pop (lua_State *L, int n); lua_insert (lua_State *L, int index); lua_pushvalue (lua_State *L, int index); lua_remove (lua_State *L, int index); lua_replace (lua_State *L, int index); lua_settop (lua_State *L, int index);

u A primeira funo, lua gettop, retorna o nmero de elementos na pilha, sendo ca que um valor de retorno 0 indica que a pilha est vazia. a A funo lua pop, como j vimos, desempilha n elementos da pilha. ca a

41

Podemos mover um elemento na pilha atravs da funo lua insert, que e ca recebe um ndice na pilha e move o elemento no topo da pilha para esse ndice, deslocando outros elementos para cima se necessrio. a ndice para o topo J a funo lua pushvalue, copia o elemento em um dado a ca da pilha, aumentando portanto o tamanho da pilha. Para remover elementos da pilha, podemos usar a funo lua remove, que ca remove o elemento no ndice fornecido, deslocando para baixo os elementos que se encontram acima. A funo lua replace, por sua vez, move o elemento no ca topo da pilha (desempilhando-o) para um ndice espec co, substituindo o valor que existia no ndice anteriormente. A funo lua settop pode tanto aumentar como diminuir o tamanho da pilha. ca Quando usamos essa funo, estabelecemos o elemento no ca ndice fornecido como o novo topo da pilha. Se o novo topo for maior do que o anterior, os novos elementos sero inicializados com nil. Caso o a ndice fornecido seja 0, todos os elementos da pilha sero removidos. a Na gura 6.2, podemos ver o exemplo de um programa que usa algumas das funes apresentadas anteriormente. A principal nalidade do programa co e exercitar o uso das funes da API C de Lua. co Nas linhas 5 15, temos a denio da funo printtypes, que ir imprimir o ca ca a tipo de cada elemento da pilha. Essa funo recebe como unico parmetro um ca a estado Lua. Usando a funo lua gettop (linha 7), obtemos o nmero de elementos na ca u pilha. Lembre-se que o elemento na base da pilha est no a ndice 1 e o elemento no topo no ndice 1. Em seguida (linhas 10 15), temos um lao que obtm o tipo de cada c e elemento da pilha e o imprime. Para obter o tipo de um elemento na pilha usamos a funo lua type, que retorna um valor inteiro. Os valores de retorno ca para cada tipo esto denidos no arquivo lua.h. Quando o elemento uma a e cadeia de caracteres, por exemplo, o valor retornado LUA TSTRING. e A funo lua typename, por sua vez, recebe um valor inteiro e retorna uma ca cadeia que corresponde ao nome de um tipo. O nosso programa principal (linhas 1738) comea ento denindo um novo c a estado Lua e empilhando trs valores. Em seguida, na linha 24, chamamos a e funo printtypes pela primeira vez. O tipo dos elementos da pilha listado a ca e partir do elemento mais embaixo, de forma que a cadeia mais ` direita (string) a corresponde ao tipo do elemento no topo da pilha.

42

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

#include <stdio.h> #include "lua.h" #include "lauxlib.h" static void printtypes (lua_State *L) { int n = lua_gettop (L); int i; int type; for (i=1; i<=n; ++i) { type = lua_type (L, i); printf ("%s ", lua_typename (L, type)); } printf ("\n"); } int main () { lua_State *L = luaL_newstate (); lua_pushinteger (L, 51); /* colocando uma boa idia na pilha */ e lua_pushnumber (L, 3.14); lua_pushstring (L, "lua"); printtypes (L); /* number number string */

lua_settop (L, 5); printtypes (L); /* number lua_replace (L, 2); printtypes (L); /* number lua_pushboolean (L, 1); lua_insert (L, -4); printtypes (L); /* number lua_close (L); return 0; }

number

string

nil

nil */

nil

string

nil */

boolean

nil

string

nil */

Figura 6.2: Exemplo de uso das funes da API C de Lua co

43

Cap tulo 7

Usando Lua em uma Aplicao C ca


Como vimos, Lua tanto pode estender uma aplicao (linguagem de extenso), ca a como pode pode ser estendida por funes denidas em C (linguagem extens co vel). Vamos comear usando Lua para estender uma aplicao C. c ca Quando estendendo uma aplicao C, bastante comum usarmos Lua para ca e escrever um arquivo de congurao. ca O uso de Lua oferece vrias vantagens. A primeira vantagem que no precia e a saremos termos que nos preocupar em ler e entender o arquivo de congurao, ca Lua far isso por ns. Outra vantagem que, como j vimos, muito fcil a o e a e a embarcar Lua em uma aplicao. Alm disso, Lua ocupa pouqu ca e ssimo espao, c de forma que o tamanho da sua aplicao no ir crescer muito. ca a a Devemos lembrar tambm que desde o princ e pio Lua foi pensada como uma linguagem de descrio de dados, de forma que no precisamos ter nenhuma ca a preocupao excessiva com a sintaxe do nosso arquivo de congurao. ca ca A seguir, apresentaremos alguns usos da API C de Lua, introduzindo tambm e novas funes. Porm, no iremos apresentar todas as funes dispon co e a co veis da API. Caso voc deseje saber mais detalhes sobre as funes dispon e co veis na API C de Lua, consulte o manual de referncia de Lua. e

7.1

Um arquivo de congurao simples ca

O primeiro arquivo de congurao que iremos utilizar bastante simples. O ca e nosso intuito utilizar o arquivo de congurao para representar algumas cae ca racter sticas de um objeto. Supondo que temos em C uma estrutura Car, representando um carro, podemos usar um arquivo de congurao para fornecer valores para algumas ca propriedades do carro, como velocidade, combust vel e cor. O nosso primeiro arquivo de congurao mostrado a seguir: ca e

44

fuel = 43 speed = 100 color = "blue" Agora, nosso programa C precisa carregar o arquivo de congurao e ler os ca valores que ele deseja. Para fazer isso, iremos denir a funo loadcong, que ca e mostrada na gura 7.1. A funo loadcong recebe trs parmetros: um estado Lua, o nome do ca e a arquivo de congurao e um ponteiro para uma estrutura Car. ca Na linhas 5 tentamos compilar o arquivo de congurao e execut-lo em ca a caso de sucesso. Caso algum erro ocorra, imprimimos uma mensagem de erro e retornamos. As linhas 1113 vo colocar na pilha o valor das variveis speed, fuel e color. a a Caso essas variveis no existam, um valor nil ser empilhado, j que esse o a a a a e valor padro de uma varivel no declarada/inicializada. a a a Na linha 18, retornamos o elemento na posio 3 da pilha como um valor ca inteiro. Poder amos tambm usar uma abordagem mais cuidadosa e testar se e o valor pode ser convertido para um nmero, utilizando a funo lua isnumber. u ca Lembre-se que o valor 0 ser retornado quando o elemento na posio 3 no a ca a puder ser convertido para um nmero. u A linha 22 testa se o valor no topo da pilha pode ser convertido para uma cadeia de caracteres. Caso ele no possa, devemos tratar o erro, j que esa a pervamos uma cadeia. a Quando o valor no topo da pilha puder ser convertido para uma cadeia, executaremos o bloco else (linhas 24 29). A funo lua tolstring retorna um ca ponteiro para a cadeia e atualiza a varivel len com o tamanho da cadeia rea tornada. Note que estamos copiando a cadeia retornada e no guardando um a ponteiro para ela. Lembre-se que caso o valor seja removido da pilha, o coletor de lixo de Lua pode tornar a referncia invlida. e a

7.2

Incrementado o arquivo de congurao ca

Vamos colocar agora em nosso arquivo de congurao uma tabela Lua, com ca campos X, Y e Z, para indicar a localizao do carro. ca O novo arquivo de congurao pode ser visto a seguir: ca fuel = 43 speed = 100 color = "blue" pos = {X=100, Y=200, Z=100} Temos agora que modicar a funo loadcong, de modo a obter o valor da ca varivel global pos e em seguida o valor de cada um dos seus campos, como a ilustra o cdigo a seguir: o lua_getglobal (L, "pos");

45

01 void loadconfig (lua_State *L, const char *file, Car *car) { 02 const char *color; 03 size_t len; 04 05 if (luaL_loadfile (L, file) || lua_pcall (L, 0, 0, 0)) { 06 printf ("Erro lendo arquivo de configura~o: %s\n", lua_tostring (L, -1)); ca 07 return; 08 } 09 10 /* Coloca o valor da varivel no topo da pilha */ a 11 lua_getglobal (L, "speed"); 12 lua_getglobal (L, "fuel"); 13 lua_getglobal (L, "color"); 14 15 /* O valor de "color" est no topo da pilha e o valor de "speed" na base */ a 16 17 /* Inicia car->speed com 0 caso o elemento -3 da pilha n~o possa ser convertido */ a 18 car->speed = lua_tointeger (L, -3); 19 20 car->fuel = lua_tointeger (L, -2); 21 22 if (!lua_isstring (L, -1)) { 23 /* Trata o erro */ 24 } else { 25 color = lua_tolstring (L, -1, &len); 26 car->color = malloc (len + 1); 27 /* Copia o valor da cor (n~o guarda o ponteiro) */ a 28 strcpy (car->color, color); 29 } 30 } Figura 7.1: Usando um Arquivo de Congurao Simples ca

46

if (!lua_istable (L, -1) { /* Trata o erro */ } else { lua_getfield (L, -1, "X"); car->pos.X = lua_tointeger (L, -1); lua_pop (1); } Aps obtermos o valor da varivel pos, vericamos se o valor que ela guarda o a e mesmo uma tabela. Caso seja, usamos a funo lua geteld para colocar na pilha ca o valor do seu campo X. A funo lua geteld possui como segundo parmetro ca a o ndice na pilha de uma tabela e como terceiro parmetro a chave da tabela a que queremos acessar. O valor correspondente a aquela chave da tabela ento e a colocado na pilha. Em seguida, usamos a funo a lua tointeger para obter o valor retornado ca como um nmero inteiro e a funo lua pop para retirar o valor da pilha. O u ca prximo passo realizar uma operao similar para obter o valor dos campos Y o e ca e Z da tabela. Para evitar repetir o mesmo cdigo vrias vezes e dado que o acesso ao o a campo de uma tabela uma tarefa muito comum, vamos criar uma funo em e ca C que faa isso. A denio da funo c geteld mostrada a seguir: c ca ca e 01 int c_getfield (lua_State *L, const char *k) { 02 /* Assume que a tabela est no topo */ a 03 int v; 04 05 lua_getfield (L, -1, k); 06 if (!lua_isnumber (L, -1)) { 07 /* Trata o erro */ 08 } 09 v = lua_tointeger (L, -1); 10 lua_pop (L, 1); 11 12 return v; 13 } A nova denio da funo loadcong mostrada na gura 7.2. Podemos notar ca ca e que o cdigo anterior da funo, que acessava o valor das variveis speed, fuel e o ca a color, no foi alterado. a

7.3

Usando funoes c

Usando a API C de Lua podemos denir funes em Lua que sero chamadas co a pelo cdigo C. No mesmo arquivo de congurao em que denimos o valor o ca de algumas variveis, iremos denir agora uma funo canachieve. A funo a ca ca canachieve receber como parmetros a quantidade de gasolina que o carro a a

47

01 void loadconfig (lua_State *L, const char *file, Car *car) { 02 const char *color; 03 size_t len; 04 05 if (luaL_loadfile (L, file) || lua_pcall (L, 0, 0, 0)) { 06 printf ("Erro lendo arquivo de configura~o: %s\n", lua_tostring (L, -1)); ca 07 return; 08 } 09 10 /* Coloca o valor da varivel no topo da pilha */ a 11 lua_getglobal (L, "speed"); 12 lua_getglobal (L, "fuel"); 13 lua_getglobal (L, "color"); 14 15 /* O valor de "color" est no topo da pilha e o valor de "speed" na base */ a 16 17 /* Inicia car->speed com 0 caso o elemento -3 da pilha n~o possa ser convertido */ a 18 car->speed = lua_tointeger (L, -3); 19 20 car->fuel = lua_tointeger (L, -2); 21 22 if (!lua_isstring (L, -1)) { 23 /* Trata o erro */ 24 } else { 25 color = lua_tolstring (L, -1, &len); 26 car->color = malloc (len + 1); 27 /* Copia o valor da cor (n~o guarda o ponteiro) */ a 28 strcpy (car->color, color); 29 } 30 31 lua_getglobal (L, "pos"); 32 if (!lua_istable (L, -1)) { 33 /* Trata o erro */ 34 } else { 35 car->pos.X = c_getfield (L, "X"); 36 car->pos.Y = c_getfield (L, "Y"); 37 car->pos.Z = c_getfield (L, "Z"); 38 } 39 40 } Figura 7.2: Nova denio de loadcong ca

48

possui e uma distncia em quilmetros que se deseja percorrer. O valor de a o retorno deve ser um booleano, indicando se a distncia pode ser percorrida com a aquela quantidade de gasolina. A denio de canachieve dada a seguir: ca e function canachieve (fuel, dist) if dist / fuel > 10 then -- muito longe, a gasolina n~o d a a return false else return true end end Agora, supondo que o estado Lua j foi inicializado pelo cdigo da nossa aplicao a o ca C e o arquivo de congurao foi executado, podemos usar canachieve seguindo ca um protocolo simples: a funo deve ser colocada na pilha (do mesmo modo ca que zemos com as variveis speed, color, etc); em seguida, devemos colocar a os valores dos parmetros (o primeiro parmetro primeiro, depois o segundo e a a a ca assim por diante); usamos lua pcall para realizar a chamada ` funo; e por ultimo obtemos os resultados e os removemos da pilha. A funo canachievec, apresentada a seguir, uma funo C que usa a funo ca e ca ca canachieve denida em Lua: 01 int canachievec (lua_State *L, int fuel, int dist) { 02 int res; 03 04 lua_getglobal (L, "canachieve"); 05 06 lua_pushinteger (L, fuel); 07 lua_pushinteger (L, dist); 08 09 if (lua_pcall (L, 2, 1, 0) != 0) { 10 printf ("Erro chamando canachieve: %s\n", lua_tostring (L, -1)); 11 } 12 13 res = lua_toboolean (L, -1); 14 lua_pop (L, 1); 15 return res; 16 } Na linha 4, colocamos na pilha a funo canachieve e em seguida (linhas 6 7) ca o valor dos seus parmetros. O prximo passo usar lua pcall para chamar a o e canachieve (linhas 911). Como vimos antes, o segundo argumento de lua pcall indica o nmero de parmetros que a funo que est sendo chamada recebe e u a ca a o terceiro argumento indica o nmero de resultados que ela retorna. O quarto u argumento indica o ndice na pilha de uma funo que ir tratar o erro, onde 0 ca a indica que no usaremos nenhuma funo, a mensagem de erro ser deixada na a ca a pilha.

49

01 int main () { 02 Car *car = malloc (sizeof (Car)); 03 lua_State *L = luaL_newstate (); 04 int d; 05 06 loadconfig (L, "config.lua", car); 07 08 printf ("speed = %d\n", car->speed); 09 printf ("fuel = %d\n", car->fuel); 10 printf ("color = %s\n", car->color); 11 printf ("pos.X = %d\n", car->pos.X); 12 printf ("pos.Y = %d\n", car->pos.Y); 13 printf ("pos.Z = %d\n", car->pos.Z); 14 15 d = canachievec (L, car->fuel, 40); 16 printf ("Achieve: %d\n", d); 17 18 d = canachievec (L, car->fuel, 430); 19 printf ("Achieve: %d\n", d); 20 21 d = canachievec (L, car->fuel, 440); 22 printf ("Achieve: %d\n", d); 23 24 return 0; 25 } Figura 7.3: Exemplo de uso das funes loadcong e canachievec co Se tudo ocorreu bem, guardamos o valor retornado por canachieve na varivel a res, removemos esse valor da pilha e retornamos o resultado. No caso de uma funo que retorna mais de um resultado, o ultimo valor retornado ca no topo ca da pilha. Dessa forma, se uma funo retorna dois valores, o primeiro valor ca retornado car no a ndice 2 da pilha e o segundo (e ultimo) no ndice 1. Um exemplo de programa main que utiliza as funes loadcong e canachico evec, denidas anteriormente, apresentado na gura 7.3. e

50

Cap tulo 8

Usando Funes C em Lua co


Do mesmo modo que a API C de Lua permite que funes Lua sejam usadas em co cdigo C, poss fazermos chamadas a funes C em nosso programa Lua. o e vel co Para conseguir isso, devemos primeiro denir uma funo C com a seguinte ca assinatura: int myfunction (lua_State *L) Toda funo C que ser usada em programas Lua deve seguir esse padro: a ca a a funo retorna um nmero inteiro e recebe como unico parmetro um lua State. ca u a Assim como zemos anteriormente com funes Lua que eram usadas em C, co devemos seguir um certo protocolo. Quando a funo C chamada por Lua, os ca e parmetros passados estaro dispon a a veis na pilha, com o primeiro elemento no ndice 1, o segundo no ndice 2 e assim por diante. Vamos ento denir uma a funo pow l, que realiza a operao de exponenciao: ca ca ca 01 static int pow_l (lua_State *L) { 02 double a = luaL_checknumber (L, 1); 03 double b = luaL_checknumber (L, 2); 04 05 lua_pushnumber (L, pow (a, b)); 06 07 return 1; 08 } Nas linhas 2 3 obtemos o valor dos parmetros usando luaL checknumber, a que lana um erro com uma mensagem de erro caso o elemento na posio c ca correspondente da pilha no seja um nmero. a u Em seguida, na linha 5, colocamos o resultado da funo na pilha. O ultimo ca passo indicar quantos valores estamos retornando, isso feito na linha 7, onde e e indicamos que um unico valor est sendo retornado pela funo. Os valores de a ca retorno devem estar no topo da pilha no momento que a funo retorna. ca Agora que denimos a nossa funo pow l, devemos export-la, de modo ca a que ela possa ser usada por programas Lua. Uma boa maneira de se fazer isso 51

exportando as funes como um mdulo Lua, usando um array de registro, e co o onde listamos o nome pelo qual a funo pode ser acessada em Lua e o nome ca da funo correspondente em C, como mostrado a seguir: ca static const struct luaL_Reg mylib [] = { {"pow", pow_l}, {NULL, NULL} }; Colocamos um par {NULL, NULL} para indicar o m do array. Agora, basta denirmos uma funo principal, que ir registrar o nosso mdulo: ca a o int luaopen_mylib (lua_State *L) { luaL_register (L, "mylib", mylib); return 1; } co A funo luaL register recebe um array de registro e exporta as funes do array ca para uma tabela cujo nome indicado pelo seu segundo parmetro. Nesse caso, e a estamos criando uma tabela mylib, que ter uma funo pow. Caso o nome a ca fornecido para luaL register fosse o de uma tabela Lua j existente, as funo a ca do nosso array de registro seriam adicionadas ` tabela, que manteria as suas a funes antigas. Poder co amos, por exemplo, exportar a nossa funo pow na ca tabela global G. O prximo passo compilar o nosso arquivo C como uma biblioteca dinmica, o e a de modo que Lua possa carregar dinamicamente as novas funes que denico mos. Em sistemas Unix, uma soluo poss ca vel seria usar a seguinte linha de compilao: ca gcc -shared -o mylib.so mylib.c Gerando assim um arquivo .so. Em um ambiente Windows, devemos gerar um arquivo equivalente, ou seja, um arquivo .dll. Agora que j compilamos o nosso arquivo C como uma biblioteca dinmica, a a tudo que temos que fazer em Lua importar o mdulo mylib que acabamos de e o denir: require mylib print (mylib.pow (3, 4)) print (mylib.pow (5, 3)) --> 81 --> 125

Podemos notar que a funo pow utilizada da mesma forma que uma funo ca e ca declarada em Lua.

8.1

Denindo Funes com Estado co

Da mesma forma que denimos fechos em Lua, tambm podemos denir em C e funes que possuem um estado. A API C de Lua oferece trs maneiras de fazer co e isso, que veremos a seguir. 52

8.1.1

Usando registry

A primeira maneira atravs do uso da tabela global registry, que pode ser e e acessada somente pelo cdigo C. A tabela registry est localizada em um pseudoo a ndice, isto quer dizer que a tabela registry no est realmente na pilha. Para a a acessar a chave bola dessa tabela, devemos usar o seguinte trecho de cdigo: o lua_getfield (L, LUA_REGISTRYINDEX, "bola"); Ao invs de usarmos e ndices numricos como 1, 2 ou 4, usamos o pseudo- e ndice a LUA REGISTRYINDEX. Ao contrrio de outras tabelas de Lua, onde as chaves podem ser qualquer valor, voc no deve usar chaves numricas com a tabela e a e registry, pois os resultados so imprevis a veis. Como a tabela registry global, ela ser compartilhada por vrios mdulos. e a a o Se voc deseja que funes de diferentes mdulos acessem a mesma varivel, e co o a ento o uso de registry aconselhvel. a e a

8.1.2

Usando o ambiente

Se o que voc deseja que vrias funes do mesmo mdulo utilizem a mesma e e a co o varivel, ento voc deve usar a facilidade de ambiente de funes. A idia a a e co e fazer com que as funes de um mdulo compartilhem o mesmo ambiente, e co o usando o pseudo- ndice LUA ENVIRONINDEX. O seguinte trecho de cdigo o ilustra o uso de LUA ENVIRONINDEX : 01 int luaopen_mylib (lua_State *L) { 02 lua_newtable (L); 03 lua_replace (L, LUA_ENVIRONINDEX); 04 luaL_register (L, "nomeDaLib", arrayDeRegistro); 05 06 lua_pushnumber (L, 16); 07 lua_setfield (L, LUA_ENVIRONINDEX, "bola"); 08 09 return 1; 10 } O primeiro passo foi criar uma nova tabela, que vai car no topo da pilha. Em seguida, usamos a funo lua replace, de modo que a tabela ser colocada ca a no pseudo- ndice LUA ENVIRONINDEX e ir servir como o ambiente a ser a compartilhado pelas funes do mdulo. co o O prximo passo registrar as funes do mdulo, como fazemos na linha o e co o 4. Agora, podemos inicializar as variveis que sero compartilhadas. Vamos ter a a somente a varivel bola, cujo valor inicial 16. a e Para manipular a varivel bola, vamos denir as funes get l e multiply l. A a co funo get l ir simplesmente retornar o valor associado com a varivel bola, ao ca a a a passo que multiply l ir multiplicar por 2 o valor de bola. O programa completo mostrado na gura 8.1. e

53

#include "lua.h" #include "lauxlib.h" #include <math.h> static int get_l (lua_State *L) { lua_getfield (L, LUA_ENVIRONINDEX, "bola"); return 1; } static int multiply_l (lua_State *L) { int d; lua_getfield (L, LUA_ENVIRONINDEX, "bola"); d = lua_tonumber (L, -1); lua_pop (L, 1); lua_pushnumber (L, d * 2); lua_setfield (L, LUA_ENVIRONINDEX, "bola"); return 0; } static const struct luaL_Reg mylib [] = { {"get", get_l}, {"multiply", multiply_l}, {NULL, NULL} }; int luaopen_mylib (lua_State *L) { lua_newtable (L); lua_replace (L, LUA_ENVIRONINDEX); luaL_register (L, "mylib", mylib); lua_pushnumber (L, 16); lua_setfield (L, LUA_ENVIRONINDEX, "bola"); return 1; } Figura 8.1: Exemplo de uso de LUA ENVIRONINDEX

54

8.1.3

Usando upvalues

Por m, podemos fazer uso de upvalues, que so vis a veis somente dentro de uma funo. Quando criamos uma nova funo C para ser usada por Lua, podemos ca ca associar a ela um nmero arbitrrio de upvalues, formando um fecho. u a Para mostrar o uso de upvalues, vamos denir uma verso mais simples a da funo generateinc, apresentada anteriormente. A nova verso de generaca a teinc recebe apenas um parmetro, o valor inicial do contador, que ser sempre a a incrementado em uma unidade. A seguir, temos a denio da verso C de ca a generateinc: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 static int inc (lua_State *L) { int d = lua_tointeger (L, lua_upvalueindex (1)); /* Obtm valor do upvalue */ e lua_pushinteger (L, ++d); lua_pushvalue (L, -1); /* Copia o topo da pilha */ lua_replace (L, lua_upvalueindex (1)); /* Atualiza o upvalue */ return 1; } static int generateinc_l (lua_State *L) { int d = luaL_checkint (L, -1); /* Recebe valor inicial do contador */ lua_pushinteger (L, d); lua_pushcclosure (L, &inc, 1); /* Cria um novo fecho, com 1 upvalue */ return 1; }

Devemos comear a olhar a denio a partir da linha 9, onde declaramos a c ca funo generateinc l. Na linha 10, obtemos o valor inicial do contador que foi ca passado na pilha. Esse valor ser o upvalue do nosso fecho, sendo que todos os a upvalues devem ser colocados na pilha antes do fecho. Ento, na linha 11, ema pilhamos o upvalue e na linha 12 o fecho. O segundo parmetro de pushcclosure a a funo base do fecho e o terceiro parmetro o nmero de upvalues. Na e ca a e u linha 13, retornamos 1 para indicar que h um valor de retorno (o fecho). a Agora, devemos olhar a denio da funo inc, nas linhas 1 7. O valor ca ca do upvalue obtido na linha 2, atravs da funo lua upvalueindex, e depois e e ca e empilhado acrescido de uma unidade na linha 3. Na linha 4, empilhamos uma cpia do valor no topo da pilha e a usamos para atualizar o valor do upvalue. o A funo inc retorna somente um resultado, o novo valor do upvalue. ca

55

Cap tulo 9

Denindo Novos Tipos de Dados em C


Atravs da API C de Lua, podemos no somente denir novas funes para e a co estender a linguagem, como tambm podemos denir novos tipos de dados que e sero usados pela aplicao Lua. a ca Para denir um novo tipo de dado em C, iremos usar a funo lua newuserdata, ca que aloca um novo bloco de memria de um tamanho especicado. O bloco de o memria criado representar um objeto do tipo userdata de Lua e pode ter a o a sua prpria meta-tabela e ser recolhido pelo coletor de lixo. o Um objeto do tipo userdata, da mesma forma que uma tabela, somente e igual a ele mesmo. Para ilustrar o uso de userdata, vamos denir em C uma estrutura que ir a representar um buer circular. typedef struct CircBuffer { int start, end; int n, size; int array [1]; } CircBuffer; As variveis start e end guardam, respectivamente, as posies de in e m a co cio do buer. A varivel n ir armazenar o nmero de elementos atualmente no a a u buer, ao passo que size representa o nmero mximo de elementos que o buer u a pode armazenar. A varivel array representa o buer e ser inicializada com a a um tamanho adequado quando o buer for criado. Inicialmente array possui tamanho 1, uma vez que no poss criar um array com tamanho 0 em C 89. a e vel O trecho de cdigo a seguir mostra a funo que cria um novo buer circular: o ca 01 static int buffernew (lua_State *L) { 02 int n; 03 size_t bsize; 04 CircBuffer *cb; 56

05 06 07 08 09 10 11 12 13 14 15 16 17 }

n = luaL_checkinteger (L, 1); luaL_argcheck (L, n >= 1, 1, "invalid buffer size"); bsize = sizeof (CircBuffer) + (n - 1) * sizeof (int); cb = (CircBuffer *) lua_newuserdata (L, bsize); cb->size = n; cb->start = 0; cb->end = 0; cb->n = 0; return 1;

e A funo luaL argcheck util para gerar mensagens de erro sobre algum arca gumento da funo. O segundo parmetro de luaL argcheck a condio a ca a e ca ser testada, o terceiro parmetro indica qual argumento est sendo testado e o a a ultimo parmetro especica uma parte da mensagem de erro. Assumindo que a buer.new seja a funo Lua equivalente para buernew, no caso da chamada a ca seguir: buffer.new (0) ter amos a seguinte mensagem de erro: stdin:1: bad argument #1 to new (invalid buffer size) Na funo buernew, aps armazenarmos na varivel n o tamanho do buer ca o a circular, iremos calcular o tamanho, em bytes, do objeto userdata que iremos criar na linha 9. As linhas 1114 inicializam algumas variveis do buer circular a e na linha 16 indicamos que buernew retorna um elemento, o userdata que foi colocado na pilha por lua newuserdata. O prximo passo denir operaes que o e co operem sobre o buer circular. Primeiro, vamos denir uma funo que insere ca elementos no nal do buer: 01 static int bufferinsert (lua_State *L) { 02 CircBuffer *cb = (CircBuffer *) lua_touserdata (L, 1); 03 04 cb->array [cb->end] = luaL_checkinteger (L, 2); 05 cb->end = (cb->end + 1) % cb->size; 06 07 if (cb->n == cb->size) 08 cb->start = (cb->start + 1) % cb->size; 09 else 10 cb->n++; 11 12 return 0; 13 } 57

A primeira coisa que a funo buerinsert faz converter o userdata que est na ca e a pilha para um ponteiro para uma estrutura C. Em seguida, na linha 4, obtemos o valor do elemento que desejamos inserir no buer circular e na linha 5 atualizamos a varivel que guarda o m do buer. Caso o buer esteja cheio, ento a a o comeo do buer deve ser incrementado, caso contrrio, iremos incrementar o c a nmero de elementos do buer circular. O prximo passo denir uma funo u o e ca que remova os elementos do buer, como mostra o trecho de cdigo a seguir: o 01 static int bufferremove (lua_State *L) { 02 CircBuffer *cb = (CircBuffer *) lua_touserdata (L, 1); 03 04 if (cb->n < 1) 05 return 0; 06 07 cb->n--; 08 cb->end--; 09 if (cb->end < 0) 10 cb->end += cb->size; 11 12 return 0; 13 } Assim como buerinsert, a funo buerremove comea convertendo o userdata ca c para uma estrutura C. Em seguida, atualizamos algumas variveis do buer a circular e retornamos 0, indicando que a funo no deixou nenhum valor de ca a retorno na pilha. Agora, precisamos criar uma funo que acesse os elementos do buer circa cular. O trecho de cdigo a seguir faz isso: o 01 static int bufferelement (lua_State *L) { 02 CircBuffer *cb = (CircBuffer *) lua_touserdata (L, 1); 03 int i = luaL_checkinteger (L, 2); 04 05 luaL_argcheck (L, 1 <= i && i <= cb->n, 2, "index out of range"); 06 07 lua_pushinteger (L, cb->array [(cb->start + i -1) % cb->size]); 08 return 1; 09 } Na linha 5, usamos novamente a funo luaL argcheck para testar se o elemento ca que estamos tentando acessar est dentro dos limites do buer circular. Optaa mos por indexar os elementos do buer circular em Lua a partir do ndice 1, de modo que precisamos subtrair uma unidade da varivel i, na linha 7. a Por m, iremos criar funes que retornam o tamanho atual do buer e o co tamanho mximo do mesmo, como ilustrado a seguir: a 01 static int buffersize (lua_State *L) { 02 CircBuffer *cb = (CircBuffer *) lua_touserdata (L, 1); 58

03 04 lua_pushinteger (L, cb->n); 05 return 1; 06 } 07 08 static int buffermaxsize (lua_State *L) { 09 CircBuffer *cb = (CircBuffer *) lua_touserdata (L, 1); 10 11 lua_pushinteger (L, cb->size); 12 return 1; 13 } O ultimo passo registrar as funes que manipulam o buer circular, de modo e co que elas possam ser exportadas e usadas por Lua: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 static const struct luaL_Reg bufferlib [] = { {"new", buffernew}, {"insert", bufferinsert}, {"remove", bufferremove}, {"size", buffersize}, {"maxsize", buffermaxsize}, {"get", bufferelement}, {NULL, NULL} }; int luaopen_buffer (lua_State *L) { luaL_register (L, "buffer", bufferlib); return 1; }

Agora, podemos importar o mdulo do buer circular e utiliz-lo em um proo a grama Lua: require buffer b = buffer.new (3) buffer.insert (b, 100) buffer.insert (b, 2) print ("size = ", buffer.size (b)) buffer.insert (b, 33) buffer.remove (b) buffer.insert (b, 44) for i=1, buffer.size (b) do print (buffer.get (b, i)) end 59

9.1

Usando meta-tabelas

A implementao anterior do buer circular possui um problema. Estamos ca sempre obtendo o valor de um userdata e o convertendo para uma estrutura CircBuer, contudo, no estamos vericando se o valor que est na pilha a a e mesmo um userdata que representa um buer circular. Uma maneira de se fazer essa vericao atravs do uso de meta-tabelas. ca e e A idia associar uma meta-tabela com objetos userdata que representam um e e buer circular, de modo que depois, ao tentarmos converter um userdata na pilha, iremos vericar qual a sua meta-tabela associada, assim saberemos se e aquele userdata representa realmente um buer circular. O trecho de cdigo a seguir mostra a nova implementao da funo luaoo ca ca pen buer : 01 int luaopen_buffer (lua_State *L) { 02 luaL_newmetatable (L, "CircBuffer"); 03 luaL_register (L, "buffer", bufferlib); 04 return 1; 05 } Na linha 2, usamos a funo luaL newmetatable, que cria uma nova metaca tabela na tabela global registry. O nome da meta-tabela que passamos para luaL newmetatable deve ser unico, pois caso uma entrada com o mesmo nome j exista em registry a meta-tabela no ser criada e o valor 0 ser retornado. a a a a Caso a meta-tabela seja criada com sucesso, o valor 1 ser retornado. a Agora, ao criar um novo objeto userdata que representa um buer circular, devemos associ-lo com a meta-tabela CircBuer. Precisamos ento modicar a a a funo buernew, que ter o seu cdigo a partir da linha 16 alterado para: ca a o 16 luaL_getmetatable (L, "CircBuffer"); 17 lua_setmetatable (L, -2); 18 19 return 1; 20 } Na linha 16, usamos a funao luaL getmetatable, que coloca na pilha a metac tabela associada com CircBuer na tabela registry. Em seguida, na linha 17, associamos o userdata recm criado, que est logo abaixo do topo da pilha, com e a a meta-tabela CircBuer. Uma vez que zemos a associao entre os objetos userdata e a meta-tabela ca correspondente, devemos agora modicar as outras funes, de modo a vericar co se o userdata que est na pilha representa mesmo um buer circular. A seguir, a mostramos a nova implementao da funo buermaxsize: ca ca 01 static int buffermaxsize (lua_State *L) { 02 CircBuffer *cb = (CircBuffer *) luaL_checkudata (L, 1, "CircBuffer"); 03 04 lua_pushinteger (L, cb->size); 60

05 return 1; 06 } A unica mudana que zemos foi na linha 2, onde estamos utilizando a funo c ca luaL checkudata, que verica se o userdata em uma determinada posio da pilha ca est associado com uma dada meta-tabela. Agora, tente executar o seguinte a trecho de cdigo e veja o que acontece: o require buffer b = buffer.new (3) t = {} print (buffer.maxsize (b)) print (buffer.maxsize (t))

9.2

Usando um modelo OO

Uma vez que temos uma meta-tabela associada com os objetos userdata, podemos fazer uso dela tambm para prover um acesso orientado a objeto para as e funes do userdata. co Para conseguir isso, iremos usar o meta-mtodo index da meta-tabela assoe ciada com o buer circular, de uma maneira similar ao que zemos no cap tulo em que abordamos programao OO em Lua. ca Com o novo modelo, a unica funo a ser exportada ser buernew, de modo ca a que iremos alterar o nosso array de registro, como ilustrado a seguir: 01 02 03 04 05 06 07 08 90 10 11 12 13 static const struct luaL_Reg bufferlib_f [] = { {"new", buffernew}, {NULL, NULL} }; static const struct luaL_Reg bufferlib_m [] = { {"insert", bufferinsert}, {"remove", bufferremove}, {"size", buffersize}, {"maxsize", buffermaxsize}, {"get", bufferelement}, {NULL, NULL} };

Agora, vamos modicar luaopen buer : 01 int luaopen_buffer (lua_State *L) { 02 luaL_newmetatable (L, "CircBuffer"); 03 04 lua_pushvalue (L, -1); /* duplica a meta-tabela */ 61

05 06 07 08 09 10 11 }

lua_setfield (L, -2, "__index");

/* CircBuffer.__index = CircBuffer */

luaL_register (L, NULL, bufferlib_m); luaL_register (L, "buffer", bufferlib_f); return 1;

Na linha 7, ao fornecermos NULL como o segundo parmetro de luaL register, a estamos inserindo as funes do array de registro buerlib m na tabela que est co a no topo da pilha, que no caso a meta-tabela CircBuer. e Na linha 9, exportamos as funes do array de registro buerlib f para a co tabela buer, ou seja, estamos exportando para Lua apenas a funo buernew ca do buer circular. O trecho de cdigo a seguir mostra a utilizao do buer circular em Lua o ca usando uma notao OO: ca require buffer b = buffer.new (2) print (b:size()) b:insert (99) b:insert (44) b:insert (55) b:remove () print ("size", b:size()) b:insert (44) print ("size", b:size()) for i=1, b:size () do print (b:get (i)) end

9.3

Meta-mtodos e

Algumas das funes relacionadas com o buer circular poderiam ser fornecidas co como meta-mtodos da meta-tabela CircBuer, de modo que poder e amos, por exemplo, usar o operador de tamanho # para obter o tamanho atual de um buer. Uma maneira simples de fazer isso, alterando o array de registro buerlib m, e como mostrado a seguir: 01 static const struct luaL_Reg bufferlib_m [] = { 02 {"insert", bufferinsert}, 03 {"remove", bufferremove},

62

04 {"__len", buffersize}, 05 {"maxsize", buffermaxsize}, 06 {"get", bufferelement}, 07 {NULL, NULL} 08 }; O trecho de cdigo abaixo mostra o uso do operador # para obter o tamanho o do buer circular: require buffer b = buffer.new (2) b:insert (4) print (#b) b:insert (33) for i=1, #b do print (b:get (i)) end

9.4

Criando Interfaces

No exemplo anterior, as funes que operam sobre o buer circular tambm so co e a responsveis por realizar a comunicao entre C e Lua. a ca Embora esse seja poss vel projetar nosso programas dessa forma, muitas vezes queremos exportar para Lua uma estrutura de dados que j foi denida a em C sem ter que alterar as funes que manipulam essa estrutura. co Nesse caso, o que devemos fazer criar apenas funes de interface, que e co pegam os dados da pilha de Lua, chamam a funo C correspondente e empilham ca o resultado na pilha. Vamos redenir ento a funo buernew, retirando dela todo o cdigo que a ca o est relacionado com Lua. Agora, buernew deve retornar o novo buer circular a que foi criado. A nova denio da funo mostrada a seguir: ca ca e 01 CircBuffer *buffernew (int n) { 02 size_t bsize; 03 CircBuffer *cb; 04 05 bsize = sizeof (CircBuffer) + (n - 1) * sizeof (int); 06 cb = (CircBuffer*)malloc(bsize); 07 08 cb->size = n; 09 cb->start = 0; 10 cb->end = 0; 11 cb->n = 0; 63

12 13 return cb; 14 } Notem que tambm retiramos o cdigo que vericava se o tamanho do bufe o fer era vlido ou no. Poder a a amos ter deixado algum cdigo que fazia essa o vericao (sem usar funes da API C de Lua) em buernew, mas preferimos ca co fazer a vericao do tamanho do buer na funo de interface buernew l, que ca ca mostrada a seguir: e 01 int buffernewl (lua_State *L) { 02 CircBuffer *cb; 03 CircBuffer **ud; 04 int n; 05 06 n = luaL_checkinteger (L, 1); 07 luaL_argcheck (L, n >= 1, 1, "invalid buffer size"); 08 09 cb = buffernew (n); 10 ud = lua_newuserdata (L, sizeof (CircBuffer *)); 11 *ud = cb; 12 13 luaL_getmetatable (L, "CircBuffer"); 14 lua_setmetatable (L, -2); 15 16 return 1; 17 } As linhas 6 7 de buernewl vericam se o tamanho do buer que o usurio a deseja criar vlido. e a Como a funo buernew quem est realmente criando o buer circular ca e a e realizando a alocao de memria, iremos criar um userdata cujo tamanho ca o e apenas um ponteiro para uma estrutura CircBuer (linha 10). O userdata ir a apontar ento para o buer circular que foi retornado pela funo buernew a ca (linha 11). As demais funes de interface so semelhantes a buernewl e iro obter os co a a parmetros da pilha, fazer alguma vericao quando necessrio, chamar uma a ca a funo C e empilhar os resultados na pilha. A seguir, temos a denio da ca ca funo buerinserl : ca 01 int bufferinsertl (lua_State *L) { 02 CircBuffer **ud = (CircBuffer **) luaL_checkudata (L, 1, "CircBuffer"); 03 CircBuffer *cb = *ud; 04 int n = luaL_checkinteger (L, 2); 05 06 if (buffersize (cb) == buffermaxsize (cb)) 07 luaL_error (L, "Error inserting %d: buffer is full\n", n); 64

08 09 bufferinsert (cb, n); 10 11 return 0; 12 } Na linha 7, usamos a funo luaL error, que dispara uma mensagem de erro. ca O segundo parmetro de luaL error uma mensagem de erro, onde podemos a e usar alguns formatadores, como %s e %d. Notem que buerinsertl no manipula a o buer diretamente, apenas chama as funes que operam sobre o buer, como co buersize e buerinsert.

9.5

Coleta de Lixo

Quando as funes que usam a API C de Lua so meramente uma interface para co a funes C, que fazem realmente o trabalho pesado, podemos ter um problema co com o gerenciamento de memria. o Como a alocao das estruturas de dados feita por C, Lua no tem controle ca e a sobre a memria e voc (programador C) responsvel por liberar a memria o e e a o quando ela no est mais sendo usada. a a Uma soluo simples, seria exportar a funo C que libera a memria e ca ca o cham-la de Lua, de modo que quando um buer b no estivesse mais sendo a a utilizado, poder amos ter uma chamada como buer:free(). Essa soluo, embora seja vlida, faz com que o programador Lua tenha que ca a gerenciar explicitamente a memria dos objetos, o que no uma boa idia. o a e e Uma soluo mais adequada denir na meta-tabela CircBuer um metaca e mtodo gc. Dessa forma, quando um userdata que representa um buer cire cular no estiver mais sendo referenciado em Lua, o coletor de lixo ir chamar a a o seu meta-mtodo gc, que ser responsvel por liberar a memria e outros e a a o recursos alocados pelo objeto.

65

Você também pode gostar