UNPKG

@designliquido/delegua

Version:

Linguagem de programação simples e moderna usando português estruturado.

869 lines (868 loc) 2.5 MB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Delegua = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AvaliadorSintaticoBase = void 0; const construtos_1 = require("../construtos"); const declaracoes_1 = require("../declaracoes"); const erro_avaliador_sintatico_1 = require("./erro-avaliador-sintatico"); const comum_1 = __importDefault(require("../tipos-de-simbolos/comum")); /** * O Avaliador Sintático Base é uma tentativa de mapear métodos em comum * entre todos os outros Avaliadores Sintáticos. Depende de um dicionário * de tipos de símbolos comuns entre todos os dialetos. */ class AvaliadorSintaticoBase { consumir(tipo, mensagemDeErro) { if (this.verificarTipoSimboloAtual(tipo)) return this.avancarEDevolverAnterior(); let simboloErro = this.simbolos[this.atual]; if (this.simbolos.length === 0) { simboloErro = { hashArquivo: this.hashArquivo, linha: 1, }; } else if (this.atual >= this.simbolos.length) { simboloErro = this.simbolos[this.simbolos.length - 1]; } throw this.erro(simboloErro, mensagemDeErro); } erro(simbolo, mensagemDeErro) { const excecao = new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(simbolo, mensagemDeErro); return excecao; } simboloAnterior() { if (this.atual === 0) { throw new Error('Este é o primeiro símbolo da sequência vinda do Lexador.'); } return this.simbolos[this.atual - 1]; } verificarTipoSimboloAtual(tipo) { if (this.estaNoFinal()) return false; return this.simbolos[this.atual].tipo === tipo; } verificarTipoProximoSimbolo(tipo) { return this.simbolos[this.atual + 1].tipo === tipo; } estaNoFinal() { return this.atual === this.simbolos.length; } avancarEDevolverAnterior() { if (!this.estaNoFinal()) this.atual += 1; return this.simbolos[this.atual - 1]; } verificarSeSimboloAtualEIgualA(...argumentos) { for (let i = 0; i < argumentos.length; i++) { const tipoAtual = argumentos[i]; if (this.verificarTipoSimboloAtual(tipoAtual)) { this.avancarEDevolverAnterior(); return true; } } return false; } finalizarChamada(entidadeChamada) { const argumentos = []; if (!this.verificarTipoSimboloAtual(comum_1.default.PARENTESE_DIREITO)) { do { // `apply()` em JavaScript aceita até 255 parâmetros. if (argumentos.length >= 255) { throw this.erro(this.simbolos[this.atual], 'Não pode haver mais de 255 argumentos.'); } argumentos.push(this.expressao()); } while (this.verificarSeSimboloAtualEIgualA(comum_1.default.VIRGULA)); } this.consumir(comum_1.default.PARENTESE_DIREITO, "Esperado ')' após os argumentos."); return new construtos_1.Chamada(this.hashArquivo, entidadeChamada, argumentos); } unario() { if (this.verificarSeSimboloAtualEIgualA(comum_1.default.NEGACAO, comum_1.default.SUBTRACAO)) { const operador = this.simbolos[this.atual - 1]; const direito = this.unario(); return new construtos_1.Unario(this.hashArquivo, operador, direito, 'ANTES'); } return this.chamar(); } exponenciacao() { let expressao = this.unario(); while (this.verificarSeSimboloAtualEIgualA(comum_1.default.EXPONENCIACAO)) { const operador = this.simbolos[this.atual - 1]; const direito = this.unario(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } multiplicar() { let expressao = this.exponenciacao(); while (this.verificarSeSimboloAtualEIgualA(comum_1.default.DIVISAO, comum_1.default.DIVISAO_INTEIRA, comum_1.default.MULTIPLICACAO, comum_1.default.MODULO)) { const operador = this.simbolos[this.atual - 1]; const direito = this.exponenciacao(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } adicaoOuSubtracao() { let expressao = this.multiplicar(); while (this.verificarSeSimboloAtualEIgualA(comum_1.default.SUBTRACAO, comum_1.default.ADICAO)) { const operador = this.simbolos[this.atual - 1]; const direito = this.multiplicar(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } /** * Este método é usado por alguns dialetos de Portugol que possuem declarações * de múltiplas variáveis na mesma linha. */ declaracaoDeVariaveis() { throw new Error('Método não implementado.'); } comparar() { let expressao = this.adicaoOuSubtracao(); while (this.verificarSeSimboloAtualEIgualA(comum_1.default.MAIOR, comum_1.default.MAIOR_IGUAL, comum_1.default.MENOR, comum_1.default.MENOR_IGUAL)) { const operador = this.simbolos[this.atual - 1]; const direito = this.adicaoOuSubtracao(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } comparacaoIgualdade() { let expressao = this.comparar(); while (this.verificarSeSimboloAtualEIgualA(comum_1.default.DIFERENTE, comum_1.default.IGUAL, comum_1.default.IGUAL_IGUAL)) { const operador = this.simbolos[this.atual - 1]; const direito = this.comparar(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } e() { let expressao = this.comparacaoIgualdade(); while (this.verificarSeSimboloAtualEIgualA(comum_1.default.E)) { const operador = this.simbolos[this.atual - 1]; const direito = this.comparacaoIgualdade(); expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito); } return expressao; } ou() { let expressao = this.e(); while (this.verificarSeSimboloAtualEIgualA(comum_1.default.OU)) { const operador = this.simbolos[this.atual - 1]; const direito = this.e(); expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito); } return expressao; } expressao() { return this.atribuir(); } funcao(tipo) { // Avançar `função` ou `funcao`. this.avancarEDevolverAnterior(); const nomeFuncao = this.consumir(comum_1.default.IDENTIFICADOR, `Esperado nome ${tipo}.`); return new declaracoes_1.FuncaoDeclaracao(nomeFuncao, this.corpoDaFuncao(tipo)); } logicaComumParametros() { const parametros = []; do { if (parametros.length >= 255) { throw this.erro(this.simbolos[this.atual], 'Função não pode ter mais de 255 parâmetros.'); } const parametro = {}; if (this.simbolos[this.atual].tipo === comum_1.default.MULTIPLICACAO) { this.consumir(comum_1.default.MULTIPLICACAO, null); parametro.abrangencia = 'multiplo'; } else { parametro.abrangencia = 'padrao'; } parametro.nome = this.consumir(comum_1.default.IDENTIFICADOR, 'Esperado nome do parâmetro.'); if (this.verificarSeSimboloAtualEIgualA(comum_1.default.IGUAL)) { parametro.valorPadrao = this.primario(); } parametros.push(parametro); if (parametro.abrangencia === 'multiplo') break; } while (this.verificarSeSimboloAtualEIgualA(comum_1.default.VIRGULA)); return parametros; } /** * Os métodos a seguir só devem ser implementados se o dialeto * em questão realmente possui a funcionalidade, e devem levantar * erro em caso contrário. */ bitShift() { throw new Error('Método não implementado.'); } bitE() { throw new Error('Método não implementado.'); } bitOu() { throw new Error('Método não implementado.'); } declaracaoContinua() { throw new Error('Método não implementado.'); } declaracaoDeClasse() { throw new Error('Método não implementado.'); } declaracaoDeVariavel() { throw new Error('Método não implementado.'); } declaracaoExpressao(simboloAnterior) { throw new Error('Método não implementado.'); } declaracaoImportar() { throw new Error('Método não implementado.'); } declaracaoRetorna() { throw new Error('Método não implementado.'); } declaracaoSustar() { throw new Error('Método não implementado.'); } declaracaoTente() { throw new Error('Método não implementado.'); } em() { throw new Error('Método não implementado.'); } resolverDeclaracao() { throw new Error('Método não implementado.'); } } exports.AvaliadorSintaticoBase = AvaliadorSintaticoBase; },{"../construtos":44,"../declaracoes":85,"../tipos-de-simbolos/comum":170,"./erro-avaliador-sintatico":9}],2:[function(require,module,exports){ "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AvaliadorSintatico = void 0; const browser_process_hrtime_1 = __importDefault(require("browser-process-hrtime")); const construtos_1 = require("../construtos"); const erro_avaliador_sintatico_1 = require("./erro-avaliador-sintatico"); const tuplas_1 = require("../construtos/tuplas"); const declaracoes_1 = require("../declaracoes"); const avaliador_sintatico_base_1 = require("./avaliador-sintatico-base"); const inferenciador_1 = require("../inferenciador"); const pilha_escopos_1 = require("./pilha-escopos"); const informacao_escopo_1 = require("./informacao-escopo"); const informacao_variavel_ou_constante_1 = require("../informacao-variavel-ou-constante"); const delegua_1 = __importDefault(require("../tipos-de-dados/delegua")); const delegua_2 = __importDefault(require("../tipos-de-simbolos/delegua")); 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 comum_1 = require("./comum"); /** * O avaliador sintático (_Parser_) é responsável por transformar os símbolos do Lexador em estruturas de alto nível. * Essas estruturas de alto nível são as partes que executam lógica de programação de fato. * Há dois grupos de estruturas de alto nível: Construtos e Declarações. * * Construtos não existem por si só: cada construto precisa estar dentro de uma declaração para ser * aceito pela próxima etapa, como tradução, interpretação, análise semântica, etc. * * Este é o avaliador sintático de Delégua que, assim como todos os demais dialetos baseados * neste núcleo, são uma derivação do avaliador sintático base. Aqui estão implementadas várias mecânicas * a mais relacionadas a tipagem e registros de bibliotecas externas. Por exemplo, `tiposDeFerramentasExternas` * é utilizada em [Liquido](https://github.com/DesignLiquido/liquido) para registro de tipos exclusivos * de Liquido, como classes de requisição e resposta. `primitivasConhecidas` é utilizada aqui para * registro de métodos relacionados a tipos, e também para as bibliotecas externas de Delégua registrarem * suas respectivas resoluções de tipos. */ class AvaliadorSintatico extends avaliador_sintatico_base_1.AvaliadorSintaticoBase { constructor(performance = false) { super(); this.hashArquivo = 0; this.atual = 0; this.blocos = 0; this.erros = []; this.performance = performance; this.tiposDefinidosEmCodigo = {}; this.tiposDeFerramentasExternas = {}; this.primitivasConhecidas = {}; (0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'dicionário', primitivas_dicionario_1.default); (0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'número', primitivas_numero_1.default); (0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'texto', primitivas_texto_1.default); (0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'vetor', primitivas_vetor_1.default); this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos(); } verificarDefinicaoTipoAtual() { const tipos = [...Object.values(delegua_1.default)]; if (this.simbolos[this.atual].lexema in this.tiposDefinidosEmCodigo) { return this.simbolos[this.atual].lexema; } const lexemaElementar = this.simbolos[this.atual].lexema.toLowerCase(); const tipoElementarResolvido = tipos.find((tipo) => tipo === lexemaElementar); if (!tipoElementarResolvido) { throw this.erro(this.simbolos[this.atual], `Tipo de dados desconhecido: '${this.simbolos[this.atual].lexema}'.`); } if (this.verificarTipoProximoSimbolo(delegua_2.default.COLCHETE_ESQUERDO)) { const tiposVetores = [ 'inteiro[]', 'numero[]', 'número[]', 'qualquer[]', 'real[]', 'texto[]', ]; this.avancarEDevolverAnterior(); if (!this.verificarTipoProximoSimbolo(delegua_2.default.COLCHETE_DIREITO)) { throw this.erro(this.simbolos[this.atual], `Esperado símbolo de fechamento do vetor: ']'. Atual: ${this.simbolos[this.atual].lexema}`); } const tipoVetor = tiposVetores.find((tipo) => tipo === `${lexemaElementar}[]`); this.avancarEDevolverAnterior(); return tipoVetor; } return tipoElementarResolvido; } obterChaveDicionario() { switch (this.simbolos[this.atual].tipo) { case delegua_2.default.NUMERO: case delegua_2.default.TEXTO: case delegua_2.default.FALSO: case delegua_2.default.VERDADEIRO: return this.primario(); case delegua_2.default.IDENTIFICADOR: const simboloIdentificador = this.avancarEDevolverAnterior(); let tipoOperando; if (simboloIdentificador.lexema in this.tiposDefinidosEmCodigo) { tipoOperando = simboloIdentificador.lexema; } else { tipoOperando = this.pilhaEscopos.obterTipoVariavelPorNome(simboloIdentificador.lexema); } if (!['numero', 'número', 'texto', 'lógico'].includes(tipoOperando)) { throw this.erro(simboloIdentificador, `Tipo ${tipoOperando} de identificador ${simboloIdentificador.lexema} não é válido como chave de dicionário.`); } return new construtos_1.Variavel(this.hashArquivo, simboloIdentificador, tipoOperando); case delegua_2.default.COLCHETE_ESQUERDO: this.avancarEDevolverAnterior(); if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) { return this.construtoTupla(); } throw this.erro(this.simbolos[this.atual], `Esperado parêntese esquerdo após colchete esquerdo para definição de chave de dicionário. Atual: ${this.simbolos[this.atual].tipo}.`); default: throw this.erro(this.simbolos[this.atual], `Símbolo ${this.simbolos[this.atual].tipo} inesperado ou inválido como chave de dicionário.`); } } construtoDicionario(simboloChaveEsquerda) { this.avancarEDevolverAnterior(); const chaves = []; const valores = []; if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_DIREITA)) { return new construtos_1.Dicionario(this.hashArquivo, Number(simboloChaveEsquerda.linha), [], []); } while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_DIREITA)) { const chave = this.obterChaveDicionario(); this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' entre chave e valor."); const valor = this.atribuir(); chaves.push(chave); valores.push(valor); if (this.simbolos[this.atual].tipo !== delegua_2.default.CHAVE_DIREITA) { this.consumir(delegua_2.default.VIRGULA, 'Esperado vírgula antes da próxima expressão.'); } } return new construtos_1.Dicionario(this.hashArquivo, Number(simboloChaveEsquerda.linha), chaves, valores); } construtoTupla() { const expressao = this.expressao(); const argumentos = [expressao]; while (this.simbolos[this.atual].tipo === delegua_2.default.VIRGULA) { this.avancarEDevolverAnterior(); argumentos.push(this.expressao()); } this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após a expressão."); this.consumir(delegua_2.default.COLCHETE_DIREITO, "Esperado ']' após a expressão."); return new tuplas_1.SeletorTuplas(...argumentos); } primario() { const simboloAtual = this.simbolos[this.atual]; let valores = []; switch (simboloAtual.tipo) { case delegua_2.default.CHAVE_ESQUERDA: return this.construtoDicionario(simboloAtual); case delegua_2.default.COLCHETE_ESQUERDO: this.avancarEDevolverAnterior(); valores = []; if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_DIREITO)) { return new construtos_1.Vetor(this.hashArquivo, Number(simboloAtual.linha), [], 0, 'qualquer[]'); } while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_DIREITO)) { if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) { return this.construtoTupla(); } const valor = this.atribuir(); valores.push(valor); if (this.simbolos[this.atual].tipo !== delegua_2.default.COLCHETE_DIREITO) { this.consumir(delegua_2.default.VIRGULA, 'Esperado vírgula antes da próxima expressão.'); } } const tipoVetor = (0, inferenciador_1.inferirTipoVariavel)(valores); return new construtos_1.Vetor(this.hashArquivo, Number(simboloAtual.linha), valores, valores.length, tipoVetor); case delegua_2.default.EXPRESSAO_REGULAR: let valor = ''; let linhaAtual = this.simbolos[this.atual].linha; let eParExpressaoRegular = this.simbolos.filter((l) => l.linha === linhaAtual && l.tipo === delegua_2.default.EXPRESSAO_REGULAR).length % 2 === 0; if (eParExpressaoRegular) { this.avancarEDevolverAnterior(); while (!this.verificarTipoSimboloAtual(delegua_2.default.EXPRESSAO_REGULAR)) { valor += this.simbolos[this.atual].lexema || ''; this.avancarEDevolverAnterior(); } this.avancarEDevolverAnterior(); return new construtos_1.ExpressaoRegular(this.hashArquivo, simboloAtual, valor); } case delegua_2.default.FALSO: this.avancarEDevolverAnterior(); return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), false, 'lógico'); case delegua_2.default.FUNCAO: case delegua_2.default.FUNÇÃO: const simboloFuncao = this.avancarEDevolverAnterior(); const corpoDaFuncao = this.corpoDaFuncao(simboloFuncao.lexema); this.pilhaEscopos.definirInformacoesVariavel(simboloFuncao.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simboloFuncao.lexema, 'função')); return corpoDaFuncao; case delegua_2.default.IDENTIFICADOR: const simboloIdentificador = this.avancarEDevolverAnterior(); let tipoOperando; if (simboloIdentificador.lexema in this.tiposDefinidosEmCodigo) { tipoOperando = simboloIdentificador.lexema; } else { try { tipoOperando = this.pilhaEscopos.obterTipoVariavelPorNome(simboloIdentificador.lexema); } catch (erro) { throw this.erro(simboloIdentificador, erro.message); } } // Se o próximo símbolo é um incremento ou um decremento, // aqui deve retornar um unário correspondente. // Caso contrário, apenas retornar um construto de variável. if (this.simbolos[this.atual] && [delegua_2.default.INCREMENTAR, delegua_2.default.DECREMENTAR].includes(this.simbolos[this.atual].tipo)) { const simboloIncrementoDecremento = this.avancarEDevolverAnterior(); return new construtos_1.Unario(this.hashArquivo, simboloIncrementoDecremento, new construtos_1.Variavel(this.hashArquivo, simboloIdentificador, tipoOperando || 'qualquer'), 'DEPOIS'); } return new construtos_1.Variavel(this.hashArquivo, simboloIdentificador, tipoOperando || 'qualquer'); case delegua_2.default.IMPORTAR: this.avancarEDevolverAnterior(); return this.declaracaoImportar(); case delegua_2.default.ISTO: this.avancarEDevolverAnterior(); return new construtos_1.Isto(this.hashArquivo, Number(simboloAtual.linha), simboloAtual); case delegua_2.default.LEIA: return this.expressaoLeia(); case delegua_2.default.NULO: this.avancarEDevolverAnterior(); return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), null, 'nulo'); case delegua_2.default.NUMERO: case delegua_2.default.NÚMERO: case delegua_2.default.TEXTO: const simboloNumeroTexto = this.avancarEDevolverAnterior(); const tipoInferido = (0, inferenciador_1.inferirTipoVariavel)(simboloNumeroTexto.literal); const tipoDadosElementar = (0, inferenciador_1.tipoInferenciaParaTipoDadosElementar)(tipoInferido); return new construtos_1.Literal(this.hashArquivo, Number(simboloNumeroTexto.linha), simboloNumeroTexto.literal, tipoDadosElementar); case delegua_2.default.PARENTESE_ESQUERDO: this.avancarEDevolverAnterior(); const expressao = this.expressao(); this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após a expressão."); return new construtos_1.Agrupamento(this.hashArquivo, Number(simboloAtual.linha), expressao); case delegua_2.default.SUPER: const simboloSuper = this.avancarEDevolverAnterior(); if (!this.superclasseAtual) { throw this.erro(this.simbolos[this.atual], "'Super' usado fora de declaração de classe com herança."); } return new construtos_1.Super(this.hashArquivo, simboloSuper, this.superclasseAtual); case delegua_2.default.VERDADEIRO: this.avancarEDevolverAnterior(); return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), true, 'lógico'); case delegua_2.default.TIPO: this.avancarEDevolverAnterior(); this.consumir(delegua_2.default.DE, "Esperado 'de' após 'tipo'."); let construto; if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.ESCREVA, delegua_2.default.LEIA, delegua_2.default.FUNCAO, delegua_2.default.FUNÇÃO, delegua_2.default.SE, delegua_2.default.ENQUANTO, delegua_2.default.PARA, delegua_2.default.RETORNA, delegua_1.default.INTEIRO, delegua_1.default.TEXTO, delegua_1.default.VETOR, delegua_1.default.LOGICO, delegua_1.default.LÓGICO, delegua_1.default.VAZIO)) { construto = new construtos_1.ComponenteLinguagem(this.hashArquivo, this.simboloAnterior()); } else { construto = this.expressao(); } if (construto.constructor.name === 'AcessoMetodoOuPropriedade') { const construtoTipado = construto; switch (construtoTipado.tipo) { case delegua_1.default.DICIONARIO: case delegua_1.default.DICIONÁRIO: if (!(construtoTipado.simbolo.lexema in primitivas_dicionario_1.default)) { throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de dicionário.`); } const primitivaDicionarioSelecionada = primitivas_dicionario_1.default[construtoTipado.simbolo.lexema]; construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaDicionarioSelecionada.tipoRetorno); break; case delegua_1.default.INTEIRO: case delegua_1.default.NUMERO: case delegua_1.default.NÚMERO: if (!(construtoTipado.simbolo.lexema in primitivas_numero_1.default)) { throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de número.`); } const primitivaNumeroSelecionada = primitivas_numero_1.default[construtoTipado.simbolo.lexema]; construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaNumeroSelecionada.tipoRetorno); break; case delegua_1.default.TEXTO: if (!(construtoTipado.simbolo.lexema in primitivas_texto_1.default)) { throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de texto.`); } const primitivaTextoSelecionada = primitivas_texto_1.default[construtoTipado.simbolo.lexema]; construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaTextoSelecionada.tipoRetorno); 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: if (!(construtoTipado.simbolo.lexema in primitivas_vetor_1.default)) { throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de vetor.`); } const primitivaVetorSelecionada = primitivas_vetor_1.default[construtoTipado.simbolo.lexema]; construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaVetorSelecionada.tipoRetorno); break; default: if (construtoTipado.tipo in this.tiposDefinidosEmCodigo) { const tipoCorrespondente = this.tiposDefinidosEmCodigo[construtoTipado.tipo]; const possivelMetodo = tipoCorrespondente.metodos.filter((m) => m.simbolo.lexema === construtoTipado.simbolo.lexema); if (possivelMetodo.length > 0) { construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, possivelMetodo[0].tipo); break; } const possivelPropriedade = tipoCorrespondente.propriedades.filter((p) => p.nome.lexema === construtoTipado.simbolo.lexema); if (possivelPropriedade.length > 0) { construto = new construtos_1.AcessoPropriedade(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, possivelPropriedade[0].tipo); break; } } } } return new construtos_1.TipoDe(this.hashArquivo, simboloAtual, construto); } throw this.erro(this.simbolos[this.atual], 'Esperado expressão.'); } chamar() { let expressao = this.primario(); let tipoPrimitiva = undefined; while (true) { if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) { expressao = this.finalizarChamada(expressao, tipoPrimitiva); } else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO)) { const nome = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome de método ou propriedade após '.'."); tipoPrimitiva = expressao.tipo; expressao = new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, expressao, nome); } else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_ESQUERDO)) { const indice = this.expressao(); const simboloFechamento = this.consumir(delegua_2.default.COLCHETE_DIREITO, "Esperado ']' após escrita do indice."); expressao = new construtos_1.AcessoIndiceVariavel(this.hashArquivo, expressao, indice, simboloFechamento); } else { break; } } return expressao; } /** * `AcessoMetodoOuPropriedade` é um construto intermediário em Delégua, e deve ser resolvido como outro * construto antes de qualquer outra próxima etapa. Algumas validações adicionais também ocorrem aqui. * @param {AcessoMetodoOuPropriedade} entidadeChamadaResolvida O construto original. * @returns {Construto} O construto resolvido como um tipo mais específico. * @see finalizarChamada */ resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamadaResolvida) { const construtoTipado = entidadeChamadaResolvida; switch (entidadeChamadaResolvida.tipo) { case delegua_1.default.DICIONARIO: case delegua_1.default.DICIONÁRIO: if (!(construtoTipado.simbolo.lexema in primitivas_dicionario_1.default)) { throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de dicionário.`); } const primitivaDicionarioSelecionada = primitivas_dicionario_1.default[construtoTipado.simbolo.lexema]; return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaDicionarioSelecionada.tipoRetorno); case delegua_1.default.INTEIRO: case delegua_1.default.NUMERO: case delegua_1.default.NÚMERO: if (!(construtoTipado.simbolo.lexema in primitivas_numero_1.default)) { throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de número.`); } const primitivaNumeroSelecionada = primitivas_numero_1.default[construtoTipado.simbolo.lexema]; return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaNumeroSelecionada.tipoRetorno); case delegua_1.default.MODULO: case delegua_1.default.MÓDULO: // Há dois casos para resolução de módulo: // Um quando o módulo é definido no próprio código (por exemplo, em um outro arquivo `.delegua`). // Outro quando é importado de uma biblioteca externa. // Este é o caso quando o módulo vem de outro arquivo `.delegua`. if (construtoTipado.simbolo.lexema in this.tiposDefinidosEmCodigo) { // Construtor de classe. return new construtos_1.Variavel(construtoTipado.hashArquivo, construtoTipado.simbolo, construtoTipado.objeto.tipo); } return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema); case delegua_1.default.TEXTO: if (!(construtoTipado.simbolo.lexema in primitivas_texto_1.default)) { throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de texto.`); } const primitivaTextoSelecionada = primitivas_texto_1.default[construtoTipado.simbolo.lexema]; return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaTextoSelecionada.tipoRetorno); case delegua_1.default.VETOR: case delegua_1.default.VETOR_NUMERO: case delegua_1.default.VETOR_NÚMERO: case delegua_1.default.VETOR_TEXTO: if (!(construtoTipado.simbolo.lexema in primitivas_vetor_1.default)) { throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de vetor.`); } const primitivaVetorSelecionada = primitivas_vetor_1.default[construtoTipado.simbolo.lexema]; return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaVetorSelecionada.tipoRetorno); } return entidadeChamadaResolvida; } validarArgumentosEntidadeChamada(argumentosEntidadeChamada, argumentosUtilizados) { if (argumentosEntidadeChamada.length === 0) { return []; } const possiveisErros = []; for (const [indice, argumentoEntidadeChamada] of argumentosEntidadeChamada.entries()) { const argumentoUtilizado = argumentosUtilizados[indice]; if (!argumentoUtilizado) { if (argumentoEntidadeChamada.obrigatorio) { possiveisErros.push(`Argumento ${argumentoEntidadeChamada.nome} é obrigatório, mas não foi fornecido.`); } continue; } if (argumentoUtilizado.tipo === null || argumentoUtilizado.tipo === undefined) { continue; } const argumentoEntidadeChamadaQualquer = argumentoEntidadeChamada.tipo.startsWith('qualquer'); const argumentoUtilizadoQualquer = argumentoUtilizado.tipo.startsWith('qualquer'); // Este caso é tarefa do analisador semântico apontar. if (argumentoEntidadeChamadaQualquer || argumentoUtilizadoQualquer) { continue; } const argumentoEntidadeChamadaVetor = argumentoEntidadeChamada.tipo.endsWith('[]'); const argumentoUtilizadoVetor = argumentoUtilizado.tipo.endsWith('[]'); if (argumentoEntidadeChamadaVetor !== argumentoUtilizadoVetor) { possiveisErros.push(`Argumento: ${argumentoEntidadeChamada.nome}. Tipo esperado: ${argumentoEntidadeChamada.tipo}; Tipo utilizado: ${argumentoUtilizado.tipo}`); continue; } const tipoArgumentoUtilizado = argumentoUtilizado.tipo.startsWith('funcao') || argumentoUtilizado.tipo.startsWith('função') ? 'função' : argumentoUtilizado.tipo; const tipoArgumentoEntidadeChamada = argumentoEntidadeChamada.tipo.startsWith('funcao') || argumentoEntidadeChamada.tipo.startsWith('função') ? 'função' : argumentoEntidadeChamada.tipo; if (tipoArgumentoUtilizado !== tipoArgumentoEntidadeChamada) { possiveisErros.push(`Argumento: ${argumentoEntidadeChamada.nome}. Tipo esperado: ${argumentoEntidadeChamada.tipo}; Tipo utilizado: ${argumentoUtilizado.tipo}`); } } return possiveisErros; } /** * Diversas verificações de resolução de entidade chamada, como resolver chamada da pilha ou usar referência, argumentos, etc. * @param entidadeChamada O construto da entidade chamada. * @param argumentos Os argumentos utilizados na chamada. * @param tipoPrimitiva Se for uma primitiva, o tipo dela. Senão, `undefined`. * @returns A entidade chamada resolvida, se as validações passarem. */ resolverEntidadeChamada(entidadeChamada, argumentos, tipoPrimitiva = undefined) { if (entidadeChamada.constructor.name === 'Variavel') { const entidadeChamadaResolvidaVariavel = entidadeChamada; if (tipoPrimitiva === undefined) { // Provavelmente uma chamada a alguma função da biblioteca global. const informacoesPossivelFuncaoBibliotecaGlobal = this.pilhaEscopos.obterBibliotecaGlobal(entidadeChamadaResolvidaVariavel.simbolo.lexema); if (informacoesPossivelFuncaoBibliotecaGlobal) { const erros = this.validarArgumentosEntidadeChamada(informacoesPossivelFuncaoBibliotecaGlobal.argumentos, argumentos); if (erros.length > 0) { throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamadaResolvidaVariavel.simbolo, `Erros ao resolver argumentos de chamada a ${entidadeChamadaResolvidaVariavel.simbolo.lexema}: \n${erros.reduce((mensagem, erro) => (mensagem += `${erro}\n`), '')}`); } return entidadeChamadaResolvidaVariavel; } } if (tipoPrimitiva !== undefined && this.primitivasConhecidas[tipoPrimitiva].hasOwnProperty(entidadeChamadaResolvidaVariavel.simbolo.lexema)) { var informacoesPrimitiva = this.primitivasConhecidas[tipoPrimitiva][entidadeChamadaResolvidaVariavel.simbolo.lexema]; const erros = this.validarArgumentosEntidadeChamada(informacoesPrimitiva.argumentos, argumentos); if (erros.length > 0) { throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamadaResolvidaVariavel.simbolo, `Erros ao resolver argumentos de chamada a ${entidadeChamadaResolvidaVariavel.simbolo.lexema}: \n${erros.reduce((mensagem, erro) => (mensagem += `${erro}\n`), '')}`); } return entidadeChamadaResolvidaVariavel; } if (entidadeChamadaResolvidaVariavel.simbolo.lexema in this.tiposDefinidosEmCodigo) { return entidadeChamadaResolvidaVariavel; } const possivelReferencia = this.pilhaEscopos.obterReferenciaFuncao(entidadeChamadaResolvidaVariavel.simbolo.lexema); if (possivelReferencia !== null) { return new construtos_1.ReferenciaFuncao(entidadeChamada.hashArquivo, entidadeChamada.linha, entidadeChamadaResolvidaVariavel.simbolo, entidadeChamadaResolvidaVariavel.tipo, possivelReferencia.id); } return new construtos_1.ArgumentoReferenciaFuncao(entidadeChamada.hashArquivo, entidadeChamada.linha, entidadeChamadaResolvidaVariavel.simbolo); } if (entidadeChamada.constructor.name === 'AcessoMetodoOuPropriedade') { return this.resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamada); } return entidadeChamada; } finalizarChamada(entidadeChamada, tipoPrimitiva = undefined) { const argumentos = []; if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) { do { // `apply()` em JavaScript aceita até 255 parâmetros. if (argumentos.length >= 255) { throw this.erro(this.simbolos[this.atual], 'Não pode haver mais de 255 argumentos.'); } argumentos.push(this.expressao()); } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA)); } this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após os argumentos."); // Toda chamada precisa saber de antemão qual o tipo resolvido. const entidadeChamadaResolvida = this.resolverEntidadeChamada(entidadeChamada, argumentos, tipoPrimitiva); const construtoChamada = new construtos_1.Chamada(this.hashArquivo, entidadeChamadaResolvida, argumentos); // A validação de tipos dos argumentos da entidade chamada existe em // avaliadores sintáticos derivados deste, como em `delegua-node`. // Pode ser que esta lógica seja trazida para cá no futuro. construtoChamada.tipo = 'qualquer'; return construtoChamada; } unario() { if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.NEGACAO, delegua_2.default.SUBTRACAO, delegua_2.default.BIT_NOT, delegua_2.default.INCREMENTAR, delegua_2.default.DECREMENTAR)) { const operador = this.simbolos[this.atual - 1]; const direito = this.unario(); return new construtos_1.Unario(this.hashArquivo, operador, direito, 'ANTES'); } return this.chamar(); } multiplicar() { let expressao = this.exponenciacao(); while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DIVISAO, delegua_2.default.DIVISAO_IGUAL, delegua_2.default.DIVISAO_INTEIRA, delegua_2.default.DIVISAO_INTEIRA_IGUAL, delegua_2.default.MODULO, delegua_2.default.MODULO_IGUAL, delegua_2.default.MULTIPLICACAO, delegua_2.default.MULTIPLICACAO_IGUAL)) { const operador = this.simbolos[this.atual - 1]; const direito = this.exponenciacao(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } /** * Se símbolo de operação é `+`, `-`, `+=` ou `-=`, monta objeto `Binario` para * ser avaliado pelo Interpretador. * @returns Um Construto, normalmente um `Binario`, ou `Unario` se houver alguma operação unária para ser avaliada. */ adicaoOuSubtracao() { let expressao = this.multiplicar(); while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SUBTRACAO, delegua_2.default.ADICAO, delegua_2.default.MENOS_IGUAL)) { const operador = this.simbolos[this.atual - 1]; const direito = this.multiplicar(); // const tipoInferido = inferirTipoParaBinario(expressao, operador, direito); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MAIS_IGUAL)) { const operador = this.simbolos[this.atual - 1]; const direito = this.atribuir(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } bitShift() { let expressao = this.adicaoOuSubtracao(); while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MENOR_MENOR, delegua_2.default.MAIOR_MAIOR)) { const operador = this.simbolos[this.atual - 1]; const direito = this.adicaoOuSubtracao(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } bitE() { let expressao = this.bitShift(); while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.BIT_AND)) { const operador = this.simbolos[this.atual - 1]; const direito = this.bitShift(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } bitOu() { let expressao = this.bitE(); while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.BIT_OR, delegua_2.default.BIT_XOR)) { const operador = this.simbolos[this.atual - 1]; const direito = this.bitE(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } comparar() { let expressao = this.bitOu(); while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MAIOR, delegua_2.default.MAIOR_IGUAL, delegua_2.default.MENOR, delegua_2.default.MENOR_IGUAL)) { const operador = this.simbolos[this.atual - 1]; const direito = this.bitOu(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } comparacaoIgualdade() { let expressao = this.comparar(); while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DIFERENTE, delegua_2.default.IGUAL_IGUAL)) { const operador = this.simbolos[this.atual - 1]; const direito = this.comparar(); expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito); } return expressao; } em() { let expressao = this.comparacaoIgualdade(); while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.EM)) { const operador = this.simbolos[this.atual - 1]; const direito = this.comparacaoIgualdade(); expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito); } return expressao; } e() { let expressao = this.em(); while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.E)) { const operador = this.simbolos[this.atual - 1]; const direito = this.em(); expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito); } return expressao; } /** * Método que resolve atribuições. * @returns Um construto do tipo `Atribuir`, `Conjunto` ou `AtribuicaoPorIndice`. */ atribuir() { const expressao = this.ou(); if (expressao instanceof construtos_1.Binario && [ delegua_2.default.MAIS_IGUAL, delegua_2.default.MENOS_IGUAL, delegua_2.default.MULTIPLICACAO_IGUAL, delegua_2.default.DIVISAO_IGUAL, delegua_2.default.DIVISAO_INTEIRA_IGUAL, delegua_2.default.MODULO_IGUAL, ].includes(expressao.operador.tipo)) { if (expressao.esquerda instanceof construtos_1.AcessoIndiceVariavel) { const entidade = expressao.esquerda; return new construtos_1.Atribuir(this.hashArquivo, entidade.entidadeChamada, expressao, entidade.indice, expressao.operador); } return new construtos_1.Atribuir(this.hashArquivo, expressao.esquerda, expressao, undefined, expressao.operador); } else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL)) { const igual = this.simbolos[this.atual - 1]; const valor = this.expressao(); switch (expressao.constructor.name) { case 'Variavel': return new construtos_1.Atribuir(this.hashArquivo, expressao, valor); case 'AcessoMetodoOuPropriedade': const expressaoAcessoMetodoOuPropriedade =