Executando código Lua
Existem várias maneiras de executar código Lua. Neste tutorial, veremos algumas delas.
Nossa principal ferramenta será o interpretador lua
, que
usamos para executar código Lua. Ele pode receber o código por vários métodos
diferentes, e então se encarrega da execução.
Por enquanto, vamos trabalhar com três métodos: interativo, por arquivos e por argumentos de linha de comando.
Também vamos explorar outra ferramenta, o compilador luac
,
que podemos usar para agilizar o carregamento de códigos Lua e verificar por
erros eventuais.
Se você ainda não obteve Lua, deverá cumprir
este requisito primeiro. Feito isso, terá os programas lua
e
luac
, que usaremos aqui.
Modo interativo
Lua é uma linguagem dinâmica. Podemos informar um comando ao interpretador e já observar os seus resultados, antes de passar para a execução do próximo comando. Isto facilita o aprendizado. Vamos começar explorando essa característica.
Como vimos no tutorial anterior, ao executar o programa lua
sem nenhum parâmetro, veremos o seu prompt de comandos.
$ lua
Lua 5.4.7 Copyright (C) 1994-2024 Lua.org, PUC-Rio
>
Recapitulando, o sinal >
(isto é, o prompt) quer
dizer que o interpretador está à espera de um próximo comando. Ao inserir o
comando (seguido da tecla Enter
), o interpretador vai executar o
comando informado, que pode ou não ter alguma saída na tela (ou seja, algum
efeito visível).
Vamos começar com um comando bem simples, que apenas exibirá na tela do seu terminal o texto “Estou na Lua”.
> print("Estou na Lua")
Estou na Lua
Neste comando estamos chamando a função print
que serve para
exibir algo na tela. Informamos o valor que quermos exibir, e a função print
escreve (ou imprime, na terminologia original) na tela.
Essa não é a única forma de fazer algo aparecer na tela. Existe outro método, que é retornar algum valor. Um valor como o texto “Estou na Lua” que usamos antes, por exemplo.
> return "Estou na Lua"
Estou na Lua
Parece confuso que haja duas maneiras de obter o mesmo efeito? Pode ser,
mas logo veremos a diferença entre esses dois métodos. Por enquanto, vamos
nos ater a um fato mais pragmático: podemos omitir o comando
return
:
> "Estou na Lua"
Estou na Lua
Há uma observação importante a fazer, sobre essa omissão do comando
return
: até Lua 5.2, era necessário inserir um sinal de igual
antes do valor retornado (o que ainda funciona nas versões mais novas):
> = "Estou na Lua"
Estou na Lua
A partir da versão 5.3, isso deixou de ser necessário, e podemos omitir também o sinal de igual.
Até aqui trabalhamos apenas com um valor, que é um texto. A rigor, ele é o que chamamos de uma expressão. Em Lua, existem vários tipos de expressão, e o texto é um deles.
Mas além do texto, números são expressões, assim como operações aritméticas sobre números. Vejamos:
> 3
3
> 2 + 2
4
O exemplo de operação aritmética nos mostra que o interpretador pode ser usado como uma calculadora. Mas ele vai muito além disso. Podemos trabalhar com variáveis também:
> i = 3
Note que esse comando não emite nenhuma saída. Note também que eu disse comando e não expressão. Tecnicamente, há uma diferença. Expressões sempre são retornadas, e portanto você sempre verá uma saída para elas. Comandos não são retornados, e eles podem emitir alguma saída, ou não.
Neste caso, estamos executando um comando de atribuição
de um valor (3
) a uma variável (i
). Dito isso,
podemos criar expressões mais complexas e até mesmo utilizar variáveis dentro
delas.
> i = 3
> 2 * (i + 14) + i * i
43
Ou seja, na primeira linha, informamos um comando (de atribuição), e na segunda linha informamos uma expressão. Relembrando, ao informar uma expressão ao interpretador, ele retorna seu resultado.
Para ser ainda mais preciso, quando visualizamos o prompt, isso significa que o interpretador está à espera do que chamamos, na terminologia de Lua, de um trecho.
Um trecho é um agrupamento lógico (um bloco), que é interpretado de uma vez só, e pode conter um ou mais comandos ou blocos internos.
Então podemos em uma mesma linha informar mais de um comando.
> i = 3 print(i)
3
> i = 2 return i
2
Veja que não foi necessário nem mesmo separar a atribuição de
i
da chamada ao comando posterior. Podemos, por uma questão
puramente estética, inserir um ponto-e-vírgula entre os dois comandos. Isso
não afeta em nada a saída.
> i = 3 ; print(i)
3
E por falar em estética, outro recurso que ajuda a melhorar a visualização do código ao usar o interpretador, é dividir partes de um mesmo trecho em mais de uma linha. Quando está óbvio para o interpretador que o trecho informado está incompleto, ele aguardará pelo resto antes de executar.
Vejamos um exemplo:
> print (
>> 2 +
>> 2
>> )
4
Veja que ao chamar a função print
, abrimos parênteses, mas
não fechamos na mesma linha, dando a entender que isso será feito em outra
linha. O interpretador então altera o prompt para dois sinais de
maior (>>
) no lugar de apenas um, indicando que aguarda
pelo resto do trecho, antes de executá-lo.
Antes de encerrar a chamada de função informamos uma expressão (2 +
2
), que por acaso também foi quebrada em mais de uma linha. Somente ao
informar o fechamento dos parênteses o trecho é dado como completo e o
interpretador o executa.
Esse prompt modificado que vimos, chamamos usualmente de prompt secundário. No exemplo acima, pode não fazer tanto sentido dividir o trecho, por ser demasiado simples, mas em trechos envolvendo definições de funções ou laços repetitivos (que veremos em outros tutoriais) o uso desse recurso será mais produtivo.
Para encerrar o modo interativo, você pode usar, como vimos no tutorial
anterior, uma chamada à função os.exit()
(ainda exploraremos
funções em outro tutorial).
> os.exit()
Um modo ainda mais prático é inserir um caractere de fim de linha, embora
isso seja específico do sistema operacional. Tipicamente, em sistemas POSIX,
podemos inserí-lo com a combinação Ctrl+D
, e no terminal do
Windows com a combinação Ctrl+Z
.
Bem, até aqui acho que já deu para entender o básico de como funciona o interpretador no modo interativo.
E a propósito, não subestime esse método de desenvolvimento. Ele não apenas é uma ferramenta de grande valor para o aprendizado, como também permite explorar o seu programa enquanto o desenvolve. E ele pode ainda ser enriquecido por outros métodos, como o uso de arquivos-fonte Lua, como veremos a seguir.
Arquivos-fonte Lua
Do mesmo modo que podemos informar trechos interativamente ao
interpretador lua
, podemos também armazenar todo o seu conteúdo
em um arquivo, e então passar este arquivo como argumento para o
interpretador.
Convencionalmente chamamos esses arquivos de arquivo-fonte, visto que ele contém código-fonte Lua, que será executado pelo interpretador. Um arquivo-fonte não possui nada de muito especial, é um arquivo de texto comum, no qual inserimos os trechos Lua que queremos executar. Também é comum chamar esses arquivos de scripts.
O que são scripts?
Também, por convenção, utilizamos a extensão .lua
nesses
arquivos, para denotar que se trata de um arquivo-fonte Lua. Você pode
utilizar qualquer editor de texto simples como o editor vi
em
sistemas POSIX ou o bloco de notas do Windows, por exemplo.
Talvez eles não ofereçam muito conforto para programar, mas é fato que eles servem a esse propósito, na falta de uma opção mais ergonômica.
Em defesa do vi
Alguns editores de texto, como vim ou geany por exemplo, já oferecem mais recursos para a programação, e talvez sejam mais ao seu estilo.
Seja como for, Lua é uma linguagem simples, o que elimina a necessidade de um editor muito avançado para programar. Essa é uma das vantagens de ter a simplicidade como uma característica impressa na linguagem.
Então vamos começar com um teste básico. Crie um arquivo chamado
astronauta.lua
com o conteúdo:
print("Estou na Lua")
Depois, execute no terminal o seguinte comando:
$ lua astronauta.lua
Estou na Lua
O resultado, como podemos ver, será a exibição na tela do texto
“Estou na Lua”, igual já vimos antes na execução interativa. É
importante notar que como não estamos no modo interativo, precisamos recorrer
à função print
para exibir uma saída.
Perceba também que nesse texto de exemplo não temos nenhuma ocorrência de acentos ou cedilha. Isso não foi por acaso. Arquivos-fonte são, como falei, arquivos de texto, e portanto estão sujeitos a questões relacionadas à codificação de texto, algo que não vamos explorar agora.
O que é codificação de caracteres?
Por hora, apenas saiba que existem situações em que, executar no terminal um arquivo-fonte contendo acentos e cedilhas pode emitir alguns caracteres estranhos na tela, então por enquanto vamos evitá-los.
Voltando ao tema central deste tópico, a execução de códigos a partir de arquivo-fonte possui como vantagem o fato de que podemos armazenar nosso código em um arquivo e reexecutá-lo tantas vezes quanto for preciso, e gradualmente fazer modificações no programa, quando necessário.
Para ver como isso é prático faça uma modificação no arquivo
astronauta.lua
, insira nele uma segunda linha, com o
comando:
print("Sinto falta da Terra")
Agora execute novamente, e você verá não uma, mas duas mensagens, como a seguir:
$ lua astronauta.lua
Estou na Lua
Sinto falta da Terra
O ponto a se observar aqui é que você não precisou reescrever o primeiro comando, apenas inserir o segundo. À medida que criar programas maiores, perceberá o quanto é conveniente salvar em arquivos-fonte as partes que quer reaproveitar (ou que pode querer modificar no futuro).
Comentários
Outra vantagem no uso de arquivos-fonte é o fato de que podemos inserir neles alguns comentários. Como em várias outras linguagens, comentários não afetam a execução do programa, mas podem ser úteis para o programador, ao ler o código do programa.
Comentários servem para facilitar a compreensão do programa, em geral para alertar sobre algo que não deve ser modificado, ou para explicar por que algo foi feito de determinado modo.
Em Lua, comentários são iniciados pela sequência de caracteres
--
(dois hífens), e se estendem até o fim da linha. Vamos
adicionar um comentário ao programa astronauta.lua
, de modo que
ele possua o seguinte conteúdo:
-- Mensagens de um astronauta
print("Estou na Lua")
print("Sinto falta da Terra")
Veja que na primeira linha inserimos um comentário. Execute o programa, e
perceba que o comentário em nada alterou o comportamento do programa, mas ao
ler o arquivo astronauta.lua
você verá a explicação de que o
programa emite mensagens de um astronauta.
Comentários podem ser inseridos na mesma linha onde há um comando (ou mesmo parte de um comando). Muitas vezes, fazemos isso para anunciar qual é o resultado que se espera da execução de uma linha específica. Por exemplo:
print(2+2) --> 4
O sinal de maior (>
) logo após o início do comentário não
muda absolutamente nada, apenas é uma convenção utilizada quando a finalidade
é justamente essa de anunciar um resultado esperado, pois o formato de seta
pode ter essa conotação de “resultado”.
Além desse comentário de linha, existem também os comentários de blocos, eles abrangem um grupo de linhas entre os seus delimitadores. Vejamos um exemplo.
--[[
Astronauta
----------
Mensagens de um astronauta na Lua.
--]]
print("Estou na Lua")
print("Sinto falta da Terra")
Veja que usamos os delimitadores --[[
e --]]
para, respectivamente, abrir e fechar o bloco, que vai da primeira à sexta
linha desse exemplo.
Comentários de bloco são úteis para mensagens mais longas, que necessitem várias linhas, mas não apenas isso. Também são úteis em situações nas quais você pode querer facilmente “ativar/desativar” determinado trecho de código.
Vejamos um exemplo:
i = 3
--[[
print("ALERTA: usando valor dobrado")
i = 2 * i
--]]
print(i)
Neste exemplo, atribuímos um valor à variável i
, e depois
imprimimos esse valor na tela. Porém no meio há um comentário de bloco. Sendo
um comentário, não afeta o resultado da execução.
Mas digamos que eu queira testar o resultado da execução fazendo com que o código dentro do comentário passe a ser válido. Muito simples: adicionamos um hífen extra no delimitador de abertura do bloco, do seguinte modo:
i = 3
---[[
print("ALERTA: usando valor dobrado")
i = 2 * i
--]]
print(i)
Notou a diferença na abertura do comentário? Agora são três hífens no lugar de dois. Isso faz com que ele passe a ser um comentário normal, e não mais uma abertura de comentário de bloco (e o de fechamento também passa a ser tratado como um comentário normal).
Logo, todo o código dentro do bloco passa a ser válido. Para desativar novamente, basta remover esse hífen extra.
Chamando arquivo em código Lua
Lembra quando eu disse que podemos combinar o método interativo com o uso
de arquivos-fonte? Chegou a hora de ver como. Em Lua, existe uma função
chamada dofile
, que lê o conteúdo de um arquivo-fonte e o
executa.
Podemos chamar essa função enquanto executamos interativamente o
interpretador. Vejamos um exemplo com nosso script,
astronauta.lua
.
> dofile("astronauta.lua")
Estou na Lua
Sinto falta da Terra
Nosso arquivo astronauta.lua
apenas emite algumas mensagens,
portanto se executarmos de novo, o resultado será exatamente o mesmo, ou
seja, as mensagens serão exibidas de novo. Em outros casos, poderíamos ter um
arquivo-fonte cuja execução depende do valor de alguma variável, e nesse caso
o resultado poderá ser diferente a cada nova chamada.
Podemos usar este recurso para realizar uma sequência de cálculos e validações, e ao final retornar o resultado. Vamos ver um outro exemplo de arquivo-fonte, que realiza uma operação aritmética e retorna seu resultado.
return 2 * (i + 14) + i * i
Salve este arquivo como calculo.lua
, e em seguida, execute
interativamente, definindo previamente o valor de i
:
> i = 3
> dofile("calculo.lua")
43
Veja que o arquivo utilizado possui um retorno, que corresponde ao
resultado da expressão. Neste caso, poderíamos definir qualquer valor para
i
e reexecutar o cálculo, obtendo um novo resultado. Esse
retorno é apresentado na tela, como qualquer outro retorno seria (o que já
vimos na seção anterior).
Se quisermos, podemos salvar este retorno em uma variável, no lugar de exibir na tela:
> i = 3
> resultado = dofile("calculo.lua")
> resultado
43
A função dofile
não precisa ser chamada apenas no modo
interativo. Podemos usá-la dentro de arquivos-fonte também, de modo que
teremos assim chamadas aninhadas, um arquivo chamando outro arquivo. Porém,
em tutoriais futuros veremos maneiras mais interessantes de realizar esse
tipo de operação.
Pré-executar arquivo
Além da função dofile
, temos ainda outra possibilidade de
combinação dos modos de execução. Em vez de chamar o arquivo após iniciado o
modo interativo, podemos informar um parâmetro (-i
) para o
interpretador lua
, seguido do nome de um ou mais arquivos-fonte
Lua que queremos pré-executar.
Isto fará com que o arquivo seja executado antes de entrar no
modo interativo. Vejamos um exemplo com o nosso já conhecido
astronauta.lua
:
$ lua -i astronauta.lua
Lua 5.4.7 Copyright (C) 1994-2024 Lua.org, PUC-Rio
Estou na Lua
Sinto falta da Terra
>
Perceba a ordem dos eventos neste caso: primeiro o interpretador Lua é
iniciado (a mensagem de direitos autorais que aparece no início está aí para
nos mostrar isso), depois nosso arquivo astronauta.lua
é
executado, exibindo as mensagens de saída, e por fim vemos o prompt
de comandos do interpretador.
Neste caso, usamos um arquivo simples, que apenas emite mensagens. Mas poderíamos ter usado um arquivo que define variáveis ou novas funções, o que nos habilita a criar um ambiente que pode ser reutilizado toda vez que iniciarmos o interpretador.
A título de exemplo, crie um arquivo chamado ambiente.lua
,
com o seguinte conteúdo:
i = 3
function dobro(n) return n * 2 end
Neste exemplo definimos uma variável e uma função (veremos mais sobre criação de funções em outro tutorial), que vamos utilizar no modo interativo. Então inicie o modo interativo importando esse arquivo, como a seguir:
$ lua -i ambiente.lua
> i
3
> dobro(i)
6
Isto ficará ainda mais interessante quando explorarmos o conceito de módulos, mas por enquanto acho que você já possui algumas ideias para colocar em prática. Vamos explorar módulos em outro momento.
Na linha de comando
Além de informar código Lua em um interpretador interativo e via
arquivos-fonte, podemos ainda informar o código como parâmetro de linha de
comando para o interpretador lua
, isto é, de modo não
interativo.
Veja um exemplo:
$ lua -e 'print("Amigo, estou aqui.")'
Amigo, estou aqui.
O que acontece neste exemplo é que o interpretador lua
é
iniciado, recebe um pequeno trecho de código como argumento e o executa. Uma
vez executado esse trecho, o interpretador é encerrado.
Não parece muito útil, não é mesmo? Bem, é útil para alguns pequenos
testes, mas podemos incrementar essa execução não interativa, ao encadear o
parâmetro -e
diversas vezes.
$ lua -e 'i = 2 + 2' -e 'print(i)'
4
Note que no primeiro trecho temos uma atribuição da variável
i
e no segundo trecho passamos i
como argumento
para a função print
. Isso mostra que a ordem dos trechos
informados importa, e que variáveis definidas em um trecho anterior podem ser
usadas em um trecho posterior.
Podemos combinar esse modo de execução com arquivos-fonte, ao executar a
função dofile
em um dos trechos informados, veja:
$ lua -e 'i = 3' -e 'print(dofile("calculo.lua"))'
43
Existe diferença entre usar vários parâmetros -e
separados ou
usar um só com todo o código? Não muita, mas existe.
Além da diferença estética óbvia, o que permite reorganizar a chamada de
linha de comando para ficar mais legível, cada parâmetro -e
informado é um trecho Lua por si só.
E vamos lembrar, cada trecho de código é “compilado” individualmente (veremos na seção seguinte o que essa compilação significa), o que permite que você determine explicitamente a ordem de compilação dos trechos, como se os estivesse informando, um por um, no modo interativo.
E antes que me esqueça de dizer, podemos também combinar esse modo de
execução (na linha de comando) com o uso de arquivo-fontes sem recorrer à
função dofile
, informando o arquivo após todas as opções do
interpretador. Por exemplo:
$ lua -e 'print("Mensagens de um astronauta:")' astronauta.lua
Veja que nesse caso executamos o trecho da linha de comando primeiro, e em seguida o arquivo-fonte. Isso pode ser útil na realizaçao de testes rápidos com arquivos-fonte que precisem de comandos pré-executados.
O compilador Lua
Falar em compilação em Lua pode confundir algumas
pessoas, ainda mais para quem leu o tutorial
anterior, que trata da instalação de Lua, e que menciona a compilação do
código-fonte da biblioteca Lua e dos programas lua
e
luac
.
No contexto da linguagem Lua, ou seja, ao falar em compilação de código Lua, o significado do termo compilação muda bastante. Aqui não se trata de gerar código em linguagem de máquina pronto para executar, e sim de gerar bytecode, um formato intermediário entre a linguagem Lua e a linguagem de máquina.
Lembra-se quando disse, no tutorial anterior, que a biblioteca Lua
transforma o código Lua em código de máquina, e que essa biblioteca é usada
pelo interpretador lua
? Pois bem, essa biblioteca possui dentro
de si uma máquina virtual bytecode, e converte em linguagem de
máquina, para executar.
O que é uma máquina virtual?
Portanto, quando você informa um trecho ao interpretador, ele converte primeiro esse trecho em bytecode e depois a máquina virtual lê o bytecode gerado, para enfim transformar em linguagem de máquina e executar.
Esse procedimento é repetido para cada trecho que você informa ao
interpretador. O que o compilador luac
faz é antecipar essa
primeira parte, da conversão para o bytecode. Vamos ver como isso é
feito (novamente com o astronauta.lua
).
$ luac astronauta.lua
Feito isso, o arquivo luac.out
terá sido gerado. Você não
conseguirá ler diretamente o seu conteúdo por se tratar de um formato
binário. Mas você pode executá-lo, como se ele fosse um arquivo-fonte Lua.
Veja:
$ lua luac.out
Estou na Lua
Sinto falta da Terra
A princípio, pode não parecer muito diferente de uma execução direta, porém nesse caso encurtamos o processo em uma etapa, o que faz diferença quando trabalhamos com arquivos-fonte maiores e/ou em maior quantidade.
No entanto, é preciso dizer que a compilação Lua não afeta em nada o tempo de execução do programa, apenas antecipa a conversão para o bytecode.
Se preferir, você pode usar outro nome para o arquivo de saída, no lugar
de luac.out
. Basta usar a opção -o
.
$ luac -o astronauta.out astronauta.lua
Isto é útil quando precisar gerar vários arquivos intermediários dentro de
um mesmo diretório. Ainda outra opção é não gerar arquivo de saída nenhum,
para isto utilizamos o parâmetro -p
:
$ luac -p astronauta.lua
E por que isso é útil? Porque se houver erros de sintaxe no arquivo-fonte, o compilador vai avisar, permitindo que você identifique e corrija esses erros. Caso nenhum erro seja detectado, o programa não emite nenhuma saída. Caso encontre, informará a linha e uma breve mensagem de erro.
Essa técnica pode inclusive ser usada com arquivos de bytecode gerados anteriormente, para se certificar de que são válidos e/ou não foram corrompidos.
$ luac -p astronauta.out
É importante lembrar que bytecodes gerados para uma versão específica de Lua não podem ser usados em outra versão. Sempre tenha o arquivo-fonte original como garantia de que poderá gerar um bytecode válido, quando necessário.
Resumo
Muito bem! Agora você já sabe como executar código Lua de três maneiras diferentes:
- Interativamente, chamando o interpretador
lua
sem parâmetros; - Via arquivos-fonte, passados como argumento para o interpretador;
- Como argumentos de linha de comando para o interpretador, usando a
opção
-e
.
Também viu como combinar esses métodos de execução, usando a função
dofile
e importações com a opção -i
do
interpretador. Além disso, viu alguns exemplos de comentários em
arquivos-fonte, para facilitar o entendimento do código.
E ainda compreendeu o que é um processo de compilação de código Lua e
quando faz sentido usar essa técnica, com o programa luac
.
Adendos
As seções a seguir tem relação com o conteúdo abordado, mas podem visar cenários mais específicos ou não interessar a uma parte do público, e por isso foram mantidas em separado:
Conteúdo relacionado
- Bibliografia – Lua – Conceitos básicos e API C
- Bibliografia – Uma introdução à programação em Lua
- Glossário – Bloco
- Glossário – Compilação Lua
- Glossário – Distribuição padrão
- Glossário – Interpretador Lua
- Glossário – Máquina virtual
- Glossário – Trecho
- Blog – Simples casa com simples