@yakuzaa/jade-compiler 0.1.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/dist/ast/nodes.d.ts +219 -0
- package/dist/ast/nodes.d.ts.map +1 -0
- package/dist/ast/nodes.js +2 -0
- package/dist/ast/nodes.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +108 -0
- package/dist/cli.js.map +1 -0
- package/dist/codegen/ir_generator.d.ts +42 -0
- package/dist/codegen/ir_generator.d.ts.map +1 -0
- package/dist/codegen/ir_generator.js +691 -0
- package/dist/codegen/ir_generator.js.map +1 -0
- package/dist/codegen/ir_nodes.d.ts +143 -0
- package/dist/codegen/ir_nodes.d.ts.map +1 -0
- package/dist/codegen/ir_nodes.js +3 -0
- package/dist/codegen/ir_nodes.js.map +1 -0
- package/dist/codegen/ir_printer.d.ts +10 -0
- package/dist/codegen/ir_printer.d.ts.map +1 -0
- package/dist/codegen/ir_printer.js +131 -0
- package/dist/codegen/ir_printer.js.map +1 -0
- package/dist/codegen/wasm_generator.d.ts +12 -0
- package/dist/codegen/wasm_generator.d.ts.map +1 -0
- package/dist/codegen/wasm_generator.js +48 -0
- package/dist/codegen/wasm_generator.js.map +1 -0
- package/dist/codegen/wat_generator.d.ts +29 -0
- package/dist/codegen/wat_generator.d.ts.map +1 -0
- package/dist/codegen/wat_generator.js +345 -0
- package/dist/codegen/wat_generator.js.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/lexer/lexer.d.ts +31 -0
- package/dist/lexer/lexer.d.ts.map +1 -0
- package/dist/lexer/lexer.js +475 -0
- package/dist/lexer/lexer.js.map +1 -0
- package/dist/lexer/token.d.ts +8 -0
- package/dist/lexer/token.d.ts.map +1 -0
- package/dist/lexer/token.js +2 -0
- package/dist/lexer/token.js.map +1 -0
- package/dist/lexer/token_type.d.ts +80 -0
- package/dist/lexer/token_type.d.ts.map +1 -0
- package/dist/lexer/token_type.js +98 -0
- package/dist/lexer/token_type.js.map +1 -0
- package/dist/parser/parse_result.d.ts +12 -0
- package/dist/parser/parse_result.d.ts.map +1 -0
- package/dist/parser/parse_result.js +2 -0
- package/dist/parser/parse_result.js.map +1 -0
- package/dist/parser/parser.d.ts +57 -0
- package/dist/parser/parser.d.ts.map +1 -0
- package/dist/parser/parser.js +1094 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/semantic/semantic_analyzer.d.ts +10 -0
- package/dist/semantic/semantic_analyzer.d.ts.map +1 -0
- package/dist/semantic/semantic_analyzer.js +16 -0
- package/dist/semantic/semantic_analyzer.js.map +1 -0
- package/dist/semantic/symbol_table.d.ts +33 -0
- package/dist/semantic/symbol_table.d.ts.map +1 -0
- package/dist/semantic/symbol_table.js +105 -0
- package/dist/semantic/symbol_table.js.map +1 -0
- package/dist/semantic/type_checker.d.ts +66 -0
- package/dist/semantic/type_checker.d.ts.map +1 -0
- package/dist/semantic/type_checker.js +963 -0
- package/dist/semantic/type_checker.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
export class IRGenerator {
|
|
2
|
+
module;
|
|
3
|
+
currentFunction = null;
|
|
4
|
+
currentBlock = null;
|
|
5
|
+
tempCounter = 0;
|
|
6
|
+
blockCounter = 0;
|
|
7
|
+
constructor(moduleName) {
|
|
8
|
+
this.module = {
|
|
9
|
+
name: moduleName,
|
|
10
|
+
typeDefinitions: [],
|
|
11
|
+
globals: [],
|
|
12
|
+
functions: []
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
generate(program) {
|
|
16
|
+
// Passo 1: registrar todos os tipos (entidades/classes)
|
|
17
|
+
for (const declaracao of program.declaracoes) {
|
|
18
|
+
if (declaracao.kind === 'Entidade') {
|
|
19
|
+
this.generateEntidade(declaracao);
|
|
20
|
+
}
|
|
21
|
+
else if (declaracao.kind === 'Classe') {
|
|
22
|
+
this.generateClasse(declaracao);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Passo 2: gerar funções
|
|
26
|
+
for (const declaracao of program.declaracoes) {
|
|
27
|
+
if (declaracao.kind === 'Funcao') {
|
|
28
|
+
this.generateFuncao(declaracao);
|
|
29
|
+
}
|
|
30
|
+
else if (declaracao.kind === 'Servico') {
|
|
31
|
+
this.generateServico(declaracao);
|
|
32
|
+
}
|
|
33
|
+
else if (declaracao.kind === 'Tela') {
|
|
34
|
+
// Telas são tratadas pelo UIEngine em runtime — sem geração de IR
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return this.module;
|
|
38
|
+
}
|
|
39
|
+
// ── Geradores de declaração ──────────────────────────────
|
|
40
|
+
generateEntidade(node) {
|
|
41
|
+
const fields = node.campos.map(campo => ({
|
|
42
|
+
name: campo.nome,
|
|
43
|
+
type: this.jadeTypeToIR(campo.tipo)
|
|
44
|
+
}));
|
|
45
|
+
this.module.typeDefinitions.push({
|
|
46
|
+
name: node.nome,
|
|
47
|
+
fields
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
generateClasse(node) {
|
|
51
|
+
const fields = node.campos.map(campo => ({
|
|
52
|
+
name: campo.nome,
|
|
53
|
+
type: this.jadeTypeToIR(campo.tipo)
|
|
54
|
+
}));
|
|
55
|
+
this.module.typeDefinitions.push({
|
|
56
|
+
name: node.nome,
|
|
57
|
+
fields
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
generateFuncao(node) {
|
|
61
|
+
const parameters = node.parametros.map(param => ({
|
|
62
|
+
name: '%' + param.nome,
|
|
63
|
+
type: this.jadeTypeToIR(param.tipo)
|
|
64
|
+
}));
|
|
65
|
+
const returnType = node.tipoRetorno ? this.jadeTypeToIR(node.tipoRetorno) : 'void';
|
|
66
|
+
const func = {
|
|
67
|
+
name: '@' + node.nome,
|
|
68
|
+
parameters,
|
|
69
|
+
returnType,
|
|
70
|
+
blocks: [],
|
|
71
|
+
locals: []
|
|
72
|
+
};
|
|
73
|
+
this.currentFunction = func;
|
|
74
|
+
this.module.functions.push(func);
|
|
75
|
+
// Criar bloco entry
|
|
76
|
+
const entryBlock = this.createBlock('entry');
|
|
77
|
+
this.switchToBlock(entryBlock);
|
|
78
|
+
// Gerar corpo da função
|
|
79
|
+
this.generateBloco(node.corpo);
|
|
80
|
+
// Garantir que o último bloco tem terminador
|
|
81
|
+
if (this.currentBlock && !this.currentBlock.terminator) {
|
|
82
|
+
this.setTerminator({ kind: 'Return' });
|
|
83
|
+
}
|
|
84
|
+
this.currentFunction = null;
|
|
85
|
+
this.currentBlock = null;
|
|
86
|
+
}
|
|
87
|
+
generateServico(node) {
|
|
88
|
+
// Gera cada método como função independente
|
|
89
|
+
// Nome: '@NomeServico_nomeFuncao'
|
|
90
|
+
for (const metodo of node.metodos) {
|
|
91
|
+
const parameters = metodo.parametros.map(param => ({
|
|
92
|
+
name: '%' + param.nome,
|
|
93
|
+
type: this.jadeTypeToIR(param.tipo)
|
|
94
|
+
}));
|
|
95
|
+
const returnType = metodo.tipoRetorno ? this.jadeTypeToIR(metodo.tipoRetorno) : 'void';
|
|
96
|
+
const func = {
|
|
97
|
+
name: '@' + node.nome + '_' + metodo.nome,
|
|
98
|
+
parameters,
|
|
99
|
+
returnType,
|
|
100
|
+
blocks: [],
|
|
101
|
+
locals: []
|
|
102
|
+
};
|
|
103
|
+
this.currentFunction = func;
|
|
104
|
+
this.module.functions.push(func);
|
|
105
|
+
// Criar bloco entry
|
|
106
|
+
const entryBlock = this.createBlock('entry');
|
|
107
|
+
this.switchToBlock(entryBlock);
|
|
108
|
+
// Gerar corpo do método
|
|
109
|
+
this.generateBloco(metodo.corpo);
|
|
110
|
+
// Garantir que o último bloco tem terminador
|
|
111
|
+
if (this.currentBlock && !this.currentBlock.terminator) {
|
|
112
|
+
this.setTerminator({ kind: 'Return' });
|
|
113
|
+
}
|
|
114
|
+
this.currentFunction = null;
|
|
115
|
+
this.currentBlock = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// ── Geradores de instrução ───────────────────────────────
|
|
119
|
+
generateBloco(node) {
|
|
120
|
+
for (const instrucao of node.instrucoes) {
|
|
121
|
+
this.generateInstrucao(instrucao);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
generateInstrucao(node) {
|
|
125
|
+
switch (node.kind) {
|
|
126
|
+
case 'Variavel':
|
|
127
|
+
this.generateVariavel(node);
|
|
128
|
+
break;
|
|
129
|
+
case 'Atribuicao':
|
|
130
|
+
this.generateAtribuicao(node);
|
|
131
|
+
break;
|
|
132
|
+
case 'Condicional':
|
|
133
|
+
this.generateCondicional(node);
|
|
134
|
+
break;
|
|
135
|
+
case 'Enquanto':
|
|
136
|
+
this.generateEnquanto(node);
|
|
137
|
+
break;
|
|
138
|
+
case 'Para':
|
|
139
|
+
this.generatePara(node);
|
|
140
|
+
break;
|
|
141
|
+
case 'Retorno':
|
|
142
|
+
this.generateRetorno(node);
|
|
143
|
+
break;
|
|
144
|
+
case 'Erro':
|
|
145
|
+
this.generateErro(node);
|
|
146
|
+
break;
|
|
147
|
+
case 'EmissaoEvento':
|
|
148
|
+
this.generateEmissaoEvento(node);
|
|
149
|
+
break;
|
|
150
|
+
case 'ChamadaFuncao':
|
|
151
|
+
this.generateExpressao(node); // Chamada como instrução
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
generateVariavel(node) {
|
|
156
|
+
const type = node.tipo ? this.jadeTypeToIR(node.tipo) : 'void';
|
|
157
|
+
if (node.inicializador) {
|
|
158
|
+
const value = this.generateExpressao(node.inicializador);
|
|
159
|
+
this.emit({
|
|
160
|
+
kind: 'Store',
|
|
161
|
+
target: '%' + node.nome,
|
|
162
|
+
value,
|
|
163
|
+
type
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
// Se não tem inicializador: emite Store com valor zero do tipo
|
|
168
|
+
const zeroValue = {
|
|
169
|
+
kind: 'Constant',
|
|
170
|
+
type,
|
|
171
|
+
value: type === 'i1' ? false : type === 'f64' ? 0.0 : 0
|
|
172
|
+
};
|
|
173
|
+
this.emit({
|
|
174
|
+
kind: 'Store',
|
|
175
|
+
target: '%' + node.nome,
|
|
176
|
+
value: zeroValue,
|
|
177
|
+
type
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
generateAtribuicao(node) {
|
|
182
|
+
const value = this.generateExpressao(node.valor);
|
|
183
|
+
if (typeof node.alvo === 'string') {
|
|
184
|
+
// Se alvo é string: Store simples
|
|
185
|
+
this.emit({
|
|
186
|
+
kind: 'Store',
|
|
187
|
+
target: '%' + node.alvo,
|
|
188
|
+
value,
|
|
189
|
+
type: 'void' // Tipo será inferido do valor
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
// Se alvo é AcessoMembro: SetField
|
|
194
|
+
const objeto = this.generateExpressao(node.alvo.objeto);
|
|
195
|
+
this.emit({
|
|
196
|
+
kind: 'SetField',
|
|
197
|
+
object: objeto,
|
|
198
|
+
field: node.alvo.membro,
|
|
199
|
+
value,
|
|
200
|
+
type: 'void'
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
generateCondicional(node) {
|
|
205
|
+
// Cria blocos: 'then_N', 'else_N', 'merge_N'
|
|
206
|
+
const thenBlock = this.createBlock(this.newBlockLabel('then'));
|
|
207
|
+
const elseBlock = this.createBlock(this.newBlockLabel('else'));
|
|
208
|
+
const mergeBlock = this.createBlock(this.newBlockLabel('merge'));
|
|
209
|
+
// Gera condição → CondBranch
|
|
210
|
+
const condition = this.generateExpressao(node.condicao);
|
|
211
|
+
this.setTerminator({
|
|
212
|
+
kind: 'CondBranch',
|
|
213
|
+
condition,
|
|
214
|
+
trueBlock: thenBlock.label,
|
|
215
|
+
falseBlock: node.senao ? elseBlock.label : mergeBlock.label
|
|
216
|
+
});
|
|
217
|
+
// Gera corpo then → Branch para merge
|
|
218
|
+
this.switchToBlock(thenBlock);
|
|
219
|
+
this.generateBloco(node.entao);
|
|
220
|
+
if (this.currentBlock && !this.currentBlock.terminator) {
|
|
221
|
+
this.setTerminator({ kind: 'Branch', target: mergeBlock.label });
|
|
222
|
+
}
|
|
223
|
+
// Gera corpo else (se houver) → Branch para merge
|
|
224
|
+
if (node.senao) {
|
|
225
|
+
this.switchToBlock(elseBlock);
|
|
226
|
+
this.generateBloco(node.senao);
|
|
227
|
+
if (this.currentBlock && !this.currentBlock.terminator) {
|
|
228
|
+
this.setTerminator({ kind: 'Branch', target: mergeBlock.label });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Continua no bloco merge
|
|
232
|
+
this.switchToBlock(mergeBlock);
|
|
233
|
+
}
|
|
234
|
+
generateEnquanto(node) {
|
|
235
|
+
// Cria blocos: 'loop_header_N', 'loop_body_N', 'loop_exit_N'
|
|
236
|
+
const headerBlock = this.createBlock(this.newBlockLabel('loop_header'));
|
|
237
|
+
const bodyBlock = this.createBlock(this.newBlockLabel('loop_body'));
|
|
238
|
+
const exitBlock = this.createBlock(this.newBlockLabel('loop_exit'));
|
|
239
|
+
// Branch para header
|
|
240
|
+
this.setTerminator({ kind: 'Branch', target: headerBlock.label });
|
|
241
|
+
// Header: avalia condição → CondBranch body/exit
|
|
242
|
+
this.switchToBlock(headerBlock);
|
|
243
|
+
const condition = this.generateExpressao(node.condicao);
|
|
244
|
+
this.setTerminator({
|
|
245
|
+
kind: 'CondBranch',
|
|
246
|
+
condition,
|
|
247
|
+
trueBlock: bodyBlock.label,
|
|
248
|
+
falseBlock: exitBlock.label
|
|
249
|
+
});
|
|
250
|
+
// Body: gera instruções → Branch para header
|
|
251
|
+
this.switchToBlock(bodyBlock);
|
|
252
|
+
this.generateBloco(node.corpo);
|
|
253
|
+
if (this.currentBlock && !this.currentBlock.terminator) {
|
|
254
|
+
this.setTerminator({ kind: 'Branch', target: headerBlock.label });
|
|
255
|
+
}
|
|
256
|
+
// Continua no exit
|
|
257
|
+
this.switchToBlock(exitBlock);
|
|
258
|
+
}
|
|
259
|
+
generatePara(node) {
|
|
260
|
+
// 1. Obter a lista e seu tamanho
|
|
261
|
+
const lista = this.generateExpressao(node.iteravel);
|
|
262
|
+
const lenVar = this.newTemp();
|
|
263
|
+
this.emit({
|
|
264
|
+
kind: 'Call',
|
|
265
|
+
result: lenVar,
|
|
266
|
+
callee: '@jade_lista_tamanho',
|
|
267
|
+
args: [lista],
|
|
268
|
+
returnType: 'i32'
|
|
269
|
+
});
|
|
270
|
+
// 2. Inicializar contador %i = 0
|
|
271
|
+
const iVar = this.newTemp();
|
|
272
|
+
this.emit({
|
|
273
|
+
kind: 'Store',
|
|
274
|
+
target: iVar,
|
|
275
|
+
value: { kind: 'Constant', type: 'i32', value: 0 },
|
|
276
|
+
type: 'i32'
|
|
277
|
+
});
|
|
278
|
+
// 3. Criar blocos
|
|
279
|
+
const headerBlock = this.createBlock(this.newBlockLabel('loop_header'));
|
|
280
|
+
const bodyBlock = this.createBlock(this.newBlockLabel('loop_body'));
|
|
281
|
+
const exitBlock = this.createBlock(this.newBlockLabel('loop_exit'));
|
|
282
|
+
// Branch para header
|
|
283
|
+
this.setTerminator({ kind: 'Branch', target: headerBlock.label });
|
|
284
|
+
// 4. loop_header: condição %i < %len → body, senão → exit
|
|
285
|
+
this.switchToBlock(headerBlock);
|
|
286
|
+
const iLoadHeader = this.newTemp();
|
|
287
|
+
this.emit({
|
|
288
|
+
kind: 'Load',
|
|
289
|
+
result: iLoadHeader,
|
|
290
|
+
source: iVar,
|
|
291
|
+
type: 'i32'
|
|
292
|
+
});
|
|
293
|
+
const condVar = this.newTemp();
|
|
294
|
+
this.emit({
|
|
295
|
+
kind: 'BinaryOp',
|
|
296
|
+
result: condVar,
|
|
297
|
+
op: 'lt',
|
|
298
|
+
left: { kind: 'LocalRef', name: iLoadHeader, type: 'i32' },
|
|
299
|
+
right: { kind: 'LocalRef', name: lenVar, type: 'i32' },
|
|
300
|
+
type: 'i1'
|
|
301
|
+
});
|
|
302
|
+
this.setTerminator({
|
|
303
|
+
kind: 'CondBranch',
|
|
304
|
+
condition: { kind: 'LocalRef', name: condVar, type: 'i1' },
|
|
305
|
+
trueBlock: bodyBlock.label,
|
|
306
|
+
falseBlock: exitBlock.label
|
|
307
|
+
});
|
|
308
|
+
// 5. loop_body: obter elemento, gerar corpo, incrementar %i
|
|
309
|
+
this.switchToBlock(bodyBlock);
|
|
310
|
+
const iLoadBody = this.newTemp();
|
|
311
|
+
this.emit({
|
|
312
|
+
kind: 'Load',
|
|
313
|
+
result: iLoadBody,
|
|
314
|
+
source: iVar,
|
|
315
|
+
type: 'i32'
|
|
316
|
+
});
|
|
317
|
+
const elemVar = this.newTemp();
|
|
318
|
+
this.emit({
|
|
319
|
+
kind: 'Call',
|
|
320
|
+
result: elemVar,
|
|
321
|
+
callee: '@jade_lista_obter',
|
|
322
|
+
args: [lista, { kind: 'LocalRef', name: iLoadBody, type: 'i32' }],
|
|
323
|
+
returnType: 'ptr'
|
|
324
|
+
});
|
|
325
|
+
this.emit({
|
|
326
|
+
kind: 'Store',
|
|
327
|
+
target: '%' + node.variavel,
|
|
328
|
+
value: { kind: 'LocalRef', name: elemVar, type: 'ptr' },
|
|
329
|
+
type: 'ptr'
|
|
330
|
+
});
|
|
331
|
+
this.generateBloco(node.corpo);
|
|
332
|
+
if (this.currentBlock && this.currentBlock.terminator.kind === 'Unreachable') {
|
|
333
|
+
const iLoadInc = this.newTemp();
|
|
334
|
+
this.emit({
|
|
335
|
+
kind: 'Load',
|
|
336
|
+
result: iLoadInc,
|
|
337
|
+
source: iVar,
|
|
338
|
+
type: 'i32'
|
|
339
|
+
});
|
|
340
|
+
const iPlusOne = this.newTemp();
|
|
341
|
+
this.emit({
|
|
342
|
+
kind: 'BinaryOp',
|
|
343
|
+
result: iPlusOne,
|
|
344
|
+
op: 'add',
|
|
345
|
+
left: { kind: 'LocalRef', name: iLoadInc, type: 'i32' },
|
|
346
|
+
right: { kind: 'Constant', type: 'i32', value: 1 },
|
|
347
|
+
type: 'i32'
|
|
348
|
+
});
|
|
349
|
+
this.emit({
|
|
350
|
+
kind: 'Store',
|
|
351
|
+
target: iVar,
|
|
352
|
+
value: { kind: 'LocalRef', name: iPlusOne, type: 'i32' },
|
|
353
|
+
type: 'i32'
|
|
354
|
+
});
|
|
355
|
+
this.setTerminator({ kind: 'Branch', target: headerBlock.label });
|
|
356
|
+
}
|
|
357
|
+
// 6. Continua no exit
|
|
358
|
+
this.switchToBlock(exitBlock);
|
|
359
|
+
}
|
|
360
|
+
generateRetorno(node) {
|
|
361
|
+
// Se tem valor: gera expressão → Return com valor
|
|
362
|
+
// Se não tem: Return sem valor
|
|
363
|
+
if (node.valor) {
|
|
364
|
+
const value = this.generateExpressao(node.valor);
|
|
365
|
+
this.setTerminator({ kind: 'Return', value });
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
this.setTerminator({ kind: 'Return' });
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
generateErro(node) {
|
|
372
|
+
// Emite Call para função runtime @jade_erro
|
|
373
|
+
const mensagem = this.generateExpressao(node.mensagem);
|
|
374
|
+
this.emit({
|
|
375
|
+
kind: 'Call',
|
|
376
|
+
callee: '@jade_erro',
|
|
377
|
+
args: [mensagem],
|
|
378
|
+
returnType: 'void'
|
|
379
|
+
});
|
|
380
|
+
// Emite Unreachable (erro nunca retorna)
|
|
381
|
+
this.setTerminator({ kind: 'Unreachable' });
|
|
382
|
+
}
|
|
383
|
+
generateEmissaoEvento(node) {
|
|
384
|
+
// Emite Call para função runtime @jade_emitir_evento
|
|
385
|
+
const args = node.argumentos.map(arg => this.generateExpressao(arg));
|
|
386
|
+
this.emit({
|
|
387
|
+
kind: 'Call',
|
|
388
|
+
callee: '@jade_emitir_evento',
|
|
389
|
+
args: [{ kind: 'Constant', type: 'ptr', value: node.evento }, ...args],
|
|
390
|
+
returnType: 'void'
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
// ── Geradores de expressão (retornam IRValue) ────────────
|
|
394
|
+
generateExpressao(node) {
|
|
395
|
+
switch (node.kind) {
|
|
396
|
+
case 'Literal':
|
|
397
|
+
return this.generateLiteral(node);
|
|
398
|
+
case 'Identificador':
|
|
399
|
+
return this.generateIdentificador(node);
|
|
400
|
+
case 'Binario':
|
|
401
|
+
return this.generateBinario(node);
|
|
402
|
+
case 'Unario':
|
|
403
|
+
return this.generateUnario(node);
|
|
404
|
+
case 'ChamadaFuncao':
|
|
405
|
+
return this.generateChamadaFuncao(node);
|
|
406
|
+
case 'AcessoMembro':
|
|
407
|
+
return this.generateAcessoMembro(node);
|
|
408
|
+
case 'Atribuicao':
|
|
409
|
+
this.generateAtribuicao(node);
|
|
410
|
+
// Retornar valor atribuído
|
|
411
|
+
return this.generateExpressao(node.valor);
|
|
412
|
+
default:
|
|
413
|
+
throw new Error(`Expressão não suportada: ${node.kind}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
generateLiteral(node) {
|
|
417
|
+
// Retorna IRConstant com o valor e tipo corretos
|
|
418
|
+
let type;
|
|
419
|
+
let value = node.valor;
|
|
420
|
+
switch (node.tipoLiteral) {
|
|
421
|
+
case 'texto':
|
|
422
|
+
type = 'ptr';
|
|
423
|
+
break;
|
|
424
|
+
case 'numero':
|
|
425
|
+
type = 'i32';
|
|
426
|
+
value = Number(node.valor);
|
|
427
|
+
break;
|
|
428
|
+
case 'decimal':
|
|
429
|
+
type = 'f64';
|
|
430
|
+
value = Number(node.valor);
|
|
431
|
+
break;
|
|
432
|
+
case 'booleano':
|
|
433
|
+
type = 'i1';
|
|
434
|
+
value = Boolean(node.valor);
|
|
435
|
+
break;
|
|
436
|
+
case 'data':
|
|
437
|
+
case 'hora':
|
|
438
|
+
type = 'ptr';
|
|
439
|
+
break;
|
|
440
|
+
default:
|
|
441
|
+
type = 'ptr';
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
kind: 'Constant',
|
|
445
|
+
type,
|
|
446
|
+
value
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
generateIdentificador(node) {
|
|
450
|
+
// Emite Load da variável local
|
|
451
|
+
const result = this.newTemp();
|
|
452
|
+
// Busca tipo nos parâmetros da função atual
|
|
453
|
+
const param = this.currentFunction?.parameters.find(p => p.name === '%' + node.nome || p.name === node.nome);
|
|
454
|
+
// Busca tipo nas variáveis locais da função atual
|
|
455
|
+
const local = this.currentFunction?.locals.find(l => l.name === '%' + node.nome || l.name === node.nome);
|
|
456
|
+
const type = param?.type || local?.type || 'i32';
|
|
457
|
+
this.emit({
|
|
458
|
+
kind: 'Load',
|
|
459
|
+
result,
|
|
460
|
+
source: '%' + node.nome,
|
|
461
|
+
type
|
|
462
|
+
});
|
|
463
|
+
return {
|
|
464
|
+
kind: 'LocalRef',
|
|
465
|
+
name: result,
|
|
466
|
+
type
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
generateBinario(node) {
|
|
470
|
+
// Gera esquerda e direita
|
|
471
|
+
const left = this.generateExpressao(node.esquerda);
|
|
472
|
+
const right = this.generateExpressao(node.direita);
|
|
473
|
+
const result = this.newTemp();
|
|
474
|
+
const op = this.mapOperator(node.operador);
|
|
475
|
+
// Concatenação de texto: op '+' com operando esquerdo do tipo 'ptr'
|
|
476
|
+
if (op === 'add' && left.type === 'ptr') {
|
|
477
|
+
this.emit({
|
|
478
|
+
kind: 'Call',
|
|
479
|
+
result,
|
|
480
|
+
callee: '@jade_concat',
|
|
481
|
+
args: [left, right],
|
|
482
|
+
returnType: 'ptr'
|
|
483
|
+
});
|
|
484
|
+
return { kind: 'LocalRef', name: result, type: 'ptr' };
|
|
485
|
+
}
|
|
486
|
+
// Propaga tipo do operando esquerdo
|
|
487
|
+
const type = left.type !== 'void' ? left.type : right.type !== 'void' ? right.type : 'i32';
|
|
488
|
+
// Operadores de comparação sempre retornam i1
|
|
489
|
+
const resultType = ['eq', 'ne', 'lt', 'le', 'gt', 'ge', 'and', 'or'].includes(op) ? 'i1' : type;
|
|
490
|
+
this.emit({
|
|
491
|
+
kind: 'BinaryOp',
|
|
492
|
+
result,
|
|
493
|
+
op,
|
|
494
|
+
left,
|
|
495
|
+
right,
|
|
496
|
+
type: resultType
|
|
497
|
+
});
|
|
498
|
+
return {
|
|
499
|
+
kind: 'LocalRef',
|
|
500
|
+
name: result,
|
|
501
|
+
type: resultType
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
generateUnario(node) {
|
|
505
|
+
const operand = this.generateExpressao(node.operando);
|
|
506
|
+
const result = this.newTemp();
|
|
507
|
+
const op = node.operador === '-' ? 'neg' : 'not';
|
|
508
|
+
const type = operand.type !== 'void' ? operand.type : 'i32';
|
|
509
|
+
const resultType = op === 'not' ? 'i1' : type;
|
|
510
|
+
this.emit({
|
|
511
|
+
kind: 'UnaryOp',
|
|
512
|
+
result,
|
|
513
|
+
op,
|
|
514
|
+
operand,
|
|
515
|
+
type: resultType
|
|
516
|
+
});
|
|
517
|
+
return {
|
|
518
|
+
kind: 'LocalRef',
|
|
519
|
+
name: result,
|
|
520
|
+
type: resultType
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
generateChamadaFuncao(node) {
|
|
524
|
+
const args = node.argumentos.map(arg => this.generateExpressao(arg));
|
|
525
|
+
const result = this.newTemp();
|
|
526
|
+
// Busca o tipo de retorno na lista de funções do módulo
|
|
527
|
+
const funcIR = this.module.functions.find(f => f.name === '@' + node.nome || f.name === node.nome);
|
|
528
|
+
const returnType = funcIR?.returnType || 'void';
|
|
529
|
+
this.emit({
|
|
530
|
+
kind: 'Call',
|
|
531
|
+
result: returnType === 'void' ? undefined : result,
|
|
532
|
+
callee: '@' + node.nome,
|
|
533
|
+
args,
|
|
534
|
+
returnType
|
|
535
|
+
});
|
|
536
|
+
if (returnType === 'void') {
|
|
537
|
+
return { kind: 'Constant', type: 'void', value: 0 };
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
kind: 'LocalRef',
|
|
541
|
+
name: result,
|
|
542
|
+
type: returnType
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
generateAcessoMembro(node) {
|
|
546
|
+
// Gera objeto
|
|
547
|
+
const object = this.generateExpressao(node.objeto);
|
|
548
|
+
// Emite GetField
|
|
549
|
+
const result = this.newTemp();
|
|
550
|
+
const type = 'void'; // TODO: Obter tipo do campo
|
|
551
|
+
this.emit({
|
|
552
|
+
kind: 'GetField',
|
|
553
|
+
result,
|
|
554
|
+
object,
|
|
555
|
+
field: node.membro,
|
|
556
|
+
type
|
|
557
|
+
});
|
|
558
|
+
return {
|
|
559
|
+
kind: 'LocalRef',
|
|
560
|
+
name: result,
|
|
561
|
+
type
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
// ── Utilitários ──────────────────────────────────────────
|
|
565
|
+
newTemp() {
|
|
566
|
+
return `%t${this.tempCounter++}`;
|
|
567
|
+
}
|
|
568
|
+
newBlockLabel(prefix) {
|
|
569
|
+
return `${prefix}_${this.blockCounter++}`;
|
|
570
|
+
}
|
|
571
|
+
emit(instruction) {
|
|
572
|
+
if (!this.currentBlock) {
|
|
573
|
+
throw new Error('Não há bloco atual para emitir instrução');
|
|
574
|
+
}
|
|
575
|
+
this.currentBlock.instructions.push(instruction);
|
|
576
|
+
this.declareLocalIfNeeded(instruction);
|
|
577
|
+
}
|
|
578
|
+
declareLocalIfNeeded(instr) {
|
|
579
|
+
if (!this.currentFunction)
|
|
580
|
+
return;
|
|
581
|
+
let name;
|
|
582
|
+
let type;
|
|
583
|
+
switch (instr.kind) {
|
|
584
|
+
case 'BinaryOp':
|
|
585
|
+
name = instr.result;
|
|
586
|
+
type = instr.type;
|
|
587
|
+
break;
|
|
588
|
+
case 'UnaryOp':
|
|
589
|
+
name = instr.result;
|
|
590
|
+
type = instr.type;
|
|
591
|
+
break;
|
|
592
|
+
case 'Load':
|
|
593
|
+
name = instr.result;
|
|
594
|
+
type = instr.type;
|
|
595
|
+
break;
|
|
596
|
+
case 'GetField':
|
|
597
|
+
name = instr.result;
|
|
598
|
+
type = instr.type;
|
|
599
|
+
break;
|
|
600
|
+
case 'Assign':
|
|
601
|
+
name = instr.result;
|
|
602
|
+
type = instr.type;
|
|
603
|
+
break;
|
|
604
|
+
case 'Alloc':
|
|
605
|
+
name = instr.result;
|
|
606
|
+
type = 'ptr';
|
|
607
|
+
break;
|
|
608
|
+
case 'Call':
|
|
609
|
+
if (instr.result) {
|
|
610
|
+
name = instr.result;
|
|
611
|
+
type = instr.returnType;
|
|
612
|
+
}
|
|
613
|
+
break;
|
|
614
|
+
case 'Store':
|
|
615
|
+
name = instr.target;
|
|
616
|
+
type = instr.type;
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
if (!name || !type || type === 'void')
|
|
620
|
+
return;
|
|
621
|
+
const isParam = this.currentFunction.parameters.some(p => p.name === name);
|
|
622
|
+
if (isParam)
|
|
623
|
+
return;
|
|
624
|
+
const alreadyDeclared = this.currentFunction.locals.some(l => l.name === name);
|
|
625
|
+
if (alreadyDeclared)
|
|
626
|
+
return;
|
|
627
|
+
this.currentFunction.locals.push({ name, type });
|
|
628
|
+
}
|
|
629
|
+
setTerminator(terminator) {
|
|
630
|
+
if (!this.currentBlock) {
|
|
631
|
+
throw new Error('Não há bloco atual para definir terminador');
|
|
632
|
+
}
|
|
633
|
+
this.currentBlock.terminator = terminator;
|
|
634
|
+
}
|
|
635
|
+
switchToBlock(block) {
|
|
636
|
+
this.currentBlock = block;
|
|
637
|
+
}
|
|
638
|
+
createBlock(label) {
|
|
639
|
+
if (!this.currentFunction) {
|
|
640
|
+
throw new Error('Não há função atual para criar bloco');
|
|
641
|
+
}
|
|
642
|
+
const block = {
|
|
643
|
+
label,
|
|
644
|
+
instructions: [],
|
|
645
|
+
terminator: { kind: 'Unreachable' }
|
|
646
|
+
};
|
|
647
|
+
this.currentFunction.blocks.push(block);
|
|
648
|
+
return block;
|
|
649
|
+
}
|
|
650
|
+
jadeTypeToIR(type) {
|
|
651
|
+
switch (type.kind) {
|
|
652
|
+
case 'TipoSimples':
|
|
653
|
+
const nome = type.nome;
|
|
654
|
+
// Mapeamento tipo JADE → IRType
|
|
655
|
+
switch (nome) {
|
|
656
|
+
case 'numero': return 'i32';
|
|
657
|
+
case 'decimal': return 'f64';
|
|
658
|
+
case 'booleano': return 'i1';
|
|
659
|
+
case 'texto': return 'ptr';
|
|
660
|
+
case 'id': return 'ptr';
|
|
661
|
+
case 'data': return 'ptr';
|
|
662
|
+
case 'hora': return 'ptr';
|
|
663
|
+
default: return 'ptr'; // classes/entidades são ponteiros
|
|
664
|
+
}
|
|
665
|
+
case 'TipoLista':
|
|
666
|
+
case 'TipoMapa':
|
|
667
|
+
case 'TipoObjeto':
|
|
668
|
+
return 'ptr';
|
|
669
|
+
default:
|
|
670
|
+
return 'ptr';
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
mapOperator(op) {
|
|
674
|
+
switch (op) {
|
|
675
|
+
case '+': return 'add';
|
|
676
|
+
case '-': return 'sub';
|
|
677
|
+
case '*': return 'mul';
|
|
678
|
+
case '/': return 'div';
|
|
679
|
+
case '==': return 'eq';
|
|
680
|
+
case '!=': return 'ne';
|
|
681
|
+
case '<': return 'lt';
|
|
682
|
+
case '<=': return 'le';
|
|
683
|
+
case '>': return 'gt';
|
|
684
|
+
case '>=': return 'ge';
|
|
685
|
+
case 'e': return 'and';
|
|
686
|
+
case 'ou': return 'or';
|
|
687
|
+
default: throw new Error(`Operador não suportado: ${op}`);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
//# sourceMappingURL=ir_generator.js.map
|