@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
340 lines • 15.2 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PilhaEscoposExecucao = void 0;
const estruturas_1 = require("./estruturas");
const excecoes_1 = require("../excecoes");
const lexador_1 = require("../lexador");
const inferenciador_1 = require("../inferenciador");
const delegua_1 = __importDefault(require("../tipos-de-dados/delegua"));
const tiposNumericos = ['inteiro', 'número', 'numero', 'real', 'longo'];
const tiposLogicos = ['logico', 'lógico'];
class PilhaEscoposExecucao {
constructor() {
this.pilha = [];
}
empilhar(item) {
this.pilha.push(item);
}
eVazio() {
return this.pilha.length === 0;
}
elementos() {
return this.pilha.length;
}
naPosicao(posicao) {
return this.pilha[posicao];
}
topoDaPilha() {
if (this.eVazio())
throw new Error('Pilha vazia.');
return this.pilha[this.pilha.length - 1];
}
removerUltimo() {
if (this.eVazio())
throw new Error('Pilha vazia.');
return this.pilha.pop();
}
tiposCompativeis(tipoVariavel, tipoValor) {
if (tipoVariavel === tipoValor)
return true;
if (tiposNumericos.includes(tipoVariavel) && tiposNumericos.includes(tipoValor))
return true;
if (tiposLogicos.includes(tipoVariavel) && tiposLogicos.includes(tipoValor))
return true;
return false;
}
converterValor(tipo, valor) {
switch (tipo) {
case 'inteiro':
return parseInt(valor);
case 'longo':
// Converte para BigInt
if (typeof valor === 'bigint')
return valor;
if (typeof valor === 'number')
return BigInt(Math.floor(valor));
// Para strings, remove parte decimal antes de converter
const strValue = String(valor).split('.')[0].trim();
return BigInt(strValue || '0');
case 'logico':
case 'lógico':
return Boolean(valor);
case 'numero':
case 'número':
// Não converter objetos (ex: instâncias de classe de sobrecarga de operador)
// para número, pois resultaria em NaN.
if (typeof valor === 'object' && valor !== null) {
return valor;
}
return Number(valor);
case 'texto':
return String(valor);
default:
return valor;
}
}
definirConstante(nomeConstante, valor, tipo) {
const constante = this.pilha[this.pilha.length - 1].espacoMemoria.valores[nomeConstante];
let tipoConstante;
if (constante && constante.hasOwnProperty('tipo')) {
tipoConstante = constante.tipo;
}
else if (tipo) {
tipoConstante = tipo;
}
else {
tipoConstante = (0, inferenciador_1.inferirTipoVariavel)(valor);
}
let elementoAlvo = {
valor: this.converterValor(tipo, valor),
tipo: tipoConstante,
subtipo: undefined,
imutavel: true,
};
if ([delegua_1.default.VETOR, delegua_1.default.TUPLA].includes(tipoConstante)) {
let subtipo = '';
if (valor instanceof Array) {
const numeros = valor.some((v) => typeof v === 'number');
const textos = valor.some((v) => typeof v === 'string');
const logicos = valor.some((v) => typeof v === 'boolean');
const tiposDistintos = [numeros, textos, logicos].filter(Boolean).length;
if (tiposDistintos > 1)
subtipo = delegua_1.default.QUALQUER;
else if (numeros)
subtipo = delegua_1.default.NUMERO;
else if (logicos)
subtipo = delegua_1.default.LOGICO;
else if (textos)
subtipo = delegua_1.default.TEXTO;
else
subtipo = delegua_1.default.QUALQUER;
}
elementoAlvo.subtipo = subtipo;
}
this.pilha[this.pilha.length - 1].espacoMemoria.valores[nomeConstante] = elementoAlvo;
}
definirVariavel(nomeVariavel, valor, tipo, tipoExplicito) {
const variavel = this.pilha[this.pilha.length - 1].espacoMemoria.valores[nomeVariavel];
let tipoVariavel;
let subtipo = undefined;
if (variavel && variavel.hasOwnProperty('tipo')) {
tipoVariavel = variavel.tipo;
}
else if (valor && valor.constructor === estruturas_1.DeleguaFuncao) {
tipoVariavel = 'função';
if (tipo !== undefined) {
tipoVariavel = `função<${tipo}>`;
subtipo = tipo;
}
}
else if (tipo) {
tipoVariavel = tipo;
}
else {
tipoVariavel = (0, inferenciador_1.inferirTipoVariavel)(valor);
}
let elementoAlvo = {
valor: this.converterValor(tipoVariavel, valor),
tipo: tipoVariavel,
subtipo: subtipo,
imutavel: false,
tipoExplicito: tipoExplicito || false,
};
if ([delegua_1.default.VETOR, delegua_1.default.TUPLA].includes(tipoVariavel)) {
let subtipo = '';
if (valor instanceof Array) {
const numeros = valor.some((v) => typeof v === 'number');
const textos = valor.some((v) => typeof v === 'string');
const logicos = valor.some((v) => typeof v === 'boolean');
const tiposDistintos = [numeros, textos, logicos].filter(Boolean).length;
if (tiposDistintos > 1)
subtipo = delegua_1.default.QUALQUER;
else if (numeros)
subtipo = delegua_1.default.NUMERO;
else if (logicos)
subtipo = delegua_1.default.LOGICO;
else if (textos)
subtipo = delegua_1.default.TEXTO;
else
subtipo = delegua_1.default.QUALQUER;
}
elementoAlvo.subtipo = subtipo;
}
this.pilha[this.pilha.length - 1].espacoMemoria.valores[nomeVariavel] = elementoAlvo;
}
atribuirVariavelEm(distancia, simbolo, valor) {
const espacoMemoriaAncestral = this.pilha[this.pilha.length - distancia].espacoMemoria;
const variavel = espacoMemoriaAncestral.valores[simbolo.lexema];
if (variavel.imutavel) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Constante '${simbolo.lexema}' não pode receber novos valores.`);
}
if (variavel.tipoExplicito && variavel.tipo !== 'qualquer') {
const tipoDoValor = (0, inferenciador_1.inferirTipoVariavel)(valor);
if (!this.tiposCompativeis(variavel.tipo, tipoDoValor)) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Variável '${simbolo.lexema}' é do tipo '${variavel.tipo}' e não pode receber um valor do tipo '${tipoDoValor}'.`);
}
}
espacoMemoriaAncestral.valores[simbolo.lexema] = {
valor,
tipo: variavel.tipo === 'nulo' && !variavel.tipoExplicito
? (0, inferenciador_1.inferirTipoVariavel)(valor)
: variavel.tipo || (0, inferenciador_1.inferirTipoVariavel)(valor),
imutavel: false,
tipoExplicito: variavel.tipoExplicito,
};
}
atribuirVariavel(simbolo, valor, indice) {
for (let i = 1; i <= this.pilha.length; i++) {
const espacoMemoria = this.pilha[this.pilha.length - i].espacoMemoria;
if (espacoMemoria.valores[simbolo.lexema] !== undefined) {
const variavel = espacoMemoria.valores[simbolo.lexema];
if (variavel.imutavel) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Constante '${simbolo.lexema}' não pode receber novos valores.`);
}
if (variavel.tipoExplicito && variavel.tipo !== 'qualquer') {
const tipoDoValor = (0, inferenciador_1.inferirTipoVariavel)(valor);
if (!this.tiposCompativeis(variavel.tipo, tipoDoValor)) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Variável '${simbolo.lexema}' é do tipo '${variavel.tipo}' e não pode receber um valor do tipo '${tipoDoValor}'.`);
}
}
const tipoAtual = variavel && variavel.hasOwnProperty('tipo') ? variavel.tipo : null;
const deveLiberarTipo = tipoAtual === 'nulo' && !variavel.tipoExplicito;
const tipoInferido = tipoAtual && !deveLiberarTipo ? tipoAtual : (0, inferenciador_1.inferirTipoVariavel)(valor);
const tipo = (tipoInferido || 'objeto').toLowerCase();
const valorResolvido = this.converterValor(tipo, valor);
if (indice !== undefined && indice !== null) {
let variavelValor = variavel.valor;
if (variavelValor instanceof Array || variavelValor instanceof Object) {
variavelValor[indice] = valorResolvido;
}
else {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, 'Variável não é um vetor ou dicionário.');
}
}
else {
espacoMemoria.valores[simbolo.lexema] = {
valor: valorResolvido,
tipo,
imutavel: false,
tipoExplicito: variavel.tipoExplicito,
};
}
return;
}
}
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, "Variável não definida '" + simbolo.lexema + "'.");
}
obterEscopoPorTipo(tipo) {
for (let i = 1; i <= this.pilha.length; i++) {
const escopoAtual = this.pilha[this.pilha.length - i];
if (escopoAtual.tipo === tipo) {
return escopoAtual;
}
}
return undefined;
}
obterVariavelEm(distancia, nome) {
const ambienteAncestral = this.pilha[this.pilha.length - distancia].espacoMemoria;
return ambienteAncestral.valores[nome];
}
obterValorVariavel(simbolo) {
for (let i = 1; i <= this.pilha.length; i++) {
const ambiente = this.pilha[this.pilha.length - i].espacoMemoria;
if (ambiente.valores[simbolo.lexema] !== undefined) {
return ambiente.valores[simbolo.lexema];
}
}
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, "Variável não definida: '" + simbolo.lexema + "'.");
}
obterVariavelPorNome(nome) {
for (let i = 1; i <= this.pilha.length; i++) {
const ambiente = this.pilha[this.pilha.length - i].espacoMemoria;
if (ambiente.valores[nome] !== undefined) {
return ambiente.valores[nome];
}
}
throw new excecoes_1.ErroEmTempoDeExecucao(new lexador_1.Simbolo('especial', nome, nome, -1, -1), "Variável não definida: '" + nome + "'.");
}
/**
* Método usado pelo depurador para obter todas as variáveis definidas.
*/
obterTodasVariaveis(todasVariaveis = []) {
for (let i = 1; i <= this.pilha.length - 1; i++) {
const valoresEspacoMemoria = this.pilha[this.pilha.length - i].espacoMemoria.valores;
const vetorObjeto = Object.entries(valoresEspacoMemoria).map((chaveEValor, indice) => ({
nome: chaveEValor[0],
valor: chaveEValor[1].valor,
tipo: chaveEValor[1].tipo,
imutavel: chaveEValor[1].imutavel,
}));
todasVariaveis = todasVariaveis.concat(vetorObjeto);
}
return todasVariaveis;
}
/**
* Obtém todas as funções declaradas ou por código-fonte, ou pelo desenvolvedor
* em console, do último escopo.
*/
obterTodasDeleguaFuncao() {
const retorno = {};
const espacoMemoria = this.pilha[this.pilha.length - 1].espacoMemoria;
for (const [nome, corpo] of Object.entries(espacoMemoria.valores)) {
const corpoValor = corpo.hasOwnProperty('valor') ? corpo.valor : corpo;
if (corpoValor instanceof estruturas_1.DeleguaFuncao) {
retorno[nome] = corpoValor;
}
}
return retorno;
}
/**
* Obtém todas as declarações de classe do último escopo.
* @returns
*/
obterTodasDeclaracoesClasse() {
const retorno = {};
const ambiente = this.pilha[this.pilha.length - 1].espacoMemoria;
for (const [nome, corpo] of Object.entries(ambiente.valores)) {
const corpoValor = corpo.hasOwnProperty('valor') ? corpo.valor : corpo;
if (corpoValor instanceof estruturas_1.DescritorTipoClasse) {
retorno[nome] = corpoValor;
}
}
return retorno;
}
registrarReferenciaFuncao(idFuncao, funcao) {
const espacoMemoriaAtual = this.pilha[this.pilha.length - 1].espacoMemoria;
espacoMemoriaAtual.referenciasFuncoes[idFuncao] = funcao;
}
obterReferenciaFuncao(idFuncao) {
for (let i = 1; i <= this.pilha.length; i++) {
const espacoMemoria = this.pilha[this.pilha.length - i].espacoMemoria;
if (espacoMemoria.referenciasFuncoes[idFuncao] !== undefined) {
return espacoMemoria.referenciasFuncoes[idFuncao];
}
}
throw new excecoes_1.ErroEmTempoDeExecucao(new lexador_1.Simbolo('especial', idFuncao, idFuncao, -1, -1), "Referência para função não encontrada: '" + idFuncao + "'.");
}
registrarReferenciaMontao(endereco) {
const espacoMemoria = this.pilha[this.pilha.length - 1].espacoMemoria;
espacoMemoria.enderecosMontao.add(endereco);
}
migrarReferenciaMontaoParaEscopoDeVariavel(nomeVariavel, enderecoMontao) {
// TODO: Normalmente uma referência a ser migrada está sempre no último escopo.
// Conferir se é sempre este o caso.
const ultimoEspacoMemoria = this.pilha[this.pilha.length - 1].espacoMemoria;
ultimoEspacoMemoria.enderecosMontao.delete(enderecoMontao);
for (let i = 1; i <= this.pilha.length; i++) {
const espacoMemoria = this.pilha[this.pilha.length - i].espacoMemoria;
if (espacoMemoria.valores[nomeVariavel] !== undefined) {
espacoMemoria.enderecosMontao.add(enderecoMontao);
break;
}
}
// TODO: Devemos emitir erro em algum momento?
}
}
exports.PilhaEscoposExecucao = PilhaEscoposExecucao;
//# sourceMappingURL=pilha-escopos-execucao.js.map