@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
975 lines • 66.3 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 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