"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AvaliadorSintaticoEguaClassico = void 0;
const construtos_1 = require("../../construtos");
const erro_avaliador_sintatico_1 = require("../erro-avaliador-sintatico");
const declaracoes_1 = require("../../declaracoes");
const egua_classico_1 = __importDefault(require("../../tipos-de-simbolos/egua-classico"));
/**
 * 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.
 *
 * Esta implementação tenta seguir à risca o que está atualmente em https://github.com/eguatech/egua/blob/master/src/parser.js.
 */
class AvaliadorSintaticoEguaClassico {
    constructor(simbolos) {
        this.simbolos = simbolos;
        this.atual = 0;
        this.blocos = 0;
    }
    declaracaoDeConstantes() {
        throw new Error('Método não implementado.');
    }
    declaracaoDeVariaveis() {
        throw new Error('Método não implementado.');
    }
    declaracaoLeia() {
        throw new Error('Método não implementado.');
    }
    sincronizar() {
        this.avancarEDevolverAnterior();
        while (!this.estaNoFinal()) {
            if (this.simboloAnterior().tipo === egua_classico_1.default.PONTO_E_VIRGULA)
                return;
            switch (this.simboloAtual().tipo) {
                case egua_classico_1.default.CLASSE:
                case egua_classico_1.default.FUNCAO:
                case egua_classico_1.default.VARIAVEL:
                case egua_classico_1.default.PARA:
                case egua_classico_1.default.SE:
                case egua_classico_1.default.ENQUANTO:
                case egua_classico_1.default.ESCREVA:
                case egua_classico_1.default.RETORNA:
                    return;
            }
            this.avancarEDevolverAnterior();
        }
    }
    erro(simbolo, mensagemDeErro) {
        const excecao = new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(simbolo, mensagemDeErro);
        this.erros.push(excecao);
        return excecao;
    }
    consumir(tipo, mensagemDeErro) {
        if (this.verificarTipoSimboloAtual(tipo))
            return this.avancarEDevolverAnterior();
        else
            throw this.erro(this.simboloAtual(), mensagemDeErro);
    }
    verificarTipoSimboloAtual(tipo) {
        if (this.estaNoFinal())
            return false;
        return this.simboloAtual().tipo === tipo;
    }
    verificarTipoProximoSimbolo(tipo) {
        if (this.estaNoFinal())
            return false;
        return this.simbolos[this.atual + 1].tipo === tipo;
    }
    simboloAtual() {
        return this.simbolos[this.atual];
    }
    simboloAnterior() {
        return this.simbolos[this.atual - 1];
    }
    simboloNaPosicao(posicao) {
        return this.simbolos[this.atual + posicao];
    }
    estaNoFinal() {
        return this.simboloAtual().tipo === egua_classico_1.default.EOF;
    }
    avancarEDevolverAnterior() {
        if (!this.estaNoFinal())
            this.atual += 1;
        return this.simboloAnterior();
    }
    verificarSeSimboloAtualEIgualA(...argumentos) {
        for (let i = 0; i < argumentos.length; i++) {
            const tipoAtual = argumentos[i];
            if (this.verificarTipoSimboloAtual(tipoAtual)) {
                this.avancarEDevolverAnterior();
                return true;
            }
        }
        return false;
    }
    primario() {
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.SUPER)) {
            const simboloChave = this.simboloAnterior();
            return new construtos_1.Super(this.hashArquivo, simboloChave, this.superclasseAtual);
            /* this.consumir(tiposDeSimbolos.PONTO, "Esperado '.' após 'super'.");
            const metodo = this.consumir(tiposDeSimbolos.IDENTIFICADOR, 'Esperado nome do método da Superclasse.');
            return new Super(this.hashArquivo, simboloChave, metodo); */
        }
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.COLCHETE_ESQUERDO)) {
            const valores = [];
            if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.COLCHETE_DIREITO)) {
                return new construtos_1.Vetor(this.hashArquivo, 0, []);
            }
            while (!this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.COLCHETE_DIREITO)) {
                const valor = this.atribuir();
                valores.push(valor);
                if (this.simboloAtual().tipo !== egua_classico_1.default.COLCHETE_DIREITO) {
                    this.consumir(egua_classico_1.default.VIRGULA, 'Esperado vírgula antes da próxima expressão.');
                }
            }
            return new construtos_1.Vetor(this.hashArquivo, 0, valores);
        }
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.CHAVE_ESQUERDA)) {
            const chaves = [];
            const valores = [];
            if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.CHAVE_DIREITA)) {
                return new construtos_1.Dicionario(this.hashArquivo, 0, [], []);
            }
            while (!this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.CHAVE_DIREITA)) {
                const chave = this.atribuir();
                this.consumir(egua_classico_1.default.DOIS_PONTOS, "Esperado ':' entre chave e valor.");
                const valor = this.atribuir();
                chaves.push(chave);
                valores.push(valor);
                if (this.simboloAtual().tipo !== egua_classico_1.default.CHAVE_DIREITA) {
                    this.consumir(egua_classico_1.default.VIRGULA, 'Esperado vírgula antes da próxima expressão.');
                }
            }
            return new construtos_1.Dicionario(this.hashArquivo, 0, chaves, valores);
        }
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.FUNCAO))
            return this.corpoDaFuncao('função');
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.FALSO))
            return new construtos_1.Literal(this.hashArquivo, 0, false);
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.VERDADEIRO))
            return new construtos_1.Literal(this.hashArquivo, 0, true);
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.NULO))
            return new construtos_1.Literal(this.hashArquivo, 0, null);
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.ISTO))
            return new construtos_1.Isto(this.hashArquivo, Number(this.simboloAnterior()));
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.NUMERO, egua_classico_1.default.TEXTO)) {
            return new construtos_1.Literal(this.hashArquivo, 0, this.simboloAnterior().literal);
        }
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.IDENTIFICADOR)) {
            return new construtos_1.Variavel(this.hashArquivo, this.simboloAnterior());
        }
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.PARENTESE_ESQUERDO)) {
            const expressao = this.expressao();
            this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado ')' após a expressão.");
            return new construtos_1.Agrupamento(this.hashArquivo, 0, expressao);
        }
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.IMPORTAR))
            return this.construtoImportar();
        throw this.erro(this.simboloAtual(), 'Esperado expressão.');
    }
    finalizarChamada(entidadeChamada) {
        const argumentos = [];
        if (!this.verificarTipoSimboloAtual(egua_classico_1.default.PARENTESE_DIREITO)) {
            do {
                if (argumentos.length >= 255) {
                    throw this.erro(this.simboloAtual(), 'Não pode haver mais de 255 argumentos.');
                }
                argumentos.push(this.expressao());
            } while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.VIRGULA));
        }
        this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado ')' após os argumentos.");
        return new construtos_1.Chamada(this.hashArquivo, entidadeChamada, argumentos);
    }
    chamar() {
        let expressao = this.primario();
        while (true) {
            if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.PARENTESE_ESQUERDO)) {
                expressao = this.finalizarChamada(expressao);
            }
            else if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.PONTO)) {
                const nome = this.consumir(egua_classico_1.default.IDENTIFICADOR, "Esperado nome do método após '.'.");
                expressao = new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, expressao, nome);
            }
            else if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.COLCHETE_ESQUERDO)) {
                const indice = this.expressao();
                const simboloFechamento = this.consumir(egua_classico_1.default.COLCHETE_DIREITO, "Esperado ']' após escrita do indice.");
                expressao = new construtos_1.AcessoIndiceVariavel(this.hashArquivo, expressao, indice, simboloFechamento);
            }
            else {
                break;
            }
        }
        return expressao;
    }
    unario() {
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.NEGACAO, egua_classico_1.default.SUBTRACAO, egua_classico_1.default.BIT_NOT)) {
            const operador = this.simboloAnterior();
            const direito = this.unario();
            return new construtos_1.Unario(this.hashArquivo, operador, direito);
        }
        return this.chamar();
    }
    /**
     * A exponenciacão de Égua [é implementada com resolução à esquerda](https://github.com/eguadev/egua/blob/main/src/parser.js#L230).
     * Por isso esse dialeto resolve `direito` chamando `unario()`, e não `exponenciacao()` como os demais.
     * @returns {Binario} A expressão binária na forma do construto `Binario`.
     */
    exponenciacao() {
        let expressao = this.unario();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.EXPONENCIACAO)) {
            const operador = this.simboloAnterior();
            const direito = this.unario();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    multiplicar() {
        let expressao = this.exponenciacao();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.DIVISAO, egua_classico_1.default.MULTIPLICACAO, egua_classico_1.default.MODULO)) {
            const operador = this.simboloAnterior();
            const direito = this.exponenciacao();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    adicionar() {
        let expressao = this.multiplicar();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.SUBTRACAO, egua_classico_1.default.ADICAO)) {
            const operador = this.simboloAnterior();
            const direito = this.multiplicar();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    bitShift() {
        let expressao = this.adicionar();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.MENOR_MENOR, egua_classico_1.default.MAIOR_MAIOR)) {
            const operador = this.simboloAnterior();
            const direito = this.adicionar();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    bitE() {
        let expressao = this.bitShift();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.BIT_AND)) {
            const operador = this.simboloAnterior();
            const direito = this.bitShift();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    bitOu() {
        let expressao = this.bitE();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.BIT_OR, egua_classico_1.default.BIT_XOR)) {
            const operador = this.simboloAnterior();
            const direito = this.bitE();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    comparar() {
        let expressao = this.bitOu();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.MAIOR, egua_classico_1.default.MAIOR_IGUAL, egua_classico_1.default.MENOR, egua_classico_1.default.MENOR_IGUAL)) {
            const operador = this.simboloAnterior();
            const direito = this.bitOu();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    comparacaoIgualdade() {
        let expressao = this.comparar();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.DIFERENTE, egua_classico_1.default.IGUAL_IGUAL)) {
            const operador = this.simboloAnterior();
            const direito = this.comparar();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    em() {
        let expressao = this.comparacaoIgualdade();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.EM)) {
            const operador = this.simboloAnterior();
            const direito = this.comparacaoIgualdade();
            expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    e() {
        let expressao = this.em();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.E)) {
            const operador = this.simboloAnterior();
            const direito = this.em();
            expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    ou() {
        let expressao = this.e();
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.OU)) {
            const operador = this.simboloAnterior();
            const direito = this.e();
            expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    atribuir() {
        const expressao = this.ou();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.IGUAL)) {
            const igual = this.simboloAnterior();
            const valor = this.atribuir();
            if (expressao instanceof construtos_1.Variavel) {
                return new construtos_1.Atribuir(this.hashArquivo, expressao, valor);
            }
            else if (expressao instanceof construtos_1.AcessoMetodoOuPropriedade) {
                const get = expressao;
                return new construtos_1.DefinirValor(this.hashArquivo, 0, get.objeto, get.simbolo, valor);
            }
            else if (expressao instanceof construtos_1.AcessoIndiceVariavel) {
                return new construtos_1.AtribuicaoPorIndice(this.hashArquivo, 0, expressao.entidadeChamada, expressao.indice, valor);
            }
            throw this.erro(igual, 'Tarefa de atribuição inválida');
        }
        return expressao;
    }
    expressao() {
        return this.atribuir();
    }
    declaracaoEscreva() {
        const simboloAtual = this.simboloAtual();
        this.consumir(egua_classico_1.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos valores em escreva.");
        const valor = this.expressao();
        this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado ')' após os valores em escreva.");
        this.consumir(egua_classico_1.default.PONTO_E_VIRGULA, "Esperado ';' após o valor.");
        return new declaracoes_1.Escreva(Number(simboloAtual.linha), simboloAtual.hashArquivo, [valor]);
    }
    declaracaoExpressao() {
        const expressao = this.expressao();
        this.consumir(egua_classico_1.default.PONTO_E_VIRGULA, "Esperado ';' após expressão.");
        return new declaracoes_1.Expressao(expressao);
    }
    blocoEscopo() {
        const declaracoes = [];
        while (!this.verificarTipoSimboloAtual(egua_classico_1.default.CHAVE_DIREITA) &&
            !this.estaNoFinal()) {
            declaracoes.push(this.resolverDeclaracaoForaDeBloco());
        }
        this.consumir(egua_classico_1.default.CHAVE_DIREITA, "Esperado '}' após o bloco.");
        return declaracoes;
    }
    declaracaoSe() {
        this.consumir(egua_classico_1.default.PARENTESE_ESQUERDO, "Esperado '(' após 'se'.");
        const condicao = this.expressao();
        this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado ')' após condição do se.");
        const caminhoEntao = this.resolverDeclaracao();
        const caminhosSeSenao = [];
        while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.SENÃOSE)) {
            this.consumir(egua_classico_1.default.PARENTESE_ESQUERDO, "Esperado '(' após 'senãose'.");
            const condicaoSeSenao = this.expressao();
            this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado ')' apóes codição do 'senãose.");
            const caminho = this.resolverDeclaracao();
            caminhosSeSenao.push({
                condicao: condicaoSeSenao,
                caminho: caminho,
            });
        }
        let caminhoSenao = null;
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.SENÃO)) {
            caminhoSenao = this.resolverDeclaracao();
        }
        return new declaracoes_1.Se(condicao, caminhoEntao, caminhosSeSenao, caminhoSenao);
    }
    declaracaoEnquanto() {
        try {
            this.blocos += 1;
            this.consumir(egua_classico_1.default.PARENTESE_ESQUERDO, "Esperado '(' após 'enquanto'.");
            const condicao = this.expressao();
            this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado ')' após condicional.");
            const corpo = this.resolverDeclaracao();
            return new declaracoes_1.Enquanto(condicao, corpo);
        }
        finally {
            this.blocos -= 1;
        }
    }
    declaracaoPara() {
        try {
            const simboloPara = this.simboloAnterior();
            this.blocos += 1;
            this.consumir(egua_classico_1.default.PARENTESE_ESQUERDO, "Esperado '(' após 'para'.");
            let inicializador;
            if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.PONTO_E_VIRGULA)) {
                inicializador = null;
            }
            else if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.VARIAVEL)) {
                inicializador = this.declaracaoDeVariavel();
            }
            else {
                inicializador = this.declaracaoExpressao();
            }
            let condicao = null;
            if (!this.verificarTipoSimboloAtual(egua_classico_1.default.PONTO_E_VIRGULA)) {
                condicao = this.expressao();
            }
            this.consumir(egua_classico_1.default.PONTO_E_VIRGULA, "Esperado ';' após valores da condicional");
            let incrementar = null;
            if (!this.verificarTipoSimboloAtual(egua_classico_1.default.PARENTESE_DIREITO)) {
                incrementar = this.expressao();
            }
            this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado ')' após cláusulas");
            const corpo = this.resolverDeclaracao();
            return new declaracoes_1.Para(this.hashArquivo, Number(simboloPara.linha), inicializador, condicao, incrementar, corpo);
        }
        finally {
            this.blocos -= 1;
        }
    }
    declaracaoSustar() {
        if (this.blocos < 1) {
            throw this.erro(this.simboloAnterior(), "'pausa' deve estar dentro de um loop.");
        }
        this.consumir(egua_classico_1.default.PONTO_E_VIRGULA, "Esperado ';' após 'pausa'.");
        return new declaracoes_1.Sustar(this.simboloAtual());
    }
    declaracaoContinua() {
        if (this.blocos < 1) {
            throw this.erro(this.simboloAnterior(), "'continua' precisa estar em um laço de repetição.");
        }
        this.consumir(egua_classico_1.default.PONTO_E_VIRGULA, "Esperado ';' após 'continua'.");
        return new declaracoes_1.Continua(this.simboloAtual());
    }
    declaracaoRetorna() {
        const palavraChave = this.simboloAnterior();
        let valor = null;
        if (!this.verificarTipoSimboloAtual(egua_classico_1.default.PONTO_E_VIRGULA)) {
            valor = this.expressao();
        }
        this.consumir(egua_classico_1.default.PONTO_E_VIRGULA, "Esperado ';' após o retorno.");
        return new declaracoes_1.Retorna(palavraChave, valor);
    }
    declaracaoEscolha() {
        try {
            this.blocos += 1;
            this.consumir(egua_classico_1.default.PARENTESE_ESQUERDO, "Esperado '{' após 'escolha'.");
            const condicao = this.expressao();
            this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado '}' após a condição de 'escolha'.");
            this.consumir(egua_classico_1.default.CHAVE_ESQUERDA, "Esperado '{' antes do escopo do 'escolha'.");
            const caminhos = [];
            let caminhoPadrao = null;
            while (!this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.CHAVE_DIREITA) &&
                !this.estaNoFinal()) {
                if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.CASO)) {
                    const caminhoCondicoes = [this.expressao()];
                    this.consumir(egua_classico_1.default.DOIS_PONTOS, "Esperado ':' após o 'caso'.");
                    while (this.verificarTipoSimboloAtual(egua_classico_1.default.CASO)) {
                        this.consumir(egua_classico_1.default.CASO, null);
                        caminhoCondicoes.push(this.expressao());
                        this.consumir(egua_classico_1.default.DOIS_PONTOS, "Esperado ':' após declaração do 'caso'.");
                    }
                    const declaracoes = [];
                    do {
                        declaracoes.push(this.resolverDeclaracao());
                    } while (!this.verificarTipoSimboloAtual(egua_classico_1.default.CASO) &&
                        !this.verificarTipoSimboloAtual(egua_classico_1.default.PADRAO) &&
                        !this.verificarTipoSimboloAtual(egua_classico_1.default.CHAVE_DIREITA));
                    caminhos.push({
                        condicoes: caminhoCondicoes,
                        declaracoes,
                    });
                }
                else if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.PADRAO)) {
                    if (caminhoPadrao !== null) {
                        const excecao = new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(this.simboloAtual(), "Você só pode ter um 'padrao' em cada declaração de 'escolha'.");
                        this.erros.push(excecao);
                        throw excecao;
                    }
                    this.consumir(egua_classico_1.default.DOIS_PONTOS, "Esperado ':' após declaração do 'padrao'.");
                    const declaracoes = [];
                    do {
                        declaracoes.push(this.resolverDeclaracao());
                    } while (!this.verificarTipoSimboloAtual(egua_classico_1.default.CASO) &&
                        !this.verificarTipoSimboloAtual(egua_classico_1.default.PADRAO) &&
                        !this.verificarTipoSimboloAtual(egua_classico_1.default.CHAVE_DIREITA));
                    caminhoPadrao = {
                        declaracoes,
                    };
                }
            }
            return new declaracoes_1.Escolha(condicao, caminhos, caminhoPadrao);
        }
        finally {
            this.blocos -= 1;
        }
    }
    construtoImportar() {
        this.consumir(egua_classico_1.default.PARENTESE_ESQUERDO, "Esperado '(' após declaração.");
        const caminho = this.expressao();
        this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado ')' após declaração.");
        return new construtos_1.ImportarComoConstruto(caminho);
    }
    declaracaoTente() {
        this.consumir(egua_classico_1.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'tente'.");
        const tryBlock = this.blocoEscopo();
        let catchBlock = null;
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.PEGUE)) {
            this.consumir(egua_classico_1.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'pegue'.");
            catchBlock = this.blocoEscopo();
        }
        let elseBlock = null;
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.SENÃO)) {
            this.consumir(egua_classico_1.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'pegue'.");
            elseBlock = this.blocoEscopo();
        }
        let finallyBlock = null;
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.FINALMENTE)) {
            this.consumir(egua_classico_1.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'pegue'.");
            finallyBlock = this.blocoEscopo();
        }
        return new declaracoes_1.Tente(0, 0, tryBlock, catchBlock, elseBlock, finallyBlock);
    }
    declaracaoFazer() {
        try {
            this.blocos += 1;
            const caminhoFazer = this.resolverDeclaracao();
            this.consumir(egua_classico_1.default.ENQUANTO, "Esperado declaração do 'enquanto' após o escopo do 'fazer'.");
            this.consumir(egua_classico_1.default.PARENTESE_ESQUERDO, "Esperado '(' após declaração 'enquanto'.");
            const condicaoEnquanto = this.expressao();
            this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado ')' após declaração do 'enquanto'.");
            return new declaracoes_1.Fazer(0, 0, caminhoFazer, condicaoEnquanto);
        }
        finally {
            this.blocos -= 1;
        }
    }
    resolverDeclaracao() {
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.FAZER))
            return this.declaracaoFazer();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.TENTE))
            return this.declaracaoTente();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.ESCOLHA))
            return this.declaracaoEscolha();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.RETORNA))
            return this.declaracaoRetorna();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.CONTINUA))
            return this.declaracaoContinua();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.PAUSA))
            return this.declaracaoSustar();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.PARA))
            return this.declaracaoPara();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.ENQUANTO))
            return this.declaracaoEnquanto();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.SE))
            return this.declaracaoSe();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.ESCREVA))
            return this.declaracaoEscreva();
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.CHAVE_ESQUERDA))
            return new declaracoes_1.Bloco(0, 0, this.blocoEscopo());
        return this.declaracaoExpressao();
    }
    declaracaoDeVariavel() {
        const nome = this.consumir(egua_classico_1.default.IDENTIFICADOR, 'Esperado nome de variável.');
        let inicializador = null;
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.IGUAL)) {
            inicializador = this.expressao();
        }
        this.consumir(egua_classico_1.default.PONTO_E_VIRGULA, "Esperado ';' após a declaração da variável.");
        return new declaracoes_1.Var(nome, inicializador);
    }
    funcao(kind) {
        const nome = this.consumir(egua_classico_1.default.IDENTIFICADOR, `Esperado nome ${kind}.`);
        return new declaracoes_1.FuncaoDeclaracao(nome, this.corpoDaFuncao(kind));
    }
    corpoDaFuncao(kind) {
        this.consumir(egua_classico_1.default.PARENTESE_ESQUERDO, `Esperado '(' após o nome ${kind}.`);
        const parametros = [];
        if (!this.verificarTipoSimboloAtual(egua_classico_1.default.PARENTESE_DIREITO)) {
            do {
                if (parametros.length >= 255) {
                    throw this.erro(this.simboloAtual(), 'Função não pode ter mais de 255 parâmetros.');
                }
                const parametro = {};
                if (this.simboloAtual().tipo === egua_classico_1.default.MULTIPLICACAO) {
                    this.consumir(egua_classico_1.default.MULTIPLICACAO, null);
                    parametro['tipo'] = 'multiplo';
                }
                else {
                    parametro['tipo'] = 'padrao';
                }
                parametro['nome'] = this.consumir(egua_classico_1.default.IDENTIFICADOR, 'Esperado nome do parâmetro.');
                if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.IGUAL)) {
                    parametro['padrao'] = this.primario();
                }
                parametros.push(parametro);
                if (parametro['tipo'] === 'multiplo')
                    break;
            } while (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.VIRGULA));
        }
        this.consumir(egua_classico_1.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros.");
        this.consumir(egua_classico_1.default.CHAVE_ESQUERDA, `Esperado '{' antes do escopo do ${kind}.`);
        const corpo = this.blocoEscopo();
        return new construtos_1.FuncaoConstruto(this.hashArquivo, 0, parametros, corpo);
    }
    declaracaoDeClasse() {
        const nome = this.consumir(egua_classico_1.default.IDENTIFICADOR, 'Esperado nome da classe.');
        let superClasse = null;
        if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.HERDA)) {
            const simboloSuperclasse = this.consumir(egua_classico_1.default.IDENTIFICADOR, 'Esperado nome da Superclasse.');
            this.superclasseAtual = simboloSuperclasse.lexema;
            superClasse = new construtos_1.Variavel(this.hashArquivo, this.simboloAnterior());
        }
        this.consumir(egua_classico_1.default.CHAVE_ESQUERDA, "Esperado '{' antes do escopo da classe.");
        const metodos = [];
        while (!this.verificarTipoSimboloAtual(egua_classico_1.default.CHAVE_DIREITA) &&
            !this.estaNoFinal()) {
            metodos.push(this.funcao('método'));
        }
        this.consumir(egua_classico_1.default.CHAVE_DIREITA, "Esperado '}' após o escopo da classe.");
        this.superclasseAtual = undefined;
        return new declaracoes_1.Classe(nome, superClasse, metodos);
    }
    resolverDeclaracaoForaDeBloco() {
        try {
            if (this.verificarTipoSimboloAtual(egua_classico_1.default.FUNCAO) &&
                this.verificarTipoProximoSimbolo(egua_classico_1.default.IDENTIFICADOR)) {
                this.consumir(egua_classico_1.default.FUNCAO, null);
                return this.funcao('função');
            }
            if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.VARIAVEL))
                return this.declaracaoDeVariavel();
            if (this.verificarSeSimboloAtualEIgualA(egua_classico_1.default.CLASSE))
                return this.declaracaoDeClasse();
            return this.resolverDeclaracao();
        }
        catch (erro) {
            this.sincronizar();
            return null;
        }
    }
    async analisar(retornoLexador, hashArquivo) {
        this.erros = [];
        this.atual = 0;
        this.blocos = 0;
        this.hashArquivo = hashArquivo || 0;
        this.simbolos = (retornoLexador === null || retornoLexador === void 0 ? void 0 : retornoLexador.simbolos) || [];
        const declaracoes = [];
        while (!this.estaNoFinal()) {
            declaracoes.push(this.resolverDeclaracaoForaDeBloco());
        }
        return Promise.resolve({
            declaracoes: declaracoes,
            erros: this.erros,
        });
    }
}
exports.AvaliadorSintaticoEguaClassico = AvaliadorSintaticoEguaClassico;
//# sourceMappingURL=avaliador-sintatico-egua-classico.js.map