@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
181 lines • 8.68 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ObjetoDeleguaClasse = void 0;
const excecoes_1 = require("../../excecoes");
/**
* A instância de uma classe em Delégua.
* Possui propriedades e métodos. Propriedades são definidas localmente.
* Métodos são extraídos do descritor da classe, uma `DeleguaClasse`.
*/
class ObjetoDeleguaClasse {
valorPadraoParaTipo(tipo) {
switch (tipo) {
case 'numero':
return 0;
case 'texto':
return '';
case 'logico':
return false;
default:
return undefined;
}
}
constructor(classe) {
this.classe = classe;
this.propriedades = {};
// Inicializar propriedades herdadas do OReM (do mais genérico ao mais específico)
// para que ancestrais mais próximos sobrescrevam os mais distantes.
const ancestrais = classe.orem.slice(1).reverse();
for (const ancestral of ancestrais) {
for (const propriedade of ancestral.propriedades) {
if (propriedade.estatico)
continue;
if (propriedade.autoObter || propriedade.autoDefinir) {
this.propriedades['_' + propriedade.nome.lexema] = this.valorPadraoParaTipo(propriedade.tipo);
}
else {
this.propriedades[propriedade.nome.lexema] = this.valorPadraoParaTipo(propriedade.tipo);
}
}
}
// Propriedades da própria classe (têm prioridade máxima no valor padrão)
for (const propriedade of classe.propriedades) {
if (propriedade.estatico)
continue;
if (propriedade.autoObter || propriedade.autoDefinir) {
this.propriedades['_' + propriedade.nome.lexema] = this.valorPadraoParaTipo(propriedade.tipo);
}
else {
this.propriedades[propriedade.nome.lexema] = this.valorPadraoParaTipo(propriedade.tipo);
}
}
}
verificarAcessoLeitura(nome, simbolo, visitante) {
// Percorre o OReM para encontrar a classe que declarou o membro com um modificador de acesso.
let declaradorClasse = undefined;
let acesso = undefined;
for (const cls of this.classe.orem) {
const acessoCls = cls.acessoPropriedades?.[nome] ?? cls.acessoMetodos?.[nome];
if (acessoCls) {
acesso = acessoCls;
declaradorClasse = cls;
break;
}
}
if (!acesso || acesso === 'publico')
return;
const classeAtual = visitante?.classeAtualEmExecucao;
if (acesso === 'privado') {
if (classeAtual !== declaradorClasse) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Membro '${nome}' é privado e não pode ser acessado fora da classe '${declaradorClasse.simboloOriginal?.lexema}'.`);
}
}
else if (acesso === 'protegido') {
const eAcessivel = classeAtual?.orem?.includes(declaradorClasse) ?? false;
if (!eAcessivel) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Membro '${nome}' é protegido e não pode ser acessado fora da hierarquia da classe '${declaradorClasse.simboloOriginal?.lexema}'.`);
}
}
}
async obter(simbolo, visitante) {
const obtenedor = this.classe.encontrarObtenedor(simbolo.lexema);
if (obtenedor) {
if (!visitante) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Obtenedor '${simbolo.lexema}' requer contexto de execução.`);
}
const metodoObtenedor = obtenedor.funcaoPorMetodoDeClasse(this);
return await metodoObtenedor.chamar(visitante, []);
}
// Auto-property: acessa o campo de armazenamento interno '_nome'
const propAuto = this.classe.propriedades.find((p) => p.nome.lexema === simbolo.lexema);
if (propAuto?.autoObter) {
this.verificarAcessoLeitura(simbolo.lexema, simbolo, visitante);
return this.propriedades['_' + simbolo.lexema];
}
if (propAuto && propAuto.autoDefinir && !propAuto.autoObter) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Propriedade '${simbolo.lexema}' é somente-escrita.`);
}
if (this.propriedades.hasOwnProperty(simbolo.lexema)) {
this.verificarAcessoLeitura(simbolo.lexema, simbolo, visitante);
return this.propriedades[simbolo.lexema];
}
if (Object.prototype.hasOwnProperty.call(this.classe.membrosEstaticos, simbolo.lexema)) {
return await this.classe.obterEstatico(simbolo.lexema, visitante);
}
const metodo = this.classe.encontrarMetodo(simbolo.lexema);
if (metodo) {
this.verificarAcessoLeitura(simbolo.lexema, simbolo, visitante);
return metodo.funcaoPorMetodoDeClasse(this);
}
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Método ou propriedade "${simbolo.lexema}" não existe neste objeto.`);
}
obterMetodo(nomeMetodo) {
const metodo = this.classe.encontrarMetodo(nomeMetodo);
if (metodo)
return metodo.funcaoPorMetodoDeClasse(this);
throw new excecoes_1.ErroEmTempoDeExecucao(null, `Método "${nomeMetodo}" não existe neste objeto.`);
}
obterPropriedade(nomePropriedade) {
if (this.propriedades.hasOwnProperty(nomePropriedade)) {
return this.propriedades[nomePropriedade];
}
throw new excecoes_1.ErroEmTempoDeExecucao(null, `Propriedade "${nomePropriedade}" não existe neste objeto.`);
}
async definir(simbolo, valor, visitante) {
const definidor = this.classe.encontrarDefinidor(simbolo.lexema);
if (definidor) {
if (!visitante) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Definidor '${simbolo.lexema}' requer contexto de execução.`);
}
const metodoDefinidor = definidor.funcaoPorMetodoDeClasse(this);
await metodoDefinidor.chamar(visitante, [{ nome: null, valor }]);
return;
}
// Auto-property: armazena no campo interno '_nome'
const propAuto = this.classe.propriedades.find((p) => p.nome.lexema === simbolo.lexema);
if (propAuto?.autoDefinir) {
this.verificarAcessoLeitura(simbolo.lexema, simbolo, visitante);
this.propriedades['_' + simbolo.lexema] = valor;
return;
}
if (propAuto && propAuto.autoObter && !propAuto.autoDefinir) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Propriedade '${simbolo.lexema}' é somente-leitura.`);
}
if (Object.prototype.hasOwnProperty.call(this.classe.membrosEstaticos, simbolo.lexema)) {
if (this.classe.sombrearPropriedadesDeClasse) {
this.verificarAcessoLeitura(simbolo.lexema, simbolo, visitante);
this.propriedades[simbolo.lexema] = valor;
}
else {
await this.classe.definirEstatico(simbolo.lexema, valor, visitante);
}
return;
}
// Verificar acesso antes da atribuição.
this.verificarAcessoLeitura(simbolo.lexema, simbolo, visitante);
if (this.classe.dialetoRequerDeclaracaoPropriedades &&
!this.propriedades.hasOwnProperty(simbolo.lexema)) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Propriedade "${simbolo.lexema}" não foi definida na declaração da classe ${this.classe.simboloOriginal.lexema}.`);
}
this.propriedades[simbolo.lexema] = valor;
}
/**
* Método utilizado por Delégua para inspecionar este objeto em depuração.
* @returns {string} A representação do objeto como texto.
*/
paraTexto() {
const nome = this.classe.simboloOriginal?.lexema ?? 'Objeto';
const nomesMetodos = Object.keys(this.classe.metodos).join(', ');
const nomesPropriedades = Object.keys(this.propriedades).join(', ');
return `<[ ${nome} métodos=[${nomesMetodos}] propriedades=[${nomesPropriedades}] ]>`;
}
/**
* Método utilizado pelo VSCode para representar este objeto quando impresso.
* @returns {string} A representação do objeto como texto.
*/
toString() {
return this.paraTexto();
}
}
exports.ObjetoDeleguaClasse = ObjetoDeleguaClasse;
//# sourceMappingURL=objeto-delegua-classe.js.map