UNPKG

@designliquido/delegua

Version:

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

975 lines 66.3 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 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 declaracoes_1 = require("../declaracoes"); const quebras_1 = require("../quebras"); const montao_1 = require("./montao"); const comum_1 = require("./comum"); 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 primitivas_tupla_1 = __importDefault(require("../bibliotecas/dialetos/pitugues/primitivas-tupla")); const primitivos_1 = __importDefault(require("../tipos-de-dados/primitivos")); const delegua_1 = __importDefault(require("../tipos-de-dados/delegua")); const delegua_2 = __importDefault(require("../tipos-de-simbolos/delegua")); /** * O interpretador de Delégua, usado também por Pituguês usando herança. * @see InterpretadorPitugues */ 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(); this.pontoInicializacaoBibliotecasGlobais(); } /** * Cada dialeto que deriva deste interpretador conhece este ponto de inicialização. * A partir daqui, cada dialeto pode carregar as bibliotecas globais específicas do seu dialeto. */ pontoInicializacaoBibliotecasGlobais() { (0, comum_1.carregarBibliotecasGlobais)(this.pilhaEscoposExecucao); } async avaliarArgumentosEscreva(argumentos) { if (this.constructor !== Interpretador) { return await super.avaliarArgumentosEscreva(argumentos); } let formatoTexto = ''; for (const argumento of argumentos) { let resultadoAvaliacao = await this.avaliar(argumento); if (resultadoAvaliacao && resultadoAvaliacao.hasOwnProperty && resultadoAvaliacao.hasOwnProperty('valorRetornado')) { resultadoAvaliacao = resultadoAvaliacao.valorRetornado; } if (argumento instanceof construtos_1.Atribuir || argumento instanceof construtos_1.AtribuicaoPorIndice) { formatoTexto += `${argumento.paraTexto()} `; continue; } const valor = this.resolverValor(resultadoAvaliacao); formatoTexto += `${this.paraTexto(valor)} `; } return formatoTexto.trimEnd(); } resolverReferenciaMontao(referenciaMontao) { const valorMontao = this.montao.obterReferencia(this.hashArquivoDeclaracaoAtual, this.linhaDeclaracaoAtual, referenciaMontao.endereco); return valorMontao; } resolverValor(objeto, referencia = false) { if (objeto === null || objeto === undefined) { return objeto; } if (Array.isArray(objeto)) { return objeto; } if (objeto instanceof estruturas_1.ReferenciaMontao) { return this.resolverReferenciaMontao(objeto); } if (objeto instanceof quebras_1.RetornoQuebra) { return this.resolverValor(objeto.valor); } if (objeto.hasOwnProperty) { if (objeto.hasOwnProperty('valorRetornado')) { return this.resolverValor(objeto.valorRetornado); } 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; } serializarSemEspacos(objeto) { return JSON.stringify(objeto).replace(/,\s+/g, ',').replace(/:\s+/g, ':'); } 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 instanceof estruturas_1.ReferenciaMontao) { objeto = this.resolverReferenciaMontao(objeto); } if (objeto.valor instanceof estruturas_1.ObjetoPadrao) return objeto.valor.paraTexto(); if (objeto instanceof construtos_1.Literal || objeto instanceof construtos_1.Tupla) return objeto.paraTextoSaida(); if (objeto instanceof estruturas_1.ObjetoDeleguaClasse || objeto instanceof estruturas_1.DeleguaFuncao || objeto instanceof estruturas_1.DescritorTipoClasse) return objeto.paraTexto(); if (objeto instanceof quebras_1.RetornoQuebra) { if (typeof objeto.valor === 'boolean') return objeto.valor ? 'verdadeiro' : 'falso'; } if (objeto instanceof estruturas_1.MetodoPrimitiva) { return objeto.paraTexto(); } 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) { // Resolve referências ao montão antes de processar if (elemento instanceof estruturas_1.ReferenciaMontao) { elemento = this.resolverValor(elemento); } if (elemento instanceof construtos_1.Tupla) { retornoVetor += elemento.paraTextoSaida() + ', '; continue; } // Se o elemento é um array (incluindo arrays resolvidos de referências), // chama paraTexto recursivamente para processá-lo corretamente if (Array.isArray(elemento)) { retornoVetor += this.paraTexto(elemento) + ', '; continue; } if (typeof elemento === 'object') { retornoVetor += `${this.serializarSemEspacos(elemento)}, `; continue; } retornoVetor += typeof elemento === 'string' ? `'${elemento}', ` : `${this.paraTexto(elemento)}, `; } if (retornoVetor.length > 1) { retornoVetor = retornoVetor.slice(0, -2); } retornoVetor += ']'; return retornoVetor; } 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 === null || valor === void 0 ? void 0 : valor.hasOwnProperty) && (valor === null || valor === void 0 ? void 0 : valor.hasOwnProperty('tipo')))) { valor = this.resolverValor(valor); } objetoEscrita[propriedade] = valor; } return JSON.stringify(objetoEscrita); } switch (objeto.constructor) { case Object: if ('tipo' in objeto) { switch (objeto.tipo) { case 'dicionário': return JSON.stringify(objeto.valor); default: return objeto.valor; } } } 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; } /** * Declaração de ajuda. * Neste interpretador básico, a ajuda apenas retorna texto sobre um determinado * tópico, ou ainda sobre a ajuda em si. * Outros ambientes implementam mecanismos mais sofisticados, como o modo de ajuda. * @param declaracao A declaração de ajuda. */ visitarDeclaracaoInterface(_declaracao) { // Interfaces não possuem comportamento em tempo de execução. return Promise.resolve(); } async visitarDeclaracaoAjuda(declaracao) { return Promise.resolve((0, comum_1.pontoEntradaAjuda)(declaracao.funcao, declaracao.elemento)); } async visitarDeclaracaoDefinicaoFuncao(declaracao) { var _a, _b; let funcao = new estruturas_1.DeleguaFuncao(declaracao.simbolo.lexema, declaracao.funcao); funcao.documentacao = declaracao.documentacao; if (declaracao.decoradores && declaracao.decoradores.length > 0) { for (const decorador of [...declaracao.decoradores].reverse()) { const nomeDecorador = decorador.nome.slice(1); const variavelDecoradora = this.pilhaEscoposExecucao.obterVariavelPorNome(nomeDecorador); const funcaoDecoradora = variavelDecoradora.valor; const resultado = await funcaoDecoradora.chamar(this, [ { nome: null, valor: funcao }, ]); funcao = this.resolverValorRecursivo(resultado); } } // TODO: Depreciar essa abordagem a favor do uso por referências? this.pilhaEscoposExecucao.definirVariavel(declaracao.simbolo.lexema, funcao); this.pilhaEscoposExecucao.registrarReferenciaFuncao(declaracao.id, funcao); return Promise.resolve({ tipo: `função<${((_a = funcao.declaracao) === null || _a === void 0 ? void 0 : _a.tipo) || 'qualquer'}>`, tipoExplicito: (_b = funcao.declaracao) === null || _b === void 0 ? void 0 : _b.tipoExplicito, declaracao: funcao, }); } async logicaComumExecucaoEnquanto(enquanto, acumularRetornos) { let retornoExecucao; const retornos = []; while ((acumularRetornos || !(retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.Quebra)) && this.eVerdadeiro(await this.avaliar(enquanto.condicao))) { try { retornoExecucao = await this.executar(enquanto.corpo); if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.SustarQuebra) { if (acumularRetornos) { return { valorRetornado: retornos, tipo: 'vetor', }; } return null; } if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.ContinuarQuebra) { retornoExecucao = null; } if (acumularRetornos) { retornos.push(this.resolverValor(retornoExecucao)); } } catch (erro) { this.erros.push({ erroInterno: erro, linha: enquanto.linha, hashArquivo: enquanto.hashArquivo, }); return Promise.reject(erro); } } if (acumularRetornos) { return { valorRetornado: retornos, tipo: 'vetor', }; } return retornoExecucao; } async visitarDeclaracaoEnquanto(declaracao) { return this.logicaComumExecucaoEnquanto(declaracao, false); } async logicaComumExecucaoFazer(fazer, acumularRetornos) { let retornoExecucao; const retornos = []; do { try { retornoExecucao = await this.executar(fazer.caminhoFazer); if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.SustarQuebra) { if (acumularRetornos) { return { valorRetornado: retornos, tipo: 'vetor', }; } return null; } if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.ContinuarQuebra) { retornoExecucao = null; } if (acumularRetornos) { retornos.push(this.resolverValor(retornoExecucao)); } } catch (erro) { this.erros.push({ erroInterno: erro, linha: fazer.linha, hashArquivo: fazer.hashArquivo, }); return Promise.reject(erro); } } while ((acumularRetornos || !(retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.Quebra)) && this.eVerdadeiro(await this.avaliar(fazer.condicaoEnquanto))); if (acumularRetornos) { return { valorRetornado: retornos, tipo: 'vetor', }; } } async visitarDeclaracaoFazer(declaracao) { return this.logicaComumExecucaoFazer(declaracao, false); } async logicaComumExecucaoPara(para, acumularRetornos) { const declaracaoInicializador = Array.isArray(para.inicializador) ? para.inicializador[0] : para.inicializador; if (declaracaoInicializador !== null) { await this.avaliar(declaracaoInicializador); } let retornoExecucao; const retornos = []; while (acumularRetornos || !(retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.Quebra)) { if (para.condicao !== null && !this.eVerdadeiro(await this.avaliar(para.condicao))) { break; } retornoExecucao = await this.executar(para.corpo); if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.SustarQuebra) { if (acumularRetornos) { return { valorRetornado: retornos, tipo: 'vetor', }; } return null; } if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.ContinuarQuebra) { retornoExecucao = null; } if (acumularRetornos) { retornos.push(this.resolverValor(retornoExecucao)); } if (para.incrementar !== null) { await this.avaliar(para.incrementar); } } if (acumularRetornos) { return { valorRetornado: retornos, tipo: 'vetor', }; } return retornoExecucao; } async visitarDeclaracaoPara(declaracao) { return this.logicaComumExecucaoPara(declaracao, false); } async logicaComumExecucaoParaCada(paraCada, acumularRetornos) { let retornoExecucao; // Posição atual precisa ser reiniciada, pois pode estar dentro de outro // laço de repetição. paraCada.posicaoAtual = 0; const vetorOuDicionarioResolvido = await this.avaliar(paraCada.vetorOuDicionario); let valorVetorOuDicionarioResolvido = this.resolverValor(vetorOuDicionarioResolvido); // Se até aqui vetor resolvido é um dicionário, converte dicionário // para vetor de duplas. if (paraCada.vetorOuDicionario.tipo === 'dicionário') { valorVetorOuDicionarioResolvido = Object.entries(valorVetorOuDicionarioResolvido).map((v) => new construtos_1.Dupla(new construtos_1.Literal(paraCada.hashArquivo, paraCada.linha, v[0], 'texto'), new construtos_1.Literal(paraCada.hashArquivo, paraCada.linha, v[1], (0, inferenciador_1.inferirTipoVariavel)(v[1])))); } if (paraCada.vetorOuDicionario.tipo === 'texto' || typeof valorVetorOuDicionarioResolvido === 'string') { valorVetorOuDicionarioResolvido = valorVetorOuDicionarioResolvido.split(''); } if (!Array.isArray(valorVetorOuDicionarioResolvido)) { return Promise.reject("Variável ou literal provida em instrução 'para cada' não é um vetor."); } const retornos = []; while ((acumularRetornos || !(retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.Quebra)) && paraCada.posicaoAtual < valorVetorOuDicionarioResolvido.length) { try { if (paraCada.variavelIteracao instanceof construtos_1.Variavel) { this.pilhaEscoposExecucao.definirVariavel(paraCada.variavelIteracao.simbolo.lexema, valorVetorOuDicionarioResolvido[paraCada.posicaoAtual]); } if (paraCada.variavelIteracao instanceof construtos_1.Dupla) { const valorComoDupla = valorVetorOuDicionarioResolvido[paraCada.posicaoAtual]; const nomesVariaveis = await Promise.all([ this.avaliar(paraCada.variavelIteracao.primeiro), this.avaliar(paraCada.variavelIteracao.segundo), ]); const valoresDupla = await Promise.all([ this.avaliar(valorComoDupla.primeiro), this.avaliar(valorComoDupla.segundo), ]); // nomesVariaveis são strings (nomes das variáveis) this.pilhaEscoposExecucao.definirVariavel(String(nomesVariaveis[0]), valoresDupla[0]); this.pilhaEscoposExecucao.definirVariavel(String(nomesVariaveis[1]), valoresDupla[1]); } retornoExecucao = await this.executar(paraCada.corpo); if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.SustarQuebra) { if (acumularRetornos) { return { valorRetornado: retornos, tipo: 'vetor', }; } return null; } if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.ContinuarQuebra) { retornoExecucao = null; } if (acumularRetornos) { retornos.push(this.resolverValor(retornoExecucao)); } paraCada.posicaoAtual++; } catch (erro) { this.erros.push({ erroInterno: erro, linha: paraCada.linha, hashArquivo: paraCada.hashArquivo, }); return Promise.reject(erro); } } if (acumularRetornos) { return { valorRetornado: retornos, tipo: 'vetor', }; } return retornoExecucao; } async visitarDeclaracaoParaCada(declaracao) { return this.logicaComumExecucaoParaCada(declaracao, false); } async visitarDeclaracaoTendoComo(declaracao) { const retornoInicializacao = await this.avaliar(declaracao.inicializacaoVariavel); const retornoInicializacaoResolvido = this.resolverValor(retornoInicializacao); this.pilhaEscoposExecucao.definirConstante(declaracao.simboloVariavel.lexema, retornoInicializacaoResolvido); await this.executar(declaracao.corpo); if (retornoInicializacaoResolvido instanceof estruturas_1.ObjetoDeleguaClasse) { const metodoFinalizar = retornoInicializacaoResolvido.classe.encontrarMetodo('finalizar'); if (metodoFinalizar) { const chamavel = metodoFinalizar.funcaoPorMetodoDeClasse(retornoInicializacaoResolvido); chamavel.chamar(this, []); } } return null; } 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) { valorIndice += objeto.length; } if (valorIndice >= objeto.length || valorIndice < 0) { 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.TuplaN || objeto.constructor.name === 'TuplaN') { if (!Number.isInteger(valorIndice)) { return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Somente inteiros podem ser usados para indexar uma tupla.', expressao.linha)); } if (valorIndice < 0 && objeto.elementos.length !== 0) { valorIndice += objeto.elementos.length; } if (valorIndice >= objeto.elementos.length || valorIndice < 0) { return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Índice da tupla fora de intervalo.', expressao.linha)); } const elemento = objeto.elementos[valorIndice]; if (elemento && elemento.constructor && elemento.constructor.name === 'Literal') { return elemento.valor; } return elemento; } 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) { valorIndice += objeto.length; } if (valorIndice >= objeto.length || valorIndice < 0) { 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) { const nomeObjeto = this.resolverNomeObjectoAcessado(expressao.objeto); 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 === quebras_1.RetornoQuebra) { variavelObjeto = variavelObjeto.valor; } const objeto = this.resolverValor(variavelObjeto); if (objeto.constructor && objeto.constructor === estruturas_1.ObjetoDeleguaClasse) { try { return objeto.obterMetodo(expressao.nomeMetodo); } catch (e) { const nomeClasse = objeto.classe.simboloOriginal.lexema; const funcaoExt = this.encontrarMetodoExtensao([nomeClasse, 'objeto'], expressao.nomeMetodo, this.hashArquivoDeclaracaoAtual); if (funcaoExt) return funcaoExt.funcaoPorExtensao(objeto); throw e; } } if (objeto instanceof construtos_1.TuplaN || objeto.constructor.name === 'TuplaN') { const metodoDePrimitivaTupla = primitivas_tupla_1.default[expressao.nomeMetodo]; if (metodoDePrimitivaTupla) { return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaTupla.implementacao, expressao.nomeMetodo, 'tupla'); } } // 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(nomeObjeto, objeto, metodoDePrimitivaDicionario, expressao.nomeMetodo, 'dicionário'); } 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]; } // Caso 3: Constante de tipo em FuncaoPadrao (ex: inteiro.MAXIMO, longo.MINIMO). if (objeto instanceof estruturas_1.FuncaoPadrao) { const constante = objeto[expressao.nomeMetodo]; if (constante !== undefined) return constante; } // 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)(objeto); } // 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: if (expressao.nomeMetodo in primitivas_numero_1.default) { const metodoDePrimitivaNumero = primitivas_numero_1.default[expressao.nomeMetodo].implementacao; if (metodoDePrimitivaNumero) { return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaNumero, expressao.nomeMetodo, tipoObjeto); } } else { const funcaoExt = this.encontrarMetodoExtensao(['número', 'objeto'], expressao.nomeMetodo, this.hashArquivoDeclaracaoAtual); if (funcaoExt) return funcaoExt.funcaoPorExtensao(objeto); } break; case delegua_1.default.TEXTO: if (expressao.nomeMetodo in primitivas_texto_1.default) { const metodoDePrimitivaTexto = primitivas_texto_1.default[expressao.nomeMetodo].implementacao; if (metodoDePrimitivaTexto) { return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaTexto, expressao.nomeMetodo, 'texto'); } } else { const funcaoExt = this.encontrarMetodoExtensao(['texto', 'objeto'], expressao.nomeMetodo, this.hashArquivoDeclaracaoAtual); if (funcaoExt) return funcaoExt.funcaoPorExtensao(objeto); } 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: if (expressao.nomeMetodo in primitivas_vetor_1.default) { const metodoDePrimitivaVetor = primitivas_vetor_1.default[expressao.nomeMetodo].implementacao; if (metodoDePrimitivaVetor) { return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaVetor, expressao.nomeMetodo, tipoObjeto); } } else { const funcaoExt = this.encontrarMetodoExtensao(['vetor', 'objeto'], expressao.nomeMetodo, this.hashArquivoDeclaracaoAtual); if (funcaoExt) return funcaoExt.funcaoPorExtensao(objeto); } 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) { const nomeObjeto = this.resolverNomeObjectoAcessado(expressao.objeto); 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 === quebras_1.RetornoQuebra) { const retornoQuebra = variavelObjeto; variavelObjeto = retornoQuebra.valor; } const objeto = this.resolverValor(variavelObjeto, true); let descritorTipoClasse = null; if (objeto instanceof estruturas_1.DescritorTipoClasse) { descritorTipoClasse = objeto; } else if (expressao.objeto instanceof construtos_1.Variavel) { try { const variavelClasse = this.procurarVariavel(expressao.objeto.simbolo); const valorClasse = this.resolverValor(variavelClasse, true); if (valorClasse instanceof estruturas_1.DescritorTipoClasse) { descritorTipoClasse = valorClasse; } } catch (_a) { // Ignora e continua fluxo padrão de resolução abaixo. } } if (descritorTipoClasse) { return await descritorTipoClasse.obterEstatico(expressao.simbolo.lexema, this); } if (objeto.constructor === estruturas_1.ObjetoDeleguaClasse) { try { return await objeto.obter(expressao.simbolo, this); } catch (_b) { const nomeClasse = objeto.classe.simboloOriginal.lexema; const funcaoExt = this.encontrarMetodoExtensao([nomeClasse, 'objeto'], expressao.simbolo.lexema, this.hashArquivoDeclaracaoAtual); if (funcaoExt) return funcaoExt.funcaoPorExtensao(objeto); throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Método ou propriedade '${expressao.simbolo.lexema}' não encontrado em '${nomeClasse}'.`, expressao.linha); } } if (objeto instanceof construtos_1.TuplaN || objeto.constructor.name === 'TuplaN') { const metodoDePrimitivaTupla = primitivas_tupla_1.default[expressao.simbolo.lexema]; if (metodoDePrimitivaTupla) { return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaTupla.implementacao, expressao.simbolo.lexema, 'tupla'); } } // 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(nomeObjeto, objeto, metodoDePrimitivaDicionario, expressao.simbolo.lexema, 'dicionário'); } return objeto[expressao.simbolo.lexema]; } // String do JavaScript, ou seja, primitiva de texto. if (objeto.constructor === String) { if (!(expressao.simbolo.lexema in primitivas_texto_1.default)) { const funcaoExt = this.encontrarMetodoExtensao(['texto', 'objeto'], expressao.simbolo.lexema, this.hashArquivoDeclaracaoAtual); if (funcaoExt) return funcaoExt.funcaoPorExtensao(objeto); throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Método de primitiva '${expressao.simbolo.lexema}' não existe para o tipo texto.`); } const metodoDePrimitivaTexto = primitivas_texto_1.default[expressao.simbolo.lexema].implementacao; return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaTexto, expressao.simbolo.lexema, 'texto'); } // 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)(objeto); } // 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: { if (!(expressao.simbolo.lexema in primitivas_numero_1.default)) { const funcaoExt = this.encontrarMetodoExtensao(['número', 'objeto'], expressao.simbolo.lexema, this.hashArquivoDeclaracaoAtual); if (funcaoExt) return funcaoExt.funcaoPorExtensao(objeto); throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Método de primitiva '${expressao.simbolo.lexema}' não existe para o tipo ${tipoObjeto}.`); } const metodoDePrimitivaNumero = primitivas_numero_1.default[expressao.simbolo.lexema].implementacao; if (metodoDePrimitivaNumero) { return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaNumero, expressao.simbolo.lexema, 'número'); } break; } case delegua_1.default.TEXTO: { if (!(expressao.simbolo.lexema in primitivas_texto_1.default)) { const funcaoExt = this.encontrarMetodoExtensao(['texto', 'objeto'], expressao.simbolo.lexema, this.hashArquivoDeclaracaoAtual); if (funcaoExt) return funcaoExt.funcaoPorExtensao(objeto); throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Método de primitiva '${expressao.simbolo.lexema}' não existe para o tipo ${tipoObjeto}.`); } const metodoDePrimitivaTexto = primitivas_texto_1.default[expressao.simbolo.lexema].implementacao; if (metodoDePrimitivaTexto) { return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaTexto, expressao.simbolo.lexema, 'texto'); } 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: { if (!(expressao.simbolo.lexema in primitivas_vetor_1.default)) { const funcaoExt = this.encontrarMetodoExtensao(['vetor', 'objeto'], expressao.simbolo.lexema, this.hashArquivoDeclaracaoAtual); if (funcaoExt) return funcaoExt.funcaoPorExtensao(objeto); throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Método de primitiva '${expressao.simbolo.lexema}' não existe para o tipo ${tipoObjeto}.`); } const metodoDePrimitivaVetor = primitivas_vetor_1.default[expressao.simbolo.lexema].implementacao; if (metodoDePrimitivaVetor) { return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaVetor, expressao.simbolo.lexema, tipoObjeto); } break; } } // Objeto de uma classe JavaScript regular (ou seja, com construtor e propriedades) // que possua a propriedade. // Exemplos: classes de LinConEs, como `RetornoComando`, ou bibliotecas globais com objetos próprios. if (objeto.hasOwnProperty && objeto.hasOwnProperty(expressao.simbolo.lexema)) { return objeto[expressao.simbolo.lexema]; } // Último caso: objeto simples, sem construtor, sem protótipo. Exemplo: {'a': 1, 'b': 2} if (typeof objeto[expressao.simbolo.lexema] !== 'undefined') { return objeto[expressao.simbolo.lexema]; } // Último recurso: extensões em 'objeto' (base universal). const funcaoExtObjeto = this.encontrarMetodoExtensao(['objeto'], expressao.simbolo.lexema, this.hashArquivoDeclaracaoAtual); if (funcaoExtObjeto) return funcaoExtObjeto.funcaoPorExtensao(objeto); 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) { const nomeObjeto = this.resolverNomeObjectoAcessado(expressao.objeto); 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 === quebras_1.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.constructor === estruturas_1.ObjetoDeleguaClasse) { return objeto.obterMetodo(expressao.nomePropriedade) || null; } if (objeto instanceof construtos_1.TuplaN || objeto.constructor.name === 'TuplaN') { const metodoPrimitivaTupla = primitivas_tupla_1.default[expressao.nomePropriedade]; if (metodoPrimitivaTupla) { return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoPrimitivaTupla.implementacao, expressao.nomePropriedade, 'tupla'); } } // 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(nomeObjeto, objeto, metodoDePrimitivaDicionario, expressao.nomePropriedade, 'dicionário'); } 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)(objeto); } return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `Propriedade para objeto ou primitiva não encontrado: ${expressao.nomePropriedade}.`, expressao.linha)); } async visitarExpressaoAjuda(expressao) { if (expressao.funcao && expressao.valor && !(expressao.valor instanceof construtos_1.Leia)) { try { const resultado = await this.avaliar(expressao.valor); const valorAvaliado = this.resolverValor(resultado); if (valorAvaliado instanceof estruturas_1.DeleguaFuncao || valorAvaliado instanceof estruturas_1.ObjetoDeleguaClasse || valorAvaliado instanceof estruturas_1.DescritorTipoClasse) { return (0, comum_1.pontoEntradaAjuda)(expressao.funcao, valorAvaliado); } } catch (_a) { // Se a avaliação falhar, usa o comportamento padrão } } return (0, comum_1.pontoEntradaAjuda)(expressao.funcao, expressao.valor); } 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, '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; // this.pilhaEscoposExecucao.atribuirVariavel((expressao.objeto as any).simbolo, objeto); } else if ((objeto && 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)); } } /** * Em Delégua e Pituguês, comentários não são importantes para a interpretação. * @param expressao Uma `Promise` sempre resolvida. */ async visitarExpressaoComentario(expressao) { return Promise.resolve(); } /** * Execução de uma expressão de atribuição. * @param expressao A expressão. * @returns O valor atribuído. */ async visitarExpressaoDeAtribuicao(expressao) { let valor = await this.avaliar(expressao.valor); if (valor && valor.hasOwnProperty('valorRetornado')) { valor = valor.valorRetornado; } const valorResolvido = this.resolverValor(valor); let indice = null; if (expressao.indice) { indice = this.resolverValor(await this.avaliar(expressao.indice)); } switch (expressao.alvo.constructor) { case construtos_1.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 construtos_1.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 === estruturas_1.ObjetoDeleguaClasse) { const objetoDeleguaClasse = objeto; await objetoDeleguaClasse.definir(alvoPropriedade.simbolo, valor, this); } 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: ${JSON.stringify(expressao)}.`); } return valorResolvido; } async visitarExpressaoDefinirVa