@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
657 lines • 32.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Interpretador = void 0;
const construtos_1 = require("../construtos");
const estruturas_1 = require("./estruturas");
const interpretador_base_1 = require("./interpretador-base");
const inferenciador_1 = require("../inferenciador");
const excecoes_1 = require("../excecoes");
const quebras_1 = require("../quebras");
const montao_1 = require("./montao");
const primitivas_dicionario_1 = __importDefault(require("../bibliotecas/primitivas-dicionario"));
const primitivas_numero_1 = __importDefault(require("../bibliotecas/primitivas-numero"));
const primitivas_texto_1 = __importDefault(require("../bibliotecas/primitivas-texto"));
const primitivas_vetor_1 = __importDefault(require("../bibliotecas/primitivas-vetor"));
const primitivos_1 = __importDefault(require("../tipos-de-dados/primitivos"));
const delegua_1 = __importDefault(require("../tipos-de-dados/delegua"));
/**
* O interpretador de Delégua.
*/
class Interpretador extends interpretador_base_1.InterpretadorBase {
constructor(diretorioBase, performance = false, funcaoDeRetorno = null, funcaoDeRetornoMesmaLinha = null) {
super(diretorioBase, performance, funcaoDeRetorno, funcaoDeRetornoMesmaLinha);
this.montao = new montao_1.Montao();
}
resolverReferenciaMontao(referenciaMontao) {
const valorMontao = this.montao.obterReferencia(this.hashArquivoDeclaracaoAtual, this.linhaDeclaracaoAtual, referenciaMontao.endereco);
return valorMontao;
}
resolverValor(objeto) {
if (objeto === null || objeto === undefined) {
return objeto;
}
if (Array.isArray(objeto)) {
const vetorResolvido = [];
for (const elemento of objeto) {
vetorResolvido.push(this.resolverValor(elemento));
}
return vetorResolvido;
}
if (objeto instanceof estruturas_1.ReferenciaMontao) {
return this.resolverReferenciaMontao(objeto);
}
if (objeto.hasOwnProperty('valor')) {
if (Array.isArray(objeto.valor)) {
return this.resolverValor(objeto.valor);
}
if (objeto.valor instanceof estruturas_1.ReferenciaMontao) {
return this.resolverReferenciaMontao(objeto.valor);
}
return objeto.valor;
}
return objeto;
}
paraTexto(objeto) {
if (objeto === null || objeto === undefined)
return delegua_1.default.NULO;
if (typeof objeto === primitivos_1.default.BOOLEANO) {
return objeto ? 'verdadeiro' : 'falso';
}
if (objeto.valor instanceof estruturas_1.ObjetoPadrao)
return objeto.valor.paraTexto();
if (objeto instanceof estruturas_1.ObjetoDeleguaClasse ||
objeto instanceof estruturas_1.DeleguaFuncao ||
typeof objeto.paraTexto === 'function')
return objeto.paraTexto();
if (objeto instanceof quebras_1.RetornoQuebra) {
if (typeof objeto.valor === 'boolean')
return objeto.valor ? 'verdadeiro' : 'falso';
}
if (objeto instanceof Date) {
const formato = Intl.DateTimeFormat('pt', {
dateStyle: 'full',
timeStyle: 'full',
});
return formato.format(objeto);
}
if (Array.isArray(objeto)) {
let retornoVetor = '[';
for (let elemento of objeto) {
if (typeof elemento === 'object') {
retornoVetor += `${JSON.stringify(elemento)}, `;
continue;
}
retornoVetor +=
typeof elemento === 'string'
? `'${elemento}', `
: `${this.paraTexto(elemento)}, `;
}
if (retornoVetor.length > 1) {
retornoVetor = retornoVetor.slice(0, -2);
}
retornoVetor += ']';
return retornoVetor;
}
switch (objeto.constructor.name) {
case 'Object':
if ('tipo' in objeto) {
switch (objeto.tipo) {
case 'dicionário':
return JSON.stringify(objeto.valor);
default:
return objeto.valor;
}
}
}
if (typeof objeto === primitivos_1.default.OBJETO) {
const objetoEscrita = {};
for (const propriedade in objeto) {
let valor = objeto[propriedade];
if (typeof valor === primitivos_1.default.BOOLEANO) {
valor = valor ? 'verdadeiro' : 'falso';
}
if (valor instanceof estruturas_1.ReferenciaMontao) {
valor = this.resolverValor(valor);
}
objetoEscrita[propriedade] = valor;
}
return JSON.stringify(objetoEscrita);
}
return objeto.toString();
}
async avaliacaoDeclaracaoVarOuConst(declaracao) {
let valorOuOutraVariavel = null;
if (declaracao.inicializador !== null) {
valorOuOutraVariavel = await this.avaliar(declaracao.inicializador);
}
let valorFinal = null;
if (valorOuOutraVariavel !== null && valorOuOutraVariavel !== undefined) {
valorFinal = this.resolverValor(valorOuOutraVariavel);
}
return valorFinal;
}
async avaliarArgumentosEscreva(argumentos) {
let formatoTexto = '';
for (const argumento of argumentos) {
const resultadoAvaliacao = await this.avaliar(argumento);
let valor = this.resolverValor(resultadoAvaliacao);
formatoTexto += `${this.paraTexto(valor)} `;
}
return formatoTexto.trimEnd();
}
visitarDeclaracaoDefinicaoFuncao(declaracao) {
const funcao = new estruturas_1.DeleguaFuncao(declaracao.simbolo.lexema, declaracao.funcao);
// TODO: Depreciar essa abordagem a favor do uso por referências.
this.pilhaEscoposExecucao.definirVariavel(declaracao.simbolo.lexema, funcao);
this.pilhaEscoposExecucao.registrarReferenciaFuncao(declaracao.id, funcao);
}
async visitarExpressaoAcessoIndiceVariavel(expressao) {
const promises = await Promise.all([
this.avaliar(expressao.entidadeChamada),
this.avaliar(expressao.indice),
]);
const variavelObjeto = promises[0];
const indice = promises[1];
const objeto = this.resolverValor(variavelObjeto);
let valorIndice = this.resolverValor(indice);
if (Array.isArray(objeto)) {
if (!Number.isInteger(valorIndice)) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Somente inteiros podem ser usados para indexar um vetor.', expressao.linha));
}
if (valorIndice < 0 && objeto.length !== 0) {
while (valorIndice < 0) {
valorIndice += objeto.length;
}
}
if (valorIndice >= objeto.length) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Índice do vetor fora do intervalo.', expressao.linha));
}
return objeto[valorIndice];
}
if (objeto instanceof construtos_1.Vetor) {
return objeto.valores[valorIndice];
}
if (objeto.constructor === Object ||
objeto instanceof estruturas_1.ObjetoDeleguaClasse ||
objeto instanceof estruturas_1.DeleguaFuncao ||
objeto instanceof estruturas_1.DescritorTipoClasse ||
objeto instanceof estruturas_1.DeleguaModulo) {
if (objeto[valorIndice] === 0)
return 0;
return objeto[valorIndice] || null;
}
if (typeof objeto === primitivos_1.default.TEXTO) {
if (!Number.isInteger(valorIndice)) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Somente inteiros podem ser usados para indexar um vetor.', expressao.linha));
}
if (valorIndice < 0 && objeto.length !== 0) {
while (valorIndice < 0) {
valorIndice += objeto.length;
}
}
if (valorIndice >= objeto.length) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Índice fora do tamanho.', expressao.linha));
}
return objeto.charAt(valorIndice);
}
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao({
hashArquivo: this.hashArquivoDeclaracaoAtual,
linha: this.linhaDeclaracaoAtual,
}, 'Somente listas, dicionários, classes e objetos podem ter seus valores indexados.', expressao.linha));
}
async visitarExpressaoAcessoMetodo(expressao) {
let variavelObjeto = await this.avaliar(expressao.objeto);
// Este caso acontece quando há encadeamento de métodos.
// Por exemplo, `objeto1.metodo1().metodo2()`.
// Como `RetornoQuebra` também possui `valor`, precisamos extrair o
// valor dele primeiro.
if (variavelObjeto.constructor.name === 'RetornoQuebra') {
variavelObjeto = variavelObjeto.valor;
}
const objeto = this.resolverValor(variavelObjeto);
if (objeto.constructor.name === 'ObjetoDeleguaClasse') {
return objeto.obterMetodo(expressao.nomeMetodo) || null;
}
// Objeto simples do JavaScript, ou dicionário de Delégua.
if (objeto.constructor === Object) {
if (expressao.nomeMetodo in primitivas_dicionario_1.default) {
const metodoDePrimitivaDicionario = primitivas_dicionario_1.default[expressao.nomeMetodo].implementacao;
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaDicionario);
}
return objeto[expressao.nomeMetodo] || null;
}
// Casos em que o objeto possui algum outro tipo que não o de objeto simples.
// Normalmente executam quando uma biblioteca é importada, e estamos tentando
// obter alguma propriedade ou método desse objeto.
// Caso 1: Função tradicional do JavaScript.
if (typeof objeto[expressao.nomeMetodo] === primitivos_1.default.FUNCAO) {
return objeto[expressao.nomeMetodo];
}
// Caso 2: Objeto tradicional do JavaScript.
if (typeof objeto[expressao.nomeMetodo] === primitivos_1.default.OBJETO) {
return objeto[expressao.nomeMetodo];
}
// A partir daqui, presume-se que o objeto é uma das estruturas
// de Delégua.
if (objeto instanceof estruturas_1.DeleguaModulo) {
return objeto.componentes[expressao.nomeMetodo] || null;
}
let tipoObjeto = variavelObjeto.tipo;
if (tipoObjeto === null || tipoObjeto === undefined) {
tipoObjeto = (0, inferenciador_1.inferirTipoVariavel)(variavelObjeto);
}
// Como internamente um dicionário de Delégua é simplesmente um objeto de
// JavaScript, as primitivas de dicionário, especificamente, são tratadas
// mais acima.
switch (tipoObjeto) {
case delegua_1.default.INTEIRO:
case delegua_1.default.NUMERO:
case delegua_1.default.NÚMERO:
const metodoDePrimitivaNumero = primitivas_numero_1.default[expressao.nomeMetodo].implementacao;
if (metodoDePrimitivaNumero) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaNumero);
}
break;
case delegua_1.default.TEXTO:
const metodoDePrimitivaTexto = primitivas_texto_1.default[expressao.nomeMetodo].implementacao;
if (metodoDePrimitivaTexto) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaTexto);
}
break;
case delegua_1.default.VETOR:
case delegua_1.default.VETOR_NUMERO:
case delegua_1.default.VETOR_NÚMERO:
case delegua_1.default.VETOR_TEXTO:
const metodoDePrimitivaVetor = primitivas_vetor_1.default[expressao.nomeMetodo].implementacao;
if (metodoDePrimitivaVetor) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaVetor);
}
break;
}
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao({
hashArquivo: this.hashArquivoDeclaracaoAtual,
linha: this.linhaDeclaracaoAtual,
}, `Método para objeto ou primitiva não encontrado: ${expressao.nomeMetodo}.`, expressao.linha));
}
/**
* Casos que ocorrem aqui:
*
* - Quando o método ou propriedade é ou 'qualquer', ou vetor
* de 'qualquer' ('qualquer[]'), e uma primitiva é usada.
* - Quando o objeto é uma classe definida em código.
* @param {AcessoMetodoOuPropriedade} expressao A expressão de acesso a método ou propriedade.
* @returns A primitiva encontrada.
*/
async visitarExpressaoAcessoMetodoOuPropriedade(expressao) {
let variavelObjeto = await this.avaliar(expressao.objeto);
// Este caso acontece quando há encadeamento de métodos.
// Por exemplo, `objeto1.metodo1().metodo2()`.
// Como `RetornoQuebra` também possui `valor`, precisamos extrair o
// valor dele primeiro.
if (variavelObjeto.constructor.name === 'RetornoQuebra') {
variavelObjeto = variavelObjeto.valor;
}
const objeto = this.resolverValor(variavelObjeto);
if (objeto.constructor.name === 'ObjetoDeleguaClasse') {
return objeto.obter(expressao.simbolo);
}
// Objeto simples do JavaScript, ou dicionário de Delégua.
if (objeto.constructor === Object) {
if (expressao.simbolo.lexema in primitivas_dicionario_1.default) {
const metodoDePrimitivaDicionario = primitivas_dicionario_1.default[expressao.simbolo.lexema].implementacao;
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaDicionario);
}
return objeto[expressao.simbolo.lexema];
}
// A partir daqui, presume-se que o objeto é uma das estruturas
// de Delégua.
if (objeto instanceof estruturas_1.DeleguaModulo) {
return objeto.componentes[expressao.simbolo.lexema] || null;
}
let tipoObjeto = variavelObjeto.tipo;
if (tipoObjeto === null || tipoObjeto === undefined) {
tipoObjeto = (0, inferenciador_1.inferirTipoVariavel)(variavelObjeto);
}
// Como internamente um dicionário de Delégua é simplesmente um objeto de
// JavaScript, as primitivas de dicionário, especificamente, são tratadas
// mais acima.
switch (tipoObjeto) {
case delegua_1.default.INTEIRO:
case delegua_1.default.NUMERO:
case delegua_1.default.NÚMERO:
const metodoDePrimitivaNumero = primitivas_numero_1.default[expressao.simbolo.lexema].implementacao;
if (metodoDePrimitivaNumero) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaNumero);
}
break;
case delegua_1.default.TEXTO:
const metodoDePrimitivaTexto = primitivas_texto_1.default[expressao.simbolo.lexema].implementacao;
if (metodoDePrimitivaTexto) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaTexto);
}
break;
case delegua_1.default.VETOR:
case delegua_1.default.VETOR_INTEIRO:
case delegua_1.default.VETOR_LOGICO:
case delegua_1.default.VETOR_LÓGICO:
case delegua_1.default.VETOR_NUMERO:
case delegua_1.default.VETOR_NÚMERO:
case delegua_1.default.VETOR_QUALQUER:
case delegua_1.default.VETOR_TEXTO:
const metodoDePrimitivaVetor = primitivas_vetor_1.default[expressao.simbolo.lexema].implementacao;
if (metodoDePrimitivaVetor) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaVetor);
}
break;
}
// Último caso válido: objeto de uma classe JavaScript que possua a propriedade.
// Exemplos: classes de LinConEs, como `RetornoComando`, ou bibliotecas globais com objetos próprios.
if (objeto.hasOwnProperty(expressao.simbolo.lexema) ||
typeof objeto[expressao.simbolo.lexema] !== 'undefined') {
return objeto[expressao.simbolo.lexema];
}
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `Método ou propriedade para objeto ou primitiva não encontrado: ${expressao.simbolo.lexema}.`, expressao.linha));
}
async visitarExpressaoAcessoPropriedade(expressao) {
let variavelObjeto = await this.avaliar(expressao.objeto);
// Este caso acontece quando há encadeamento de métodos.
// Por exemplo, `objeto1.metodo1().metodo2()`.
// Como `RetornoQuebra` também possui `valor`, precisamos extrair o
// valor dele primeiro.
if (variavelObjeto.constructor.name === 'RetornoQuebra') {
variavelObjeto = variavelObjeto.valor;
}
const objeto = this.resolverValor(variavelObjeto);
// Outro caso que `instanceof` simplesmente não funciona para casos em Liquido,
// então testamos também o nome do construtor.
if (objeto instanceof estruturas_1.ObjetoDeleguaClasse ||
objeto.constructor.name === 'ObjetoDeleguaClasse') {
return objeto.obterMetodo(expressao.nomePropriedade) || null;
}
// Objeto simples do JavaScript, ou dicionário de Delégua.
if (objeto.constructor === Object) {
if (expressao.nomePropriedade in primitivas_dicionario_1.default) {
const metodoDePrimitivaDicionario = primitivas_dicionario_1.default[expressao.nomePropriedade].implementacao;
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaDicionario);
}
return objeto[expressao.nomePropriedade] || null;
}
// Casos em que o objeto possui algum outro tipo que não o de objeto simples.
// Normalmente executam quando uma biblioteca é importada, e estamos tentando
// obter alguma propriedade ou método desse objeto.
// Caso 1: Função tradicional do JavaScript.
if (typeof objeto[expressao.nomePropriedade] === primitivos_1.default.FUNCAO) {
return objeto[expressao.nomePropriedade];
}
// Caso 2: Objeto tradicional do JavaScript.
if (typeof objeto[expressao.nomePropriedade] === primitivos_1.default.OBJETO) {
return objeto[expressao.nomePropriedade];
}
// A partir daqui, presume-se que o objeto é uma das estruturas
// de Delégua.
if (objeto instanceof estruturas_1.DeleguaModulo) {
return objeto.componentes[expressao.nomePropriedade] || null;
}
let tipoObjeto = variavelObjeto.tipo;
if (tipoObjeto === null || tipoObjeto === undefined) {
tipoObjeto = (0, inferenciador_1.inferirTipoVariavel)(variavelObjeto);
}
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `Propriedade para objeto ou primitiva não encontrado: ${expressao.nomePropriedade}.`, expressao.linha));
}
async visitarExpressaoArgumentoReferenciaFuncao(expressao) {
const deleguaFuncao = this.pilhaEscoposExecucao.obterVariavelPorNome(expressao.simboloFuncao.lexema);
return deleguaFuncao;
}
async visitarExpressaoAtribuicaoPorIndice(expressao) {
const promises = await Promise.all([
this.avaliar(expressao.objeto),
this.avaliar(expressao.indice),
this.avaliar(expressao.valor),
]);
let objeto = promises[0];
let indice = promises[1];
const valor = promises[2];
if (objeto.tipo === delegua_1.default.TUPLA) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.objeto.simbolo.lexema, 'Não é possível modificar uma tupla. As tuplas são estruturas de dados imutáveis.', expressao.linha));
}
objeto = this.resolverValor(objeto);
indice = this.resolverValor(indice);
// Se o valor é uma referência ao montão, e o índice que a recebe é
// de uma variável/constante que vive num escopo superior, a referência
// precisa ser transferida para o escopo correspondente.
if (valor instanceof estruturas_1.ReferenciaMontao) {
// TODO: Terminar
const nomeVariavel = expressao.objeto.simbolo.lexema;
if (this.pilhaEscoposExecucao.obterVariavelEm(1, nomeVariavel) === undefined) {
this.pilhaEscoposExecucao.migrarReferenciaMontaoParaEscopoDeVariavel(nomeVariavel, valor.endereco);
}
}
if (Array.isArray(objeto)) {
if (indice < 0 && objeto.length !== 0) {
while (indice < 0) {
indice += objeto.length;
}
}
while (objeto.length < indice) {
objeto.push(null);
}
objeto[indice] = valor;
}
else if (objeto.constructor === Object ||
objeto instanceof estruturas_1.ObjetoDeleguaClasse ||
objeto instanceof estruturas_1.DeleguaFuncao ||
objeto instanceof estruturas_1.DescritorTipoClasse ||
objeto instanceof estruturas_1.DeleguaModulo) {
objeto[indice] = valor;
}
else {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.objeto.nome, 'Somente listas, dicionários, classes e objetos podem ser mudados por índice.', expressao.linha));
}
}
/**
* Execução de uma expressão de atribuição.
* @param expressao A expressão.
* @returns O valor atribuído.
*/
async visitarExpressaoDeAtribuicao(expressao) {
const valor = await this.avaliar(expressao.valor);
const valorResolvido = this.resolverValor(valor);
let indice = null;
if (expressao.indice) {
indice = await this.avaliar(expressao.indice);
}
switch (expressao.alvo.constructor.name) {
case 'Variavel':
const alvoVariavel = expressao.alvo;
const variavelResolvida = this.pilhaEscoposExecucao.obterValorVariavel(alvoVariavel.simbolo);
if (variavelResolvida.valor instanceof estruturas_1.ReferenciaMontao) {
const referenciaMontao = this.montao.obterReferencia(this.hashArquivoDeclaracaoAtual, this.linhaDeclaracaoAtual, variavelResolvida.valor.endereco);
referenciaMontao[indice] = valorResolvido;
}
else {
this.pilhaEscoposExecucao.atribuirVariavel(alvoVariavel.simbolo, valorResolvido, indice);
}
break;
case 'AcessoMetodoOuPropriedade':
// Nunca será método aqui: apenas propriedade.
const alvoPropriedade = expressao.alvo;
const variavelObjeto = await this.avaliar(alvoPropriedade.objeto);
const objeto = this.resolverValor(variavelObjeto);
const valor = await this.avaliar(expressao.valor);
if (objeto.constructor.name === 'ObjetoDeleguaClasse') {
const objetoDeleguaClasse = objeto;
objetoDeleguaClasse.definir(alvoPropriedade.simbolo, valor);
}
else {
// Se cair aqui, provavelmente `objeto.constructor.name` é 'Object'.
objeto[alvoPropriedade.simbolo.lexema] = valor;
}
break;
default:
throw new excecoes_1.ErroEmTempoDeExecucao(null, `Atribuição com caso faltante: ${expressao.alvo.constructor.name}.`);
}
return valorResolvido;
}
async visitarExpressaoDefinirValor(expressao) {
const variavelObjeto = await this.avaliar(expressao.objeto);
const objeto = this.resolverValor(variavelObjeto);
if (objeto.constructor.name !== 'ObjetoDeleguaClasse' && objeto.constructor !== Object) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.nome, 'Somente instâncias e dicionários podem possuir campos.', expressao.linha));
}
const valor = await this.avaliar(expressao.valor);
const valorResolvido = this.resolverValor(valor);
if (objeto.constructor.name === 'ObjetoDeleguaClasse') {
objeto.definir(expressao.nome, valorResolvido);
return valorResolvido;
}
if (objeto.constructor === Object) {
objeto[expressao.nome.lexema] = valorResolvido;
}
}
/**
* Dicionários em Delégua são passados por referência, portanto, são
* armazenados no montão.
*/
async visitarExpressaoDicionario(expressao) {
const dicionario = {};
for (let i = 0; i < expressao.chaves.length; i++) {
const promises = await Promise.all([
this.avaliar(expressao.chaves[i]),
this.avaliar(expressao.valores[i]),
]);
if (typeof promises[0] === 'boolean') {
const chaveLogico = promises[0] === true ? 'verdadeiro' : 'falso';
dicionario[chaveLogico] = promises[1];
continue;
}
dicionario[promises[0]] = this.resolverValor(promises[1]);
}
const enderecoDicionarioMontao = this.montao.adicionarReferencia(dicionario);
this.pilhaEscoposExecucao.registrarReferenciaMontao(enderecoDicionarioMontao);
return new estruturas_1.ReferenciaMontao(enderecoDicionarioMontao);
}
async visitarExpressaoReferenciaFuncao(expressao) {
const deleguaFuncao = this.pilhaEscoposExecucao.obterReferenciaFuncao(expressao.idFuncao);
return deleguaFuncao;
}
async visitarExpressaoRetornar(declaracao) {
let valor = null;
if (declaracao.valor !== null && declaracao.valor !== undefined) {
valor = await this.avaliar(declaracao.valor);
}
const retornoQuebra = new quebras_1.RetornoQuebra(valor);
// Se o retorno for uma função anônima, o escopo precisa ser preservado.
// Como quebras matam o topo da pilha de escopos, precisamos dizer
// para a finalização para copiar as variáveis para o escopo de baixo.
if (retornoQuebra.valor) {
const construtorRetorno = retornoQuebra.valor.constructor.name.replaceAll('_', '');
if (construtorRetorno === 'DeleguaFuncao') {
retornoQuebra.preservarEscopo = true;
}
}
return retornoQuebra;
}
async visitarExpressaoTipoDe(expressao) {
let valorTipoDe = expressao.valor;
switch (valorTipoDe.constructor.name) {
case 'AcessoIndiceVariavel':
case 'Agrupamento':
case 'Binario':
case 'Chamada':
case 'Dicionario':
case 'Unario':
valorTipoDe = await this.avaliar(valorTipoDe);
if (valorTipoDe instanceof estruturas_1.ReferenciaMontao) {
valorTipoDe = this.montao.obterReferencia(this.hashArquivoDeclaracaoAtual, this.linhaDeclaracaoAtual, valorTipoDe.endereco);
}
return valorTipoDe.tipo || (0, inferenciador_1.inferirTipoVariavel)(valorTipoDe);
case 'AcessoMetodo':
const acessoMetodo = valorTipoDe;
return `método<${acessoMetodo.tipoRetornoMetodo}>`;
case 'AcessoPropriedade':
const acessoPropriedade = valorTipoDe;
return acessoPropriedade.tipoRetornoPropriedade;
case 'AcessoMetodoOuPropriedade':
// TODO: Deve ser removido mais futuramente.
// Apenas `AcessoMetodo` e `AcessoPropriedade` devem funcionar aqui.
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, 'Não deveria cair aqui.');
case 'Escreva':
return 'função<vazio>';
case 'Leia':
return 'função<texto>';
case 'Literal':
const tipoLiteral = valorTipoDe;
return tipoLiteral.tipo;
case 'TipoDe':
const alvoTipoDe = await this.avaliar(valorTipoDe);
return `tipo de<${alvoTipoDe}>`;
case 'Variavel':
return valorTipoDe.tipo;
case 'Vetor':
return (0, inferenciador_1.inferirTipoVariavel)(valorTipoDe === null || valorTipoDe === void 0 ? void 0 : valorTipoDe.valores);
default:
return (0, inferenciador_1.inferirTipoVariavel)(valorTipoDe);
}
}
/**
* Executa o último escopo empilhado no topo na pilha de escopos do interpretador.
* Esse método pega exceções, mas apenas as devolve.
*
* O tratamento das exceções é feito de acordo com o bloco chamador.
* Por exemplo, em `tente ... pegue ... finalmente`, a exceção é capturada e tratada.
* Em outros blocos, pode ser desejável ter o erro em tela.
* @param manterAmbiente Se verdadeiro, ambiente do topo da pilha de escopo é copiado para o ambiente imediatamente abaixo.
* @returns O resultado da execução do escopo, se houver.
*/
async executarUltimoEscopo(manterAmbiente = false) {
const ultimoEscopo = this.pilhaEscoposExecucao.topoDaPilha();
let retornoExecucao;
try {
for (; !(retornoExecucao instanceof quebras_1.Quebra) &&
ultimoEscopo.declaracaoAtual < ultimoEscopo.declaracoes.length; ultimoEscopo.declaracaoAtual++) {
const declaracaoAtual = ultimoEscopo.declaracoes[ultimoEscopo.declaracaoAtual];
this.linhaDeclaracaoAtual = declaracaoAtual.linha;
this.hashArquivoDeclaracaoAtual = declaracaoAtual.hashArquivo;
retornoExecucao = await this.executar(declaracaoAtual);
}
return retornoExecucao;
}
catch (erro) {
const declaracaoAtual = ultimoEscopo.declaracoes[ultimoEscopo.declaracaoAtual];
if (!this.emDeclaracaoTente) {
this.erros.push({
erroInterno: erro,
linha: declaracaoAtual.linha,
hashArquivo: declaracaoAtual.hashArquivo,
});
}
else {
return Promise.reject(erro);
}
}
finally {
const escopoFinalizado = this.pilhaEscoposExecucao.removerUltimo();
const escopoAnterior = this.pilhaEscoposExecucao.topoDaPilha();
this.montao.excluirReferencias(...escopoFinalizado.espacoMemoria.enderecosMontao);
if (manterAmbiente || (retornoExecucao && retornoExecucao.preservarEscopo === true)) {
escopoAnterior.espacoMemoria.valores = Object.assign(escopoAnterior.espacoMemoria.valores, ultimoEscopo.espacoMemoria.valores);
}
}
}
/**
* Método que efetivamente inicia o processo de interpretação.
* @param declaracoes Um vetor de declarações gerado pelo Avaliador Sintático.
* @param manterAmbiente Se ambiente de execução (variáveis, classes, etc.) deve ser mantido. Normalmente usado
* pelo modo REPL (LAIR).
* @returns Um objeto com o resultado da interpretação.
*/
async interpretar(declaracoes, manterAmbiente) {
this.montao = new montao_1.Montao();
return super.interpretar(declaracoes, manterAmbiente);
}
}
exports.Interpretador = Interpretador;
//# sourceMappingURL=interpretador.js.map