@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
1,001 lines • 79.9 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 modulo_testes_1 = require("../bibliotecas/testes/modulo-testes");
const registro_testes_1 = require("../bibliotecas/testes/registro-testes");
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 = undefined, funcaoDeRetornoMesmaLinha = undefined) {
super(diretorioBase, performance, funcaoDeRetorno, funcaoDeRetornoMesmaLinha);
this.acumularRetornos = false;
this.registroTestes = new registro_testes_1.RegistroTestes();
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.replace(/\s+$/, '');
}
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', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
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?.hasOwnProperty && 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 visitarDeclaracaoImportar(declaracao) {
const resultadoCaminho = await this.avaliar(declaracao.caminho);
const caminho = this.resolverValor(resultadoCaminho);
if (caminho === 'testes') {
this.registroTestes = new registro_testes_1.RegistroTestes();
const modulo = (0, modulo_testes_1.construirModuloDeTestes)(this, this.registroTestes);
if (declaracao.simboloTudo !== null) {
this.pilhaEscoposExecucao.definirVariavel(declaracao.simboloTudo.lexema, modulo);
}
else {
for (const elemento of declaracao.elementosImportacao) {
const componente = modulo.componentes[elemento.lexema];
if (componente !== undefined) {
this.pilhaEscoposExecucao.definirVariavel(elemento.lexema, componente);
}
}
}
return modulo;
}
return Promise.reject('Importação de arquivos não suportada neste interpretador. Use delegua-node para importações de arquivos.');
}
async visitarDeclaracaoAjuda(declaracao) {
if (declaracao.funcao && declaracao.elemento) {
try {
const resultado = await this.avaliar(declaracao.elemento);
const valorAvaliado = this.resolverValor(resultado);
if (typeof valorAvaliado === 'string' ||
valorAvaliado instanceof estruturas_1.DeleguaFuncao ||
valorAvaliado instanceof estruturas_1.ObjetoDeleguaClasse ||
valorAvaliado instanceof estruturas_1.DescritorTipoClasse) {
return Promise.resolve((0, comum_1.pontoEntradaAjuda)(declaracao.funcao, valorAvaliado));
}
}
catch {
// Se a avaliação falhar, usa o comportamento padrão
}
}
return Promise.resolve((0, comum_1.pontoEntradaAjuda)(declaracao.funcao, declaracao.elemento));
}
async visitarDeclaracaoDefinicaoFuncao(declaracao) {
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: '', 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<${funcao.declaracao?.tipo || 'qualquer'}>`,
tipoExplicito: funcao.declaracao?.tipoExplicito,
declaracao: funcao,
});
}
async logicaComumExecucaoEnquanto(enquanto, acumularRetornos) {
const retornos = [];
let retornoExecucao = undefined;
let iteracoes = 0;
while ((acumularRetornos ||
!(retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.Quebra)) &&
this.eVerdadeiro(await this.avaliar(enquanto.condicao))) {
try {
if (this.funcaoVerificarIteracao) {
await this.funcaoVerificarIteracao();
}
await this.cederControle(++iteracoes);
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 = undefined;
}
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) {
const retornos = [];
let retornoExecucao = undefined;
let iteracoes = 0;
do {
try {
if (this.funcaoVerificarIteracao) {
await this.funcaoVerificarIteracao();
}
await this.cederControle(++iteracoes);
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 = undefined;
}
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 && declaracaoInicializador !== undefined) {
await this.avaliar(declaracaoInicializador);
}
const retornos = [];
let retornoExecucao = undefined;
let iteracoes = 0;
while (acumularRetornos ||
!(retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.Quebra)) {
if (para.condicao !== null && !this.eVerdadeiro(await this.avaliar(para.condicao))) {
break;
}
if (this.funcaoVerificarIteracao) {
await this.funcaoVerificarIteracao();
}
await this.cederControle(++iteracoes);
retornoExecucao = await this.executar(para.corpo);
if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.SustarQuebra) {
if (acumularRetornos) {
return {
valorRetornado: retornos,
tipo: 'vetor',
};
}
return undefined;
}
if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.ContinuarQuebra) {
retornoExecucao = undefined;
}
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);
}
/**
* Define a variável de iteração de um laço 'para cada' (ou compreensão de lista) SEM coerção de tipo. A iteração sempre preserva o valor original do elemento, independentemente do tipo inferido para a variável.
*/
definirVariavelIteracao(variavelIteracao, valorElemento) {
if (variavelIteracao instanceof construtos_1.Variavel) {
this.pilhaEscoposExecucao.definirVariavel(variavelIteracao.simbolo.lexema, valorElemento, 'qualquer');
}
else if (variavelIteracao instanceof construtos_1.Dupla) {
const nomePrimeiro = variavelIteracao.primeiro?.valor?.toString?.() ?? variavelIteracao.primeiro?.lexema;
const nomeSegundo = variavelIteracao.segundo?.valor?.toString?.() ?? variavelIteracao.segundo?.lexema;
if (nomePrimeiro) {
this.pilhaEscoposExecucao.definirVariavel(nomePrimeiro, valorElemento.primeiro?.valor ?? valorElemento.primeiro, 'qualquer');
}
if (nomeSegundo) {
this.pilhaEscoposExecucao.definirVariavel(nomeSegundo, valorElemento.segundo?.valor ?? valorElemento.segundo, 'qualquer');
}
}
}
async logicaComumExecucaoParaCada(paraCada, acumularRetornos) {
let retornoExecucao = undefined;
// 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 = [];
let iteracoes = 0;
while ((acumularRetornos ||
!(retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.Quebra)) &&
paraCada.posicaoAtual < valorVetorOuDicionarioResolvido.length) {
try {
if (this.funcaoVerificarIteracao) {
await this.funcaoVerificarIteracao();
}
await this.cederControle(++iteracoes);
this.definirVariavelIteracao(paraCada.variavelIteracao, valorVetorOuDicionarioResolvido[paraCada.posicaoAtual]);
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 = undefined;
}
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));
}
logicaPropriedadesEMetodosDeClasse(declaracao, superClassesResolvidas, mesclaResolvidas) {
const metodos = {};
const metodosEstaticos = {};
const obtenedores = {};
const definidores = {};
const obtenedoresEstaticos = {};
const definidoresEstaticos = {};
const metodosAbstratos = [];
const acessoMetodos = {};
const acessoPropriedades = {};
for (const metodoAtual of declaracao.metodos) {
const nomeMetodo = metodoAtual.simbolo.lexema;
if (metodoAtual.acesso && metodoAtual.acesso !== 'publico') {
acessoMetodos[nomeMetodo] = metodoAtual.acesso;
}
if (metodoAtual.abstrato) {
metodosAbstratos.push(nomeMetodo);
continue;
}
const eInicializador = nomeMetodo === 'construtor';
const funcao = new estruturas_1.DeleguaFuncao(nomeMetodo, metodoAtual.funcao, undefined, eInicializador);
funcao.documentacao = metodoAtual.documentacao;
// Numa classe estática, todos os métodos (exceto construtor) são estáticos.
const ehEstatico = declaracao.classeEstatica ? !eInicializador : metodoAtual.estatico;
if (metodoAtual.eObtenedor) {
if (ehEstatico) {
obtenedoresEstaticos[nomeMetodo] = funcao;
}
else {
obtenedores[nomeMetodo] = funcao;
}
continue;
}
if (metodoAtual.eDefinidor) {
if (ehEstatico) {
definidoresEstaticos[nomeMetodo] = funcao;
}
else {
definidores[nomeMetodo] = funcao;
}
continue;
}
const destino = ehEstatico && !eInicializador ? metodosEstaticos : metodos;
if (destino[nomeMetodo]) {
if (!Array.isArray(destino[nomeMetodo])) {
destino[nomeMetodo] = [destino[nomeMetodo]];
}
destino[nomeMetodo].push(funcao);
}
else {
destino[nomeMetodo] = funcao;
}
}
// Registrar propriedades estáticas no mapa de membros estáticos e níveis de acesso.
// Numa classe estática, todas as propriedades são tratadas como estáticas.
const membrosEstaticos = {};
for (const prop of declaracao.propriedades) {
if (prop.estatico || declaracao.classeEstatica) {
membrosEstaticos[prop.nome.lexema] = undefined;
}
if (prop.acesso && prop.acesso !== 'publico') {
acessoPropriedades[prop.nome.lexema] = prop.acesso;
}
}
const descritorTipoClasse = new estruturas_1.DescritorTipoClasse(declaracao.simbolo, superClassesResolvidas, metodos, declaracao.propriedades);
descritorTipoClasse.metodosEstaticos = metodosEstaticos;
descritorTipoClasse.membrosEstaticos = membrosEstaticos;
descritorTipoClasse.obtenedores = obtenedores;
descritorTipoClasse.definidores = definidores;
descritorTipoClasse.obtenedoresEstaticos = obtenedoresEstaticos;
descritorTipoClasse.definidoresEstaticos = definidoresEstaticos;
descritorTipoClasse.abstrata = declaracao.abstrata;
descritorTipoClasse.estrangeira = declaracao.estrangeira;
descritorTipoClasse.classeEstatica = declaracao.classeEstatica;
descritorTipoClasse.metodosAbstratos = metodosAbstratos;
descritorTipoClasse.acessoMetodos = acessoMetodos;
descritorTipoClasse.acessoPropriedades = acessoPropriedades;
// Toda classe sem superclasse explícita herda implicitamente de `Objeto`.
if (descritorTipoClasse.superClasses.length === 0 &&
estruturas_1.OBJETO_BASE &&
descritorTipoClasse !== estruturas_1.OBJETO_BASE) {
descritorTipoClasse.superClasses = [estruturas_1.OBJETO_BASE];
}
// Calcular o OReM (linearização C3) após os pais estarem definidos.
descritorTipoClasse.orem = estruturas_1.DescritorTipoClasse.computarOReM(descritorTipoClasse);
// Mesclar métodos e propriedades dos misturávels (primeiro misturável ganha se não definido na classe).
for (const misturável of mesclaResolvidas) {
for (const [nome, funcao] of Object.entries(misturável.metodos)) {
if (!descritorTipoClasse.metodos.hasOwnProperty(nome)) {
descritorTipoClasse.metodos[nome] = funcao;
}
}
for (const [nome, funcao] of Object.entries(misturável.obtenedores)) {
if (!descritorTipoClasse.obtenedores.hasOwnProperty(nome)) {
descritorTipoClasse.obtenedores[nome] = funcao;
}
}
for (const [nome, funcao] of Object.entries(misturável.definidores)) {
if (!descritorTipoClasse.definidores.hasOwnProperty(nome)) {
descritorTipoClasse.definidores[nome] = funcao;
}
}
for (const prop of misturável.propriedades) {
const jaDeclarada = descritorTipoClasse.propriedades.some((p) => p.nome.lexema === prop.nome.lexema);
if (!jaDeclarada) {
descritorTipoClasse.propriedades.push(prop);
}
}
}
// Verifica se a subclasse concreta implementa todos os métodos abstratos
// da(s) superclasse(s) abstrata(s).
if (!declaracao.abstrata && !declaracao.estrangeira) {
for (const superClasse of superClassesResolvidas) {
if (superClasse.abstrata || superClasse.estrangeira) {
superClasse.verificarImplementacaoAbstrata(descritorTipoClasse);
}
}
}
// Marcar classeDefinidora em todos os métodos para que `super()` funcione.
for (const funcaoOuSobrecargas of Object.values(descritorTipoClasse.metodos)) {
const lista = Array.isArray(funcaoOuSobrecargas)
? funcaoOuSobrecargas
: [funcaoOuSobrecargas];
for (const funcao of lista) {
funcao.classeDefinidora = descritorTipoClasse;
}
}
return descritorTipoClasse;
}
resolverMetodoDeClasse(declaracao, superClassesResolvidas, mesclaResolvidas) {
return this.logicaPropriedadesEMetodosDeClasse(declaracao, superClassesResolvidas, mesclaResolvidas);
}
/**
* Retorna um `SuperProxy` que aponta para a próxima classe no OReM após a classe
* atualmente em execução. Se não houver `classeExecutora` no escopo (chamada fora
* de método de instância), usa o primeiro pai direto como fallback.
*/
visitarExpressaoSuper(_expressao) {
const variavelIsto = this.pilhaEscoposExecucao.obterVariavelPorNome('isto');
const instancia = variavelIsto?.valor;
if (!instancia)
return null;
const variavelClasseExecutora = this.pilhaEscoposExecucao.obterVariavelPorNome('classeExecutora');
const classeExecutora = variavelClasseExecutora?.valor ?? null;
const orem = instancia.classe.orem;
if (classeExecutora) {
const posicao = orem.indexOf(classeExecutora);
if (posicao >= 0 && posicao + 1 < orem.length) {
return new estruturas_1.SuperProxy(instancia, orem[posicao + 1]);
}
}
// Fallback: primeiro pai direto.
if (instancia.classe.superClasses.length > 0) {
return new estruturas_1.SuperProxy(instancia, instancia.classe.superClasses[0]);
}
return null;
}
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 === null || objeto === undefined) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(undefined, `Não é possível acessar a propriedade '${expressao.nomeMetodo}' de um valor nulo.`, expressao.linha));
}
if (objeto.constructor && objeto.constructor === estruturas_1.ObjetoDeleguaClasse) {
try {
return objeto.obterMetodo(expressao.nomeMetodo);
}
catch (e) {
const nomeClasse = objeto.classe.simboloOriginal?.lexema || 'objeto';
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);
if (objeto === null || objeto === undefined) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(undefined, `Não é possível acessar a propriedade '${expressao.simbolo.lexema}' de um valor nulo.`, expressao.linha));
}
// Acesso a método via `super()`: percorre o OReM a partir de `proximaClasse`,
// vincula o método encontrado à instância original e registra `classeDefinidora`
// para que chamadas aninhadas a `super()` avancem corretamente na cadeia.
if (objeto instanceof estruturas_1.SuperProxy) {
const proxy = objeto;
const orem = proxy.instancia.classe.orem;
const indiceInicio = orem.indexOf(proxy.proximaClasse);
if (indiceInicio < 0) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Classe '${proxy.proximaClasse.simboloOriginal?.lexema}' não encontrada no OReM.`, expressao.linha));
}
const nomeMetodo = expressao.simbolo.lexema;
for (let i = indiceInicio; i < orem.length; i++) {
const classe = orem[i];
if (!Object.prototype.hasOwnProperty.call(classe.metodos, nomeMetodo))
continue;
const metodoRaw = classe.metodos[nomeMetodo];
const funcao = Array.isArray(metodoRaw) ? metodoRaw[0] : metodoRaw;
const funcaoVinculada = funcao.funcaoPorMetodoDeClasse(proxy.instancia);
funcaoVinculada.classeDefinidora = classe;
return funcaoVinculada;
}
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Método '${nomeMetodo}' não encontrado nas superclasses.`, expressao.linha));
}
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 {
// 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 {
const nomeClasse = objeto.classe.simboloOriginal?.lexema || 'objeto';
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