@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.
Files changed (65) hide show
  1. package/dist/ast/nodes.d.ts +219 -0
  2. package/dist/ast/nodes.d.ts.map +1 -0
  3. package/dist/ast/nodes.js +2 -0
  4. package/dist/ast/nodes.js.map +1 -0
  5. package/dist/cli.d.ts +14 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +108 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/codegen/ir_generator.d.ts +42 -0
  10. package/dist/codegen/ir_generator.d.ts.map +1 -0
  11. package/dist/codegen/ir_generator.js +691 -0
  12. package/dist/codegen/ir_generator.js.map +1 -0
  13. package/dist/codegen/ir_nodes.d.ts +143 -0
  14. package/dist/codegen/ir_nodes.d.ts.map +1 -0
  15. package/dist/codegen/ir_nodes.js +3 -0
  16. package/dist/codegen/ir_nodes.js.map +1 -0
  17. package/dist/codegen/ir_printer.d.ts +10 -0
  18. package/dist/codegen/ir_printer.d.ts.map +1 -0
  19. package/dist/codegen/ir_printer.js +131 -0
  20. package/dist/codegen/ir_printer.js.map +1 -0
  21. package/dist/codegen/wasm_generator.d.ts +12 -0
  22. package/dist/codegen/wasm_generator.d.ts.map +1 -0
  23. package/dist/codegen/wasm_generator.js +48 -0
  24. package/dist/codegen/wasm_generator.js.map +1 -0
  25. package/dist/codegen/wat_generator.d.ts +29 -0
  26. package/dist/codegen/wat_generator.d.ts.map +1 -0
  27. package/dist/codegen/wat_generator.js +345 -0
  28. package/dist/codegen/wat_generator.js.map +1 -0
  29. package/dist/index.d.ts +61 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +79 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/lexer/lexer.d.ts +31 -0
  34. package/dist/lexer/lexer.d.ts.map +1 -0
  35. package/dist/lexer/lexer.js +475 -0
  36. package/dist/lexer/lexer.js.map +1 -0
  37. package/dist/lexer/token.d.ts +8 -0
  38. package/dist/lexer/token.d.ts.map +1 -0
  39. package/dist/lexer/token.js +2 -0
  40. package/dist/lexer/token.js.map +1 -0
  41. package/dist/lexer/token_type.d.ts +80 -0
  42. package/dist/lexer/token_type.d.ts.map +1 -0
  43. package/dist/lexer/token_type.js +98 -0
  44. package/dist/lexer/token_type.js.map +1 -0
  45. package/dist/parser/parse_result.d.ts +12 -0
  46. package/dist/parser/parse_result.d.ts.map +1 -0
  47. package/dist/parser/parse_result.js +2 -0
  48. package/dist/parser/parse_result.js.map +1 -0
  49. package/dist/parser/parser.d.ts +57 -0
  50. package/dist/parser/parser.d.ts.map +1 -0
  51. package/dist/parser/parser.js +1094 -0
  52. package/dist/parser/parser.js.map +1 -0
  53. package/dist/semantic/semantic_analyzer.d.ts +10 -0
  54. package/dist/semantic/semantic_analyzer.d.ts.map +1 -0
  55. package/dist/semantic/semantic_analyzer.js +16 -0
  56. package/dist/semantic/semantic_analyzer.js.map +1 -0
  57. package/dist/semantic/symbol_table.d.ts +33 -0
  58. package/dist/semantic/symbol_table.d.ts.map +1 -0
  59. package/dist/semantic/symbol_table.js +105 -0
  60. package/dist/semantic/symbol_table.js.map +1 -0
  61. package/dist/semantic/type_checker.d.ts +66 -0
  62. package/dist/semantic/type_checker.d.ts.map +1 -0
  63. package/dist/semantic/type_checker.js +963 -0
  64. package/dist/semantic/type_checker.js.map +1 -0
  65. package/package.json +84 -0
@@ -0,0 +1,963 @@
1
+ export class TypeChecker {
2
+ tabela;
3
+ erros = [];
4
+ funcaoAtual; // para verificar retorno
5
+ grafoEventos = new Map();
6
+ constructor(tabela) {
7
+ this.tabela = tabela;
8
+ }
9
+ // ── Built-in methods tables ──────────────────────────────
10
+ metodosTexto = {
11
+ 'maiusculo': 'texto',
12
+ 'minusculo': 'texto',
13
+ 'aparar': 'texto',
14
+ 'tamanho': 'numero',
15
+ 'contem': 'booleano',
16
+ 'comecaCom': 'booleano',
17
+ 'terminaCom': 'booleano',
18
+ 'substituir': 'texto',
19
+ 'dividir': 'lista<texto>',
20
+ 'normalizar': 'texto'
21
+ };
22
+ metodosLista = {
23
+ 'tamanho': 'numero',
24
+ 'contem': 'booleano',
25
+ 'adicionar': 'vazio',
26
+ 'remover': 'vazio',
27
+ 'obter': 'T', // tipo do elemento
28
+ 'filtrar': 'lista', // lista.filtrar(condicao) -> lista<T>
29
+ 'ordenar': 'lista', // lista.ordenar(campo) -> lista<T>
30
+ 'primeiro': 'T', // lista.primeiro() -> T
31
+ 'ultimo': 'T', // lista.ultimo() -> T
32
+ 'vazia': 'booleano' // lista.vazia() -> booleano
33
+ };
34
+ // ── Verificações principais ──────────────────────────────
35
+ verificarPrograma(node) {
36
+ // Passo 1: registrar todas as declarações de topo
37
+ this.registrarDeclaracoesTopo(node.declaracoes);
38
+ // Passo 2: verificar os corpos
39
+ for (const declaracao of node.declaracoes) {
40
+ this.verificarDeclaracao(declaracao);
41
+ }
42
+ // Passo 3: detectar ciclos de eventos
43
+ this.detectarCiclosEventos();
44
+ return this.erros;
45
+ }
46
+ // Adicionar ao final da classe TypeChecker
47
+ // Grafo de dependências de eventos
48
+ // chave: nome do evento emitido
49
+ // valor: lista de eventos que podem ser emitidos em resposta
50
+ registrarDependenciaEvento(eventoEscutado, eventoEmitido) {
51
+ if (!this.grafoEventos.has(eventoEscutado)) {
52
+ this.grafoEventos.set(eventoEscutado, new Set());
53
+ }
54
+ this.grafoEventos.get(eventoEscutado).add(eventoEmitido);
55
+ }
56
+ // DFS para detectar ciclos
57
+ detectarCiclosEventos() {
58
+ const visitados = new Set();
59
+ const emPilha = new Set();
60
+ const dfs = (evento, caminho) => {
61
+ visitados.add(evento);
62
+ emPilha.add(evento);
63
+ const vizinhos = this.grafoEventos.get(evento) ?? new Set();
64
+ for (const vizinho of vizinhos) {
65
+ if (!visitados.has(vizinho)) {
66
+ if (dfs(vizinho, [...caminho, vizinho]))
67
+ return true;
68
+ }
69
+ else if (emPilha.has(vizinho)) {
70
+ // Ciclo encontrado
71
+ const ciclo = [...caminho, vizinho].join(' → ');
72
+ this.erro(`Ciclo de eventos detectado: ${ciclo}. ` +
73
+ `Isso causaria loop infinito em runtime.`, 0, 0);
74
+ return true;
75
+ }
76
+ }
77
+ emPilha.delete(evento);
78
+ return false;
79
+ };
80
+ for (const evento of this.grafoEventos.keys()) {
81
+ if (!visitados.has(evento)) {
82
+ dfs(evento, [evento]);
83
+ }
84
+ }
85
+ }
86
+ registrarDeclaracoesTopo(declaracoes) {
87
+ for (const declaracao of declaracoes) {
88
+ switch (declaracao.kind) {
89
+ case 'Classe':
90
+ this.registrarClasse(declaracao);
91
+ break;
92
+ case 'Entidade':
93
+ this.registrarEntidade(declaracao);
94
+ break;
95
+ case 'Servico':
96
+ this.registrarServico(declaracao);
97
+ break;
98
+ case 'Evento':
99
+ this.registrarEvento(declaracao);
100
+ break;
101
+ case 'Enum':
102
+ this.registrarEnum(declaracao);
103
+ break;
104
+ case 'Interface':
105
+ this.registrarInterface(declaracao);
106
+ break;
107
+ case 'Funcao':
108
+ this.registrarFuncao(declaracao);
109
+ break;
110
+ case 'Importacao':
111
+ this.registrarImportacao(declaracao);
112
+ break;
113
+ case 'Tela':
114
+ this.registrarTela(declaracao);
115
+ break;
116
+ }
117
+ }
118
+ }
119
+ registrarImportacao(node) {
120
+ try {
121
+ if (node.alias) {
122
+ // importar financeiro como fin → registra 'fin' como namespace do módulo
123
+ this.tabela.declarar({
124
+ nome: node.alias,
125
+ kind: 'entidade',
126
+ tipo: node.modulo,
127
+ linha: node.line,
128
+ coluna: node.column,
129
+ escopo: this.tabela.escopoAtual()
130
+ });
131
+ }
132
+ else if (node.item && !node.wildcard) {
133
+ // importar estoque.Produto → registra 'Produto' como tipo externo conhecido
134
+ this.tabela.declarar({
135
+ nome: node.item,
136
+ kind: 'classe',
137
+ tipo: node.item,
138
+ linha: node.line,
139
+ coluna: node.column,
140
+ escopo: this.tabela.escopoAtual()
141
+ });
142
+ }
143
+ // Wildcard (importar vendas.*) — não é possível registrar símbolos
144
+ // específicos sem resolver o módulo; aceito silenciosamente por enquanto.
145
+ }
146
+ catch {
147
+ // Silenciar conflito de nome se o tipo já foi declarado localmente.
148
+ // A precedência é do símbolo local.
149
+ }
150
+ }
151
+ registrarClasse(node) {
152
+ const simbolo = {
153
+ nome: node.nome,
154
+ kind: 'classe',
155
+ tipo: node.nome,
156
+ linha: node.line,
157
+ coluna: node.column,
158
+ escopo: this.tabela.escopoAtual()
159
+ };
160
+ this.tabela.declarar(simbolo);
161
+ }
162
+ registrarEntidade(node) {
163
+ const simbolo = {
164
+ nome: node.nome,
165
+ kind: 'entidade',
166
+ tipo: node.nome,
167
+ linha: node.line,
168
+ coluna: node.column,
169
+ escopo: this.tabela.escopoAtual()
170
+ };
171
+ this.tabela.declarar(simbolo);
172
+ }
173
+ registrarServico(node) {
174
+ const simbolo = {
175
+ nome: node.nome,
176
+ kind: 'servico',
177
+ tipo: node.nome,
178
+ linha: node.line,
179
+ coluna: node.column,
180
+ escopo: this.tabela.escopoAtual()
181
+ };
182
+ this.tabela.declarar(simbolo);
183
+ }
184
+ registrarEvento(node) {
185
+ const simbolo = {
186
+ nome: node.nome,
187
+ kind: 'evento',
188
+ tipo: node.nome,
189
+ linha: node.line,
190
+ coluna: node.column,
191
+ escopo: this.tabela.escopoAtual()
192
+ };
193
+ this.tabela.declarar(simbolo);
194
+ }
195
+ registrarEnum(node) {
196
+ const simbolo = {
197
+ nome: node.nome,
198
+ kind: 'enum',
199
+ tipo: node.nome,
200
+ linha: node.line,
201
+ coluna: node.column,
202
+ escopo: this.tabela.escopoAtual()
203
+ };
204
+ this.tabela.declarar(simbolo);
205
+ // Registrar valores do enum
206
+ for (const valor of node.valores) {
207
+ const simboloValor = {
208
+ nome: valor,
209
+ kind: 'enum_valor',
210
+ tipo: node.nome,
211
+ linha: node.line,
212
+ coluna: node.column,
213
+ escopo: this.tabela.escopoAtual()
214
+ };
215
+ this.tabela.declarar(simboloValor);
216
+ }
217
+ }
218
+ registrarInterface(node) {
219
+ const simbolo = {
220
+ nome: node.nome,
221
+ kind: 'interface',
222
+ tipo: node.nome,
223
+ linha: node.line,
224
+ coluna: node.column,
225
+ escopo: this.tabela.escopoAtual()
226
+ };
227
+ this.tabela.declarar(simbolo);
228
+ }
229
+ registrarFuncao(node) {
230
+ const simbolo = {
231
+ nome: node.nome,
232
+ kind: 'funcao',
233
+ tipo: node.tipoRetorno ? this.tipoParaString(node.tipoRetorno) : 'vazio',
234
+ linha: node.line,
235
+ coluna: node.column,
236
+ escopo: this.tabela.escopoAtual()
237
+ };
238
+ this.tabela.declarar(simbolo);
239
+ }
240
+ // Declarações
241
+ verificarClasse(node) {
242
+ this.tabela.entrarEscopo(node.nome);
243
+ // Verificar superclasse
244
+ if (node.superClasse) {
245
+ if (!this.tipoExiste(node.superClasse)) {
246
+ this.erro(`Superclasse '${node.superClasse}' não encontrada`, node.line, node.column);
247
+ }
248
+ else {
249
+ // Registrar superclasse para verificação de herança
250
+ this.tabela.registrarSuperClasse(node.nome, node.superClasse);
251
+ }
252
+ }
253
+ // Verificar interfaces
254
+ for (const interfaceNome of node.interfaces) {
255
+ if (!this.tipoExiste(interfaceNome)) {
256
+ this.erro(`Interface '${interfaceNome}' não encontrada`, node.line, node.column);
257
+ }
258
+ }
259
+ // Registrar campos e métodos
260
+ for (const campo of node.campos) {
261
+ const tipoCampo = this.tipoParaString(campo.tipo);
262
+ const simbolo = {
263
+ nome: campo.nome,
264
+ kind: 'variavel',
265
+ tipo: tipoCampo,
266
+ linha: campo.line,
267
+ coluna: campo.column,
268
+ escopo: this.tabela.escopoAtual()
269
+ };
270
+ this.tabela.declarar(simbolo);
271
+ this.tabela.registrarCampo(node.nome, campo.nome, tipoCampo);
272
+ }
273
+ for (const metodo of node.metodos) {
274
+ this.verificarFuncao(metodo);
275
+ }
276
+ this.tabela.sairEscopo();
277
+ }
278
+ verificarEntidade(node) {
279
+ this.tabela.entrarEscopo(node.nome);
280
+ let temId = false;
281
+ for (const campo of node.campos) {
282
+ const tipoCampo = this.tipoParaString(campo.tipo);
283
+ if (tipoCampo === 'id') {
284
+ temId = true;
285
+ }
286
+ if (!this.tipoExiste(tipoCampo)) {
287
+ this.erro(`Tipo '${tipoCampo}' não existe`, campo.line, campo.column);
288
+ }
289
+ const simbolo = {
290
+ nome: campo.nome,
291
+ kind: 'variavel',
292
+ tipo: tipoCampo,
293
+ linha: campo.line,
294
+ coluna: campo.column,
295
+ escopo: this.tabela.escopoAtual()
296
+ };
297
+ this.tabela.declarar(simbolo);
298
+ // Registra permanentemente para acesso a membro
299
+ this.tabela.registrarCampo(node.nome, campo.nome, tipoCampo);
300
+ }
301
+ if (!temId) {
302
+ this.erro(`Entidade '${node.nome}' deve ter exatamente um campo do tipo 'id'`, node.line, node.column);
303
+ }
304
+ this.tabela.sairEscopo();
305
+ }
306
+ verificarServico(node) {
307
+ this.tabela.entrarEscopo(node.nome);
308
+ for (const metodo of node.metodos) {
309
+ this.verificarFuncao(metodo);
310
+ }
311
+ for (const ouvinte of node.ouvintes) {
312
+ this.verificarOuvinte(ouvinte);
313
+ }
314
+ this.tabela.sairEscopo();
315
+ }
316
+ verificarFuncao(node) {
317
+ const funcaoAnterior = this.funcaoAtual;
318
+ this.funcaoAtual = node;
319
+ this.tabela.entrarEscopo(node.nome);
320
+ // Registrar parâmetros
321
+ const tiposParams = [];
322
+ for (const parametro of node.parametros) {
323
+ const tipoParam = this.tipoParaString(parametro.tipo);
324
+ if (!this.tipoExiste(tipoParam)) {
325
+ this.erro(`Tipo '${tipoParam}' não existe`, parametro.line, parametro.column);
326
+ }
327
+ tiposParams.push(tipoParam);
328
+ const simbolo = {
329
+ nome: parametro.nome,
330
+ kind: 'parametro',
331
+ tipo: tipoParam,
332
+ linha: parametro.line,
333
+ coluna: parametro.column,
334
+ escopo: this.tabela.escopoAtual()
335
+ };
336
+ this.tabela.declarar(simbolo);
337
+ }
338
+ // Guarda parâmetros permanentemente para verificação de chamadas
339
+ this.tabela.registrarParametrosFuncao(node.nome, tiposParams);
340
+ // Verificar corpo
341
+ this.verificarBloco(node.corpo);
342
+ // Verificar retorno
343
+ if (node.tipoRetorno) {
344
+ const tipoRetorno = this.tipoParaString(node.tipoRetorno);
345
+ // Se tipoRetorno != void, verificar se bloco termina com Retorno
346
+ if (tipoRetorno !== 'vazio') {
347
+ if (!this.verificarRetornoEmTodosCaminhos(node.corpo)) {
348
+ this.erro(`Função '${node.nome}' deve retornar valor em todos os caminhos`, node.line, node.column);
349
+ }
350
+ }
351
+ }
352
+ this.tabela.sairEscopo();
353
+ this.funcaoAtual = funcaoAnterior;
354
+ }
355
+ verificarOuvinte(node) {
356
+ // Verificar se evento existe
357
+ const evento = this.tabela.buscar(node.evento);
358
+ if (!evento || evento.kind !== 'evento') {
359
+ this.erro(`Evento '${node.evento}' não encontrado`, node.line, node.column);
360
+ return; // Retorna para não continuar com um evento inválido
361
+ }
362
+ // Entrar no escopo do ouvinte
363
+ this.tabela.entrarEscopo(`ouvinte_${node.evento}`);
364
+ // Declarar os campos do evento como variáveis disponíveis no escopo
365
+ const camposEvento = this.tabela.buscarCamposEvento(node.evento);
366
+ if (camposEvento) {
367
+ for (const [nomeCampo, tipoCampo] of camposEvento.entries()) {
368
+ const simboloCampo = {
369
+ nome: nomeCampo,
370
+ kind: 'variavel',
371
+ tipo: tipoCampo,
372
+ linha: node.line,
373
+ coluna: node.column,
374
+ escopo: this.tabela.escopoAtual()
375
+ };
376
+ this.tabela.declarar(simboloCampo);
377
+ }
378
+ }
379
+ // Registrar dependências de eventos para detecção de ciclo
380
+ const eventoEscutado = node.evento;
381
+ for (const instrucao of node.corpo.instrucoes) {
382
+ if (instrucao.kind === 'EmissaoEvento') {
383
+ const emissao = instrucao;
384
+ this.registrarDependenciaEvento(eventoEscutado, emissao.evento);
385
+ }
386
+ }
387
+ this.verificarBloco(node.corpo);
388
+ // Sair do escopo do ouvinte
389
+ this.tabela.sairEscopo();
390
+ }
391
+ verificarEvento(node) {
392
+ this.tabela.entrarEscopo(node.nome);
393
+ for (const campo of node.campos) {
394
+ const tipoCampo = this.tipoParaString(campo.tipo);
395
+ if (!this.tipoExiste(tipoCampo)) {
396
+ this.erro(`Tipo '${tipoCampo}' não existe`, campo.line, campo.column);
397
+ }
398
+ const simbolo = {
399
+ nome: campo.nome,
400
+ kind: 'variavel',
401
+ tipo: tipoCampo,
402
+ linha: campo.line,
403
+ coluna: campo.column,
404
+ escopo: this.tabela.escopoAtual()
405
+ };
406
+ this.tabela.declarar(simbolo);
407
+ // Registrar campo permanentemente para verificação de emissão de eventos
408
+ this.tabela.registrarCampo(node.nome, campo.nome, tipoCampo);
409
+ }
410
+ this.tabela.sairEscopo();
411
+ }
412
+ verificarEnum(node) {
413
+ // Nada a verificar além do registro já feito
414
+ }
415
+ verificarInterface(node) {
416
+ this.tabela.entrarEscopo(node.nome);
417
+ for (const assinatura of node.assinaturas) {
418
+ const simbolo = {
419
+ nome: assinatura.nome,
420
+ kind: 'funcao',
421
+ tipo: this.tipoParaString(assinatura.tipoRetorno),
422
+ linha: assinatura.line,
423
+ coluna: assinatura.column,
424
+ escopo: this.tabela.escopoAtual()
425
+ };
426
+ this.tabela.declarar(simbolo);
427
+ }
428
+ this.tabela.sairEscopo();
429
+ }
430
+ verificarRegra(node) {
431
+ // Verificar se a condição é booleana
432
+ const tipoCondicao = this.resolverTipo(node.condicao);
433
+ if (tipoCondicao !== 'booleano') {
434
+ this.erro(`Condição da regra '${node.nome}' deve ser booleana, recebido '${tipoCondicao}'`, node.condicao.line, node.condicao.column);
435
+ }
436
+ // Verificar o bloco então
437
+ this.verificarBloco(node.entao);
438
+ // Verificar o bloco senão (se existir)
439
+ if (node.senao) {
440
+ this.verificarBloco(node.senao);
441
+ }
442
+ }
443
+ verificarImportacao(_node) {
444
+ // O registro do símbolo importado já é feito em registrarDeclaracoesTopo.
445
+ // A verificação completa (se o módulo existe, se o item exportado existe)
446
+ // requer o sistema de resolução de módulos — implementado na v0.2.0.
447
+ }
448
+ // Instruções
449
+ verificarDeclaracao(declaracao) {
450
+ switch (declaracao.kind) {
451
+ case 'Classe':
452
+ this.verificarClasse(declaracao);
453
+ break;
454
+ case 'Entidade':
455
+ this.verificarEntidade(declaracao);
456
+ break;
457
+ case 'Servico':
458
+ this.verificarServico(declaracao);
459
+ break;
460
+ case 'Funcao':
461
+ this.verificarFuncao(declaracao);
462
+ break;
463
+ case 'Evento':
464
+ this.verificarEvento(declaracao);
465
+ break;
466
+ case 'Enum':
467
+ this.verificarEnum(declaracao);
468
+ break;
469
+ case 'Interface':
470
+ this.verificarInterface(declaracao);
471
+ break;
472
+ case 'Importacao':
473
+ this.verificarImportacao(declaracao);
474
+ break;
475
+ case 'Variavel':
476
+ this.verificarVariavel(declaracao);
477
+ break;
478
+ case 'Regra':
479
+ this.verificarRegra(declaracao);
480
+ break;
481
+ case 'Tela':
482
+ this.verificarTela(declaracao);
483
+ break;
484
+ }
485
+ }
486
+ verificarBloco(node) {
487
+ for (const instrucao of node.instrucoes) {
488
+ this.verificarInstrucao(instrucao);
489
+ }
490
+ }
491
+ verificarVariavel(node) {
492
+ let tipoDeclarado;
493
+ if (node.tipo) {
494
+ tipoDeclarado = this.tipoParaString(node.tipo);
495
+ if (!this.tipoExiste(tipoDeclarado)) {
496
+ this.erro(`Tipo '${tipoDeclarado}' não existe`, node.line, node.column);
497
+ }
498
+ }
499
+ if (node.inicializador) {
500
+ const tipoInicializador = this.resolverTipo(node.inicializador);
501
+ if (tipoDeclarado && !this.tiposCompatíveis(tipoDeclarado, tipoInicializador)) {
502
+ this.erro(`Tipo incompatível: esperado '${tipoDeclarado}', recebido '${tipoInicializador}'`, node.line, node.column);
503
+ }
504
+ if (!tipoDeclarado) {
505
+ tipoDeclarado = tipoInicializador;
506
+ }
507
+ }
508
+ else if (!tipoDeclarado) {
509
+ this.erro(`Variável '${node.nome}' precisa de tipo ou inicializador`, node.line, node.column);
510
+ }
511
+ const simbolo = {
512
+ nome: node.nome,
513
+ kind: 'variavel',
514
+ tipo: tipoDeclarado || 'desconhecido',
515
+ linha: node.line,
516
+ coluna: node.column,
517
+ escopo: this.tabela.escopoAtual()
518
+ };
519
+ try {
520
+ this.tabela.declarar(simbolo);
521
+ }
522
+ catch (e) {
523
+ this.erro(e.message, node.line, node.column);
524
+ }
525
+ }
526
+ verificarAtribuicao(node) {
527
+ const tipoValor = this.resolverTipo(node.valor);
528
+ if (typeof node.alvo === 'string') {
529
+ const simbolo = this.tabela.buscar(node.alvo);
530
+ if (!simbolo) {
531
+ this.erro(`Variável '${node.alvo}' não declarada`, node.line, node.column);
532
+ return;
533
+ }
534
+ if (!this.tiposCompatíveis(simbolo.tipo, tipoValor)) {
535
+ this.erro(`Tipo incompatível: esperado '${simbolo.tipo}', recebido '${tipoValor}'`, node.line, node.column);
536
+ }
537
+ }
538
+ else {
539
+ // Acesso a membro: produto.estoque = x
540
+ const tipoObjeto = this.resolverTipo(node.alvo.objeto);
541
+ const objetoSimbolo = this.tabela.buscar(tipoObjeto);
542
+ if (!objetoSimbolo || (objetoSimbolo.kind !== 'classe' && objetoSimbolo.kind !== 'entidade')) {
543
+ this.erro(`'${tipoObjeto}' não é uma classe ou entidade`, node.alvo.line, node.alvo.column);
544
+ return;
545
+ }
546
+ // Verificar se campo existe e tipo compatível
547
+ const tipoCampo = this.tabela.buscarCampo(tipoObjeto, node.alvo.membro);
548
+ if (tipoCampo === null) {
549
+ this.erro(`'${tipoObjeto}' não possui campo '${node.alvo.membro}'`, node.alvo.line, node.alvo.column);
550
+ return;
551
+ }
552
+ if (!this.tiposCompatíveis(tipoCampo, tipoValor)) {
553
+ this.erro(`Tipo incompatível: campo '${node.alvo.membro}' espera '${tipoCampo}', recebido '${tipoValor}'`, node.line, node.column);
554
+ }
555
+ }
556
+ }
557
+ verificarCondicional(node) {
558
+ const tipoCondicao = this.resolverTipo(node.condicao);
559
+ if (tipoCondicao !== 'booleano') {
560
+ this.erro(`Condição do 'se' deve ser booleano, recebeu '${tipoCondicao}'`, node.condicao.line, node.condicao.column);
561
+ }
562
+ this.verificarBloco(node.entao);
563
+ if (node.senao) {
564
+ this.verificarBloco(node.senao);
565
+ }
566
+ }
567
+ verificarEnquanto(node) {
568
+ const tipoCondicao = this.resolverTipo(node.condicao);
569
+ if (tipoCondicao !== 'booleano') {
570
+ this.erro(`Condição do 'enquanto' deve ser booleano, recebeu '${tipoCondicao}'`, node.condicao.line, node.condicao.column);
571
+ }
572
+ this.verificarBloco(node.corpo);
573
+ }
574
+ verificarPara(node) {
575
+ const tipoIteravel = this.resolverTipo(node.iteravel);
576
+ if (!tipoIteravel.startsWith('lista<')) {
577
+ this.erro(`Iterável do 'para' deve ser do tipo 'lista<T>', recebeu '${tipoIteravel}'`, node.iteravel.line, node.iteravel.column);
578
+ return;
579
+ }
580
+ // Extrair tipo do elemento: lista<Tipo> -> Tipo
581
+ const tipoElemento = tipoIteravel.substring(6, tipoIteravel.length - 1);
582
+ // Declarar variável de iteração
583
+ const simbolo = {
584
+ nome: node.variavel,
585
+ kind: 'variavel',
586
+ tipo: tipoElemento,
587
+ linha: node.line,
588
+ coluna: node.column,
589
+ escopo: this.tabela.escopoAtual()
590
+ };
591
+ try {
592
+ this.tabela.declarar(simbolo);
593
+ }
594
+ catch (e) {
595
+ this.erro(e.message, node.line, node.column);
596
+ }
597
+ this.verificarBloco(node.corpo);
598
+ }
599
+ verificarRetorno(node) {
600
+ if (!this.funcaoAtual) {
601
+ this.erro(`'retornar' só pode ser usado dentro de uma função`, node.line, node.column);
602
+ return;
603
+ }
604
+ if (node.valor) {
605
+ if (!this.funcaoAtual.tipoRetorno) {
606
+ this.erro(`Função '${this.funcaoAtual.nome}' não deve retornar valor`, node.line, node.column);
607
+ return;
608
+ }
609
+ const tipoRetorno = this.tipoParaString(this.funcaoAtual.tipoRetorno);
610
+ const tipoValor = this.resolverTipo(node.valor);
611
+ if (!this.tiposCompatíveis(tipoRetorno, tipoValor)) {
612
+ this.erro(`Tipo incompatível: esperado '${tipoRetorno}', recebido '${tipoValor}'`, node.line, node.column);
613
+ }
614
+ }
615
+ else if (this.funcaoAtual.tipoRetorno) {
616
+ this.erro(`Função '${this.funcaoAtual.nome}' deve retornar valor`, node.line, node.column);
617
+ }
618
+ }
619
+ verificarEmissaoEvento(node) {
620
+ const evento = this.tabela.buscar(node.evento);
621
+ if (!evento || evento.kind !== 'evento') {
622
+ this.erro(`Evento '${node.evento}' não encontrado`, node.line, node.column);
623
+ return;
624
+ }
625
+ // Verificar argumentos contra campos do evento
626
+ const camposEvento = this.tabela.buscarCamposEvento(node.evento);
627
+ if (camposEvento) {
628
+ // Verificar quantidade de argumentos
629
+ if (node.argumentos.length !== camposEvento.size) {
630
+ this.erro(`Evento '${node.evento}' espera ${camposEvento.size} argumentos, recebeu ${node.argumentos.length}`, node.line, node.column);
631
+ return;
632
+ }
633
+ // Verificar tipo de cada argumento
634
+ // NOTA: Precisaríamos da ordem dos campos no evento para verificar corretamente
635
+ // Por ora, vamos verificar apenas se a quantidade bate
636
+ const camposArray = Array.from(camposEvento.entries());
637
+ for (let i = 0; i < node.argumentos.length; i++) {
638
+ const tipoArgumento = this.resolverTipo(node.argumentos[i]);
639
+ const [nomeCampo, tipoCampo] = camposArray[i];
640
+ if (!this.tiposCompatíveis(tipoCampo, tipoArgumento)) {
641
+ this.erro(`Argumento '${nomeCampo}' do evento '${node.evento}' deve ser '${tipoCampo}', recebido '${tipoArgumento}'`, node.argumentos[i].line || node.line, node.argumentos[i].column || node.column);
642
+ }
643
+ }
644
+ }
645
+ }
646
+ verificarErro(node) {
647
+ const tipoMensagem = this.resolverTipo(node.mensagem);
648
+ if (tipoMensagem !== 'texto') {
649
+ this.erro(`Mensagem de erro deve ser do tipo 'texto', recebeu '${tipoMensagem}'`, node.line, node.column);
650
+ }
651
+ }
652
+ verificarInstrucao(instrucao) {
653
+ switch (instrucao.kind) {
654
+ case 'Variavel':
655
+ this.verificarVariavel(instrucao);
656
+ break;
657
+ case 'Atribuicao':
658
+ this.verificarAtribuicao(instrucao);
659
+ break;
660
+ case 'ChamadaFuncao':
661
+ this.resolverTipo(instrucao);
662
+ break;
663
+ case 'Retorno':
664
+ this.verificarRetorno(instrucao);
665
+ break;
666
+ case 'Condicional':
667
+ this.verificarCondicional(instrucao);
668
+ break;
669
+ case 'Enquanto':
670
+ this.verificarEnquanto(instrucao);
671
+ break;
672
+ case 'Para':
673
+ this.verificarPara(instrucao);
674
+ break;
675
+ case 'EmissaoEvento':
676
+ this.verificarEmissaoEvento(instrucao);
677
+ break;
678
+ case 'Erro':
679
+ this.verificarErro(instrucao);
680
+ break;
681
+ }
682
+ }
683
+ // Expressões — retornam o tipo resolvido (string)
684
+ resolverTipo(node) {
685
+ switch (node.kind) {
686
+ case 'Literal':
687
+ return this.resolverTipoLiteral(node);
688
+ case 'Identificador':
689
+ return this.resolverTipoIdentificador(node);
690
+ case 'Binario':
691
+ return this.resolverTipoBinario(node);
692
+ case 'Unario':
693
+ return this.resolverTipoUnario(node);
694
+ case 'ChamadaFuncao':
695
+ return this.resolverTipoChamada(node);
696
+ case 'AcessoMembro':
697
+ return this.resolverTipoAcessoMembro(node);
698
+ case 'Atribuicao':
699
+ this.verificarAtribuicao(node);
700
+ return 'vazio';
701
+ default:
702
+ return 'desconhecido';
703
+ }
704
+ }
705
+ resolverTipoLiteral(node) {
706
+ return node.tipoLiteral;
707
+ }
708
+ resolverTipoIdentificador(node) {
709
+ const simbolo = this.tabela.buscar(node.nome);
710
+ if (!simbolo) {
711
+ this.erro(`Variável '${node.nome}' não declarada`, node.line, node.column);
712
+ return 'desconhecido';
713
+ }
714
+ return simbolo.tipo;
715
+ }
716
+ resolverTipoBinario(node) {
717
+ const tipoEsquerda = this.resolverTipo(node.esquerda);
718
+ const tipoDireita = this.resolverTipo(node.direita);
719
+ switch (node.operador) {
720
+ case '+':
721
+ case '-':
722
+ case '*':
723
+ case '/':
724
+ if (tipoEsquerda === 'numero' && tipoDireita === 'numero')
725
+ return 'numero';
726
+ if (tipoEsquerda === 'decimal' && tipoDireita === 'decimal')
727
+ return 'decimal';
728
+ if (tipoEsquerda === 'decimal' && tipoDireita === 'numero')
729
+ return 'decimal';
730
+ if (tipoEsquerda === 'numero' && tipoDireita === 'decimal')
731
+ return 'decimal';
732
+ if (node.operador === '+' && tipoEsquerda === 'texto' && tipoDireita === 'texto')
733
+ return 'texto';
734
+ break;
735
+ case '==':
736
+ case '!=':
737
+ if (this.tiposCompatíveis(tipoEsquerda, tipoDireita))
738
+ return 'booleano';
739
+ break;
740
+ case '<':
741
+ case '<=':
742
+ case '>':
743
+ case '>=':
744
+ if ((tipoEsquerda === 'numero' || tipoEsquerda === 'decimal') &&
745
+ (tipoDireita === 'numero' || tipoDireita === 'decimal'))
746
+ return 'booleano';
747
+ if (tipoEsquerda === 'data' && tipoDireita === 'data')
748
+ return 'booleano';
749
+ if (tipoEsquerda === 'hora' && tipoDireita === 'hora')
750
+ return 'booleano';
751
+ break;
752
+ case 'e':
753
+ case 'ou':
754
+ if (tipoEsquerda === 'booleano' && tipoDireita === 'booleano')
755
+ return 'booleano';
756
+ break;
757
+ }
758
+ this.erro(`Operador '${node.operador}' não pode ser aplicado entre '${tipoEsquerda}' e '${tipoDireita}'`, node.line, node.column);
759
+ return 'desconhecido';
760
+ }
761
+ resolverTipoUnario(node) {
762
+ const tipoOperando = this.resolverTipo(node.operando);
763
+ switch (node.operador) {
764
+ case '-':
765
+ if (tipoOperando === 'numero' || tipoOperando === 'decimal')
766
+ return tipoOperando;
767
+ break;
768
+ case 'nao':
769
+ if (tipoOperando === 'booleano')
770
+ return 'booleano';
771
+ break;
772
+ }
773
+ this.erro(`Operador unário '${node.operador}' não pode ser aplicado ao tipo '${tipoOperando}'`, node.line, node.column);
774
+ return 'desconhecido';
775
+ }
776
+ resolverTipoChamada(node) {
777
+ const funcao = this.tabela.buscar(node.nome);
778
+ if (!funcao || funcao.kind !== 'funcao') {
779
+ this.erro(`Função '${node.nome}' não encontrada`, node.line, node.column);
780
+ return 'desconhecido';
781
+ }
782
+ // Verificar número de argumentos
783
+ const params = this.tabela.buscarParametrosFuncao(node.nome);
784
+ if (params !== null && node.argumentos.length !== params.length) {
785
+ this.erro(`Função '${node.nome}' espera ${params.length} argumentos, recebeu ${node.argumentos.length}`, node.line, node.column);
786
+ }
787
+ return funcao.tipo === 'vazio' ? 'vazio' : funcao.tipo;
788
+ }
789
+ resolverTipoAcessoMembro(node) {
790
+ const tipoObjeto = this.resolverTipo(node.objeto);
791
+ if (tipoObjeto === 'desconhecido')
792
+ return 'desconhecido';
793
+ // Check for built-in methods first
794
+ if (tipoObjeto === 'texto') {
795
+ const metodoTipo = this.metodosTexto[node.membro];
796
+ if (metodoTipo) {
797
+ return metodoTipo;
798
+ }
799
+ }
800
+ if (tipoObjeto.startsWith('lista<')) {
801
+ const metodoTipo = this.metodosLista[node.membro];
802
+ if (metodoTipo) {
803
+ if (metodoTipo === 'T') {
804
+ // Extract element type from lista<Tipo>
805
+ const elementoTipo = tipoObjeto.substring(6, tipoObjeto.length - 1);
806
+ return elementoTipo;
807
+ }
808
+ return metodoTipo;
809
+ }
810
+ }
811
+ const tipoCampo = this.tabela.buscarCampo(tipoObjeto, node.membro);
812
+ if (tipoCampo === null) {
813
+ this.erro(`'${tipoObjeto}' não possui campo '${node.membro}'`, node.line, node.column);
814
+ return 'desconhecido';
815
+ }
816
+ return tipoCampo;
817
+ }
818
+ // ── Utilitários ──────────────────────────────────────────
819
+ // Verifica se dois tipos são compatíveis para atribuição
820
+ // Ex: 'numero' é compatível com 'numero', mas não com 'texto'
821
+ tiposCompatíveis(esperado, recebido) {
822
+ if (esperado === recebido)
823
+ return true;
824
+ // decimal aceita numero
825
+ if (esperado === 'decimal' && recebido === 'numero')
826
+ return true;
827
+ // id aceita numero (IDs podem ser representados como números)
828
+ if (esperado === 'id' && recebido === 'numero')
829
+ return true;
830
+ // Verificar herança de classes
831
+ if (this.verificarHeranca(esperado, recebido))
832
+ return true;
833
+ return false;
834
+ }
835
+ // Verifica se recebido é subclasse de esperado (herança)
836
+ verificarHeranca(esperado, recebido) {
837
+ // Buscar símbolos dos tipos
838
+ const simboloEsperado = this.tabela.buscar(esperado);
839
+ const simboloRecebido = this.tabela.buscar(recebido);
840
+ // Ambos devem ser classes ou entidades
841
+ if (!simboloEsperado || !simboloRecebido)
842
+ return false;
843
+ if (!['classe', 'entidade'].includes(simboloEsperado.kind))
844
+ return false;
845
+ if (!['classe', 'entidade'].includes(simboloRecebido.kind))
846
+ return false;
847
+ // Verificar cadeia de herança recursivamente
848
+ let tipoAtual = recebido;
849
+ const visitados = new Set();
850
+ while (tipoAtual && !visitados.has(tipoAtual)) {
851
+ visitados.add(tipoAtual);
852
+ if (tipoAtual === esperado) {
853
+ return true;
854
+ }
855
+ // Buscar superclasse do tipo atual
856
+ const simboloAtual = this.tabela.buscar(tipoAtual);
857
+ if (!simboloAtual)
858
+ break;
859
+ // Encontrar definição da classe/entidade para verificar superclasse
860
+ const superClasse = this.buscarSuperClasse(tipoAtual);
861
+ if (!superClasse)
862
+ break;
863
+ tipoAtual = superClasse;
864
+ }
865
+ return false;
866
+ }
867
+ // Busca superclasse de uma classe/entidade na tabela de símbolos
868
+ buscarSuperClasse(nomeTipo) {
869
+ return this.tabela.buscarSuperClasse(nomeTipo);
870
+ }
871
+ // Verifica se todos os caminhos de um bloco terminam com retorno
872
+ verificarRetornoEmTodosCaminhos(bloco) {
873
+ if (bloco.instrucoes.length === 0) {
874
+ return false;
875
+ }
876
+ // Verificar última instrução do bloco
877
+ const ultimaInstrucao = bloco.instrucoes[bloco.instrucoes.length - 1];
878
+ if (ultimaInstrucao.kind === 'Retorno') {
879
+ return true;
880
+ }
881
+ if (ultimaInstrucao.kind === 'Condicional') {
882
+ const condicional = ultimaInstrucao;
883
+ // Para se/senão: ambos os branches devem retornar
884
+ if (condicional.senao) {
885
+ const entaoRetorna = this.verificarRetornoEmTodosCaminhos(condicional.entao);
886
+ const senaoRetorna = this.verificarRetornoEmTodosCaminhos(condicional.senao);
887
+ return entaoRetorna && senaoRetorna;
888
+ }
889
+ else {
890
+ // se sem senão: não garante retorno em todos os caminhos
891
+ return false;
892
+ }
893
+ }
894
+ // Para outros tipos de instrução, não garante retorno
895
+ return false;
896
+ }
897
+ // Verifica se um tipo existe (primitivo, classe, entidade, enum declarado)
898
+ tipoExiste(nome) {
899
+ const tiposPrimitivos = ['texto', 'numero', 'decimal', 'booleano', 'data', 'hora', 'id'];
900
+ if (tiposPrimitivos.includes(nome))
901
+ return true;
902
+ // Verificar tipos genéricos
903
+ if (nome.startsWith('lista<') && nome.endsWith('>')) {
904
+ const elementoTipo = nome.substring(6, nome.length - 1);
905
+ return this.tipoExiste(elementoTipo);
906
+ }
907
+ if (nome.startsWith('mapa<') && nome.endsWith('>')) {
908
+ const partes = nome.substring(4, nome.length - 1).split(',');
909
+ if (partes.length !== 2)
910
+ return false;
911
+ return this.tipoExiste(partes[0].trim()) && this.tipoExiste(partes[1].trim());
912
+ }
913
+ const simbolo = this.tabela.buscar(nome);
914
+ return simbolo !== null && ['classe', 'entidade', 'enum', 'interface'].includes(simbolo.kind);
915
+ }
916
+ // Converte TipoNode em string para comparação
917
+ // Ex: TipoLista<Produto> → 'lista<Produto>'
918
+ tipoParaString(tipo) {
919
+ switch (tipo.kind) {
920
+ case 'TipoSimples':
921
+ let resultado = tipo.nome;
922
+ if (tipo.opcional)
923
+ resultado += '?';
924
+ if (tipo.obrigatorio)
925
+ resultado += '!';
926
+ return resultado;
927
+ case 'TipoLista':
928
+ return `lista<${this.tipoParaString(tipo.elementoTipo)}>`;
929
+ case 'TipoMapa':
930
+ return `mapa<${this.tipoParaString(tipo.chaveTipo)},${this.tipoParaString(tipo.valorTipo)}>`;
931
+ case 'TipoObjeto':
932
+ return 'objeto';
933
+ default:
934
+ return 'desconhecido';
935
+ }
936
+ }
937
+ registrarTela(node) {
938
+ const simbolo = {
939
+ nome: node.nome,
940
+ kind: 'tela',
941
+ tipo: 'Tela',
942
+ linha: node.line,
943
+ coluna: node.column,
944
+ escopo: this.tabela.escopoAtual()
945
+ };
946
+ this.tabela.declarar(simbolo);
947
+ }
948
+ verificarTela(node) {
949
+ const tiposElementosValidos = ['tabela', 'formulario', 'botao', 'card', 'modal', 'grafico'];
950
+ for (const elem of node.elementos) {
951
+ if (!tiposElementosValidos.includes(elem.tipo)) {
952
+ this.erro(`Tipo de elemento '${elem.tipo}' inválido. Use: tabela, formulario, botao ou card`, elem.line, elem.column);
953
+ }
954
+ // Referências a entidades em tela podem vir de outros módulos —
955
+ // resolução completa está prevista para v0.2.0 (sistema de módulos).
956
+ }
957
+ }
958
+ // Adiciona erro sem lançar exceção (continua verificando o resto)
959
+ erro(mensagem, linha, coluna) {
960
+ this.erros.push({ mensagem, linha, coluna });
961
+ }
962
+ }
963
+ //# sourceMappingURL=type_checker.js.map