UNPKG

@designliquido/delegua

Version:

Linguagem de programação simples e moderna usando português estruturado.

340 lines 17 kB
"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