"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buscarRetornos = buscarRetornos;
exports.logicaDescobertaRetornoFuncao = logicaDescobertaRetornoFuncao;
exports.registrarPrimitiva = registrarPrimitiva;
const declaracoes_1 = require("../declaracoes");
const informacao_elemento_sintatico_1 = require("../informacao-elemento-sintatico");
function* buscarRetornosEmBloco(construtoBloco) {
    for (const declaracao of construtoBloco.declaracoes) {
        if (declaracao.constructor === declaracoes_1.Retorna) {
            yield declaracao;
        }
    }
}
function* buscarRetornosEmSe(construtoSe) {
    const blocoEntao = construtoSe.caminhoEntao;
    for (const declaracao of buscarRetornosEmBloco(blocoEntao)) {
        if (declaracao.constructor === declaracoes_1.Retorna) {
            yield declaracao;
        }
    }
    if (!construtoSe.caminhoSenao)
        return;
    switch (construtoSe.caminhoSenao.constructor) {
        case declaracoes_1.Bloco:
            const blocoSenao = construtoSe.caminhoSenao;
            for (const declaracao of blocoSenao.declaracoes) {
                if (declaracao.constructor === declaracoes_1.Retorna) {
                    yield declaracao;
                }
            }
            break;
        case declaracoes_1.Se:
            const senaoSe = construtoSe.caminhoSenao;
            for (const declaracao of buscarRetornosEmSe(senaoSe)) {
                if (declaracao.constructor === declaracoes_1.Retorna) {
                    yield declaracao;
                }
            }
            break;
    }
}
function buscarRetornos(declaracao) {
    let retornasEncontrados = [];
    switch (declaracao.constructor) {
        case declaracoes_1.Retorna:
            retornasEncontrados.push(declaracao);
            break;
        case declaracoes_1.Se:
            for (const retorna of buscarRetornosEmSe(declaracao)) {
                retornasEncontrados.push(retorna);
            }
            break;
        default:
            break;
    }
    return retornasEncontrados;
}
function logicaDescobertaRetornoFuncao(avaliadorSintatico, declaracoesDaFuncao, tipoRetorno, definicaoExplicitaDeTipo, simboloParaErros) {
    let expressoesRetorna = [];
    for (const declaracao of declaracoesDaFuncao) {
        expressoesRetorna = expressoesRetorna.concat(buscarRetornos(declaracao));
    }
    if (tipoRetorno === 'vazio' && expressoesRetorna.length > 0) {
        const retornosNaoVazios = expressoesRetorna.filter((e) => e.tipo !== 'vazio');
        if (retornosNaoVazios.length > 0) {
            throw avaliadorSintatico.erro(retornosNaoVazios[0].simboloChave, `Função declara explicitamente 'vazio', mas usa expressão 'retorna' com tipo de retorno diferente de vazio.`);
        }
    }
    const tiposRetornos = new Set(expressoesRetorna.filter((e) => e.tipo !== 'qualquer').map((e) => e.tipo));
    let retornaChamadoExplicitamente = tiposRetornos.size > 0;
    // Verifica se há retornos com valores (incluindo retornos 'qualquer')
    let temRetornosComValor = expressoesRetorna.some((e) => e.valor !== null && e.valor !== undefined);
    if (tiposRetornos.size > 1 && tipoRetorno !== 'qualquer') {
        let tiposEncontrados = Array.from(tiposRetornos).reduce((acumulador, valor) => (acumulador += valor + ', '), '');
        tiposEncontrados = tiposEncontrados.slice(0, -2);
        throw avaliadorSintatico.erro(simboloParaErros, `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,
            // o 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 (!temRetornosComValor && !definicaoExplicitaDeTipo) {
            // Ou, se não há retornos com valores, e não foi definido um tipo
            // explícito com 'qualquer', o tipo inferido é 'vazio'.
            tipoRetorno = 'vazio';
        }
    }
    return tipoRetorno;
}
function registrarPrimitiva(primitivasConhecidas, tipo, catalogoPrimitivas) {
    primitivasConhecidas[tipo] = {};
    for (const [nomePrimitiva, dadosPrimitiva] of Object.entries(catalogoPrimitivas)) {
        primitivasConhecidas[tipo][nomePrimitiva] = new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomePrimitiva, dadosPrimitiva.tipoRetorno, true, dadosPrimitiva.argumentos);
    }
}
//# sourceMappingURL=comum.js.map