UNPKG

@designliquido/delegua

Version:

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

189 lines 7.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DeleguaFuncao = void 0; const chamavel_1 = require("./chamavel"); const espaco_memoria_1 = require("../espaco-memoria"); const quebras_1 = require("../../quebras"); const objeto_delegua_classe_1 = require("./objeto-delegua-classe"); const declaracoes_1 = require("../../declaracoes"); /** * Qualquer função declarada em código é uma DeleguaFuncao. */ class DeleguaFuncao extends chamavel_1.Chamavel { constructor(nome, declaracao, instancia = undefined, eInicializador = false) { super(); /** Classe que declarou este método; usado para calcular a posição no OReM em chamadas a `super()`. */ this.classeDefinidora = null; this.nome = nome; this.declaracao = declaracao; this.instancia = instancia; this.eInicializador = eInicializador; } aridade() { return this.declaracao?.parametros?.filter((p) => p.abrangencia !== 'multiplo').length || 0; } /** * Método utilizado por Delégua para representar esta função quando impressa. * @returns {string} A representação da função como texto. */ paraTexto() { if (!this.nome) return '<função />'; let resultado = `<função nome=${this.nome}`; let parametros = ''; let retorno = ''; for (let parametro of this.declaracao.parametros) { parametros += `${parametro.nome.lexema}: ${parametro.tipoDado || 'qualquer'}, `; } if (this.declaracao.parametros.length > 0) { parametros = `argumentos=<${parametros.slice(0, -2)}>`; } const retorna = this.declaracao.corpo.filter((c) => c instanceof declaracoes_1.Retorna)[0]; if (retorna instanceof declaracoes_1.Retorna) { const valor = retorna?.valor?.valor; retorno = `retorna=<${typeof valor === 'number' ? valor : `'${valor}'`}>`; } if (parametros) { resultado += ` ${parametros}`; } if (retorno) { resultado += ` ${retorno}`; } resultado += ' />'; return resultado; } /** * Método utilizado pelo VSCode para inspecionar esta função em depuração. * @returns {string} A representação da função como texto. */ toString() { return this.paraTexto(); } resolverParametrosEspalhados(argumentos, indiceArgumentoAtual) { const argumentosResolvidos = []; for (let i = indiceArgumentoAtual; i < argumentos.length; i++) { const argumentoAtual = argumentos[i]; argumentosResolvidos.push(argumentoAtual && argumentoAtual.hasOwnProperty('valor') ? argumentoAtual.valor : argumentoAtual); } return argumentosResolvidos; } resolverAmbiente(argumentos) { const ambiente = new espaco_memoria_1.EspacoMemoria(); const parametros = this.declaracao.parametros || []; for (let i = 0; i < parametros.length; i++) { const parametro = parametros[i]; const nome = parametro['nome'].lexema; if (parametro.abrangencia === 'multiplo') { const argumentosResolvidos = this.resolverParametrosEspalhados(argumentos, i); // TODO: Verificar se `imutavel` é `true` aqui mesmo. ambiente.valores[nome] = { tipo: 'vetor', valor: argumentosResolvidos, imutavel: true, }; } else { let argumento = argumentos[i]; if (argumento.valor === null) { argumentos[i].valor = parametro.valorPadrao ? parametro.valorPadrao : null; } ambiente.valores[nome] = argumento && argumento.hasOwnProperty('valor') ? argumento.valor : argumento; // Se o argumento é `DeleguaFuncao`, para habilitar o recurso de _currying_, // copiamos seu valor para o escopo atual. Nem sempre podemos contar com a tipagem explícita aqui. if (argumento.valor && ['funcao', 'função'].includes(argumento.valor.tipo)) { parametro.referencia = true; } } } return ambiente; } async chamar(visitante, argumentos) { const ambiente = this.resolverAmbiente(argumentos); if (this.instancia !== undefined) { ambiente.valores['isto'] = { valor: this.instancia, tipo: this.instancia instanceof objeto_delegua_classe_1.ObjetoDeleguaClasse ? 'objeto' : tipoDeDados(this.instancia), imutavel: false, }; if (this.classeDefinidora) { ambiente.valores['classeExecutora'] = { valor: this.classeDefinidora, tipo: 'classe', imutavel: true, }; } } const interpretador = visitante; interpretador.proximoEscopo = 'funcao'; // Rastrear a classe atual em execução para verificação de acesso. const classeAnteriorEmExecucao = interpretador.classeAtualEmExecucao; if (this.instancia instanceof objeto_delegua_classe_1.ObjetoDeleguaClasse) { interpretador.classeAtualEmExecucao = this.instancia.classe; } let retornoBloco; try { retornoBloco = await interpretador.executarBloco(this.declaracao.corpo, ambiente); } finally { interpretador.classeAtualEmExecucao = classeAnteriorEmExecucao; } const referencias = this.declaracao.parametros .map((p, indice) => { if (p.referencia) { return { indice: indice, parametro: p, }; } }) .filter((r) => r); const pilha = interpretador.pilhaEscoposExecucao; for (let referencia of referencias) { let argumentoReferencia = ambiente.valores[referencia.parametro.nome.lexema]; pilha.definirVariavel(referencia.parametro.nome.lexema, argumentoReferencia.valor); } if (retornoBloco instanceof quebras_1.RetornoQuebra) { return retornoBloco.valor; } if (this.eInicializador) { return this.instancia; } return retornoBloco; } funcaoPorMetodoDeClasse(instancia) { const funcao = new DeleguaFuncao(this.nome, this.declaracao, instancia, this.eInicializador); funcao.documentacao = this.documentacao; funcao.classeDefinidora = this.classeDefinidora; return funcao; } funcaoPorExtensao(valor) { const funcao = new DeleguaFuncao(this.nome, this.declaracao, valor, false); funcao.documentacao = this.documentacao; return funcao; } } exports.DeleguaFuncao = DeleguaFuncao; /** * Mapeia o tipo JS de um valor primitivo para o nome de tipo de Delégua. */ function tipoDeDados(valor) { if (Array.isArray(valor)) return 'vetor'; switch (typeof valor) { case 'number': case 'bigint': return 'número'; case 'string': return 'texto'; case 'boolean': return 'lógico'; default: return 'objeto'; } } //# sourceMappingURL=delegua-funcao.js.map