@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
528 lines • 22 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InterpretadorPortugolIpt = void 0;
const espaco_memoria_1 = require("../../espaco-memoria");
const estruturas_1 = require("../../estruturas");
const excecoes_1 = require("../../../excecoes");
const quebras_1 = require("../../../quebras");
const pilha_escopos_execucao_1 = require("../../pilha-escopos-execucao");
const inferenciador_1 = require("../../../inferenciador");
const portugol_ipt_1 = __importDefault(require("../../../tipos-de-simbolos/portugol-ipt"));
class InterpretadorPortugolIpt {
constructor(diretorioBase, funcaoDeRetorno = null, funcaoDeRetornoMesmaLinha = null) {
this.funcaoDeRetorno = null;
this.funcaoDeRetornoMesmaLinha = null;
this.resultadoInterpretador = [];
this.diretorioBase = diretorioBase;
this.funcaoDeRetorno = funcaoDeRetorno || console.log;
this.funcaoDeRetornoMesmaLinha =
funcaoDeRetornoMesmaLinha || process.stdout.write.bind(process.stdout);
this.erros = [];
this.declaracoes = [];
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);
}
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.');
}
visitarExpressaoLiteral(expressao) {
return expressao.valor;
}
async avaliar(expressao) {
// Descomente o código abaixo quando precisar detectar expressões undefined ou nulas.
// Por algum motivo o depurador do VSCode não funciona direito aqui
// com breakpoint condicional.
if (expressao === null || expressao === undefined) {
console.log('Aqui');
}
return await expressao.aceitar(this);
}
async visitarExpressaoAgrupamento(expressao) {
return await this.avaliar(expressao.expressao);
}
visitarExpressaoUnaria(expressao) {
throw new Error('Método não implementado');
}
/**
* Verifica se operandos são números, que podem ser tanto variáveis puras do JavaScript
* (neste caso, `number`), ou podem ser variáveis de Delégua com inferência (`VariavelInterface`).
* @param operador O símbolo do operador.
* @param direita O operando direito.
* @param esquerda O operando esquerdo.
* @returns Se ambos os operandos são números ou não.
*/
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);
}
eIgual(esquerda, direita) {
if (esquerda === null && direita === null)
return true;
if (esquerda === null)
return false;
return esquerda === direita;
}
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 === null || esquerda === void 0 ? void 0 : esquerda.hasOwnProperty('tipo'))
? esquerda.tipo
: (0, inferenciador_1.inferirTipoVariavel)(esquerda);
const tipoDireito = (direita === null || direita === void 0 ? void 0 : direita.hasOwnProperty('tipo'))
? direita.tipo
: (0, inferenciador_1.inferirTipoVariavel)(direita);
switch (expressao.operador.tipo) {
case portugol_ipt_1.default.EXPONENCIACAO:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Math.pow(valorEsquerdo, valorDireito);
case portugol_ipt_1.default.MAIOR:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) > Number(valorDireito);
case portugol_ipt_1.default.MAIOR_IGUAL:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) >= Number(valorDireito);
case portugol_ipt_1.default.MENOR:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) < Number(valorDireito);
case portugol_ipt_1.default.MENOR_IGUAL:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) <= Number(valorDireito);
case portugol_ipt_1.default.SUBTRACAO:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) - Number(valorDireito);
case portugol_ipt_1.default.ADICAO:
if (tipoEsquerdo === 'número' && tipoDireito === 'número') {
return Number(valorEsquerdo) + Number(valorDireito);
}
else {
return String(valorEsquerdo) + String(valorDireito);
}
case portugol_ipt_1.default.DIVISAO:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) / Number(valorDireito);
case portugol_ipt_1.default.DIVISAO_INTEIRA:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Math.floor(Number(valorEsquerdo) / Number(valorDireito));
case portugol_ipt_1.default.MULTIPLICACAO:
if (tipoEsquerdo === 'texto' || tipoDireito === 'texto') {
// Sem ambos os valores resolvem como texto, multiplica normal.
// Se apenas um resolve como texto, o outro repete o
// texto n vezes, sendo n o valor do outro.
if (tipoEsquerdo === 'texto' && tipoDireito === 'texto') {
return Number(valorEsquerdo) * Number(valorDireito);
}
if (tipoEsquerdo === 'texto') {
return valorEsquerdo.repeat(Number(valorDireito));
}
return valorDireito.repeat(Number(valorEsquerdo));
}
return Number(valorEsquerdo) * Number(valorDireito);
case portugol_ipt_1.default.MODULO:
this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
return Number(valorEsquerdo) % Number(valorDireito);
case portugol_ipt_1.default.DIFERENTE:
return !this.eIgual(valorEsquerdo, valorDireito);
case portugol_ipt_1.default.IGUAL:
return this.eIgual(valorEsquerdo, valorDireito);
}
}
catch (erro) {
return Promise.reject(erro);
}
}
visitarExpressaoDeChamada(expressao) {
throw new Error('Método não implementado');
}
visitarExpressaoDeAtribuicao(expressao) {
throw new Error('Método não implementado');
}
procurarVariavel(simbolo) {
return this.pilhaEscoposExecucao.obterValorVariavel(simbolo);
}
visitarExpressaoDeVariavel(expressao) {
return this.procurarVariavel(expressao.simbolo);
}
visitarDeclaracaoDeExpressao(declaracao) {
throw new Error('Método não implementado');
}
/**
* Execução da leitura de valores da entrada configurada no
* início da aplicação.
* @param expressao Expressão do tipo Leia
* @returns Promise com o resultado da leitura.
*/
async visitarExpressaoLeia(expressao) {
const mensagem = expressao.argumentos && expressao.argumentos[0] ? expressao.argumentos[0].valor : '> ';
return new Promise((resolucao) => this.interfaceEntradaSaida.question(mensagem, (resposta) => {
resolucao(resposta);
}));
}
visitarExpressaoLogica(expressao) {
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;
}
/**
* Executa uma expressão Se, que tem uma condição, pode ter um bloco
* Senão, e múltiplos blocos Senão-se.
* @param declaracao A declaração Se.
* @returns O resultado da avaliação do bloco cuja condição é verdadeira.
*/
async visitarDeclaracaoSe(declaracao) {
if (this.eVerdadeiro(await this.avaliar(declaracao.condicao))) {
return await this.executar(declaracao.caminhoEntao);
}
if (declaracao.caminhoSenao !== null) {
return await this.executar(declaracao.caminhoSenao);
}
return null;
}
visitarDeclaracaoPara(declaracao) {
return Promise.reject('Método não implementado');
}
visitarExpressaoFimPara(declaracao) {
throw new Error('Método não implementado');
}
visitarDeclaracaoFazer(declaracao) {
throw new Error('Método não implementado');
}
async visitarExpressaoFormatacaoEscrita(declaracao) {
let resultado = '';
const conteudo = await this.avaliar(declaracao.expressao);
const valorConteudo = this.resolverValor(conteudo);
const tipoConteudo = conteudo.hasOwnProperty('tipo')
? conteudo.tipo
: typeof conteudo;
resultado = valorConteudo;
if (['número', 'number'].includes(tipoConteudo) && declaracao.casasDecimais > 0) {
resultado = valorConteudo.toLocaleString('pt', {
maximumFractionDigits: declaracao.casasDecimais,
});
}
if (declaracao.espacos > 0) {
resultado += ' '.repeat(declaracao.espacos);
}
return resultado;
}
visitarDeclaracaoEscolha(declaracao) {
throw new Error('Método não implementado');
}
visitarDeclaracaoTente(declaracao) {
throw new Error('Método não implementado');
}
visitarDeclaracaoEnquanto(declaracao) {
throw new Error('Método não implementado');
}
visitarDeclaracaoImportar(declaracao) {
throw new Error('Método não implementado');
}
async avaliarArgumentosEscreva(argumentos) {
let formatoTexto = '';
for (const argumento of argumentos) {
const resultadoAvaliacao = await this.avaliar(argumento);
let valor = this.resolverValor(resultadoAvaliacao);
formatoTexto += `${this.paraTexto(valor)} `;
}
return formatoTexto.trimEnd();
}
/**
* Execução de uma escrita na saída configurada, que pode ser `console` (padrão) ou
* alguma função para escrever numa página Web.
* @param declaracao A declaração.
* @returns Sempre nulo, por convenção de visita.
*/
async visitarDeclaracaoEscreva(declaracao) {
try {
const formatoTexto = await this.avaliarArgumentosEscreva(declaracao.argumentos);
this.funcaoDeRetorno(formatoTexto);
return null;
}
catch (erro) {
this.erros.push({
erroInterno: erro,
linha: declaracao.linha,
hashArquivo: declaracao.hashArquivo,
});
}
}
async visitarDeclaracaoEscrevaMesmaLinha(declaracao) {
try {
const formatoTexto = await this.avaliarArgumentosEscreva(declaracao.argumentos);
this.funcaoDeRetornoMesmaLinha(formatoTexto);
return null;
}
catch (erro) {
this.erros.push({
erroInterno: erro,
linha: declaracao.linha,
hashArquivo: declaracao.hashArquivo,
});
}
}
executarBloco(declaracoes, ambiente) {
throw new Error('Método não implementado');
}
visitarExpressaoBloco(declaracao) {
throw new Error('Método não implementado');
}
async avaliacaoDeclaracaoVar(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;
}
/**
* Executa expressão de definição de variável.
* @param declaracao A declaração Var
* @returns Sempre retorna nulo.
*/
async visitarDeclaracaoVar(declaracao) {
const valorFinal = await this.avaliacaoDeclaracaoVar(declaracao);
this.pilhaEscoposExecucao.definirVariavel(declaracao.simbolo.lexema, valorFinal, declaracao.tipo);
return null;
}
visitarDeclaracaoVarMultiplo(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');
}
visitarExpressaoContinua(declaracao) {
throw new Error('Método não implementado');
}
visitarExpressaoSustar(declaracao) {
throw new Error('Método não implementado');
}
visitarExpressaoRetornar(declaracao) {
throw new Error('Método não implementado');
}
visitarExpressaoFuncaoConstruto(expressao) {
throw new Error('Método não implementado');
}
visitarExpressaoAtribuicaoPorIndice(expressao) {
throw new Error('Método não implementado');
}
visitarExpressaoAcessoIndiceVariavel(expressao) {
throw new Error('Método não implementado');
}
visitarExpressaoDefinirValor(expressao) {
throw new Error('Método não implementado');
}
visitarDeclaracaoDefinicaoFuncao(declaracao) {
throw new Error('Método não implementado');
}
visitarDeclaracaoClasse(declaracao) {
throw new Error('Método não implementado');
}
visitarExpressaoAcessoMetodoOuPropriedade(expressao) {
throw new Error('Método não implementado');
}
visitarExpressaoIsto(expressao) {
throw new Error('Método não implementado');
}
visitarExpressaoDicionario(expressao) {
throw new Error('Método não implementado');
}
visitarExpressaoVetor(expressao) {
throw new Error('Método não implementado');
}
visitarExpressaoSuper(expressao) {
throw new Error('Método não implementado');
}
paraTexto(objeto) {
if (objeto === null || objeto === undefined)
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 (objeto.valor instanceof estruturas_1.ObjetoPadrao)
return objeto.valor.paraTexto();
if (typeof objeto === 'object')
return JSON.stringify(objeto);
return objeto.toString();
}
/**
* Efetivamente executa uma declaração.
* @param declaracao A declaração a ser executada.
* @param mostrarResultado Se resultado deve ser mostrado ou não. Normalmente usado
* pelo modo LAIR.
*/
async executar(declaracao, mostrarResultado = false) {
const resultado = await declaracao.aceitar(this);
if (mostrarResultado) {
this.funcaoDeRetorno(this.paraTexto(resultado));
}
if (resultado || typeof resultado === 'boolean') {
this.resultadoInterpretador.push(this.paraTexto(resultado));
}
return resultado;
}
visitarDeclaracaoAleatorio(declaracao) {
throw new Error('Método não implementado.');
}
/**
* Executa o último escopo empilhado no topo na pilha de escopos do interpretador.
* Esse método pega exceções, mas apenas as devolve.
*
* 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.
* @param manterAmbiente Se verdadeiro, ambiente do topo da pilha de escopo é copiado para o ambiente imediatamente abaixo.
* @returns O resultado da execução do escopo, se houver.
*/
async executarUltimoEscopo(manterAmbiente = false) {
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();
if (manterAmbiente) {
const escopoAnterior = this.pilhaEscoposExecucao.topoDaPilha();
escopoAnterior.espacoMemoria.valores = Object.assign(escopoAnterior.espacoMemoria.valores, ultimoEscopo.espacoMemoria.valores);
}
}
}
async interpretar(declaracoes, manterAmbiente) {
this.erros = [];
const escopoExecucao = {
declaracoes: declaracoes,
declaracaoAtual: 0,
espacoMemoria: new espaco_memoria_1.EspacoMemoria(),
finalizado: false,
tipo: 'outro',
emLacoRepeticao: false,
};
this.pilhaEscoposExecucao.empilhar(escopoExecucao);
try {
const retornoOuErro = await this.executarUltimoEscopo(manterAmbiente);
if (retornoOuErro instanceof excecoes_1.ErroEmTempoDeExecucao) {
this.erros.push(retornoOuErro);
}
}
catch (erro) {
this.erros.push(erro);
}
finally {
const retorno = {
erros: this.erros,
resultado: this.resultadoInterpretador,
};
this.resultadoInterpretador = [];
return retorno;
}
}
}
exports.InterpretadorPortugolIpt = InterpretadorPortugolIpt;
//# sourceMappingURL=interpretador-portugol-ipt.js.map