UNPKG

@designliquido/delegua

Version:

Linguagem de programação simples e moderna usando português estruturado.

280 lines 15.1 kB
"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 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 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 { 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 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 = variavelObjeto.hasOwnProperty('valor') ? variavelObjeto.valor : 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(null, `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 = variavelObjeto.hasOwnProperty('valor') ? variavelObjeto.valor : 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] || 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`. if (objeto.hasOwnProperty(expressao.simbolo.lexema)) { 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 = variavelObjeto.hasOwnProperty('valor') ? variavelObjeto.valor : 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 visitarExpressaoReferenciaFuncao(expressao) { const deleguaFuncao = this.pilhaEscoposExecucao.obterReferenciaFuncao(expressao.idFuncao); return deleguaFuncao; } async visitarExpressaoRetornar(declaracao) { let valor = null; if (declaracao.valor !== null) { 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.constructor.name === '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); 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); } } } exports.Interpretador = Interpretador; //# sourceMappingURL=interpretador.js.map