UNPKG

@designliquido/delegua

Version:

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

3,127 lines 190 kB
"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_elemento_sintatico_1 = require("../informacao-elemento-sintatico");
const comum_1 = require("./comum");
const montao_tipos_1 = require("./montao-tipos");
const elemento_montao_tipos_1 = require("./elemento-montao-tipos");
const simbolo_1 = require("../lexador/simbolo");
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"));
/**
 * 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:
 *
 * - `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;
 * - `montaoTipos` é uma implementação de montão muito semelhante ao montão do interpretador, para
 * tipos complexos com N níveis de profundidade, como dicionários e objetos.
 */
class AvaliadorSintatico extends avaliador_sintatico_base_1.AvaliadorSintaticoBase {
    constructor(performance = false) {
        super();
        this.pilhaDecoradores = [];
        this.simbolos = [];
        this.erros = [];
        this.tiposDefinidosEmCodigo = {};
        this.interfacesDeclaradas = {};
        this.tiposDefinidosPorBibliotecas = {};
        this.tiposDeFerramentasExternas = {};
        this.primitivasConhecidas = {};
        this.hashArquivo = 0;
        this.atual = 0;
        this.blocos = 0;
        this.erros = [];
        this.performance = performance;
        this.tiposDefinidosEmCodigo = {};
        this.interfacesDeclaradas = {};
        this.tiposDeFerramentasExternas = {};
        this.primitivasConhecidas = {};
        this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
        this.montaoTipos = new montao_tipos_1.MontaoTipos();
        this.intuirTipoQualquerParaIdentificadores = false;
        this.emAjuda = false;
        (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);
    }
    verificarDefinicaoTipoAtual() {
        const tipos = [...Object.values(delegua_1.default)];
        if (this.simbolos[this.atual].lexema in this.tiposDefinidosEmCodigo) {
            const nomeBase = this.simbolos[this.atual].lexema;
            if (this.verificarTipoProximoSimbolo(delegua_2.default.COLCHETE_ESQUERDO)) {
                this.avancarEDevolverAnterior(); // avança do tipo para '['
                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}`);
                }
                this.avancarEDevolverAnterior(); // avança de '[' para ']'
                return `${nomeBase}[]`;
            }
            return nomeBase;
        }
        if (this.simbolos[this.atual].lexema in this.interfacesDeclaradas) {
            return this.simbolos[this.atual].lexema;
        }
        const lexemaOriginal = this.simbolos[this.atual].lexema;
        const lexemaElementar = lexemaOriginal.toLowerCase();
        const tipoElementarResolvido = tipos.find((tipo) => tipo === lexemaElementar);
        if (!tipoElementarResolvido) {
            // Mantém o avaliador sintático sincronizado mesmo com tipos não reconhecidos
            // (ex.: anotações vindas de módulos externos ainda não resolvidos).
            // Assim evitamos que o próximo token (como '[') seja interpretado
            // como início de outra produção e gere erros encadeados.
            if (this.verificarTipoProximoSimbolo(delegua_2.default.COLCHETE_ESQUERDO)) {
                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}`);
                }
                this.avancarEDevolverAnterior();
                return `${lexemaOriginal}[]`;
            }
            return lexemaOriginal;
        }
        if ((tipoElementarResolvido === 'funcao' || tipoElementarResolvido === 'função') &&
            this.verificarTipoProximoSimbolo(delegua_2.default.MENOR)) {
            this.avancarEDevolverAnterior(); // avança por 'funcao'
            this.avancarEDevolverAnterior(); // avança por '<'
            const tiposParametros = [];
            do {
                tiposParametros.push(this.simbolos[this.atual].lexema);
                this.avancarEDevolverAnterior();
            } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
            // this.atual aponta para '>'
            const tipoFuncaoGenerico = `${tipoElementarResolvido}<${tiposParametros.join(', ')}>`;
            if (this.verificarTipoProximoSimbolo(delegua_2.default.COLCHETE_ESQUERDO)) {
                this.avancarEDevolverAnterior(); // avança por '>'
                // this.atual aponta para '['
                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}`);
                }
                this.avancarEDevolverAnterior(); // avança por '['
                // this.atual aponta para ']'
                return `${tipoFuncaoGenerico}[]`;
            }
            return tipoFuncaoGenerico;
        }
        if (this.verificarTipoProximoSimbolo(delegua_2.default.COLCHETE_ESQUERDO)) {
            const tiposVetores = [
                'funcao[]',
                'função[]',
                '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;
    }
    async construtoAjuda() {
        const simboloAjuda = this.avancarEDevolverAnterior();
        if (this.estaNoFinal() ||
            this.simbolos[this.atual].tipo !== delegua_2.default.PARENTESE_ESQUERDO) {
            return new construtos_1.AjudaComoConstruto(simboloAjuda.hashArquivo, simboloAjuda.linha, undefined, false);
        }
        this.avancarEDevolverAnterior(); // parêntese esquerdo
        if (this.simbolos[this.atual].tipo === delegua_2.default.PARENTESE_DIREITO) {
            this.avancarEDevolverAnterior();
            return new construtos_1.AjudaComoConstruto(simboloAjuda.hashArquivo, simboloAjuda.linha, undefined, true);
        }
        const topicoPalavraChave = this.tentarResolverTopicoAjudaPalavraChave();
        if (topicoPalavraChave) {
            this.consumir(delegua_2.default.PARENTESE_DIREITO, `Esperado parêntese direito após expressão usada como argumento em ajuda(). Atual: ${this.simbolos[this.atual].lexema}.`);
            return new construtos_1.AjudaComoConstruto(simboloAjuda.hashArquivo, simboloAjuda.linha, topicoPalavraChave);
        }
        this.emAjuda = true;
        const expressaoAjuda = await this.expressao();
        this.emAjuda = false;
        this.consumir(delegua_2.default.PARENTESE_DIREITO, `Esperado parêntese direito após expressão usada como argumento em ajuda(). Atual: ${this.simbolos[this.atual].lexema}.`);
        return new construtos_1.AjudaComoConstruto(simboloAjuda.hashArquivo, simboloAjuda.linha, expressaoAjuda);
    }
    tentarResolverTopicoAjudaPalavraChave() {
        const simboloAtual = this.simbolos[this.atual];
        switch (simboloAtual.tipo) {
            case delegua_2.default.CLASSE: {
                const simboloClasse = this.avancarEDevolverAnterior();
                let topico = 'classe';
                if (this.simbolos[this.atual].tipo === delegua_2.default.ABSTRATO) {
                    this.avancarEDevolverAnterior();
                    topico = 'classe abstrata';
                }
                else if (this.simbolos[this.atual].tipo === delegua_2.default.ESTRANGEIRA) {
                    this.avancarEDevolverAnterior();
                    topico = 'classe estrangeira';
                }
                return new construtos_1.Literal(this.hashArquivo, Number(simboloClasse.linha), topico, 'texto');
            }
            case delegua_2.default.HERDA:
                this.avancarEDevolverAnterior();
                return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), 'herda', 'texto');
            case delegua_2.default.MESCLA:
                this.avancarEDevolverAnterior();
                return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), 'mescla', 'texto');
            case delegua_2.default.EXTENSAO:
                this.avancarEDevolverAnterior();
                return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), 'extensao', 'texto');
            case delegua_2.default.ASSERCAO:
                this.avancarEDevolverAnterior();
                return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), 'assercao', 'texto');
            default:
                return undefined;
        }
    }
    async 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 await 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 await 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.`);
        }
    }
    async 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 = await this.obterChaveDicionario();
            this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' entre chave e valor.");
            const valor = await 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);
    }
    /**
     * `delegua-node` e a extensão para VSCode precisam que este método seja assíncrono.
     * @returns
     */
    async construtoImportar() {
        this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após declaração.");
        const caminho = await this.expressao();
        this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após declaração.");
        return Promise.resolve(new construtos_1.ImportarComoConstruto(caminho));
    }
    async construtoTupla() {
        const expressao = await this.expressao();
        const argumentos = [expressao];
        while (this.simbolos[this.atual].tipo === delegua_2.default.VIRGULA) {
            this.avancarEDevolverAnterior();
            argumentos.push(await 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);
    }
    async enquantoComoConstruto() {
        const { condicao, corpo } = await this.logicaComumEnquanto();
        return new construtos_1.EnquantoComoConstruto(condicao, corpo);
    }
    async fazerComoConstruto(simboloFazer) {
        try {
            this.blocos += 1;
            const { caminhoFazer, condicaoEnquanto } = await this.logicaComumFazer();
            return new construtos_1.FazerComoConstruto(simboloFazer.hashArquivo, Number(simboloFazer.linha), caminhoFazer, condicaoEnquanto);
        }
        finally {
            this.blocos -= 1;
        }
    }
    async paraCadaComoConstrutoVetor(simboloPara) {
        const { variavelIteracao, vetor, corpo } = await this.logicaComumParaCadaVetor(simboloPara);
        return new construtos_1.ParaCadaComoConstruto(this.hashArquivo, Number(simboloPara.linha), variavelIteracao, vetor, corpo);
    }
    async paraCadaComoConstrutoDicionario(simboloPara) {
        const { nomeVariavelChave, nomeVariavelValor, dicionario, corpo } = await this.logicaParaCadaDicionario(simboloPara);
        return new construtos_1.ParaCadaComoConstruto(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);
    }
    async paraCadaComoConstruto(simboloPara) {
        if (this.verificarTipoSimboloAtual(delegua_2.default.IDENTIFICADOR)) {
            return await this.paraCadaComoConstrutoVetor(simboloPara);
        }
        if (this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_ESQUERDA)) {
            return await this.paraCadaComoConstrutoDicionario(simboloPara);
        }
        throw this.erro(simboloPara, 'Identificador de iteração deve ser ou um par chave-valor, ou um nome de variável.');
    }
    async paraTradicionalComoConstruto(simboloPara) {
        const { inicializador, condicao, incrementar, corpo } = await this.logicaComumPara();
        return new construtos_1.ParaComoConstruto(simboloPara.hashArquivo, simboloPara.linha, inicializador, condicao, incrementar, corpo);
    }
    /**
     * Método que resolve `para` ou `para cada` como construto.
     */
    async paraComoConstruto(simboloPara) {
        try {
            this.blocos += 1;
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CADA)) {
                return await this.paraCadaComoConstruto(simboloPara);
            }
            return await this.paraTradicionalComoConstruto(simboloPara);
        }
        finally {
            this.blocos -= 1;
        }
    }
    /**
     * Resolve uma lista de compreensão.
     * @returns {ListaCompreensao} A lista de compreensão resolvida.
     */
    async resolverCompreensaoDeLista(retornoExpressao) {
        this.consumir(delegua_2.default.PARA, "Esperado instrução 'para' após identificador.");
        this.consumir(delegua_2.default.CADA, "Esperado instrução 'cada' após 'para'.");
        const simboloVariavelIteracao = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado identificador de variável após '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 em lista de compreensão.");
        }
        const localizacaoVetor = this.simboloAnterior();
        const vetor = await this.ou();
        let condicao = null;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SE)) {
            condicao = await this.expressao();
        }
        else {
            condicao = new construtos_1.Literal(this.hashArquivo, Number(localizacaoVetor.linha), true);
        }
        this.consumir(delegua_2.default.COLCHETE_DIREITO, 'Espero fechamento de colchetes após condição.');
        const tipoVetor = vetor.tipo;
        if (!tipoVetor.endsWith('[]') && !['qualquer', 'vetor'].includes(tipoVetor)) {
            throw this.erro(localizacaoVetor, `Variável ou constante em 'para cada' não é iterável. Tipo resolvido: ${tipoVetor}.`);
        }
        const variavelIteracao = new construtos_1.Variavel(this.hashArquivo, simboloVariavelIteracao);
        return new construtos_1.ListaCompreensao(Number(this.simbolos[this.atual]), this.hashArquivo, retornoExpressao, vetor, new construtos_1.ParaCadaComoConstruto(retornoExpressao.hashArquivo, retornoExpressao.linha, variavelIteracao, vetor, new declaracoes_1.Bloco(retornoExpressao.hashArquivo, retornoExpressao.linha, [
            new declaracoes_1.Se(condicao, new declaracoes_1.Bloco(retornoExpressao.hashArquivo, retornoExpressao.linha, [
                new declaracoes_1.Retorna(simboloVariavelIteracao, retornoExpressao),
            ]), [], undefined),
        ])), retornoExpressao.tipo ? `${retornoExpressao.tipo}[]` : 'qualquer[]');
    }
    async primario() {
        const simboloAtual = this.simbolos[this.atual];
        let valores = [];
        switch (simboloAtual.tipo) {
            case delegua_2.default.AJUDA:
                return await this.construtoAjuda();
            case delegua_2.default.CHAVE_ESQUERDA:
                return await 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), [], 'qualquer[]');
                }
                if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
                    return this.construtoTupla();
                }
                // Ao resolver a expressão aqui, identificadores dentro da expressão de compreensão
                // de lista serão tratados como 'qualquer', para evitar erros de tipo.
                this.intuirTipoQualquerParaIdentificadores = true;
                const retornoExpressaoOuPrimeiroValor = await this.seTernario();
                this.intuirTipoQualquerParaIdentificadores = false;
                if (this.simbolos[this.atual].tipo === delegua_2.default.PARA) {
                    return await this.resolverCompreensaoDeLista(retornoExpressaoOuPrimeiroValor);
                }
                // Aqui já sabemos que não é uma compreensão de lista.
                valores.push(retornoExpressaoOuPrimeiroValor);
                while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_DIREITO)) {
                    switch (this.simbolos[this.atual].tipo) {
                        case delegua_2.default.VIRGULA:
                            const simboloVirgula = this.avancarEDevolverAnterior();
                            valores.push(new construtos_1.Separador(simboloVirgula));
                            break;
                        case delegua_2.default.COMENTARIO:
                            const simboloComentario = this.avancarEDevolverAnterior();
                            valores.push(new construtos_1.ComentarioComoConstruto(simboloComentario));
                            break;
                        default:
                            const valor = await this.seTernario();
                            valores.push(valor);
                            break;
                    }
                }
                // Remover comentários, verificar se vírgulas fazem sentido.
                const valoresSemComentarios = valores.filter((v) => v.constructor !== construtos_1.ComentarioComoConstruto);
                let elementoSeparador = false; // O primeiro elemento não pode ser separador.
                for (const elemento of valoresSemComentarios) {
                    if (elementoSeparador) {
                        if (elemento.constructor !== construtos_1.Separador) {
                            throw this.erro(elemento.simbolo, 'Não podem haver duas vírgulas seguidas em uma definição de vetor, ou definição de vetor começando em vírgula.');
                        }
                        elementoSeparador = false;
                    }
                    else {
                        if (elemento.constructor === construtos_1.Separador) {
                            throw this.erro(elemento.simbolo, 'Não podem haver duas vírgulas seguidas em uma definição de vetor, ou definição de vetor começando em vírgula.');
                        }
                        elementoSeparador = true;
                    }
                }
                const valoresSemSeparadores = valoresSemComentarios.filter((v) => v.constructor !== construtos_1.Separador);
                const tipoVetor = (0, inferenciador_1.inferirTipoVariavel)(valoresSemSeparadores);
                return new construtos_1.Vetor(this.hashArquivo, Number(simboloAtual.linha), valores, tipoVetor);
            case delegua_2.default.ENQUANTO:
                this.avancarEDevolverAnterior();
                return await this.enquantoComoConstruto();
            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.FAZER:
                const simboloFazer = this.avancarEDevolverAnterior();
                return await this.fazerComoConstruto(simboloFazer);
            case delegua_2.default.FUNCAO:
            case delegua_2.default.FUNÇÃO:
                const simboloFuncao = this.avancarEDevolverAnterior();
                const corpoDaFuncao = await this.corpoDaFuncao(simboloFuncao.lexema);
                this.pilhaEscopos.definirInformacoesVariavel(simboloFuncao.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(simboloFuncao.lexema, 'função'));
                return corpoDaFuncao;
            case delegua_2.default.IDENTIFICADOR:
                const simboloIdentificador = this.avancarEDevolverAnterior();
                let tipoOperando;
                if (this.intuirTipoQualquerParaIdentificadores) {
                    // Esta indicação é utilizada para compreensões de lista, onde o
                    // tipo do identificador de iteração é 'qualquer' por definição.
                    tipoOperando = 'qualquer';
                    this.pilhaEscopos.definirInformacoesVariavel(simboloIdentificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(simboloIdentificador.lexema, 'qualquer'));
                }
                else 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 await this.construtoImportar();
            case delegua_2.default.ISTO:
                this.avancarEDevolverAnterior();
                return new construtos_1.Isto(this.hashArquivo, Number(simboloAtual.linha), simboloAtual);
            case delegua_2.default.LEIA:
                return await 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 delimitadorTexto = simboloNumeroTexto.tipo === delegua_2.default.TEXTO
                    ? simboloNumeroTexto.delimitadorTexto
                    : undefined;
                return new construtos_1.Literal(this.hashArquivo, Number(simboloNumeroTexto.linha), simboloNumeroTexto.literal, tipoInferido, delimitadorTexto);
            case delegua_2.default.PARA:
                const simboloPara = this.avancarEDevolverAnterior();
                return await this.paraComoConstruto(simboloPara);
            case delegua_2.default.PARENTESE_ESQUERDO:
                this.avancarEDevolverAnterior();
                if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_DIREITO)) {
                    return new construtos_1.TuplaN(this.hashArquivo, Number(simboloAtual.linha), []);
                }
                const expressao = await this.tupla();
                if (expressao instanceof construtos_1.TuplaN) {
                    this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após itens da tupla.");
                    return 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 = await this.expressao();
                }
                if (construto.constructor === construtos_1.AcessoMetodoOuPropriedade) {
                    const construtoTipado = construto;
                    const acessoMetodoConhecido = this.resolverAcessoMetodoConhecido(construtoTipado);
                    if (acessoMetodoConhecido) {
                        construto = acessoMetodoConhecido;
                    }
                    else {
                        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 &&
                                    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);
        }
        // TODO: O correto seria emitir algum aviso aqui que este avaliador sintático não consegue
        // lidar com tópicos de ajuda neste ponto.
        if (this.emAjuda) {
            console.log(this.simbolos[this.atual]);
        }
        throw this.erro(this.simbolos[this.atual], 'Esperado expressão.');
    }
    resolverTipoAcessoIndiceVariavel(expressaoAnterior) {
        let identificadorAcessado = '';
        switch (expressaoAnterior.constructor) {
            case construtos_1.Variavel:
                identificadorAcessado = expressaoAnterior.simbolo.lexema;
                break;
            default:
                return 'qualquer';
        }
        // Primeiro verificar se é acesso a índice de vetor.
        let tipoIdentificadorCorrespondente;
        try {
            tipoIdentificadorCorrespondente = this.pilhaEscopos.obterTipoVariavelPorNome(expressaoAnterior.simbolo.lexema);
        }
        catch (erro) {
            // Referência a identificador ainda não declarado; assumimos 'qualquer' e deixamos
            // o analisador semântico emitir o diagnóstico quando apropriado.
            tipoIdentificadorCorrespondente = 'qualquer';
        }
        if (!tipoIdentificadorCorrespondente.endsWith('[]') &&
            !['dicionário', 'qualquer', 'texto', 'tupla', 'vetor'].includes(tipoIdentificadorCorrespondente)) {
            throw this.erro(this.simbolos[this.atual], `Tipo ${tipoIdentificadorCorrespondente} não suporta acesso por índice.`);
        }
        let tipoAcesso = 'qualquer';
        if (tipoIdentificadorCorrespondente.endsWith('[]')) {
            tipoAcesso = tipoIdentificadorCorrespondente.replace('[]', '');
        }
        else {
            tipoAcesso = 'qualquer';
        }
        return tipoAcesso;
    }
    /**
     * Resolve um construto do tipo `Variavel`, `AcessoMetodoOuPropriedade` ou `AcessoIndiceVariavel`
     * para um `ElementoMontaoTipos` correspondente, caso exista.
     * @param construto O construto.
     * @returns O `ElementoMontaoTipos` correspondente ou `null` se não existir.
     */
    resolverElementoMontao(construto) {
        switch (construto.constructor) {
            case construtos_1.Variavel:
                try {
                    const nomeVariavel = construto.simbolo.lexema;
                    const elementoMontao = this.pilhaEscopos.obterElementoMontaoTipos(nomeVariavel);
                    if (elementoMontao.endereco) {
                        const referenciaMontaoTipos = this.montaoTipos.obterReferencia(construto.hashArquivo, construto.linha, elementoMontao.endereco);
                        if (referenciaMontaoTipos instanceof elemento_montao_tipos_1.ElementoMontaoTipos) {
                            return referenciaMontaoTipos;
                        }
                        return null;
                    }
                    return elementoMontao;
                }
                catch {
                    return null;
                }
            case construtos_1.AcessoMetodoOuPropriedade: {
                const acesso = construto;
                const elementoObjeto = this.resolverElementoMontao(acesso.objeto);
                if (!elementoObjeto) {
                    return null;
                }
                return elementoObjeto.subElementos[acesso.simbolo.lexema] || null;
            }
            case construtos_1.AcessoIndiceVariavel: {
                const acessoIndice = construto;
                const elementoObjeto = this.resolverElementoMontao(acessoIndice.entidadeChamada);
                if (!elementoObjeto || !(acessoIndice.indice instanceof construtos_1.Literal)) {
                    return null;
                }
                return elementoObjeto.subElementos[String(acessoIndice.indice.valor)] || null;
            }
            default:
                return null;
        }
    }
    async resolverCadeiaChamadas(expressaoAnterior, tipoAnterior = 'qualquer') {
        if (!this.simbolos[this.atual]) {
            return expressaoAnterior;
        }
        switch (this.simbolos[this.atual].tipo) {
            case delegua_2.default.PARENTESE_ESQUERDO:
                this.avancarEDevolverAnterior();
                const chamada = await this.finalizarChamada(expressaoAnterior, tipoAnterior);
                return await this.resolverCadeiaChamadas(chamada);
            case delegua_2.default.PONTO:
                this.avancarEDevolverAnterior();
                const simboloAtual = this.simbolos[this.atual];
                if (!simboloAtual ||
                    !/^[A-Za-z_$À-ÿ][A-Za-z0-9_$À-ÿ]*$/.test(simboloAtual.lexema)) {
                    throw this.erro(simboloAtual, "Esperado nome do método ou propriedade após o '.'");
                }
                const nome = this.avancarEDevolverAnterior();
                let tipoInferido = expressaoAnterior.tipo;
                // Se não for um dicionário anônimo (ou seja, ser variável ou constante com nome)
                if (expressaoAnterior.tipo === 'dicionário' &&
                    expressaoAnterior.constructor !== construtos_1.Dicionario) {
                    const elementoMontaoTipos = this.resolverElementoMontao(expressaoAnterior);
                    if (elementoMontaoTipos && nome.lexema in elementoMontaoTipos.subElementos) {
                        tipoInferido = elementoMontaoTipos.subElementos[nome.lexema].tipo;
                    }
                }
                const acesso = new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, expressaoAnterior, nome, tipoInferido);
                return await this.resolverCadeiaChamadas(acesso, tipoInferido);
            case delegua_2.default.COLCHETE_ESQUERDO:
                const tipoAcesso = this.resolverTipoAcessoIndiceVariavel(expressaoAnterior);
                this.avancarEDevolverAnterior();
                const indice = await this.expressao();
                const simboloFechamento = this.consumir(delegua_2.default.COLCHETE_DIREITO, "Esperado ']' após escrita do indice.");
                const acessoVariavel = new construtos_1.AcessoIndiceVariavel(this.hashArquivo, expressaoAnterior, indice, simboloFechamento, tipoAcesso);
                return await this.resolverCadeiaChamadas(acessoVariavel);
            default:
                return expressaoAnterior;
        }
    }
    async chamar() {
        let expressao = await this.primario();
        return await this.resolverCadeiaChamadas(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 {ConstrutoInterface} O construto resolvido como um tipo mais específico.
     * @see finalizarChamada
     */
    resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamadaResolvida) {
        const construtoTipado = entidadeChamadaResolvida;
        const acessoMetodoConhecido = this.resolverAcessoMetodoConhecido(construtoTipado);
        if (acessoMetodoConhecido) {
            return acessoMetodoConhecido;
        }
        switch (entidadeChamadaResolvida.tipo) {
            case delegua_1.default.DICIONARIO:
            case delegua_1.default.DICIONÁRIO:
                if (construtoTipado.simbolo.lexema in primitivas_dicionario_1.default) {
                    const primitivaDicionarioSelecionada = primitivas_dicionario_1.default[construtoTipado.simbolo.lexema];
                    return 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) {
                    const primitivaNumeroSelecionada = primitivas_numero_1.default[construtoTipado.simbolo.lexema];
                    return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaNumeroSelecionada.tipoRetorno);
                }
                break;
            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) {
                    const primitivaTextoSelecionada = primitivas_texto_1.default[construtoTipado.simbolo.lexema];
                    return 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) {
                    const primitivaVetorSelecionada = primitivas_vetor_1.default[construtoTipado.simbolo.lexema];
                    return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaVetorSelecionada.tipoRetorno);
                }
                break;
        }
        return entidadeChamadaResolvida;
    }
    obterTiposParaConsultaPrimitiva(tipo) {
        switch (tipo) {
            case delegua_1.default.DICIONARIO:
            case delegua_1.default.DICIONÁRIO:
                return ['dicionário'];
            case delegua_1.default.INTEIRO:
            case delegua_1.default.NUMERO:
            case delegua_1.default.NÚMERO:
            case delegua_1.default.REAL:
                return ['número'];
            case delegua_1.default.TEXTO:
                return ['texto'];
            case delegua_1.default.VETOR:
            case delegua_1.default.VETOR_INTEIRO:
            case delegua_1.default.VETOR_LOGICO:
            case delegua_1.default.VETOR_LÓGICO:
            case delegua_1.default.VETOR_NUMERO:
            case delegua_1.default.VETOR_NÚMERO:
            case delegua_1.default.VETOR_QUALQUER:
            case delegua_1.default.VETOR_TEXTO:
                return ['vetor'];
            default:
                return [tipo];
        }
    }
    resolverAcessoMetodoConhecido(construtoTipado) {
        const tiposParaConsulta = this.obterTiposParaConsultaPrimitiva(construtoTipado.tipo);
        for (const tipoConsulta of tiposParaConsulta) {
            const primitivasPorTipo = this.primitivasConhecidas[tipoConsulta];
            if (!primitivasPorTipo) {
                continue;
            }
            const informacoesPrimitiva = primitivasPorTipo[construtoTipado.simbolo.lexema];
            if (informacoesPrimitiva) {
                return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, informacoesPrimitiva.tipo);
            }
        }
        return null;
    }
    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') ||
                argumentoUtilizado instanceof construtos_1.FuncaoConstruto
                ? '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 === construtos_1.AcessoMetodoOuPropriedade) {
            return this.resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamada);
        }
        if (entidadeChamada.constructor === construtos_1.Variavel) {
            const entidadeChamadaResolvidaVariavel = entidadeChamada;
            const informacoesFuncaoBibliotecaGlobal = this.pilhaEscopos.obterBibliotecaGlobal(entidadeChamadaResolvidaVariavel.simbolo.lexema);
            if (informacoesFuncaoBibliotecaGlobal &&
                Array.isArray(informacoesFuncaoBibliotecaGlobal.subElementos) &&
                informacoesFuncaoBibliotecaGlobal.subElementos.length > 0) {
                const erros = this.validarArgumentosEntidadeChamada(informacoesFuncaoBibliotecaGlobal.subElementos, 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 === 'qualquer') {
                // Provavelmente uma chamada a alguma função da biblioteca global.
                // Até então, é sempre do tipo `InformacaoElementoSintatico`.
                const informacoesPossivelFuncaoBibliotecaGlobal = this.pilhaEscopos.obterBibliotecaGlobal(entidadeChamadaResolvidaVariavel.simbolo.lexema);
                if (informacoesPossivelFuncaoBibliotecaGlobal) {
                    const erros = this.validarArgumentosEntidadeChamada(informacoesPossivelFuncaoBibliotecaGlobal.subElementos, 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 !== 'qualquer' &&
                this.primitivasConhecidas[tipoPrimitiva].hasOwnProperty(entidadeChamadaResolvidaVariavel.simbolo.lexema)) {
                var informacoesPrimitiva = this.primitivasConhecidas[tipoPrimitiva][entidadeChamadaResolvidaVariavel.simbolo.lexema];
                const erros = this.validarArgumentosEntidadeChamada(informacoesPrimitiva.subElementos, 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(entidadeChamadaResolvidaVariavel.hashArquivo, entidadeChamadaResolvidaVariavel.linha, entidadeChamadaResolvidaVariavel.simbolo, entidadeChamadaResolvidaVariavel.tipo, possivelReferencia.id);
            }
            return new construtos_1.ArgumentoReferenciaFuncao(entidadeChamadaResolvidaVariavel.hashArquivo, entidadeChamadaResolvidaVariavel.linha, entidadeChamadaResolvidaVariavel.simbolo);
        }
        return entidadeChamada;
    }
    async 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(await 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;
    }
    async unario() {
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.INTERROGACAO)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = await this.unario();
            this.erros.push(this.erro(operador, "O operador '?' deve ser precedido de uma condição para uso como operador ternário (ex: condição ? então : senão)."));
            return direito;
        }
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.NAO, delegua_2.default.NEGACAO, delegua_2.default.ADICAO, 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 = await this.unario();
            if (operador.tipo === delegua_2.default.NEGACAO ||
                operador.tipo === delegua_2.default.NAO) {
                this.verificarOperandoNegacao(operador, direito);
            }
            return new construtos_1.Unario(this.hashArquivo, operador, direito, 'ANTES');
        }
        return await this.chamar();
    }
    verificarOperandosComparacao(operador, esquerda, direita) {
        if (esquerda instanceof construtos_1.Literal && direita instanceof construtos_1.Literal) {
            const tipoEsquerda = esquerda.tipo;
            const tipoDireita = direita.tipo;
            if (tipoEsquerda !== 'qualquer' &&
                tipoEsquerda !== 'nulo' &&
                tipoDireita !== 'qualquer' &&
                tipoDireita !== 'nulo' &&
                tipoEsquerda !== tipoDireita) {
                this.erros.push(this.erro(operador, `Operadores de comparação não podem ser usados com tipos diferentes. Tipos recebidos: '${tipoEsquerda}' e '${tipoDireita}'.`));
            }
        }
    }
    verificarOperandoNegacao(operador, operando) {
        if (operando instanceof construtos_1.Literal) {
            if (operando.tipo !== 'lógico') {
                this.erros.push(this.erro(operador, `Operador '!' só pode ser usado com valores lógicos. Tipo recebido: ${operando.tipo}.`));
            }
            return;
        }
        if (operando instanceof construtos_1.Unario) {
            const tipoOp = operando.operador.tipo;
            if (tipoOp === delegua_2.default.NEGACAO || tipoOp === delegua_2.default.NAO) {
                return;
            }
            this.verificarOperandoNegacao(operador, operando.operando);
        }
    }
    /**
     * A exponenciacão é uma exceção na ordem de avaliação (resolve primeiro à direita).
     * Por isso `direito` chama `exponenciacao()`, e não `unario()`.
     * @returns {Binario} A expressão binária na forma do construto `Binario`.
     */
    async exponenciacao() {
        let expressao = await this.unario();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.EXPONENCIACAO)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = await this.exponenciacao();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    /**
     * Verifica recursivamente se um construto é ou contém uma operação unária em um vetor.
     * Isso bloqueia padrões de ofuscação como !![] usado em operações aritméticas.
     */
    verificarOperacaoUnariaEmVetor(construto) {
        if (construto instanceof construtos_1.Unario) {
            const operando = construto.operando;
            // Verifica se o operando é um vetor
            if (operando instanceof construtos_1.Vetor ||
                operando.tipo === 'vetor' ||
                operando.tipo.endsWith('[]')) {
                return true;
            }
            // Verifica recursivamente para casos como !![]
            if (operando instanceof construtos_1.Unario) {
                return this.verificarOperacaoUnariaEmVetor(operando);
            }
        }
        return false;
    }
    verificacaoOperacoesBinariasIlegais(esquerdo, direito, operador) {
        // Bloquear operações aritméticas com operações unárias em vetores (padrão de ofuscação tipo !![] * 1)
        if (this.verificarOperacaoUnariaEmVetor(esquerdo)) {
            throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} com expressão unária aplicada a vetor.`);
        }
        if (this.verificarOperacaoUnariaEmVetor(direito)) {
            throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} com expressão unária aplicada a vetor.`);
        }
        if (esquerdo.tipo === 'vetor' || esquerdo.tipo.endsWith('[]')) {
            if (['dicionario', 'dicionário', 'nulo'].includes(direito.tipo)) {
                throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} entre vetor e ${direito.tipo}.`);
            }
        }
        if (direito.tipo === 'vetor' || direito.tipo.endsWith('[]')) {
            if (['dicionario', 'dicionário', 'nulo'].includes(esquerdo.tipo)) {
                throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} entre vetor e ${esquerdo.tipo}.`);
            }
        }
        if (esquerdo.tipo === 'dicionario' || esquerdo.tipo === 'dicionário') {
            if (['vetor', 'nulo'].includes(direito.tipo)) {
                throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} entre dicionário e ${direito.tipo}.`);
            }
        }
        if (direito.tipo === 'dicionario' || direito.tipo === 'dicionário') {
            if (['vetor', 'nulo'].includes(esquerdo.tipo)) {
                throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} entre dicionário e ${esquerdo.tipo}.`);
            }
        }
    }
    async multiplicar() {
        let expressao = await 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 = await this.exponenciacao();
            this.verificacaoOperacoesBinariasIlegais(expressao, direito, operador);
            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.
     */
    async adicaoOuSubtracao() {
        let expressao = await 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 = await this.multiplicar();
            this.verificacaoOperacoesBinariasIlegais(expressao, direito, operador);
            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 = await this.ou();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    async bitShift() {
        let expressao = await this.adicaoOuSubtracao();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MENOR_MENOR, delegua_2.default.MAIOR_MAIOR)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = await this.adicaoOuSubtracao();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    async bitE() {
        let expressao = await this.bitShift();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.BIT_AND)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = await this.bitShift();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    async bitOu() {
        let expressao = await this.bitE();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.BIT_OR, delegua_2.default.CIRCUMFLEXO)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = await this.bitE();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    async comparar() {
        let expressao = await 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 esquerda = expressao;
            const direito = await this.bitOu();
            this.verificarOperandosComparacao(operador, esquerda, direito);
            expressao = new construtos_1.Binario(this.hashArquivo, esquerda, operador, direito);
        }
        return expressao;
    }
    async comparacaoIgualdade() {
        let expressao = await this.comparar();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DIFERENTE, delegua_2.default.IGUAL_IGUAL)) {
            const operador = this.simbolos[this.atual - 1];
            const esquerda = expressao;
            const direito = await this.comparar();
            this.verificarOperandosComparacao(operador, esquerda, direito);
            expressao = new construtos_1.Binario(this.hashArquivo, esquerda, operador, direito);
        }
        return expressao;
    }
    async em() {
        let expressao = await this.comparacaoIgualdade();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.EM, delegua_2.default.CONTEM, delegua_2.default.NAO)) {
            let operador = this.simbolos[this.atual - 1];
            let negado = false;
            if (operador.tipo === delegua_2.default.NAO) {
                operador = this.consumir(delegua_2.default.CONTEM, `Esperado palavra reservada 'contém' ou 'contem' após palavra reservada ${operador.lexema}.`);
                negado = true;
            }
            const direito = await this.comparacaoIgualdade();
            expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
            expressao.negado = negado;
        }
        return expressao;
    }
    async e() {
        let expressao = await this.em();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.E)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = await this.em();
            expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    async elvis() {
        let expressao = await this.ou();
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.ELVIS)) {
            const direito = await this.ou();
            return new construtos_1.Elvis(this.hashArquivo, expressao, direito);
        }
        return expressao;
    }
    async seTernario() {
        let expressaoOuCondicao = await this.elvis();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.INTERROGACAO)) {
            const operador = this.simbolos[this.atual - 1];
            const expressaoEntao = await this.seTernario();
            this.consumir(delegua_2.default.DOIS_PONTOS, `Esperado dois-pontos após caminho positivo em se ternário. Atual: ${this.simbolos[this.atual].lexema}.`);
            const expressaoSenao = await this.seTernario();
            expressaoOuCondicao = new construtos_1.SeTernario(this.hashArquivo, expressaoOuCondicao, expressaoEntao, operador, expressaoSenao);
        }
        return expressaoOuCondicao;
    }
    /**
     * Método que resolve atribuições.
     * @returns Um construto do tipo `Atribuir`, `Conjunto` ou `AtribuicaoPorIndice`.
     */
    /**
     * Processa tuplas, que são expressões separadas por vírgula entre parênteses.
     * Se não houver vírgula, retorna apenas a expressão simples.
     * Sobrescreve o método da base para usar seTernario() em vez de ou().
     */
    async tupla() {
        let expressao = await this.seTernario();
        // Se não há vírgula, retorna a expressão simples
        if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA)) {
            return expressao;
        }
        // Se há vírgula, então é uma tupla
        const elementos = [expressao];
        do {
            if (this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
                break;
            }
            elementos.push(await this.seTernario());
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        return new construtos_1.TuplaN(this.hashArquivo, expressao.linha, elementos);
    }
    /**
     * Método que resolve atribuições.
     * @returns Um construto do tipo `Atribuir`, `Conjunto` ou `AtribuicaoPorIndice`.
     */
    async atribuir() {
        const expressao = await this.seTernario();
        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, delegua_2.default.SETA_ESQUERDA)) {
            const igual = this.simbolos[this.atual - 1];
            const valor = await this.seTernario();
            switch (expressao.constructor) {
                case construtos_1.Variavel:
                    return new construtos_1.Atribuir(this.hashArquivo, expressao, valor);
                case construtos_1.AcessoMetodoOuPropriedade:
                    const expressaoAcessoMetodoOuPropriedade = expressao;
                    return new construtos_1.DefinirValor(this.hashArquivo, igual.linha, expressaoAcessoMetodoOuPropriedade.objeto, expressaoAcessoMetodoOuPropriedade.simbolo, valor);
                case construtos_1.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`.
     */
    async expressaoLeia() {
        const simboloLeia = this.avancarEDevolverAnterior();
        if (this.emAjuda && this.simbolos[this.atual].tipo !== delegua_2.default.PARENTESE_ESQUERDO) {
            return new construtos_1.Leia(simboloLeia, []);
        }
        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(await 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);
    }
    async blocoEscopo() {
        this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
        let declaracoes = [];
        while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
            !this.estaNoFinal()) {
            const retornoDeclaracao = await this.resolverDeclaracaoForaDeBloco();
            if (Array.isArray(retornoDeclaracao)) {
                declaracoes = declaracoes.concat(retornoDeclaracao);
            }
            else if (retornoDeclaracao != null) {
                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]);
    }
    async logicaComumEnquanto() {
        const condicao = await this.expressao();
        const corpo = await this.declaracaoBloco();
        return {
            condicao,
            corpo,
        };
    }
    async declaracaoEnquanto() {
        try {
            this.blocos += 1;
            const { condicao, corpo } = await this.logicaComumEnquanto();
            return new declaracoes_1.Enquanto(condicao, corpo);
        }
        finally {
            this.blocos -= 1;
        }
    }
    async declaracaoEscolha() {
        try {
            this.blocos += 1;
            const condicao = await 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 = [await this.expressao()];
                    this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após o 'caso'.");
                    while (this.verificarTipoSimboloAtual(delegua_2.default.CASO)) {
                        this.avancarEDevolverAnterior();
                        caminhoCondicoes.push(await this.expressao());
                        this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após declaração do 'caso'.");
                    }
                    let declaracoes = [];
                    do {
                        const retornoDeclaracao = await 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((await 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;
        }
    }
    async 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(await 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);
    }
    async 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 = await this.expressao();
        // Ponto-e-vírgula é opcional aqui.
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return new declaracoes_1.Expressao(expressao, decoradores);
    }
    async declaracaoFalhar() {
        const simboloFalha = this.simbolos[this.atual - 1];
        const expressaoFalha = await this.expressao();
        return new declaracoes_1.Falhar(simboloFalha, expressaoFalha);
    }
    async declaracaoAssercao() {
        const simboloAssercao = this.simbolos[this.atual - 1];
        let condicao;
        let mensagemFalha;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
            condicao = await this.expressao();
            if (this.verificarTipoSimboloAtual(delegua_2.default.VIRGULA)) {
                this.avancarEDevolverAnterior();
                mensagemFalha = await this.expressao();
                if (this.verificarTipoSimboloAtual(delegua_2.default.VIRGULA)) {
                    throw this.erro(this.simbolos[this.atual], "'asserção' aceita apenas condição obrigatória e mensagem opcional.");
                }
            }
            else {
                mensagemFalha = new construtos_1.Literal(simboloAssercao.hashArquivo, Number(simboloAssercao.linha), 'A asserção falhou.', 'texto', "'");
            }
            this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após argumentos de 'asserção'.");
        }
        else {
            condicao = await this.expressao();
            if (this.verificarTipoSimboloAtual(delegua_2.default.VIRGULA)) {
                throw this.erro(this.simbolos[this.atual], "Mensagem em 'asserção' exige uso de parênteses.");
            }
            mensagemFalha = new construtos_1.Literal(simboloAssercao.hashArquivo, Number(simboloAssercao.linha), 'A asserção falhou.', 'texto', "'");
        }
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        const simboloNao = new simbolo_1.Simbolo(delegua_2.default.NEGACAO, 'nao', null, Number(simboloAssercao.linha), simboloAssercao.hashArquivo, simboloAssercao.colunaInicio, simboloAssercao.colunaFim);
        const condicaoNegada = new construtos_1.Unario(simboloAssercao.hashArquivo, simboloNao, condicao, 'ANTES');
        const declaracaoFalhar = new declaracoes_1.Falhar(simboloAssercao, mensagemFalha);
        const blocoFalha = new declaracoes_1.Bloco(simboloAssercao.hashArquivo, Number(simboloAssercao.linha), [
            declaracaoFalhar,
        ]);
        return new declaracoes_1.Se(condicaoNegada, blocoFalha);
    }
    async logicaComumFazer() {
        const caminhoFazer = await this.declaracaoBloco();
        this.consumir(delegua_2.default.ENQUANTO, "Esperado declaração do 'enquanto' após o escopo do 'fazer'.");
        const condicaoEnquanto = await this.expressao();
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return {
            caminhoFazer,
            condicaoEnquanto,
        };
    }
    async declaracaoFazer(simboloFazer) {
        try {
            this.blocos += 1;
            const { caminhoFazer, condicaoEnquanto } = await this.logicaComumFazer();
            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() {
        let identificadorDeTudo = null;
        const elementosImportacao = [];
        switch (this.simbolos[this.atual].tipo) {
            case delegua_2.default.TUDO:
                this.avancarEDevolverAnterior();
                this.consumir(delegua_2.default.COMO, "Esperado 'como' após 'tudo' em declaração de importação.");
                identificadorDeTudo = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado identificador após 'como' em declaração de importação de 'tudo'.");
                break;
            case delegua_2.default.CHAVE_ESQUERDA:
                this.avancarEDevolverAnterior();
                do {
                    const identificadorImportacao = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado identificador de elemento a ser importado.');
                    elementosImportacao.push(identificadorImportacao);
                } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
                this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após lista de elementos a serem importados.");
                break;
            default:
                throw this.erro(this.simbolos[this.atual], "Esperado ou palavra reservada 'tudo' ou abertura de chaves após palavra reservada 'importar'.");
        }
        this.consumir(delegua_2.default.DE, "Esperado 'de' após identificador em declaração de importação de 'tudo'.");
        let construtoCaminhoModulo;
        switch (this.simbolos[this.atual].tipo) {
            case delegua_2.default.TEXTO:
                const simboloCaminhoModulo = this.avancarEDevolverAnterior();
                construtoCaminhoModulo = new construtos_1.Literal(simboloCaminhoModulo.hashArquivo, Number(simboloCaminhoModulo.linha), simboloCaminhoModulo.literal);
                break;
            case delegua_2.default.IDENTIFICADOR:
                const identificadorModulo = this.avancarEDevolverAnterior();
                construtoCaminhoModulo = new construtos_1.Literal(identificadorModulo.hashArquivo, Number(identificadorModulo.linha), identificadorModulo.lexema);
                break;
            default:
                throw this.erro(this.simbolos[this.atual], 'Esperado caminho do módulo como texto ou identificador em declaração de importação.');
        }
        const importar = new declaracoes_1.Importar(construtoCaminhoModulo);
        if (identificadorDeTudo !== null) {
            importar.simboloTudo = identificadorDeTudo;
            this.pilhaEscopos.definirInformacoesVariavel(identificadorDeTudo.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificadorDeTudo.lexema, 'módulo'));
        }
        else {
            importar.elementosImportacao = elementosImportacao;
            for (const elemento of elementosImportacao) {
                this.pilhaEscopos.definirInformacoesVariavel(elemento.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(elemento.lexema, 'qualquer'));
            }
        }
        return Promise.resolve(importar);
    }
    async declaracaoPara() {
        try {
            const simboloPara = this.simbolos[this.atual - 1];
            this.blocos += 1;
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CADA)) {
                return await this.declaracaoParaCada(simboloPara);
            }
            return await this.declaracaoParaTradicional(simboloPara);
        }
        finally {
            this.blocos -= 1;
        }
    }
    async logicaParaCadaDicionario(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 = await 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_elemento_sintatico_1.InformacaoElementoSintatico(nomeVariavelChave.lexema, 'qualquer'));
        this.pilhaEscopos.definirInformacoesVariavel(nomeVariavelValor.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomeVariavelValor.lexema, 'qualquer'));
        const corpo = await this.declaracaoBloco();
        return {
            nomeVariavelChave,
            nomeVariavelValor,
            dicionario,
            corpo,
        };
    }
    async declaracaoParaCadaDicionario(simboloPara) {
        const { nomeVariavelChave, nomeVariavelValor, dicionario, corpo } = await this.logicaParaCadaDicionario(simboloPara);
        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);
    }
    async logicaComumParaCadaVetor(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'.");
        }
        let vetorOuDicionario = await this.expressao();
        if (vetorOuDicionario.constructor === construtos_1.AcessoIndiceVariavel) {
            const construtoAcessoIndiceVariavel = vetorOuDicionario;
            if (construtoAcessoIndiceVariavel.entidadeChamada.tipo === 'dicionário') {
                // A avaliação sintática não deve verificar valores de dicionários.
                // Aqui se supõe que o programador sabe o que está fazendo.
                // TODO: Talvez pensar numa forma melhor de fazer isso.
                vetorOuDicionario.tipo = 'vetor';
            }
        }
        const tipoVetor = vetorOuDicionario.tipo;
        if (!tipoVetor.endsWith('[]') &&
            !['dicionário', 'qualquer', 'texto', '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_elemento_sintatico_1.InformacaoElementoSintatico(nomeVariavelIteracao.lexema, tipoVariavelIteracao));
        const corpo = await this.declaracaoBloco();
        return {
            variavelIteracao,
            vetor: vetorOuDicionario,
            corpo,
        };
    }
    async declaracaoParaCadaVetor(simboloPara) {
        const { variavelIteracao, vetor, corpo } = await this.logicaComumParaCadaVetor(simboloPara);
        return new declaracoes_1.ParaCada(this.hashArquivo, Number(simboloPara.linha), variavelIteracao, vetor, corpo);
    }
    async declaracaoParaCada(simboloPara) {
        if (this.verificarTipoSimboloAtual(delegua_2.default.IDENTIFICADOR)) {
            return await this.declaracaoParaCadaVetor(simboloPara);
        }
        if (this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_ESQUERDA)) {
            return await this.declaracaoParaCadaDicionario(simboloPara);
        }
        throw this.erro(simboloPara, 'Identificador de iteração deve ser ou um par chave-valor, ou um nome de variável.');
    }
    async logicaComumPara() {
        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 = await this.declaracaoDeVariaveis();
        }
        else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CONSTANTE)) {
            inicializador = await this.declaracaoDeConstantes();
        }
        else {
            inicializador = await this.declaracaoExpressao();
        }
        let condicao = null;
        if (!this.verificarTipoSimboloAtual(delegua_2.default.PONTO_E_VIRGULA)) {
            condicao = await 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 = await 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.");
        }
        const corpo = await this.declaracaoBloco();
        return {
            inicializador,
            condicao,
            incrementar,
            corpo,
        };
    }
    async declaracaoParaTradicional(simboloPara) {
        const { inicializador, condicao, incrementar, corpo } = await this.logicaComumPara();
        return new declaracoes_1.Para(this.hashArquivo, Number(simboloPara.linha), inicializador, condicao, incrementar, corpo);
    }
    async 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.NAO,
            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 = await this.expressao();
        }
        // Ponto-e-vírgula é opcional aqui.
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return new declaracoes_1.Retorna(simboloChave, valor);
    }
    async declaracaoSe() {
        const condicao = await this.expressao();
        const caminhoEntao = (await this.resolverDeclaracao());
        let caminhoSenao = undefined;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SENAO, delegua_2.default.SENÃO)) {
            caminhoSenao = (await 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]);
    }
    async declaracaoTente() {
        const simboloTente = this.simbolos[this.atual - 1];
        this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'tente'.");
        const blocoTente = await 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 = await 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 = await 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 = await 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 = await this.blocoEscopo();
        }
        return new declaracoes_1.Tente(simboloTente.hashArquivo, Number(simboloTente.linha), blocoTente, blocoPegue, blocoSenao, blocoFinalmente);
    }
    async resolverDecoradores() {
        while (this.verificarTipoSimboloAtual(delegua_2.default.ARROBA)) {
            this.avancarEDevolverAnterior(); // Arroba
            let nomeDecorador = '@';
            let linha;
            let parametros = [];
            const atributos = {};
            const primeiraParteNomeDecorador = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome de decorador após '@'.");
            linha = Number(primeiraParteNomeDecorador.linha);
            nomeDecorador += primeiraParteNomeDecorador.lexema;
            while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO)) {
                const parteNomeDecorador = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome de decorador após '.'.");
                nomeDecorador += '.' + parteNomeDecorador.lexema;
            }
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
                if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
                    parametros = await this.logicaComumParametros();
                }
                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.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após nome de decorador.");
            }
            this.pilhaDecoradores.push(new construtos_1.Decorador(this.hashArquivo, linha, nomeDecorador, atributos));
        }
    }
    async declaracaoTendoComo() {
        const simboloTendo = this.simbolos[this.atual - 1];
        const expressaoInicializacao = await 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) {
            case construtos_1.Chamada:
                const construtoChamada = expressaoInicializacao;
                switch (construtoChamada.entidadeChamada.constructor) {
                    case construtos_1.AcessoMetodo:
                        const entidadeChamadaAcessoMetodo = construtoChamada.entidadeChamada;
                        tipoInicializacao = entidadeChamadaAcessoMetodo.tipoRetornoMetodo.replace('<T>', entidadeChamadaAcessoMetodo.objeto.tipo);
                        break;
                    case construtos_1.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_elemento_sintatico_1.InformacaoElementoSintatico(simboloNomeVariavel.lexema, tipoInicializacao));
        const blocoCorpo = await this.blocoEscopo();
        return new declaracoes_1.TendoComo(simboloTendo.linha, simboloTendo.hashArquivo, simboloNomeVariavel, expressaoInicializacao, new declaracoes_1.Bloco(simboloTendo.linha, simboloTendo.hashArquivo, blocoCorpo));
    }
    async 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.');
        if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL, delegua_2.default.SETA_ESQUERDA)) {
            throw this.erro(this.simbolos[this.atual], 'Esperado igual ou seta esquerda após relação de propriedades da desestruturação.');
        }
        const inicializador = await this.expressao();
        const retornos = [];
        for (let identificador of identificadores) {
            this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificador.lexema, this.logicaComumInferenciaTiposVariaveisEConstantes(inicializador, 'qualquer') ?? '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;
    }
    logicaComumInferenciaTiposAcessoMetodoOuPropriedade(entidadeChamada) {
        // Algumas coisas podem acontecer aqui.
        // Uma delas é a variável/constante ser uma classe padrão.
        // Isso ocorre quando a importação é feita de uma biblioteca Node.js.
        // Nesse caso, o tipo de `entidadeChamada.objeto` começa com uma letra maiúscula.
        if (entidadeChamada.objeto.tipo && entidadeChamada.objeto.tipo.match(/^[A-Z]/)) {
            const tipoObjeto = entidadeChamada.objeto.tipo;
            // Classe definida em código (local ou importada de arquivo .delegua) tem precedência.
            if (tipoObjeto in this.tiposDefinidosEmCodigo) {
                const classeDefinida = this.tiposDefinidosEmCodigo[tipoObjeto];
                if (classeDefinida instanceof declaracoes_1.Classe) {
                    const nomeMembro = entidadeChamada.simbolo.lexema;
                    const metodo = classeDefinida.metodos?.find((m) => m.simbolo.lexema === nomeMembro);
                    if (metodo) {
                        return metodo.tipo || 'qualquer';
                    }
                    const propriedade = classeDefinida.propriedades?.find((p) => p.nome.lexema === nomeMembro);
                    if (propriedade) {
                        return propriedade.tipo || 'qualquer';
                    }
                    return 'qualquer';
                }
            }
            const tipoCorrespondente = this.tiposDefinidosPorBibliotecas[tipoObjeto];
            if (!tipoCorrespondente) {
                throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamada.simbolo, `Tipo '${tipoObjeto}' não foi encontrado entre os tipos definidos por bibliotecas.`);
            }
            if (!(entidadeChamada.simbolo.lexema in tipoCorrespondente.metodos) &&
                !(entidadeChamada.simbolo.lexema in tipoCorrespondente.propriedades)) {
                throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamada.simbolo, `Membro '${entidadeChamada.simbolo.lexema}' não existe no tipo '${entidadeChamada.objeto.tipo}'.`);
            }
            if (entidadeChamada.simbolo.lexema in tipoCorrespondente.metodos) {
                const metodoCorrespondente = tipoCorrespondente.metodos[entidadeChamada.simbolo.lexema];
                return metodoCorrespondente.tipoRetorno || 'qualquer';
            }
            const propriedadeCorrespondente = tipoCorrespondente.propriedades[entidadeChamada.simbolo.lexema];
            return propriedadeCorrespondente.tipo;
        }
        // Chamada via 'isto': verificar métodos da classe em análise antes de checar primitivas.
        if (entidadeChamada.objeto instanceof construtos_1.Isto) {
            if (this.metodosClasseAtualEmAnalise) {
                const nomeMembro = entidadeChamada.simbolo.lexema;
                const metodo = this.metodosClasseAtualEmAnalise.find((m) => m.simbolo.lexema === nomeMembro);
                if (metodo) {
                    return metodo.tipo || 'qualquer';
                }
            }
            return 'qualquer';
        }
        // 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.
        for (const primitiva in this.primitivasConhecidas) {
            if (this.primitivasConhecidas[primitiva].hasOwnProperty(entidadeChamada.simbolo.lexema)) {
                return this.primitivasConhecidas[primitiva][entidadeChamada.simbolo.lexema].tipo;
            }
        }
        throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamada.simbolo, `Primitiva '${entidadeChamada.simbolo.lexema}' não existe.`);
    }
    logicaComumInferenciaTiposVariaveisEConstantes(inicializador, tipo) {
        if (tipo !== 'qualquer') {
            return tipo;
        }
        switch (inicializador.constructor) {
            case construtos_1.AcessoIndiceVariavel:
                const entidadeChamadaAcessoIndiceVariavel = inicializador
                    .entidadeChamada;
                // Este condicional ocorre com chamadas aninhadas. Por exemplo, `vetor[1][2]`.
                if (entidadeChamadaAcessoIndiceVariavel.constructor === construtos_1.AcessoIndiceVariavel) {
                    return this.logicaComumInferenciaTiposVariaveisEConstantes(entidadeChamadaAcessoIndiceVariavel, tipo);
                }
                const tipoEntidadeChamadaAcessoIndiceVariavel = entidadeChamadaAcessoIndiceVariavel.tipo;
                if (tipoEntidadeChamadaAcessoIndiceVariavel.endsWith('[]')) {
                    return tipoEntidadeChamadaAcessoIndiceVariavel.slice(0, -2);
                }
                // Normalmente, `entidadeChamadaAcessoIndiceVariavel.tipo` aqui será 'vetor'.
                return 'qualquer';
            case construtos_1.Chamada:
                const entidadeChamadaChamada = inicializador.entidadeChamada;
                switch (entidadeChamadaChamada.constructor) {
                    case construtos_1.AcessoMetodo:
                        const entidadeChamadaAcessoMetodo = entidadeChamadaChamada;
                        const tipoRetornoAcessoMetodoResolvido = entidadeChamadaAcessoMetodo.tipoRetornoMetodo.replace('<T>', entidadeChamadaAcessoMetodo.objeto.tipo);
                        return tipoRetornoAcessoMetodoResolvido;
                    case construtos_1.AcessoMetodoOuPropriedade:
                        return this.logicaComumInferenciaTiposAcessoMetodoOuPropriedade(entidadeChamadaChamada);
                    case construtos_1.AcessoPropriedade:
                        const entidadeChamadaAcessoPropriedade = entidadeChamadaChamada;
                        return entidadeChamadaAcessoPropriedade.tipoRetornoPropriedade;
                    case construtos_1.ArgumentoReferenciaFuncao:
                        // TODO: Voltar aqui se necessário.
                        return 'qualquer';
                    case construtos_1.ReferenciaFuncao:
                        const entidadeChamadaReferenciaFuncao = entidadeChamadaChamada;
                        return entidadeChamadaReferenciaFuncao.tipo;
                    case construtos_1.Variavel:
                        const entidadeChamadaVariavel = entidadeChamadaChamada;
                        return entidadeChamadaVariavel.tipo;
                }
                break;
            case construtos_1.FuncaoConstruto:
                const funcaoConstruto = inicializador;
                return `função<${funcaoConstruto.tipo}>`;
            case construtos_1.Leia:
                return 'texto';
            case tuplas_1.Dupla:
            case tuplas_1.Trio:
            case tuplas_1.Quarteto:
            case tuplas_1.Quinteto:
            case tuplas_1.Sexteto:
            case tuplas_1.Septeto:
            case tuplas_1.Octeto:
            case tuplas_1.Noneto:
            case tuplas_1.Deceto:
                return delegua_1.default.TUPLA;
            default:
                // Construtos mapeados em `delegua-node`.
                switch (inicializador.constructor.name) {
                    case 'ImportarBiblioteca':
                    case 'ModuloDeclaracoes':
                        return 'módulo';
                }
                return inicializador.tipo;
        }
    }
    resolverValorConstruto(construto) {
        if (construto instanceof construtos_1.Literal) {
            return String(construto.valor);
        }
        throw this.erro({ hashArquivo: construto.hashArquivo, linha: construto.linha }, `Construto do tipo ${construto.constructor} não possui um mapeamento de valor.`);
    }
    resolverInformacaoElementoSintaticoDeDicionario(construto) {
        let retorno;
        if (construto instanceof construtos_1.Dicionario) {
            retorno = new elemento_montao_tipos_1.ElementoMontaoTipos('dicionário');
            const subElementos = {};
            for (var i = 0; i < construto.valores.length; i++) {
                const chaveCorrespondente = this.resolverValorConstruto(construto.chaves[i]);
                const valorCorrespondente = construto.valores[i];
                subElementos[chaveCorrespondente] =
                    this.resolverInformacaoElementoSintaticoDeDicionario(valorCorrespondente);
            }
            retorno.subElementos = subElementos;
        }
        else {
            retorno = new elemento_montao_tipos_1.ElementoMontaoTipos(construto.tipo);
        }
        const endereco = this.montaoTipos.adicionarReferencia(retorno);
        retorno.endereco = endereco;
        return retorno;
    }
    /**
     * Caso símbolo atual seja `var`, devolve uma declaração de variável.
     * @returns Um Construto do tipo Var.
     */
    async declaracaoDeVariaveis() {
        const simboloVariavel = this.simboloAnterior();
        const identificadores = [];
        const retorno = [];
        let tipo = 'qualquer';
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_ESQUERDA)) {
            return await this.declaracaoDesestruturacaoVariavel();
        }
        do {
            identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da variável.'));
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        let tipoExplicito = false;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
            tipo = this.verificarDefinicaoTipoAtual();
            tipoExplicito = true;
            this.avancarEDevolverAnterior();
        }
        if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL, delegua_2.default.SETA_ESQUERDA)) {
            // Inicialização de variáveis sem valor.
            for (let identificador of identificadores.values()) {
                this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificador.lexema, tipo));
                retorno.push(new declaracoes_1.Var(identificador, undefined, tipo, tipoExplicito, Array.from(this.pilhaDecoradores)));
            }
            this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
            this.pilhaDecoradores = [];
            return retorno;
        }
        const inicializadores = [];
        do {
            inicializadores.push(await 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()) {
            const tipoOriginal = tipo; // Preserva o tipo antes da inferência
            tipo =
                this.logicaComumInferenciaTiposVariaveisEConstantes(inicializadores[indice], tipo) ?? tipo;
            if (tipo !== 'dicionário') {
                this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificador.lexema, tipo));
            }
            else {
                const inicializadorDicionario = inicializadores[indice];
                this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, this.resolverInformacaoElementoSintaticoDeDicionario(inicializadorDicionario));
            }
            retorno.push(new declaracoes_1.Var(identificador, inicializadores[indice], tipo, tipoExplicito, Array.from(this.pilhaDecoradores), tipoOriginal // Passa o tipo original para o construtor
            ));
        }
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        this.pilhaDecoradores = [];
        return retorno;
    }
    async 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.');
        if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL, delegua_2.default.SETA_ESQUERDA)) {
            throw this.erro(this.simbolos[this.atual], 'Esperado igual ou seta esquerda após relação de propriedades da desestruturação.');
        }
        const inicializador = await this.expressao();
        const retornos = [];
        for (let identificador of identificadores) {
            this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(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.
     */
    async declaracaoDeConstantes() {
        const simboloConstante = this.simboloAnterior();
        const identificadores = [];
        let tipo = 'qualquer';
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_ESQUERDA)) {
            return await this.declaracaoDesestruturacaoConstante();
        }
        do {
            identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da constante.'));
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        let tipoExplicito = false;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
            tipo = this.verificarDefinicaoTipoAtual();
            tipoExplicito = true;
            this.avancarEDevolverAnterior();
        }
        if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL, delegua_2.default.SETA_ESQUERDA)) {
            throw this.erro(this.simbolos[this.atual], "Esperado '=' ou '<-' após identificador em instrução 'constante'.");
        }
        const inicializadores = [];
        do {
            inicializadores.push(await 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) ?? tipo;
            if (tipo !== 'dicionário') {
                this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificador.lexema, tipo));
            }
            else {
                const inicializadorDicionario = inicializadores[indice];
                this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, this.resolverInformacaoElementoSintaticoDeDicionario(inicializadorDicionario));
            }
            retorno.push(new declaracoes_1.Const(identificador, inicializadores[indice], tipo, tipoExplicito, Array.from(this.pilhaDecoradores)));
        }
        this.pilhaDecoradores = [];
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return retorno;
    }
    async 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_elemento_sintatico_1.InformacaoElementoSintatico(simbolo.lexema, 'qualquer'));
        const corpoDaFuncao = await this.corpoDaFuncao(tipo);
        const tipoDaFuncao = `função<${corpoDaFuncao.tipo}>`;
        this.pilhaEscopos.definirInformacoesVariavel(simbolo.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(simbolo.lexema, tipoDaFuncao));
        const funcaoDeclaracao = new declaracoes_1.FuncaoDeclaracao(simbolo, corpoDaFuncao, tipoDaFuncao, decoradores);
        this.pilhaEscopos.registrarReferenciaFuncao(simbolo.lexema, funcaoDeclaracao);
        return funcaoDeclaracao;
    }
    async logicaComumParametros() {
        const parametros = [];
        do {
            const parametro = {};
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.RETICENCIAS)) {
                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)) {
                try {
                    const valorPadrao = await this.primario();
                    parametro.valorPadrao = valorPadrao;
                }
                catch (erro) {
                    throw this.erro(this.simbolos[this.atual], `Valor padrão do parâmetro '${parametro.nome.lexema}' é inválido.`);
                }
            }
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
                let tipoDadoParametro = this.verificarDefinicaoTipoAtual();
                parametro.tipoDado = tipoDadoParametro;
                this.avancarEDevolverAnterior();
            }
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL)) {
                const valorPadrao = await this.primario();
                parametro.valorPadrao = valorPadrao;
            }
            this.pilhaEscopos.definirInformacoesVariavel(parametro.nome.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(parametro.nome.lexema, parametro.tipoDado || 'qualquer'));
            parametros.push(parametro);
            if (parametro.abrangencia === 'multiplo')
                break;
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        return parametros;
    }
    async corpoDaFuncao(tipo) {
        // O parêntese esquerdo é considerado o símbolo inicial para
        // fins de localização.
        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 = await 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 = await this.blocoEscopo();
        let expressoesRetorna = [];
        for (const declaracao of corpo) {
            expressoesRetorna = expressoesRetorna.concat((0, comum_1.buscarRetornos)(declaracao));
        }
        if (tipoRetorno === 'vazio' && expressoesRetorna.length > 0) {
            // Filtra retornos que têm tipo conhecido e diferente de 'vazio'.
            // 'qualquer' é excluído pois o tipo não pode ser determinado em tempo de análise sintática.
            const retornosNaoVazios = expressoesRetorna.filter((e) => e.tipo !== 'vazio' && e.tipo !== 'qualquer');
            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.filter((e) => e.tipo !== 'qualquer').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);
    }
    /**
     * Analisa uma declaração de interface.
     * Interfaces definem contratos de métodos e propriedades que as classes devem implementar.
     * A verificação é feita em tempo de análise (parse-time).
     */
    async declaracaoDeInterface() {
        const simbolo = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da interface.');
        this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do corpo da interface.");
        const metodos = [];
        const propriedades = [];
        while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
            !this.estaNoFinal()) {
            this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
            if (this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA))
                break;
            const nomeMembro = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome de método ou propriedade na interface.');
            if (this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_ESQUERDO)) {
                // Assinatura de método: nome(params): tipoRetorno
                this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após nome do método.");
                let params = [];
                if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
                    params = await this.logicaComumParametros();
                }
                this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros.");
                let tipoRetorno = 'qualquer';
                if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
                    tipoRetorno = this.verificarDefinicaoTipoAtual();
                    this.avancarEDevolverAnterior();
                }
                this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
                metodos.push(new declaracoes_1.AssinaturaMetodo(nomeMembro, params, tipoRetorno));
            }
            else {
                // Assinatura de propriedade: nome: tipo
                this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após nome de propriedade na interface.");
                const tipo = this.verificarDefinicaoTipoAtual();
                this.avancarEDevolverAnterior();
                this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
                propriedades.push(new declaracoes_1.PropriedadeClasse(nomeMembro, tipo));
            }
        }
        this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após o corpo da interface.");
        const declaracao = new declaracoes_1.InterfaceDeclaracao(simbolo, metodos, propriedades);
        this.interfacesDeclaradas[simbolo.lexema] = declaracao;
        return declaracao;
    }
    async declaracaoDeClasse() {
        // Modificadores opcionais no nível da classe: `abstrata`, `estrangeira` e/ou `estática`.
        // Sintaxe: `classe abstrata NomeDaClasse`, `classe estrangeira NomeDaClasse`, etc.
        let ehAbstrata = false;
        let ehEstrangeira = false;
        let ehEstatica = false;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.ABSTRATO))
            ehAbstrata = true;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.ESTRANGEIRA))
            ehEstrangeira = true;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.ESTATICO))
            ehEstatica = true;
        // Também permite a ordem invertida: `classe estática abstrata`
        if (!ehAbstrata && this.verificarSeSimboloAtualEIgualA(delegua_2.default.ABSTRATO))
            ehAbstrata = true;
        const simbolo = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da classe.');
        // Pré-registro para permitir auto-referência no corpo da própria classe,
        // como em `MinhaClasse.propriedadeEstatica` dentro de métodos.
        this.tiposDefinidosEmCodigo[simbolo.lexema] =
            this.tiposDefinidosEmCodigo[simbolo.lexema] ?? {};
        const pilhaDecoradoresClasse = Array.from(this.pilhaDecoradores);
        // Verificar `herda SuperclasseA, SuperclasseB`
        const superClasses = [];
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.HERDA)) {
            do {
                const simboloSuperclasse = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da Superclasse.');
                if (superClasses.length === 0) {
                    this.superclasseAtual = simboloSuperclasse.lexema;
                }
                superClasses.push(new construtos_1.Variavel(this.hashArquivo, this.simbolos[this.atual - 1], simboloSuperclasse.lexema));
            } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        }
        // Verificar `implementa InterfaceA, InterfaceB`
        const implementaInterfaces = [];
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.IMPLEMENTA)) {
            do {
                implementaInterfaces.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome de interface após "implementa".'));
            } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        }
        // Verificar `mescla MisturávelA, MisturávelB`
        const mesclas = [];
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MESCLA)) {
            do {
                const simboloMisturavel = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome de classe após "mescla".');
                mesclas.push(new construtos_1.Variavel(this.hashArquivo, this.simbolos[this.atual - 1], simboloMisturavel.lexema));
            } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        }
        this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do escopo da classe.");
        this.pilhaDecoradores = [];
        const metodos = [];
        const propriedades = [];
        /**
         * Analisa membros do corpo da classe com um contexto de acesso e estático padrão.
         * Suporta blocos de contexto aninhados: `estático { }`, `abstrato { }`, `privado { }`, `protegido { }`, `publico { }`.
         */
        const compreenderMembros = async (acessoPadrao, ehEstaticoPadrao, ehAbstratoPadrao = false) => {
            while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
                !this.estaNoFinal()) {
                // Pular comentários normais dentro do corpo da classe.
                if (this.simbolos[this.atual].tipo === delegua_2.default.COMENTARIO ||
                    this.simbolos[this.atual].tipo === delegua_2.default.LINHA_COMENTARIO) {
                    this.avancarEDevolverAnterior();
                    continue;
                }
                // Documentário (/** ... */)
                let docAtual = undefined;
                if (this.simbolos[this.atual].tipo === delegua_2.default.DOCUMENTARIO) {
                    const simboloDoc = this.avancarEDevolverAnterior();
                    docAtual = new construtos_1.ComentarioComoConstruto(simboloDoc);
                }
                // Decorador
                if (this.simbolos[this.atual].tipo === delegua_2.default.ARROBA) {
                    await this.resolverDecoradores();
                    continue;
                }
                // Detecção de bloco de contexto: modificador seguido de '{'
                const tipoAtual = this.simbolos[this.atual].tipo;
                const tipoProximo = this.simbolos[this.atual + 1]?.tipo;
                const ehBlocoAcesso = [delegua_2.default.PRIVADO, delegua_2.default.PROTEGIDO].includes(tipoAtual) &&
                    tipoProximo === delegua_2.default.CHAVE_ESQUERDA;
                const ehBlocoEstatico = tipoAtual === delegua_2.default.ESTATICO &&
                    tipoProximo === delegua_2.default.CHAVE_ESQUERDA;
                const ehBlocoAbstrato = tipoAtual === delegua_2.default.ABSTRATO &&
                    tipoProximo === delegua_2.default.CHAVE_ESQUERDA;
                if (ehBlocoAcesso) {
                    const novoAcesso = tipoAtual === delegua_2.default.PRIVADO ? 'privado' : 'protegido';
                    this.avancarEDevolverAnterior(); // consume modificador de acesso
                    this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após modificador de acesso de bloco.");
                    await compreenderMembros(novoAcesso, ehEstaticoPadrao, ehAbstratoPadrao);
                    this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' para fechar bloco de modificador de acesso.");
                    continue;
                }
                if (ehBlocoEstatico) {
                    this.avancarEDevolverAnterior(); // consume 'estático'
                    this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após 'estático'.");
                    await compreenderMembros(acessoPadrao, true, ehAbstratoPadrao);
                    this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' para fechar bloco estático.");
                    continue;
                }
                if (ehBlocoAbstrato) {
                    this.avancarEDevolverAnterior(); // consume 'abstrato'
                    this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após 'abstrato'.");
                    await compreenderMembros(acessoPadrao, ehEstaticoPadrao, true);
                    this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' para fechar bloco abstrato.");
                    continue;
                }
                const modificadorAcesso = acessoPadrao;
                const ehEstatico = ehEstaticoPadrao;
                // Método operador sobrecarregado: `operador+ (outro) { ... }`
                if (this.simbolos[this.atual].tipo === delegua_2.default.OPERADOR) {
                    const simboloOperadorKeyword = this.avancarEDevolverAnterior();
                    const simboloDoOperador = this.avancarEDevolverAnterior();
                    const nomeMetodoOp = 'operador' + simboloDoOperador.lexema;
                    const simboloNomeMetodo = {
                        tipo: delegua_2.default.IDENTIFICADOR,
                        lexema: nomeMetodoOp,
                        literal: null,
                        linha: simboloOperadorKeyword.linha,
                        hashArquivo: this.hashArquivo,
                    };
                    this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após operador sobrecarregado.");
                    let paramsOp = [];
                    if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
                        paramsOp = await this.logicaComumParametros();
                    }
                    this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros do operador.");
                    this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do corpo do operador.");
                    const indiceAberturaCorpoOp = this.atual - 1;
                    const quantidadeErrosAntesCorpoOp = this.erros.length;
                    let corpoOp = [];
                    try {
                        corpoOp = await this.blocoEscopo();
                    }
                    catch (erro) {
                        this.erros.push(erro);
                    }
                    if (this.erros.length > quantidadeErrosAntesCorpoOp) {
                        this.atual =
                            this.encontrarIndiceAposFechamentoDeBloco(indiceAberturaCorpoOp);
                        corpoOp = [];
                        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
                    }
                    const corpoFuncaoOp = new construtos_1.FuncaoConstruto(this.hashArquivo, simboloNomeMetodo.linha, paramsOp, corpoOp);
                    const metodoOp = new declaracoes_1.FuncaoDeclaracao(simboloNomeMetodo, corpoFuncaoOp);
                    metodoOp.estatico = ehEstatico;
                    metodoOp.acesso = modificadorAcesso;
                    metodoOp.documentacao = docAtual;
                    metodos.push(metodoOp);
                    this.pilhaDecoradores = [];
                    continue;
                }
                // Método ou propriedade, determinado pelo token seguinte ao nome
                const proximoSimbolo = this.simbolos[this.atual + 1];
                switch (proximoSimbolo?.tipo) {
                    case delegua_2.default.PARENTESE_ESQUERDO: {
                        // Analisa: nome ( params ) [abstrato] [: tipoRetorno] { corpo }
                        const nomeMetodo = this.avancarEDevolverAnterior();
                        // Pré-registrar para suportar chamadas recursivas (igual a funcao()).
                        this.pilhaEscopos.definirInformacoesVariavel(nomeMetodo.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomeMetodo.lexema, 'qualquer'));
                        this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após nome do método.");
                        let params = [];
                        if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
                            params = await this.logicaComumParametros();
                        }
                        this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros do método.");
                        // Tipo de retorno opcional (igual a corpoDaFuncao())
                        let tipoRetorno = 'qualquer';
                        let definicaoExplicitaDeTipo = false;
                        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
                            tipoRetorno = this.verificarDefinicaoTipoAtual();
                            this.avancarEDevolverAnterior();
                            definicaoExplicitaDeTipo = true;
                        }
                        // Método de classe estrangeira não pode ter corpo.
                        if (ehEstrangeira &&
                            this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_ESQUERDA)) {
                            throw this.erro(this.simbolos[this.atual], 'Métodos de classe estrangeira não podem ter corpo.');
                        }
                        // Método é abstrato quando: (a) está dentro de um bloco `abstrato {}`,
                        // ou (b) a classe é abstrata/estrangeira e o próximo token não é `{`.
                        const ehAbstrato = ehAbstratoPadrao ||
                            ehEstrangeira ||
                            (ehAbstrata &&
                                !this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_ESQUERDA));
                        if (ehAbstrato) {
                            // Método abstrato/estrangeiro: sem corpo
                            this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
                            const corpoVazio = new construtos_1.FuncaoConstruto(this.hashArquivo, nomeMetodo.linha, params, [], tipoRetorno, definicaoExplicitaDeTipo);
                            const metodoAbstrato = new declaracoes_1.FuncaoDeclaracao(nomeMetodo, corpoVazio, tipoRetorno);
                            metodoAbstrato.estatico = ehEstatico;
                            metodoAbstrato.abstrato = true;
                            metodoAbstrato.acesso = modificadorAcesso;
                            metodoAbstrato.documentacao = docAtual;
                            metodos.push(metodoAbstrato);
                        }
                        else {
                            // Método concreto: com corpo.
                            // Inferência de tipo de retorno igual a corpoDaFuncao().
                            this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do corpo do método.");
                            const indiceAberturaCorpo = this.atual - 1;
                            const quantidadeErrosAntesCorpo = this.erros.length;
                            let corpo = [];
                            try {
                                corpo = await this.blocoEscopo();
                            }
                            catch (erro) {
                                this.erros.push(erro);
                            }
                            if (this.erros.length > quantidadeErrosAntesCorpo) {
                                this.atual =
                                    this.encontrarIndiceAposFechamentoDeBloco(indiceAberturaCorpo);
                                corpo = [];
                                this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
                            }
                            let expressoesRetorna = [];
                            for (const declaracao of corpo) {
                                expressoesRetorna = expressoesRetorna.concat((0, comum_1.buscarRetornos)(declaracao));
                            }
                            const tiposRetornos = new Set(expressoesRetorna
                                .filter((e) => e.tipo !== 'qualquer')
                                .map((e) => e.tipo));
                            const retornaChamadoExplicitamente = tiposRetornos.size > 0;
                            tiposRetornos.delete('qualquer');
                            if (tipoRetorno === 'qualquer') {
                                if (tiposRetornos.size > 0) {
                                    tipoRetorno = tiposRetornos.values().next().value;
                                }
                                else if (!retornaChamadoExplicitamente &&
                                    !definicaoExplicitaDeTipo) {
                                    tipoRetorno = 'vazio';
                                }
                            }
                            const corpoFuncao = new construtos_1.FuncaoConstruto(this.hashArquivo, nomeMetodo.linha, params, corpo, tipoRetorno);
                            const tipoDaFuncao = `função<${tipoRetorno}>`;
                            const metodo = new declaracoes_1.FuncaoDeclaracao(nomeMetodo, corpoFuncao, tipoDaFuncao);
                            metodo.estatico = ehEstatico;
                            metodo.acesso = modificadorAcesso;
                            metodo.decoradores = Array.from(this.pilhaDecoradores);
                            metodo.documentacao = docAtual;
                            metodos.push(metodo);
                            this.pilhaEscopos.definirInformacoesVariavel(nomeMetodo.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomeMetodo.lexema, tipoDaFuncao));
                            this.pilhaEscopos.registrarReferenciaFuncao(nomeMetodo.lexema, metodo);
                        }
                        this.pilhaDecoradores = [];
                        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();
                        let nomeTipoPropriedade = tipoPropriedade.lexema;
                        if (this.verificarTipoSimboloAtual(delegua_2.default.COLCHETE_ESQUERDO)) {
                            this.avancarEDevolverAnterior(); // consume '['
                            this.consumir(delegua_2.default.COLCHETE_DIREITO, "Esperado ']' após '[' na definição do tipo de propriedade.");
                            nomeTipoPropriedade = `${nomeTipoPropriedade}[]`;
                        }
                        const prop = new declaracoes_1.PropriedadeClasse(nomePropriedade, nomeTipoPropriedade, Array.from(this.pilhaDecoradores), modificadorAcesso, ehEstatico);
                        prop.documentacao = docAtual;
                        // Auto-propriedade: `nome: tipo { obter; definir; }`
                        // Ou corpo personalizado: `nome: tipo { obter() { ... } definir(valor) { ... } }`
                        if (this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_ESQUERDA)) {
                            this.avancarEDevolverAnterior(); // consume '{'
                            let temCorpoPersonalizado = false;
                            while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
                                !this.estaNoFinal()) {
                                const lexema = String(this.simbolos[this.atual].lexema || '').toLowerCase();
                                if (lexema === 'obter' || lexema === 'definir') {
                                    const ehObter = lexema === 'obter';
                                    this.avancarEDevolverAnterior(); // consume 'obter' / 'definir'
                                    if (this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_ESQUERDO)) {
                                        // Corpo personalizado: obter() { ... } / definir(valor) { ... }
                                        temCorpoPersonalizado = true;
                                        this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após acessor.");
                                        let paramsAcessor = [];
                                        if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
                                            paramsAcessor = await this.logicaComumParametros();
                                        }
                                        this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros do acessor.");
                                        this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do corpo do acessor.");
                                        const indiceAberturaCorpoAcessor = this.atual - 1;
                                        const quantidadeErrosAntesCorpoAcessor = this.erros.length;
                                        let corpoAcessor = [];
                                        try {
                                            corpoAcessor = await this.blocoEscopo();
                                        }
                                        catch (erro) {
                                            this.erros.push(erro);
                                        }
                                        if (this.erros.length > quantidadeErrosAntesCorpoAcessor) {
                                            this.atual = this.encontrarIndiceAposFechamentoDeBloco(indiceAberturaCorpoAcessor);
                                            corpoAcessor = [];
                                            this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
                                        }
                                        // Inferência de tipo de retorno
                                        let tipoAcessor = 'qualquer';
                                        let expressoesRetornaAcessor = [];
                                        for (const declaracao of corpoAcessor) {
                                            expressoesRetornaAcessor =
                                                expressoesRetornaAcessor.concat((0, comum_1.buscarRetornos)(declaracao));
                                        }
                                        const tiposRetornosAcessor = new Set(expressoesRetornaAcessor
                                            .filter((e) => e.tipo !== 'qualquer')
                                            .map((e) => e.tipo));
                                        const retornaExplicitamenteAcessor = tiposRetornosAcessor.size > 0;
                                        tiposRetornosAcessor.delete('qualquer');
                                        if (tiposRetornosAcessor.size > 0) {
                                            tipoAcessor = tiposRetornosAcessor
                                                .values()
                                                .next().value;
                                        }
                                        else if (!retornaExplicitamenteAcessor) {
                                            tipoAcessor = 'vazio';
                                        }
                                        const corpoFuncaoAcessor = new construtos_1.FuncaoConstruto(this.hashArquivo, nomePropriedade.linha, paramsAcessor, corpoAcessor, tipoAcessor);
                                        const tipoDaFuncaoAcessor = `função<${tipoAcessor}>`;
                                        const metodoAcessor = new declaracoes_1.FuncaoDeclaracao(nomePropriedade, corpoFuncaoAcessor, tipoDaFuncaoAcessor);
                                        metodoAcessor.estatico = ehEstatico;
                                        metodoAcessor.acesso = modificadorAcesso;
                                        metodoAcessor.eObtenedor = ehObter;
                                        metodoAcessor.eDefinidor = !ehObter;
                                        metodos.push(metodoAcessor);
                                    }
                                    else {
                                        // Forma trivial: obter; / definir;
                                        if (ehObter) {
                                            prop.autoObter = true;
                                        }
                                        else {
                                            prop.autoDefinir = true;
                                        }
                                        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
                                    }
                                }
                                else {
                                    break;
                                }
                            }
                            this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após acessores da propriedade.");
                            // Corpo personalizado: obtenedor/definidor são métodos — não há campos base a declarar.
                            // Para auto-propriedades, o campo base iniciado por '_' é criado em tempo de execução com base
                            // nos indicadores autoObter/autoDefinir. O avaliador sintático usa o nome original ('nome').
                            if (!temCorpoPersonalizado) {
                                propriedades.push(prop);
                            }
                        }
                        else {
                            this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
                            propriedades.push(prop);
                        }
                        this.pilhaDecoradores = [];
                        break;
                    }
                    default:
                        throw this.erro(this.simbolos[this.atual], 'Esperado definição de método ou propriedade.');
                }
            }
        };
        this.metodosClasseAtualEmAnalise = metodos;
        await compreenderMembros('publico', false);
        this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após o escopo da classe.");
        // Verificação em tempo de análise: classe deve implementar todos os contratos das interfaces.
        const linhaFinalClasse = this.simbolos[this.atual - 1].linha;
        for (const nomeInterface of implementaInterfaces) {
            const interfaceDecl = this.interfacesDeclaradas[nomeInterface.lexema];
            if (!interfaceDecl) {
                this.erros.push(this.erro(nomeInterface, `Interface '${nomeInterface.lexema}' não foi declarada antes da classe '${simbolo.lexema}'.`));
                continue;
            }
            const membrosFaltando = [];
            for (const assinatura of interfaceDecl.metodos) {
                const nomeMetodo = assinatura.nome.lexema;
                const implementado = metodos.some((m) => m.simbolo.lexema === nomeMetodo);
                if (!implementado) {
                    membrosFaltando.push({
                        tipo: 'metodo',
                        nome: nomeMetodo,
                        parametros: assinatura.parametros.map((p) => ({
                            nome: p.nome.lexema,
                            tipoDado: p.tipoDado,
                        })),
                        tipoRetorno: assinatura.tipoRetorno,
                    });
                }
            }
            for (const prop of interfaceDecl.propriedades) {
                const nomeProp = prop.nome.lexema;
                const implementada = propriedades.some((p) => p.nome.lexema === nomeProp);
                if (!implementada) {
                    membrosFaltando.push({
                        tipo: 'propriedade',
                        nome: nomeProp,
                        tipoPropriedade: prop.tipo,
                    });
                }
            }
            if (membrosFaltando.length > 0) {
                const correcaoSugerida = {
                    tipo: 'implementar-interface',
                    nomeInterface: nomeInterface.lexema,
                    nomeClasse: simbolo.lexema,
                    membrosFaltando,
                    linhaFinalClasse,
                };
                for (const membro of membrosFaltando) {
                    const mensagem = membro.tipo === 'metodo'
                        ? `Classe '${simbolo.lexema}' não implementa o método '${membro.nome}' exigido pela interface '${nomeInterface.lexema}'.`
                        : `Classe '${simbolo.lexema}' não declara a propriedade '${membro.nome}' exigida pela interface '${nomeInterface.lexema}'.`;
                    const erro = this.erro(simbolo, mensagem);
                    erro.correcaoSugerida = correcaoSugerida;
                    this.erros.push(erro);
                }
            }
        }
        const definicaoClasse = new declaracoes_1.Classe(simbolo, superClasses, metodos, propriedades, pilhaDecoradoresClasse, ehAbstrata, ehEstrangeira, ehEstatica, implementaInterfaces, mesclas);
        this.tiposDefinidosEmCodigo[definicaoClasse.simbolo.lexema] = definicaoClasse;
        this.superclasseAtual = undefined;
        this.metodosClasseAtualEmAnalise = undefined;
        return definicaoClasse;
    }
    /**
     * Analisa uma declaração de extensão:
     * `extensão [global] de <tipo> { <métodos> }`
     */
    async declaracaoDeExtensao() {
        let ehGlobal = false;
        if (this.simbolos[this.atual].tipo === delegua_2.default.IDENTIFICADOR &&
            this.simbolos[this.atual].lexema === 'global') {
            this.avancarEDevolverAnterior();
            ehGlobal = true;
        }
        this.consumir(delegua_2.default.DE, "Esperado 'de' após 'extensão'.");
        const simboloTipo = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome do tipo após 'de'.");
        this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após o nome do tipo.");
        const metodos = [];
        const nomeTipo = simboloTipo.lexema;
        if (!this.primitivasConhecidas[nomeTipo]) {
            this.primitivasConhecidas[nomeTipo] = {};
        }
        while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
            !this.estaNoFinal()) {
            // Pular comentários dentro do corpo da extensão.
            if (this.simbolos[this.atual].tipo === delegua_2.default.COMENTARIO ||
                this.simbolos[this.atual].tipo === delegua_2.default.LINHA_COMENTARIO) {
                this.avancarEDevolverAnterior();
                continue;
            }
            // Apenas métodos são suportados em extensões.
            const proximoSimbolo = this.simbolos[this.atual + 1];
            if (proximoSimbolo?.tipo !== delegua_2.default.PARENTESE_ESQUERDO) {
                throw this.erro(this.simbolos[this.atual], 'Esperado declaração de método na extensão.');
            }
            const nomeMetodo = this.avancarEDevolverAnterior();
            this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após nome do método.");
            let params = [];
            if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
                params = await this.logicaComumParametros();
            }
            this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros do método.");
            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 corpo do método.");
            const corpo = await this.blocoEscopo();
            let expressoesRetorna = [];
            for (const declaracao of corpo) {
                expressoesRetorna = expressoesRetorna.concat((0, comum_1.buscarRetornos)(declaracao));
            }
            const tiposRetornos = new Set(expressoesRetorna.filter((e) => e.tipo !== 'qualquer').map((e) => e.tipo));
            const retornaChamadoExplicitamente = tiposRetornos.size > 0;
            tiposRetornos.delete('qualquer');
            if (tipoRetorno === 'qualquer') {
                if (tiposRetornos.size > 0) {
                    tipoRetorno = tiposRetornos.values().next().value;
                }
                else if (!retornaChamadoExplicitamente && !definicaoExplicitaDeTipo) {
                    tipoRetorno = 'vazio';
                }
            }
            const corpoFuncao = new construtos_1.FuncaoConstruto(this.hashArquivo, nomeMetodo.linha, params, corpo, tipoRetorno);
            const tipoDaFuncao = `função<${tipoRetorno}>`;
            const metodo = new declaracoes_1.FuncaoDeclaracao(nomeMetodo, corpoFuncao, tipoDaFuncao);
            metodos.push(metodo);
            const argumentos = params.map((parametro) => new informacao_elemento_sintatico_1.InformacaoElementoSintatico(parametro.nome.lexema, parametro.tipoDado || 'qualquer', parametro.valorPadrao === undefined));
            this.primitivasConhecidas[nomeTipo][nomeMetodo.lexema] =
                new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomeMetodo.lexema, tipoRetorno, true, argumentos);
        }
        this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' ao final da extensão.");
        return new declaracoes_1.Extensao(simboloTipo, metodos, ehGlobal, this.hashArquivo);
    }
    /**
     * 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 resolverDecoradores
     */
    async resolverDeclaracaoForaDeBloco() {
        try {
            while (this.verificarTipoSimboloAtual(delegua_2.default.ARROBA)) {
                await this.resolverDecoradores();
            }
            // Documentário (/** ... */) antes de uma declaração de função.
            let documentacaoTopo = undefined;
            if (this.verificarTipoSimboloAtual(delegua_2.default.DOCUMENTARIO)) {
                const simboloDoc = this.avancarEDevolverAnterior();
                documentacaoTopo = new construtos_1.ComentarioComoConstruto(simboloDoc);
            }
            // Decoradores podem aparecer após um documentário.
            while (this.verificarTipoSimboloAtual(delegua_2.default.ARROBA)) {
                await this.resolverDecoradores();
            }
            if (this.verificarTipoSimboloAtual(delegua_2.default.FUNCAO) ||
                this.verificarTipoSimboloAtual(delegua_2.default.FUNÇÃO)) {
                if (this.verificarTipoProximoSimbolo(delegua_2.default.DE)) {
                    this.avancarEDevolverAnterior(); // `função`
                    this.avancarEDevolverAnterior(); // `de`
                    const simboloDecorador = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado 'decorador' após 'função de'.");
                    if (simboloDecorador.lexema !== 'decorador') {
                        throw this.erro(simboloDecorador, "Esperado 'decorador' após 'função de'.");
                    }
                    const declaracaoFuncao = (await this.funcao('funcao'));
                    declaracaoFuncao.eFuncaoDeDecorador = true;
                    declaracaoFuncao.documentacao = documentacaoTopo;
                    return declaracaoFuncao;
                }
                if (this.verificarTipoProximoSimbolo(delegua_2.default.IDENTIFICADOR)) {
                    this.avancarEDevolverAnterior();
                    const declaracaoFuncao = (await this.funcao('funcao'));
                    declaracaoFuncao.documentacao = documentacaoTopo;
                    return declaracaoFuncao;
                }
            }
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CLASSE)) {
                return await this.declaracaoDeClasse();
            }
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.INTERFACE)) {
                return await this.declaracaoDeInterface();
            }
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.EXTENSAO)) {
                return await this.declaracaoDeExtensao();
            }
            return await this.resolverDeclaracao();
        }
        catch (erro) {
            this.sincronizar();
            this.erros.push(erro);
            return undefined;
        }
    }
    /**
     * Usado quando há erros na avaliação sintática.
     * Garante que o avaliador sintático não entre em _loop_ infinito.
     * @returns Sempre retorna `void`.
     */
    sincronizar() {
        if (this.estaNoFinal()) {
            return;
        }
        this.avancarEDevolverAnterior(); // avança além do token com erro
        while (!this.estaNoFinal()) {
            // Um ponto-e-vírgula já consumido indica fronteira limpa entre declarações.
            if (this.simbolos[this.atual].tipo === delegua_2.default.PONTO_E_VIRGULA)
                return;
            // Uma palavra-chave de início de declaração ou fecha-chave à frente:
            // retorna SEM consumir o token, para que o chamador o analise normalmente.
            switch (this.simbolos[this.atual].tipo) {
                case delegua_2.default.CHAVE_DIREITA:
                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();
        }
    }
    encontrarIndiceAposFechamentoDeBloco(indiceAbertura) {
        let aberturasPendentes = 1;
        for (let indice = indiceAbertura + 1; indice < this.simbolos.length; indice++) {
            if (this.simbolos[indice].tipo === delegua_2.default.CHAVE_ESQUERDA) {
                aberturasPendentes++;
            }
            else if (this.simbolos[indice].tipo === delegua_2.default.CHAVE_DIREITA) {
                aberturasPendentes--;
                if (aberturasPendentes === 0) {
                    return indice + 1;
                }
            }
        }
        return this.simbolos.length;
    }
    /**
     * 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.
     */
    async resolverDeclaracao() {
        switch (this.simbolos[this.atual].tipo) {
            case delegua_2.default.AJUDA:
                return await this.declaracaoAjuda();
            case delegua_2.default.ASSERCAO:
                this.avancarEDevolverAnterior();
                return await this.declaracaoAssercao();
            case delegua_2.default.CHAVE_ESQUERDA:
                return await this.declaracaoBloco();
            case delegua_2.default.COMENTARIO:
                return this.declaracaoComentarioUmaLinha();
            case delegua_2.default.DOCUMENTARIO: {
                const simboloDocumentario = this.avancarEDevolverAnterior();
                // Se o próximo token for uma declaração de função com identificador,
                // anexa o documentário como documentação da função.
                if ((this.verificarTipoSimboloAtual(delegua_2.default.FUNCAO) ||
                    this.verificarTipoSimboloAtual(delegua_2.default.FUNÇÃO)) &&
                    this.verificarTipoProximoSimbolo(delegua_2.default.IDENTIFICADOR)) {
                    this.avancarEDevolverAnterior();
                    const declaracaoFuncao = (await this.funcao('funcao'));
                    declaracaoFuncao.documentacao = new construtos_1.ComentarioComoConstruto(simboloDocumentario);
                    return declaracaoFuncao;
                }
                return new declaracoes_1.Comentario(simboloDocumentario.hashArquivo, simboloDocumentario.linha, simboloDocumentario.literal, false);
            }
            case delegua_2.default.CONSTANTE:
                this.avancarEDevolverAnterior();
                return await this.declaracaoDeConstantes();
            case delegua_2.default.CONTINUA:
                this.avancarEDevolverAnterior();
                return this.declaracaoContinua();
            case delegua_2.default.ENQUANTO:
                this.avancarEDevolverAnterior();
                return await this.declaracaoEnquanto();
            case delegua_2.default.ESCOLHA:
                this.avancarEDevolverAnterior();
                return await this.declaracaoEscolha();
            case delegua_2.default.ESCREVA:
                this.avancarEDevolverAnterior();
                return await this.declaracaoEscreva();
            case delegua_2.default.FALHAR:
                this.avancarEDevolverAnterior();
                return await this.declaracaoFalhar();
            case delegua_2.default.FAZER:
                const simboloFazer = this.avancarEDevolverAnterior();
                return await this.declaracaoFazer(simboloFazer);
            case delegua_2.default.IMPORTAR:
                this.avancarEDevolverAnterior();
                return await this.declaracaoImportar();
            case delegua_2.default.LINHA_COMENTARIO:
                return this.declaracaoComentarioMultilinha();
            case delegua_2.default.PARA:
                this.avancarEDevolverAnterior();
                return await this.declaracaoPara();
            case delegua_2.default.SUSTAR:
                this.avancarEDevolverAnterior();
                return this.declaracaoSustar();
            case delegua_2.default.SE:
                this.avancarEDevolverAnterior();
                return await this.declaracaoSe();
            case delegua_2.default.RETORNA:
                this.avancarEDevolverAnterior();
                return await this.declaracaoRetorna();
            case delegua_2.default.TENDO:
                this.avancarEDevolverAnterior();
                return await this.declaracaoTendoComo();
            case delegua_2.default.TENTE:
                this.avancarEDevolverAnterior();
                return await this.declaracaoTente();
            case delegua_2.default.VARIAVEL:
                this.avancarEDevolverAnterior();
                return await 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 await this.declaracaoExpressao();
    }
    async declaracaoAjuda() {
        const simboloAjuda = this.avancarEDevolverAnterior();
        if (this.estaNoFinal() ||
            this.simbolos[this.atual].tipo !== delegua_2.default.PARENTESE_ESQUERDO) {
            return new declaracoes_1.Ajuda(simboloAjuda.hashArquivo, simboloAjuda.linha, undefined, false);
        }
        this.avancarEDevolverAnterior(); // parêntese esquerdo
        if (this.simbolos[this.atual].tipo === delegua_2.default.PARENTESE_DIREITO) {
            this.avancarEDevolverAnterior();
            return new declaracoes_1.Ajuda(simboloAjuda.hashArquivo, simboloAjuda.linha, undefined, true);
        }
        const topicoPalavraChave = this.tentarResolverTopicoAjudaPalavraChave();
        if (topicoPalavraChave) {
            this.consumir(delegua_2.default.PARENTESE_DIREITO, `Esperado parêntese direito após expressão usada como argumento em ajuda(). Atual: ${this.simbolos[this.atual].lexema}.`);
            return new declaracoes_1.Ajuda(simboloAjuda.hashArquivo, simboloAjuda.linha, topicoPalavraChave);
        }
        const expressaoAjuda = await this.expressao();
        this.consumir(delegua_2.default.PARENTESE_DIREITO, `Esperado parêntese direito após expressão usada como argumento em ajuda(). Atual: ${this.simbolos[this.atual].lexema}.`);
        return new declaracoes_1.Ajuda(simboloAjuda.hashArquivo, simboloAjuda.linha, expressaoAjuda);
    }
    /**
     * 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_elemento_sintatico_1.InformacaoElementoSintatico('aleatorio', 'número'));
        this.pilhaEscopos.definirInformacoesVariavel('aleatório', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('aleatório', 'número'));
        this.pilhaEscopos.definirInformacoesVariavel('aleatorioEntre', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('aleatorioEntre', 'número', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('minimo', 'número'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('maximo', 'número'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('aleatórioEntre', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('aleatórioEntre', 'número', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('minimo', 'número'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('maximo', 'número'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('algum', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('algum', 'lógico', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('clonar', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('clonar', 'qualquer', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valor', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrar', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrar', 'qualquer', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrarIndice', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarIndice', 'inteiro', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrarÍndice', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarÍndice', 'inteiro', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrarUltimo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarUltimo', 'inteiro', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrarÚltimo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarÚltimo', 'inteiro', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrarUltimoIndice', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarUltimoIndice', 'inteiro', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrarÚltimoÍndice', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarÚltimoÍndice', 'inteiro', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('filtrarPor', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('filtrarPor', 'qualquer[]', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoFiltragem', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('incluido', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('incluido', 'lógico', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valor', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('incluído', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('incluído', 'lógico', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valor', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('inteiro', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('inteiro', 'inteiro', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valor', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('longo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('longo', 'longo', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valor', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('intervalo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('intervalo', 'inteiro[]', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorInicial', 'qualquer'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorFinal', 'qualquer', false),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorPasso', 'qualquer', false),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('mapear', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('mapear', 'qualquer[]', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoMapeamento', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('maximo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('maximo', 'qualquer', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('máximo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('máximo', 'qualquer', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('minimo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('minimo', 'qualquer', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('mínimo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('mínimo', 'qualquer', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('numero', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('número', 'número', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorParaConverter', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('número', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('número', 'número', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorParaConverter', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('ordenar', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('ordenar', 'qualquer[]', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoOrdenacao', 'função', false),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('paraCada', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('paraCada', 'qualquer[]', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoFiltragem', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('primeiroEmCondicao', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('primeiroEmCondicao', 'qualquer', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoFiltragem', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('primeiroEmCondição', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('primeiroEmCondição', 'qualquer', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoFiltragem', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('real', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('real', 'número', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorParaConverter', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('reduzir', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('reduzir', 'qualquer', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoReducao', 'função'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorInicial', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('tamanho', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('tamanho', 'inteiro', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('objeto', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('texto', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('texto', 'texto', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorParaConverter', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('todos', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('todos', 'lógico', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('iteravel', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('todosEmCondicao', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('todosEmCondicao', 'lógico', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('iteravel', 'qualquer'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoCondicional', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('todosEmCondição', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('todosEmCondição', 'lógico', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('iteravel', 'qualquer'),
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoCondicional', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('tupla', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('tupla', 'tupla', true, [
            new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
        ]));
        // Classe base global `Objeto`, registrada pelo interpretador em tempo de execução.
        this.pilhaEscopos.definirInformacoesVariavel('Objeto', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('Objeto', '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_elemento_sintatico_1.InformacaoElementoSintatico(nomeTipo, tipo));
            }
        }
    }
    async 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?.simbolos || [];
        this.pilhaDecoradores = [];
        this.tiposDefinidosEmCodigo = {};
        this.interfacesDeclaradas = {};
        this.montaoTipos = new montao_tipos_1.MontaoTipos();
        this.inicializarPilhaEscopos();
        let declaracoes = [];
        while (!this.estaNoFinal()) {
            await this.resolverDecoradores();
            const retornoDeclaracao = await 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