@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
280 lines • 15.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Interpretador = void 0;
const estruturas_1 = require("./estruturas");
const interpretador_base_1 = require("./interpretador-base");
const inferenciador_1 = require("../inferenciador");
const excecoes_1 = require("../excecoes");
const quebras_1 = require("../quebras");
const primitivas_dicionario_1 = __importDefault(require("../bibliotecas/primitivas-dicionario"));
const primitivas_numero_1 = __importDefault(require("../bibliotecas/primitivas-numero"));
const primitivas_texto_1 = __importDefault(require("../bibliotecas/primitivas-texto"));
const primitivas_vetor_1 = __importDefault(require("../bibliotecas/primitivas-vetor"));
const primitivos_1 = __importDefault(require("../tipos-de-dados/primitivos"));
const delegua_1 = __importDefault(require("../tipos-de-dados/delegua"));
/**
* O interpretador de Delégua.
*/
class Interpretador extends interpretador_base_1.InterpretadorBase {
visitarDeclaracaoDefinicaoFuncao(declaracao) {
const funcao = new estruturas_1.DeleguaFuncao(declaracao.simbolo.lexema, declaracao.funcao);
// TODO: Depreciar essa abordagem a favor do uso por referências.
this.pilhaEscoposExecucao.definirVariavel(declaracao.simbolo.lexema, funcao);
this.pilhaEscoposExecucao.registrarReferenciaFuncao(declaracao.id, funcao);
}
async visitarExpressaoAcessoMetodo(expressao) {
let variavelObjeto = await this.avaliar(expressao.objeto);
// Este caso acontece quando há encadeamento de métodos.
// Por exemplo, `objeto1.metodo1().metodo2()`.
// Como `RetornoQuebra` também possui `valor`, precisamos extrair o
// valor dele primeiro.
if (variavelObjeto.constructor.name === 'RetornoQuebra') {
variavelObjeto = variavelObjeto.valor;
}
const objeto = variavelObjeto.hasOwnProperty('valor') ? variavelObjeto.valor : variavelObjeto;
if (objeto.constructor.name === 'ObjetoDeleguaClasse') {
return objeto.obterMetodo(expressao.nomeMetodo) || null;
}
// Objeto simples do JavaScript, ou dicionário de Delégua.
if (objeto.constructor === Object) {
if (expressao.nomeMetodo in primitivas_dicionario_1.default) {
const metodoDePrimitivaDicionario = primitivas_dicionario_1.default[expressao.nomeMetodo].implementacao;
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaDicionario);
}
return objeto[expressao.nomeMetodo] || null;
}
// Casos em que o objeto possui algum outro tipo que não o de objeto simples.
// Normalmente executam quando uma biblioteca é importada, e estamos tentando
// obter alguma propriedade ou método desse objeto.
// Caso 1: Função tradicional do JavaScript.
if (typeof objeto[expressao.nomeMetodo] === primitivos_1.default.FUNCAO) {
return objeto[expressao.nomeMetodo];
}
// Caso 2: Objeto tradicional do JavaScript.
if (typeof objeto[expressao.nomeMetodo] === primitivos_1.default.OBJETO) {
return objeto[expressao.nomeMetodo];
}
// A partir daqui, presume-se que o objeto é uma das estruturas
// de Delégua.
if (objeto instanceof estruturas_1.DeleguaModulo) {
return objeto.componentes[expressao.nomeMetodo] || null;
}
let tipoObjeto = variavelObjeto.tipo;
if (tipoObjeto === null || tipoObjeto === undefined) {
tipoObjeto = (0, inferenciador_1.inferirTipoVariavel)(variavelObjeto);
}
// Como internamente um dicionário de Delégua é simplesmente um objeto de
// JavaScript, as primitivas de dicionário, especificamente, são tratadas
// mais acima.
switch (tipoObjeto) {
case delegua_1.default.INTEIRO:
case delegua_1.default.NUMERO:
case delegua_1.default.NÚMERO:
const metodoDePrimitivaNumero = primitivas_numero_1.default[expressao.nomeMetodo].implementacao;
if (metodoDePrimitivaNumero) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaNumero);
}
break;
case delegua_1.default.TEXTO:
const metodoDePrimitivaTexto = primitivas_texto_1.default[expressao.nomeMetodo].implementacao;
if (metodoDePrimitivaTexto) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaTexto);
}
break;
case delegua_1.default.VETOR:
case delegua_1.default.VETOR_NUMERO:
case delegua_1.default.VETOR_NÚMERO:
case delegua_1.default.VETOR_TEXTO:
const metodoDePrimitivaVetor = primitivas_vetor_1.default[expressao.nomeMetodo].implementacao;
if (metodoDePrimitivaVetor) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaVetor);
}
break;
}
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `Método para objeto ou primitiva não encontrado: ${expressao.nomeMetodo}.`, expressao.linha));
}
/**
* Casos que ocorrem aqui:
*
* - Quando o método ou propriedade é ou 'qualquer', ou vetor
* de 'qualquer' ('qualquer[]'), e uma primitiva é usada.
* - Quando o objeto é uma classe definida em código.
* @param {AcessoMetodoOuPropriedade} expressao A expressão de acesso a método ou propriedade.
* @returns A primitiva encontrada.
*/
async visitarExpressaoAcessoMetodoOuPropriedade(expressao) {
let variavelObjeto = await this.avaliar(expressao.objeto);
// Este caso acontece quando há encadeamento de métodos.
// Por exemplo, `objeto1.metodo1().metodo2()`.
// Como `RetornoQuebra` também possui `valor`, precisamos extrair o
// valor dele primeiro.
if (variavelObjeto.constructor.name === 'RetornoQuebra') {
variavelObjeto = variavelObjeto.valor;
}
const objeto = variavelObjeto.hasOwnProperty('valor') ? variavelObjeto.valor : variavelObjeto;
if (objeto.constructor.name === 'ObjetoDeleguaClasse') {
return objeto.obter(expressao.simbolo);
}
// Objeto simples do JavaScript, ou dicionário de Delégua.
if (objeto.constructor === Object) {
if (expressao.simbolo.lexema in primitivas_dicionario_1.default) {
const metodoDePrimitivaDicionario = primitivas_dicionario_1.default[expressao.simbolo.lexema].implementacao;
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaDicionario);
}
return objeto[expressao.simbolo.lexema] || null;
}
let tipoObjeto = variavelObjeto.tipo;
if (tipoObjeto === null || tipoObjeto === undefined) {
tipoObjeto = (0, inferenciador_1.inferirTipoVariavel)(variavelObjeto);
}
// Como internamente um dicionário de Delégua é simplesmente um objeto de
// JavaScript, as primitivas de dicionário, especificamente, são tratadas
// mais acima.
switch (tipoObjeto) {
case delegua_1.default.INTEIRO:
case delegua_1.default.NUMERO:
case delegua_1.default.NÚMERO:
const metodoDePrimitivaNumero = primitivas_numero_1.default[expressao.simbolo.lexema].implementacao;
if (metodoDePrimitivaNumero) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaNumero);
}
break;
case delegua_1.default.TEXTO:
const metodoDePrimitivaTexto = primitivas_texto_1.default[expressao.simbolo.lexema].implementacao;
if (metodoDePrimitivaTexto) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaTexto);
}
break;
case delegua_1.default.VETOR:
case delegua_1.default.VETOR_INTEIRO:
case delegua_1.default.VETOR_LOGICO:
case delegua_1.default.VETOR_LÓGICO:
case delegua_1.default.VETOR_NUMERO:
case delegua_1.default.VETOR_NÚMERO:
case delegua_1.default.VETOR_QUALQUER:
case delegua_1.default.VETOR_TEXTO:
const metodoDePrimitivaVetor = primitivas_vetor_1.default[expressao.simbolo.lexema].implementacao;
if (metodoDePrimitivaVetor) {
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaVetor);
}
break;
}
// Último caso válido: objeto de uma classe JavaScript que possua a propriedade.
// Exemplos: classes de LinConEs, como `RetornoComando`.
if (objeto.hasOwnProperty(expressao.simbolo.lexema)) {
return objeto[expressao.simbolo.lexema];
}
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `Método ou propriedade para objeto ou primitiva não encontrado: ${expressao.simbolo.lexema}.`, expressao.linha));
}
async visitarExpressaoAcessoPropriedade(expressao) {
let variavelObjeto = await this.avaliar(expressao.objeto);
// Este caso acontece quando há encadeamento de métodos.
// Por exemplo, `objeto1.metodo1().metodo2()`.
// Como `RetornoQuebra` também possui `valor`, precisamos extrair o
// valor dele primeiro.
if (variavelObjeto.constructor.name === 'RetornoQuebra') {
variavelObjeto = variavelObjeto.valor;
}
const objeto = variavelObjeto.hasOwnProperty('valor') ? variavelObjeto.valor : variavelObjeto;
// Outro caso que `instanceof` simplesmente não funciona para casos em Liquido,
// então testamos também o nome do construtor.
if (objeto instanceof estruturas_1.ObjetoDeleguaClasse || objeto.constructor.name === 'ObjetoDeleguaClasse') {
return objeto.obterMetodo(expressao.nomePropriedade) || null;
}
// Objeto simples do JavaScript, ou dicionário de Delégua.
if (objeto.constructor === Object) {
if (expressao.nomePropriedade in primitivas_dicionario_1.default) {
const metodoDePrimitivaDicionario = primitivas_dicionario_1.default[expressao.nomePropriedade].implementacao;
return new estruturas_1.MetodoPrimitiva(objeto, metodoDePrimitivaDicionario);
}
return objeto[expressao.nomePropriedade] || null;
}
// Casos em que o objeto possui algum outro tipo que não o de objeto simples.
// Normalmente executam quando uma biblioteca é importada, e estamos tentando
// obter alguma propriedade ou método desse objeto.
// Caso 1: Função tradicional do JavaScript.
if (typeof objeto[expressao.nomePropriedade] === primitivos_1.default.FUNCAO) {
return objeto[expressao.nomePropriedade];
}
// Caso 2: Objeto tradicional do JavaScript.
if (typeof objeto[expressao.nomePropriedade] === primitivos_1.default.OBJETO) {
return objeto[expressao.nomePropriedade];
}
// A partir daqui, presume-se que o objeto é uma das estruturas
// de Delégua.
if (objeto instanceof estruturas_1.DeleguaModulo) {
return objeto.componentes[expressao.nomePropriedade] || null;
}
let tipoObjeto = variavelObjeto.tipo;
if (tipoObjeto === null || tipoObjeto === undefined) {
tipoObjeto = (0, inferenciador_1.inferirTipoVariavel)(variavelObjeto);
}
return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `Propriedade para objeto ou primitiva não encontrado: ${expressao.nomePropriedade}.`, expressao.linha));
}
async visitarExpressaoArgumentoReferenciaFuncao(expressao) {
const deleguaFuncao = this.pilhaEscoposExecucao.obterVariavelPorNome(expressao.simboloFuncao.lexema);
return deleguaFuncao;
}
async visitarExpressaoReferenciaFuncao(expressao) {
const deleguaFuncao = this.pilhaEscoposExecucao.obterReferenciaFuncao(expressao.idFuncao);
return deleguaFuncao;
}
async visitarExpressaoRetornar(declaracao) {
let valor = null;
if (declaracao.valor !== null) {
valor = await this.avaliar(declaracao.valor);
}
const retornoQuebra = new quebras_1.RetornoQuebra(valor);
// Se o retorno for uma função anônima, o escopo precisa ser preservado.
// Como quebras matam o topo da pilha de escopos, precisamos dizer
// para a finalização para copiar as variáveis para o escopo de baixo.
if (retornoQuebra.valor.constructor.name === 'DeleguaFuncao') {
retornoQuebra.preservarEscopo = true;
}
return retornoQuebra;
}
async visitarExpressaoTipoDe(expressao) {
let valorTipoDe = expressao.valor;
switch (valorTipoDe.constructor.name) {
case 'AcessoIndiceVariavel':
case 'Agrupamento':
case 'Binario':
case 'Chamada':
case 'Dicionario':
case 'Unario':
valorTipoDe = await this.avaliar(valorTipoDe);
return valorTipoDe.tipo || (0, inferenciador_1.inferirTipoVariavel)(valorTipoDe);
case 'AcessoMetodo':
const acessoMetodo = valorTipoDe;
return `método<${acessoMetodo.tipoRetornoMetodo}>`;
case 'AcessoPropriedade':
const acessoPropriedade = valorTipoDe;
return acessoPropriedade.tipoRetornoPropriedade;
case 'AcessoMetodoOuPropriedade':
// TODO: Deve ser removido mais futuramente.
// Apenas `AcessoMetodo` e `AcessoPropriedade` devem funcionar aqui.
throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, 'Não deveria cair aqui.');
case 'Escreva':
return 'função<vazio>';
case 'Leia':
return 'função<texto>';
case 'Literal':
const tipoLiteral = valorTipoDe;
return tipoLiteral.tipo;
case 'TipoDe':
const alvoTipoDe = await this.avaliar(valorTipoDe);
return `tipo de<${alvoTipoDe}>`;
case 'Variavel':
return valorTipoDe.tipo;
case 'Vetor':
return (0, inferenciador_1.inferirTipoVariavel)(valorTipoDe === null || valorTipoDe === void 0 ? void 0 : valorTipoDe.valores);
default:
return (0, inferenciador_1.inferirTipoVariavel)(valorTipoDe);
}
}
}
exports.Interpretador = Interpretador;
//# sourceMappingURL=interpretador.js.map