UNPKG

@designliquido/delegua

Version:

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

928 lines 40 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TradutorElixir = void 0; const delegua_1 = __importDefault(require("../tipos-de-simbolos/delegua")); /** * Tradutor que converte código Delégua para Elixir. * * Elixir é uma linguagem funcional, então algumas conversões são necessárias: * - Classes → Módulos com structs * - Métodos → Funções que recebem structs como primeiro parâmetro * - Loops imperativos → Funções recursivas ou Enum.each * - Variables → Bindings imutáveis */ class TradutorElixir { constructor() { this.indentacaoAtual = 0; this.moduloAtual = null; this.modulosConhecidos = new Set(); this.funcoesConhecidas = new Set(); this.atributosModulo = new Map(); this.dentroDeMetodo = false; this.nomeParametroStruct = null; this.contadorVariavelTemporaria = 0; } /** * Adiciona a indentação atual (Elixir usa 2 espaços por convenção) */ adicionarIndentacao() { return ' '.repeat(this.indentacaoAtual); } /** * Aumenta o nível de indentação em 2 espaços */ aumentarIndentacao() { this.indentacaoAtual += 2; } /** * Diminui o nível de indentação em 2 espaços */ diminuirIndentacao() { this.indentacaoAtual -= 2; if (this.indentacaoAtual < 0) { this.indentacaoAtual = 0; } } /** * Converte identificadores de camelCase para snake_case (convenção Elixir) */ converterIdentificador(nome) { return nome .replace(/([A-Z])/g, '_$1') .toLowerCase() .replace(/^_/, ''); } /** * Converte nomes de classes/módulos, preservando PascalCase */ converterNomeModulo(nome) { return nome.charAt(0).toUpperCase() + nome.slice(1); } /** * Gera nome único para variável temporária */ gerarVariavelTemporaria() { return `_temp_${this.contadorVariavelTemporaria++}`; } /** * Mapeia operadores Delégua para Elixir */ traduzirOperador(simbolo) { const tipoSimbolo = simbolo.tipo; switch (tipoSimbolo) { // Aritméticos case delegua_1.default.ADICAO: return '+'; case delegua_1.default.SUBTRACAO: return '-'; case delegua_1.default.MULTIPLICACAO: return '*'; case delegua_1.default.DIVISAO: return '/'; case delegua_1.default.MODULO: return 'rem'; case delegua_1.default.EXPONENCIACAO: return '**'; // Comparação case delegua_1.default.MAIOR: return '>'; case delegua_1.default.MAIOR_IGUAL: return '>='; case delegua_1.default.MENOR: return '<'; case delegua_1.default.MENOR_IGUAL: return '<='; case delegua_1.default.IGUAL_IGUAL: return '=='; case delegua_1.default.DIFERENTE: return '!='; // Lógicos case delegua_1.default.E: return 'and'; case delegua_1.default.OU: return 'or'; case delegua_1.default.NEGACAO: return 'not'; // Bitwise case delegua_1.default.BIT_AND: return '&&&'; case delegua_1.default.BIT_OR: return '|||'; case delegua_1.default.CIRCUMFLEXO: return '^^^'; case delegua_1.default.BIT_NOT: return '~~~'; default: return simbolo.lexema; } } /** * Ponto de entrada para tradução */ async traduzir(declaracoes) { let resultado = ''; for (const declaracao of declaracoes) { const traducao = await declaracao.aceitar(this); if (traducao) { resultado += traducao + '\n'; } } return resultado; } // ========== DECLARAÇÕES ========== visitarDeclaracaoCabecalhoPrograma(declaracao) { // Elixir não tem conceito de cabeçalho de programa return Promise.resolve(''); } async visitarDeclaracaoClasse(declaracao) { const nomeModulo = this.converterNomeModulo(declaracao.simbolo.lexema); this.modulosConhecidos.add(nomeModulo); let resultado = this.adicionarIndentacao(); resultado += `defmodule ${nomeModulo} do\n`; this.aumentarIndentacao(); const moduloAnterior = this.moduloAtual; this.moduloAtual = nomeModulo; // Extrair campos do struct do construtor const camposStruct = await this.extrairCamposStruct(declaracao); if (camposStruct.length > 0) { resultado += this.adicionarIndentacao(); resultado += `defstruct [${camposStruct.join(', ')}]\n\n`; } // Traduzir métodos for (const metodo of declaracao.metodos) { const traducaoMetodo = await this.traduzirMetodoClasse(metodo, nomeModulo); resultado += traducaoMetodo + '\n\n'; } this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end'; this.moduloAtual = moduloAnterior; return Promise.resolve(resultado); } /** * Extrai nomes de campos do struct a partir do construtor da classe */ async extrairCamposStruct(declaracao) { const campos = new Set(); // Procurar pelo construtor const construtor = declaracao.metodos.find((m) => m.simbolo.lexema === 'construtor' || m.simbolo.lexema === 'inicializar'); if (!construtor) { return []; } // Analisar corpo do construtor para encontrar atribuições a "isto.campo" for (const declaracaoCorpo of construtor.funcao.corpo) { this.extrairCamposDeDeclaracao(declaracaoCorpo, campos); } // Converter para atoms do Elixir return Array.from(campos).map((c) => `:${this.converterIdentificador(c)}`); } /** * Extrai campos de uma declaração recursivamente */ extrairCamposDeDeclaracao(declaracao, campos) { // Se é uma expressão de atribuição com isto.campo if (declaracao.constructor.name === 'Expressao' && declaracao.expressao) { const expressao = declaracao.expressao; // DefinirValor: usado para isto.campo = valor if (expressao.constructor.name === 'DefinirValor') { if (expressao.objeto && expressao.objeto.constructor.name === 'Isto') { campos.add(expressao.nome.lexema); } } // Atribuir: pode ser usado para isto.campo = valor if (expressao.constructor.name === 'Atribuir') { // Verificar se o alvo é um acesso a propriedade de isto if (expressao.alvo && expressao.alvo.constructor.name === 'AcessoPropriedade') { const acesso = expressao.alvo; if (acesso.objeto && acesso.objeto.constructor.name === 'Isto') { campos.add(acesso.nomePropriedade); } } } } // Se é um bloco, processar declarações internas if (declaracao.constructor.name === 'Bloco') { for (const decl of declaracao.declaracoes) { this.extrairCamposDeDeclaracao(decl, campos); } } } /** * Traduz um método de classe para função de módulo */ async traduzirMetodoClasse(metodo, nomeModulo) { const nomeMetodo = this.converterIdentificador(metodo.simbolo.lexema); let resultado = this.adicionarIndentacao(); // Construtor vira função new/N if (metodo.simbolo.lexema === 'construtor' || metodo.simbolo.lexema === 'inicializar') { resultado += `def new(`; const parametros = metodo.funcao.parametros.map((p) => this.converterIdentificador(p.nome.lexema)); resultado += parametros.join(', '); resultado += ') do\n'; this.aumentarIndentacao(); resultado += this.adicionarIndentacao(); resultado += `%${nomeModulo}{`; // Extrair inicializações do construtor const inicializacoes = await this.extrairInicializacoesStruct(metodo.funcao.corpo, nomeModulo); resultado += inicializacoes; resultado += '}\n'; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end'; } else { // Métodos normais recebem o struct como primeiro parâmetro resultado += `def ${nomeMetodo}(`; const nomeParametroStruct = this.converterIdentificador(nomeModulo.toLowerCase()); this.nomeParametroStruct = nomeParametroStruct; const parametros = [nomeParametroStruct].concat(metodo.funcao.parametros.map((p) => this.converterIdentificador(p.nome.lexema))); resultado += parametros.join(', '); resultado += ') do\n'; this.aumentarIndentacao(); this.dentroDeMetodo = true; for (const declaracaoCorpo of metodo.funcao.corpo) { const traducao = await declaracaoCorpo.aceitar(this); if (traducao) { resultado += traducao + '\n'; } } this.dentroDeMetodo = false; this.nomeParametroStruct = null; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end'; } return resultado; } /** * Extrai inicializações de struct do corpo do construtor */ async extrairInicializacoesStruct(corpo, nomeModulo) { const inicializacoes = []; for (const declaracao of corpo) { if (declaracao.constructor.name === 'Expressao' && declaracao.expressao) { const expressao = declaracao.expressao; // DefinirValor: isto.campo = valor if (expressao.constructor.name === 'DefinirValor') { if (expressao.objeto && expressao.objeto.constructor.name === 'Isto') { const campo = this.converterIdentificador(expressao.nome.lexema); const valor = await expressao.valor.aceitar(this); inicializacoes.push(`${campo}: ${valor}`); } } // Atribuir: pode ser isto.campo = valor (se alvo é AcessoPropriedade) if (expressao.constructor.name === 'Atribuir') { if (expressao.alvo && expressao.alvo.constructor.name === 'AcessoPropriedade') { const acesso = expressao.alvo; if (acesso.objeto && acesso.objeto.constructor.name === 'Isto') { const campo = this.converterIdentificador(acesso.nomePropriedade); const valor = await expressao.valor.aceitar(this); inicializacoes.push(`${campo}: ${valor}`); } } } } } return inicializacoes.length > 0 ? inicializacoes.join(', ') : ''; } async visitarDeclaracaoComentario(declaracao) { const conteudo = Array.isArray(declaracao.conteudo) ? declaracao.conteudo.join('\n# ') : declaracao.conteudo; return Promise.resolve(`${this.adicionarIndentacao()}# ${conteudo}`); } async visitarDeclaracaoConst(declaracao) { // Em Elixir, constantes em módulos são atributos de módulo (@constante) // Fora de módulos, são apenas bindings normais (imutáveis por padrão) let resultado = this.adicionarIndentacao(); if (this.moduloAtual) { // Dentro de módulo, usar atributo de módulo const nomeAtributo = this.converterIdentificador(declaracao.simbolo.lexema); resultado += `@${nomeAtributo} `; } else { // Fora de módulo, binding normal (mantem nome original em maiúsculas) resultado += declaracao.simbolo.lexema; resultado += ' = '; } if (declaracao.inicializador) { resultado += await declaracao.inicializador.aceitar(this); } else { resultado += 'nil'; } return Promise.resolve(resultado); } visitarDeclaracaoConstMultiplo(declaracao) { throw new Error('Método não implementado: visitarDeclaracaoConstMultiplo'); } async visitarDeclaracaoDeExpressao(declaracao) { const resultado = this.adicionarIndentacao() + (await declaracao.expressao.aceitar(this)); return Promise.resolve(resultado); } async visitarDeclaracaoDefinicaoFuncao(declaracao) { const nomeFuncao = this.converterIdentificador(declaracao.simbolo.lexema); this.funcoesConhecidas.add(nomeFuncao); let resultado = this.adicionarIndentacao(); resultado += `def ${nomeFuncao}(`; // Parâmetros const parametros = declaracao.funcao.parametros.map((p) => this.converterIdentificador(p.nome.lexema)); resultado += parametros.join(', '); resultado += ') do\n'; // Corpo this.aumentarIndentacao(); for (const declaracaoCorpo of declaracao.funcao.corpo) { const traducao = await declaracaoCorpo.aceitar(this); if (traducao) { resultado += traducao + '\n'; } } this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end'; return Promise.resolve(resultado); } async visitarDeclaracaoEnquanto(declaracao) { // Enquanto em Elixir vira função recursiva // Padrão: (fn -> loop = fn when cond -> corpo; loop.() end; loop = fn -> :ok end; loop.() end).() let resultado = this.adicionarIndentacao(); resultado += '(fn ->\n'; this.aumentarIndentacao(); // Função recursiva com guard resultado += this.adicionarIndentacao(); resultado += 'loop = fn when '; resultado += await declaracao.condicao.aceitar(this); resultado += ' ->\n'; this.aumentarIndentacao(); const traducaoCorpo = await declaracao.corpo.aceitar(this); resultado += traducaoCorpo; // Chamada recursiva resultado += this.adicionarIndentacao() + 'loop.()\n'; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end\n'; // Caso base resultado += this.adicionarIndentacao() + 'loop = fn -> :ok end\n'; resultado += this.adicionarIndentacao() + 'loop.()\n'; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end).()'; return Promise.resolve(resultado); } async visitarDeclaracaoEscolha(declaracao) { let resultado = this.adicionarIndentacao(); resultado += 'case '; resultado += await declaracao.identificadorOuLiteral.aceitar(this); resultado += ' do\n'; this.aumentarIndentacao(); // Processar cada caminho for (const caminho of declaracao.caminhos) { for (const condicao of caminho.condicoes) { resultado += this.adicionarIndentacao(); resultado += await condicao.aceitar(this); resultado += ' ->\n'; } this.aumentarIndentacao(); for (const decl of caminho.declaracoes) { resultado += await decl.aceitar(this); resultado += '\n'; } this.diminuirIndentacao(); } // Caminho padrão if (declaracao.caminhoPadrao && declaracao.caminhoPadrao.declaracoes.length > 0) { resultado += this.adicionarIndentacao() + '_ ->\n'; this.aumentarIndentacao(); for (const decl of declaracao.caminhoPadrao.declaracoes) { resultado += await decl.aceitar(this); resultado += '\n'; } this.diminuirIndentacao(); } this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end'; return Promise.resolve(resultado); } async visitarDeclaracaoEscreva(declaracao) { let resultado = this.adicionarIndentacao(); resultado += 'IO.puts('; if (declaracao.argumentos.length === 0) { resultado += '""'; } else if (declaracao.argumentos.length === 1) { resultado += await declaracao.argumentos[0].aceitar(this); } else { // Múltiplos argumentos - concatenar const argumentos = []; for (const arg of declaracao.argumentos) { argumentos.push(await arg.aceitar(this)); } resultado += argumentos.join(' <> '); } resultado += ')'; return Promise.resolve(resultado); } visitarDeclaracaoEscrevaMesmaLinha(declaracao) { throw new Error('Método não implementado: visitarDeclaracaoEscrevaMesmaLinha'); } async visitarDeclaracaoFazer(declaracao) { // Fazer...enquanto é similar a enquanto, mas executa o corpo pelo menos uma vez let resultado = this.adicionarIndentacao(); resultado += '(fn ->\n'; this.aumentarIndentacao(); resultado += this.adicionarIndentacao(); resultado += 'loop = fn ->\n'; this.aumentarIndentacao(); // Corpo const traducaoCorpo = await declaracao.caminhoFazer.aceitar(this); resultado += traducaoCorpo; // Verificar condição e decidir se continua resultado += this.adicionarIndentacao() + 'if '; resultado += await declaracao.condicaoEnquanto.aceitar(this); resultado += ' do\n'; this.aumentarIndentacao(); resultado += this.adicionarIndentacao() + 'loop.()\n'; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'else\n'; this.aumentarIndentacao(); resultado += this.adicionarIndentacao() + ':ok\n'; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end\n'; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end\n'; resultado += this.adicionarIndentacao() + 'loop.()\n'; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end).()'; return Promise.resolve(resultado); } visitarDeclaracaoInicioAlgoritmo(declaracao) { // Elixir não tem conceito de início de algoritmo return Promise.resolve(''); } async visitarDeclaracaoPara(declaracao) { // Para loop vira função recursiva com inicializador, condição e incremento let resultado = this.adicionarIndentacao(); resultado += '(fn ->\n'; this.aumentarIndentacao(); // Extrair variável e valor inicial let nomeVar = ''; let valorInicial = ''; if (declaracao.inicializador) { const init = Array.isArray(declaracao.inicializador) ? declaracao.inicializador[0] : declaracao.inicializador; if (init.constructor.name === 'Var') { nomeVar = this.converterIdentificador(init.simbolo.lexema); if (init.inicializador) { valorInicial = await init.inicializador.aceitar(this); } else { valorInicial = '0'; } } } // Função recursiva com parâmetro e guard resultado += this.adicionarIndentacao(); resultado += `loop = fn ${nomeVar} when `; resultado += await declaracao.condicao.aceitar(this); resultado += ' ->\n'; this.aumentarIndentacao(); const traducaoCorpo = await declaracao.corpo.aceitar(this); resultado += traducaoCorpo; // Incremento e chamada recursiva const incremento = await declaracao.incrementar.aceitar(this); resultado += this.adicionarIndentacao() + `loop.(${incremento})\n`; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end\n'; // Caso base resultado += this.adicionarIndentacao() + `loop = fn _ -> :ok end\n`; resultado += this.adicionarIndentacao() + `loop.(${valorInicial})\n`; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end).()'; return Promise.resolve(resultado); } async visitarDeclaracaoParaCada(declaracao) { let resultado = this.adicionarIndentacao(); resultado += 'Enum.each('; resultado += await declaracao.vetorOuDicionario.aceitar(this); resultado += ', fn '; resultado += await declaracao.variavelIteracao.aceitar(this); resultado += ' ->\n'; this.aumentarIndentacao(); const traducaoCorpo = await declaracao.corpo.aceitar(this); resultado += traducaoCorpo; this.diminuirIndentacao(); resultado += this.adicionarIndentacao() + 'end)'; return Promise.resolve(resultado); } async visitarDeclaracaoSe(declaracao) { let resultado = this.adicionarIndentacao(); resultado += 'if '; resultado += await declaracao.condicao.aceitar(this); resultado += ' do\n'; this.aumentarIndentacao(); const traducaoEntao = await declaracao.caminhoEntao.aceitar(this); resultado += traducaoEntao; this.diminuirIndentacao(); if (declaracao.caminhoSenao) { resultado += this.adicionarIndentacao() + 'else\n'; this.aumentarIndentacao(); const traducaoSenao = await declaracao.caminhoSenao.aceitar(this); resultado += traducaoSenao; this.diminuirIndentacao(); } resultado += this.adicionarIndentacao() + 'end'; return Promise.resolve(resultado); } async visitarDeclaracaoTendoComo(declaracao) { throw new Error('Método não implementado: visitarDeclaracaoTendoComo'); } visitarDeclaracaoTente(declaracao) { throw new Error('Método não implementado: visitarDeclaracaoTente'); } visitarDeclaracaoTextoDocumentacao(declaracao) { throw new Error('Método não implementado: visitarDeclaracaoTextoDocumentacao'); } async visitarDeclaracaoVar(declaracao) { let resultado = this.adicionarIndentacao(); resultado += this.converterIdentificador(declaracao.simbolo.lexema); resultado += ' = '; if (declaracao.inicializador) { resultado += await declaracao.inicializador.aceitar(this); } else { resultado += 'nil'; } return Promise.resolve(resultado); } visitarDeclaracaoVarMultiplo(declaracao) { throw new Error('Método não implementado: visitarDeclaracaoVarMultiplo'); } // ========== EXPRESSÕES ========== async visitarExpressaoDeAtribuicao(expressao) { const alvo = await expressao.alvo.aceitar(this); const valor = await expressao.valor.aceitar(this); return Promise.resolve(`${alvo} = ${valor}`); } async visitarExpressaoAcessoIndiceVariavel(expressao) { const objeto = await expressao.entidadeChamada.aceitar(this); const indice = await expressao.indice.aceitar(this); // Em Elixir, acesso por índice usa Enum.at/2 return Promise.resolve(`Enum.at(${objeto}, ${indice})`); } visitarExpressaoAcessoIntervaloVariavel(expressao) { throw new Error('Método não implementado: visitarExpressaoAcessoIntervaloVariavel'); } visitarExpressaoAcessoElementoMatriz(expressao) { throw new Error('Método não implementado: visitarExpressaoAcessoElementoMatriz'); } async visitarExpressaoAcessoMetodo(expressao) { const objeto = await expressao.objeto.aceitar(this); const metodo = this.converterIdentificador(expressao.nomeMetodo); // AcessoMetodo é apenas a referência ao método, não a chamada // A chamada é feita por visitarExpressaoDeChamada return Promise.resolve(`${objeto}.${metodo}`); } /** * Mapeia métodos built-in de Delégua para Elixir */ mapearMetodoBuiltIn(metodo, objeto, argumentos) { switch (metodo) { // Array/List methods case 'adicionar': case 'empilhar': return argumentos.length > 0 ? `[${argumentos[0]} | ${objeto}]` : `${objeto}`; case 'tamanho': return `length(${objeto})`; case 'inclui': return argumentos.length > 0 ? `Enum.member?(${objeto}, ${argumentos[0]})` : null; case 'inverter': return `Enum.reverse(${objeto})`; case 'mapear': return argumentos.length > 0 ? `Enum.map(${objeto}, ${argumentos[0]})` : null; case 'filtrar': return argumentos.length > 0 ? `Enum.filter(${objeto}, ${argumentos[0]})` : null; case 'ordenar': return `Enum.sort(${objeto})`; case 'juntar': return argumentos.length > 0 ? `Enum.join(${objeto}, ${argumentos[0]})` : `Enum.join(${objeto})`; case 'fatiar': if (argumentos.length >= 2) { return `Enum.slice(${objeto}, ${argumentos[0]}, ${argumentos[1]})`; } return null; case 'remover': return argumentos.length > 0 ? `List.delete(${objeto}, ${argumentos[0]})` : null; case 'somar': return `Enum.sum(${objeto})`; // String methods case 'maiusculo': return `String.upcase(${objeto})`; case 'minusculo': return `String.downcase(${objeto})`; case 'dividir': return argumentos.length > 0 ? `String.split(${objeto}, ${argumentos[0]})` : null; case 'substituir': if (argumentos.length >= 2) { return `String.replace(${objeto}, ${argumentos[0]}, ${argumentos[1]})`; } return null; case 'aparar': return `String.trim(${objeto})`; default: return null; } } /** * Tenta extrair o nome do módulo de uma expressão de objeto */ obterNomeModulo(objetoStr) { // Se o objeto é uma variável simples, assumir que o módulo tem o mesmo nome em PascalCase // Isso é uma heurística; em casos reais, precisaríamos de análise semântica const match = objetoStr.match(/^([a-z_][a-z0-9_]*)$/); if (match) { return this.converterNomeModulo(match[1]); } return objetoStr; } async visitarExpressaoAcessoMetodoOuPropriedade(expressao) { const objeto = await expressao.objeto.aceitar(this); const simbolo = this.converterIdentificador(expressao.simbolo.lexema); // AcessoMetodoOuPropriedade é apenas a referência, não a chamada // A chamada com argumentos é feita por visitarExpressaoDeChamada return Promise.resolve(`${objeto}.${simbolo}`); } async visitarExpressaoAcessoPropriedade(expressao) { const objeto = await expressao.objeto.aceitar(this); const propriedade = this.converterIdentificador(expressao.nomePropriedade); return Promise.resolve(`${objeto}.${propriedade}`); } async visitarExpressaoAgrupamento(expressao) { const conteudo = await expressao.expressao.aceitar(this); return Promise.resolve(`(${conteudo})`); } visitarExpressaoArgumentoReferenciaFuncao(expressao) { throw new Error('Método não implementado: visitarExpressaoArgumentoReferenciaFuncao'); } visitarExpressaoAtribuicaoPorIndice(expressao) { throw new Error('Método não implementado: visitarExpressaoAtribuicaoPorIndice'); } visitarExpressaoAtribuicaoPorIndicesMatriz(expressao) { throw new Error('Método não implementado: visitarExpressaoAtribuicaoPorIndicesMatriz'); } async visitarExpressaoBinaria(expressao) { const esquerda = await expressao.esquerda.aceitar(this); const direita = await expressao.direita.aceitar(this); const operador = this.traduzirOperador(expressao.operador); return Promise.resolve(`${esquerda} ${operador} ${direita}`); } async visitarExpressaoBloco(declaracao) { let resultado = ''; for (const decl of declaracao.declaracoes) { const traducao = await decl.aceitar(this); if (traducao) { resultado += traducao + '\n'; } } return Promise.resolve(resultado); } visitarExpressaoComentario(expressao) { throw new Error('Método não implementado: visitarExpressaoComentario'); } visitarExpressaoContinua(declaracao) { throw new Error('Método não implementado: visitarExpressaoContinua'); } async visitarExpressaoDeChamada(expressao) { // Processar argumentos const argumentos = []; for (const arg of expressao.argumentos) { const argTraduzido = await arg.aceitar(this); if (argTraduzido && argTraduzido.trim() !== '') { argumentos.push(argTraduzido); } } // Verificar se é instanciação de módulo (classe) if (expressao.entidadeChamada.constructor.name === 'Variavel') { const nomeEntidade = expressao.entidadeChamada.simbolo.lexema; if (this.modulosConhecidos.has(this.converterNomeModulo(nomeEntidade))) { // Chamada de construtor de módulo return Promise.resolve(`${this.converterNomeModulo(nomeEntidade)}.new(${argumentos.join(', ')})`); } } // Verificar se é chamada de método (AcessoMetodo ou AcessoMetodoOuPropriedade) if (expressao.entidadeChamada.constructor.name === 'AcessoMetodo') { const acessoMetodo = expressao.entidadeChamada; const objeto = await acessoMetodo.objeto.aceitar(this); const metodo = this.converterIdentificador(acessoMetodo.nomeMetodo); // Mapear métodos built-in const metodoMapeado = this.mapearMetodoBuiltIn(metodo, objeto, argumentos); if (metodoMapeado) { return Promise.resolve(metodoMapeado); } // Método de módulo/struct - passar o struct como primeiro argumento return Promise.resolve(`${this.obterNomeModulo(objeto)}.${metodo}(${objeto}${argumentos.length > 0 ? ', ' + argumentos.join(', ') : ''})`); } if (expressao.entidadeChamada.constructor.name === 'AcessoMetodoOuPropriedade') { const acesso = expressao.entidadeChamada; const objeto = await acesso.objeto.aceitar(this); const simbolo = this.converterIdentificador(acesso.simbolo.lexema); // Mapear métodos built-in const metodoMapeado = this.mapearMetodoBuiltIn(simbolo, objeto, argumentos); if (metodoMapeado) { return Promise.resolve(metodoMapeado); } // Método de módulo/struct return Promise.resolve(`${this.obterNomeModulo(objeto)}.${simbolo}(${objeto}${argumentos.length > 0 ? ', ' + argumentos.join(', ') : ''})`); } // Chamada normal de função const entidadeChamada = await expressao.entidadeChamada.aceitar(this); return Promise.resolve(`${entidadeChamada}(${argumentos.join(', ')})`); } visitarExpressaoDefinirValor(expressao) { throw new Error('Método não implementado: visitarExpressaoDefinirValor'); } async visitarExpressaoFuncaoConstruto(expressao) { let resultado = 'fn '; // Parâmetros const parametros = expressao.parametros.map((p) => this.converterIdentificador(p.nome.lexema)); resultado += parametros.join(', '); resultado += ' ->'; // Corpo - se for uma única expressão, inline; se for bloco, multi-linha if (expressao.corpo.length === 1) { resultado += ' '; const traducao = await expressao.corpo[0].aceitar(this); resultado += traducao; } else { resultado += '\n'; this.aumentarIndentacao(); for (const decl of expressao.corpo) { const traducao = await decl.aceitar(this); if (traducao) { resultado += traducao + '\n'; } } this.diminuirIndentacao(); resultado += this.adicionarIndentacao(); } resultado += ' end'; return Promise.resolve(resultado); } async visitarExpressaoDeVariavel(expressao) { return Promise.resolve(this.converterIdentificador(expressao.simbolo.lexema)); } async visitarExpressaoDicionario(expressao) { if (expressao.chaves.length === 0) { return Promise.resolve('%{}'); } const pares = []; for (let i = 0; i < expressao.chaves.length; i++) { const chave = await expressao.chaves[i].aceitar(this); const valor = await expressao.valores[i].aceitar(this); // Ignorar pares vazios (separadores) if (chave && chave.trim() !== '' && valor && valor.trim() !== '') { // Em Elixir, usa-se atoms (:chave) quando possível ou string => valor // Por simplicidade, vamos usar sempre a sintaxe de string pares.push(`${chave} => ${valor}`); } } return Promise.resolve(`%{${pares.join(', ')}}`); } visitarExpressaoExpressaoRegular(expressao) { throw new Error('Método não implementado: visitarExpressaoExpressaoRegular'); } visitarExpressaoFalhar(expressao) { throw new Error('Método não implementado: visitarExpressaoFalhar'); } visitarExpressaoFimPara(declaracao) { throw new Error('Método não implementado: visitarExpressaoFimPara'); } visitarExpressaoFormatacaoEscrita(declaracao) { throw new Error('Método não implementado: visitarExpressaoFormatacaoEscrita'); } async visitarExpressaoIsto(expressao) { // "isto" em Elixir é substituído pelo nome do parâmetro do struct if (this.nomeParametroStruct) { return Promise.resolve(this.nomeParametroStruct); } // Se não estamos em contexto de método, usar nome genérico return Promise.resolve('self'); } async visitarExpressaoLeia(expressao) { let resultado = 'IO.gets('; if (expressao.argumentos && expressao.argumentos.length > 0) { resultado += await expressao.argumentos[0].aceitar(this); } else { resultado += '""'; } resultado += ') |> String.trim()'; return Promise.resolve(resultado); } async visitarExpressaoLiteral(expressao) { const valor = expressao.valor; // Null/nulo if (valor === null || valor === undefined) { return Promise.resolve('nil'); } // Boolean if (typeof valor === 'boolean') { return Promise.resolve(valor ? 'true' : 'false'); } // Number if (typeof valor === 'number') { return Promise.resolve(String(valor)); } // String if (typeof valor === 'string') { // Elixir suporta interpolação com #{} return Promise.resolve(`"${valor}"`); } return Promise.resolve(String(valor)); } async visitarExpressaoLogica(expressao) { const esquerda = await expressao.esquerda.aceitar(this); const direita = await expressao.direita.aceitar(this); const operador = this.traduzirOperador(expressao.operador); return Promise.resolve(`${esquerda} ${operador} ${direita}`); } visitarExpressaoReferenciaFuncao(expressao) { throw new Error('Método não implementado: visitarExpressaoReferenciaFuncao'); } async visitarExpressaoRetornar(expressao) { // Em Elixir, o retorno é implícito (última expressão) // Mas podemos usar explicitamente para clareza ou retorno antecipado let resultado = this.adicionarIndentacao(); if (expressao.valor) { // Apenas retornar o valor, pois em Elixir a última expressão é o retorno resultado += await expressao.valor.aceitar(this); } else { resultado += 'nil'; } return Promise.resolve(resultado); } visitarExpressaoSeparador(expressao) { return Promise.resolve(''); } visitarExpressaoSuper(expressao) { throw new Error('Método não implementado: visitarExpressaoSuper'); } visitarExpressaoSustar(declaracao) { throw new Error('Método não implementado: visitarExpressaoSustar'); } async visitarExpressaoTupla(expressao) { // Tupla pode ter apenas um valor (expressao.valor) ou ser TuplaN com elementos // Por enquanto, apenas retornar o valor se houver if (expressao.valor !== undefined) { return Promise.resolve(`{${expressao.valor}}`); } // Se não houver valor, tupla vazia return Promise.resolve('{}'); } async visitarExpressaoTuplaN(expressao) { const valores = []; for (const elemento of expressao.elementos) { const valorTraduzido = await elemento.aceitar(this); valores.push(valorTraduzido); } return Promise.resolve(`{${valores.join(', ')}}`); } visitarExpressaoTipoDe(expressao) { throw new Error('Método não implementado: visitarExpressaoTipoDe'); } async visitarExpressaoUnaria(expressao) { const operando = await expressao.operando.aceitar(this); const operador = this.traduzirOperador(expressao.operador); // Elixir não tem ++ ou --, então operações de incremento/decremento precisam ser convertidas if (expressao.operador.tipo === delegua_1.default.INCREMENTAR) { return Promise.resolve(`${operando} + 1`); } if (expressao.operador.tipo === delegua_1.default.DECREMENTAR) { return Promise.resolve(`${operando} - 1`); } // Operações unárias normais (-, !, ~) if (expressao.incidenciaOperador === 'ANTES') { return Promise.resolve(`${operador} ${operando}`); } else { return Promise.resolve(`${operando} ${operador}`); } } async visitarExpressaoVetor(expressao) { if (expressao.valores.length === 0) { return Promise.resolve('[]'); } const valores = []; for (const valor of expressao.valores) { const valorTraduzido = await valor.aceitar(this); // Ignorar separadores vazios if (valorTraduzido && valorTraduzido.trim() !== '') { valores.push(valorTraduzido); } } return Promise.resolve(`[${valores.join(', ')}]`); } } exports.TradutorElixir = TradutorElixir; //# sourceMappingURL=tradutor-elixir.js.map