@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
314 lines • 14.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DescritorTipoClasse = void 0;
const excecoes_1 = require("../../excecoes");
const chamavel_1 = require("./chamavel");
const metodo_polimorfico_1 = require("./metodo-polimorfico");
const objeto_delegua_classe_1 = require("./objeto-delegua-classe");
const mapaDeNormalizacao = {
numero: 'número',
logico: 'lógico',
funcao: 'função',
dicionario: 'dicionário',
modulo: 'módulo',
};
function normalizarTipo(tipo) {
if (!tipo || tipo === 'qualquer')
return 'qualquer';
const tipoMinusculo = tipo.toLowerCase();
return mapaDeNormalizacao[tipoMinusculo] || tipoMinusculo;
}
function assinaturasIguais(a, b) {
const paramsA = a.declaracao?.parametros || [];
const paramsB = b.declaracao?.parametros || [];
if (paramsA.length !== paramsB.length)
return false;
for (let i = 0; i < paramsA.length; i++) {
if (normalizarTipo(paramsA[i].tipoDado) !== normalizarTipo(paramsB[i].tipoDado)) {
return false;
}
}
return true;
}
/**
* Descritor de tipo de classe. Quando uma declaração de classe é visitada, o que
* vai para a pilha de escopos de execução é esta estrutura. Quando uma nova instância
* de classe é criada, a referência para a instância é implementada aqui.
*/
class DescritorTipoClasse extends chamavel_1.Chamavel {
/** Obtenedor de compat: primeiro pai direto (usado por tradutores e partes do interpretador). */
get superClasse() {
return this.superClasses[0] ?? null;
}
/** Definidor de compat: atribui um único pai direto (usado pela atribuição implícita de OBJETO_BASE). */
set superClasse(v) {
this.superClasses = v ? [v] : [];
}
constructor(simboloOriginal, superClasses, metodos, propriedades) {
super();
this.dialetoRequerExpansaoPropriedadesEspacoMemoria = false;
this.sombrearPropriedadesDeClasse = false;
this.simboloOriginal = simboloOriginal;
if (Array.isArray(superClasses)) {
this.superClasses = superClasses;
}
else if (superClasses) {
this.superClasses = [superClasses];
}
else {
this.superClasses = [];
}
this.orem = [this];
this.metodos = metodos || {};
this.metodosEstaticos = {};
this.membrosEstaticos = {};
this.obtenedores = {};
this.definidores = {};
this.obtenedoresEstaticos = {};
this.definidoresEstaticos = {};
this.propriedades = propriedades || [];
this.dialetoRequerDeclaracaoPropriedades = false;
this.abstrata = false;
this.estrangeira = false;
this.classeEstatica = false;
this.metodosAbstratos = [];
this.acessoMetodos = {};
this.acessoPropriedades = {};
}
// ─── C3 OReM ──────────────────────────────────────────────────────────────
static mesclaC3(listas) {
const resultado = [];
while (true) {
const listasNaoVazias = listas.filter((l) => l.length > 0);
if (listasNaoVazias.length === 0)
break;
let candidato = null;
for (const lista of listasNaoVazias) {
const cabeca = lista[0];
const naCauda = listasNaoVazias.some((l) => l.slice(1).indexOf(cabeca) >= 0);
if (!naCauda) {
candidato = cabeca;
break;
}
}
if (candidato === null) {
throw new excecoes_1.ErroEmTempoDeExecucao(undefined, 'Hierarquia de classes inconsistente: não foi possível calcular o OReM (C3).');
}
resultado.push(candidato);
for (const lista of listas) {
const idx = lista.indexOf(candidato);
if (idx === 0)
lista.shift();
}
}
return resultado;
}
/** Calcula e armazena o OReM (linearização C3) para esta classe e retorna a lista resultante. */
static computarOReM(cls) {
if (cls.superClasses.length === 0) {
return [cls];
}
const oremsDePais = cls.superClasses.map((p) => DescritorTipoClasse.computarOReM(p));
const listas = [...oremsDePais.map((m) => [...m]), [...cls.superClasses]];
return [cls, ...DescritorTipoClasse.mesclaC3(listas)];
}
// ─── Verificação abstrata ─────────────────────────────────────────────────
/**
* Verifica se todos os métodos abstratos da superclasse estão implementados
* na subclasse fornecida. Lança erro em tempo de execução se algum faltar.
*/
verificarImplementacaoAbstrata(subclasse) {
for (const nomeAbstrato of this.metodosAbstratos) {
const implementado = subclasse.metodos.hasOwnProperty(nomeAbstrato);
if (!implementado) {
throw new excecoes_1.ErroEmTempoDeExecucao(subclasse.simboloOriginal, `Classe '${subclasse.simboloOriginal?.lexema}' não implementa o método abstrato '${nomeAbstrato}' ` +
`da classe '${this.simboloOriginal?.lexema}'.`);
}
}
}
// ─── Obtenedores e definidores ────────────────────────────────────────────
encontrarObtenedor(nome, estatico = false) {
const mapa = estatico ? this.obtenedoresEstaticos : this.obtenedores;
if (Object.prototype.hasOwnProperty.call(mapa, nome)) {
return mapa[nome];
}
for (const ancestral of this.orem.slice(1)) {
const mapaAnc = estatico ? ancestral.obtenedoresEstaticos : ancestral.obtenedores;
if (Object.prototype.hasOwnProperty.call(mapaAnc, nome)) {
return mapaAnc[nome];
}
}
return undefined;
}
encontrarDefinidor(nome, estatico = false) {
const mapa = estatico ? this.definidoresEstaticos : this.definidores;
if (Object.prototype.hasOwnProperty.call(mapa, nome)) {
return mapa[nome];
}
for (const ancestral of this.orem.slice(1)) {
const mapaAnc = estatico ? ancestral.definidoresEstaticos : ancestral.definidores;
if (Object.prototype.hasOwnProperty.call(mapaAnc, nome)) {
return mapaAnc[nome];
}
}
return undefined;
}
// ─── Membros estáticos ────────────────────────────────────────────────────
async obterEstatico(nome, visitante) {
const obtenedor = this.encontrarObtenedor(nome, true);
if (obtenedor) {
if (!visitante) {
throw new excecoes_1.ErroEmTempoDeExecucao(this.simboloOriginal, `Obtenedor estático '${nome}' requer contexto de execução.`);
}
return await obtenedor.chamar(visitante, []);
}
if (Object.prototype.hasOwnProperty.call(this.metodosEstaticos, nome)) {
return this.metodosEstaticos[nome];
}
if (Object.prototype.hasOwnProperty.call(this.membrosEstaticos, nome)) {
return this.membrosEstaticos[nome];
}
throw new excecoes_1.ErroEmTempoDeExecucao(this.simboloOriginal, `Membro estático '${nome}' não encontrado na classe '${this.simboloOriginal?.lexema}'.`);
}
async definirEstatico(nome, valor, visitante) {
const definidor = this.encontrarDefinidor(nome, true);
if (definidor) {
if (!visitante) {
throw new excecoes_1.ErroEmTempoDeExecucao(this.simboloOriginal, `Definidor estático '${nome}' requer contexto de execução.`);
}
await definidor.chamar(visitante, [{ nome: null, valor }]);
return;
}
this.membrosEstaticos[nome] = valor;
}
// ─── Resolução de métodos via OReM ─────────────────────────────────────────
/**
* Mescla sobrecargas da classe atual com as de ancestrais (respeitando MRO).
* Sobrecargas da subclasse com mesma assinatura substituem as do ancestral.
*/
mesclarComSuperclasse(metodosAtuais, metodosSuperclasse) {
const resultado = [...metodosAtuais];
for (const metodoSuper of metodosSuperclasse) {
const jaSobrescrito = metodosAtuais.some((m) => assinaturasIguais(m, metodoSuper));
if (!jaSobrescrito) {
resultado.push(metodoSuper);
}
}
return resultado;
}
obterSobrecargasDaSuperclasse(nome) {
let sobrecarga = [];
// Percorre a OReM[1:] na ordem do OReM — o primeiro ancestral vence
for (const ancestral of this.orem.slice(1)) {
if (!ancestral.metodos.hasOwnProperty(nome))
continue;
const metodo = ancestral.metodos[nome];
const novos = Array.isArray(metodo) ? metodo : [metodo];
for (const m of novos) {
if (!sobrecarga.some((s) => assinaturasIguais(s, m))) {
sobrecarga.push(m);
}
}
}
return sobrecarga;
}
encontrarMetodo(nome) {
let metodosAtuais = [];
if (this.metodos.hasOwnProperty(nome)) {
const metodo = this.metodos[nome];
metodosAtuais = Array.isArray(metodo) ? metodo : [metodo];
}
const metodosSuperclasse = this.obterSobrecargasDaSuperclasse(nome);
let todasSobrecargas;
if (metodosAtuais.length > 0 && metodosSuperclasse.length > 0) {
todasSobrecargas = this.mesclarComSuperclasse(metodosAtuais, metodosSuperclasse);
}
else if (metodosAtuais.length > 0) {
todasSobrecargas = metodosAtuais;
}
else if (metodosSuperclasse.length > 0) {
todasSobrecargas = metodosSuperclasse;
}
else {
return undefined;
}
if (todasSobrecargas.length === 1) {
return todasSobrecargas[0];
}
return new metodo_polimorfico_1.MetodoPolimorfico(nome, todasSobrecargas);
}
encontrarPropriedade(nome) {
if (nome in this.propriedades) {
return this.propriedades[nome];
}
for (const ancestral of this.orem.slice(1)) {
if (nome in ancestral.propriedades) {
return ancestral.propriedades[nome];
}
}
if (this.dialetoRequerDeclaracaoPropriedades) {
throw new excecoes_1.ErroEmTempoDeExecucao(this.simboloOriginal, `Propriedade "${nome}" não declarada na classe ${this.simboloOriginal?.lexema}.`);
}
return undefined;
}
// ─── Representação textual ────────────────────────────────────────────────
/**
* Método utilizado por Delégua para representar esta classe quando impressa.
* @returns {string} A representação da classe como texto.
*/
paraTexto() {
const nome = this.simboloOriginal?.lexema ?? 'Objeto';
const nomesMetodos = Object.keys(this.metodos).join(', ');
const nomesPropriedades = this.propriedades.map((p) => p.nome.lexema).join(', ');
return `<[ ${nome} estático métodos=[${nomesMetodos}] propriedades=[${nomesPropriedades}] ]>`;
}
/**
* Método utilizado pelo VSCode para inspecionar esta classe em depuração.
* @returns {string} A representação da classe como texto.
*/
toString() {
return this.paraTexto();
}
aridade() {
const inicializador = this.encontrarMetodo('construtor');
if (inicializador instanceof metodo_polimorfico_1.MetodoPolimorfico) {
return inicializador.aridade();
}
return inicializador ? inicializador.aridade() : 0;
}
async chamar(visitante, argumentos) {
if (this.classeEstatica) {
throw new excecoes_1.ErroEmTempoDeExecucao(this.simboloOriginal, `Não é possível instanciar a classe estática '${this.simboloOriginal?.lexema}'.`);
}
if (this.abstrata) {
throw new excecoes_1.ErroEmTempoDeExecucao(this.simboloOriginal, `Não é possível instanciar a classe abstrata '${this.simboloOriginal?.lexema}'.`);
}
if (this.estrangeira) {
throw new excecoes_1.ErroEmTempoDeExecucao(this.simboloOriginal, `Não é possível instanciar a classe estrangeira '${this.simboloOriginal?.lexema}' diretamente.`);
}
const instancia = new objeto_delegua_classe_1.ObjetoDeleguaClasse(this);
const inicializador = this.encontrarMetodo('construtor');
if (inicializador) {
if (inicializador instanceof metodo_polimorfico_1.MetodoPolimorfico) {
const construtorVinculado = inicializador.funcaoPorMetodoDeClasse(instancia);
await construtorVinculado.chamar(visitante, argumentos);
}
else {
// Para construtor não polimórfico, completar os argumentos
// não preenchidos com valores indefinidos.
const aridadeConstrutor = inicializador.aridade();
if (argumentos.length < aridadeConstrutor) {
const diferenca = aridadeConstrutor - argumentos.length;
for (let i = 0; i < diferenca; i++) {
argumentos.push({ nome: null, valor: null });
}
}
const metodoConstrutor = inicializador.funcaoPorMetodoDeClasse(instancia);
await metodoConstrutor.chamar(visitante, argumentos);
}
}
return instancia;
}
}
exports.DescritorTipoClasse = DescritorTipoClasse;
//# sourceMappingURL=descritor-tipo-classe.js.map