"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FormatadorPitugues = void 0;
const construtos_1 = require("../construtos");
const declaracoes_1 = require("../declaracoes");
const pitugues_1 = __importDefault(require("../tipos-de-simbolos/pitugues"));
class FormatadorPitugues {
    constructor(quebraLinha, tamanhoIndentacao = 4) {
        this.nívelIndentação = 0;
        this.tamanhoIndentação = 4;
        this.códigoFormatado = '';
        this.quebraLinha = '\n';
        this.quebraLinha = quebraLinha;
        this.tamanhoIndentação = tamanhoIndentacao;
    }
    indentar() {
        return ' '.repeat(this.nívelIndentação);
    }
    adicionarLinha(texto = '') {
        this.códigoFormatado += this.indentar() + texto + this.quebraLinha;
    }
    aumentarIndentação() {
        this.nívelIndentação += this.tamanhoIndentação;
    }
    diminuirIndentação() {
        this.nívelIndentação -= this.tamanhoIndentação;
    }
    // Métodos obrigatórios da interface, não usados no Pituguês.
    visitarDeclaracaoCabecalhoPrograma() { return Promise.resolve(); }
    visitarDeclaracaoConstMultiplo() { return Promise.resolve(); }
    visitarDeclaracaoEscrevaMesmaLinha() { return Promise.resolve(); }
    visitarDeclaracaoInicioAlgoritmo() { return Promise.resolve(); }
    visitarDeclaracaoTendoComo() { return Promise.resolve(); }
    visitarDeclaracaoVarMultiplo() { return Promise.resolve(); }
    visitarExpressaoAcessoElementoMatriz() { return Promise.resolve(); }
    visitarExpressaoAcessoMetodo() { return Promise.resolve(); }
    visitarExpressaoAcessoPropriedade() { return Promise.resolve(); }
    visitarExpressaoArgumentoReferenciaFuncao() { return Promise.resolve(); }
    visitarExpressaoAtribuicaoPorIndicesMatriz() { return Promise.resolve(); }
    visitarExpressaoComentario() { return Promise.resolve(); }
    visitarExpressaoExpressaoRegular() { return Promise.resolve(); }
    visitarExpressaoFimPara() { return Promise.resolve(); }
    visitarExpressaoFormatacaoEscrita() { return Promise.resolve(); }
    visitarExpressaoReferenciaFuncao() { return Promise.resolve(); }
    visitarExpressaoSeparador() { return Promise.resolve(); }
    visitarExpressaoTupla() { return Promise.resolve(); }
    async visitarExpressaoTuplaN(expressao) {
        this.códigoFormatado += '(';
        for (let i = 0; i < expressao.elementos.length; i++) {
            const elemento = expressao.elementos[i];
            this.códigoFormatado += await elemento.aceitar(this);
            if (i < expressao.elementos.length - 1) {
                this.códigoFormatado += ', ';
            }
        }
        this.códigoFormatado += ')';
    }
    async visitarDeclaracaoDeExpressao(declaracao) {
        this.códigoFormatado += this.indentar();
        this.códigoFormatado += await declaracao.expressao.aceitar(this) + `\n`;
    }
    async visitarDeclaracaoClasse(declaração) {
        this.adicionarLinha(`classe ${declaração.simbolo.lexema}:`);
        this.aumentarIndentação();
        // Atributos
        for (const atributo of declaração.propriedades || []) {
            const tipo = atributo.tipo ? `: ${atributo.tipo}` : '';
            this.adicionarLinha(`${atributo.nome.lexema}${tipo}`);
        }
        // Métodos
        for (const método of declaração.metodos || []) {
            const nome = método.simbolo.lexema;
            const éConstrutor = nome === 'construtor';
            const parâmetros = método.funcao.parametros;
            this.códigoFormatado += this.indentar();
            this.códigoFormatado += `${éConstrutor ? 'construtor' : `função ${nome}`}(`;
            if (parâmetros.length > 0) {
                for (let i = 0; i < parâmetros.length; i++) {
                    const p = parâmetros[i];
                    const tipo = p.tipoDado ? `: ${p.tipoDado}` : '';
                    this.códigoFormatado += `${p.nome.lexema}${tipo}`;
                    this.códigoFormatado += ', ';
                }
                if (parâmetros.length > 1) {
                    this.códigoFormatado = this.códigoFormatado.slice(0, -2);
                }
            }
            this.códigoFormatado += '):\n';
            this.aumentarIndentação();
            for (const instrução of método.funcao.corpo) {
                await instrução.aceitar(this);
            }
            this.diminuirIndentação();
        }
        this.diminuirIndentação();
    }
    async visitarDeclaracaoDefinicaoFuncao(declaração) {
        const parâmetros = declaração.funcao.parametros;
        this.códigoFormatado += this.indentar() + `função ${declaração.simbolo.lexema}(`;
        for (let i = 0; i < parâmetros.length; i++) {
            const p = parâmetros[i];
            const tipo = p.tipoDado ? `: ${p.tipoDado}` : '';
            this.códigoFormatado += `${p.nome.lexema}${tipo}`;
            if (i < parâmetros.length - 1)
                this.códigoFormatado += ', ';
        }
        this.códigoFormatado += '):\n';
        this.aumentarIndentação();
        for (const instrução of declaração.funcao.corpo) {
            await instrução.aceitar(this);
        }
        this.diminuirIndentação();
    }
    visitarDeclaracaoTextoDocumentacao(declaracao) {
        this.códigoFormatado += `'''${declaracao.conteudo}'''`;
    }
    async visitarDeclaracaoVar(declaração) {
        this.códigoFormatado += this.indentar() + `${declaração.simbolo.lexema} = `;
        if (declaração.inicializador) {
            this.códigoFormatado += await declaração.inicializador.aceitar(this);
        }
        else {
            this.códigoFormatado += 'nulo';
        }
        this.códigoFormatado += `\n`;
    }
    async visitarDeclaracaoConst(declaração) {
        return Promise.resolve();
    }
    async visitarDeclaracaoSe(declaração) {
        this.adicionarLinha(`se ${await declaração.condicao.aceitar(this)}:`);
        this.aumentarIndentação();
        for (const instrução of declaração.caminhoEntao.declaracoes) {
            await instrução.aceitar(this);
        }
        this.diminuirIndentação();
        if (declaração.caminhoSenao) {
            this.adicionarLinha('senão:');
            this.aumentarIndentação();
            if (declaração.caminhoSenao instanceof declaracoes_1.Bloco) {
                for (const instrução of declaração.caminhoSenao.declaracoes) {
                    await instrução.aceitar(this);
                }
            }
            else {
                await declaração.caminhoSenao.aceitar(this);
            }
            this.diminuirIndentação();
        }
    }
    async visitarDeclaracaoEnquanto(declaração) {
        this.adicionarLinha(`enquanto ${await declaração.condicao.aceitar(this)}:`);
        this.aumentarIndentação();
        for (const instrução of declaração.corpo.declaracoes) {
            await instrução.aceitar(this);
        }
        this.diminuirIndentação();
    }
    async visitarDeclaracaoEscolha(declaração) {
        var _a, _b;
        const valor = await declaração.identificadorOuLiteral.aceitar(this);
        this.adicionarLinha(`escolha ${valor}:`);
        this.aumentarIndentação();
        for (const caminho of declaração.caminhos) {
            for (const condição of caminho.condicoes) {
                this.adicionarLinha(`caso ${await condição.aceitar(this)}:`);
                this.aumentarIndentação();
                for (const instrução of caminho.declaracoes) {
                    await instrução.aceitar(this);
                }
                this.diminuirIndentação();
            }
        }
        if ((_b = (_a = declaração.caminhoPadrao) === null || _a === void 0 ? void 0 : _a.declaracoes) === null || _b === void 0 ? void 0 : _b.length) {
            this.adicionarLinha('padrão:');
            this.aumentarIndentação();
            for (const instrução of declaração.caminhoPadrao.declaracoes) {
                await instrução.aceitar(this);
            }
            this.diminuirIndentação();
        }
        this.diminuirIndentação();
    }
    async visitarDeclaracaoFazer(declaração) {
        this.adicionarLinha('fazer:');
        this.aumentarIndentação();
        for (const instrução of declaração.caminhoFazer.declaracoes) {
            await instrução.aceitar(this);
        }
        this.diminuirIndentação();
        this.adicionarLinha(`enquanto ${await declaração.condicaoEnquanto.aceitar(this)}`);
    }
    async visitarDeclaracaoParaCada(declaração) {
        const variavelIteracao = await declaração.variavelIteracao.aceitar(this);
        const vetorOuDicionario = await declaração.vetorOuDicionario.aceitar(this);
        this.adicionarLinha(`para cada ${variavelIteracao} de ${vetorOuDicionario}:`);
        this.aumentarIndentação();
        for (const instrução of declaração.corpo.declaracoes) {
            await instrução.aceitar(this);
        }
        this.diminuirIndentação();
    }
    async visitarDeclaracaoPara(declaração) {
        return Promise.resolve();
    }
    async visitarDeclaracaoTente(declaração) {
        this.adicionarLinha('tente:');
        this.aumentarIndentação();
        for (const instrução of declaração.caminhoTente)
            await instrução.aceitar(this);
        this.diminuirIndentação();
        if (declaração.caminhoPegue) {
            this.adicionarLinha('pegue como erro:');
            this.aumentarIndentação();
            const declaracoes = declaração.caminhoPegue instanceof construtos_1.FuncaoConstruto ? declaração.caminhoPegue.corpo : declaração.caminhoPegue;
            for (const instrução of declaracoes) {
                await instrução.aceitar(this);
            }
            this.diminuirIndentação();
        }
        if (declaração.caminhoFinalmente) {
            this.adicionarLinha('finalmente:');
            this.aumentarIndentação();
            for (const instrução of declaração.caminhoFinalmente)
                await instrução.aceitar(this);
            this.diminuirIndentação();
        }
    }
    async visitarDeclaracaoEscreva(declaração) {
        this.códigoFormatado += this.indentar() + `${declaração.simboloEscreva ? declaração.simboloEscreva.lexema : 'imprima'}(`;
        for (let i = 0; i < declaração.argumentos.length; i++) {
            this.códigoFormatado += await declaração.argumentos[i].aceitar(this);
            if (i < declaração.argumentos.length - 1)
                this.códigoFormatado += ', ';
        }
        this.códigoFormatado += `)${this.quebraLinha}`;
    }
    async visitarDeclaracaoImportar(declaração) {
        const caminho = await declaração.caminho.aceitar(this);
        this.adicionarLinha(`importar "${caminho.replace(/'/g, '')}"`);
    }
    async visitarDeclaracaoComentario(declaração) {
        this.adicionarLinha(`# ${declaração.conteudo}`);
    }
    // ================================================================
    // Expressões
    // ================================================================
    async visitarExpressaoBloco(declaração) {
        for (const instrução of declaração.declaracoes) {
            await instrução.aceitar(this);
        }
    }
    async visitarExpressaoRetornar(declaração) {
        if (declaração.valor) {
            return `retorna ${await declaração.valor.aceitar(this)}`;
        }
        else {
            return 'retorna';
        }
    }
    visitarExpressaoContinua() {
        this.adicionarLinha('continue');
    }
    visitarExpressaoSustar() {
        this.adicionarLinha('sustar');
    }
    async visitarExpressaoFalhar(expressão) {
        const mensagem = expressão.explicacao ? await expressão.explicacao.aceitar(this) : '"Erro"';
        this.adicionarLinha(`levante Erro(${mensagem})`);
    }
    async visitarExpressaoDeAtribuicao(expressão) {
        return `${await expressão.alvo.aceitar(this)} = ${await expressão.valor.aceitar(this)}`;
    }
    async visitarExpressaoBinaria(expressão) {
        const esquerda = await expressão.esquerda.aceitar(this);
        const direita = await expressão.direita.aceitar(this);
        const operador = this.mapearOperador(expressão.operador.tipo);
        return `${esquerda} ${operador} ${direita}`;
    }
    async visitarExpressaoLogica(expressão) {
        const esquerda = await expressão.esquerda.aceitar(this);
        const direita = await expressão.direita.aceitar(this);
        const operador = expressão.operador.tipo === pitugues_1.default.E ? 'e' : 'ou';
        return `${esquerda} ${operador} ${direita}`;
    }
    async visitarExpressaoLiteral(expressão) {
        if (expressão.valor === null)
            return 'nulo';
        if (expressão.valor === true)
            return 'verdadeiro';
        if (expressão.valor === false)
            return 'falso';
        if (typeof expressão.valor === 'string') {
            return `'${expressão.valor.replace(/'/g, "\\'")}'`;
        }
        return String(expressão.valor);
    }
    async visitarExpressaoDeVariavel(expressão) {
        return expressão.simbolo.lexema;
    }
    async visitarExpressaoAgrupamento(expressão) {
        return `(${await expressão.expressao.aceitar(this)})`;
    }
    async visitarExpressaoUnaria(expressão) {
        const operador = expressão.operador.tipo === pitugues_1.default.SUBTRACAO ? '-'
            : expressão.operador.tipo === pitugues_1.default.NEGACAO ? 'não '
                : '';
        const operando = await expressão.operando.aceitar(this);
        return expressão.incidenciaOperador === 'ANTES'
            ? `${operador}${operando}`
            : `${operando}${operador}`;
    }
    async visitarExpressaoDeChamada(expressão) {
        const função = await expressão.entidadeChamada.aceitar(this);
        const argumentos = await Promise.all(expressão.argumentos.map(a => a.aceitar(this)));
        return `${função}(${argumentos.join(', ')})`;
    }
    visitarExpressaoIsto() {
        return Promise.resolve('isto');
    }
    async visitarExpressaoSuper() {
        return 'super()';
    }
    async visitarExpressaoVetor(expressão) {
        const valores = await Promise.all(expressão.valores.map(v => v.aceitar(this)));
        return `[${valores.join(', ')}]`;
    }
    async visitarExpressaoDicionario(expressão) {
        const pares = await Promise.all(expressão.chaves.map(async (chave, i) => {
            const k = await chave.aceitar(this);
            const v = await expressão.valores[i].aceitar(this);
            return `${k}: ${v}`;
        }));
        return `{${pares.join(', ')}}`;
    }
    async visitarExpressaoAcessoIndiceVariavel(expressão) {
        const entidade = await expressão.entidadeChamada.aceitar(this);
        const índice = await expressão.indice.aceitar(this);
        return `${entidade}[${índice}]`;
    }
    async visitarExpressaoAtribuicaoPorIndice(expressão) {
        const objeto = await expressão.objeto.aceitar(this);
        const índice = await expressão.indice.aceitar(this);
        const valor = await expressão.valor.aceitar(this);
        return `${objeto}[${índice}] = ${valor}`;
    }
    async visitarExpressaoAcessoIntervaloVariavel(expressao) {
        const inicio = await expressao.indiceInicio.aceitar(this);
        const fim = await expressao.indiceFim.aceitar(this);
        const entidade = await expressao.entidadeChamada.aceitar(this);
        return `${entidade}[${inicio}:${fim}]`;
    }
    async visitarExpressaoAcessoMetodoOuPropriedade(expressão) {
        const objeto = await expressão.objeto.aceitar(this);
        return `${objeto}.${expressão.simbolo.lexema}`;
    }
    async visitarExpressaoDefinirValor(expressão) {
        const objeto = await expressão.objeto.aceitar(this);
        const valor = await expressão.valor.aceitar(this);
        return `${objeto}.${expressão.nome.lexema} = ${valor}`;
    }
    async visitarExpressaoFuncaoConstruto(expressão) {
        const parâmetros = expressão.parametros.map(p => {
            const tipo = p.tipoDado ? `: ${p.tipoDado}` : '';
            return p.nome.lexema + tipo;
        }).join(', ');
        let funçãoStr = `função(${parâmetros || ''}):\n`;
        this.aumentarIndentação();
        for (const instrução of expressão.corpo) {
            const linha = await instrução.aceitar(this);
            if (linha) { // Only add if it returns something
                funçãoStr += this.indentar() + linha;
                if (!linha.endsWith('\n'))
                    funçãoStr += '\n';
            }
        }
        this.diminuirIndentação();
        return funçãoStr.trimEnd();
    }
    async visitarExpressaoLeia(expressão) {
        const argumentos = await Promise.all(expressão.argumentos.map(a => a.aceitar(this)));
        return argumentos.length > 0 ? `input(${argumentos.join(', ')})` : 'input()';
    }
    async visitarExpressaoTipoDe(expressão) {
        const valor = await expressão.valor.aceitar(this);
        return `type(${valor})`;
    }
    mapearOperador(tipo) {
        const mapa = {
            [pitugues_1.default.ADICAO]: '+',
            [pitugues_1.default.SUBTRACAO]: '-',
            [pitugues_1.default.MULTIPLICACAO]: '*',
            [pitugues_1.default.DIVISAO]: '/',
            [pitugues_1.default.MODULO]: '%',
            [pitugues_1.default.MAIOR]: '>',
            [pitugues_1.default.MAIOR_IGUAL]: '>=',
            [pitugues_1.default.MENOR]: '<',
            [pitugues_1.default.MENOR_IGUAL]: '<=',
            [pitugues_1.default.IGUAL_IGUAL]: '==',
            [pitugues_1.default.DIFERENTE]: '!=',
        };
        return mapa[tipo] || String(tipo);
    }
    async formatar(declarações) {
        this.códigoFormatado = '';
        this.nívelIndentação = 0;
        for (const declaração of declarações) {
            await declaração.aceitar(this);
        }
        return this.códigoFormatado.trimEnd() + '\n';
    }
}
exports.FormatadorPitugues = FormatadorPitugues;
//# sourceMappingURL=formatador-pitugues.js.map