@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
1,700 lines • 101 kB
JavaScript
"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 = expressao;
return new construtos_1.DefinirValor(this.hashArquivo, igual.linha, expressaoAcessoMetodoOuPropriedade.objeto, expressaoAcessoMetodoOuPropriedade.simbolo, valor);
case 'AcessoIndiceVariavel':
const expressaoAcessoIndiceVariavel = expressao;
return new construtos_1.AtribuicaoPorIndice(this.hashArquivo, expressaoAcessoIndiceVariavel.linha, expressaoAcessoIndiceVariavel.entidadeChamada, expressaoAcessoIndiceVariavel.indice, valor);
}
throw this.erro(igual, 'Tarefa de atribuição inválida');
}
return expressao;
}
/**
* Declaração para comando `leia`, para ler dados de entrada do usuário.
* @returns Um objeto da classe `Leia`.
*/
expressaoLeia() {
const simboloLeia = this.avancarEDevolverAnterior();
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos argumentos em instrução `leia`.");
const argumentos = [];
if (this.simbolos[this.atual].tipo !== delegua_2.default.PARENTESE_DIREITO) {
do {
argumentos.push(this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após os argumentos em instrução `leia`.");
return new construtos_1.Leia(simboloLeia, argumentos);
}
// TODO: Depreciar.
expressao() {
return this.atribuir();
}
blocoEscopo() {
this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
let declaracoes = [];
while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
!this.estaNoFinal()) {
const retornoDeclaracao = this.resolverDeclaracaoForaDeBloco();
if (Array.isArray(retornoDeclaracao)) {
declaracoes = declaracoes.concat(retornoDeclaracao);
}
else {
declaracoes.push(retornoDeclaracao);
}
}
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após o bloco.");
this.pilhaEscopos.removerUltimo();
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return declaracoes;
}
declaracaoComentarioMultilinha() {
let simboloComentario;
const conteudos = [];
do {
simboloComentario = this.avancarEDevolverAnterior();
conteudos.push(simboloComentario.literal);
} while (this.verificarTipoSimboloAtual(delegua_2.default.LINHA_COMENTARIO));
return new declaracoes_1.Comentario(simboloComentario.hashArquivo, simboloComentario.linha, conteudos, true);
}
declaracaoComentarioUmaLinha() {
const simboloComentario = this.avancarEDevolverAnterior();
return new declaracoes_1.Comentario(simboloComentario.hashArquivo, simboloComentario.linha, simboloComentario.literal, false);
}
declaracaoContinua() {
if (this.blocos < 1) {
throw this.erro(this.simbolos[this.atual - 1], "'continua' precisa estar em um laço de repetição.");
}
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Continua(this.simbolos[this.atual - 1]);
}
declaracaoEnquanto() {
try {
this.blocos += 1;
const condicao = this.expressao();
// TODO: Talvez não seja uma ideia melhor chamar o método de `Bloco` aqui?
const corpo = this.resolverDeclaracao();
return new declaracoes_1.Enquanto(condicao, corpo);
}
finally {
this.blocos -= 1;
}
}
declaracaoEscolha() {
try {
this.blocos += 1;
const condicao = this.expressao();
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do escopo do 'escolha'.");
const caminhos = [];
let caminhoPadrao = null;
while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_DIREITA) &&
!this.estaNoFinal()) {
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CASO)) {
const caminhoCondicoes = [this.expressao()];
this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após o 'caso'.");
while (this.verificarTipoSimboloAtual(delegua_2.default.CASO)) {
this.consumir(delegua_2.default.CASO, null);
caminhoCondicoes.push(this.expressao());
this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após declaração do 'caso'.");
}
let declaracoes = [];
do {
const retornoDeclaracao = this.resolverDeclaracao();
if (Array.isArray(retornoDeclaracao)) {
declaracoes = declaracoes.concat(retornoDeclaracao);
}
else {
declaracoes.push(retornoDeclaracao);
}
} while (!this.verificarTipoSimboloAtual(delegua_2.default.CASO) &&
!this.verificarTipoSimboloAtual(delegua_2.default.PADRAO) &&
!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA));
caminhos.push({
condicoes: caminhoCondicoes,
declaracoes,
});
}
else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PADRAO)) {
if (caminhoPadrao !== null) {
const excecao = new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(this.simbolos[this.atual], "Você só pode ter um 'padrao' em cada declaração de 'escolha'.");
this.erros.push(excecao);
throw excecao;
}
this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após declaração do 'padrao'.");
const declaracoes = [];
do {
declaracoes.push(this.resolverDeclaracao());
} while (!this.verificarTipoSimboloAtual(delegua_2.default.CASO) &&
!this.verificarTipoSimboloAtual(delegua_2.default.PADRAO) &&
!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA));
caminhoPadrao = {
declaracoes,
};
}
}
return new declaracoes_1.Escolha(condicao, caminhos, caminhoPadrao);
}
finally {
this.blocos -= 1;
}
}
declaracaoEscreva() {
const simboloAtual = this.simbolos[this.atual];
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos valores em escreva.");
const argumentos = [];
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
do {
argumentos.push(this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após os valores em escreva.");
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Escreva(Number(simboloAtual.linha), simboloAtual.hashArquivo, argumentos);
}
declaracaoExpressao() {
// Se há decoradores a serem adicionados aqui, obtemo-los agora,
// para evitar que outros passos recursivos peguem-los antes.
const decoradores = Array.from(this.pilhaDecoradores);
this.pilhaDecoradores = [];
const expressao = this.expressao();
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Expressao(expressao, decoradores);
}
declaracaoFalhar() {
const simboloFalha = this.simbolos[this.atual - 1];
return new declaracoes_1.Falhar(simboloFalha, this.declaracaoExpressao().expressao);
}
declaracaoFazer() {
const simboloFazer = this.simbolos[this.atual - 1];
try {
this.blocos += 1;
const caminhoFazer = this.resolverDeclaracao();
this.consumir(delegua_2.default.ENQUANTO, "Esperado declaração do 'enquanto' após o escopo do 'fazer'.");
const condicaoEnquanto = this.expressao();
return new declaracoes_1.Fazer(simboloFazer.hashArquivo, Number(simboloFazer.linha), caminhoFazer, condicaoEnquanto);
}
finally {
this.blocos -= 1;
}
}
/**
* O símbolo é emitido aqui para fins de formatação, mas este método é
* sobrescrito em `delegua-node`.
* @returns {Importar} Uma declaração `Importar`.
*/
declaracaoImportar() {
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após declaração.");
const caminho = this.expressao();
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após declaração.");
return new declaracoes_1.Importar(caminho);
}
declaracaoPara() {
try {
const simboloPara = this.simbolos[this.atual - 1];
this.blocos += 1;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CADA)) {
return this.declaracaoParaCada(simboloPara);
}
return this.declaracaoParaTradicional(simboloPara);
}
finally {
this.blocos -= 1;
}
}
declaracaoParaCadaDicionario(simboloPara) {
this.avancarEDevolverAnterior(); // chave esquerda
const nomeVariavelChave = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado identificador de variável para chave de iteração, em instrução 'para cada'.");
this.consumir(delegua_2.default.VIRGULA, "Esperado vírgula após nome de variável para chave de iteração, em instrução 'para cada'.");
const nomeVariavelValor = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado identificador de variável para valor de iteração, em instrução 'para cada'.");
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado chave direita após nome de variável para valor de iteração, em instrução 'para cada'.");
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.DE, delegua_2.default.EM)) {
throw this.erro(this.simbolos[this.atual], "Esperado palavras reservadas 'em' ou 'de' após variável de iteração em instrução 'para cada'.");
}
const dicionario = this.expressao();
if (!dicionario.hasOwnProperty('tipo')) {
throw this.erro(simboloPara, `Variável ou constante em 'para cada' não parece ser um dicionário.`);
}
const tipoDicionario = dicionario.tipo;
if (tipoDicionario !== 'dicionário') {
throw this.erro(simboloPara, `Variável ou constante em 'para cada' não é um dicionário. Tipo resolvido: ${tipoDicionario}.`);
}
this.pilhaEscopos.definirInformacoesVariavel(nomeVariavelChave.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomeVariavelChave.lexema, 'qualquer'));
this.pilhaEscopos.definirInformacoesVariavel(nomeVariavelValor.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomeVariavelValor.lexema, 'qualquer'));
// TODO: Talvez não seja uma ideia melhor chamar o método de `Bloco` aqui?
const corpo = this.resolverDeclaracao();
return new declaracoes_1.ParaCada(this.hashArquivo, Number(simboloPara.linha), new tuplas_1.Dupla(new construtos_1.Literal(this.hashArquivo, Number(simboloPara.linha), nomeVariavelChave.lexema), new construtos_1.Literal(this.hashArquivo, Number(simboloPara.linha), nomeVariavelValor.lexema)), dicionario, corpo);
}
declaracaoParaCadaVetor(simboloPara) {
const nomeVariavelIteracao = this.avancarEDevolverAnterior();
const variavelIteracao = new construtos_1.Variavel(this.hashArquivo, nomeVariavelIteracao);
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.DE, delegua_2.default.EM)) {
throw this.erro(this.simbolos[this.atual], "Esperado palavras reservadas 'em' ou 'de' após variável de iteração em instrução 'para cada'.");
}
const vetor = this.expressao();
if (!vetor.hasOwnProperty('tipo')) {
throw this.erro(simboloPara, `Variável ou constante em 'para cada' não parece possuir um tipo iterável.`);
}
const tipoVetor = vetor.tipo;
if (!tipoVetor.endsWith('[]') && !['dicionário', 'qualquer', 'vetor'].includes(tipoVetor)) {
throw this.erro(simboloPara, `Variável ou constante em 'para cada' não é iterável. Tipo resolvido: ${tipoVetor}.`);
}
let tipoVariavelIteracao = 'qualquer';
if (tipoVetor.endsWith('[]')) {
tipoVariavelIteracao = tipoVetor.slice(0, -2);
}
this.pilhaEscopos.definirInformacoesVariavel(nomeVariavelIteracao.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomeVariavelIteracao.lexema, tipoVariavelIteracao));
// TODO: Talvez não seja uma ideia melhor chamar o método de `Bloco` aqui?
const corpo = this.resolverDeclaracao();
return new declaracoes_1.ParaCada(this.hashArquivo, Number(simboloPara.linha), variavelIteracao, vetor, corpo);
}
declaracaoParaCada(simboloPara) {
if (this.verificarTipoSimboloAtual(delegua_2.default.IDENTIFICADOR)) {
return this.declaracaoParaCadaVetor(simboloPara);
}
if (this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_ESQUERDA)) {
return this.declaracaoParaCadaDicionario(simboloPara);
}
throw this.erro(simboloPara, 'Identificador de iteração deve ser ou um par chave-valor, ou um nome de variável.');
}
declaracaoParaTradicional(simboloPara) {
const comParenteses = this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO);
let inicializador;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA)) {
inicializador = null;
}
else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VARIAVEL)) {
inicializador = this.declaracaoDeVariaveis();
}
else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CONSTANTE)) {
inicializador = this.declaracaoDeConstantes();
}
else {
inicializador = this.declaracaoExpressao();
}
let condicao = null;
if (!this.verificarTipoSimboloAtual(delegua_2.default.PONTO_E_VIRGULA)) {
condicao = this.expressao();
}
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
let incrementar = null;
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
incrementar = this.expressao();
this.verificarSeSimboloAtualEIgualA(delegua_2.default.INCREMENTAR, delegua_2.default.DECREMENTAR);
}
if (comParenteses) {
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após cláusulas de inicialização, condição e incremento.");
}
// TODO: Talvez não seja uma ideia melhor chamar o método de `Bloco` aqui?
const corpo = this.resolverDeclaracao();
return new declaracoes_1.Para(this.hashArquivo, Number(simboloPara.linha), inicializador, condicao, incrementar, corpo);
}
declaracaoRetorna() {
const simboloChave = this.simbolos[this.atual - 1];
let valor = null;
if ([
delegua_2.default.CHAVE_ESQUERDA,
delegua_2.default.COLCHETE_ESQUERDO,
delegua_2.default.FALSO,
delegua_2.default.FUNCAO,
delegua_2.default.FUNÇÃO,
delegua_2.default.IDENTIFICADOR,
delegua_2.default.ISTO,
delegua_2.default.NEGACAO,
delegua_2.default.NUMERO,
delegua_2.default.NULO,
delegua_2.default.PARENTESE_ESQUERDO,
delegua_2.default.SUPER,
delegua_2.default.TEXTO,
delegua_2.default.VERDADEIRO,
].includes(this.simbolos[this.atual].tipo)) {
valor = this.expressao();
}
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Retorna(simboloChave, valor);
}
declaracaoSe() {
const condicao = this.expressao();
const caminhoEntao = this.resolverDeclaracao();
let caminhoSenao = null;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SENAO, delegua_2.default.SENÃO)) {
caminhoSenao = this.resolverDeclaracao();
}
return new declaracoes_1.Se(condicao, caminhoEntao, [], caminhoSenao);
}
declaracaoSustar() {
if (this.blocos < 1) {
throw this.erro(this.simbolos[this.atual - 1], "'sustar' ou 'pausa' deve estar dentro de um laço de repetição.");
}
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Sustar(this.simbolos[this.atual - 1]);
}
declaracaoTente() {
const simboloTente = this.simbolos[this.atual - 1];
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'tente'.");
const blocoTente = this.blocoEscopo();
let blocoPegue = null;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PEGUE)) {
if (this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_ESQUERDO)) {
// Caso 1: com parâmetro de erro.
// `pegue` recebe um `FuncaoConstruto`.
blocoPegue = this.corpoDaFuncao('bloco `pegue`');
}
else {
// Caso 2: sem parâmetro de erro.
// `pegue` recebe um bloco.
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'pegue'.");
blocoPegue = this.blocoEscopo();
}
}
let blocoSenao = null;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SENAO, delegua_2.default.SENÃO)) {
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'senão'.");
blocoSenao = this.blocoEscopo();
}
let blocoFinalmente = null;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.FINALMENTE)) {
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'finalmente'.");
blocoFinalmente = this.blocoEscopo();
}
return new declaracoes_1.Tente(simboloTente.hashArquivo, Number(simboloTente.linha), blocoTente, blocoPegue, blocoSenao, blocoFinalmente);
}
resolverDecorador() {
while (this.verificarTipoSimboloAtual(delegua_2.default.ARROBA)) {
let nomeDecorador = '';
let linha;
let parametros = [];
let parenteseEsquerdo = false;
linha = this.simbolos[this.atual].linha;
let simbolosLinhaAtual = this.simbolos.filter((l) => l.linha === linha);
for (let simbolo of simbolosLinhaAtual) {
parenteseEsquerdo = this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO);
if (parenteseEsquerdo) {
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
parametros = this.logicaComumParametros();
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros.");
break;
}
this.avancarEDevolverAnterior();
nomeDecorador += simbolo.lexema || '.';
}
const atributos = {};
for (const parametro of parametros) {
if (parametro.nome.lexema in atributos) {
throw this.erro(parametro.nome, `Atributo de decorador declarado duas ou mais vezes: ${parametro.nome.lexema}`);
}
atributos[parametro.nome.lexema] = parametro.valorPadrao;
}
this.pilhaDecoradores.push(new construtos_1.Decorador(this.hashArquivo, linha, nomeDecorador, atributos));
}
}
/**
* Todas as resoluções triviais da linguagem, ou seja, todas as
* resoluções que podem ocorrer dentro ou fora de um bloco.
* @returns Normalmente uma `Declaracao`, mas há casos em que
* outros objetos podem ser retornados.
* @see resolverDeclaracaoForaDeBloco para as declarações que não podem
* ocorrer em blocos de escopo elementares.
*/
resolverDeclaracao() {
switch (this.simbolos[this.atual].tipo) {
case delegua_2.default.CHAVE_ESQUERDA:
const simboloInicioBloco = this.avancarEDevolverAnterior();
return new declaracoes_1.Bloco(simboloInicioBloco.hashArquivo, Number(simboloInicioBloco.linha), this.blocoEscopo());
case delegua_2.default.COMENTARIO:
return this.declaracaoComentarioUmaLinha();
case delegua_2.default.CONSTANTE:
this.avancarEDevolverAnterior();
return this.declaracaoDeConstantes();
case delegua_2.default.CONTINUA:
this.avancarEDevolverAnterior();
return this.declaracaoContinua();
case delegua_2.default.ENQUANTO:
this.avancarEDevolverAnterior();
return this.declaracaoEnquanto();
case delegua_2.default.ESCOLHA:
this.avancarEDevolverAnterior();
return this.declaracaoEscolha();
case delegua_2.default.ESCREVA:
this.avancarEDevolverAnterior();
return this.declaracaoEscreva();
case delegua_2.default.FALHAR:
this.avancarEDevolverAnterior();
return this.declaracaoFalhar();
case delegua_2.default.FAZER:
this.avancarEDevolverAnterior();
return this.declaracaoFazer();
case delegua_2.default.LINHA_COMENTARIO:
return this.declaracaoComentarioMultilinha();
case delegua_2.default.PARA:
this.avancarEDevolverAnterior();
return this.declaracaoPara();
case delegua_2.default.PAUSA:
case delegua_2.default.SUSTAR:
this.avancarEDevolverAnterior();
return this.declaracaoSustar();
case delegua_2.default.SE:
this.avancarEDevolverAnterior();
return this.declaracaoSe();
case delegua_2.default.RETORNA:
this.avancarEDevolverAnterior();
return this.declaracaoRetorna();
case delegua_2.default.TENDO:
this.avancarEDevolverAnterior();
return this.declaracaoTendoComo();
case delegua_2.default.TENTE:
this.avancarEDevolverAnterior();
return this.declaracaoTente();
case delegua_2.default.VARIAVEL:
this.avancarEDevolverAnterior();
return this.declaracaoDeVariaveis();
}
const simboloAtual = this.simbolos[this.atual];
if (simboloAtual.tipo === delegua_2.default.IDENTIFICADOR) {
// Pela gramática, a seguinte situação não pode ocorrer:
// 1. O símbolo anterior ser um identificador; e
// 2. O símbolo anterior estar na mesma linha do identificador atual.
const simboloAnterior = this.simbolos[this.atual - 1];
if (!!simboloAnterior &&
simboloAnterior.tipo === delegua_2.default.IDENTIFICADOR &&
simboloAnterior.linha === simboloAtual.linha) {
throw this.erro(this.simbolos[this.atual], 'Não é permitido ter dois identificadores seguidos na mesma linha.');
}
}
return this.declaracaoExpressao();
}
declaracaoTendoComo() {
const simboloTendo = this.simbolos[this.atual - 1];
const expressaoInicializacao = this.expressao();
this.consumir(delegua_2.default.COMO, "Esperado palavra reservada 'como' após expressão de inicialização de variável, em declaração 'tendo'.");
const simboloNomeVariavel = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome do identificador em declaração 'tendo'.");
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado chave esquerda para abertura de bloco em declaração 'tendo'.");
let tipoInicializacao = 'qualquer';
switch (expressaoInicializacao.constructor.name) {
case 'Chamada':
const construtoChamada = expressaoInicializacao;
switch (construtoChamada.entidadeChamada.constructor.name) {
case 'Variavel':
const entidadeChamadaVariavel = construtoChamada.entidadeChamada;
tipoInicializacao = entidadeChamadaVariavel.tipo;
break;
// TODO: Demais casos
default:
break;
}
break;
// TODO: Demais casos
default:
break;
}
this.pilhaEscopos.definirInformacoesVariavel(simboloNomeVariavel.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simboloNomeVariavel.lexema, tipoInicializacao));
const blocoCorpo = this.blocoEscopo();
return new declaracoes_1.TendoComo(simboloTendo.linha, simboloTendo.hashArquivo, simboloNomeVariavel, expressaoInicializacao, new declaracoes_1.Bloco(simboloTendo.linha, simboloTendo.hashArquivo, blocoCorpo));
}
declaracaoDesestruturacaoVariavel() {
const identificadores = [];
do {
identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da variável.'));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
this.consumir(delegua_2.default.CHAVE_DIREITA, 'Esperado chave direita para concluir relação de variáveis a serem desestruturadas.');
this.consumir(delegua_2.default.IGUAL, 'Esperado igual após relação de propriedades da desestruturação.');
const inicializador = this.expressao();
const retornos = [];
for (let identificador of identificadores) {
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, this.logicaComumInferenciaTiposVariaveisEConstantes(inicializador, 'qualquer')));
const declaracaoVar = new declaracoes_1.Var(identificador, new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, inicializador, identificador));
declaracaoVar.decoradores = Array.from(this.pilhaDecoradores);
retornos.push(declaracaoVar);
}
this.pilhaDecoradores = [];
return retornos;
}
logicaComumInferenciaTiposVariaveisEConstantes(inicializador, tipo) {
if (tipo !== 'qualquer') {
return tipo;
}
switch (inicializador.constructor.name) {
case 'AcessoIndiceVariavel':
const entidadeChamadaAcessoIndiceVariavel = inicializador
.entidadeChamada;
// Este condicional ocorre com chamadas aninhadas. Por exemplo, `vetor[1][2]`.
if (entidadeChamadaAcessoIndiceVariavel.constructor.name === 'AcessoIndiceVariavel') {
return this.logicaComumInferenciaTiposVariaveisEConstantes(entidadeChamadaAcessoIndiceVariavel, tipo);
}
if (entidadeChamadaAcessoIndiceVariavel.tipo.endsWith('[]')) {
return entidadeChamadaAcessoIndiceVariavel.tipo.slice(0, -2);
}
// Normalmente, `entidadeChamadaAcessoIndiceVariavel.tipo` aqui será 'vetor'.
return 'qualquer';
case 'Chamada':
const entidadeChamadaChamada = inicializador.entidadeChamada;
switch (entidadeChamadaChamada.constructor.name) {
case 'AcessoMetodo':
const entidadeChamadaAcessoMetodo = entidadeChamadaChamada;
return entidadeChamadaAcessoMetodo.tipoRetornoMetodo;
case 'AcessoMetodoOuPropriedade':
// Este caso ocorre quando a variável/constante é do tipo 'qualquer',
// e a chamada normalmente é feita para uma primitiva.
// A inferência, portanto, ocorre pelo uso da primitiva.
const entidadeChamadaAcessoMetodoOuPropriedade = entidadeChamadaChamada;
for (const primitiva in this.primitivasConhecidas) {
if (this.primitivasConhecidas[primitiva].hasOwnProperty(entidadeChamadaAcessoMetodoOuPropriedade.simbolo.lexema)) {
return this.primitivasConhecidas[primitiva][entidadeChamadaAcessoMetodoOuPropriedade.simbolo.lexema].tipo;
}
}
throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamadaAcessoMetodoOuPropriedade.simbolo, `Primitiva '${entidadeChamadaAcessoMetodoOuPropriedade.simbolo.lexema}' não existe.`);
case 'AcessoPropriedade':
const entidadeChamadaAcessoPropriedade = entidadeChamadaChamada;
return entidadeChamadaAcessoPropriedade.tipoRetornoPropriedade;
case 'ArgumentoReferenciaFuncao':
// TODO: Voltar aqui se necessário.
return 'qualquer';
case 'ReferenciaFuncao':
const entidadeChamadaReferenciaFuncao = entidadeChamadaChamada;
return entidadeChamadaReferenciaFuncao.tipo;
case 'Variavel':
const entidadeChamadaVariavel = entidadeChamadaChamada;
return entidadeChamadaVariavel.tipo;
}
break;
case 'FuncaoConstruto':
const funcaoConstruto = inicializador;
return `função<${funcaoConstruto.tipo}>`;
case 'Leia':
return 'texto';
case 'Dupla':
case 'Trio':
case 'Quarteto':
case 'Quinteto':
case 'Sexteto':
case 'Septeto':
case 'Octeto':
case 'Noneto':
case 'Deceto':
return delegua_1.default.TUPLA;
case 'ImportarBiblioteca':
case 'ModuloDeclaracoes':
return 'módulo';
default:
return inicializador.tipo;
}
}
/**
* Caso símbolo atual seja `var`, devolve uma declaração de variável.
* @returns Um Construto do tipo Var.
*/
declaracaoDeVariaveis() {
const simboloVariavel = this.simboloAnterior();
const identificadores = [];
const retorno = [];
let tipo = 'qualquer';
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_ESQUERDA)) {
return this.declaracaoDesestruturacaoVariavel();
}
do {
identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da variável.'));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
tipo = this.verificarDefinicaoTipoAtual();
this.avancarEDevolverAnterior();
}
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL)) {
// Inicialização de variáveis sem valor.
for (let identificador of identificadores.values()) {
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, tipo));
retorno.push(new declaracoes_1.Var(identificador, null, tipo, Array.from(this.pilhaDecoradores)));
}
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
this.pilhaDecoradores = [];
return retorno;
}
const inicializadores = [];
do {
inicializadores.push(this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
if (identificadores.length !== inicializadores.length) {
throw this.erro(simboloVariavel, 'Quantidade de identificadores à esquerda do igual é diferente da quantidade de valores à direita.');
}
for (let [indice, identificador] of identificadores.entries()) {
tipo = this.logicaComumInferenciaTiposVariaveisEConstantes(inicializadores[indice], tipo);
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, tipo));
retorno.push(new declaracoes_1.Var(identificador, inicializadores[indice], tipo, Array.from(this.pilhaDecoradores)));
}
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
this.pilhaDecoradores = [];
return retorno;
}
declaracaoDesestruturacaoConstante() {
const identificadores = [];
do {
identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da variável.'));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
this.consumir(delegua_2.default.CHAVE_DIREITA, 'Esperado chave direita para concluir relação de variáveis a serem desestruturadas.');
this.consumir(delegua_2.default.IGUAL, 'Esperado igual após relação de propriedades da desestruturação.');
const inicializador = this.expressao();
const retornos = [];
for (let identificador of identificadores) {
// TODO: Melhorar dicionário para intuir o tipo de cada propriedade.
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, 'qualquer'));
const declaracaoConst = new declaracoes_1.Const(identificador, new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, inicializador, identificador));
declaracaoConst.decoradores = Array.from(this.pilhaDecoradores);
retornos.push(declaracaoConst);
}
return retornos;
}
/**
* Caso símbolo atual seja `const, constante ou fixo`, devolve uma declaração de const.
* @returns Um Construto do tipo Const.
*/
declaracaoDeConstantes() {
const simboloConstante = this.simboloAnterior();
const identificadores = [];
let tipo = 'qualquer';
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_ESQUERDA)) {
return this.declaracaoDesestruturacaoConstante();
}
do {
identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da constante.'));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
tipo = this.verificarDefinicaoTipoAtual();
this.avancarEDevolverAnterior();
}
this.consumir(delegua_2.default.IGUAL, "Esperado '=' após identificador em instrução 'constante'.");
const inicializadores = [];
do {
inicializadores.push(this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
if (identificadores.length !== inicializadores.length) {
throw this.erro(simboloConstante, 'Quantidade de identificadores à esquerda do igual é diferente da quantidade de valores à direita.');
}
let retorno = [];
for (let [indice, identificador] of identificadores.entries()) {
// Se tipo ainda não foi definido, infere.
tipo = this.logicaComumInferenciaTiposVariaveisEConstantes(inicializadores[indice], tipo);
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, tipo));
retorno.push(new declaracoes_1.Const(identificador, inicializadores[indice], tipo, Array.from(this.pilhaDecoradores)));
}
this.pilhaDecoradores = [];
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return retorno;
}
funcao(tipo) {
let simbolo;
switch (this.simbolos[this.atual].tipo) {
case delegua_2.default.CONSTRUTOR:
simbolo = this.avancarEDevolverAnterior();
break;
default:
simbolo = this.consumir(delegua_2.default.IDENTIFICADOR, `Esperado nome de ${tipo}.`);
break;
}
const decoradores = Array.from(this.pilhaDecoradores);
this.pilhaDecoradores = [];
// Se houver chamadas recursivas à função, precisamos definir um tipo
// para ela. Vai ser atualizado após avaliação do corpo da função.
this.pilhaEscopos.definirInformacoesVariavel(simbolo.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simbolo.lexema, 'qualquer'));
const corpoDaFuncao = this.corpoDaFuncao(tipo);
const tipoDaFuncao = `função<${corpoDaFuncao.tipo}>`;
this.pilhaEscopos.definirInformacoesVariavel(simbolo.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simbolo.lexema, tipoDaFuncao));
const funcaoDeclaracao = new declaracoes_1.FuncaoDeclaracao(simbolo, corpoDaFuncao, tipoDaFuncao, decoradores);
this.pilhaEscopos.registrarReferenciaFuncao(simbolo.lexema, funcaoDeclaracao);
return funcaoDeclaracao;
}
logicaComumParametros() {
const parametros = [];
do {
const parametro = {};
if (this.simbolos[this.atual].tipo === delegua_2.default.MULTIPLICACAO) {
this.consumir(delegua_2.default.MULTIPLICACAO, null);
parametro.abrangencia = 'multiplo';
}
else {
parametro.abrangencia = 'padrao';
}
parametro.nome = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome do parâmetro.');
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL)) {
const valorPadrao = this.primario();
parametro.valorPadrao = valorPadrao;
}
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
let tipoDadoParametro = this.verificarDefinicaoTipoAtual();
parametro.tipoDado = tipoDadoParametro;
this.avancarEDevolverAnterior();
}
this.pilhaEscopos.definirInformacoesVariavel(parametro.nome.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(parametro.nome.lexema, parametro.tipoDado || 'qualquer'));
parametros.push(parametro);
if (parametro.abrangencia === 'multiplo')
break;
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
return parametros;
}
*buscarRetornosEmBloco(construtoBloco) {
for (const declaracao of construtoBloco.declaracoes) {
if (declaracao.constructor.name === 'Retorna') {
yield declaracao;
}
}
}
*buscarRetornosEmSe(construtoSe) {
const blocoEntao = construtoSe.caminhoEntao;
for (const declaracao of this.buscarRetornosEmBloco(blocoEntao)) {
if (declaracao.constructor.name === 'Retorna') {
yield declaracao;
}
}
if (!construtoSe.caminhoSenao)
return;
switch (construtoSe.caminhoSenao.constructor.name) {
case 'Bloco':
const blocoSenao = construtoSe.caminhoSenao;
for (const declaracao of blocoSenao.declaracoes) {
if (declaracao.constructor.name === 'Retorna') {
yield declaracao;
}
}
break;
case 'Se':
const senaoSe = construtoSe.caminhoSenao;
for (const declaracao of this.buscarRetornosEmSe(senaoSe)) {
if (declaracao.constructor.name === 'Retorna') {
yield declaracao;
}
}
break;
}
}
buscarRetornos(declaracao) {
let retornasEncontrados = [];
switch (declaracao.constructor.name) {
case 'Retorna':
retornasEncontrados.push(declaracao);
break;
case 'Se':
for (const retorna of this.buscarRetornosEmSe(declaracao)) {
retornasEncontrados.push(retorna);
}
break;
default:
break;
}
return retornasEncontrados;
}
corpoDaFuncao(tipo) {
// O parêntese esquerdo é considerado o símbolo inicial para
// fins de pragma.
const parenteseEsquerdo = this.consumir(delegua_2.default.PARENTESE_ESQUERDO, `Esperado '(' após o nome ${tipo}.`);
let parametros = [];
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
parametros = this.logicaComumParametros();
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros.");
let tipoRetorno = 'qualquer';
let definicaoExplicitaDeTipo = false;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
tipoRetorno = this.verificarDefinicaoTipoAtual();
this.avancarEDevolverAnterior();
definicaoExplicitaDeTipo = true;
}
this.consumir(delegua_2.default.CHAVE_ESQUERDA, `Esperado '{' antes do escopo do ${tipo}.`);
const corpo = this.blocoEscopo();
let expressoesRetorna = [];
for (const declaracao of corpo) {
expressoesRetorna = expressoesRetorna.concat(this.buscarRetornos(declaracao));
}
if (tipoRetorno === 'vazio' && expressoesRetorna.length > 0) {
const retornosNaoVazios = expressoesRetorna.filter((e) => e.tipo !== 'vazio');
if (retornosNaoVazios.length > 0) {
throw this.erro(retornosNaoVazios[0].simboloChave, `Função declara explicitamente 'vazio', mas usa expressão 'retorna' com tipo de retorno diferente de vazio.`);
}
}
const tiposRetornos = new Set(expressoesRetorna.map((e) => e.tipo));
let retornaChamadoExplicitamente = tiposRetornos.size > 0;
if (tiposRetornos.size > 1 && tipoRetorno !== 'qualquer') {
let tiposEncontrados = Array.from(tiposRetornos).reduce((acumulador, valor) => (acumulador += valor + ', '), '');
tiposEncontrados = tiposEncontrados.slice(0, -2);
throw this.erro(parenteseEsquerdo, `Função retorna valores com mais de um tipo. Tipo esperado: ${tipoRetorno}. Tipos encontrados: ${tiposEncontrados}.`);
}
tiposRetornos.delete('qualquer');
if (tipoRetorno === 'qualquer') {
if (tiposRetornos.size > 0) {
// Se o tipo de retorno é 'qualquer', seja implícito ou explícito,
// este avaliador sintático pode restringir o tipo baseado nos construtos
// de retornos encontrados nos blocos internos da função.
const tipoRetornoDeduzido = tiposRetornos.values().next().value;
tipoRetorno = tipoRetornoDeduzido;
}
else if (!retornaChamadoExplicitamente && !definicaoExplicitaDeTipo) {
// Ou, se esses retornos sequer existem, e o tipo explícito não é
// 'qualquer', o tipo inferido é 'vazio'.
tipoRetorno = 'vazio';
}
}
return new construtos_1.FuncaoConstruto(this.hashArquivo, Number(parenteseEsquerdo.linha), parametros, corpo, tipoRetorno, definicaoExplicitaDeTipo);
}
declaracaoDeClasse() {
const simbolo = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da classe.');
const pilhaDecoradoresClasse = Array.from(this.pilhaDecoradores);
let superClasse = null;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.HERDA)) {
const simboloSuperclasse = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da Superclasse.');
// TODO: Validar classes existentes?
this.superclasseAtual = simboloSuperclasse.lexema;
// TODO: Colocar tipo aqui?
superClasse = new construtos_1.Variavel(this.hashArquivo, this.simbolos[this.atual - 1], simboloSuperclasse.lexema);
}
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do escopo da classe.");
this.pilhaDecoradores = [];
const metodos = [];
const propriedades = [];
while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
!this.estaNoFinal()) {
// Se o símbolo atual é arroba, é um decorador.
// Caso contrário, verificamos o próximo símbolo.
if (this.simbolos[this.atual].tipo === delegua_2.default.ARROBA) {
this.resolverDecorador();
continue;
}
// Se o próximo símbolo ao atual for um parênteses, é um método.
// Caso contrário, é uma propriedade.
const proximoSimbolo = this.simbolos[this.atual + 1];
switch (proximoSimbolo.tipo) {
case delegua_2.default.PARENTESE_ESQUERDO:
metodos.push(this.funcao('método'));
break;
case delegua_2.default.DOIS_PONTOS:
const nomePropriedade = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado identificador para nome de propriedade.');
this.consumir(delegua_2.default.DOIS_PONTOS, 'Esperado dois-pontos após nome de propriedade.');
const tipoPropriedade = this.avancarEDevolverAnterior();
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
propriedades.push(new declaracoes_1.PropriedadeClasse(nomePropriedade, tipoPropriedade.lexema, Array.from(this.pilhaDecoradores)));
this.pilhaDecoradores = [];
break;
default:
throw this.erro(this.simbolos[this.atual], 'Esperado definição de método ou propriedade.');
}
}
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após o escopo da classe.");
const definicaoClasse = new declaracoes_1.Classe(simbolo, superClasse, metodos, propriedades, pilhaDecoradoresClasse);
this.tiposDefinidosEmCodigo[definicaoClasse.simbolo.lexema] = definicaoClasse;
this.superclasseAtual = undefined;
return definicaoClasse;
}
/**
* Declarações fora de bloco precisam ser verificadas primeiro por
* uma série de motivos, como, por exemplo:
*
* - Não é possível declarar uma classe/função dentro de um bloco `enquanto`,
* `fazer ... enquanto`, `para`, `escolha`, etc;
* - Qualquer declaração pode ter um decorador.
* @returns Uma função ou classe se o símbolo atual resolver aqui.
* O retorno de `resolverDeclaracao()` em caso contrário.
* @see resolverDeclaracao
* @see resolverDecorador
*/
resolverDeclaracaoForaDeBloco() {
try {
while (this.verificarTipoSimboloAtual(delegua_2.default.ARROBA)) {
this.resolverDecorador();
}
if ((this.verificarTipoSimboloAtual(delegua_2.default.FUNCAO) ||
this.verificarTipoSimboloAtual(delegua_2.default.FUNÇÃO)) &&
this.verificarTipoProximoSimbolo(delegua_2.default.IDENTIFICADOR)) {
this.avancarEDevolverAnterior();
return this.funcao('funcao');
}
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CLASSE)) {
return this.declaracaoDeClasse();
}
return this.resolverDeclaracao();
}
catch (erro) {
this.sincronizar();
this.erros.push(erro);
return null;
}
}
/**
* Usado quando há erros na avaliação sintática.
* Garante que o código não entre em loop infinito.
* @returns Sempre retorna `void`.
*/
sincronizar() {
this.avancarEDevolverAnterior();
while (!this.estaNoFinal()) {
const tipoSimboloAtual = this.simbolos[this.atual - 1].tipo;
switch (tipoSimboloAtual) {
case delegua_2.default.CLASSE:
case delegua_2.default.FUNCAO:
case delegua_2.default.FUNÇÃO:
case delegua_2.default.VARIAVEL:
case delegua_2.default.PARA:
case delegua_2.default.SE:
case delegua_2.default.ENQUANTO:
case delegua_2.default.ESCREVA:
case delegua_2.default.RETORNA:
return;
}
this.avancarEDevolverAnterior();
}
}
/**
* Inicializa o primeiro nível da pilha de escopos, normalmente com ítens da biblioteca global.
* É separada da inicialização do avaliador sintático, pois é necessário manipular essa
* inicialização de outra forma em `delegua-node`.
*/
inicializarPilhaEscopos() {
this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
// Funções nativas de Delégua
this.pilhaEscopos.definirInformacoesVariavel('aleatorio', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('aleatorio', 'número'));
this.pilhaEscopos.definirInformacoesVariavel('aleatorioEntre', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('aleatorioEntre', 'número', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('minimo', 'número'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('maximo', 'número'),
]));
this.pilhaEscopos.definirInformacoesVariavel('algum', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('algum', 'lógico', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrar', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('encontrar', 'qualquer', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrarIndice', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('encontrarIndice', 'inteiro', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrarUltimo', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('encontrarUltimo', 'inteiro', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrarUltimoIndice', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('encontrarUltimoIndice', 'inteiro', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('filtrarPor', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('filtrarPor', 'qualquer[]', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoFiltragem', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('incluido', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('incluido', 'lógico', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valor', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('inteiro', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('inteiro', 'inteiro', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valor', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('mapear', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('mapear', 'qualquer[]', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoMapeamento', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('numero', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('número', 'número', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valorParaConverter', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('número', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('número', 'número', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valorParaConverter', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('ordenar', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('ordenar', 'qualquer[]', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoOrdenacao', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('paraCada', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('paraCada', 'qualquer[]', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoFiltragem', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('primeiroEmCondicao', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('primeiroEmCondicao', 'qualquer', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoFiltragem', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('real', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('real', 'número', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valorParaConverter', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('reduzir', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('reduzir', 'qualquer', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoReducao', 'função'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valorInicial', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('tamanho', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('tamanho', 'inteiro', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('objeto', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('texto', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('texto', 'texto', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valorParaConverter', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('todosEmCondicao', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('todosEmCondicao', 'lógico', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoCondicional', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('tupla', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('tupla', 'tupla', true, [
new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
]));
// TODO: Escrever algum tipo de validação aqui.
for (const tipos of Object.values(this.tiposDeFerramentasExternas)) {
for (const [nomeTipo, tipo] of Object.entries(tipos)) {
this.pilhaEscopos.definirInformacoesVariavel(nomeTipo, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomeTipo, tipo));
}
}
}
analisar(retornoLexador, hashArquivo) {
const inicioAnalise = (0, browser_process_hrtime_1.default)();
this.erros = [];
this.atual = 0;
this.blocos = 0;
this.hashArquivo = hashArquivo || 0;
this.simbolos = (retornoLexador === null || retornoLexador === void 0 ? void 0 : retornoLexador.simbolos) || [];
this.pilhaDecoradores = [];
this.tiposDefinidosEmCodigo = {};
this.inicializarPilhaEscopos();
let declaracoes = [];
while (!this.estaNoFinal()) {
this.resolverDecorador();
const retornoDeclaracao = this.resolverDeclaracaoForaDeBloco();
if (Array.isArray(retornoDeclaracao)) {
declaracoes = declaracoes.concat(retornoDeclaracao);
}
else if (retornoDeclaracao !== null) {
declaracoes.push(retornoDeclaracao);
}
}
if (this.performance) {
const deltaAnalise = (0, browser_process_hrtime_1.default)(inicioAnalise);
// eslint-disable-next-line no-undef
console.log(`[Avaliador Sintático] Tempo para análise: ${deltaAnalise[0] * 1e9 + deltaAnalise[1]}ns`);
}
return {
declaracoes: declaracoes,
erros: this.erros,
};
}
}
exports.AvaliadorSintatico = AvaliadorSintatico;
//# sourceMappingURL=avaliador-sintatico.js.map