"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.visitarExpressaoAcessoMetodo = visitarExpressaoAcessoMetodo;
exports.visitarExpressaoAcessoMetodoOuPropriedade = visitarExpressaoAcessoMetodoOuPropriedade;
exports.visitarExpressaoAcessoPropriedade = visitarExpressaoAcessoPropriedade;
exports.resolverInterpolacoes = resolverInterpolacoes;
exports.visitarExpressaoAcessoIntervaloVariavel = visitarExpressaoAcessoIntervaloVariavel;
exports.visitarExpressaoTuplaN = visitarExpressaoTuplaN;
const construtos_1 = require("../../../construtos");
const inferenciador_1 = require("../../../inferenciador");
const quebras_1 = require("../../../quebras");
const estruturas_1 = require("../../estruturas");
const excecoes_1 = require("../../../excecoes");
const primitivas_dicionario_1 = __importDefault(require("../../../bibliotecas/dialetos/pitugues/primitivas-dicionario"));
const primitivas_numero_1 = __importDefault(require("../../../bibliotecas/dialetos/pitugues/primitivas-numero"));
const primitivas_texto_1 = __importDefault(require("../../../bibliotecas/dialetos/pitugues/primitivas-texto"));
const primitivas_vetor_1 = __importDefault(require("../../../bibliotecas/dialetos/pitugues/primitivas-vetor"));
const primitivas_tupla_1 = __importDefault(require("../../../bibliotecas/dialetos/pitugues/primitivas-tupla"));
const primitivos_1 = __importDefault(require("../../../tipos-de-dados/primitivos"));
const pitugues_1 = __importDefault(require("../../../tipos-de-dados/dialetos/pitugues"));
async function visitarExpressaoAcessoMetodo(interpretador, expressao) {
    var _a;
    const nomeObjeto = interpretador.resolverNomeObjectoAcessado(expressao.objeto);
    let variavelObjeto = await interpretador.avaliar(expressao.objeto);
    // Este caso acontece quando há encadeamento de métodos.
    // Por exemplo, `objeto1.metodo1().metodo2()`.
    // Como `RetornoQuebra` também possui `valor`, precisamos extrair o
    // valor dele primeiro.
    if (variavelObjeto.constructor && variavelObjeto.constructor === quebras_1.RetornoQuebra) {
        variavelObjeto = variavelObjeto.valor;
    }
    const objeto = interpretador.resolverValor(variavelObjeto);
    if (Array.isArray(objeto)) {
        const metodoDePrimitivaVetor = (_a = primitivas_vetor_1.default[expressao.nomeMetodo]) === null || _a === void 0 ? void 0 : _a.implementacao;
        if (metodoDePrimitivaVetor) {
            return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaVetor, expressao.nomeMetodo, 'vetor');
        }
    }
    if (objeto.constructor === estruturas_1.ObjetoDeleguaClasse) {
        return objeto.obterMetodo(expressao.nomeMetodo) || null;
    }
    if (objeto instanceof construtos_1.TuplaN || objeto.constructor.name === 'TuplaN') {
        const metodoDePrimitivaTupla = primitivas_tupla_1.default[expressao.nomeMetodo];
        if (metodoDePrimitivaTupla) {
            return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaTupla.implementacao, expressao.nomeMetodo, 'tupla');
        }
    }
    // Objeto simples do JavaScript, ou dicionário de Delégua.
    if (objeto.constructor === Object) {
        if (expressao.nomeMetodo in primitivas_dicionario_1.default) {
            const metodoDePrimitivaDicionario = primitivas_dicionario_1.default[expressao.nomeMetodo].implementacao;
            return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaDicionario, expressao.nomeMetodo, 'dicionário');
        }
        return objeto[expressao.nomeMetodo] || null;
    }
    // Casos em que o objeto possui algum outro tipo que não o de objeto simples.
    // Normalmente executam quando uma biblioteca é importada, e estamos tentando
    // obter alguma propriedade ou método desse objeto.
    // Caso 1: Função tradicional do JavaScript.
    if (typeof objeto[expressao.nomeMetodo] === primitivos_1.default.FUNCAO) {
        return objeto[expressao.nomeMetodo];
    }
    // Caso 2: Objeto tradicional do JavaScript.
    if (typeof objeto[expressao.nomeMetodo] === primitivos_1.default.OBJETO) {
        return objeto[expressao.nomeMetodo];
    }
    // A partir daqui, presume-se que o objeto é uma das estruturas
    // de Delégua.
    if (objeto instanceof estruturas_1.DeleguaModulo) {
        return objeto.componentes[expressao.nomeMetodo] || null;
    }
    let tipoObjeto = variavelObjeto.tipo;
    if (tipoObjeto === null || tipoObjeto === undefined) {
        tipoObjeto = (0, inferenciador_1.inferirTipoVariavel)(variavelObjeto);
    }
    // Como internamente um dicionário de Delégua é simplesmente um objeto de
    // JavaScript, as primitivas de dicionário, especificamente, são tratadas
    // mais acima.
    switch (tipoObjeto) {
        case pitugues_1.default.INTEIRO:
        case pitugues_1.default.NUMERO:
        case pitugues_1.default.NÚMERO:
            const metodoDePrimitivaNumero = primitivas_numero_1.default[expressao.nomeMetodo].implementacao;
            if (metodoDePrimitivaNumero) {
                return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaNumero, expressao.nomeMetodo, tipoObjeto);
            }
            break;
        case pitugues_1.default.TEXTO:
            const metodoDePrimitivaTexto = primitivas_texto_1.default[expressao.nomeMetodo].implementacao;
            if (metodoDePrimitivaTexto) {
                return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaTexto, expressao.nomeMetodo, 'texto');
            }
            break;
        case pitugues_1.default.VETOR:
        case pitugues_1.default.VETOR_NUMERO:
        case pitugues_1.default.VETOR_NÚMERO:
        case pitugues_1.default.VETOR_TEXTO:
            const metodoDePrimitivaVetor = primitivas_vetor_1.default[expressao.nomeMetodo].implementacao;
            if (metodoDePrimitivaVetor) {
                return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaVetor, expressao.nomeMetodo, tipoObjeto);
            }
            break;
    }
    return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao({
        hashArquivo: interpretador.hashArquivoDeclaracaoAtual,
        linha: interpretador.linhaDeclaracaoAtual,
    }, `Método para objeto ou primitiva não encontrado: ${expressao.nomeMetodo}.`, expressao.linha));
}
/**
 * Casos que ocorrem aqui:
 *
 * - Quando o método ou propriedade é ou 'qualquer', ou vetor
 *   de 'qualquer' ('qualquer[]'), e uma primitiva é usada.
 * - Quando o objeto é uma classe definida em código.
 * @param {AcessoMetodoOuPropriedade} expressao A expressão de acesso a método ou propriedade.
 * @returns A primitiva encontrada.
 */
async function visitarExpressaoAcessoMetodoOuPropriedade(interpretador, expressao) {
    const nomeObjeto = interpretador.resolverNomeObjectoAcessado(expressao.objeto);
    let variavelObjeto = await interpretador.avaliar(expressao.objeto);
    // Este caso acontece quando há encadeamento de métodos.
    // Por exemplo, `objeto1.metodo1().metodo2()`.
    // Como `RetornoQuebra` também possui `valor`, precisamos extrair o
    // valor dele primeiro.
    if (variavelObjeto.constructor === quebras_1.RetornoQuebra) {
        const retornoQuebra = variavelObjeto;
        variavelObjeto = retornoQuebra.valor;
    }
    const objeto = interpretador.resolverValor(variavelObjeto, true);
    if (Array.isArray(objeto)) {
        if (expressao.simbolo.lexema in primitivas_vetor_1.default) {
            const metodoDePrimitivaVetor = primitivas_vetor_1.default[expressao.simbolo.lexema].implementacao;
            return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaVetor, expressao.simbolo.lexema, 'vetor');
        }
    }
    if (objeto.constructor === estruturas_1.ObjetoDeleguaClasse) {
        return objeto.obter(expressao.simbolo);
    }
    if (objeto instanceof construtos_1.TuplaN || objeto.constructor.name === 'TuplaN') {
        const metodoDePrimitivaTupla = primitivas_tupla_1.default[expressao.simbolo.lexema];
        if (metodoDePrimitivaTupla) {
            return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaTupla.implementacao, expressao.simbolo.lexema, 'tupla');
        }
    }
    // Objeto simples do JavaScript, ou dicionário de Delégua.
    if (objeto.constructor === Object) {
        if (expressao.simbolo.lexema in primitivas_dicionario_1.default) {
            const metodoDePrimitivaDicionario = primitivas_dicionario_1.default[expressao.simbolo.lexema].implementacao;
            return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaDicionario, expressao.simbolo.lexema, 'dicionário');
        }
        return objeto[expressao.simbolo.lexema];
    }
    // String do JavaScript, ou seja, primitiva de texto.
    if (objeto.constructor === String) {
        if (!(expressao.simbolo.lexema in primitivas_texto_1.default)) {
            throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Método de primitiva '${expressao.simbolo.lexema}' não existe para o tipo texto.`);
        }
        const metodoDePrimitivaTexto = primitivas_texto_1.default[expressao.simbolo.lexema].implementacao;
        return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaTexto, expressao.simbolo.lexema, 'texto');
    }
    // A partir daqui, presume-se que o objeto é uma das estruturas
    // de Delégua.
    if (objeto instanceof estruturas_1.DeleguaModulo) {
        return objeto.componentes[expressao.simbolo.lexema] || null;
    }
    let tipoObjeto = variavelObjeto.tipo;
    if (tipoObjeto === null || tipoObjeto === undefined) {
        tipoObjeto = (0, inferenciador_1.inferirTipoVariavel)(variavelObjeto);
    }
    // Como internamente um dicionário de Delégua é simplesmente um objeto de
    // JavaScript, as primitivas de dicionário, especificamente, são tratadas
    // mais acima.
    switch (tipoObjeto) {
        case pitugues_1.default.INTEIRO:
        case pitugues_1.default.NUMERO:
        case pitugues_1.default.NÚMERO:
            if (!(expressao.simbolo.lexema in primitivas_numero_1.default)) {
                throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Método de primitiva '${expressao.simbolo.lexema}' não existe para o tipo ${tipoObjeto}.`);
            }
            const metodoDePrimitivaNumero = primitivas_numero_1.default[expressao.simbolo.lexema].implementacao;
            if (metodoDePrimitivaNumero) {
                return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaNumero, expressao.simbolo.lexema, tipoObjeto);
            }
            break;
        case pitugues_1.default.TEXTO:
            if (!(expressao.simbolo.lexema in primitivas_texto_1.default)) {
                throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Método de primitiva '${expressao.simbolo.lexema}' não existe para o tipo ${tipoObjeto}.`);
            }
            const metodoDePrimitivaTexto = primitivas_texto_1.default[expressao.simbolo.lexema].implementacao;
            if (metodoDePrimitivaTexto) {
                return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaTexto, expressao.simbolo.lexema, 'texto');
            }
            break;
        case pitugues_1.default.VETOR:
        case pitugues_1.default.VETOR_INTEIRO:
        case pitugues_1.default.VETOR_LOGICO:
        case pitugues_1.default.VETOR_LÓGICO:
        case pitugues_1.default.VETOR_NUMERO:
        case pitugues_1.default.VETOR_NÚMERO:
        case pitugues_1.default.VETOR_QUALQUER:
        case pitugues_1.default.VETOR_TEXTO:
            if (!(expressao.simbolo.lexema in primitivas_vetor_1.default)) {
                throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simbolo, `Método de primitiva '${expressao.simbolo.lexema}' não existe para o tipo ${tipoObjeto}.`);
            }
            const metodoDePrimitivaVetor = primitivas_vetor_1.default[expressao.simbolo.lexema].implementacao;
            if (metodoDePrimitivaVetor) {
                return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaVetor, expressao.simbolo.lexema, tipoObjeto);
            }
            break;
    }
    // Objeto de uma classe JavaScript regular (ou seja, com construtor e propriedades)
    // que possua a propriedade.
    // Exemplos: classes de LinConEs, como `RetornoComando`, ou bibliotecas globais com objetos próprios.
    if (objeto.hasOwnProperty && objeto.hasOwnProperty(expressao.simbolo.lexema)) {
        return objeto[expressao.simbolo.lexema];
    }
    // Último caso: objeto simples, sem construtor, sem protótipo. Exemplo: {'a': 1, 'b': 2}
    if (typeof objeto[expressao.simbolo.lexema] !== 'undefined') {
        return objeto[expressao.simbolo.lexema];
    }
    return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `Método ou propriedade para objeto ou primitiva não encontrado: ${expressao.simbolo.lexema}.`, expressao.linha));
}
async function visitarExpressaoAcessoPropriedade(interpretador, expressao) {
    const nomeObjeto = interpretador.resolverNomeObjectoAcessado(expressao.objeto);
    let variavelObjeto = await interpretador.avaliar(expressao.objeto);
    // Este caso acontece quando há encadeamento de métodos.
    // Por exemplo, `objeto1.metodo1().metodo2()`.
    // Como `RetornoQuebra` também possui `valor`, precisamos extrair o
    // valor dele primeiro.
    if (variavelObjeto.constructor && variavelObjeto.constructor === quebras_1.RetornoQuebra) {
        variavelObjeto = variavelObjeto.valor;
    }
    const objeto = interpretador.resolverValor(variavelObjeto);
    // Outro caso que `instanceof` simplesmente não funciona para casos em Liquido,
    // então testamos também o nome do construtor.
    if (objeto instanceof estruturas_1.ObjetoDeleguaClasse ||
        (objeto.constructor === estruturas_1.ObjetoDeleguaClasse)) {
        return objeto.obterMetodo(expressao.nomePropriedade) || null;
    }
    if (objeto instanceof construtos_1.TuplaN || objeto.constructor.name === 'TuplaN') {
        const metodoPrimitivaTupla = primitivas_tupla_1.default[expressao.nomePropriedade];
        if (metodoPrimitivaTupla) {
            return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoPrimitivaTupla.implementacao, expressao.nomePropriedade, 'tupla');
        }
    }
    // Objeto simples do JavaScript, ou dicionário de Delégua.
    if (objeto.constructor === Object) {
        if (expressao.nomePropriedade in primitivas_dicionario_1.default) {
            const metodoDePrimitivaDicionario = primitivas_dicionario_1.default[expressao.nomePropriedade].implementacao;
            return new estruturas_1.MetodoPrimitiva(nomeObjeto, objeto, metodoDePrimitivaDicionario, expressao.nomePropriedade, 'dicionário');
        }
        return objeto[expressao.nomePropriedade] || null;
    }
    // Casos em que o objeto possui algum outro tipo que não o de objeto simples.
    // Normalmente executam quando uma biblioteca é importada, e estamos tentando
    // obter alguma propriedade ou método desse objeto.
    // Caso 1: Função tradicional do JavaScript.
    if (typeof objeto[expressao.nomePropriedade] === primitivos_1.default.FUNCAO) {
        return objeto[expressao.nomePropriedade];
    }
    // Caso 2: Objeto tradicional do JavaScript.
    if (typeof objeto[expressao.nomePropriedade] === primitivos_1.default.OBJETO) {
        return objeto[expressao.nomePropriedade];
    }
    // A partir daqui, presume-se que o objeto é uma das estruturas
    // de Delégua.
    if (objeto instanceof estruturas_1.DeleguaModulo) {
        return objeto.componentes[expressao.nomePropriedade] || null;
    }
    let tipoObjeto = variavelObjeto.tipo;
    if (tipoObjeto === null || tipoObjeto === undefined) {
        tipoObjeto = (0, inferenciador_1.inferirTipoVariavel)(variavelObjeto);
    }
    return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `Propriedade para objeto ou primitiva não encontrado: ${expressao.nomePropriedade}.`, expressao.linha));
}
async function resolverInterpolacoes(interpretador, textoOriginal, linha) {
    const regexInterpolacao = /\$\{[a-zA-Z_][a-zA-Z0-9_]*\}/g;
    const variaveis = textoOriginal.match(regexInterpolacao);
    if (!variaveis)
        return [];
    const resultadosAvaliacaoSintatica = variaveis.map((s) => {
        // s tem a forma "${identificador}" — removemos apenas os dois primeiros e o último caractere
        const expressaoInterpolacao = s.slice(2, -1);
        let microLexador = interpretador.microLexador.mapear(expressaoInterpolacao);
        const resultadoMicroAvaliadorSintatico = interpretador.microAvaliadorSintatico.analisar(microLexador, linha);
        return {
            expressaoInterpolacao,
            resultadoMicroAvaliadorSintatico,
        };
    });
    const resolucoesPromises = await Promise.all(resultadosAvaliacaoSintatica
        .flatMap((r) => r.resultadoMicroAvaliadorSintatico.declaracoes)
        .map((d) => interpretador.avaliar(d)));
    return resolucoesPromises.map((item, indice) => ({
        expressaoInterpolacao: resultadosAvaliacaoSintatica[indice].expressaoInterpolacao,
        valor: item,
    }));
}
async function visitarExpressaoAcessoIntervaloVariavel(interpretador, expressao) {
    const resultadoEntidade = await interpretador.avaliar(expressao.entidadeChamada);
    const objeto = interpretador.resolverValor(resultadoEntidade);
    let tamanho = 0;
    if (objeto instanceof construtos_1.TuplaN) {
        tamanho = objeto.elementos.length;
    }
    else if (Array.isArray(objeto) || typeof objeto === 'string') {
        tamanho = objeto.length;
    }
    else {
        throw new excecoes_1.ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Acesso por intervalo só é suportado em vetores, textos e tuplas.', expressao.linha);
    }
    let inicio = 0;
    if (expressao.indiceInicio) {
        const resInicio = await interpretador.avaliar(expressao.indiceInicio);
        inicio = interpretador.resolverValor(resInicio);
        if (inicio < 0)
            inicio = tamanho + inicio;
    }
    let fim = tamanho;
    if (expressao.indiceFim) {
        const resFim = await interpretador.avaliar(expressao.indiceFim);
        fim = interpretador.resolverValor(resFim);
        if (fim < 0)
            fim = tamanho + fim;
    }
    if (objeto instanceof construtos_1.TuplaN) {
        const novosElementos = objeto.elementos.slice(inicio, fim);
        return new construtos_1.TuplaN(objeto.hashArquivo, objeto.linha, novosElementos);
    }
    return objeto.slice(inicio, fim);
}
async function visitarExpressaoTuplaN(interpretador, expressao) {
    const elementos = [];
    for (let i = 0; i < expressao.elementos.length; i++) {
        const res = await interpretador.avaliar(expressao.elementos[i]);
        elementos.push(interpretador.resolverValor(res));
    }
    const elementosComoConstrutos = elementos.map(valor => new construtos_1.Literal(expressao.hashArquivo, expressao.linha, valor));
    return new construtos_1.TuplaN(expressao.hashArquivo, expressao.linha, elementosComoConstrutos);
}
//# sourceMappingURL=comum.js.map