"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.InterpretadorPitugues = void 0;
const construtos_1 = require("../../../construtos");
const interpretador_1 = require("../../interpretador");
const excecoes_1 = require("../../../excecoes");
const comum = __importStar(require("./comum"));
const inferenciador_1 = require("../../../inferenciador");
const quebras_1 = require("../../../quebras");
class InterpretadorPitugues extends interpretador_1.Interpretador {
    constructor(diretorioBase, performance = false, funcaoDeRetorno = null, funcaoDeRetornoMesmaLinha = null) {
        super(diretorioBase, performance, funcaoDeRetorno, funcaoDeRetornoMesmaLinha);
        this.lancarErroPorDivisaoPorZero = true;
    }
    async visitarExpressaoAcessoMetodo(expressao) {
        return comum.visitarExpressaoAcessoMetodo(this, expressao);
    }
    async visitarExpressaoAcessoMetodoOuPropriedade(expressao) {
        return comum.visitarExpressaoAcessoMetodoOuPropriedade(this, expressao);
    }
    async visitarExpressaoAcessoPropriedade(expressao) {
        return comum.visitarExpressaoAcessoPropriedade(this, expressao);
    }
    async visitarExpressaoAcessoIntervaloVariavel(expressao) {
        return comum.visitarExpressaoAcessoIntervaloVariavel(this, expressao);
    }
    async visitarExpressaoTuplaN(expressao) {
        return comum.visitarExpressaoTuplaN(this, expressao);
    }
    async visitarExpressaoDeAtribuicao(expressao) {
        if (expressao.alvo.constructor === construtos_1.Variavel) {
            const alvoVariavel = expressao.alvo;
            try {
                this.pilhaEscoposExecucao.obterValorVariavel(alvoVariavel.simbolo);
            }
            catch (e) {
                // Em Pituguês, a variável não precisa ser declarada antes da atribuição.
                let valor = await this.avaliar(expressao.valor);
                if (valor && valor.hasOwnProperty('valorRetornado')) {
                    valor = valor.valorRetornado;
                }
                const valorResolvido = this.resolverValor(valor);
                this.pilhaEscoposExecucao.definirVariavel(alvoVariavel.simbolo.lexema, valorResolvido);
                return valorResolvido;
            }
        }
        return super.visitarExpressaoDeAtribuicao(expressao);
    }
    async visitarExpressaoAtribuicaoPorIndice(expressao) {
        const objeto = await this.avaliar(expressao.objeto);
        const objetoResolvido = this.resolverValor(objeto);
        if (objetoResolvido instanceof construtos_1.TuplaN || objetoResolvido.tipo === 'tupla') {
            throw new excecoes_1.ErroEmTempoDeExecucao(expressao.objeto.simbolo, 'Não é possível modificar uma tupla. As tuplas são estruturas de dados imutáveis.', expressao.linha);
        }
        return super.visitarExpressaoAtribuicaoPorIndice(expressao);
    }
    async visitarExpressaoAcessoIndiceVariavel(expressao) {
        const objeto = await this.avaliar(expressao.entidadeChamada);
        const indice = await this.avaliar(expressao.indice);
        let valorIndice = this.resolverValor(indice);
        const objetoResolvido = this.resolverValor(objeto);
        if (objetoResolvido instanceof construtos_1.TuplaN) {
            if (!Number.isInteger(valorIndice)) {
                throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Índice deve ser inteiro.', expressao.linha);
            }
            if (valorIndice < 0 && objetoResolvido.elementos.length !== 0) {
                valorIndice += objetoResolvido.elementos.length;
            }
            if (valorIndice < 0 || valorIndice >= objetoResolvido.elementos.length) {
                throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Índice fora do intervalo.', expressao.linha);
            }
            const elemento = objetoResolvido.elementos[valorIndice];
            if (elemento instanceof construtos_1.Literal)
                return elemento.valor;
            return this.avaliar(elemento);
        }
        return super.visitarExpressaoAcessoIndiceVariavel(expressao);
    }
    async visitarExpressaoTipoDe(expressao) {
        const resultado = await super.visitarExpressaoTipoDe(expressao);
        if (typeof resultado === 'string')
            return resultado.replace('tipo de', 'tipo');
        return resultado;
    }
    /**
     * Normaliza o valor resolvido para um array iterável.
     * Converte dicionários em listas de Duplas e strings em listas de caracteres.
     */
    prepararListaParaIteracao(valor, declaracao) {
        let valorFinal = this.resolverValor(valor);
        const ehDicionario = declaracao.vetorOuDicionario.tipo === 'dicionário';
        const ehObjetoPuro = valorFinal && typeof valorFinal === 'object' && !Array.isArray(valorFinal);
        if (ehDicionario || ehObjetoPuro) {
            return Object.entries(valorFinal).map(([chave, valor]) => new construtos_1.Dupla(new construtos_1.Literal(declaracao.hashArquivo, declaracao.linha, chave, 'texto'), new construtos_1.Literal(declaracao.hashArquivo, declaracao.linha, valor, (0, inferenciador_1.inferirTipoVariavel)(valor))));
        }
        if (typeof valorFinal === 'string')
            return valorFinal.split('');
        if (!Array.isArray(valorFinal)) {
            throw new Error("O objeto provido para 'para cada' não é iterável.");
        }
        return valorFinal;
    }
    /**
     * Resolve a lógica de atribuição das variáveis no escopo.
     * Suporta variáveis simples ou pares (Dupla).
     */
    definirVariaveisIteracao(variavel, elemento) {
        if (variavel instanceof construtos_1.Variavel) {
            this.pilhaEscoposExecucao.definirVariavel(variavel.simbolo.lexema, this.resolverValor(elemento));
            return;
        }
        if (variavel instanceof construtos_1.Dupla) {
            const var1 = variavel.primeiro;
            const var2 = variavel.segundo;
            let v1, v2;
            if (elemento instanceof construtos_1.Dupla) {
                v1 = this.resolverValor(elemento.primeiro);
                v2 = this.resolverValor(elemento.segundo);
            }
            else {
                v1 = elemento[0];
                v2 = elemento[1];
            }
            this.pilhaEscoposExecucao.definirVariavel(var1.simbolo.lexema, v1);
            this.pilhaEscoposExecucao.definirVariavel(var2.simbolo.lexema, v2);
        }
    }
    async visitarDeclaracaoParaCada(declaracao) {
        let retornoExecucao;
        declaracao.posicaoAtual = 0;
        const valorResolvido = await this.avaliar(declaracao.vetorOuDicionario);
        let listaParaIterar;
        try {
            listaParaIterar = this.prepararListaParaIteracao(valorResolvido, declaracao);
        }
        catch (erro) {
            this.erros.push({
                erroInterno: erro,
                linha: declaracao.linha,
                hashArquivo: declaracao.hashArquivo,
            });
            return Promise.reject(erro);
        }
        while (!(retornoExecucao instanceof quebras_1.Quebra) &&
            declaracao.posicaoAtual < listaParaIterar.length) {
            try {
                const elementoAtual = listaParaIterar[declaracao.posicaoAtual];
                this.definirVariaveisIteracao(declaracao.variavelIteracao, elementoAtual);
                retornoExecucao = await this.executar(declaracao.corpo);
                if (retornoExecucao instanceof quebras_1.SustarQuebra)
                    return null;
                if (retornoExecucao instanceof quebras_1.ContinuarQuebra)
                    retornoExecucao = null;
                declaracao.posicaoAtual++;
            }
            catch (erro) {
                this.erros.push({
                    erroInterno: erro,
                    linha: declaracao.linha,
                    hashArquivo: declaracao.hashArquivo,
                });
                return Promise.reject(erro);
            }
        }
        return retornoExecucao;
    }
    async visitarExpressaoRetornar(declaracao) {
        var _a, _b, _c;
        let valor = null;
        if (declaracao.valor !== null && declaracao.valor !== undefined) {
            valor = await this.avaliar(declaracao.valor);
        }
        const retornoQuebra = new quebras_1.RetornoQuebra(valor, declaracao.tipo);
        if (retornoQuebra.valor) {
            const valorResolvido = this.resolverValor(retornoQuebra.valor);
            const construtorRetorno = (_c = (_b = (_a = valorResolvido === null || valorResolvido === void 0 ? void 0 : valorResolvido.constructor) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.replaceAll('_', '')) !== null && _c !== void 0 ? _c : '';
            if (['DeleguaFuncao', 'ReferenciaMontao'].includes(construtorRetorno))
                retornoQuebra.preservarEscopo = true;
        }
        return retornoQuebra;
    }
}
exports.InterpretadorPitugues = InterpretadorPitugues;
//# sourceMappingURL=interpretador-pitugues.js.map