• Nenhum resultado encontrado

Passando uma Linguagem pelo Buraco de uma Agulha

N/A
N/A
Protected

Academic year: 2021

Share "Passando uma Linguagem pelo Buraco de uma Agulha"

Copied!
10
0
0

Texto

(1)

1

Passando uma Linguagem pelo Buraco

de uma Agulha

Como a capacidade de Lua de ser embarcada impactou seu projeto

Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes

Linguagens de script constituem um elemento importante no cenário atual de linguagens de

programação. Uma característica-chave de uma linguagem de script é a sua capacidade de integração com uma linguagem do sistema. Tal integração assume duas formas principais: (i) através de

mecanismos de extensão, e (ii) embarcando-se a linguagem de script. Na primeira forma, estende-se a linguagem de script com bibliotecas e funções escritos na linguagem do sistema e escreve-estende-se o programa principal na linguagem de script. Na segunda forma, a linguagem de script é embarcada em um programa anfitrião, denominado de programa hospedeiro (escrito na linguagem do sistema), de modo que o hospedeiro possa executar scripts e chamar funções definidas nos scripts; o programa principal é o programa hospedeiro. Nesse tipo de configuração, a linguagem do sistema é geralmente chamada de linguagem hospedeira.

Muitas linguagens (não necessariamente linguagens de script) suportam extensão através de uma FFI (interface de função estrangeira, do inglês foreign function interface). Uma FFI não é suficiente para permitir que uma função na linguagem do sistema seja capaz de fazer tudo o que uma função no script pode fazer. No entanto, na prática FFIs suprem as necessidades mais comuns para a extensão, tais como o acesso a bibliotecas externas e chamadas de sistema. Embarcar a linguagem, por outro lado, é mais difícil de suportar, porque, geralmente, exige uma maior integração entre o programa hospedeiro e o script, e apenas utilizar uma FFI passa a não ser suficiente.

Neste artigo iremos discutir como a capacidade de ser embarcada pode impactar o projeto de uma linguagem, e em especial como tal capacidade impactou o projeto de Lua desde a sua concepção. Lua3,4 é uma linguagem de script com uma ênfase particularmente forte na capacidade de ser

embarcável. Ela tem sido embarcada em uma ampla gama de aplicações e é uma linguagem que tem ocupado uma posição de liderança na construção de jogos baseados em scripts2.

O BURACO DE UMA AGULHA

À primeira vista, a capacidade de uma linguagem de script de ser embarcável parece ser uma característica da implementação do seu interpretador. Dado qualquer interpretador, pode-se anexar ao mesmo uma API para permitir que o programa hospedeiro e o script interajam. O projeto da linguagem em si, no entanto, tem uma grande influência sobre a forma com que ela pode ser embarcada. Por outro lado, se uma linguagem for projetada com a capacidade de ser embarcável em mente, essa fato terá uma grande influência sobre a linguagem final.

(2)

2

uma linguagem de script onde os métodos devem ser escritos lexicamente dentro de suas classes,

a linguagem do hospedeiro não pode adicionar métodos a uma classe, a menos que a API ofereça mecanismos adequados para tal. Da mesma forma, é difícil fazer passar o escopo léxico através de uma API, porque as funções do hospedeiro não podem estar lexicamente dentro de funções de script.

Um ingrediente chave na API para uma linguagem embarcada é uma função eval, que executa uma parte de código. Em particular, quando uma linguagem de script é embarcada, todos os scripts são executados pelo hospedeiro chamando eval.

Uma função eval também permite uma abordagem minimalista para projetar uma API. Com uma função eval adequada, um hospedeiro pode fazer praticamente qualquer coisa no ambiente de script: ele pode fazer atribuições a variáveis (eval”a = 20”), fazer consultas aos valores de variáveis (eval”return a”), chamar funções (eval”foo(32,’stat’)”), etc. Estruturas de dados tais como vetores e matrizes podem ser construídas e decompostas por meio da avaliação de código apropriado. Por exemplo, novamente assumindo uma função hipotética eval, o seguinte código em C copiaria um vetor C de inteiros no script:

void copy (int ar[], int n) { int i;

eval(“ar = {}”); /* create an empty array */ for (i =0; i <n; i++){

char buff[100];

sprintf(buff, “ar[%d] = %d”, i + 1, ar[i]); eval(buff); /* assign i-th element */ } }

Apesar de sua simplicidade e sua completude satisfatórias, uma API composta por uma única função eval possui dois inconvenientes: é demasiado ineficiente para ser usada de forma intensiva, devido ao custo de análise e interpretação de um pedaço de código a cada interação; e é muito difícil de usar, devido a manipulação de string necessária para criar comandos em C e a necessidade de serializar todos os dados que passam através da API. No entanto, esta abordagem é frequentemente usada em aplicações reais. Python chama tal abordagem de “Embarcar em Muito Alto nível.”8

Para uma API mais eficiente e fácil de usar, precisamos de mais complexidade. Além de uma função eval para a execução de scripts, precisamos de formas diretas para chamar funções definidas por scripts, para lidar com erros em scripts, para transferir dados entre o programa hospedeiro e o ambiente de script, etc. Nas seções seguintes, discutiremos esses vários aspectos de uma API para uma linguagem embarcada e como eles têm afetado e sido afetados pelo projeto de Lua. Porém, primeiro vamos discutir como a simples existência de tal API pode afetar uma linguagem.

(3)

3

simplifica a implementação do interpretador; uma vez que um mecanismo é disponível para a API,

ele pode facilmente ser colocado como disponível para a linguagem. Por outro lado, ele também força os recursos de linguagem a passarem pelo buraco da agulha. Vamos ver um exemplo concreto deste trade-off quando discutirmos manipulação de exceções.

CONTROLE

O primeiro problema relacionado ao controle que toda linguagem de script deve resolver é o

problema “quem-tem-a-função-principal (main)”. Quando usamos a linguagem de script embarcada em um hospedeiro, desejamos que a linguagem seja uma biblioteca, com a função principal no hospedeiro. Para muitas aplicações, no entanto, desejamos a linguagem como um programa

stand-alone com a sua própria função principal interna. Lua resolve este problema com o uso de um

programa stand-alone separado. Lua em si é inteiramente implementada como uma biblioteca, com o objetivo de ser embarcada em outras aplicações. O programa de linha de comando Lua é apenas uma pequena aplicação que utiliza a biblioteca de Lua como qualquer outra máquina para executar partes de código Lua. O código a seguir é uma versão reduzida ao essencial desta aplicação. A aplicação real, é claro, é maior do que o trecho apresentado, uma vez que ela tem de lidar com opções, erros, sinais e outros detalhes de uma configuração real, mas ainda assim, ela tem menos de 500 linhas de código C.

#include <stdio.h>

#include “luaxlib.h” #include “lualib.h” int main (void) {

char line[256];

lua_State *L = luaL_newstate(); /* create a new state */ luaL_openlibs(L); /* open the standard libraries */ /* reads lines and executes them */

while (fgets(line, sizeof(line), stdin) != NULL) {

luaL_loadstring(L, line); /* compile line to a function */ lua_pcall(L, 0, 0, 0); /* call the function */

}

lua_close(L); return 0; }

Apesar de chamadas de funções constituírem a maior parte da comunicação de controle entre Lua e C, há outras formas de controle expostas através da API: iteradores, manipuladores de erros e co-rotinas. Iteradores em Lua permitem construções como as seguintes, que iteram sobre todas as linhas de um arquivo:

for line in io.lines(file) do print(line) end

(4)

4

retorna uma nova linha do arquivo cada vez que é chamada. Assim, a API não precisa de nada

especial para lidar com iteradores. É fácil tanto para o código Lua usar iteradores escritos em C (como no caso de io.lines) quanto para o código C iterar usando um iterador escrito em Lua. Para este caso, não há suporte sintático; o código C tem que fazer explicitamente tudo o que a construção for faz implicitamente em Lua.

Tratamento de erros é outra área onde Lua sofreu uma forte influência da API. Toda manipulação de erro em Lua baseia-se no mecanismo de longjump de C. É um exemplo de um recurso exportado da API para a linguagem.

A API suporta dois mecanismos para chamar uma função Lua: desprotegido (unprotected) e protegido (protected). Uma chamada desprotegida não manipula erros: qualquer erro durante a chamada sofre um desvio longo (longjumps) através deste código para aterrissar em uma chamada protegida mais a frente na pilha de chamadas. Uma chamada protegida define um ponto de

recuperação usando setjmp, de modo que qualquer erro durante a chamada é capturado; a chamada sempre retorna com um código de erro apropriado. Tais chamadas protegidas são muito importantes em um cenário embarcado, onde um programa hospedeiro não pode se dar ao luxo de abortar por consequência de eventuais erros em um script. A aplicação simples previamente apresentada utiliza lua_pcall (chamada protegida) para chamar cada linha compilada em modo protegido.

A biblioteca padrão de Lua simplesmente exporta a função da API da chamada protegida para Lua sob o nome de pcall. Com o uso de pcall, o equivalente a uma construção try-catch em Lua fica como segue:

local ok, errorobject = pcall(function() --here goes the protected code

... end) if not ok then

--here goes the error handling code

--(errorobject has more information about the error) ... end

Tal solução certamente é mais complexa do que um mecanismo de try-catch primitivo construído dentro da linguagem, porém ajusta-se perfeitamente com a API C e possui uma implementação muito leve.

O projeto de co-rotinas em Lua é outra área onde a API teve um grande impacto. Co-rotinas apresentam duas modalidades: simétricas e assimétricas. Co-rotinas simétricas oferecem uma única primitiva de transferência de controle, tipicamente chamada de transfer, que age como um goto: ele pode transferir o controle de qualquer co-rotina a qualquer outra. Co-rotinas assimétricas oferecem duas primitivas de transferência de controle, tipicamente denominadas resume e yield, as quais agem como um par chamada-retorno: um comando resume pode transferir o controle para qualquer outra co-rotina; um yield faz a co-rotina correntemente em execução ceder a vez e retorna para aquela que retomou a que estava cedendo a vez.

(5)

5

adiciona a pilha alvo no topo da pilha corrente.

Uma co-rotina simétrica é mais simples do que uma assimétrica, mas possui um grande problema para uma linguagem embarcada como Lua. Qualquer função C ativa em um script deve ter um registro de ativação correspondente na pilha C. A qualquer ponto durante a execução de um script, a pilha de chamadas pode ter uma mistura de funções C e funções Lua. (Em particular, a parte inferior da pilha de chamadas tem sempre uma função C, que é o programa hospedeiro que iniciou o script). No entanto, um programa não pode remover essas entradas em C da pilha de chamadas, porque C não oferece qualquer mecanismo para a manipulação de sua pilha de chamadas. Portanto, o programa não pode fazer qualquer transferência. Co-rotinas assimétricas não têm esse problema, porque a primitiva resume não afeta a pilha corrente. Ainda existe uma restrição de que um

programa não pode ceder a execução através de uma chamada C, isto é, não pode haver uma função C na pilha entre o resume e o yield. Esta restrição é um preço pequeno a pagar para se permitir co-rotinas portáteis em Lua.

DADOS

Um dos principais problemas com a abordagem minimalista de eval para uma API é a necessidade de serializar todos os dados como uma string ou um segmento de código que reconstrói os dados. Uma API prática deveria, portanto, oferecer outros mecanismos mais eficientes para transferir dados entre o programa hospedeiro e o ambiente de script.

Quando o hospedeiro chama um script, dados fluem a partir do programa hospedeiro para o ambiente de script como argumentos, e fluem na direção oposta, como resultado. Quando o script chama uma função do hospedeiro, temos o inverso. Em ambos os casos, os dados devem ser capazes de fluir em ambas as direções. A maioria das questões relacionadas com a transferência de dados, portanto, são relevantes tanto para a versão embarcada quanto de extensão.

Para discutir como a API Lua-C manipula esse fluxo de dados, vamos começar com um exemplo de como estender Lua. O código a seguir mostra a implementação da função io.getenv, que acessa as variáveis de ambiente do programa hospedeiro.

static int os_getenv (lua_State *L) {

const char *varname = luaL_checkstring(L, 1); const char *value = getenv(varname);

lua_pushstring(L, value); return 1; }

Para um script ser capaz de chamar essa função é preciso registrá-lo no ambiente de script. Iremos em breve ver como fazer isso, por agora, vamos supor que ele foi registrado como uma variável global getenv, que pode ser usada como a seguir:

print(getenv(“PATH”))

(6)

6

estrutura de dados é uma pilha de valores Lua; dada a sua importância, nós iremos nos referir a ela

como a pilha. Quando o script Lua chama getenv, o interpretador Lua chama os_getenv com a pilha contendo somente os argumentos dados para getenv, com o primeiro argumento na posição 1 na pilha. A primeira coisa que os_getenv faz é chamar luaL_checkstring, que verifica se o valor Lua na posição 1 é realmente uma string e retorna um ponteiro para a string C correspondente. (Se o valor não for uma string luaL_checkstring sinaliza um erro utilizando um longjump, de modo que ele não retorne para os_getenv.)

Em seguida, a função chama getenv da biblioteca C, o que faz todo o trabalho real. É então chamada lua_ pushstring, que converte o valor da string C em uma string Lua e empurra tal string para a pilha. Finalmente, os_getenv retorna 1. Este retorno informa o interpretador Lua de quantos valores no topo da pilha devem ser considerados como resultados da função. (Funções em Lua pode retornar múltiplos resultados.)

Vamos agora voltar ao problema de como registrar os_getenv como getenv no ambiente de script. Uma maneira simples é mudar nosso exemplo anterior do programa Lua stand-alone básico da seguinte forma:

lua_State *L = luaL_newstate(); /* creates a new state */ luaL_openlibs(L); /* opens the standard libraries */ lua_pushcfunction(L, os_getenv);

lua_setglobal(L, “getenv”);

A primeira linha adicionada faz toda a mágica que precisamos para estender Lua com funções do hospedeiro. A função lua_pushcfunction recebe um ponteiro para uma função C e coloca na pilha uma função (Lua) que, quando chamada, chama sua correspondente função C. Como funções em Lua são valores de primeira classe, a API não precisa de facilidades extras para registrar funções globais, funções locais, métodos, etc. A API necessita apenas da única função de injeção lua_ pushcfunction. Uma vez criado como uma função Lua, este novo valor pode ser manipulado como qualquer outro valor Lua. A segunda linha adicionada no novo código chama lua_setglobal para definir o valor no topo da pilha (a nova função) como o valor da variável global getenv.

Além de serem valores de primeira classe, as funções em Lua são sempre anônimas. Uma declaração como:

function inc (x) return x + 1 end

é um facilitador sintático (syntactic sugar) para a seguinte atribuição: inc = function (x) return x + 1 end

O código da API que usamos para registrar a função getenv faz exatamente a mesma coisa que uma declaração em Lua: ele cria uma função anônima e a atribui a uma variável global.

(7)

7

para a pilha, e depois ele empurra os argumentos. Uma vez que a função (como um valor de primeira

classe) e os argumentos estão na pilha, o hospedeiro pode chamá-la com uma única primitiva da API, independentemente de onde a função veio.

Uma das características mais distintivas da Lua é o seu uso generalizado de tabelas. Uma tabela é essencialmente uma matriz associativa. As tabelas são os únicos mecanismos de estruturas de dados de Lua, de modo que elas desempenham um papel muito maior do que em outras linguagens com construções semelhantes. Lua usa tabelas não apenas para todas as suas estruturas de dados (registros, matrizes, etc.), mas também para outros mecanismos da linguagem, como módulos, objetos e ambientes.

O próximo exemplo ilustra a manipulação de tabelas através da API. A função os_environ cria e retorna uma tabela com todas as variáveis de ambiente disponíveis para um processo. A função assume o acesso à matriz de ambiente, que é predefinida em sistemas Posix; cada entrada nessa matriz é uma cadeia de caracteres na forma NOME= VALOR, descrevendo uma variável de ambiente. extern char **environ;

static int os_environ (lua_State *L) { int i;

/* push a new table onto the stack */ lua_newtable(L);

/* repeat for each environment variable */ for (i = 0; environ[i] != NULL; i++) { /* find the ’=’ in NAME=VALUE */ char *eq = strchr(environ[i], ’=’);

if (eq) { /* push name */ lua_pushlstring(L, environ[i], eq -environ[i]); /* push value */ lua_pushstring(L, eq + 1);

/* table[name] = value */ lua_settable(L, -3); } }

/* result is the table */ return 1; }

O primeiro passo de os_environ é criar uma nova tabela no topo da pilha, chamando lua_ newtable. Em seguida, a função percorre a matriz de ambiente para construir uma tabela em Lua refletindo o conteúdo de tal matriz. Para cada entrada no ambiente, a função empurra o nome da variável na pilha, empurra o valor da variável, e depois chama lua_settable para armazenar o par na nova tabela. (Diferentemente de lua_pushstring, que assume uma string terminada em zero, lua_pushlstring recebe um comprimento explícito.)

A função lua_settable assume que a chave e o valor para a nova entrada estão na parte superior da pilha, o argumento -3 na chamada informa onde a tabela está na pilha. (Índice de números negativos a partir do topo, então -3 significa três slots a partir do topo.)

A função lua_settable desempilha tanto a chave quanto o valor, mas deixa a tabela onde ela estava na pilha. Portanto, após cada iteração, a tabela está de volta ao topo. O retorno final 1 informa a Lua que esta tabela é o único resultado de os_environ.

Uma propriedade chave da API Lua é que ela não oferece nenhuma forma de código C se referir diretamente a objetos Lua; para que qualquer valor seja manipulado por código C, ele deve estar na pilha. Em nosso último exemplo, a função os_ environ cria uma tabela Lua, preenche-a com algumas entradas, e a retorna para o interpretador. O tempo todo a tabela permanece na pilha.

(8)

8

Versões mais antigas de Lua também ofereciam um recurso similar: um tipo lua_Object. Após algum

tempo, entretanto, nós decidimos modificar a API6.

O principal problema de um tipo lua_Object é a interação com o coletor de lixo. Em Python, o programador é responsável por chamar macros tais como Py_INCREF e DECREF para incrementar e decrementar a contagem de referência de objetos sendo manipulados pela API. Essa contagem explícita é complexa e sujeita a erros. Em JNI (e em versões anteriores do Lua), uma referência para um objeto é válida até que a função onde ele foi criado retorne. Essa abordagem é mais simples e mais segura do que uma contagem manual de referências, mas o programador perde o controle do tempo de vida dos objetos. Qualquer objeto criado em uma função só pode ser liberado quando a função retorna. Em contraste, a pilha permite ao programador controlar o tempo de vida de qualquer objeto de uma forma segura. Enquanto um objeto está na pilha, ele não pode ser coletado; uma vez fora da pilha, não pode ser manipulado. Além disso, a pilha oferece um modo natural para passar parâmetros e resultados.

O uso generalizado de tabelas em Lua tem um impacto claro sobre a API C. Qualquer coisa em Lua representada como uma tabela pode ser manipulada com exatamente as mesmas operações. Como um exemplo, módulos em Lua são implementados como tabelas. Um módulo em Lua nada mais é do que uma tabela contendo as funções do módulo e seus ocasionais dados. (Lembre-se, as funções são valores de primeira classe em Lua.) Quando você escreve algo como math.sin(x), você pensa nisso como uma chamada a função sin do módulo math, mas você está realmente chamando o conteúdo do campo “sin” na tabela armazenada na variável global math. Portanto, é muito fácil para o hospedeiro criar módulos, adicionar funções a módulos existentes, “importar” módulos escritos em Lua, e outros procedimentos semelhantes.

Objetos em Lua seguem um padrão semelhante. Lua usa um estilo baseado em protótipo para programação orientada a objetos, onde os objetos são representados por tabelas. Métodos são implementados como funções armazenadas em protótipos. Da mesma forma que ocorre com módulos, é muito fácil para o hospedeiro criar objetos, chamar métodos, etc. Em sistemas baseados em classes, instâncias de uma classe e suas subclasses devem compartilhar alguma estrutura. Sistemas baseados em protótipos não possuem essa exigência, então objetos do hospedeiro podem herdar o comportamento de objetos de script e vice-versa.

EVAL E AMBIENTES

Uma característica primária de uma linguagem dinâmica é a presença do construtor eval, o qual permite a execução de código construído em tempo de execução. Como já discutido, uma função eval é também um elemento fundamental em uma API para uma linguagem de script. Em particular, eval é o mecanismo básico usado por um hospedeiro para executar scripts.

Lua não oferece diretamente uma função eval. Em vez disso, ela oferece uma função load. (O código simples do exemplo Lua acima usa a função luaL_loadstring, que é uma variante da função load.) Esta função não executa um pedaço de código, em vez disso, ela produz uma função Lua que, quando chamada, executa o pedaço de código.

(9)

9

simplifica-se um pouco; em particular, diferentemente de eval, load nunca tem efeitos colaterais.

A separação entre a compilação e a execução também evita um problema combinatório. Lua possui três funções load diferentes, dependentes da fonte: uma para carga de cadeias de caracteres, uma para o carregamento de arquivos, e uma para o carregamento de dados lidos por uma dada função de leitura. (As duas primeiras funções são implementadas com base na última.) Devido ao fato de haver dois modos de chamar funções (protegido e desprotegido), seriam necessárias seis diferentes funções eval para cobrir todas as possibilidades.

A manipulação de erros também é mais simples, já que erros estáticos e dinâmicos ocorrem separadamente. Finalmente, load assegura que todo o código Lua esteja sempre dentro de alguma função, o que dá mais regularidade à linguagem.

Intimamente relacionado com a função eval é o conceito de ambiente. Toda linguagem Turing-completa pode interpretar-se, esta é uma marca de máquinas de Turing. O que torna eval especial é que ela executa código dinâmico no mesmo ambiente do programa que a está usando. Em outras palavras, uma construção eval oferece algum nível de reflexão. Por exemplo, não é muito difícil escrever um interpretador de C em C. Mas, confrontado com uma declaração como x = 1, este interpretador não tem como acessar a variável x no programa, se houver uma. (Algumas facilidades não-ANSI, tais como aquelas relacionadas à bibliotecas de ligação dinâmicas, permitem que um programa C encontre o endereço de um dado símbolo global, mas o programa ainda não consegue encontrar nada sobre seu tipo.)

Um ambiente em Lua é simplesmente uma tabela. Lua oferece apenas dois tipos de variáveis: variáveis locais e campos da tabela. Sintaticamente, Lua também oferece variáveis globais: qualquer nome que não esteja vinculado a uma declaração local é considerado global. Semanticamente, esses nomes não vinculados referem-se a campos em uma tabela específica associados com a função que os declara; esta tabela é denominada o ambiente de tal função. Em um programa típico, a maioria das funções (ou todas) compartilham uma única tabela de ambiente, a qual, por sua vez, tem o papel de um ambiente global.

As variáveis globais são facilmente acessíveis através da API. Devido ao fato dessas variáveis serem campos de tabela, elas podem ser acessadas através da API padrão de manipulação de tabelas. Por exemplo, a função lua_setglobal, que aparece no código Lua mostrado anteriormente, é na verdade uma macro simples escrita com as primitivas de manipulação de tabelas.

Variáveis locais, por outro lado, seguem regras léxicas de escopo rigorosas; logo elas não participam da API. Como código C não pode ser lexicalmente aninhado dentro de código Lua, o código C não pode acessar variáveis locais Lua (exceto através de alguns mecanismos de depuração). Este é praticamente o único mecanismo de Lua que não pode ser emulado através da API.

(10)

10

CONCLUSÃO

Nós argumentamos que o fornecimento de uma API para o mundo externo não é um detalhe na implementação de uma linguagem de script, mas sim uma decisão que pode afetar toda a linguagem. Nós mostramos como o projeto de Lua foi afetado pela sua API e vice-versa.

O projeto de qualquer linguagem de programação envolve muitos desses trade-offs. Alguns atributos da linguagem, como a simplicidade, favorecem da capacidade de embarcar a linguagem, enquanto outros, como a verificação estática, não o fazem. O projeto de Lua envolve vários

trade-offs em torno da sua capacidade de ser embarcada. O suporte para os módulos é um exemplo típico.

Lua suporta módulos com um mínimo de mecanismos adicionais, favorecendo a simplicidade e capacidade de ser embarcada às custas de algumas facilidades, tais como importações não

qualificadas. Outro exemplo é o suporte para escopo léxico. Nesse aspecto escolhemos uma melhor verificação estática à custa da sua capacidade de ser embarcada. Estamos felizes com o equilíbrio dos

trade-offs em Lua, mas passar pelo buraco dessa agulha foi uma experiência de aprendizagem para

nós.

REFERÊNCIAS

1. Lucia de Moura, A., Ierusalimschy, R. 2009. Revisiting coroutines. ACM Transactions on

Programming Languages and Systems 31(2): 6.1–6.31.

2. DeLoura, M. 2009. The engine survey: general results. Gamasutra; http://www.gamasutra.com/ blogs/MarkDeLoura/20090302/581/The_Engine_Survey_General_results.php.

3. Ierusalimschy, R. 2006. Programming in Lua, second edition. Lua.org, Rio de Janeiro, Brazil. 4. Ierusalimschy, R., de Figueiredo, L. H., Celes, W. 1996. Lua—an extensible extension language.

Software: Practice and Experience 26(6): 635–652.

5. Ierusalimschy, R., de Figueiredo, L. H., Celes, W. 2005. The implementation of Lua 5.0. Journal of

Universal Computer Science 11(7): 1159–1176.

6. Ierusalimschy, R., de Figueiredo, L. H., Celes, W. 2007. The evolution of Lua. In the Third ACM

SIGPLAN Conference on History of Programming Languages: 2.1–2.26, San Diego, CA (June).

7. Ousterhout, J. K. 1998. Scripting: higher-level programming for the 21st century. IEEE Computer, 31(3): 23–30.

8. Python Software Foundation. 2011. Extending and embedding the Python interpreter, release 2.7 (April); http://docs.python.org/extending/.

ADOROU? DETESTOU? CONTE PARA NÓS.

feedback@queue.acm.org

ROBERTO IERUSALIMSCHY é professor associado de ciência da computação na PUC-Rio (Pontifícia

Universidade Católica do Rio de Janeiro), onde trabalha no projeto e implementação de linguagens de programação. Ele é o principal projetista da linguagem de programação Lua e autor de Programming in

Referências

Documentos relacionados

Por último, temos o vídeo que está sendo exibido dentro do celular, que é segurado e comentado por alguém, e compartilhado e comentado no perfil de BolsoWoman no Twitter. No

Local de realização da avaliação: Centro de Aperfeiçoamento dos Profissionais da Educação - EAPE , endereço : SGAS 907 - Brasília/DF. Estamos à disposição

No sentido de reverter tal situação, a realização deste trabalho elaborado na disciplina de Prática enquanto Componente Curricular V (PeCC V), buscou proporcionar as

Código Descrição Atributo Saldo Anterior D/C Débito Crédito Saldo Final D/C. Este demonstrativo apresenta os dados consolidados da(s)

É nesse cenário que se insere o conceito de Silvicultura de Precisão, que pode ser considerado um método de gerenciamento das atividades silviculturais, que se baseia na coleta

O termo extrusão do núcleo pulposo aguda e não compressiva (Enpanc) é usado aqui, pois descreve as principais características da doença e ajuda a

Para Piaget, a forma de raciocinar e de aprender da criança passa por estágios. Por volta dos dois anos, ela evolui do estágio sensório motor, em que a ação envolve os

Principais mudanças na PNAB 2017  Estratégia Saúde da Família/Equipe de Atenção Básica  Agentes Comunitários de Saúde  Integração da AB e Vigilância 