4.1 O Avaliador Metacircular
Nosso avaliador para JavaScript será implementado como um programa JavaScript. Pode parecer circular pensar em avaliar programas JavaScript usando um avaliador que é ele mesmo implementado em JavaScript. No entanto, a avaliação é um processo, portanto é apropriado descrever o processo de avaliação usando JavaScript, que, afinal, é nossa ferramenta para descrever processos.1 Um avaliador que é escrito na mesma linguagem que ele avalia é dito ser metacircular.
O avaliador metacircular é essencialmente uma formulação JavaScript do modelo de ambiente de avaliação descrito na seção 3.2. Lembre-se de que o modelo especifica a avaliação da aplicação de função em dois passos básicos:
-
Para avaliar uma aplicação de função, avalie as subexpressões e depois aplique o valor da subexpressão de função aos valores das subexpressões de argumento.
-
Para aplicar uma função composta a um conjunto de argumentos, avalie o corpo da função em um novo ambiente. Para construir este ambiente, estenda a parte de ambiente do objeto função por um frame no qual os parâmetros da função estão vinculados aos argumentos aos quais a função é aplicada.
Estas duas regras descrevem a essência do processo de avaliação, um ciclo básico no qual declarações e expressões a serem avaliadas em ambientes são reduzidas a funções a serem aplicadas a argumentos, que por sua vez são reduzidas a novas declarações e expressões a serem avaliadas em novos ambientes, e assim por diante, até chegarmos a nomes, cujos valores são procurados no ambiente, e a operadores e funções primitivas, que são aplicados diretamente (veja a figura 4.1).
Figura 4.1: O ciclo eval-apply
Nota: O ciclo central entre evaluate e apply; casos base em verde
Este ciclo de avaliação será incorporado pela interação entre as duas funções críticas no avaliador, evaluate e apply, que são descritas na seção 4.1.1 (veja a figura 4.1).
A implementação do avaliador dependerá de funções que definem a sintaxe das declarações e expressões a serem avaliadas. Usaremos abstração de dados para tornar o avaliador independente da representação da linguagem. Por exemplo, em vez de nos comprometermos com a escolha de que uma atribuição deve ser representada por uma string começando com um nome seguido por =, usamos um predicado abstrato is_assignment para testar uma atribuição, e usamos seletores abstratos assignment_symbol e assignment_value_expression para acessar as partes de uma atribuição. As camadas de abstração de dados apresentadas na seção 4.1.2 permitirão que o avaliador permaneça independente de questões sintáticas concretas, como as palavras-chave da linguagem interpretada, e da escolha de estruturas de dados que representam os componentes do programa. Também existem operações, descritas na seção 4.1.3, que especificam a representação de funções e ambientes. Por exemplo, make_function constrói funções compostas, lookup_symbol_value acessa os valores de nomes, e apply_primitive_function aplica uma função primitiva a uma determinada lista de argumentos.