"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AvaliadorSintaticoCalango = void 0;
const construtos_1 = require("../../construtos");
const declaracoes_1 = require("../../declaracoes");
const avaliador_sintatico_base_1 = require("../avaliador-sintatico-base");
const pilha_escopos_1 = require("../pilha-escopos");
const informacao_escopo_1 = require("../informacao-escopo");
const informacao_elemento_sintatico_1 = require("../../informacao-elemento-sintatico");
const calango_1 = __importDefault(require("../../tipos-de-simbolos/calango"));
class AvaliadorSintaticoCalango extends avaliador_sintatico_base_1.AvaliadorSintaticoBase {
    constructor() {
        super();
        this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
    }
    async atribuir() {
        const expressao = await this.ou();
        if (this.verificarSeSimboloAtualEIgualA(calango_1.default.IGUAL_ATRIBUICAO)) {
            const setaAtribuicao = this.simbolos[this.atual - 1];
            const valor = await this.atribuir();
            this.verificarSeSimboloAtualEIgualA(calango_1.default.PONTO_E_VIRGULA);
            if (expressao instanceof construtos_1.Variavel) {
                return new construtos_1.Atribuir(this.hashArquivo, expressao, valor);
            }
            if (expressao instanceof construtos_1.AcessoIndiceVariavel) {
                return new construtos_1.AtribuicaoPorIndice(this.hashArquivo, expressao.linha, expressao.entidadeChamada, expressao.indice, valor);
            }
            throw this.erro(setaAtribuicao, 'Tarefa de atribuição inválida');
        }
        return expressao;
    }
    blocoEscopo() {
        throw new Error('Método não implementado.');
    }
    async chamar() {
        return await this.primario();
    }
    declaracaoEnquanto() {
        throw new Error('Método não implementado.');
    }
    declaracaoEscolha() {
        throw new Error('Método não implementado.');
    }
    // Em Calango, método "escreval"
    async declaracaoEscreva() {
        const simboloAtual = this.avancarEDevolverAnterior();
        this.consumir(calango_1.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos valores em escreva.");
        const argumentos = [];
        if (!this.verificarTipoSimboloAtual(calango_1.default.PARENTESE_DIREITO)) {
            do {
                const valor = await this.resolverDeclaracaoForaDeBloco();
                argumentos.push(new construtos_1.FormatacaoEscrita(this.hashArquivo, Number(simboloAtual.linha), valor));
            } while (this.verificarSeSimboloAtualEIgualA(calango_1.default.VIRGULA));
        }
        this.consumir(calango_1.default.PARENTESE_DIREITO, "Esperado ')' após os valores em escreva.");
        this.verificarSeSimboloAtualEIgualA(calango_1.default.PONTO_E_VIRGULA);
        this.verificarSeSimboloAtualEIgualA(calango_1.default.QUEBRA_LINHA);
        return new declaracoes_1.Escreva(Number(simboloAtual.linha), this.hashArquivo, argumentos);
    }
    /**
     * Em Calango, este é o método `escreva()`.
     * @returns {EscrevaMesmaLinha} Uma declaracao de escrita na mesma linha.
     */
    async declaracaoEscrevaMesmaLinha() {
        const simboloAtual = this.avancarEDevolverAnterior();
        this.consumir(calango_1.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos valores em escreva.");
        const argumentos = [];
        if (!this.verificarTipoSimboloAtual(calango_1.default.PARENTESE_DIREITO)) {
            do {
                const valor = await this.resolverDeclaracaoForaDeBloco();
                argumentos.push(new construtos_1.FormatacaoEscrita(this.hashArquivo, Number(simboloAtual.linha), valor));
            } while (this.verificarSeSimboloAtualEIgualA(calango_1.default.VIRGULA));
        }
        this.consumir(calango_1.default.PARENTESE_DIREITO, "Esperado ')' após os valores em escreva.");
        this.verificarSeSimboloAtualEIgualA(calango_1.default.PONTO_E_VIRGULA);
        return new declaracoes_1.EscrevaMesmaLinha(Number(simboloAtual.linha), this.hashArquivo, argumentos);
    }
    declaracaoInteiros() {
        const simboloInteiro = this.consumir(calango_1.default.INTEIRO, '');
        const inicializacoes = [];
        do {
            const identificador = this.consumir(calango_1.default.IDENTIFICADOR, "Esperado identificador após palavra reservada 'inteiro'.");
            // Inicializações de variáveis podem ter valores definidos.
            let valorInicializacao = 0;
            inicializacoes.push(new declaracoes_1.Var(identificador, new construtos_1.Literal(this.hashArquivo, Number(simboloInteiro.linha), valorInicializacao, 'inteiro')));
            this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificador.lexema, 'inteiro'));
        } while (this.verificarSeSimboloAtualEIgualA(calango_1.default.VIRGULA));
        this.consumir(calango_1.default.PONTO_E_VIRGULA, 'Esperado ponto e vírgula após declaração de variáveis.');
        return inicializacoes;
    }
    declaracaoFazer() {
        throw new Error('Método não implementado.');
    }
    declaracaoPara() {
        throw new Error('Método não implementado.');
    }
    async resolverBloco(simbolosParada) {
        const declaracoes = [];
        this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
        const primeiroSimbolo = this.simbolos[this.atual];
        while (!this.estaNoFinal() && !simbolosParada.includes(this.simbolos[this.atual].lexema)) {
            declaracoes.push(await this.resolverDeclaracaoForaDeBloco());
        }
        this.pilhaEscopos.removerUltimo();
        return new declaracoes_1.Bloco(this.hashArquivo, primeiroSimbolo.linha, declaracoes.filter((d) => d));
    }
    async declaracaoSe() {
        this.avancarEDevolverAnterior();
        this.consumir(calango_1.default.PARENTESE_ESQUERDO, "Esperado '(' após 'se'");
        const condicao = await this.expressao();
        this.consumir(calango_1.default.PARENTESE_DIREITO, "Esperado ')' após condição do 'se'");
        this.consumir(calango_1.default.ENTAO, "Esperado 'entao' após condição");
        this.verificarSeSimboloAtualEIgualA(calango_1.default.QUEBRA_LINHA);
        const caminhoEntao = await this.resolverBloco(['senao', 'fimSe']);
        while (this.verificarSeSimboloAtualEIgualA(calango_1.default.QUEBRA_LINHA))
            ;
        let caminhoSenao = null;
        if (this.verificarSeSimboloAtualEIgualA(calango_1.default.SENAO)) {
            this.verificarSeSimboloAtualEIgualA(calango_1.default.QUEBRA_LINHA);
            caminhoSenao = await this.resolverBloco(['senao', 'fimSe']);
        }
        this.verificarSeSimboloAtualEIgualA(calango_1.default.QUEBRA_LINHA);
        this.consumir(calango_1.default.FIM_SE, "Esperado 'fimSe' para finalização de uma instrução se.");
        return new declaracoes_1.Se(condicao, caminhoEntao, [], caminhoSenao);
    }
    async expressaoLeia() {
        const simboloAtual = this.avancarEDevolverAnterior();
        this.consumir(calango_1.default.PARENTESE_ESQUERDO, "Esperado '(' depois da declaração 'leia'");
        const argumentos = [];
        do {
            argumentos.push(await this.resolverDeclaracaoForaDeBloco());
        } while (this.verificarSeSimboloAtualEIgualA(calango_1.default.VIRGULA));
        this.consumir(calango_1.default.PARENTESE_DIREITO, "Esperado ')' após declaração 'leia'");
        this.consumir(calango_1.default.PONTO_E_VIRGULA, 'Esperado ponto e vírgula após declaração leia');
        return new construtos_1.Leia(simboloAtual, argumentos);
    }
    async primario() {
        switch (this.simbolos[this.atual].tipo) {
            case calango_1.default.IDENTIFICADOR:
                const simboloIdentificador = this.avancarEDevolverAnterior();
                let tipoOperando;
                try {
                    tipoOperando = this.pilhaEscopos.obterTipoVariavelPorNome(simboloIdentificador.lexema);
                }
                catch (erro) {
                    throw this.erro(simboloIdentificador, erro.message);
                }
                return new construtos_1.Variavel(this.hashArquivo, simboloIdentificador, tipoOperando);
            case calango_1.default.INTEIRO:
            case calango_1.default.NUMERO: // Precisamos substituir 'NUMERO' por 'REAL' pois Calango não possui 'NUMERO'
            case calango_1.default.TEXTO:
                const simboloAnterior = this.avancarEDevolverAnterior();
                return new construtos_1.Literal(this.hashArquivo, Number(simboloAnterior.linha), simboloAnterior.literal, simboloAnterior.tipo == calango_1.default.TEXTO ? 'texto' : 'inteiro');
            case calango_1.default.PARENTESE_ESQUERDO:
                this.avancarEDevolverAnterior();
                const expressao = await this.expressao();
                this.consumir(calango_1.default.PARENTESE_DIREITO, "Esperado ')' após a expressão.");
                return new construtos_1.Agrupamento(this.hashArquivo, this.simbolos[this.atual].linha, expressao);
        }
    }
    async resolverDeclaracaoForaDeBloco() {
        const simboloAtual = this.simbolos[this.atual];
        switch (simboloAtual.tipo) {
            case calango_1.default.ESCREVA:
                return await this.declaracaoEscrevaMesmaLinha();
            case calango_1.default.ESCREVAL:
                return await this.declaracaoEscreva();
            case calango_1.default.LEIA:
                return await this.expressaoLeia();
            case calango_1.default.INTEIRO:
                return this.declaracaoInteiros();
            case calango_1.default.SE:
                return await this.declaracaoSe();
            case calango_1.default.QUEBRA_LINHA:
                this.avancarEDevolverAnterior();
                return null;
            default:
                return await this.expressao();
        }
    }
    corpoDaFuncao(tipo) {
        throw new Error('Método não implementado.');
    }
    validarSegmentoAlgoritmo() {
        this.consumir(calango_1.default.ALGORITMO, `Expressão 'algoritmo' não declarada`);
        this.consumir(calango_1.default.IDENTIFICADOR, `Esperado identificador após 'algoritmo'.`);
        this.consumir(calango_1.default.PONTO_E_VIRGULA, `Esperado ponto e vírgula após identificador do algoritmo.`);
    }
    validarSegmentoPrincipal(algoritmoOuFuncao) {
        this.consumir(calango_1.default.PRINCIPAL, `Expressão 'principal' não declarada`);
    }
    async analisar(retornoLexador, hashArquivo) {
        this.erros = [];
        this.atual = 0;
        this.blocos = 0;
        this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
        this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
        this.hashArquivo = hashArquivo || 0;
        this.simbolos = (retornoLexador === null || retornoLexador === void 0 ? void 0 : retornoLexador.simbolos) || [];
        while (this.verificarTipoSimboloAtual(calango_1.default.QUEBRA_LINHA)) {
            this.avancarEDevolverAnterior();
        }
        let declaracoes = [];
        this.validarSegmentoAlgoritmo();
        this.validarSegmentoPrincipal('principal');
        while (!this.estaNoFinal() &&
            this.simbolos[this.atual].tipo !== calango_1.default.FIM_PRINCIPAL) {
            const resolucaoDeclaracao = await this.resolverDeclaracaoForaDeBloco();
            if (Array.isArray(resolucaoDeclaracao)) {
                declaracoes = declaracoes.concat(resolucaoDeclaracao);
            }
            else {
                declaracoes.push(resolucaoDeclaracao);
            }
        }
        return {
            declaracoes: declaracoes.filter((d) => d),
            erros: this.erros,
        };
    }
}
exports.AvaliadorSintaticoCalango = AvaliadorSintaticoCalango;
//# sourceMappingURL=avaliador-sintatico-calango.js.map