@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
793 lines • 35.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.InterpretadorEguaClassico = void 0;
const espaco_memoria_1 = require("../../espaco-memoria");
const chamavel_1 = require("../../estruturas/chamavel");
const descritor_tipo_classe_1 = require("../../estruturas/descritor-tipo-classe");
const delegua_funcao_1 = require("../../estruturas/delegua-funcao");
const funcao_padrao_1 = require("../../estruturas/funcao-padrao");
const modulo_1 = require("../../estruturas/modulo");
const objeto_delegua_classe_1 = require("../../estruturas/objeto-delegua-classe");
const excecoes_1 = require("../../../excecoes");
const quebras_1 = require("../../../quebras");
const inferenciador_1 = require("../../../inferenciador");
const pilha_escopos_execucao_1 = require("../../pilha-escopos-execucao");
const egua_classico_1 = __importDefault(require("../../../tipos-de-simbolos/egua-classico"));
const biblioteca_global_1 = __importDefault(require("../../../bibliotecas/dialetos/egua-classico/biblioteca-global"));
const resolvedor_1 = require("./resolvedor/resolvedor");
/**
* O Interpretador visita todos os elementos complexos gerados pelo analisador sintático (_Parser_)
* e de fato executa a lógica de programação descrita no código.
*/
class InterpretadorEguaClassico {
constructor(diretorioBase) {
// Esta variável indica que propriedades de classes precisam ser
// declaradas para serem válidas.
// Égua não requer isso, mas precisamos ter o valor
// aqui por questões de compatibilidade com outros elementos.
this.requerDeclaracaoPropriedades = false;
this.interfaceEntradaSaida = null;
this.resolvedor = new resolvedor_1.ResolvedorEguaClassico();
this.diretorioBase = diretorioBase;
this.funcaoDeRetorno = console.log;
this.locais = new Map();
this.erros = [];
this.pilhaEscoposExecucao = new pilha_escopos_execucao_1.PilhaEscoposExecucao();
const escopoExecucao = {
declaracoes: [],
declaracaoAtual: 0,
espacoMemoria: new espaco_memoria_1.EspacoMemoria(),
finalizado: false,
tipo: 'outro',
emLacoRepeticao: false,
};
this.pilhaEscoposExecucao.empilhar(escopoExecucao);
(0, biblioteca_global_1.default)(this, this.pilhaEscoposExecucao);
}
resolverValor(objeto) {
if (objeto === null || objeto === undefined) {
return objeto;
}
if (objeto.hasOwnProperty('valor')) {
return objeto.valor;
}
return objeto;
}
visitarExpressaoArgumentoReferenciaFuncao(expressao) {
throw new Error('Método não implementado.');
}
visitarExpressaoReferenciaFuncao(expressao) {
throw new Error('Método não implementado.');
}
visitarExpressaoAcessoMetodo(expressao) {
throw new Error('Método não implementado.');
}
visitarExpressaoAcessoPropriedade(expressao) {
throw new Error('Método não implementado.');
}
visitarDeclaracaoComentario(declaracao) {
return Promise.resolve();
}
visitarDeclaracaoTendoComo(declaracao) {
throw new Error('Método não implementado.');
}
visitarDeclaracaoInicioAlgoritmo(declaracao) {
throw new Error('Método não implementado.');
}
visitarDeclaracaoCabecalhoPrograma(declaracao) {
throw new Error('Método não implementado.');
}
visitarExpressaoTupla(expressao) {
throw new Error('Método não implementado.');
}
visitarExpressaoAcessoElementoMatriz(expressao) {
throw new Error('Método não implementado.');
}
visitarExpressaoAtribuicaoPorIndicesMatriz(expressao) {
throw new Error('Método não implementado.');
}
visitarExpressaoExpressaoRegular(expressao) {
throw new Error('Método não implementado.');
}
visitarExpressaoTipoDe(expressao) {
throw new Error('Método não implementado.');
}
visitarExpressaoFalhar(expressao) {
throw new Error('Método não implementado.');
}
visitarDeclaracaoParaCada(declaracao) {
throw new Error('Método não implementado.');
}
visitarDeclaracaoConst(declaracao) {
throw new Error('Método não implementado.');
}
visitarDeclaracaoConstMultiplo(declaracao) {
throw new Error('Método não implementado.');
}
visitarExpressaoFimPara(declaracao) {
throw new Error('Método não implementado.');
}
visitarExpressaoFormatacaoEscrita(declaracao) {
throw new Error('Método não implementado.');
}
visitarDeclaracaoEscrevaMesmaLinha(declaracao) {
throw new Error('Método não implementado.');
}
visitarExpressaoLeia(expressao) {
throw new Error('Método não implementado.');
}
visitarExpressaoLiteral(expressao) {
return expressao.valor;
}
avaliar(expressao) {
return expressao.aceitar(this);
}
visitarExpressaoAgrupamento(expressao) {
return this.avaliar(expressao.expressao);
}
visitarDeclaracaoAleatorio(declaracao) {
throw new Error('Método não implementado');
}
eVerdadeiro(objeto) {
if (objeto === null)
return false;
if (typeof objeto === 'boolean')
return Boolean(objeto);
if (objeto.hasOwnProperty('valor')) {
return Boolean(objeto.valor);
}
return true;
}
verificarOperandoNumero(operador, operando) {
if (typeof operando === 'number' || operando.tipo === 'número')
return;
throw new excecoes_1.ErroEmTempoDeExecucao(operador, 'Operador precisa ser um número.', operador.linha);
}
async visitarExpressaoUnaria(expr) {
const direita = await this.avaliar(expr.direita);
const valor = this.resolverValor(direita);
switch (expr.operador.tipo) {
case egua_classico_1.default.SUBTRACAO:
this.verificarOperandoNumero(expr.operador, valor);
return -valor;
case egua_classico_1.default.NEGACAO:
return !this.eVerdadeiro(valor);
case egua_classico_1.default.BIT_NOT:
return ~valor;
}
return null;
}
eIgual(esquerda, direita) {
if (esquerda && esquerda.tipo) {
if (esquerda.tipo === 'nulo' && direita.tipo && direita.tipo === 'nulo')
return true;
if (esquerda.tipo === 'nulo')
return false;
return esquerda.valor === direita.valor;
}
if (esquerda === null && direita === null)
return true;
if (esquerda === null)
return false;
return esquerda === direita;
}
verificarOperandosNumeros(operador, direita, esquerda) {
const tipoDireita = direita.tipo
? direita.tipo
: typeof direita === 'number'
? 'número'
: String(NaN);
const tipoEsquerda = esquerda.tipo
? esquerda.tipo
: typeof esquerda === 'number'
? 'número'
: String(NaN);
const tiposNumericos = ['inteiro', 'numero', 'número', 'real'];
if (tiposNumericos.includes(tipoDireita.toLowerCase()) &&
tiposNumericos.includes(tipoEsquerda.toLowerCase()))
return;
throw new excecoes_1.ErroEmTempoDeExecucao(operador, 'Operadores precisam ser números.', operador.linha);
}
async visitarExpressaoBinaria(expressao) {
try {
const esquerda = await this.avaliar(expressao.esquerda);
const direita = await this.avaliar(expressao.direita);
const valorEsquerdo = this.resolverValor(esquerda);
const valorDireito = this.resolverValor(direita);
const tipoEsquerdo = esquerda && esquerda.hasOwnProperty('tipo')
? esquerda.tipo
: (0, inferenciador_1.inferirTipoVariavel)(esquerda);
const tipoDireito = direita && direita.hasOwnProperty('tipo')
? direita.tipo
: (0, inferenciador_1.inferirTipoVariavel)(direita);
switch (expressao.operador.tipo) {
case egua_classico_1.default.EXPONENCIACAO:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Math.pow(valorEsquerdo, valorDireito);
case egua_classico_1.default.MAIOR:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) > Number(valorDireito);
case egua_classico_1.default.MAIOR_IGUAL:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) >= Number(valorDireito);
case egua_classico_1.default.MENOR:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) < Number(valorDireito);
case egua_classico_1.default.MENOR_IGUAL:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) <= Number(valorDireito);
case egua_classico_1.default.SUBTRACAO:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) - Number(valorDireito);
case egua_classico_1.default.ADICAO:
if (tipoEsquerdo === 'número' && tipoDireito === 'número') {
return Number(valorEsquerdo) + Number(valorDireito);
}
else if (tipoEsquerdo === 'texto' && tipoDireito === 'texto') {
return String(valorEsquerdo) + String(valorDireito);
}
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.operador, 'Operadores precisam ser dois números ou duas strings.');
case egua_classico_1.default.DIVISAO:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) / Number(valorDireito);
case egua_classico_1.default.MULTIPLICACAO:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) * Number(valorDireito);
case egua_classico_1.default.MODULO:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) % Number(valorDireito);
case egua_classico_1.default.BIT_AND:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) & Number(valorDireito);
case egua_classico_1.default.BIT_XOR:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) ^ Number(valorDireito);
case egua_classico_1.default.BIT_OR:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) | Number(valorDireito);
case egua_classico_1.default.MENOR_MENOR:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) << Number(valorDireito);
case egua_classico_1.default.MAIOR_MAIOR:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) >> Number(valorDireito);
case egua_classico_1.default.DIFERENTE:
return !this.eIgual(valorEsquerdo, valorDireito);
case egua_classico_1.default.IGUAL_IGUAL:
return this.eIgual(valorEsquerdo, valorDireito);
}
return null;
}
catch (erro) {
return Promise.reject(erro);
}
}
async visitarExpressaoDeChamada(expressao) {
const variavelEntidadeChamada = await this.avaliar(expressao.entidadeChamada);
if (variavelEntidadeChamada === null) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(expressao.parentese, 'Chamada de função ou método inexistente: ' + String(expressao.entidadeChamada), expressao.linha));
}
const entidadeChamada = this.resolverValor(variavelEntidadeChamada);
let argumentos = [];
for (let i = 0; i < expressao.argumentos.length; i++) {
const variavelArgumento = expressao.argumentos[i];
argumentos.push({
nome: variavelArgumento,
valor: await this.avaliar(variavelArgumento),
});
}
if (!(entidadeChamada instanceof chamavel_1.Chamavel)) {
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.parentese, 'Só pode chamar função ou classe.', expressao.linha);
}
let parametros;
if (entidadeChamada instanceof delegua_funcao_1.DeleguaFuncao) {
parametros = entidadeChamada.declaracao.parametros;
}
else if (entidadeChamada instanceof descritor_tipo_classe_1.DescritorTipoClasse) {
parametros = entidadeChamada.metodos.inicializacao
? entidadeChamada.metodos.inicializacao.declaracao.parametros
: [];
}
else {
parametros = [];
}
// Isso aqui completa os parâmetros não preenchidos com nulos.
if (argumentos.length < entidadeChamada.aridade()) {
const diferenca = entidadeChamada.aridade() - argumentos.length;
for (let i = 0; i < diferenca; i++) {
argumentos.push(null);
}
}
else {
if (parametros &&
parametros.length > 0 &&
parametros[parametros.length - 1]['tipo'] === 'multiplo') {
let novosArgumentos = argumentos.slice(0, parametros.length - 1);
novosArgumentos = novosArgumentos.concat(argumentos.slice(parametros.length - 1, argumentos.length));
argumentos = novosArgumentos;
}
}
if (entidadeChamada instanceof funcao_padrao_1.FuncaoPadrao) {
return entidadeChamada.chamar(undefined, argumentos.map((a) => (this.resolverValor(a))), expressao.entidadeChamada.simbolo);
}
return entidadeChamada.chamar(this, argumentos);
}
async visitarExpressaoDeAtribuicao(expressao) {
const valor = await this.avaliar(expressao.valor);
const alvoVariavel = expressao.alvo;
this.pilhaEscoposExecucao.atribuirVariavel(alvoVariavel.simbolo, valor);
return valor;
}
procurarVariavel(simbolo, expressao) {
return this.pilhaEscoposExecucao.obterValorVariavel(simbolo);
}
visitarExpressaoDeVariavel(expressao) {
return Promise.resolve(this.procurarVariavel(expressao.simbolo, expressao));
}
async visitarDeclaracaoDeExpressao(declaracao) {
return await this.avaliar(declaracao.expressao);
}
async visitarExpressaoLogica(expressao) {
const esquerda = await this.avaliar(expressao.esquerda);
if (expressao.operador.tipo === egua_classico_1.default.EM) {
const direita = await this.avaliar(expressao.direita);
if (Array.isArray(direita) || typeof direita === 'string') {
return direita.includes(esquerda);
}
else if (direita.constructor === Object) {
return esquerda in direita;
}
else {
throw new excecoes_1.ErroEmTempoDeExecucao(esquerda, "Tipo de chamada inválida com 'em'.", expressao.linha);
}
}
// se um estado for verdadeiro, retorna verdadeiro
if (expressao.operador.tipo === egua_classico_1.default.OU) {
if (this.eVerdadeiro(esquerda))
return esquerda;
}
// se um estado for falso, retorna falso
if (expressao.operador.tipo === egua_classico_1.default.E) {
if (!this.eVerdadeiro(esquerda))
return esquerda;
}
return await this.avaliar(expressao.direita);
}
async visitarDeclaracaoSe(declaracao) {
if (this.eVerdadeiro(await this.avaliar(declaracao.condicao))) {
return await this.executar(declaracao.caminhoEntao);
}
for (let i = 0; i < declaracao.caminhosSeSenao.length; i++) {
const atual = declaracao.caminhosSeSenao[i];
if (this.eVerdadeiro(await this.avaliar(atual.condicao))) {
return await this.executar(atual.caminho);
}
}
if (declaracao.caminhoSenao !== null) {
return await this.executar(declaracao.caminhoSenao);
}
return null;
}
async visitarDeclaracaoPara(declaracao) {
if (declaracao.inicializador !== null) {
await this.avaliar(declaracao.inicializador);
}
while (true) {
if (declaracao.condicao !== null) {
if (!this.eVerdadeiro(await this.avaliar(declaracao.condicao))) {
break;
}
}
try {
await this.executar(declaracao.corpo);
}
catch (erro) {
throw erro;
}
if (declaracao.incrementar !== null) {
await this.avaliar(declaracao.incrementar);
}
}
return null;
}
async visitarDeclaracaoFazer(declaracao) {
do {
try {
await this.executar(declaracao.caminhoFazer);
}
catch (erro) {
throw erro;
}
} while (this.eVerdadeiro(await this.avaliar(declaracao.condicaoEnquanto)));
}
async visitarDeclaracaoEscolha(declaracao) {
const condicaoEscolha = await this.avaliar(declaracao.identificadorOuLiteral);
const caminhos = declaracao.caminhos;
const caminhoPadrao = declaracao.caminhoPadrao;
let encontrado = false;
try {
for (let i = 0; i < caminhos.length; i++) {
const caminho = caminhos[i];
for (let j = 0; j < caminho.condicoes.length; j++) {
if ((await this.avaliar(caminho.condicoes[j])) === condicaoEscolha) {
encontrado = true;
try {
for (let k = 0; k < caminho.declaracoes.length; k++) {
await this.executar(caminho.declaracoes[k]);
}
}
catch (erro) {
throw erro;
}
}
}
}
if (caminhoPadrao !== null && encontrado === false) {
for (let i = 0; i < caminhoPadrao.declaracoes.length; i++) {
await this.executar(caminhoPadrao['declaracoes'][i]);
}
}
}
catch (erro) {
throw erro;
}
}
async visitarDeclaracaoTente(declaracao) {
let valorRetorno;
try {
let sucesso = true;
try {
valorRetorno = await this.executarBloco(declaracao.caminhoTente);
}
catch (erro) {
sucesso = false;
if (declaracao.caminhoPegue !== null) {
valorRetorno = await this.executarBloco(declaracao.caminhoPegue);
}
else {
this.erros.push(erro);
}
}
if (sucesso && declaracao.caminhoSenao !== null) {
valorRetorno = await this.executarBloco(declaracao.caminhoSenao);
}
}
finally {
if (declaracao.caminhoFinalmente !== null)
valorRetorno = await this.executarBloco(declaracao.caminhoFinalmente);
}
return valorRetorno;
}
async visitarDeclaracaoEnquanto(declaracao) {
while (this.eVerdadeiro(await this.avaliar(declaracao.condicao))) {
try {
await this.executar(declaracao.corpo);
}
catch (erro) {
throw erro;
}
}
return null;
}
// TODO: Implementar em `delegua-node`.
async visitarDeclaracaoImportar(declaracao) {
throw new excecoes_1.ErroEmTempoDeExecucao({
lexema: declaracao.caminho.valor,
linha: declaracao.linha,
hashArquivo: declaracao.caminho.hashArquivo,
}, 'Importação não suportada em núcleo da linguagem puro. Favor executar a aplicação usando o pacote NPM `delegua-node`.', declaracao.linha);
}
async visitarDeclaracaoEscreva(declaracao) {
try {
const resultadoAvaliacao = await this.avaliar(declaracao.argumentos[0]);
let valor = this.resolverValor(resultadoAvaliacao);
// eslint-disable-next-line no-undef
console.log(this.paraTexto(valor));
return null;
}
catch (erro) {
this.erros.push({
erroInterno: erro,
linha: declaracao.linha,
hashArquivo: declaracao.hashArquivo,
});
}
}
/**
* Empilha declarações na pilha de escopos de execução, cria um novo ambiente e
* executa as declarações empilhadas.
*
* Se o retorno do último bloco foi uma exceção (normalmente um erro em tempo de execução),
* atira a exceção daqui.
* Isso é usado, por exemplo, em blocos `tente ... pegue ... finalmente`.
* @param declaracoes Um vetor de declaracoes a ser executado.
* @param ambiente O ambiente de execução quando houver, como parâmetros, argumentos, etc.
*/
async executarBloco(declaracoes, ambiente) {
const escopoExecucao = {
declaracoes: declaracoes,
declaracaoAtual: 0,
espacoMemoria: ambiente || new espaco_memoria_1.EspacoMemoria(),
finalizado: false,
tipo: 'outro',
emLacoRepeticao: false,
};
this.pilhaEscoposExecucao.empilhar(escopoExecucao);
const retornoUltimoEscopo = await this.executarUltimoEscopo();
if (retornoUltimoEscopo instanceof excecoes_1.ErroEmTempoDeExecucao) {
throw retornoUltimoEscopo;
}
return retornoUltimoEscopo;
}
async visitarExpressaoBloco(declaracao) {
return await this.executarBloco(declaracao.declaracoes);
}
/**
* Executa expressão de definição de variável.
* @param declaracao A declaração Var
* @returns Sempre retorna nulo.
*/
async visitarDeclaracaoVar(declaracao) {
let valorOuOutraVariavel = null;
if (declaracao.inicializador !== null) {
valorOuOutraVariavel = await this.avaliar(declaracao.inicializador);
}
this.pilhaEscoposExecucao.definirVariavel(declaracao.simbolo.lexema, this.resolverValor(valorOuOutraVariavel));
return null;
}
async visitarDeclaracaoVarMultiplo(declaracao) {
throw new Error('Método não implementado.');
}
visitarExpressaoContinua(declaracao) {
return new quebras_1.ContinuarQuebra();
}
visitarExpressaoSustar(declaracao) {
return new quebras_1.SustarQuebra();
}
async visitarExpressaoRetornar(declaracao) {
let valor = null;
if (declaracao.valor != null)
valor = await this.avaliar(declaracao.valor);
return new quebras_1.RetornoQuebra(valor);
}
async visitarExpressaoFuncaoConstruto(expressao) {
return new delegua_funcao_1.DeleguaFuncao(null, expressao);
}
async visitarExpressaoAtribuicaoPorIndice(expressao) {
const objeto = await this.avaliar(expressao.objeto);
let indice = await this.avaliar(expressao.indice);
const valor = await this.avaliar(expressao.valor);
if (Array.isArray(objeto)) {
if (indice < 0 && objeto.length !== 0) {
while (indice < 0) {
indice += objeto.length;
}
}
while (objeto.length < indice) {
objeto.push(null);
}
objeto[indice] = valor;
}
else if (objeto.constructor === Object ||
objeto instanceof objeto_delegua_classe_1.ObjetoDeleguaClasse ||
objeto instanceof delegua_funcao_1.DeleguaFuncao ||
objeto instanceof descritor_tipo_classe_1.DescritorTipoClasse ||
objeto instanceof modulo_1.DeleguaModulo) {
objeto[indice] = valor;
}
else {
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.objeto.nome, 'Somente listas, dicionários, classes e objetos podem ser mudados por sobrescrita.', expressao.linha);
}
}
async visitarExpressaoAcessoIndiceVariavel(expressao) {
const variavelObjeto = await this.avaliar(expressao.entidadeChamada);
const objeto = this.resolverValor(variavelObjeto);
let indice = await this.avaliar(expressao.indice);
const valorIndice = this.resolverValor(indice);
if (Array.isArray(objeto)) {
if (!Number.isInteger(valorIndice)) {
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Somente inteiros podem ser usados para indexar um vetor.', expressao.linha);
}
if (indice < 0 && objeto.length !== 0) {
while (indice < 0) {
indice += objeto.length;
}
}
if (indice >= objeto.length) {
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Índice do vetor fora do intervalo.', expressao.linha);
}
return objeto[indice];
}
else if (objeto.constructor === Object ||
objeto instanceof objeto_delegua_classe_1.ObjetoDeleguaClasse ||
objeto instanceof delegua_funcao_1.DeleguaFuncao ||
objeto instanceof descritor_tipo_classe_1.DescritorTipoClasse ||
objeto instanceof modulo_1.DeleguaModulo) {
return objeto[indice] || null;
}
else if (typeof objeto === 'string') {
if (!Number.isInteger(indice)) {
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Somente inteiros podem ser usados para indexar um vetor.', expressao.linha);
}
if (indice < 0 && objeto.length !== 0) {
while (indice < 0) {
indice += objeto.length;
}
}
if (indice >= objeto.length) {
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Índice fora do tamanho.', expressao.linha);
}
return objeto.charAt(indice);
}
else {
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.entidadeChamada.nome, 'Somente listas, dicionários, classes e objetos podem ser mudados por sobrescrita.', expressao.linha);
}
}
async visitarExpressaoDefinirValor(expressao) {
const objeto = await this.avaliar(expressao.objeto);
if (!(objeto instanceof objeto_delegua_classe_1.ObjetoDeleguaClasse) && objeto.constructor !== Object) {
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.objeto.nome, 'Somente instâncias e dicionários podem possuir campos.', expressao.linha);
}
const valor = await this.avaliar(expressao.valor);
if (objeto instanceof objeto_delegua_classe_1.ObjetoDeleguaClasse) {
objeto.definir(expressao.nome, valor);
return valor;
}
else if (objeto.constructor === Object) {
objeto[expressao.simbolo.lexema] = valor;
}
}
visitarDeclaracaoDefinicaoFuncao(declaracao) {
const funcao = new delegua_funcao_1.DeleguaFuncao(declaracao.simbolo.lexema, declaracao.funcao);
this.pilhaEscoposExecucao.definirVariavel(declaracao.simbolo.lexema, funcao);
}
async visitarDeclaracaoClasse(declaracao) {
let superClasse = null;
if (declaracao.superClasse !== null) {
const variavelSuperClasse = await this.avaliar(declaracao.superClasse);
superClasse = variavelSuperClasse.valor;
if (!(superClasse instanceof descritor_tipo_classe_1.DescritorTipoClasse)) {
throw new excecoes_1.ErroEmTempoDeExecucao(declaracao.superClasse.nome, 'Superclasse precisa ser uma classe.', declaracao.linha);
}
}
// TODO: Estudar retirar isso.
// this.pilhaEscoposExecucao.definirVariavel(declaracao.simbolo.lexema, null);
if (declaracao.superClasse !== null) {
this.pilhaEscoposExecucao.definirVariavel('super', superClasse);
}
const metodos = {};
const definirMetodos = declaracao.metodos;
for (let i = 0; i < declaracao.metodos.length; i++) {
const metodoAtual = definirMetodos[i];
const eInicializador = metodoAtual.simbolo.lexema === 'construtor';
const funcao = new delegua_funcao_1.DeleguaFuncao(metodoAtual.simbolo.lexema, metodoAtual.funcao, undefined, eInicializador);
metodos[metodoAtual.simbolo.lexema] = funcao;
}
const deleguaClasse = new descritor_tipo_classe_1.DescritorTipoClasse(declaracao.simbolo, superClasse, metodos);
// TODO: Depreciar na próxima versão.
deleguaClasse.dialetoRequerExpansaoPropriedadesEspacoMemoria = false;
deleguaClasse.dialetoRequerDeclaracaoPropriedades = false;
// TODO: Recolocar isso se for necessário.
/* if (superClasse !== null) {
this.ambiente = this.ambiente.enclosing;
} */
this.pilhaEscoposExecucao.definirVariavel(declaracao.simbolo.lexema, deleguaClasse);
return null;
}
async visitarExpressaoAcessoMetodoOuPropriedade(expressao) {
const variavelObjeto = await this.avaliar(expressao.objeto);
const objeto = variavelObjeto === null || variavelObjeto === void 0 ? void 0 : variavelObjeto.valor;
if (objeto instanceof objeto_delegua_classe_1.ObjetoDeleguaClasse) {
return objeto.obter(expressao.simbolo) || null;
}
else if (objeto.constructor === Object) {
return objeto[expressao.simbolo.lexema] || null;
}
else if (objeto instanceof modulo_1.DeleguaModulo) {
return objeto[expressao.simbolo.lexema] || null;
}
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.nome, 'Você só pode acessar métodos do objeto e dicionários.', expressao.linha);
}
async visitarExpressaoIsto(expressao) {
return Promise.resolve(this.procurarVariavel(expressao.palavraChave, expressao));
}
async visitarExpressaoDicionario(expressao) {
const dicionario = {};
for (let i = 0; i < expressao.chaves.length; i++) {
dicionario[await this.avaliar(expressao.chaves[i])] = await this.avaliar(expressao.valores[i]);
}
return dicionario;
}
async visitarExpressaoVetor(expressao) {
const valores = [];
for (let i = 0; i < expressao.valores.length; i++) {
valores.push(await this.avaliar(expressao.valores[i]));
}
return valores;
}
visitarExpressaoSuper(expressao) {
const objeto = this.pilhaEscoposExecucao.obterVariavelPorNome('isto');
return objeto.valor;
}
paraTexto(objeto) {
if (objeto === null)
return 'nulo';
if (typeof objeto === 'boolean') {
return objeto ? 'verdadeiro' : 'falso';
}
if (objeto instanceof Date) {
const formato = Intl.DateTimeFormat('pt', {
dateStyle: 'full',
timeStyle: 'full',
});
return formato.format(objeto);
}
if (Array.isArray(objeto))
return objeto;
if (typeof objeto === 'object')
return JSON.stringify(objeto);
if (objeto === undefined) {
return 'nulo';
}
return objeto.toString();
}
async executar(declaracao, mostrarResultado = false) {
return await declaracao.aceitar(this);
}
/**
* Executa o último escopo empilhado no topo na pilha de escopos do Interpretador.
* Originalmente, Égua não trabalha com uma pilha de escopos.
*
* O tratamento das exceções é feito de acordo com o bloco chamador.
* Por exemplo, em `tente ... pegue ... finalmente`, a exceção é capturada e tratada.
* Em outros blocos, pode ser desejável ter o erro em tela.
*
* Essa implementação é derivada do Interpretador de Delégua, mas simulando todos os
* comportamos do interpretador Égua original.
* Interpretador Égua: https://github.com/eguatech/egua/blob/master/src/interpreter.js
* @returns O resultado da execução do escopo, se houver.
*/
async executarUltimoEscopo() {
const ultimoEscopo = this.pilhaEscoposExecucao.topoDaPilha();
try {
let retornoExecucao;
for (; !(retornoExecucao instanceof quebras_1.Quebra) &&
ultimoEscopo.declaracaoAtual < ultimoEscopo.declaracoes.length; ultimoEscopo.declaracaoAtual++) {
retornoExecucao = await this.executar(ultimoEscopo.declaracoes[ultimoEscopo.declaracaoAtual]);
}
return retornoExecucao;
}
catch (erro) {
return Promise.reject(erro);
}
finally {
this.pilhaEscoposExecucao.removerUltimo();
}
}
async interpretar(declaracoes) {
this.erros = [];
const retornoResolvedor = await this.resolvedor.resolver(declaracoes);
this.locais = retornoResolvedor.locais;
const escopoExecucao = {
declaracoes: declaracoes,
declaracaoAtual: 0,
espacoMemoria: new espaco_memoria_1.EspacoMemoria(),
finalizado: false,
tipo: 'outro',
emLacoRepeticao: false,
};
this.pilhaEscoposExecucao.empilhar(escopoExecucao);
await this.executarUltimoEscopo();
return {
erros: this.erros,
};
}
finalizacao() {
this.funcaoDeRetorno();
}
}
exports.InterpretadorEguaClassico = InterpretadorEguaClassico;
//# sourceMappingURL=interpretador-egua-classico.js.map