@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
928 lines • 40 kB
JavaScript
"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