@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
177 lines • 9.09 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) {
var _a, _b, _c, _d, _e, _f, _g;
// 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 = (_b = (_a = cls.acessoPropriedades) === null || _a === void 0 ? void 0 : _a[nome]) !== null && _b !== void 0 ? _b : (_c = cls.acessoMetodos) === null || _c === void 0 ? void 0 : _c[nome];
if (acessoCls) {
acesso = acessoCls;
declaradorClasse = cls;
break;
}
}
if (!acesso || acesso === 'publico')
return;
const classeAtual = visitante === null || visitante === void 0 ? void 0 : 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 '${(_d = declaradorClasse.simboloOriginal) === null || _d === void 0 ? void 0 : _d.lexema}'.`);
}
}
else if (acesso === 'protegido') {
const eAcessivel = (_f = (_e = classeAtual === null || classeAtual === void 0 ? void 0 : classeAtual.orem) === null || _e === void 0 ? void 0 : _e.includes(declaradorClasse)) !== null && _f !== void 0 ? _f : false;
if (!eAcessivel) {
throw new excecoes_1.ErroEmTempoDeExecucao(simbolo, `Membro '${nome}' é protegido e não pode ser acessado fora da hierarquia da classe '${(_g = declaradorClasse.simboloOriginal) === null || _g === void 0 ? void 0 : _g.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 === null || propAuto === void 0 ? void 0 : 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 === null || propAuto === void 0 ? void 0 : 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)) {
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() {
var _a, _b;
const nome = (_b = (_a = this.classe.simboloOriginal) === null || _a === void 0 ? void 0 : _a.lexema) !== null && _b !== void 0 ? _b : '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