@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
189 lines • 7.56 kB
JavaScript
"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