@designliquido/delegua 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/analisador-semantico/dialetos/analisador-semantico-pitugues.d.ts.map +1 -1
- package/analisador-semantico/dialetos/analisador-semantico-pitugues.js +5 -3
- package/analisador-semantico/dialetos/analisador-semantico-pitugues.js.map +1 -1
- package/avaliador-sintatico/comum.d.ts.map +1 -1
- package/avaliador-sintatico/comum.js +4 -2
- package/avaliador-sintatico/comum.js.map +1 -1
- package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.d.ts +1 -1
- package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.d.ts.map +1 -1
- package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.js +14 -2
- package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.js.map +1 -1
- package/bibliotecas/biblioteca-global.d.ts +22 -1
- package/bibliotecas/biblioteca-global.d.ts.map +1 -1
- package/bibliotecas/biblioteca-global.js +150 -17
- package/bibliotecas/biblioteca-global.js.map +1 -1
- package/bibliotecas/dialetos/pitugues/biblioteca-global.d.ts +21 -0
- package/bibliotecas/dialetos/pitugues/biblioteca-global.d.ts.map +1 -1
- package/bibliotecas/dialetos/pitugues/biblioteca-global.js +155 -6
- package/bibliotecas/dialetos/pitugues/biblioteca-global.js.map +1 -1
- package/bin/package.json +1 -1
- package/declaracoes/ajuda.d.ts +2 -0
- package/declaracoes/ajuda.d.ts.map +1 -1
- package/declaracoes/ajuda.js +3 -0
- package/declaracoes/ajuda.js.map +1 -1
- package/formatadores/formatador-pitugues.d.ts +1 -0
- package/formatadores/formatador-pitugues.d.ts.map +1 -1
- package/formatadores/formatador-pitugues.js +3 -1
- package/formatadores/formatador-pitugues.js.map +1 -1
- package/interfaces/tradutor-interface.d.ts +1 -1
- package/interfaces/tradutor-interface.d.ts.map +1 -1
- package/interfaces/visitante-comum-interface.d.ts +2 -2
- package/interfaces/visitante-comum-interface.d.ts.map +1 -1
- package/interpretador/comum.d.ts +1 -0
- package/interpretador/comum.d.ts.map +1 -1
- package/interpretador/comum.js +22 -0
- package/interpretador/comum.js.map +1 -1
- package/interpretador/dialetos/pitugues/comum.d.ts.map +1 -1
- package/interpretador/dialetos/pitugues/comum.js +13 -0
- package/interpretador/dialetos/pitugues/comum.js.map +1 -1
- package/interpretador/dialetos/pitugues/interpretador-pitugues.d.ts +1 -2
- package/interpretador/dialetos/pitugues/interpretador-pitugues.d.ts.map +1 -1
- package/interpretador/dialetos/pitugues/interpretador-pitugues.js +0 -38
- package/interpretador/dialetos/pitugues/interpretador-pitugues.js.map +1 -1
- package/interpretador/interpretador-base.d.ts.map +1 -1
- package/interpretador/interpretador-base.js +1 -12
- package/interpretador/interpretador-base.js.map +1 -1
- package/interpretador/interpretador.d.ts +6 -3
- package/interpretador/interpretador.d.ts.map +1 -1
- package/interpretador/interpretador.js +53 -15
- package/interpretador/interpretador.js.map +1 -1
- package/package.json +1 -1
- package/tradutores/index.d.ts +2 -0
- package/tradutores/index.d.ts.map +1 -1
- package/tradutores/index.js +2 -0
- package/tradutores/index.js.map +1 -1
- package/tradutores/mermaid/index.d.ts +1 -1
- package/tradutores/mermaid/index.js +1 -1
- package/tradutores/mermaid/subgrafo-classe.d.ts +7 -5
- package/tradutores/mermaid/subgrafo-classe.d.ts.map +1 -1
- package/tradutores/mermaid/subgrafo-classe.js +18 -18
- package/tradutores/mermaid/subgrafo-classe.js.map +1 -1
- package/tradutores/mermaid/subgrafo-metodo.d.ts +13 -0
- package/tradutores/mermaid/subgrafo-metodo.d.ts.map +1 -0
- package/tradutores/mermaid/subgrafo-metodo.js +16 -0
- package/tradutores/mermaid/subgrafo-metodo.js.map +1 -0
- package/tradutores/tradutor-elixir.d.ts +141 -0
- package/tradutores/tradutor-elixir.d.ts.map +1 -0
- package/tradutores/tradutor-elixir.js +926 -0
- package/tradutores/tradutor-elixir.js.map +1 -0
- package/tradutores/tradutor-mermaidjs.d.ts +78 -78
- package/tradutores/tradutor-mermaidjs.d.ts.map +1 -1
- package/tradutores/tradutor-mermaidjs.js +501 -372
- package/tradutores/tradutor-mermaidjs.js.map +1 -1
- package/tradutores/tradutor-python.d.ts +11 -0
- package/tradutores/tradutor-python.d.ts.map +1 -1
- package/tradutores/tradutor-python.js +32 -2
- package/tradutores/tradutor-python.js.map +1 -1
- package/tradutores/tradutor-reverso-javascript.d.ts.map +1 -1
- package/tradutores/tradutor-reverso-javascript.js +0 -2
- package/tradutores/tradutor-reverso-javascript.js.map +1 -1
- package/tradutores/tradutor-ruby.d.ts +101 -0
- package/tradutores/tradutor-ruby.d.ts.map +1 -0
- package/tradutores/tradutor-ruby.js +687 -0
- package/tradutores/tradutor-ruby.js.map +1 -0
- package/umd/delegua.js +2681 -723
- package/tradutores/mermaid/diagrama-classe.d.ts +0 -16
- package/tradutores/mermaid/diagrama-classe.d.ts.map +0 -1
- package/tradutores/mermaid/diagrama-classe.js +0 -33
- package/tradutores/mermaid/diagrama-classe.js.map +0 -1
|
@@ -0,0 +1,926 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TradutorElixir = void 0;
|
|
7
|
+
const delegua_1 = __importDefault(require("../tipos-de-simbolos/delegua"));
|
|
8
|
+
/**
|
|
9
|
+
* Tradutor que converte código Delégua para Elixir.
|
|
10
|
+
*
|
|
11
|
+
* Elixir é uma linguagem funcional, então algumas conversões são necessárias:
|
|
12
|
+
* - Classes → Módulos com structs
|
|
13
|
+
* - Métodos → Funções que recebem structs como primeiro parâmetro
|
|
14
|
+
* - Loops imperativos → Funções recursivas ou Enum.each
|
|
15
|
+
* - Variables → Bindings imutáveis
|
|
16
|
+
*/
|
|
17
|
+
class TradutorElixir {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.indentacaoAtual = 0;
|
|
20
|
+
this.moduloAtual = null;
|
|
21
|
+
this.modulosConhecidos = new Set();
|
|
22
|
+
this.funcoesConhecidas = new Set();
|
|
23
|
+
this.atributosModulo = new Map();
|
|
24
|
+
this.dentroDeMetodo = false;
|
|
25
|
+
this.nomeParametroStruct = null;
|
|
26
|
+
this.contadorVariavelTemporaria = 0;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Adiciona a indentação atual (Elixir usa 2 espaços por convenção)
|
|
30
|
+
*/
|
|
31
|
+
adicionarIndentacao() {
|
|
32
|
+
return ' '.repeat(this.indentacaoAtual);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Aumenta o nível de indentação em 2 espaços
|
|
36
|
+
*/
|
|
37
|
+
aumentarIndentacao() {
|
|
38
|
+
this.indentacaoAtual += 2;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Diminui o nível de indentação em 2 espaços
|
|
42
|
+
*/
|
|
43
|
+
diminuirIndentacao() {
|
|
44
|
+
this.indentacaoAtual -= 2;
|
|
45
|
+
if (this.indentacaoAtual < 0) {
|
|
46
|
+
this.indentacaoAtual = 0;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Converte identificadores de camelCase para snake_case (convenção Elixir)
|
|
51
|
+
*/
|
|
52
|
+
converterIdentificador(nome) {
|
|
53
|
+
return nome
|
|
54
|
+
.replace(/([A-Z])/g, '_$1')
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.replace(/^_/, '');
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Converte nomes de classes/módulos, preservando PascalCase
|
|
60
|
+
*/
|
|
61
|
+
converterNomeModulo(nome) {
|
|
62
|
+
return nome.charAt(0).toUpperCase() + nome.slice(1);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Gera nome único para variável temporária
|
|
66
|
+
*/
|
|
67
|
+
gerarVariavelTemporaria() {
|
|
68
|
+
return `_temp_${this.contadorVariavelTemporaria++}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Mapeia operadores Delégua para Elixir
|
|
72
|
+
*/
|
|
73
|
+
traduzirOperador(simbolo) {
|
|
74
|
+
const tipoSimbolo = simbolo.tipo;
|
|
75
|
+
switch (tipoSimbolo) {
|
|
76
|
+
// Aritméticos
|
|
77
|
+
case delegua_1.default.ADICAO:
|
|
78
|
+
return '+';
|
|
79
|
+
case delegua_1.default.SUBTRACAO:
|
|
80
|
+
return '-';
|
|
81
|
+
case delegua_1.default.MULTIPLICACAO:
|
|
82
|
+
return '*';
|
|
83
|
+
case delegua_1.default.DIVISAO:
|
|
84
|
+
return '/';
|
|
85
|
+
case delegua_1.default.MODULO:
|
|
86
|
+
return 'rem';
|
|
87
|
+
case delegua_1.default.EXPONENCIACAO:
|
|
88
|
+
return '**';
|
|
89
|
+
// Comparação
|
|
90
|
+
case delegua_1.default.MAIOR:
|
|
91
|
+
return '>';
|
|
92
|
+
case delegua_1.default.MAIOR_IGUAL:
|
|
93
|
+
return '>=';
|
|
94
|
+
case delegua_1.default.MENOR:
|
|
95
|
+
return '<';
|
|
96
|
+
case delegua_1.default.MENOR_IGUAL:
|
|
97
|
+
return '<=';
|
|
98
|
+
case delegua_1.default.IGUAL_IGUAL:
|
|
99
|
+
return '==';
|
|
100
|
+
case delegua_1.default.DIFERENTE:
|
|
101
|
+
return '!=';
|
|
102
|
+
// Lógicos
|
|
103
|
+
case delegua_1.default.E:
|
|
104
|
+
return 'and';
|
|
105
|
+
case delegua_1.default.OU:
|
|
106
|
+
return 'or';
|
|
107
|
+
case delegua_1.default.NEGACAO:
|
|
108
|
+
return 'not';
|
|
109
|
+
// Bitwise
|
|
110
|
+
case delegua_1.default.BIT_AND:
|
|
111
|
+
return '&&&';
|
|
112
|
+
case delegua_1.default.BIT_OR:
|
|
113
|
+
return '|||';
|
|
114
|
+
case delegua_1.default.BIT_XOR:
|
|
115
|
+
return '^^^';
|
|
116
|
+
case delegua_1.default.BIT_NOT:
|
|
117
|
+
return '~~~';
|
|
118
|
+
default:
|
|
119
|
+
return simbolo.lexema;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Ponto de entrada para tradução
|
|
124
|
+
*/
|
|
125
|
+
async traduzir(declaracoes) {
|
|
126
|
+
let resultado = '';
|
|
127
|
+
for (const declaracao of declaracoes) {
|
|
128
|
+
const traducao = await declaracao.aceitar(this);
|
|
129
|
+
if (traducao) {
|
|
130
|
+
resultado += traducao + '\n';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return resultado;
|
|
134
|
+
}
|
|
135
|
+
// ========== DECLARAÇÕES ==========
|
|
136
|
+
visitarDeclaracaoCabecalhoPrograma(declaracao) {
|
|
137
|
+
// Elixir não tem conceito de cabeçalho de programa
|
|
138
|
+
return Promise.resolve('');
|
|
139
|
+
}
|
|
140
|
+
async visitarDeclaracaoClasse(declaracao) {
|
|
141
|
+
const nomeModulo = this.converterNomeModulo(declaracao.simbolo.lexema);
|
|
142
|
+
this.modulosConhecidos.add(nomeModulo);
|
|
143
|
+
let resultado = this.adicionarIndentacao();
|
|
144
|
+
resultado += `defmodule ${nomeModulo} do\n`;
|
|
145
|
+
this.aumentarIndentacao();
|
|
146
|
+
const moduloAnterior = this.moduloAtual;
|
|
147
|
+
this.moduloAtual = nomeModulo;
|
|
148
|
+
// Extrair campos do struct do construtor
|
|
149
|
+
const camposStruct = await this.extrairCamposStruct(declaracao);
|
|
150
|
+
if (camposStruct.length > 0) {
|
|
151
|
+
resultado += this.adicionarIndentacao();
|
|
152
|
+
resultado += `defstruct [${camposStruct.join(', ')}]\n\n`;
|
|
153
|
+
}
|
|
154
|
+
// Traduzir métodos
|
|
155
|
+
for (const metodo of declaracao.metodos) {
|
|
156
|
+
const traducaoMetodo = await this.traduzirMetodoClasse(metodo, nomeModulo);
|
|
157
|
+
resultado += traducaoMetodo + '\n\n';
|
|
158
|
+
}
|
|
159
|
+
this.diminuirIndentacao();
|
|
160
|
+
resultado += this.adicionarIndentacao() + 'end';
|
|
161
|
+
this.moduloAtual = moduloAnterior;
|
|
162
|
+
return Promise.resolve(resultado);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Extrai nomes de campos do struct a partir do construtor da classe
|
|
166
|
+
*/
|
|
167
|
+
async extrairCamposStruct(declaracao) {
|
|
168
|
+
const campos = new Set();
|
|
169
|
+
// Procurar pelo construtor
|
|
170
|
+
const construtor = declaracao.metodos.find(m => m.simbolo.lexema === 'construtor' || m.simbolo.lexema === 'inicializar');
|
|
171
|
+
if (!construtor) {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
// Analisar corpo do construtor para encontrar atribuições a "isto.campo"
|
|
175
|
+
for (const declaracaoCorpo of construtor.funcao.corpo) {
|
|
176
|
+
this.extrairCamposDeDeclaracao(declaracaoCorpo, campos);
|
|
177
|
+
}
|
|
178
|
+
// Converter para atoms do Elixir
|
|
179
|
+
return Array.from(campos).map(c => `:${this.converterIdentificador(c)}`);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Extrai campos de uma declaração recursivamente
|
|
183
|
+
*/
|
|
184
|
+
extrairCamposDeDeclaracao(declaracao, campos) {
|
|
185
|
+
// Se é uma expressão de atribuição com isto.campo
|
|
186
|
+
if (declaracao.constructor.name === 'Expressao' && declaracao.expressao) {
|
|
187
|
+
const expressao = declaracao.expressao;
|
|
188
|
+
// DefinirValor: usado para isto.campo = valor
|
|
189
|
+
if (expressao.constructor.name === 'DefinirValor') {
|
|
190
|
+
if (expressao.objeto && expressao.objeto.constructor.name === 'Isto') {
|
|
191
|
+
campos.add(expressao.nome.lexema);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Atribuir: pode ser usado para isto.campo = valor
|
|
195
|
+
if (expressao.constructor.name === 'Atribuir') {
|
|
196
|
+
// Verificar se o alvo é um acesso a propriedade de isto
|
|
197
|
+
if (expressao.alvo && expressao.alvo.constructor.name === 'AcessoPropriedade') {
|
|
198
|
+
const acesso = expressao.alvo;
|
|
199
|
+
if (acesso.objeto && acesso.objeto.constructor.name === 'Isto') {
|
|
200
|
+
campos.add(acesso.nomePropriedade);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Se é um bloco, processar declarações internas
|
|
206
|
+
if (declaracao.constructor.name === 'Bloco') {
|
|
207
|
+
for (const decl of declaracao.declaracoes) {
|
|
208
|
+
this.extrairCamposDeDeclaracao(decl, campos);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Traduz um método de classe para função de módulo
|
|
214
|
+
*/
|
|
215
|
+
async traduzirMetodoClasse(metodo, nomeModulo) {
|
|
216
|
+
const nomeMetodo = this.converterIdentificador(metodo.simbolo.lexema);
|
|
217
|
+
let resultado = this.adicionarIndentacao();
|
|
218
|
+
// Construtor vira função new/N
|
|
219
|
+
if (metodo.simbolo.lexema === 'construtor' || metodo.simbolo.lexema === 'inicializar') {
|
|
220
|
+
resultado += `def new(`;
|
|
221
|
+
const parametros = metodo.funcao.parametros.map(p => this.converterIdentificador(p.nome.lexema));
|
|
222
|
+
resultado += parametros.join(', ');
|
|
223
|
+
resultado += ') do\n';
|
|
224
|
+
this.aumentarIndentacao();
|
|
225
|
+
resultado += this.adicionarIndentacao();
|
|
226
|
+
resultado += `%${nomeModulo}{`;
|
|
227
|
+
// Extrair inicializações do construtor
|
|
228
|
+
const inicializacoes = await this.extrairInicializacoesStruct(metodo.funcao.corpo, nomeModulo);
|
|
229
|
+
resultado += inicializacoes;
|
|
230
|
+
resultado += '}\n';
|
|
231
|
+
this.diminuirIndentacao();
|
|
232
|
+
resultado += this.adicionarIndentacao() + 'end';
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
// Métodos normais recebem o struct como primeiro parâmetro
|
|
236
|
+
resultado += `def ${nomeMetodo}(`;
|
|
237
|
+
const nomeParametroStruct = this.converterIdentificador(nomeModulo.toLowerCase());
|
|
238
|
+
this.nomeParametroStruct = nomeParametroStruct;
|
|
239
|
+
const parametros = [nomeParametroStruct].concat(metodo.funcao.parametros.map(p => this.converterIdentificador(p.nome.lexema)));
|
|
240
|
+
resultado += parametros.join(', ');
|
|
241
|
+
resultado += ') do\n';
|
|
242
|
+
this.aumentarIndentacao();
|
|
243
|
+
this.dentroDeMetodo = true;
|
|
244
|
+
for (const declaracaoCorpo of metodo.funcao.corpo) {
|
|
245
|
+
const traducao = await declaracaoCorpo.aceitar(this);
|
|
246
|
+
if (traducao) {
|
|
247
|
+
resultado += traducao + '\n';
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
this.dentroDeMetodo = false;
|
|
251
|
+
this.nomeParametroStruct = null;
|
|
252
|
+
this.diminuirIndentacao();
|
|
253
|
+
resultado += this.adicionarIndentacao() + 'end';
|
|
254
|
+
}
|
|
255
|
+
return resultado;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Extrai inicializações de struct do corpo do construtor
|
|
259
|
+
*/
|
|
260
|
+
async extrairInicializacoesStruct(corpo, nomeModulo) {
|
|
261
|
+
const inicializacoes = [];
|
|
262
|
+
for (const declaracao of corpo) {
|
|
263
|
+
if (declaracao.constructor.name === 'Expressao' && declaracao.expressao) {
|
|
264
|
+
const expressao = declaracao.expressao;
|
|
265
|
+
// DefinirValor: isto.campo = valor
|
|
266
|
+
if (expressao.constructor.name === 'DefinirValor') {
|
|
267
|
+
if (expressao.objeto && expressao.objeto.constructor.name === 'Isto') {
|
|
268
|
+
const campo = this.converterIdentificador(expressao.nome.lexema);
|
|
269
|
+
const valor = await expressao.valor.aceitar(this);
|
|
270
|
+
inicializacoes.push(`${campo}: ${valor}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// Atribuir: pode ser isto.campo = valor (se alvo é AcessoPropriedade)
|
|
274
|
+
if (expressao.constructor.name === 'Atribuir') {
|
|
275
|
+
if (expressao.alvo && expressao.alvo.constructor.name === 'AcessoPropriedade') {
|
|
276
|
+
const acesso = expressao.alvo;
|
|
277
|
+
if (acesso.objeto && acesso.objeto.constructor.name === 'Isto') {
|
|
278
|
+
const campo = this.converterIdentificador(acesso.nomePropriedade);
|
|
279
|
+
const valor = await expressao.valor.aceitar(this);
|
|
280
|
+
inicializacoes.push(`${campo}: ${valor}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return inicializacoes.length > 0 ? inicializacoes.join(', ') : '';
|
|
287
|
+
}
|
|
288
|
+
async visitarDeclaracaoComentario(declaracao) {
|
|
289
|
+
const conteudo = Array.isArray(declaracao.conteudo)
|
|
290
|
+
? declaracao.conteudo.join('\n# ')
|
|
291
|
+
: declaracao.conteudo;
|
|
292
|
+
return Promise.resolve(`${this.adicionarIndentacao()}# ${conteudo}`);
|
|
293
|
+
}
|
|
294
|
+
async visitarDeclaracaoConst(declaracao) {
|
|
295
|
+
// Em Elixir, constantes em módulos são atributos de módulo (@constante)
|
|
296
|
+
// Fora de módulos, são apenas bindings normais (imutáveis por padrão)
|
|
297
|
+
let resultado = this.adicionarIndentacao();
|
|
298
|
+
if (this.moduloAtual) {
|
|
299
|
+
// Dentro de módulo, usar atributo de módulo
|
|
300
|
+
const nomeAtributo = this.converterIdentificador(declaracao.simbolo.lexema);
|
|
301
|
+
resultado += `@${nomeAtributo} `;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
// Fora de módulo, binding normal (mantem nome original em maiúsculas)
|
|
305
|
+
resultado += declaracao.simbolo.lexema;
|
|
306
|
+
resultado += ' = ';
|
|
307
|
+
}
|
|
308
|
+
if (declaracao.inicializador) {
|
|
309
|
+
resultado += await declaracao.inicializador.aceitar(this);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
resultado += 'nil';
|
|
313
|
+
}
|
|
314
|
+
return Promise.resolve(resultado);
|
|
315
|
+
}
|
|
316
|
+
visitarDeclaracaoConstMultiplo(declaracao) {
|
|
317
|
+
throw new Error('Método não implementado: visitarDeclaracaoConstMultiplo');
|
|
318
|
+
}
|
|
319
|
+
async visitarDeclaracaoDeExpressao(declaracao) {
|
|
320
|
+
const resultado = this.adicionarIndentacao() + (await declaracao.expressao.aceitar(this));
|
|
321
|
+
return Promise.resolve(resultado);
|
|
322
|
+
}
|
|
323
|
+
async visitarDeclaracaoDefinicaoFuncao(declaracao) {
|
|
324
|
+
const nomeFuncao = this.converterIdentificador(declaracao.simbolo.lexema);
|
|
325
|
+
this.funcoesConhecidas.add(nomeFuncao);
|
|
326
|
+
let resultado = this.adicionarIndentacao();
|
|
327
|
+
resultado += `def ${nomeFuncao}(`;
|
|
328
|
+
// Parâmetros
|
|
329
|
+
const parametros = declaracao.funcao.parametros.map(p => this.converterIdentificador(p.nome.lexema));
|
|
330
|
+
resultado += parametros.join(', ');
|
|
331
|
+
resultado += ') do\n';
|
|
332
|
+
// Corpo
|
|
333
|
+
this.aumentarIndentacao();
|
|
334
|
+
for (const declaracaoCorpo of declaracao.funcao.corpo) {
|
|
335
|
+
const traducao = await declaracaoCorpo.aceitar(this);
|
|
336
|
+
if (traducao) {
|
|
337
|
+
resultado += traducao + '\n';
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
this.diminuirIndentacao();
|
|
341
|
+
resultado += this.adicionarIndentacao() + 'end';
|
|
342
|
+
return Promise.resolve(resultado);
|
|
343
|
+
}
|
|
344
|
+
async visitarDeclaracaoEnquanto(declaracao) {
|
|
345
|
+
// Enquanto em Elixir vira função recursiva
|
|
346
|
+
// Padrão: (fn -> loop = fn when cond -> corpo; loop.() end; loop = fn -> :ok end; loop.() end).()
|
|
347
|
+
let resultado = this.adicionarIndentacao();
|
|
348
|
+
resultado += '(fn ->\n';
|
|
349
|
+
this.aumentarIndentacao();
|
|
350
|
+
// Função recursiva com guard
|
|
351
|
+
resultado += this.adicionarIndentacao();
|
|
352
|
+
resultado += 'loop = fn when ';
|
|
353
|
+
resultado += await declaracao.condicao.aceitar(this);
|
|
354
|
+
resultado += ' ->\n';
|
|
355
|
+
this.aumentarIndentacao();
|
|
356
|
+
const traducaoCorpo = await declaracao.corpo.aceitar(this);
|
|
357
|
+
resultado += traducaoCorpo;
|
|
358
|
+
// Chamada recursiva
|
|
359
|
+
resultado += this.adicionarIndentacao() + 'loop.()\n';
|
|
360
|
+
this.diminuirIndentacao();
|
|
361
|
+
resultado += this.adicionarIndentacao() + 'end\n';
|
|
362
|
+
// Caso base
|
|
363
|
+
resultado += this.adicionarIndentacao() + 'loop = fn -> :ok end\n';
|
|
364
|
+
resultado += this.adicionarIndentacao() + 'loop.()\n';
|
|
365
|
+
this.diminuirIndentacao();
|
|
366
|
+
resultado += this.adicionarIndentacao() + 'end).()';
|
|
367
|
+
return Promise.resolve(resultado);
|
|
368
|
+
}
|
|
369
|
+
async visitarDeclaracaoEscolha(declaracao) {
|
|
370
|
+
let resultado = this.adicionarIndentacao();
|
|
371
|
+
resultado += 'case ';
|
|
372
|
+
resultado += await declaracao.identificadorOuLiteral.aceitar(this);
|
|
373
|
+
resultado += ' do\n';
|
|
374
|
+
this.aumentarIndentacao();
|
|
375
|
+
// Processar cada caminho
|
|
376
|
+
for (const caminho of declaracao.caminhos) {
|
|
377
|
+
for (const condicao of caminho.condicoes) {
|
|
378
|
+
resultado += this.adicionarIndentacao();
|
|
379
|
+
resultado += await condicao.aceitar(this);
|
|
380
|
+
resultado += ' ->\n';
|
|
381
|
+
}
|
|
382
|
+
this.aumentarIndentacao();
|
|
383
|
+
for (const decl of caminho.declaracoes) {
|
|
384
|
+
resultado += await decl.aceitar(this);
|
|
385
|
+
resultado += '\n';
|
|
386
|
+
}
|
|
387
|
+
this.diminuirIndentacao();
|
|
388
|
+
}
|
|
389
|
+
// Caminho padrão
|
|
390
|
+
if (declaracao.caminhoPadrao && declaracao.caminhoPadrao.declaracoes.length > 0) {
|
|
391
|
+
resultado += this.adicionarIndentacao() + '_ ->\n';
|
|
392
|
+
this.aumentarIndentacao();
|
|
393
|
+
for (const decl of declaracao.caminhoPadrao.declaracoes) {
|
|
394
|
+
resultado += await decl.aceitar(this);
|
|
395
|
+
resultado += '\n';
|
|
396
|
+
}
|
|
397
|
+
this.diminuirIndentacao();
|
|
398
|
+
}
|
|
399
|
+
this.diminuirIndentacao();
|
|
400
|
+
resultado += this.adicionarIndentacao() + 'end';
|
|
401
|
+
return Promise.resolve(resultado);
|
|
402
|
+
}
|
|
403
|
+
async visitarDeclaracaoEscreva(declaracao) {
|
|
404
|
+
let resultado = this.adicionarIndentacao();
|
|
405
|
+
resultado += 'IO.puts(';
|
|
406
|
+
if (declaracao.argumentos.length === 0) {
|
|
407
|
+
resultado += '""';
|
|
408
|
+
}
|
|
409
|
+
else if (declaracao.argumentos.length === 1) {
|
|
410
|
+
resultado += await declaracao.argumentos[0].aceitar(this);
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
// Múltiplos argumentos - concatenar
|
|
414
|
+
const argumentos = [];
|
|
415
|
+
for (const arg of declaracao.argumentos) {
|
|
416
|
+
argumentos.push(await arg.aceitar(this));
|
|
417
|
+
}
|
|
418
|
+
resultado += argumentos.join(' <> ');
|
|
419
|
+
}
|
|
420
|
+
resultado += ')';
|
|
421
|
+
return Promise.resolve(resultado);
|
|
422
|
+
}
|
|
423
|
+
visitarDeclaracaoEscrevaMesmaLinha(declaracao) {
|
|
424
|
+
throw new Error('Método não implementado: visitarDeclaracaoEscrevaMesmaLinha');
|
|
425
|
+
}
|
|
426
|
+
async visitarDeclaracaoFazer(declaracao) {
|
|
427
|
+
// Fazer...enquanto é similar a enquanto, mas executa o corpo pelo menos uma vez
|
|
428
|
+
let resultado = this.adicionarIndentacao();
|
|
429
|
+
resultado += '(fn ->\n';
|
|
430
|
+
this.aumentarIndentacao();
|
|
431
|
+
resultado += this.adicionarIndentacao();
|
|
432
|
+
resultado += 'loop = fn ->\n';
|
|
433
|
+
this.aumentarIndentacao();
|
|
434
|
+
// Corpo
|
|
435
|
+
const traducaoCorpo = await declaracao.caminhoFazer.aceitar(this);
|
|
436
|
+
resultado += traducaoCorpo;
|
|
437
|
+
// Verificar condição e decidir se continua
|
|
438
|
+
resultado += this.adicionarIndentacao() + 'if ';
|
|
439
|
+
resultado += await declaracao.condicaoEnquanto.aceitar(this);
|
|
440
|
+
resultado += ' do\n';
|
|
441
|
+
this.aumentarIndentacao();
|
|
442
|
+
resultado += this.adicionarIndentacao() + 'loop.()\n';
|
|
443
|
+
this.diminuirIndentacao();
|
|
444
|
+
resultado += this.adicionarIndentacao() + 'else\n';
|
|
445
|
+
this.aumentarIndentacao();
|
|
446
|
+
resultado += this.adicionarIndentacao() + ':ok\n';
|
|
447
|
+
this.diminuirIndentacao();
|
|
448
|
+
resultado += this.adicionarIndentacao() + 'end\n';
|
|
449
|
+
this.diminuirIndentacao();
|
|
450
|
+
resultado += this.adicionarIndentacao() + 'end\n';
|
|
451
|
+
resultado += this.adicionarIndentacao() + 'loop.()\n';
|
|
452
|
+
this.diminuirIndentacao();
|
|
453
|
+
resultado += this.adicionarIndentacao() + 'end).()';
|
|
454
|
+
return Promise.resolve(resultado);
|
|
455
|
+
}
|
|
456
|
+
visitarDeclaracaoInicioAlgoritmo(declaracao) {
|
|
457
|
+
// Elixir não tem conceito de início de algoritmo
|
|
458
|
+
return Promise.resolve('');
|
|
459
|
+
}
|
|
460
|
+
async visitarDeclaracaoPara(declaracao) {
|
|
461
|
+
// Para loop vira função recursiva com inicializador, condição e incremento
|
|
462
|
+
let resultado = this.adicionarIndentacao();
|
|
463
|
+
resultado += '(fn ->\n';
|
|
464
|
+
this.aumentarIndentacao();
|
|
465
|
+
// Extrair variável e valor inicial
|
|
466
|
+
let nomeVar = '';
|
|
467
|
+
let valorInicial = '';
|
|
468
|
+
if (declaracao.inicializador) {
|
|
469
|
+
const init = Array.isArray(declaracao.inicializador)
|
|
470
|
+
? declaracao.inicializador[0]
|
|
471
|
+
: declaracao.inicializador;
|
|
472
|
+
if (init.constructor.name === 'Var') {
|
|
473
|
+
nomeVar = this.converterIdentificador(init.simbolo.lexema);
|
|
474
|
+
if (init.inicializador) {
|
|
475
|
+
valorInicial = await init.inicializador.aceitar(this);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
valorInicial = '0';
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// Função recursiva com parâmetro e guard
|
|
483
|
+
resultado += this.adicionarIndentacao();
|
|
484
|
+
resultado += `loop = fn ${nomeVar} when `;
|
|
485
|
+
resultado += await declaracao.condicao.aceitar(this);
|
|
486
|
+
resultado += ' ->\n';
|
|
487
|
+
this.aumentarIndentacao();
|
|
488
|
+
const traducaoCorpo = await declaracao.corpo.aceitar(this);
|
|
489
|
+
resultado += traducaoCorpo;
|
|
490
|
+
// Incremento e chamada recursiva
|
|
491
|
+
const incremento = await declaracao.incrementar.aceitar(this);
|
|
492
|
+
resultado += this.adicionarIndentacao() + `loop.(${incremento})\n`;
|
|
493
|
+
this.diminuirIndentacao();
|
|
494
|
+
resultado += this.adicionarIndentacao() + 'end\n';
|
|
495
|
+
// Caso base
|
|
496
|
+
resultado += this.adicionarIndentacao() + `loop = fn _ -> :ok end\n`;
|
|
497
|
+
resultado += this.adicionarIndentacao() + `loop.(${valorInicial})\n`;
|
|
498
|
+
this.diminuirIndentacao();
|
|
499
|
+
resultado += this.adicionarIndentacao() + 'end).()';
|
|
500
|
+
return Promise.resolve(resultado);
|
|
501
|
+
}
|
|
502
|
+
async visitarDeclaracaoParaCada(declaracao) {
|
|
503
|
+
let resultado = this.adicionarIndentacao();
|
|
504
|
+
resultado += 'Enum.each(';
|
|
505
|
+
resultado += await declaracao.vetorOuDicionario.aceitar(this);
|
|
506
|
+
resultado += ', fn ';
|
|
507
|
+
resultado += await declaracao.variavelIteracao.aceitar(this);
|
|
508
|
+
resultado += ' ->\n';
|
|
509
|
+
this.aumentarIndentacao();
|
|
510
|
+
const traducaoCorpo = await declaracao.corpo.aceitar(this);
|
|
511
|
+
resultado += traducaoCorpo;
|
|
512
|
+
this.diminuirIndentacao();
|
|
513
|
+
resultado += this.adicionarIndentacao() + 'end)';
|
|
514
|
+
return Promise.resolve(resultado);
|
|
515
|
+
}
|
|
516
|
+
async visitarDeclaracaoSe(declaracao) {
|
|
517
|
+
let resultado = this.adicionarIndentacao();
|
|
518
|
+
resultado += 'if ';
|
|
519
|
+
resultado += await declaracao.condicao.aceitar(this);
|
|
520
|
+
resultado += ' do\n';
|
|
521
|
+
this.aumentarIndentacao();
|
|
522
|
+
const traducaoEntao = await declaracao.caminhoEntao.aceitar(this);
|
|
523
|
+
resultado += traducaoEntao;
|
|
524
|
+
this.diminuirIndentacao();
|
|
525
|
+
if (declaracao.caminhoSenao) {
|
|
526
|
+
resultado += this.adicionarIndentacao() + 'else\n';
|
|
527
|
+
this.aumentarIndentacao();
|
|
528
|
+
const traducaoSenao = await declaracao.caminhoSenao.aceitar(this);
|
|
529
|
+
resultado += traducaoSenao;
|
|
530
|
+
this.diminuirIndentacao();
|
|
531
|
+
}
|
|
532
|
+
resultado += this.adicionarIndentacao() + 'end';
|
|
533
|
+
return Promise.resolve(resultado);
|
|
534
|
+
}
|
|
535
|
+
async visitarDeclaracaoTendoComo(declaracao) {
|
|
536
|
+
throw new Error('Método não implementado: visitarDeclaracaoTendoComo');
|
|
537
|
+
}
|
|
538
|
+
visitarDeclaracaoTente(declaracao) {
|
|
539
|
+
throw new Error('Método não implementado: visitarDeclaracaoTente');
|
|
540
|
+
}
|
|
541
|
+
visitarDeclaracaoTextoDocumentacao(declaracao) {
|
|
542
|
+
throw new Error('Método não implementado: visitarDeclaracaoTextoDocumentacao');
|
|
543
|
+
}
|
|
544
|
+
async visitarDeclaracaoVar(declaracao) {
|
|
545
|
+
let resultado = this.adicionarIndentacao();
|
|
546
|
+
resultado += this.converterIdentificador(declaracao.simbolo.lexema);
|
|
547
|
+
resultado += ' = ';
|
|
548
|
+
if (declaracao.inicializador) {
|
|
549
|
+
resultado += await declaracao.inicializador.aceitar(this);
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
resultado += 'nil';
|
|
553
|
+
}
|
|
554
|
+
return Promise.resolve(resultado);
|
|
555
|
+
}
|
|
556
|
+
visitarDeclaracaoVarMultiplo(declaracao) {
|
|
557
|
+
throw new Error('Método não implementado: visitarDeclaracaoVarMultiplo');
|
|
558
|
+
}
|
|
559
|
+
// ========== EXPRESSÕES ==========
|
|
560
|
+
async visitarExpressaoDeAtribuicao(expressao) {
|
|
561
|
+
const alvo = await expressao.alvo.aceitar(this);
|
|
562
|
+
const valor = await expressao.valor.aceitar(this);
|
|
563
|
+
return Promise.resolve(`${alvo} = ${valor}`);
|
|
564
|
+
}
|
|
565
|
+
async visitarExpressaoAcessoIndiceVariavel(expressao) {
|
|
566
|
+
const objeto = await expressao.entidadeChamada.aceitar(this);
|
|
567
|
+
const indice = await expressao.indice.aceitar(this);
|
|
568
|
+
// Em Elixir, acesso por índice usa Enum.at/2
|
|
569
|
+
return Promise.resolve(`Enum.at(${objeto}, ${indice})`);
|
|
570
|
+
}
|
|
571
|
+
visitarExpressaoAcessoIntervaloVariavel(expressao) {
|
|
572
|
+
throw new Error('Método não implementado: visitarExpressaoAcessoIntervaloVariavel');
|
|
573
|
+
}
|
|
574
|
+
visitarExpressaoAcessoElementoMatriz(expressao) {
|
|
575
|
+
throw new Error('Método não implementado: visitarExpressaoAcessoElementoMatriz');
|
|
576
|
+
}
|
|
577
|
+
async visitarExpressaoAcessoMetodo(expressao) {
|
|
578
|
+
const objeto = await expressao.objeto.aceitar(this);
|
|
579
|
+
const metodo = this.converterIdentificador(expressao.nomeMetodo);
|
|
580
|
+
// AcessoMetodo é apenas a referência ao método, não a chamada
|
|
581
|
+
// A chamada é feita por visitarExpressaoDeChamada
|
|
582
|
+
return Promise.resolve(`${objeto}.${metodo}`);
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Mapeia métodos built-in de Delégua para Elixir
|
|
586
|
+
*/
|
|
587
|
+
mapearMetodoBuiltIn(metodo, objeto, argumentos) {
|
|
588
|
+
switch (metodo) {
|
|
589
|
+
// Array/List methods
|
|
590
|
+
case 'adicionar':
|
|
591
|
+
case 'empilhar':
|
|
592
|
+
return argumentos.length > 0 ? `[${argumentos[0]} | ${objeto}]` : `${objeto}`;
|
|
593
|
+
case 'tamanho':
|
|
594
|
+
return `length(${objeto})`;
|
|
595
|
+
case 'inclui':
|
|
596
|
+
return argumentos.length > 0 ? `Enum.member?(${objeto}, ${argumentos[0]})` : null;
|
|
597
|
+
case 'inverter':
|
|
598
|
+
return `Enum.reverse(${objeto})`;
|
|
599
|
+
case 'mapear':
|
|
600
|
+
return argumentos.length > 0 ? `Enum.map(${objeto}, ${argumentos[0]})` : null;
|
|
601
|
+
case 'filtrar':
|
|
602
|
+
return argumentos.length > 0 ? `Enum.filter(${objeto}, ${argumentos[0]})` : null;
|
|
603
|
+
case 'ordenar':
|
|
604
|
+
return `Enum.sort(${objeto})`;
|
|
605
|
+
case 'juntar':
|
|
606
|
+
return argumentos.length > 0 ? `Enum.join(${objeto}, ${argumentos[0]})` : `Enum.join(${objeto})`;
|
|
607
|
+
case 'fatiar':
|
|
608
|
+
if (argumentos.length >= 2) {
|
|
609
|
+
return `Enum.slice(${objeto}, ${argumentos[0]}, ${argumentos[1]})`;
|
|
610
|
+
}
|
|
611
|
+
return null;
|
|
612
|
+
case 'remover':
|
|
613
|
+
return argumentos.length > 0 ? `List.delete(${objeto}, ${argumentos[0]})` : null;
|
|
614
|
+
case 'somar':
|
|
615
|
+
return `Enum.sum(${objeto})`;
|
|
616
|
+
// String methods
|
|
617
|
+
case 'maiusculo':
|
|
618
|
+
return `String.upcase(${objeto})`;
|
|
619
|
+
case 'minusculo':
|
|
620
|
+
return `String.downcase(${objeto})`;
|
|
621
|
+
case 'dividir':
|
|
622
|
+
return argumentos.length > 0 ? `String.split(${objeto}, ${argumentos[0]})` : null;
|
|
623
|
+
case 'substituir':
|
|
624
|
+
if (argumentos.length >= 2) {
|
|
625
|
+
return `String.replace(${objeto}, ${argumentos[0]}, ${argumentos[1]})`;
|
|
626
|
+
}
|
|
627
|
+
return null;
|
|
628
|
+
case 'aparar':
|
|
629
|
+
return `String.trim(${objeto})`;
|
|
630
|
+
default:
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Tenta extrair o nome do módulo de uma expressão de objeto
|
|
636
|
+
*/
|
|
637
|
+
obterNomeModulo(objetoStr) {
|
|
638
|
+
// Se o objeto é uma variável simples, assumir que o módulo tem o mesmo nome em PascalCase
|
|
639
|
+
// Isso é uma heurística; em casos reais, precisaríamos de análise semântica
|
|
640
|
+
const match = objetoStr.match(/^([a-z_][a-z0-9_]*)$/);
|
|
641
|
+
if (match) {
|
|
642
|
+
return this.converterNomeModulo(match[1]);
|
|
643
|
+
}
|
|
644
|
+
return objetoStr;
|
|
645
|
+
}
|
|
646
|
+
async visitarExpressaoAcessoMetodoOuPropriedade(expressao) {
|
|
647
|
+
const objeto = await expressao.objeto.aceitar(this);
|
|
648
|
+
const simbolo = this.converterIdentificador(expressao.simbolo.lexema);
|
|
649
|
+
// AcessoMetodoOuPropriedade é apenas a referência, não a chamada
|
|
650
|
+
// A chamada com argumentos é feita por visitarExpressaoDeChamada
|
|
651
|
+
return Promise.resolve(`${objeto}.${simbolo}`);
|
|
652
|
+
}
|
|
653
|
+
async visitarExpressaoAcessoPropriedade(expressao) {
|
|
654
|
+
const objeto = await expressao.objeto.aceitar(this);
|
|
655
|
+
const propriedade = this.converterIdentificador(expressao.nomePropriedade);
|
|
656
|
+
return Promise.resolve(`${objeto}.${propriedade}`);
|
|
657
|
+
}
|
|
658
|
+
async visitarExpressaoAgrupamento(expressao) {
|
|
659
|
+
const conteudo = await expressao.expressao.aceitar(this);
|
|
660
|
+
return Promise.resolve(`(${conteudo})`);
|
|
661
|
+
}
|
|
662
|
+
visitarExpressaoArgumentoReferenciaFuncao(expressao) {
|
|
663
|
+
throw new Error('Método não implementado: visitarExpressaoArgumentoReferenciaFuncao');
|
|
664
|
+
}
|
|
665
|
+
visitarExpressaoAtribuicaoPorIndice(expressao) {
|
|
666
|
+
throw new Error('Método não implementado: visitarExpressaoAtribuicaoPorIndice');
|
|
667
|
+
}
|
|
668
|
+
visitarExpressaoAtribuicaoPorIndicesMatriz(expressao) {
|
|
669
|
+
throw new Error('Método não implementado: visitarExpressaoAtribuicaoPorIndicesMatriz');
|
|
670
|
+
}
|
|
671
|
+
async visitarExpressaoBinaria(expressao) {
|
|
672
|
+
const esquerda = await expressao.esquerda.aceitar(this);
|
|
673
|
+
const direita = await expressao.direita.aceitar(this);
|
|
674
|
+
const operador = this.traduzirOperador(expressao.operador);
|
|
675
|
+
return Promise.resolve(`${esquerda} ${operador} ${direita}`);
|
|
676
|
+
}
|
|
677
|
+
async visitarExpressaoBloco(declaracao) {
|
|
678
|
+
let resultado = '';
|
|
679
|
+
for (const decl of declaracao.declaracoes) {
|
|
680
|
+
const traducao = await decl.aceitar(this);
|
|
681
|
+
if (traducao) {
|
|
682
|
+
resultado += traducao + '\n';
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return Promise.resolve(resultado);
|
|
686
|
+
}
|
|
687
|
+
visitarExpressaoComentario(expressao) {
|
|
688
|
+
throw new Error('Método não implementado: visitarExpressaoComentario');
|
|
689
|
+
}
|
|
690
|
+
visitarExpressaoContinua(declaracao) {
|
|
691
|
+
throw new Error('Método não implementado: visitarExpressaoContinua');
|
|
692
|
+
}
|
|
693
|
+
async visitarExpressaoDeChamada(expressao) {
|
|
694
|
+
// Processar argumentos
|
|
695
|
+
const argumentos = [];
|
|
696
|
+
for (const arg of expressao.argumentos) {
|
|
697
|
+
const argTraduzido = await arg.aceitar(this);
|
|
698
|
+
if (argTraduzido && argTraduzido.trim() !== '') {
|
|
699
|
+
argumentos.push(argTraduzido);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
// Verificar se é instanciação de módulo (classe)
|
|
703
|
+
if (expressao.entidadeChamada.constructor.name === 'Variavel') {
|
|
704
|
+
const nomeEntidade = expressao.entidadeChamada.simbolo.lexema;
|
|
705
|
+
if (this.modulosConhecidos.has(this.converterNomeModulo(nomeEntidade))) {
|
|
706
|
+
// Chamada de construtor de módulo
|
|
707
|
+
return Promise.resolve(`${this.converterNomeModulo(nomeEntidade)}.new(${argumentos.join(', ')})`);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
// Verificar se é chamada de método (AcessoMetodo ou AcessoMetodoOuPropriedade)
|
|
711
|
+
if (expressao.entidadeChamada.constructor.name === 'AcessoMetodo') {
|
|
712
|
+
const acessoMetodo = expressao.entidadeChamada;
|
|
713
|
+
const objeto = await acessoMetodo.objeto.aceitar(this);
|
|
714
|
+
const metodo = this.converterIdentificador(acessoMetodo.nomeMetodo);
|
|
715
|
+
// Mapear métodos built-in
|
|
716
|
+
const metodoMapeado = this.mapearMetodoBuiltIn(metodo, objeto, argumentos);
|
|
717
|
+
if (metodoMapeado) {
|
|
718
|
+
return Promise.resolve(metodoMapeado);
|
|
719
|
+
}
|
|
720
|
+
// Método de módulo/struct - passar o struct como primeiro argumento
|
|
721
|
+
return Promise.resolve(`${this.obterNomeModulo(objeto)}.${metodo}(${objeto}${argumentos.length > 0 ? ', ' + argumentos.join(', ') : ''})`);
|
|
722
|
+
}
|
|
723
|
+
if (expressao.entidadeChamada.constructor.name === 'AcessoMetodoOuPropriedade') {
|
|
724
|
+
const acesso = expressao.entidadeChamada;
|
|
725
|
+
const objeto = await acesso.objeto.aceitar(this);
|
|
726
|
+
const simbolo = this.converterIdentificador(acesso.simbolo.lexema);
|
|
727
|
+
// Mapear métodos built-in
|
|
728
|
+
const metodoMapeado = this.mapearMetodoBuiltIn(simbolo, objeto, argumentos);
|
|
729
|
+
if (metodoMapeado) {
|
|
730
|
+
return Promise.resolve(metodoMapeado);
|
|
731
|
+
}
|
|
732
|
+
// Método de módulo/struct
|
|
733
|
+
return Promise.resolve(`${this.obterNomeModulo(objeto)}.${simbolo}(${objeto}${argumentos.length > 0 ? ', ' + argumentos.join(', ') : ''})`);
|
|
734
|
+
}
|
|
735
|
+
// Chamada normal de função
|
|
736
|
+
const entidadeChamada = await expressao.entidadeChamada.aceitar(this);
|
|
737
|
+
return Promise.resolve(`${entidadeChamada}(${argumentos.join(', ')})`);
|
|
738
|
+
}
|
|
739
|
+
visitarExpressaoDefinirValor(expressao) {
|
|
740
|
+
throw new Error('Método não implementado: visitarExpressaoDefinirValor');
|
|
741
|
+
}
|
|
742
|
+
async visitarExpressaoFuncaoConstruto(expressao) {
|
|
743
|
+
let resultado = 'fn ';
|
|
744
|
+
// Parâmetros
|
|
745
|
+
const parametros = expressao.parametros.map(p => this.converterIdentificador(p.nome.lexema));
|
|
746
|
+
resultado += parametros.join(', ');
|
|
747
|
+
resultado += ' ->';
|
|
748
|
+
// Corpo - se for uma única expressão, inline; se for bloco, multi-linha
|
|
749
|
+
if (expressao.corpo.length === 1) {
|
|
750
|
+
resultado += ' ';
|
|
751
|
+
const traducao = await expressao.corpo[0].aceitar(this);
|
|
752
|
+
resultado += traducao;
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
resultado += '\n';
|
|
756
|
+
this.aumentarIndentacao();
|
|
757
|
+
for (const decl of expressao.corpo) {
|
|
758
|
+
const traducao = await decl.aceitar(this);
|
|
759
|
+
if (traducao) {
|
|
760
|
+
resultado += traducao + '\n';
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
this.diminuirIndentacao();
|
|
764
|
+
resultado += this.adicionarIndentacao();
|
|
765
|
+
}
|
|
766
|
+
resultado += ' end';
|
|
767
|
+
return Promise.resolve(resultado);
|
|
768
|
+
}
|
|
769
|
+
async visitarExpressaoDeVariavel(expressao) {
|
|
770
|
+
return Promise.resolve(this.converterIdentificador(expressao.simbolo.lexema));
|
|
771
|
+
}
|
|
772
|
+
async visitarExpressaoDicionario(expressao) {
|
|
773
|
+
if (expressao.chaves.length === 0) {
|
|
774
|
+
return Promise.resolve('%{}');
|
|
775
|
+
}
|
|
776
|
+
const pares = [];
|
|
777
|
+
for (let i = 0; i < expressao.chaves.length; i++) {
|
|
778
|
+
const chave = await expressao.chaves[i].aceitar(this);
|
|
779
|
+
const valor = await expressao.valores[i].aceitar(this);
|
|
780
|
+
// Ignorar pares vazios (separadores)
|
|
781
|
+
if (chave && chave.trim() !== '' && valor && valor.trim() !== '') {
|
|
782
|
+
// Em Elixir, usa-se atoms (:chave) quando possível ou string => valor
|
|
783
|
+
// Por simplicidade, vamos usar sempre a sintaxe de string
|
|
784
|
+
pares.push(`${chave} => ${valor}`);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
return Promise.resolve(`%{${pares.join(', ')}}`);
|
|
788
|
+
}
|
|
789
|
+
visitarExpressaoExpressaoRegular(expressao) {
|
|
790
|
+
throw new Error('Método não implementado: visitarExpressaoExpressaoRegular');
|
|
791
|
+
}
|
|
792
|
+
visitarExpressaoFalhar(expressao) {
|
|
793
|
+
throw new Error('Método não implementado: visitarExpressaoFalhar');
|
|
794
|
+
}
|
|
795
|
+
visitarExpressaoFimPara(declaracao) {
|
|
796
|
+
throw new Error('Método não implementado: visitarExpressaoFimPara');
|
|
797
|
+
}
|
|
798
|
+
visitarExpressaoFormatacaoEscrita(declaracao) {
|
|
799
|
+
throw new Error('Método não implementado: visitarExpressaoFormatacaoEscrita');
|
|
800
|
+
}
|
|
801
|
+
async visitarExpressaoIsto(expressao) {
|
|
802
|
+
// "isto" em Elixir é substituído pelo nome do parâmetro do struct
|
|
803
|
+
if (this.nomeParametroStruct) {
|
|
804
|
+
return Promise.resolve(this.nomeParametroStruct);
|
|
805
|
+
}
|
|
806
|
+
// Se não estamos em contexto de método, usar nome genérico
|
|
807
|
+
return Promise.resolve('self');
|
|
808
|
+
}
|
|
809
|
+
async visitarExpressaoLeia(expressao) {
|
|
810
|
+
let resultado = 'IO.gets(';
|
|
811
|
+
if (expressao.argumentos && expressao.argumentos.length > 0) {
|
|
812
|
+
resultado += await expressao.argumentos[0].aceitar(this);
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
resultado += '""';
|
|
816
|
+
}
|
|
817
|
+
resultado += ') |> String.trim()';
|
|
818
|
+
return Promise.resolve(resultado);
|
|
819
|
+
}
|
|
820
|
+
async visitarExpressaoLiteral(expressao) {
|
|
821
|
+
const valor = expressao.valor;
|
|
822
|
+
// Null/nulo
|
|
823
|
+
if (valor === null || valor === undefined) {
|
|
824
|
+
return Promise.resolve('nil');
|
|
825
|
+
}
|
|
826
|
+
// Boolean
|
|
827
|
+
if (typeof valor === 'boolean') {
|
|
828
|
+
return Promise.resolve(valor ? 'true' : 'false');
|
|
829
|
+
}
|
|
830
|
+
// Number
|
|
831
|
+
if (typeof valor === 'number') {
|
|
832
|
+
return Promise.resolve(String(valor));
|
|
833
|
+
}
|
|
834
|
+
// String
|
|
835
|
+
if (typeof valor === 'string') {
|
|
836
|
+
// Elixir suporta interpolação com #{}
|
|
837
|
+
return Promise.resolve(`"${valor}"`);
|
|
838
|
+
}
|
|
839
|
+
return Promise.resolve(String(valor));
|
|
840
|
+
}
|
|
841
|
+
async visitarExpressaoLogica(expressao) {
|
|
842
|
+
const esquerda = await expressao.esquerda.aceitar(this);
|
|
843
|
+
const direita = await expressao.direita.aceitar(this);
|
|
844
|
+
const operador = this.traduzirOperador(expressao.operador);
|
|
845
|
+
return Promise.resolve(`${esquerda} ${operador} ${direita}`);
|
|
846
|
+
}
|
|
847
|
+
visitarExpressaoReferenciaFuncao(expressao) {
|
|
848
|
+
throw new Error('Método não implementado: visitarExpressaoReferenciaFuncao');
|
|
849
|
+
}
|
|
850
|
+
async visitarExpressaoRetornar(expressao) {
|
|
851
|
+
// Em Elixir, o retorno é implícito (última expressão)
|
|
852
|
+
// Mas podemos usar explicitamente para clareza ou retorno antecipado
|
|
853
|
+
let resultado = this.adicionarIndentacao();
|
|
854
|
+
if (expressao.valor) {
|
|
855
|
+
// Apenas retornar o valor, pois em Elixir a última expressão é o retorno
|
|
856
|
+
resultado += await expressao.valor.aceitar(this);
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
resultado += 'nil';
|
|
860
|
+
}
|
|
861
|
+
return Promise.resolve(resultado);
|
|
862
|
+
}
|
|
863
|
+
visitarExpressaoSeparador(expressao) {
|
|
864
|
+
return Promise.resolve('');
|
|
865
|
+
}
|
|
866
|
+
visitarExpressaoSuper(expressao) {
|
|
867
|
+
throw new Error('Método não implementado: visitarExpressaoSuper');
|
|
868
|
+
}
|
|
869
|
+
visitarExpressaoSustar(declaracao) {
|
|
870
|
+
throw new Error('Método não implementado: visitarExpressaoSustar');
|
|
871
|
+
}
|
|
872
|
+
async visitarExpressaoTupla(expressao) {
|
|
873
|
+
// Tupla pode ter apenas um valor (expressao.valor) ou ser TuplaN com elementos
|
|
874
|
+
// Por enquanto, apenas retornar o valor se houver
|
|
875
|
+
if (expressao.valor !== undefined) {
|
|
876
|
+
return Promise.resolve(`{${expressao.valor}}`);
|
|
877
|
+
}
|
|
878
|
+
// Se não houver valor, tupla vazia
|
|
879
|
+
return Promise.resolve('{}');
|
|
880
|
+
}
|
|
881
|
+
async visitarExpressaoTuplaN(expressao) {
|
|
882
|
+
const valores = [];
|
|
883
|
+
for (const elemento of expressao.elementos) {
|
|
884
|
+
const valorTraduzido = await elemento.aceitar(this);
|
|
885
|
+
valores.push(valorTraduzido);
|
|
886
|
+
}
|
|
887
|
+
return Promise.resolve(`{${valores.join(', ')}}`);
|
|
888
|
+
}
|
|
889
|
+
visitarExpressaoTipoDe(expressao) {
|
|
890
|
+
throw new Error('Método não implementado: visitarExpressaoTipoDe');
|
|
891
|
+
}
|
|
892
|
+
async visitarExpressaoUnaria(expressao) {
|
|
893
|
+
const operando = await expressao.operando.aceitar(this);
|
|
894
|
+
const operador = this.traduzirOperador(expressao.operador);
|
|
895
|
+
// Elixir não tem ++ ou --, então operações de incremento/decremento precisam ser convertidas
|
|
896
|
+
if (expressao.operador.tipo === delegua_1.default.INCREMENTAR) {
|
|
897
|
+
return Promise.resolve(`${operando} + 1`);
|
|
898
|
+
}
|
|
899
|
+
if (expressao.operador.tipo === delegua_1.default.DECREMENTAR) {
|
|
900
|
+
return Promise.resolve(`${operando} - 1`);
|
|
901
|
+
}
|
|
902
|
+
// Operações unárias normais (-, !, ~)
|
|
903
|
+
if (expressao.incidenciaOperador === 'ANTES') {
|
|
904
|
+
return Promise.resolve(`${operador} ${operando}`);
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
return Promise.resolve(`${operando} ${operador}`);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
async visitarExpressaoVetor(expressao) {
|
|
911
|
+
if (expressao.valores.length === 0) {
|
|
912
|
+
return Promise.resolve('[]');
|
|
913
|
+
}
|
|
914
|
+
const valores = [];
|
|
915
|
+
for (const valor of expressao.valores) {
|
|
916
|
+
const valorTraduzido = await valor.aceitar(this);
|
|
917
|
+
// Ignorar separadores vazios
|
|
918
|
+
if (valorTraduzido && valorTraduzido.trim() !== '') {
|
|
919
|
+
valores.push(valorTraduzido);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return Promise.resolve(`[${valores.join(', ')}]`);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
exports.TradutorElixir = TradutorElixir;
|
|
926
|
+
//# sourceMappingURL=tradutor-elixir.js.map
|