UNPKG

@designliquido/delegua

Version:

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

1,700 lines 101 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_variavel_ou_constante_1 = require("../informacao-variavel-ou-constante");
const delegua_1 = __importDefault(require("../tipos-de-dados/delegua"));
const delegua_2 = __importDefault(require("../tipos-de-simbolos/delegua"));
const primitivas_dicionario_1 = __importDefault(require("../bibliotecas/primitivas-dicionario"));
const primitivas_numero_1 = __importDefault(require("../bibliotecas/primitivas-numero"));
const primitivas_texto_1 = __importDefault(require("../bibliotecas/primitivas-texto"));
const primitivas_vetor_1 = __importDefault(require("../bibliotecas/primitivas-vetor"));
const comum_1 = require("./comum");
/**
 * O avaliador sintático (_Parser_) é responsável por transformar os símbolos do Lexador em estruturas de alto nível.
 * Essas estruturas de alto nível são as partes que executam lógica de programação de fato.
 * Há dois grupos de estruturas de alto nível: Construtos e Declarações.
 *
 * Construtos não existem por si só: cada construto precisa estar dentro de uma declaração para ser
 * aceito pela próxima etapa, como tradução, interpretação, análise semântica, etc.
 *
 * Este é o avaliador sintático de Delégua que, assim como todos os demais dialetos baseados
 * neste núcleo, são uma derivação do avaliador sintático base. Aqui estão implementadas várias mecânicas
 * a mais relacionadas a tipagem e registros de bibliotecas externas. Por exemplo, `tiposDeFerramentasExternas`
 * é utilizada em [Liquido](https://github.com/DesignLiquido/liquido) para registro de tipos exclusivos
 * de Liquido, como classes de requisição e resposta. `primitivasConhecidas` é utilizada aqui para
 * registro de métodos relacionados a tipos, e também para as bibliotecas externas de Delégua registrarem
 * suas respectivas resoluções de tipos.
 */
class AvaliadorSintatico extends avaliador_sintatico_base_1.AvaliadorSintaticoBase {
    constructor(performance = false) {
        super();
        this.hashArquivo = 0;
        this.atual = 0;
        this.blocos = 0;
        this.erros = [];
        this.performance = performance;
        this.tiposDefinidosEmCodigo = {};
        this.tiposDeFerramentasExternas = {};
        this.primitivasConhecidas = {};
        (0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'dicionário', primitivas_dicionario_1.default);
        (0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'número', primitivas_numero_1.default);
        (0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'texto', primitivas_texto_1.default);
        (0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'vetor', primitivas_vetor_1.default);
        this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
    }
    verificarDefinicaoTipoAtual() {
        const tipos = [...Object.values(delegua_1.default)];
        if (this.simbolos[this.atual].lexema in this.tiposDefinidosEmCodigo) {
            return this.simbolos[this.atual].lexema;
        }
        const lexemaElementar = this.simbolos[this.atual].lexema.toLowerCase();
        const tipoElementarResolvido = tipos.find((tipo) => tipo === lexemaElementar);
        if (!tipoElementarResolvido) {
            throw this.erro(this.simbolos[this.atual], `Tipo de dados desconhecido: '${this.simbolos[this.atual].lexema}'.`);
        }
        if (this.verificarTipoProximoSimbolo(delegua_2.default.COLCHETE_ESQUERDO)) {
            const tiposVetores = [
                'inteiro[]',
                'numero[]',
                'número[]',
                'qualquer[]',
                'real[]',
                'texto[]',
            ];
            this.avancarEDevolverAnterior();
            if (!this.verificarTipoProximoSimbolo(delegua_2.default.COLCHETE_DIREITO)) {
                throw this.erro(this.simbolos[this.atual], `Esperado símbolo de fechamento do vetor: ']'. Atual: ${this.simbolos[this.atual].lexema}`);
            }
            const tipoVetor = tiposVetores.find((tipo) => tipo === `${lexemaElementar}[]`);
            this.avancarEDevolverAnterior();
            return tipoVetor;
        }
        return tipoElementarResolvido;
    }
    obterChaveDicionario() {
        switch (this.simbolos[this.atual].tipo) {
            case delegua_2.default.NUMERO:
            case delegua_2.default.TEXTO:
            case delegua_2.default.FALSO:
            case delegua_2.default.VERDADEIRO:
                return this.primario();
            case delegua_2.default.IDENTIFICADOR:
                const simboloIdentificador = this.avancarEDevolverAnterior();
                let tipoOperando;
                if (simboloIdentificador.lexema in this.tiposDefinidosEmCodigo) {
                    tipoOperando = simboloIdentificador.lexema;
                }
                else {
                    tipoOperando = this.pilhaEscopos.obterTipoVariavelPorNome(simboloIdentificador.lexema);
                }
                if (!['numero', 'número', 'texto', 'lógico'].includes(tipoOperando)) {
                    throw this.erro(simboloIdentificador, `Tipo ${tipoOperando} de identificador ${simboloIdentificador.lexema} não é válido como chave de dicionário.`);
                }
                return new construtos_1.Variavel(this.hashArquivo, simboloIdentificador, tipoOperando);
            case delegua_2.default.COLCHETE_ESQUERDO:
                this.avancarEDevolverAnterior();
                if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
                    return this.construtoTupla();
                }
                throw this.erro(this.simbolos[this.atual], `Esperado parêntese esquerdo após colchete esquerdo para definição de chave de dicionário. Atual: ${this.simbolos[this.atual].tipo}.`);
            default:
                throw this.erro(this.simbolos[this.atual], `Símbolo ${this.simbolos[this.atual].tipo} inesperado ou inválido como chave de dicionário.`);
        }
    }
    construtoDicionario(simboloChaveEsquerda) {
        this.avancarEDevolverAnterior();
        const chaves = [];
        const valores = [];
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_DIREITA)) {
            return new construtos_1.Dicionario(this.hashArquivo, Number(simboloChaveEsquerda.linha), [], []);
        }
        while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_DIREITA)) {
            const chave = this.obterChaveDicionario();
            this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' entre chave e valor.");
            const valor = this.atribuir();
            chaves.push(chave);
            valores.push(valor);
            if (this.simbolos[this.atual].tipo !== delegua_2.default.CHAVE_DIREITA) {
                this.consumir(delegua_2.default.VIRGULA, 'Esperado vírgula antes da próxima expressão.');
            }
        }
        return new construtos_1.Dicionario(this.hashArquivo, Number(simboloChaveEsquerda.linha), chaves, valores);
    }
    construtoTupla() {
        const expressao = this.expressao();
        const argumentos = [expressao];
        while (this.simbolos[this.atual].tipo === delegua_2.default.VIRGULA) {
            this.avancarEDevolverAnterior();
            argumentos.push(this.expressao());
        }
        this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após a expressão.");
        this.consumir(delegua_2.default.COLCHETE_DIREITO, "Esperado ']' após a expressão.");
        return new tuplas_1.SeletorTuplas(...argumentos);
    }
    primario() {
        const simboloAtual = this.simbolos[this.atual];
        let valores = [];
        switch (simboloAtual.tipo) {
            case delegua_2.default.CHAVE_ESQUERDA:
                return this.construtoDicionario(simboloAtual);
            case delegua_2.default.COLCHETE_ESQUERDO:
                this.avancarEDevolverAnterior();
                valores = [];
                if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_DIREITO)) {
                    return new construtos_1.Vetor(this.hashArquivo, Number(simboloAtual.linha), [], 0, 'qualquer[]');
                }
                while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_DIREITO)) {
                    if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
                        return this.construtoTupla();
                    }
                    const valor = this.atribuir();
                    valores.push(valor);
                    if (this.simbolos[this.atual].tipo !== delegua_2.default.COLCHETE_DIREITO) {
                        this.consumir(delegua_2.default.VIRGULA, 'Esperado vírgula antes da próxima expressão.');
                    }
                }
                const tipoVetor = (0, inferenciador_1.inferirTipoVariavel)(valores);
                return new construtos_1.Vetor(this.hashArquivo, Number(simboloAtual.linha), valores, valores.length, tipoVetor);
            case delegua_2.default.EXPRESSAO_REGULAR:
                let valor = '';
                let linhaAtual = this.simbolos[this.atual].linha;
                let eParExpressaoRegular = this.simbolos.filter((l) => l.linha === linhaAtual && l.tipo === delegua_2.default.EXPRESSAO_REGULAR).length %
                    2 ===
                    0;
                if (eParExpressaoRegular) {
                    this.avancarEDevolverAnterior();
                    while (!this.verificarTipoSimboloAtual(delegua_2.default.EXPRESSAO_REGULAR)) {
                        valor += this.simbolos[this.atual].lexema || '';
                        this.avancarEDevolverAnterior();
                    }
                    this.avancarEDevolverAnterior();
                    return new construtos_1.ExpressaoRegular(this.hashArquivo, simboloAtual, valor);
                }
            case delegua_2.default.FALSO:
                this.avancarEDevolverAnterior();
                return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), false, 'lógico');
            case delegua_2.default.FUNCAO:
            case delegua_2.default.FUNÇÃO:
                const simboloFuncao = this.avancarEDevolverAnterior();
                const corpoDaFuncao = this.corpoDaFuncao(simboloFuncao.lexema);
                this.pilhaEscopos.definirInformacoesVariavel(simboloFuncao.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simboloFuncao.lexema, 'função'));
                return corpoDaFuncao;
            case delegua_2.default.IDENTIFICADOR:
                const simboloIdentificador = this.avancarEDevolverAnterior();
                let tipoOperando;
                if (simboloIdentificador.lexema in this.tiposDefinidosEmCodigo) {
                    tipoOperando = simboloIdentificador.lexema;
                }
                else {
                    try {
                        tipoOperando = this.pilhaEscopos.obterTipoVariavelPorNome(simboloIdentificador.lexema);
                    }
                    catch (erro) {
                        throw this.erro(simboloIdentificador, erro.message);
                    }
                }
                // Se o próximo símbolo é um incremento ou um decremento,
                // aqui deve retornar um unário correspondente.
                // Caso contrário, apenas retornar um construto de variável.
                if (this.simbolos[this.atual] &&
                    [delegua_2.default.INCREMENTAR, delegua_2.default.DECREMENTAR].includes(this.simbolos[this.atual].tipo)) {
                    const simboloIncrementoDecremento = this.avancarEDevolverAnterior();
                    return new construtos_1.Unario(this.hashArquivo, simboloIncrementoDecremento, new construtos_1.Variavel(this.hashArquivo, simboloIdentificador, tipoOperando || 'qualquer'), 'DEPOIS');
                }
                return new construtos_1.Variavel(this.hashArquivo, simboloIdentificador, tipoOperando || 'qualquer');
            case delegua_2.default.IMPORTAR:
                this.avancarEDevolverAnterior();
                return this.declaracaoImportar();
            case delegua_2.default.ISTO:
                this.avancarEDevolverAnterior();
                return new construtos_1.Isto(this.hashArquivo, Number(simboloAtual.linha), simboloAtual);
            case delegua_2.default.LEIA:
                return this.expressaoLeia();
            case delegua_2.default.NULO:
                this.avancarEDevolverAnterior();
                return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), null, 'nulo');
            case delegua_2.default.NUMERO:
            case delegua_2.default.NÚMERO:
            case delegua_2.default.TEXTO:
                const simboloNumeroTexto = this.avancarEDevolverAnterior();
                const tipoInferido = (0, inferenciador_1.inferirTipoVariavel)(simboloNumeroTexto.literal);
                const tipoDadosElementar = (0, inferenciador_1.tipoInferenciaParaTipoDadosElementar)(tipoInferido);
                return new construtos_1.Literal(this.hashArquivo, Number(simboloNumeroTexto.linha), simboloNumeroTexto.literal, tipoDadosElementar);
            case delegua_2.default.PARENTESE_ESQUERDO:
                this.avancarEDevolverAnterior();
                const expressao = this.expressao();
                this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após a expressão.");
                return new construtos_1.Agrupamento(this.hashArquivo, Number(simboloAtual.linha), expressao);
            case delegua_2.default.SUPER:
                const simboloSuper = this.avancarEDevolverAnterior();
                if (!this.superclasseAtual) {
                    throw this.erro(this.simbolos[this.atual], "'Super' usado fora de declaração de classe com herança.");
                }
                return new construtos_1.Super(this.hashArquivo, simboloSuper, this.superclasseAtual);
            case delegua_2.default.VERDADEIRO:
                this.avancarEDevolverAnterior();
                return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), true, 'lógico');
            case delegua_2.default.TIPO:
                this.avancarEDevolverAnterior();
                this.consumir(delegua_2.default.DE, "Esperado 'de' após 'tipo'.");
                let construto;
                if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.ESCREVA, delegua_2.default.LEIA, delegua_2.default.FUNCAO, delegua_2.default.FUNÇÃO, delegua_2.default.SE, delegua_2.default.ENQUANTO, delegua_2.default.PARA, delegua_2.default.RETORNA, delegua_1.default.INTEIRO, delegua_1.default.TEXTO, delegua_1.default.VETOR, delegua_1.default.LOGICO, delegua_1.default.LÓGICO, delegua_1.default.VAZIO)) {
                    construto = new construtos_1.ComponenteLinguagem(this.hashArquivo, this.simboloAnterior());
                }
                else {
                    construto = this.expressao();
                }
                if (construto.constructor.name === 'AcessoMetodoOuPropriedade') {
                    const construtoTipado = construto;
                    switch (construtoTipado.tipo) {
                        case delegua_1.default.DICIONARIO:
                        case delegua_1.default.DICIONÁRIO:
                            if (!(construtoTipado.simbolo.lexema in primitivas_dicionario_1.default)) {
                                throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de dicionário.`);
                            }
                            const primitivaDicionarioSelecionada = primitivas_dicionario_1.default[construtoTipado.simbolo.lexema];
                            construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaDicionarioSelecionada.tipoRetorno);
                            break;
                        case delegua_1.default.INTEIRO:
                        case delegua_1.default.NUMERO:
                        case delegua_1.default.NÚMERO:
                            if (!(construtoTipado.simbolo.lexema in primitivas_numero_1.default)) {
                                throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de número.`);
                            }
                            const primitivaNumeroSelecionada = primitivas_numero_1.default[construtoTipado.simbolo.lexema];
                            construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaNumeroSelecionada.tipoRetorno);
                            break;
                        case delegua_1.default.TEXTO:
                            if (!(construtoTipado.simbolo.lexema in primitivas_texto_1.default)) {
                                throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de texto.`);
                            }
                            const primitivaTextoSelecionada = primitivas_texto_1.default[construtoTipado.simbolo.lexema];
                            construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaTextoSelecionada.tipoRetorno);
                            break;
                        case delegua_1.default.VETOR:
                        case delegua_1.default.VETOR_NUMERO:
                        case delegua_1.default.VETOR_NÚMERO:
                        case delegua_1.default.VETOR_TEXTO:
                            if (!(construtoTipado.simbolo.lexema in primitivas_vetor_1.default)) {
                                throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de vetor.`);
                            }
                            const primitivaVetorSelecionada = primitivas_vetor_1.default[construtoTipado.simbolo.lexema];
                            construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaVetorSelecionada.tipoRetorno);
                            break;
                        default:
                            if (construtoTipado.tipo in this.tiposDefinidosEmCodigo) {
                                const tipoCorrespondente = this.tiposDefinidosEmCodigo[construtoTipado.tipo];
                                const possivelMetodo = tipoCorrespondente.metodos.filter((m) => m.simbolo.lexema === construtoTipado.simbolo.lexema);
                                if (possivelMetodo.length > 0) {
                                    construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, possivelMetodo[0].tipo);
                                    break;
                                }
                                const possivelPropriedade = tipoCorrespondente.propriedades.filter((p) => p.nome.lexema === construtoTipado.simbolo.lexema);
                                if (possivelPropriedade.length > 0) {
                                    construto = new construtos_1.AcessoPropriedade(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, possivelPropriedade[0].tipo);
                                    break;
                                }
                            }
                    }
                }
                return new construtos_1.TipoDe(this.hashArquivo, simboloAtual, construto);
        }
        throw this.erro(this.simbolos[this.atual], 'Esperado expressão.');
    }
    chamar() {
        let expressao = this.primario();
        let tipoPrimitiva = undefined;
        while (true) {
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
                expressao = this.finalizarChamada(expressao, tipoPrimitiva);
            }
            else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO)) {
                const nome = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome de método ou propriedade após '.'.");
                tipoPrimitiva = expressao.tipo;
                expressao = new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, expressao, nome);
            }
            else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_ESQUERDO)) {
                const indice = this.expressao();
                const simboloFechamento = this.consumir(delegua_2.default.COLCHETE_DIREITO, "Esperado ']' após escrita do indice.");
                expressao = new construtos_1.AcessoIndiceVariavel(this.hashArquivo, expressao, indice, simboloFechamento);
            }
            else {
                break;
            }
        }
        return expressao;
    }
    /**
     * `AcessoMetodoOuPropriedade` é um construto intermediário em Delégua, e deve ser resolvido como outro
     * construto antes de qualquer outra próxima etapa. Algumas validações adicionais também ocorrem aqui.
     * @param {AcessoMetodoOuPropriedade} entidadeChamadaResolvida O construto original.
     * @returns {Construto} O construto resolvido como um tipo mais específico.
     * @see finalizarChamada
     */
    resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamadaResolvida) {
        const construtoTipado = entidadeChamadaResolvida;
        switch (entidadeChamadaResolvida.tipo) {
            case delegua_1.default.DICIONARIO:
            case delegua_1.default.DICIONÁRIO:
                if (!(construtoTipado.simbolo.lexema in primitivas_dicionario_1.default)) {
                    throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de dicionário.`);
                }
                const primitivaDicionarioSelecionada = primitivas_dicionario_1.default[construtoTipado.simbolo.lexema];
                return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaDicionarioSelecionada.tipoRetorno);
            case delegua_1.default.INTEIRO:
            case delegua_1.default.NUMERO:
            case delegua_1.default.NÚMERO:
                if (!(construtoTipado.simbolo.lexema in primitivas_numero_1.default)) {
                    throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de número.`);
                }
                const primitivaNumeroSelecionada = primitivas_numero_1.default[construtoTipado.simbolo.lexema];
                return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaNumeroSelecionada.tipoRetorno);
            case delegua_1.default.MODULO:
            case delegua_1.default.MÓDULO:
                // Há dois casos para resolução de módulo:
                // Um quando o módulo é definido no próprio código (por exemplo, em um outro arquivo `.delegua`).
                // Outro quando é importado de uma biblioteca externa.
                // Este é o caso quando o módulo vem de outro arquivo `.delegua`.
                if (construtoTipado.simbolo.lexema in this.tiposDefinidosEmCodigo) {
                    // Construtor de classe.
                    return new construtos_1.Variavel(construtoTipado.hashArquivo, construtoTipado.simbolo, construtoTipado.objeto.tipo);
                }
                return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema);
            case delegua_1.default.TEXTO:
                if (!(construtoTipado.simbolo.lexema in primitivas_texto_1.default)) {
                    throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de texto.`);
                }
                const primitivaTextoSelecionada = primitivas_texto_1.default[construtoTipado.simbolo.lexema];
                return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaTextoSelecionada.tipoRetorno);
            case delegua_1.default.VETOR:
            case delegua_1.default.VETOR_NUMERO:
            case delegua_1.default.VETOR_NÚMERO:
            case delegua_1.default.VETOR_TEXTO:
                if (!(construtoTipado.simbolo.lexema in primitivas_vetor_1.default)) {
                    throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de vetor.`);
                }
                const primitivaVetorSelecionada = primitivas_vetor_1.default[construtoTipado.simbolo.lexema];
                return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaVetorSelecionada.tipoRetorno);
        }
        return entidadeChamadaResolvida;
    }
    validarArgumentosEntidadeChamada(argumentosEntidadeChamada, argumentosUtilizados) {
        if (argumentosEntidadeChamada.length === 0) {
            return [];
        }
        const possiveisErros = [];
        for (const [indice, argumentoEntidadeChamada] of argumentosEntidadeChamada.entries()) {
            const argumentoUtilizado = argumentosUtilizados[indice];
            if (!argumentoUtilizado) {
                if (argumentoEntidadeChamada.obrigatorio) {
                    possiveisErros.push(`Argumento ${argumentoEntidadeChamada.nome} é obrigatório, mas não foi fornecido.`);
                }
                continue;
            }
            if (argumentoUtilizado.tipo === null || argumentoUtilizado.tipo === undefined) {
                continue;
            }
            const argumentoEntidadeChamadaQualquer = argumentoEntidadeChamada.tipo.startsWith('qualquer');
            const argumentoUtilizadoQualquer = argumentoUtilizado.tipo.startsWith('qualquer');
            // Este caso é tarefa do analisador semântico apontar.
            if (argumentoEntidadeChamadaQualquer || argumentoUtilizadoQualquer) {
                continue;
            }
            const argumentoEntidadeChamadaVetor = argumentoEntidadeChamada.tipo.endsWith('[]');
            const argumentoUtilizadoVetor = argumentoUtilizado.tipo.endsWith('[]');
            if (argumentoEntidadeChamadaVetor !== argumentoUtilizadoVetor) {
                possiveisErros.push(`Argumento: ${argumentoEntidadeChamada.nome}. Tipo esperado: ${argumentoEntidadeChamada.tipo}; Tipo utilizado: ${argumentoUtilizado.tipo}`);
                continue;
            }
            const tipoArgumentoUtilizado = argumentoUtilizado.tipo.startsWith('funcao') ||
                argumentoUtilizado.tipo.startsWith('função')
                ? 'função'
                : argumentoUtilizado.tipo;
            const tipoArgumentoEntidadeChamada = argumentoEntidadeChamada.tipo.startsWith('funcao') ||
                argumentoEntidadeChamada.tipo.startsWith('função')
                ? 'função'
                : argumentoEntidadeChamada.tipo;
            if (tipoArgumentoUtilizado !== tipoArgumentoEntidadeChamada) {
                possiveisErros.push(`Argumento: ${argumentoEntidadeChamada.nome}. Tipo esperado: ${argumentoEntidadeChamada.tipo}; Tipo utilizado: ${argumentoUtilizado.tipo}`);
            }
        }
        return possiveisErros;
    }
    /**
     * Diversas verificações de resolução de entidade chamada, como resolver chamada da pilha ou usar referência, argumentos, etc.
     * @param entidadeChamada O construto da entidade chamada.
     * @param argumentos Os argumentos utilizados na chamada.
     * @param tipoPrimitiva Se for uma primitiva, o tipo dela. Senão, `undefined`.
     * @returns A entidade chamada resolvida, se as validações passarem.
     */
    resolverEntidadeChamada(entidadeChamada, argumentos, tipoPrimitiva = undefined) {
        if (entidadeChamada.constructor.name === 'Variavel') {
            const entidadeChamadaResolvidaVariavel = entidadeChamada;
            if (tipoPrimitiva === undefined) {
                // Provavelmente uma chamada a alguma função da biblioteca global.
                const informacoesPossivelFuncaoBibliotecaGlobal = this.pilhaEscopos.obterBibliotecaGlobal(entidadeChamadaResolvidaVariavel.simbolo.lexema);
                if (informacoesPossivelFuncaoBibliotecaGlobal) {
                    const erros = this.validarArgumentosEntidadeChamada(informacoesPossivelFuncaoBibliotecaGlobal.argumentos, argumentos);
                    if (erros.length > 0) {
                        throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamadaResolvidaVariavel.simbolo, `Erros ao resolver argumentos de chamada a ${entidadeChamadaResolvidaVariavel.simbolo.lexema}: \n${erros.reduce((mensagem, erro) => (mensagem += `${erro}\n`), '')}`);
                    }
                    return entidadeChamadaResolvidaVariavel;
                }
            }
            if (tipoPrimitiva !== undefined &&
                this.primitivasConhecidas[tipoPrimitiva].hasOwnProperty(entidadeChamadaResolvidaVariavel.simbolo.lexema)) {
                var informacoesPrimitiva = this.primitivasConhecidas[tipoPrimitiva][entidadeChamadaResolvidaVariavel.simbolo.lexema];
                const erros = this.validarArgumentosEntidadeChamada(informacoesPrimitiva.argumentos, argumentos);
                if (erros.length > 0) {
                    throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamadaResolvidaVariavel.simbolo, `Erros ao resolver argumentos de chamada a ${entidadeChamadaResolvidaVariavel.simbolo.lexema}: \n${erros.reduce((mensagem, erro) => (mensagem += `${erro}\n`), '')}`);
                }
                return entidadeChamadaResolvidaVariavel;
            }
            if (entidadeChamadaResolvidaVariavel.simbolo.lexema in this.tiposDefinidosEmCodigo) {
                return entidadeChamadaResolvidaVariavel;
            }
            const possivelReferencia = this.pilhaEscopos.obterReferenciaFuncao(entidadeChamadaResolvidaVariavel.simbolo.lexema);
            if (possivelReferencia !== null) {
                return new construtos_1.ReferenciaFuncao(entidadeChamada.hashArquivo, entidadeChamada.linha, entidadeChamadaResolvidaVariavel.simbolo, entidadeChamadaResolvidaVariavel.tipo, possivelReferencia.id);
            }
            return new construtos_1.ArgumentoReferenciaFuncao(entidadeChamada.hashArquivo, entidadeChamada.linha, entidadeChamadaResolvidaVariavel.simbolo);
        }
        if (entidadeChamada.constructor.name === 'AcessoMetodoOuPropriedade') {
            return this.resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamada);
        }
        return entidadeChamada;
    }
    finalizarChamada(entidadeChamada, tipoPrimitiva = undefined) {
        const argumentos = [];
        if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
            do {
                // `apply()` em JavaScript aceita até 255 parâmetros.
                if (argumentos.length >= 255) {
                    throw this.erro(this.simbolos[this.atual], 'Não pode haver mais de 255 argumentos.');
                }
                argumentos.push(this.expressao());
            } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        }
        this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após os argumentos.");
        // Toda chamada precisa saber de antemão qual o tipo resolvido.
        const entidadeChamadaResolvida = this.resolverEntidadeChamada(entidadeChamada, argumentos, tipoPrimitiva);
        const construtoChamada = new construtos_1.Chamada(this.hashArquivo, entidadeChamadaResolvida, argumentos);
        // A validação de tipos dos argumentos da entidade chamada existe em
        // avaliadores sintáticos derivados deste, como em `delegua-node`.
        // Pode ser que esta lógica seja trazida para cá no futuro.
        construtoChamada.tipo = 'qualquer';
        return construtoChamada;
    }
    unario() {
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.NEGACAO, delegua_2.default.SUBTRACAO, delegua_2.default.BIT_NOT, delegua_2.default.INCREMENTAR, delegua_2.default.DECREMENTAR)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.unario();
            return new construtos_1.Unario(this.hashArquivo, operador, direito, 'ANTES');
        }
        return this.chamar();
    }
    multiplicar() {
        let expressao = this.exponenciacao();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DIVISAO, delegua_2.default.DIVISAO_IGUAL, delegua_2.default.DIVISAO_INTEIRA, delegua_2.default.DIVISAO_INTEIRA_IGUAL, delegua_2.default.MODULO, delegua_2.default.MODULO_IGUAL, delegua_2.default.MULTIPLICACAO, delegua_2.default.MULTIPLICACAO_IGUAL)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.exponenciacao();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    /**
     * Se símbolo de operação é `+`, `-`, `+=` ou `-=`, monta objeto `Binario` para
     * ser avaliado pelo Interpretador.
     * @returns Um Construto, normalmente um `Binario`, ou `Unario` se houver alguma operação unária para ser avaliada.
     */
    adicaoOuSubtracao() {
        let expressao = this.multiplicar();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SUBTRACAO, delegua_2.default.ADICAO, delegua_2.default.MENOS_IGUAL)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.multiplicar();
            // const tipoInferido = inferirTipoParaBinario(expressao, operador, direito);
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MAIS_IGUAL)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.atribuir();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    bitShift() {
        let expressao = this.adicaoOuSubtracao();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MENOR_MENOR, delegua_2.default.MAIOR_MAIOR)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.adicaoOuSubtracao();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    bitE() {
        let expressao = this.bitShift();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.BIT_AND)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.bitShift();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    bitOu() {
        let expressao = this.bitE();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.BIT_OR, delegua_2.default.BIT_XOR)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.bitE();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    comparar() {
        let expressao = this.bitOu();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MAIOR, delegua_2.default.MAIOR_IGUAL, delegua_2.default.MENOR, delegua_2.default.MENOR_IGUAL)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.bitOu();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    comparacaoIgualdade() {
        let expressao = this.comparar();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DIFERENTE, delegua_2.default.IGUAL_IGUAL)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.comparar();
            expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    em() {
        let expressao = this.comparacaoIgualdade();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.EM)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.comparacaoIgualdade();
            expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    e() {
        let expressao = this.em();
        while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.E)) {
            const operador = this.simbolos[this.atual - 1];
            const direito = this.em();
            expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
        }
        return expressao;
    }
    /**
     * Método que resolve atribuições.
     * @returns Um construto do tipo `Atribuir`, `Conjunto` ou `AtribuicaoPorIndice`.
     */
    atribuir() {
        const expressao = this.ou();
        if (expressao instanceof construtos_1.Binario &&
            [
                delegua_2.default.MAIS_IGUAL,
                delegua_2.default.MENOS_IGUAL,
                delegua_2.default.MULTIPLICACAO_IGUAL,
                delegua_2.default.DIVISAO_IGUAL,
                delegua_2.default.DIVISAO_INTEIRA_IGUAL,
                delegua_2.default.MODULO_IGUAL,
            ].includes(expressao.operador.tipo)) {
            if (expressao.esquerda instanceof construtos_1.AcessoIndiceVariavel) {
                const entidade = expressao.esquerda;
                return new construtos_1.Atribuir(this.hashArquivo, entidade.entidadeChamada, expressao, entidade.indice, expressao.operador);
            }
            return new construtos_1.Atribuir(this.hashArquivo, expressao.esquerda, expressao, undefined, expressao.operador);
        }
        else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL)) {
            const igual = this.simbolos[this.atual - 1];
            const valor = this.expressao();
            switch (expressao.constructor.name) {
                case 'Variavel':
                    return new construtos_1.Atribuir(this.hashArquivo, expressao, valor);
                case 'AcessoMetodoOuPropriedade':
                    const expressaoAcessoMetodoOuPropriedade = expressao;
                    return new construtos_1.DefinirValor(this.hashArquivo, igual.linha, expressaoAcessoMetodoOuPropriedade.objeto, expressaoAcessoMetodoOuPropriedade.simbolo, valor);
                case 'AcessoIndiceVariavel':
                    const expressaoAcessoIndiceVariavel = expressao;
                    return new construtos_1.AtribuicaoPorIndice(this.hashArquivo, expressaoAcessoIndiceVariavel.linha, expressaoAcessoIndiceVariavel.entidadeChamada, expressaoAcessoIndiceVariavel.indice, valor);
            }
            throw this.erro(igual, 'Tarefa de atribuição inválida');
        }
        return expressao;
    }
    /**
     * Declaração para comando `leia`, para ler dados de entrada do usuário.
     * @returns Um objeto da classe `Leia`.
     */
    expressaoLeia() {
        const simboloLeia = this.avancarEDevolverAnterior();
        this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos argumentos em instrução `leia`.");
        const argumentos = [];
        if (this.simbolos[this.atual].tipo !== delegua_2.default.PARENTESE_DIREITO) {
            do {
                argumentos.push(this.expressao());
            } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        }
        this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após os argumentos em instrução `leia`.");
        return new construtos_1.Leia(simboloLeia, argumentos);
    }
    // TODO: Depreciar.
    expressao() {
        return this.atribuir();
    }
    blocoEscopo() {
        this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
        let declaracoes = [];
        while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
            !this.estaNoFinal()) {
            const retornoDeclaracao = this.resolverDeclaracaoForaDeBloco();
            if (Array.isArray(retornoDeclaracao)) {
                declaracoes = declaracoes.concat(retornoDeclaracao);
            }
            else {
                declaracoes.push(retornoDeclaracao);
            }
        }
        this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após o bloco.");
        this.pilhaEscopos.removerUltimo();
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return declaracoes;
    }
    declaracaoComentarioMultilinha() {
        let simboloComentario;
        const conteudos = [];
        do {
            simboloComentario = this.avancarEDevolverAnterior();
            conteudos.push(simboloComentario.literal);
        } while (this.verificarTipoSimboloAtual(delegua_2.default.LINHA_COMENTARIO));
        return new declaracoes_1.Comentario(simboloComentario.hashArquivo, simboloComentario.linha, conteudos, true);
    }
    declaracaoComentarioUmaLinha() {
        const simboloComentario = this.avancarEDevolverAnterior();
        return new declaracoes_1.Comentario(simboloComentario.hashArquivo, simboloComentario.linha, simboloComentario.literal, false);
    }
    declaracaoContinua() {
        if (this.blocos < 1) {
            throw this.erro(this.simbolos[this.atual - 1], "'continua' precisa estar em um laço de repetição.");
        }
        // Ponto-e-vírgula é opcional aqui.
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return new declaracoes_1.Continua(this.simbolos[this.atual - 1]);
    }
    declaracaoEnquanto() {
        try {
            this.blocos += 1;
            const condicao = this.expressao();
            // TODO: Talvez não seja uma ideia melhor chamar o método de `Bloco` aqui?
            const corpo = this.resolverDeclaracao();
            return new declaracoes_1.Enquanto(condicao, corpo);
        }
        finally {
            this.blocos -= 1;
        }
    }
    declaracaoEscolha() {
        try {
            this.blocos += 1;
            const condicao = this.expressao();
            this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do escopo do 'escolha'.");
            const caminhos = [];
            let caminhoPadrao = null;
            while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_DIREITA) &&
                !this.estaNoFinal()) {
                if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CASO)) {
                    const caminhoCondicoes = [this.expressao()];
                    this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após o 'caso'.");
                    while (this.verificarTipoSimboloAtual(delegua_2.default.CASO)) {
                        this.consumir(delegua_2.default.CASO, null);
                        caminhoCondicoes.push(this.expressao());
                        this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após declaração do 'caso'.");
                    }
                    let declaracoes = [];
                    do {
                        const retornoDeclaracao = this.resolverDeclaracao();
                        if (Array.isArray(retornoDeclaracao)) {
                            declaracoes = declaracoes.concat(retornoDeclaracao);
                        }
                        else {
                            declaracoes.push(retornoDeclaracao);
                        }
                    } while (!this.verificarTipoSimboloAtual(delegua_2.default.CASO) &&
                        !this.verificarTipoSimboloAtual(delegua_2.default.PADRAO) &&
                        !this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA));
                    caminhos.push({
                        condicoes: caminhoCondicoes,
                        declaracoes,
                    });
                }
                else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PADRAO)) {
                    if (caminhoPadrao !== null) {
                        const excecao = new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(this.simbolos[this.atual], "Você só pode ter um 'padrao' em cada declaração de 'escolha'.");
                        this.erros.push(excecao);
                        throw excecao;
                    }
                    this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após declaração do 'padrao'.");
                    const declaracoes = [];
                    do {
                        declaracoes.push(this.resolverDeclaracao());
                    } while (!this.verificarTipoSimboloAtual(delegua_2.default.CASO) &&
                        !this.verificarTipoSimboloAtual(delegua_2.default.PADRAO) &&
                        !this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA));
                    caminhoPadrao = {
                        declaracoes,
                    };
                }
            }
            return new declaracoes_1.Escolha(condicao, caminhos, caminhoPadrao);
        }
        finally {
            this.blocos -= 1;
        }
    }
    declaracaoEscreva() {
        const simboloAtual = this.simbolos[this.atual];
        this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos valores em escreva.");
        const argumentos = [];
        if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
            do {
                argumentos.push(this.expressao());
            } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        }
        this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após os valores em escreva.");
        // Ponto-e-vírgula é opcional aqui.
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return new declaracoes_1.Escreva(Number(simboloAtual.linha), simboloAtual.hashArquivo, argumentos);
    }
    declaracaoExpressao() {
        // Se há decoradores a serem adicionados aqui, obtemo-los agora,
        // para evitar que outros passos recursivos peguem-los antes.
        const decoradores = Array.from(this.pilhaDecoradores);
        this.pilhaDecoradores = [];
        const expressao = this.expressao();
        // Ponto-e-vírgula é opcional aqui.
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return new declaracoes_1.Expressao(expressao, decoradores);
    }
    declaracaoFalhar() {
        const simboloFalha = this.simbolos[this.atual - 1];
        return new declaracoes_1.Falhar(simboloFalha, this.declaracaoExpressao().expressao);
    }
    declaracaoFazer() {
        const simboloFazer = this.simbolos[this.atual - 1];
        try {
            this.blocos += 1;
            const caminhoFazer = this.resolverDeclaracao();
            this.consumir(delegua_2.default.ENQUANTO, "Esperado declaração do 'enquanto' após o escopo do 'fazer'.");
            const condicaoEnquanto = this.expressao();
            return new declaracoes_1.Fazer(simboloFazer.hashArquivo, Number(simboloFazer.linha), caminhoFazer, condicaoEnquanto);
        }
        finally {
            this.blocos -= 1;
        }
    }
    /**
     * O símbolo é emitido aqui para fins de formatação, mas este método é
     * sobrescrito em `delegua-node`.
     * @returns {Importar} Uma declaração `Importar`.
     */
    declaracaoImportar() {
        this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após declaração.");
        const caminho = this.expressao();
        this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após declaração.");
        return new declaracoes_1.Importar(caminho);
    }
    declaracaoPara() {
        try {
            const simboloPara = this.simbolos[this.atual - 1];
            this.blocos += 1;
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CADA)) {
                return this.declaracaoParaCada(simboloPara);
            }
            return this.declaracaoParaTradicional(simboloPara);
        }
        finally {
            this.blocos -= 1;
        }
    }
    declaracaoParaCadaDicionario(simboloPara) {
        this.avancarEDevolverAnterior(); // chave esquerda
        const nomeVariavelChave = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado identificador de variável para chave de iteração, em instrução 'para cada'.");
        this.consumir(delegua_2.default.VIRGULA, "Esperado vírgula após nome de variável para chave de iteração, em instrução 'para cada'.");
        const nomeVariavelValor = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado identificador de variável para valor de iteração, em instrução 'para cada'.");
        this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado chave direita após nome de variável para valor de iteração, em instrução 'para cada'.");
        if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.DE, delegua_2.default.EM)) {
            throw this.erro(this.simbolos[this.atual], "Esperado palavras reservadas 'em' ou 'de' após variável de iteração em instrução 'para cada'.");
        }
        const dicionario = this.expressao();
        if (!dicionario.hasOwnProperty('tipo')) {
            throw this.erro(simboloPara, `Variável ou constante em 'para cada' não parece ser um dicionário.`);
        }
        const tipoDicionario = dicionario.tipo;
        if (tipoDicionario !== 'dicionário') {
            throw this.erro(simboloPara, `Variável ou constante em 'para cada' não é um dicionário. Tipo resolvido: ${tipoDicionario}.`);
        }
        this.pilhaEscopos.definirInformacoesVariavel(nomeVariavelChave.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomeVariavelChave.lexema, 'qualquer'));
        this.pilhaEscopos.definirInformacoesVariavel(nomeVariavelValor.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomeVariavelValor.lexema, 'qualquer'));
        // TODO: Talvez não seja uma ideia melhor chamar o método de `Bloco` aqui?
        const corpo = this.resolverDeclaracao();
        return new declaracoes_1.ParaCada(this.hashArquivo, Number(simboloPara.linha), new tuplas_1.Dupla(new construtos_1.Literal(this.hashArquivo, Number(simboloPara.linha), nomeVariavelChave.lexema), new construtos_1.Literal(this.hashArquivo, Number(simboloPara.linha), nomeVariavelValor.lexema)), dicionario, corpo);
    }
    declaracaoParaCadaVetor(simboloPara) {
        const nomeVariavelIteracao = this.avancarEDevolverAnterior();
        const variavelIteracao = new construtos_1.Variavel(this.hashArquivo, nomeVariavelIteracao);
        if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.DE, delegua_2.default.EM)) {
            throw this.erro(this.simbolos[this.atual], "Esperado palavras reservadas 'em' ou 'de' após variável de iteração em instrução 'para cada'.");
        }
        const vetor = this.expressao();
        if (!vetor.hasOwnProperty('tipo')) {
            throw this.erro(simboloPara, `Variável ou constante em 'para cada' não parece possuir um tipo iterável.`);
        }
        const tipoVetor = vetor.tipo;
        if (!tipoVetor.endsWith('[]') && !['dicionário', 'qualquer', 'vetor'].includes(tipoVetor)) {
            throw this.erro(simboloPara, `Variável ou constante em 'para cada' não é iterável. Tipo resolvido: ${tipoVetor}.`);
        }
        let tipoVariavelIteracao = 'qualquer';
        if (tipoVetor.endsWith('[]')) {
            tipoVariavelIteracao = tipoVetor.slice(0, -2);
        }
        this.pilhaEscopos.definirInformacoesVariavel(nomeVariavelIteracao.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomeVariavelIteracao.lexema, tipoVariavelIteracao));
        // TODO: Talvez não seja uma ideia melhor chamar o método de `Bloco` aqui?
        const corpo = this.resolverDeclaracao();
        return new declaracoes_1.ParaCada(this.hashArquivo, Number(simboloPara.linha), variavelIteracao, vetor, corpo);
    }
    declaracaoParaCada(simboloPara) {
        if (this.verificarTipoSimboloAtual(delegua_2.default.IDENTIFICADOR)) {
            return this.declaracaoParaCadaVetor(simboloPara);
        }
        if (this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_ESQUERDA)) {
            return this.declaracaoParaCadaDicionario(simboloPara);
        }
        throw this.erro(simboloPara, 'Identificador de iteração deve ser ou um par chave-valor, ou um nome de variável.');
    }
    declaracaoParaTradicional(simboloPara) {
        const comParenteses = this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO);
        let inicializador;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA)) {
            inicializador = null;
        }
        else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VARIAVEL)) {
            inicializador = this.declaracaoDeVariaveis();
        }
        else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CONSTANTE)) {
            inicializador = this.declaracaoDeConstantes();
        }
        else {
            inicializador = this.declaracaoExpressao();
        }
        let condicao = null;
        if (!this.verificarTipoSimboloAtual(delegua_2.default.PONTO_E_VIRGULA)) {
            condicao = this.expressao();
        }
        // Ponto-e-vírgula é opcional aqui.
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        let incrementar = null;
        if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
            incrementar = this.expressao();
            this.verificarSeSimboloAtualEIgualA(delegua_2.default.INCREMENTAR, delegua_2.default.DECREMENTAR);
        }
        if (comParenteses) {
            this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após cláusulas de inicialização, condição e incremento.");
        }
        // TODO: Talvez não seja uma ideia melhor chamar o método de `Bloco` aqui?
        const corpo = this.resolverDeclaracao();
        return new declaracoes_1.Para(this.hashArquivo, Number(simboloPara.linha), inicializador, condicao, incrementar, corpo);
    }
    declaracaoRetorna() {
        const simboloChave = this.simbolos[this.atual - 1];
        let valor = null;
        if ([
            delegua_2.default.CHAVE_ESQUERDA,
            delegua_2.default.COLCHETE_ESQUERDO,
            delegua_2.default.FALSO,
            delegua_2.default.FUNCAO,
            delegua_2.default.FUNÇÃO,
            delegua_2.default.IDENTIFICADOR,
            delegua_2.default.ISTO,
            delegua_2.default.NEGACAO,
            delegua_2.default.NUMERO,
            delegua_2.default.NULO,
            delegua_2.default.PARENTESE_ESQUERDO,
            delegua_2.default.SUPER,
            delegua_2.default.TEXTO,
            delegua_2.default.VERDADEIRO,
        ].includes(this.simbolos[this.atual].tipo)) {
            valor = this.expressao();
        }
        // Ponto-e-vírgula é opcional aqui.
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return new declaracoes_1.Retorna(simboloChave, valor);
    }
    declaracaoSe() {
        const condicao = this.expressao();
        const caminhoEntao = this.resolverDeclaracao();
        let caminhoSenao = null;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SENAO, delegua_2.default.SENÃO)) {
            caminhoSenao = this.resolverDeclaracao();
        }
        return new declaracoes_1.Se(condicao, caminhoEntao, [], caminhoSenao);
    }
    declaracaoSustar() {
        if (this.blocos < 1) {
            throw this.erro(this.simbolos[this.atual - 1], "'sustar' ou 'pausa' deve estar dentro de um laço de repetição.");
        }
        // Ponto-e-vírgula é opcional aqui.
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return new declaracoes_1.Sustar(this.simbolos[this.atual - 1]);
    }
    declaracaoTente() {
        const simboloTente = this.simbolos[this.atual - 1];
        this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'tente'.");
        const blocoTente = this.blocoEscopo();
        let blocoPegue = null;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PEGUE)) {
            if (this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_ESQUERDO)) {
                // Caso 1: com parâmetro de erro.
                // `pegue` recebe um `FuncaoConstruto`.
                blocoPegue = this.corpoDaFuncao('bloco `pegue`');
            }
            else {
                // Caso 2: sem parâmetro de erro.
                // `pegue` recebe um bloco.
                this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'pegue'.");
                blocoPegue = this.blocoEscopo();
            }
        }
        let blocoSenao = null;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SENAO, delegua_2.default.SENÃO)) {
            this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'senão'.");
            blocoSenao = this.blocoEscopo();
        }
        let blocoFinalmente = null;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.FINALMENTE)) {
            this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'finalmente'.");
            blocoFinalmente = this.blocoEscopo();
        }
        return new declaracoes_1.Tente(simboloTente.hashArquivo, Number(simboloTente.linha), blocoTente, blocoPegue, blocoSenao, blocoFinalmente);
    }
    resolverDecorador() {
        while (this.verificarTipoSimboloAtual(delegua_2.default.ARROBA)) {
            let nomeDecorador = '';
            let linha;
            let parametros = [];
            let parenteseEsquerdo = false;
            linha = this.simbolos[this.atual].linha;
            let simbolosLinhaAtual = this.simbolos.filter((l) => l.linha === linha);
            for (let simbolo of simbolosLinhaAtual) {
                parenteseEsquerdo = this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO);
                if (parenteseEsquerdo) {
                    if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
                        parametros = this.logicaComumParametros();
                    }
                    this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros.");
                    break;
                }
                this.avancarEDevolverAnterior();
                nomeDecorador += simbolo.lexema || '.';
            }
            const atributos = {};
            for (const parametro of parametros) {
                if (parametro.nome.lexema in atributos) {
                    throw this.erro(parametro.nome, `Atributo de decorador declarado duas ou mais vezes: ${parametro.nome.lexema}`);
                }
                atributos[parametro.nome.lexema] = parametro.valorPadrao;
            }
            this.pilhaDecoradores.push(new construtos_1.Decorador(this.hashArquivo, linha, nomeDecorador, atributos));
        }
    }
    /**
     * Todas as resoluções triviais da linguagem, ou seja, todas as
     * resoluções que podem ocorrer dentro ou fora de um bloco.
     * @returns Normalmente uma `Declaracao`, mas há casos em que
     * outros objetos podem ser retornados.
     * @see resolverDeclaracaoForaDeBloco para as declarações que não podem
     * ocorrer em blocos de escopo elementares.
     */
    resolverDeclaracao() {
        switch (this.simbolos[this.atual].tipo) {
            case delegua_2.default.CHAVE_ESQUERDA:
                const simboloInicioBloco = this.avancarEDevolverAnterior();
                return new declaracoes_1.Bloco(simboloInicioBloco.hashArquivo, Number(simboloInicioBloco.linha), this.blocoEscopo());
            case delegua_2.default.COMENTARIO:
                return this.declaracaoComentarioUmaLinha();
            case delegua_2.default.CONSTANTE:
                this.avancarEDevolverAnterior();
                return this.declaracaoDeConstantes();
            case delegua_2.default.CONTINUA:
                this.avancarEDevolverAnterior();
                return this.declaracaoContinua();
            case delegua_2.default.ENQUANTO:
                this.avancarEDevolverAnterior();
                return this.declaracaoEnquanto();
            case delegua_2.default.ESCOLHA:
                this.avancarEDevolverAnterior();
                return this.declaracaoEscolha();
            case delegua_2.default.ESCREVA:
                this.avancarEDevolverAnterior();
                return this.declaracaoEscreva();
            case delegua_2.default.FALHAR:
                this.avancarEDevolverAnterior();
                return this.declaracaoFalhar();
            case delegua_2.default.FAZER:
                this.avancarEDevolverAnterior();
                return this.declaracaoFazer();
            case delegua_2.default.LINHA_COMENTARIO:
                return this.declaracaoComentarioMultilinha();
            case delegua_2.default.PARA:
                this.avancarEDevolverAnterior();
                return this.declaracaoPara();
            case delegua_2.default.PAUSA:
            case delegua_2.default.SUSTAR:
                this.avancarEDevolverAnterior();
                return this.declaracaoSustar();
            case delegua_2.default.SE:
                this.avancarEDevolverAnterior();
                return this.declaracaoSe();
            case delegua_2.default.RETORNA:
                this.avancarEDevolverAnterior();
                return this.declaracaoRetorna();
            case delegua_2.default.TENDO:
                this.avancarEDevolverAnterior();
                return this.declaracaoTendoComo();
            case delegua_2.default.TENTE:
                this.avancarEDevolverAnterior();
                return this.declaracaoTente();
            case delegua_2.default.VARIAVEL:
                this.avancarEDevolverAnterior();
                return this.declaracaoDeVariaveis();
        }
        const simboloAtual = this.simbolos[this.atual];
        if (simboloAtual.tipo === delegua_2.default.IDENTIFICADOR) {
            // Pela gramática, a seguinte situação não pode ocorrer:
            // 1. O símbolo anterior ser um identificador; e
            // 2. O símbolo anterior estar na mesma linha do identificador atual.
            const simboloAnterior = this.simbolos[this.atual - 1];
            if (!!simboloAnterior &&
                simboloAnterior.tipo === delegua_2.default.IDENTIFICADOR &&
                simboloAnterior.linha === simboloAtual.linha) {
                throw this.erro(this.simbolos[this.atual], 'Não é permitido ter dois identificadores seguidos na mesma linha.');
            }
        }
        return this.declaracaoExpressao();
    }
    declaracaoTendoComo() {
        const simboloTendo = this.simbolos[this.atual - 1];
        const expressaoInicializacao = this.expressao();
        this.consumir(delegua_2.default.COMO, "Esperado palavra reservada 'como' após expressão de inicialização de variável, em declaração 'tendo'.");
        const simboloNomeVariavel = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome do identificador em declaração 'tendo'.");
        this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado chave esquerda para abertura de bloco em declaração 'tendo'.");
        let tipoInicializacao = 'qualquer';
        switch (expressaoInicializacao.constructor.name) {
            case 'Chamada':
                const construtoChamada = expressaoInicializacao;
                switch (construtoChamada.entidadeChamada.constructor.name) {
                    case 'Variavel':
                        const entidadeChamadaVariavel = construtoChamada.entidadeChamada;
                        tipoInicializacao = entidadeChamadaVariavel.tipo;
                        break;
                    // TODO: Demais casos
                    default:
                        break;
                }
                break;
            // TODO: Demais casos
            default:
                break;
        }
        this.pilhaEscopos.definirInformacoesVariavel(simboloNomeVariavel.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simboloNomeVariavel.lexema, tipoInicializacao));
        const blocoCorpo = this.blocoEscopo();
        return new declaracoes_1.TendoComo(simboloTendo.linha, simboloTendo.hashArquivo, simboloNomeVariavel, expressaoInicializacao, new declaracoes_1.Bloco(simboloTendo.linha, simboloTendo.hashArquivo, blocoCorpo));
    }
    declaracaoDesestruturacaoVariavel() {
        const identificadores = [];
        do {
            identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da variável.'));
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        this.consumir(delegua_2.default.CHAVE_DIREITA, 'Esperado chave direita para concluir relação de variáveis a serem desestruturadas.');
        this.consumir(delegua_2.default.IGUAL, 'Esperado igual após relação de propriedades da desestruturação.');
        const inicializador = this.expressao();
        const retornos = [];
        for (let identificador of identificadores) {
            this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, this.logicaComumInferenciaTiposVariaveisEConstantes(inicializador, 'qualquer')));
            const declaracaoVar = new declaracoes_1.Var(identificador, new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, inicializador, identificador));
            declaracaoVar.decoradores = Array.from(this.pilhaDecoradores);
            retornos.push(declaracaoVar);
        }
        this.pilhaDecoradores = [];
        return retornos;
    }
    logicaComumInferenciaTiposVariaveisEConstantes(inicializador, tipo) {
        if (tipo !== 'qualquer') {
            return tipo;
        }
        switch (inicializador.constructor.name) {
            case 'AcessoIndiceVariavel':
                const entidadeChamadaAcessoIndiceVariavel = inicializador
                    .entidadeChamada;
                // Este condicional ocorre com chamadas aninhadas. Por exemplo, `vetor[1][2]`.
                if (entidadeChamadaAcessoIndiceVariavel.constructor.name === 'AcessoIndiceVariavel') {
                    return this.logicaComumInferenciaTiposVariaveisEConstantes(entidadeChamadaAcessoIndiceVariavel, tipo);
                }
                if (entidadeChamadaAcessoIndiceVariavel.tipo.endsWith('[]')) {
                    return entidadeChamadaAcessoIndiceVariavel.tipo.slice(0, -2);
                }
                // Normalmente, `entidadeChamadaAcessoIndiceVariavel.tipo` aqui será 'vetor'.
                return 'qualquer';
            case 'Chamada':
                const entidadeChamadaChamada = inicializador.entidadeChamada;
                switch (entidadeChamadaChamada.constructor.name) {
                    case 'AcessoMetodo':
                        const entidadeChamadaAcessoMetodo = entidadeChamadaChamada;
                        return entidadeChamadaAcessoMetodo.tipoRetornoMetodo;
                    case 'AcessoMetodoOuPropriedade':
                        // Este caso ocorre quando a variável/constante é do tipo 'qualquer',
                        // e a chamada normalmente é feita para uma primitiva.
                        // A inferência, portanto, ocorre pelo uso da primitiva.
                        const entidadeChamadaAcessoMetodoOuPropriedade = entidadeChamadaChamada;
                        for (const primitiva in this.primitivasConhecidas) {
                            if (this.primitivasConhecidas[primitiva].hasOwnProperty(entidadeChamadaAcessoMetodoOuPropriedade.simbolo.lexema)) {
                                return this.primitivasConhecidas[primitiva][entidadeChamadaAcessoMetodoOuPropriedade.simbolo.lexema].tipo;
                            }
                        }
                        throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamadaAcessoMetodoOuPropriedade.simbolo, `Primitiva '${entidadeChamadaAcessoMetodoOuPropriedade.simbolo.lexema}' não existe.`);
                    case 'AcessoPropriedade':
                        const entidadeChamadaAcessoPropriedade = entidadeChamadaChamada;
                        return entidadeChamadaAcessoPropriedade.tipoRetornoPropriedade;
                    case 'ArgumentoReferenciaFuncao':
                        // TODO: Voltar aqui se necessário.
                        return 'qualquer';
                    case 'ReferenciaFuncao':
                        const entidadeChamadaReferenciaFuncao = entidadeChamadaChamada;
                        return entidadeChamadaReferenciaFuncao.tipo;
                    case 'Variavel':
                        const entidadeChamadaVariavel = entidadeChamadaChamada;
                        return entidadeChamadaVariavel.tipo;
                }
                break;
            case 'FuncaoConstruto':
                const funcaoConstruto = inicializador;
                return `função<${funcaoConstruto.tipo}>`;
            case 'Leia':
                return 'texto';
            case 'Dupla':
            case 'Trio':
            case 'Quarteto':
            case 'Quinteto':
            case 'Sexteto':
            case 'Septeto':
            case 'Octeto':
            case 'Noneto':
            case 'Deceto':
                return delegua_1.default.TUPLA;
            case 'ImportarBiblioteca':
            case 'ModuloDeclaracoes':
                return 'módulo';
            default:
                return inicializador.tipo;
        }
    }
    /**
     * Caso símbolo atual seja `var`, devolve uma declaração de variável.
     * @returns Um Construto do tipo Var.
     */
    declaracaoDeVariaveis() {
        const simboloVariavel = this.simboloAnterior();
        const identificadores = [];
        const retorno = [];
        let tipo = 'qualquer';
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_ESQUERDA)) {
            return this.declaracaoDesestruturacaoVariavel();
        }
        do {
            identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da variável.'));
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
            tipo = this.verificarDefinicaoTipoAtual();
            this.avancarEDevolverAnterior();
        }
        if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL)) {
            // Inicialização de variáveis sem valor.
            for (let identificador of identificadores.values()) {
                this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, tipo));
                retorno.push(new declaracoes_1.Var(identificador, null, tipo, Array.from(this.pilhaDecoradores)));
            }
            this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
            this.pilhaDecoradores = [];
            return retorno;
        }
        const inicializadores = [];
        do {
            inicializadores.push(this.expressao());
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        if (identificadores.length !== inicializadores.length) {
            throw this.erro(simboloVariavel, 'Quantidade de identificadores à esquerda do igual é diferente da quantidade de valores à direita.');
        }
        for (let [indice, identificador] of identificadores.entries()) {
            tipo = this.logicaComumInferenciaTiposVariaveisEConstantes(inicializadores[indice], tipo);
            this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, tipo));
            retorno.push(new declaracoes_1.Var(identificador, inicializadores[indice], tipo, Array.from(this.pilhaDecoradores)));
        }
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        this.pilhaDecoradores = [];
        return retorno;
    }
    declaracaoDesestruturacaoConstante() {
        const identificadores = [];
        do {
            identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da variável.'));
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        this.consumir(delegua_2.default.CHAVE_DIREITA, 'Esperado chave direita para concluir relação de variáveis a serem desestruturadas.');
        this.consumir(delegua_2.default.IGUAL, 'Esperado igual após relação de propriedades da desestruturação.');
        const inicializador = this.expressao();
        const retornos = [];
        for (let identificador of identificadores) {
            // TODO: Melhorar dicionário para intuir o tipo de cada propriedade.
            this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, 'qualquer'));
            const declaracaoConst = new declaracoes_1.Const(identificador, new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, inicializador, identificador));
            declaracaoConst.decoradores = Array.from(this.pilhaDecoradores);
            retornos.push(declaracaoConst);
        }
        return retornos;
    }
    /**
     * Caso símbolo atual seja `const, constante ou fixo`, devolve uma declaração de const.
     * @returns Um Construto do tipo Const.
     */
    declaracaoDeConstantes() {
        const simboloConstante = this.simboloAnterior();
        const identificadores = [];
        let tipo = 'qualquer';
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_ESQUERDA)) {
            return this.declaracaoDesestruturacaoConstante();
        }
        do {
            identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da constante.'));
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
            tipo = this.verificarDefinicaoTipoAtual();
            this.avancarEDevolverAnterior();
        }
        this.consumir(delegua_2.default.IGUAL, "Esperado '=' após identificador em instrução 'constante'.");
        const inicializadores = [];
        do {
            inicializadores.push(this.expressao());
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        if (identificadores.length !== inicializadores.length) {
            throw this.erro(simboloConstante, 'Quantidade de identificadores à esquerda do igual é diferente da quantidade de valores à direita.');
        }
        let retorno = [];
        for (let [indice, identificador] of identificadores.entries()) {
            // Se tipo ainda não foi definido, infere.
            tipo = this.logicaComumInferenciaTiposVariaveisEConstantes(inicializadores[indice], tipo);
            this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, tipo));
            retorno.push(new declaracoes_1.Const(identificador, inicializadores[indice], tipo, Array.from(this.pilhaDecoradores)));
        }
        this.pilhaDecoradores = [];
        this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
        return retorno;
    }
    funcao(tipo) {
        let simbolo;
        switch (this.simbolos[this.atual].tipo) {
            case delegua_2.default.CONSTRUTOR:
                simbolo = this.avancarEDevolverAnterior();
                break;
            default:
                simbolo = this.consumir(delegua_2.default.IDENTIFICADOR, `Esperado nome de ${tipo}.`);
                break;
        }
        const decoradores = Array.from(this.pilhaDecoradores);
        this.pilhaDecoradores = [];
        // Se houver chamadas recursivas à função, precisamos definir um tipo
        // para ela. Vai ser atualizado após avaliação do corpo da função.
        this.pilhaEscopos.definirInformacoesVariavel(simbolo.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simbolo.lexema, 'qualquer'));
        const corpoDaFuncao = this.corpoDaFuncao(tipo);
        const tipoDaFuncao = `função<${corpoDaFuncao.tipo}>`;
        this.pilhaEscopos.definirInformacoesVariavel(simbolo.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simbolo.lexema, tipoDaFuncao));
        const funcaoDeclaracao = new declaracoes_1.FuncaoDeclaracao(simbolo, corpoDaFuncao, tipoDaFuncao, decoradores);
        this.pilhaEscopos.registrarReferenciaFuncao(simbolo.lexema, funcaoDeclaracao);
        return funcaoDeclaracao;
    }
    logicaComumParametros() {
        const parametros = [];
        do {
            const parametro = {};
            if (this.simbolos[this.atual].tipo === delegua_2.default.MULTIPLICACAO) {
                this.consumir(delegua_2.default.MULTIPLICACAO, null);
                parametro.abrangencia = 'multiplo';
            }
            else {
                parametro.abrangencia = 'padrao';
            }
            parametro.nome = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome do parâmetro.');
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL)) {
                const valorPadrao = this.primario();
                parametro.valorPadrao = valorPadrao;
            }
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
                let tipoDadoParametro = this.verificarDefinicaoTipoAtual();
                parametro.tipoDado = tipoDadoParametro;
                this.avancarEDevolverAnterior();
            }
            this.pilhaEscopos.definirInformacoesVariavel(parametro.nome.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(parametro.nome.lexema, parametro.tipoDado || 'qualquer'));
            parametros.push(parametro);
            if (parametro.abrangencia === 'multiplo')
                break;
        } while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
        return parametros;
    }
    *buscarRetornosEmBloco(construtoBloco) {
        for (const declaracao of construtoBloco.declaracoes) {
            if (declaracao.constructor.name === 'Retorna') {
                yield declaracao;
            }
        }
    }
    *buscarRetornosEmSe(construtoSe) {
        const blocoEntao = construtoSe.caminhoEntao;
        for (const declaracao of this.buscarRetornosEmBloco(blocoEntao)) {
            if (declaracao.constructor.name === 'Retorna') {
                yield declaracao;
            }
        }
        if (!construtoSe.caminhoSenao)
            return;
        switch (construtoSe.caminhoSenao.constructor.name) {
            case 'Bloco':
                const blocoSenao = construtoSe.caminhoSenao;
                for (const declaracao of blocoSenao.declaracoes) {
                    if (declaracao.constructor.name === 'Retorna') {
                        yield declaracao;
                    }
                }
                break;
            case 'Se':
                const senaoSe = construtoSe.caminhoSenao;
                for (const declaracao of this.buscarRetornosEmSe(senaoSe)) {
                    if (declaracao.constructor.name === 'Retorna') {
                        yield declaracao;
                    }
                }
                break;
        }
    }
    buscarRetornos(declaracao) {
        let retornasEncontrados = [];
        switch (declaracao.constructor.name) {
            case 'Retorna':
                retornasEncontrados.push(declaracao);
                break;
            case 'Se':
                for (const retorna of this.buscarRetornosEmSe(declaracao)) {
                    retornasEncontrados.push(retorna);
                }
                break;
            default:
                break;
        }
        return retornasEncontrados;
    }
    corpoDaFuncao(tipo) {
        // O parêntese esquerdo é considerado o símbolo inicial para
        // fins de pragma.
        const parenteseEsquerdo = this.consumir(delegua_2.default.PARENTESE_ESQUERDO, `Esperado '(' após o nome ${tipo}.`);
        let parametros = [];
        if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
            parametros = this.logicaComumParametros();
        }
        this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros.");
        let tipoRetorno = 'qualquer';
        let definicaoExplicitaDeTipo = false;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
            tipoRetorno = this.verificarDefinicaoTipoAtual();
            this.avancarEDevolverAnterior();
            definicaoExplicitaDeTipo = true;
        }
        this.consumir(delegua_2.default.CHAVE_ESQUERDA, `Esperado '{' antes do escopo do ${tipo}.`);
        const corpo = this.blocoEscopo();
        let expressoesRetorna = [];
        for (const declaracao of corpo) {
            expressoesRetorna = expressoesRetorna.concat(this.buscarRetornos(declaracao));
        }
        if (tipoRetorno === 'vazio' && expressoesRetorna.length > 0) {
            const retornosNaoVazios = expressoesRetorna.filter((e) => e.tipo !== 'vazio');
            if (retornosNaoVazios.length > 0) {
                throw this.erro(retornosNaoVazios[0].simboloChave, `Função declara explicitamente 'vazio', mas usa expressão 'retorna' com tipo de retorno diferente de vazio.`);
            }
        }
        const tiposRetornos = new Set(expressoesRetorna.map((e) => e.tipo));
        let retornaChamadoExplicitamente = tiposRetornos.size > 0;
        if (tiposRetornos.size > 1 && tipoRetorno !== 'qualquer') {
            let tiposEncontrados = Array.from(tiposRetornos).reduce((acumulador, valor) => (acumulador += valor + ', '), '');
            tiposEncontrados = tiposEncontrados.slice(0, -2);
            throw this.erro(parenteseEsquerdo, `Função retorna valores com mais de um tipo. Tipo esperado: ${tipoRetorno}. Tipos encontrados: ${tiposEncontrados}.`);
        }
        tiposRetornos.delete('qualquer');
        if (tipoRetorno === 'qualquer') {
            if (tiposRetornos.size > 0) {
                // Se o tipo de retorno é 'qualquer', seja implícito ou explícito,
                // este avaliador sintático pode restringir o tipo baseado nos construtos
                // de retornos encontrados nos blocos internos da função.
                const tipoRetornoDeduzido = tiposRetornos.values().next().value;
                tipoRetorno = tipoRetornoDeduzido;
            }
            else if (!retornaChamadoExplicitamente && !definicaoExplicitaDeTipo) {
                // Ou, se esses retornos sequer existem, e o tipo explícito não é
                // 'qualquer', o tipo inferido é 'vazio'.
                tipoRetorno = 'vazio';
            }
        }
        return new construtos_1.FuncaoConstruto(this.hashArquivo, Number(parenteseEsquerdo.linha), parametros, corpo, tipoRetorno, definicaoExplicitaDeTipo);
    }
    declaracaoDeClasse() {
        const simbolo = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da classe.');
        const pilhaDecoradoresClasse = Array.from(this.pilhaDecoradores);
        let superClasse = null;
        if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.HERDA)) {
            const simboloSuperclasse = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da Superclasse.');
            // TODO: Validar classes existentes?
            this.superclasseAtual = simboloSuperclasse.lexema;
            // TODO: Colocar tipo aqui?
            superClasse = new construtos_1.Variavel(this.hashArquivo, this.simbolos[this.atual - 1], simboloSuperclasse.lexema);
        }
        this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do escopo da classe.");
        this.pilhaDecoradores = [];
        const metodos = [];
        const propriedades = [];
        while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
            !this.estaNoFinal()) {
            // Se o símbolo atual é arroba, é um decorador.
            // Caso contrário, verificamos o próximo símbolo.
            if (this.simbolos[this.atual].tipo === delegua_2.default.ARROBA) {
                this.resolverDecorador();
                continue;
            }
            // Se o próximo símbolo ao atual for um parênteses, é um método.
            // Caso contrário, é uma propriedade.
            const proximoSimbolo = this.simbolos[this.atual + 1];
            switch (proximoSimbolo.tipo) {
                case delegua_2.default.PARENTESE_ESQUERDO:
                    metodos.push(this.funcao('método'));
                    break;
                case delegua_2.default.DOIS_PONTOS:
                    const nomePropriedade = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado identificador para nome de propriedade.');
                    this.consumir(delegua_2.default.DOIS_PONTOS, 'Esperado dois-pontos após nome de propriedade.');
                    const tipoPropriedade = this.avancarEDevolverAnterior();
                    this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
                    propriedades.push(new declaracoes_1.PropriedadeClasse(nomePropriedade, tipoPropriedade.lexema, Array.from(this.pilhaDecoradores)));
                    this.pilhaDecoradores = [];
                    break;
                default:
                    throw this.erro(this.simbolos[this.atual], 'Esperado definição de método ou propriedade.');
            }
        }
        this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após o escopo da classe.");
        const definicaoClasse = new declaracoes_1.Classe(simbolo, superClasse, metodos, propriedades, pilhaDecoradoresClasse);
        this.tiposDefinidosEmCodigo[definicaoClasse.simbolo.lexema] = definicaoClasse;
        this.superclasseAtual = undefined;
        return definicaoClasse;
    }
    /**
     * Declarações fora de bloco precisam ser verificadas primeiro por
     * uma série de motivos, como, por exemplo:
     *
     * - Não é possível declarar uma classe/função dentro de um bloco `enquanto`,
     *   `fazer ... enquanto`, `para`, `escolha`, etc;
     * - Qualquer declaração pode ter um decorador.
     * @returns Uma função ou classe se o símbolo atual resolver aqui.
     *          O retorno de `resolverDeclaracao()` em caso contrário.
     * @see resolverDeclaracao
     * @see resolverDecorador
     */
    resolverDeclaracaoForaDeBloco() {
        try {
            while (this.verificarTipoSimboloAtual(delegua_2.default.ARROBA)) {
                this.resolverDecorador();
            }
            if ((this.verificarTipoSimboloAtual(delegua_2.default.FUNCAO) ||
                this.verificarTipoSimboloAtual(delegua_2.default.FUNÇÃO)) &&
                this.verificarTipoProximoSimbolo(delegua_2.default.IDENTIFICADOR)) {
                this.avancarEDevolverAnterior();
                return this.funcao('funcao');
            }
            if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CLASSE)) {
                return this.declaracaoDeClasse();
            }
            return this.resolverDeclaracao();
        }
        catch (erro) {
            this.sincronizar();
            this.erros.push(erro);
            return null;
        }
    }
    /**
     * Usado quando há erros na avaliação sintática.
     * Garante que o código não entre em loop infinito.
     * @returns Sempre retorna `void`.
     */
    sincronizar() {
        this.avancarEDevolverAnterior();
        while (!this.estaNoFinal()) {
            const tipoSimboloAtual = this.simbolos[this.atual - 1].tipo;
            switch (tipoSimboloAtual) {
                case delegua_2.default.CLASSE:
                case delegua_2.default.FUNCAO:
                case delegua_2.default.FUNÇÃO:
                case delegua_2.default.VARIAVEL:
                case delegua_2.default.PARA:
                case delegua_2.default.SE:
                case delegua_2.default.ENQUANTO:
                case delegua_2.default.ESCREVA:
                case delegua_2.default.RETORNA:
                    return;
            }
            this.avancarEDevolverAnterior();
        }
    }
    /**
     * Inicializa o primeiro nível da pilha de escopos, normalmente com ítens da biblioteca global.
     * É separada da inicialização do avaliador sintático, pois é necessário manipular essa
     * inicialização de outra forma em `delegua-node`.
     */
    inicializarPilhaEscopos() {
        this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
        this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
        // Funções nativas de Delégua
        this.pilhaEscopos.definirInformacoesVariavel('aleatorio', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('aleatorio', 'número'));
        this.pilhaEscopos.definirInformacoesVariavel('aleatorioEntre', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('aleatorioEntre', 'número', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('minimo', 'número'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('maximo', 'número'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('algum', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('algum', 'lógico', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrar', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('encontrar', 'qualquer', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrarIndice', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('encontrarIndice', 'inteiro', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrarUltimo', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('encontrarUltimo', 'inteiro', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('encontrarUltimoIndice', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('encontrarUltimoIndice', 'inteiro', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoPesquisa', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('filtrarPor', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('filtrarPor', 'qualquer[]', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoFiltragem', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('incluido', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('incluido', 'lógico', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valor', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('inteiro', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('inteiro', 'inteiro', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valor', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('mapear', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('mapear', 'qualquer[]', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoMapeamento', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('numero', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('número', 'número', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valorParaConverter', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('número', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('número', 'número', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valorParaConverter', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('ordenar', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('ordenar', 'qualquer[]', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoOrdenacao', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('paraCada', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('paraCada', 'qualquer[]', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoFiltragem', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('primeiroEmCondicao', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('primeiroEmCondicao', 'qualquer', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoFiltragem', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('real', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('real', 'número', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valorParaConverter', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('reduzir', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('reduzir', 'qualquer', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoReducao', 'função'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valorInicial', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('tamanho', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('tamanho', 'inteiro', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('objeto', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('texto', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('texto', 'texto', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('valorParaConverter', 'qualquer'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('todosEmCondicao', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('todosEmCondicao', 'lógico', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('funcaoCondicional', 'função'),
        ]));
        this.pilhaEscopos.definirInformacoesVariavel('tupla', new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('tupla', 'tupla', true, [
            new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('vetor', 'qualquer[]'),
        ]));
        // TODO: Escrever algum tipo de validação aqui.
        for (const tipos of Object.values(this.tiposDeFerramentasExternas)) {
            for (const [nomeTipo, tipo] of Object.entries(tipos)) {
                this.pilhaEscopos.definirInformacoesVariavel(nomeTipo, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomeTipo, tipo));
            }
        }
    }
    analisar(retornoLexador, hashArquivo) {
        const inicioAnalise = (0, browser_process_hrtime_1.default)();
        this.erros = [];
        this.atual = 0;
        this.blocos = 0;
        this.hashArquivo = hashArquivo || 0;
        this.simbolos = (retornoLexador === null || retornoLexador === void 0 ? void 0 : retornoLexador.simbolos) || [];
        this.pilhaDecoradores = [];
        this.tiposDefinidosEmCodigo = {};
        this.inicializarPilhaEscopos();
        let declaracoes = [];
        while (!this.estaNoFinal()) {
            this.resolverDecorador();
            const retornoDeclaracao = this.resolverDeclaracaoForaDeBloco();
            if (Array.isArray(retornoDeclaracao)) {
                declaracoes = declaracoes.concat(retornoDeclaracao);
            }
            else if (retornoDeclaracao !== null) {
                declaracoes.push(retornoDeclaracao);
            }
        }
        if (this.performance) {
            const deltaAnalise = (0, browser_process_hrtime_1.default)(inicioAnalise);
            // eslint-disable-next-line no-undef
            console.log(`[Avaliador Sintático] Tempo para análise: ${deltaAnalise[0] * 1e9 + deltaAnalise[1]}ns`);
        }
        return {
            declaracoes: declaracoes,
            erros: this.erros,
        };
    }
}
exports.AvaliadorSintatico = AvaliadorSintatico;
//# sourceMappingURL=avaliador-sintatico.js.map