@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
340 lines • 17 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.InterpretadorPitugues = void 0;
const construtos_1 = require("../../../construtos");
const interpretador_1 = require("../../interpretador");
const excecoes_1 = require("../../../excecoes");
const inferenciador_1 = require("../../../inferenciador");
const quebras_1 = require("../../../quebras");
const pilha_escopos_execucao_pitugues_1 = require("./pilha-escopos-execucao-pitugues");
const estruturas_1 = require("../../estruturas");
const bibliotecaGlobalPitugues = __importStar(require("../../../bibliotecas/dialetos/pitugues/biblioteca-global"));
const comum = __importStar(require("./comum"));
class InterpretadorPitugues extends interpretador_1.Interpretador {
constructor(diretorioBase, performance = false, funcaoDeRetorno = null, funcaoDeRetornoMesmaLinha = null) {
super(diretorioBase, performance, funcaoDeRetorno, funcaoDeRetornoMesmaLinha);
// Substituir a pilha compartilhada de Delégua pela implementação específica
// de Pituguês, que aplica a semântica de escopo local-first (estilo Python).
const pilhaPitugues = new pilha_escopos_execucao_pitugues_1.PilhaEscoposExecucaoPitugues();
for (const escopo of this.pilhaEscoposExecucao.pilha) {
pilhaPitugues.empilhar(escopo);
}
this.pilhaEscoposExecucao = pilhaPitugues;
this.lancarErroPorDivisaoPorZero = true;
this.requerDeclaracaoPropriedades = false;
}
pontoInicializacaoBibliotecasGlobais() {
for (const [nome, valor] of Object.entries(bibliotecaGlobalPitugues)) {
if (typeof valor === 'function') {
this.pilhaEscoposExecucao.definirVariavel(nome, new estruturas_1.FuncaoPadrao(valor.length, valor));
}
}
}
/**
* Sobrescreve `executarBloco` para marcar escopos de chamadas de função com
* `tipo: 'funcao'`. Quando `ambiente` é fornecido, a chamada vem de
* `DeleguaFuncao.chamar`, que repassa o espaço de memória dos parâmetros.
* Isso permite que `PilhaEscoposExecucaoPitugues` identifique fronteiras de
* função e aplique a semântica LEGB corretamente.
*/
async executarBloco(declaracoes, ambiente) {
if (ambiente !== undefined && ambiente !== null) {
const escopoFuncao = {
declaracoes,
declaracaoAtual: 0,
espacoMemoria: ambiente,
finalizado: false,
tipo: 'funcao',
emLacoRepeticao: false,
};
this.pilhaEscoposExecucao.empilhar(escopoFuncao);
const retorno = await this.executarUltimoEscopo();
if (retorno instanceof excecoes_1.ErroEmTempoDeExecucao) {
return Promise.reject(retorno);
}
return retorno;
}
return super.executarBloco(declaracoes, ambiente);
}
async visitarExpressaoAcessoMetodo(expressao) {
const variavelObjeto = await this.avaliar(expressao.objeto);
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.nomeMetodo}' de um valor nulo.`, expressao.linha));
}
if (objeto instanceof estruturas_1.DescritorTipoClasse) {
return await objeto.obterEstatico(expressao.nomeMetodo, this);
}
return comum.visitarExpressaoAcessoMetodo(this, expressao);
}
async visitarExpressaoAcessoMetodoOuPropriedade(expressao) {
const variavelObjeto = await this.avaliar(expressao.objeto);
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));
}
if (objeto instanceof estruturas_1.DescritorTipoClasse) {
return await objeto.obterEstatico(expressao.simbolo.lexema, this);
}
return comum.visitarExpressaoAcessoMetodoOuPropriedade(this, expressao);
}
async visitarExpressaoAcessoPropriedade(expressao) {
const variavelObjeto = await this.avaliar(expressao.objeto);
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.nomePropriedade}' de um valor nulo.`, expressao.linha));
}
if (objeto instanceof estruturas_1.DescritorTipoClasse) {
return await objeto.obterEstatico(expressao.nomePropriedade, this);
}
return super.visitarExpressaoAcessoPropriedade(expressao);
}
async visitarExpressaoAcessoIntervaloVariavel(expressao) {
return comum.visitarExpressaoAcessoIntervaloVariavel(this, expressao);
}
async visitarExpressaoTuplaN(expressao) {
return comum.visitarExpressaoTuplaN(this, expressao);
}
async visitarExpressaoDeAtribuicao(expressao) {
if (expressao.alvo.constructor === construtos_1.Variavel) {
const alvoVariavel = expressao.alvo;
try {
// Verifica se a variável existe em algum escopo.
// Se não existir, obterValorVariavel lança uma exceção.
this.pilhaEscoposExecucao.obterValorVariavel(alvoVariavel.simbolo);
}
catch (e) {
// Variável completamente nova: declaração implícita no escopo atual.
let valor = await this.avaliar(expressao.valor);
if (valor && valor.hasOwnProperty('valorRetornado')) {
valor = valor.valorRetornado;
}
const valorResolvido = this.resolverValor(valor);
this.pilhaEscoposExecucao.definirVariavel(alvoVariavel.simbolo.lexema, valorResolvido);
return valorResolvido;
}
}
// Variável já existe em algum escopo. O método atribuirVariavel de
// PilhaEscoposExecucaoPitugues decide onde escrever: se estivermos dentro
// de uma função e a variável só existir no escopo global, ela é criada
// localmente na função (semântica LEGB do Python).
return super.visitarExpressaoDeAtribuicao(expressao);
}
async visitarExpressaoAtribuicaoPorIndice(expressao) {
const objeto = await this.avaliar(expressao.objeto);
const objetoResolvido = this.resolverValor(objeto);
if (objetoResolvido instanceof construtos_1.TuplaN || objetoResolvido.tipo === 'tupla') {
throw 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);
}
return super.visitarExpressaoAtribuicaoPorIndice(expressao);
}
async visitarExpressaoAcessoIndiceVariavel(expressao) {
const objeto = await this.avaliar(expressao.entidadeChamada);
const indice = await this.avaliar(expressao.indice);
let valorIndice = this.resolverValor(indice);
const objetoResolvido = this.resolverValor(objeto);
if (objetoResolvido instanceof construtos_1.TuplaN) {
if (!Number.isInteger(valorIndice)) {
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Índice deve ser inteiro.', expressao.linha);
}
if (valorIndice < 0 && objetoResolvido.elementos.length !== 0) {
valorIndice += objetoResolvido.elementos.length;
}
if (valorIndice < 0 || valorIndice >= objetoResolvido.elementos.length) {
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Índice fora do intervalo.', expressao.linha);
}
const elemento = objetoResolvido.elementos[valorIndice];
if (elemento instanceof construtos_1.Literal)
return elemento.valor;
return this.avaliar(elemento);
}
return super.visitarExpressaoAcessoIndiceVariavel(expressao);
}
async visitarExpressaoTipoDe(expressao) {
const resultado = await super.visitarExpressaoTipoDe(expressao);
if (typeof resultado === 'string')
return resultado.replace('tipo de', 'tipo');
return resultado;
}
/**
* Normaliza o valor resolvido para um array iterável.
* Converte dicionários em listas de Duplas e strings em listas de caracteres.
*/
prepararListaParaIteracao(valor, declaracao) {
let valorFinal = this.resolverValor(valor);
const ehDicionario = declaracao.vetorOuDicionario.tipo === 'dicionário';
const ehObjetoPuro = valorFinal && typeof valorFinal === 'object' && !Array.isArray(valorFinal);
if (ehDicionario || ehObjetoPuro) {
return Object.entries(valorFinal).map(([chave, valor]) => new construtos_1.Dupla(new construtos_1.Literal(declaracao.hashArquivo, declaracao.linha, chave, 'texto'), new construtos_1.Literal(declaracao.hashArquivo, declaracao.linha, valor, (0, inferenciador_1.inferirTipoVariavel)(valor))));
}
if (typeof valorFinal === 'string')
return valorFinal.split('');
if (!Array.isArray(valorFinal)) {
throw new Error("O objeto provido para 'para cada' não é iterável.");
}
return valorFinal;
}
/**
* Resolve a lógica de atribuição das variáveis no escopo.
* Suporta variáveis simples ou pares (Dupla).
*/
definirVariaveisIteracao(variavel, elemento) {
if (variavel instanceof construtos_1.Variavel) {
this.pilhaEscoposExecucao.definirVariavel(variavel.simbolo.lexema, this.resolverValor(elemento));
return;
}
if (variavel instanceof construtos_1.Dupla) {
const var1 = variavel.primeiro;
const var2 = variavel.segundo;
let v1, v2;
if (elemento instanceof construtos_1.Dupla) {
v1 = this.resolverValor(elemento.primeiro);
v2 = this.resolverValor(elemento.segundo);
}
else {
v1 = elemento[0];
v2 = elemento[1];
}
this.pilhaEscoposExecucao.definirVariavel(var1.simbolo.lexema, v1);
this.pilhaEscoposExecucao.definirVariavel(var2.simbolo.lexema, v2);
}
}
async visitarDeclaracaoParaCada(declaracao) {
let retornoExecucao;
declaracao.posicaoAtual = 0;
const valorResolvido = await this.avaliar(declaracao.vetorOuDicionario);
let listaParaIterar;
try {
listaParaIterar = this.prepararListaParaIteracao(valorResolvido, declaracao);
}
catch (erro) {
this.erros.push({
erroInterno: erro,
linha: declaracao.linha,
hashArquivo: declaracao.hashArquivo,
});
return Promise.reject(erro);
}
while (!(retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.Quebra) &&
declaracao.posicaoAtual < listaParaIterar.length) {
try {
const elementoAtual = listaParaIterar[declaracao.posicaoAtual];
this.definirVariaveisIteracao(declaracao.variavelIteracao, elementoAtual);
retornoExecucao = await this.executar(declaracao.corpo);
if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.SustarQuebra)
return null;
if (retornoExecucao && retornoExecucao.valorRetornado instanceof quebras_1.ContinuarQuebra)
retornoExecucao = null;
declaracao.posicaoAtual++;
}
catch (erro) {
this.erros.push({
erroInterno: erro,
linha: declaracao.linha,
hashArquivo: declaracao.hashArquivo,
});
return Promise.reject(erro);
}
}
return retornoExecucao;
}
async visitarExpressaoRetornar(declaracao) {
let valor = null;
if (declaracao.valor !== null && declaracao.valor !== undefined) {
valor = await this.avaliar(declaracao.valor);
}
const retornoQuebra = new quebras_1.RetornoQuebra(valor, declaracao.tipo);
if (retornoQuebra.valor) {
const valorResolvido = this.resolverValor(retornoQuebra.valor);
const construtorRetorno = valorResolvido?.constructor?.name?.replaceAll('_', '') ?? '';
if (['DeleguaFuncao', 'ReferenciaMontao'].includes(construtorRetorno))
retornoQuebra.preservarEscopo = true;
}
return retornoQuebra;
}
async visitarDeclaracaoClasse(declaracao) {
const descritor = await super.visitarDeclaracaoClasse(declaracao);
descritor.sombrearPropriedadesDeClasse = true;
for (const propriedade of declaracao.propriedades) {
if (propriedade.estatico) {
const propriedadeNome = propriedade.nome.lexema;
const propriedadeValor = await this.avaliar(propriedade.valorInicial);
descritor.membrosEstaticos[propriedadeNome] = propriedadeValor;
}
}
return descritor;
}
async visitarExpressaoDefinirValor(expressao) {
const variavelObjeto = await this.avaliar(expressao.objeto);
const objeto = this.resolverValor(variavelObjeto, true);
if (objeto instanceof estruturas_1.DescritorTipoClasse) {
const valor = await this.avaliar(expressao.valor);
await objeto.definirEstatico(expressao.nome.lexema, valor, this);
return valor;
}
return super.visitarExpressaoDefinirValor(expressao);
}
async visitarExpressaoMorsa(expressao) {
let valor = await this.avaliar(expressao.valor);
if (valor && valor.hasOwnProperty('valorRetornado')) {
valor = valor.valorRetornado;
}
const valorResolvido = this.resolverValor(valor);
const simbolo = expressao.variavel.simbolo;
try {
this.pilhaEscoposExecucao.obterValorVariavel(simbolo);
this.pilhaEscoposExecucao.atribuirVariavel(simbolo, valorResolvido);
}
catch {
this.pilhaEscoposExecucao.definirVariavel(simbolo.lexema, valorResolvido);
}
return valorResolvido;
}
async visitarExpressaoBote(expressao) {
if (!(expressao.direita instanceof construtos_1.Chamada)) {
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao({
linha: expressao.linha,
hashArquivo: expressao.hashArquivo,
lexema: '~>'
}, 'O lado direito do operador bote (~>) deve ser uma chamada de função.', expressao.linha));
}
const chamadaOriginal = expressao.direita;
const novaChamada = new construtos_1.Chamada(chamadaOriginal.hashArquivo, chamadaOriginal.entidadeChamada, [expressao.esquerda, ...chamadaOriginal.argumentos]);
return await this.avaliar(novaChamada);
}
}
exports.InterpretadorPitugues = InterpretadorPitugues;
//# sourceMappingURL=interpretador-pitugues.js.map