@designliquido/delegua 0.63.0 → 0.64.1

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 (61) hide show
  1. package/analisador-semantico/analisador-semantico-base.d.ts +18 -1
  2. package/analisador-semantico/analisador-semantico-base.d.ts.map +1 -1
  3. package/analisador-semantico/analisador-semantico-base.js +129 -0
  4. package/analisador-semantico/analisador-semantico-base.js.map +1 -1
  5. package/analisador-semantico/analisador-semantico.d.ts +38 -19
  6. package/analisador-semantico/analisador-semantico.d.ts.map +1 -1
  7. package/analisador-semantico/analisador-semantico.js +353 -112
  8. package/analisador-semantico/analisador-semantico.js.map +1 -1
  9. package/analisador-semantico/escopo-variavel.d.ts +11 -0
  10. package/analisador-semantico/escopo-variavel.d.ts.map +1 -0
  11. package/analisador-semantico/escopo-variavel.js +3 -0
  12. package/analisador-semantico/escopo-variavel.js.map +1 -0
  13. package/analisador-semantico/funcao-hipotetica-interface.d.ts +5 -0
  14. package/analisador-semantico/funcao-hipotetica-interface.d.ts.map +1 -0
  15. package/analisador-semantico/funcao-hipotetica-interface.js +3 -0
  16. package/analisador-semantico/funcao-hipotetica-interface.js.map +1 -0
  17. package/analisador-semantico/gerenciador-escopos.d.ts +15 -0
  18. package/analisador-semantico/gerenciador-escopos.d.ts.map +1 -0
  19. package/analisador-semantico/gerenciador-escopos.js +77 -0
  20. package/analisador-semantico/gerenciador-escopos.js.map +1 -0
  21. package/analisador-semantico/index.d.ts +4 -0
  22. package/analisador-semantico/index.d.ts.map +1 -1
  23. package/analisador-semantico/index.js +4 -0
  24. package/analisador-semantico/index.js.map +1 -1
  25. package/analisador-semantico/variavel-hipotetica-interface.d.ts +7 -0
  26. package/analisador-semantico/variavel-hipotetica-interface.d.ts.map +1 -0
  27. package/analisador-semantico/variavel-hipotetica-interface.js +3 -0
  28. package/analisador-semantico/variavel-hipotetica-interface.js.map +1 -0
  29. package/avaliador-sintatico/avaliador-sintatico.d.ts +2 -0
  30. package/avaliador-sintatico/avaliador-sintatico.d.ts.map +1 -1
  31. package/avaliador-sintatico/avaliador-sintatico.js +49 -2
  32. package/avaliador-sintatico/avaliador-sintatico.js.map +1 -1
  33. package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.d.ts +4 -0
  34. package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.d.ts.map +1 -1
  35. package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.js +91 -11
  36. package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.js.map +1 -1
  37. package/bin/package.json +1 -1
  38. package/construtos/acesso-indice-variavel.d.ts +2 -1
  39. package/construtos/acesso-indice-variavel.d.ts.map +1 -1
  40. package/construtos/acesso-indice-variavel.js +3 -1
  41. package/construtos/acesso-indice-variavel.js.map +1 -1
  42. package/construtos/binario.d.ts +4 -0
  43. package/construtos/binario.d.ts.map +1 -1
  44. package/construtos/binario.js +7 -6
  45. package/construtos/binario.js.map +1 -1
  46. package/construtos/leia.d.ts +1 -1
  47. package/construtos/leia.d.ts.map +1 -1
  48. package/construtos/leia.js +1 -0
  49. package/construtos/leia.js.map +1 -1
  50. package/construtos/unario.d.ts +1 -0
  51. package/construtos/unario.d.ts.map +1 -1
  52. package/construtos/unario.js +2 -0
  53. package/construtos/unario.js.map +1 -1
  54. package/formatadores/formatador-pitugues.js +1 -1
  55. package/formatadores/formatador-pitugues.js.map +1 -1
  56. package/lexador/dialetos/palavras-reservadas/pitugues.d.ts +0 -3
  57. package/lexador/dialetos/palavras-reservadas/pitugues.d.ts.map +1 -1
  58. package/lexador/dialetos/palavras-reservadas/pitugues.js +0 -3
  59. package/lexador/dialetos/palavras-reservadas/pitugues.js.map +1 -1
  60. package/package.json +1 -1
  61. package/umd/delegua.js +154 -24
@@ -5,34 +5,20 @@ const construtos_1 = require("../construtos");
5
5
  const declaracoes_1 = require("../declaracoes");
6
6
  const erros_1 = require("../interfaces/erros");
7
7
  const analisador_semantico_base_1 = require("./analisador-semantico-base");
8
+ const gerenciador_escopos_1 = require("./gerenciador-escopos");
8
9
  const pilha_variaveis_1 = require("./pilha-variaveis");
10
+ /**
11
+ * O Analisador Semântico de Delégua.
12
+ */
9
13
  class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemanticoBase {
10
14
  constructor() {
11
15
  super();
12
16
  this.pilhaVariaveis = new pilha_variaveis_1.PilhaVariaveis();
13
- this.variaveis = {};
17
+ this.gerenciadorEscopos = new gerenciador_escopos_1.GerenciadorEscopos();
14
18
  this.funcoes = {};
15
19
  this.atual = 0;
16
20
  this.diagnosticos = [];
17
21
  }
18
- erro(simbolo, mensagem) {
19
- this.diagnosticos.push({
20
- simbolo: simbolo,
21
- mensagem: mensagem,
22
- hashArquivo: simbolo.hashArquivo,
23
- linha: simbolo.linha,
24
- severidade: erros_1.DiagnosticoSeveridade.ERRO,
25
- });
26
- }
27
- aviso(simbolo, mensagem) {
28
- this.diagnosticos.push({
29
- simbolo: simbolo,
30
- mensagem: mensagem,
31
- hashArquivo: simbolo.hashArquivo,
32
- linha: simbolo.linha,
33
- severidade: erros_1.DiagnosticoSeveridade.AVISO,
34
- });
35
- }
36
22
  verificarTipoAtribuido(declaracao) {
37
23
  if (declaracao.tipo) {
38
24
  if (['vetor', 'qualquer[]', 'inteiro[]', 'texto[]'].includes(declaracao.tipo)) {
@@ -131,7 +117,10 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
131
117
  }
132
118
  }
133
119
  visitarChamadaPorArgumentoReferenciaFuncao(argumentoReferenciaFuncao, argumentos) {
134
- const variavelCorrespondente = this.variaveis[argumentoReferenciaFuncao.simboloFuncao.lexema].valor;
120
+ var _a;
121
+ const variavelCorrespondente =
122
+ // this.variaveis[argumentoReferenciaFuncao.simboloFuncao.lexema].valor;
123
+ (_a = this.gerenciadorEscopos.buscar(argumentoReferenciaFuncao.simboloFuncao.lexema)) === null || _a === void 0 ? void 0 : _a.valor;
135
124
  if (!variavelCorrespondente) {
136
125
  return;
137
126
  }
@@ -146,7 +135,7 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
146
135
  }
147
136
  visitarChamadaPorVariavel(entidadeChamadaVariavel, argumentos) {
148
137
  const variavel = entidadeChamadaVariavel;
149
- const funcaoChamada = this.variaveis[variavel.simbolo.lexema] || this.funcoes[variavel.simbolo.lexema];
138
+ const funcaoChamada = this.gerenciadorEscopos.buscar(variavel.simbolo.lexema) || this.funcoes[variavel.simbolo.lexema];
150
139
  if (!funcaoChamada) {
151
140
  this.erro(entidadeChamadaVariavel.simbolo, `Chamada da função '${entidadeChamadaVariavel.simbolo.lexema}' não existe.`);
152
141
  return Promise.resolve();
@@ -155,6 +144,11 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
155
144
  this.comparacaoArgumentosContraParametrosFuncao(entidadeChamadaVariavel.simbolo, funcao.parametros, argumentos);
156
145
  }
157
146
  visitarExpressaoDeChamada(expressao) {
147
+ for (const argumento of expressao.argumentos) {
148
+ if (argumento instanceof construtos_1.Variavel) {
149
+ this.gerenciadorEscopos.marcarComoUsada(argumento.simbolo.lexema);
150
+ }
151
+ }
158
152
  switch (expressao.entidadeChamada.constructor) {
159
153
  case construtos_1.ArgumentoReferenciaFuncao:
160
154
  const entidadeChamadaArgumentoReferenciaFuncao = expressao.entidadeChamada;
@@ -172,8 +166,27 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
172
166
  return Promise.resolve();
173
167
  }
174
168
  visitarExpressaoDeAtribuicao(expressao) {
175
- // TODO: Readaptar para trabalhar com `expressao.alvo` sendo um construto.
176
169
  let simboloAlvo;
170
+ switch (expressao.alvo.constructor) {
171
+ case construtos_1.Variavel:
172
+ const alvoVariavel = expressao.alvo;
173
+ simboloAlvo = alvoVariavel.simbolo;
174
+ break;
175
+ default:
176
+ return Promise.resolve();
177
+ }
178
+ const variavel = this.gerenciadorEscopos.buscar(simboloAlvo.lexema);
179
+ if (!variavel) {
180
+ this.erro(simboloAlvo, `Variável '${simboloAlvo.lexema}' ainda não foi declarada até este ponto.`);
181
+ return Promise.resolve();
182
+ }
183
+ if (variavel.imutavel) {
184
+ this.erro(simboloAlvo, `Constante '${simboloAlvo.lexema}' não pode ser modificada.`);
185
+ return Promise.resolve();
186
+ }
187
+ // Marca como inicializada após atribuição
188
+ this.gerenciadorEscopos.marcarComoInicializada(simboloAlvo.lexema, expressao.valor);
189
+ // TODO: Readaptar para trabalhar com `expressao.alvo` sendo um construto.
177
190
  switch (expressao.alvo.constructor) {
178
191
  case construtos_1.Variavel:
179
192
  const alvoVariavel = expressao.alvo;
@@ -183,7 +196,7 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
183
196
  // throw new Error(`Implementar atribuição para ${expressao.alvo.constructor}.`);
184
197
  return Promise.resolve();
185
198
  }
186
- let valor = this.variaveis[simboloAlvo.lexema];
199
+ let valor = this.gerenciadorEscopos.buscar(simboloAlvo.lexema);
187
200
  if (!valor) {
188
201
  this.erro(simboloAlvo, `Variável ${simboloAlvo.lexema} ainda não foi declarada até este ponto.`);
189
202
  return Promise.resolve();
@@ -232,15 +245,14 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
232
245
  }
233
246
  }
234
247
  }
235
- if (valor.imutavel) {
248
+ /* if (valor.imutavel) {
236
249
  this.erro(simboloAlvo, `Constante ${simboloAlvo.lexema} não pode ser modificada.`);
237
250
  return Promise.resolve();
238
- }
239
- else {
251
+ } else {
240
252
  if (this.variaveis[simboloAlvo.lexema]) {
241
253
  this.variaveis[simboloAlvo.lexema].valor = expressao.valor;
242
254
  }
243
- }
255
+ } */
244
256
  }
245
257
  async visitarDeclaracaoDeExpressao(declaracao) {
246
258
  return await declaracao.expressao.aceitar(this);
@@ -265,7 +277,7 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
265
277
  case construtos_1.Variavel:
266
278
  const condicaoVariavel = condicao;
267
279
  this.verificarVariavel(condicaoVariavel);
268
- const variavelHipotetica = this.variaveis[condicaoVariavel.simbolo.lexema];
280
+ const variavelHipotetica = this.gerenciadorEscopos.buscar(condicaoVariavel.simbolo.lexema);
269
281
  if (variavelHipotetica && typeof variavelHipotetica.valor !== tipo) {
270
282
  this.erro(condicaoVariavel.simbolo, `'caso ${condicaoVariavel.simbolo.lexema}:' não é do mesmo tipo esperado em 'escolha'`);
271
283
  }
@@ -298,7 +310,7 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
298
310
  }
299
311
  verificarVariavelBinaria(variavel) {
300
312
  this.verificarVariavel(variavel);
301
- const variavelHipotetica = this.variaveis[variavel.simbolo.lexema];
313
+ const variavelHipotetica = this.gerenciadorEscopos.buscar(variavel.simbolo.lexema);
302
314
  if (variavelHipotetica &&
303
315
  !(variavelHipotetica.valor instanceof construtos_1.Binario) &&
304
316
  typeof variavelHipotetica.valor !== 'boolean') {
@@ -307,54 +319,200 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
307
319
  return Promise.resolve();
308
320
  }
309
321
  verificarVariavel(variavel) {
310
- const variavelHipotetica = this.variaveis[variavel.simbolo.lexema];
311
- if (!variavelHipotetica) {
312
- this.erro(variavel.simbolo, `Variável ${variavel.simbolo.lexema} ainda não foi declarada até este ponto.`);
322
+ const variavelEscopo = this.gerenciadorEscopos.buscar(variavel.simbolo.lexema);
323
+ if (!variavelEscopo) {
324
+ this.erro(variavel.simbolo, `Variável '${variavel.simbolo.lexema}' ainda não foi declarada até este ponto.`);
325
+ return Promise.resolve();
326
+ }
327
+ // Marca como usada
328
+ this.gerenciadorEscopos.marcarComoUsada(variavel.simbolo.lexema);
329
+ // Verifica se foi inicializada
330
+ if (!variavelEscopo.inicializada) {
331
+ this.aviso(variavel.simbolo, `Variável '${variavel.simbolo.lexema}' pode não ter sido inicializada antes do uso.`);
313
332
  }
314
333
  return Promise.resolve();
315
334
  }
316
335
  verificarBinario(binario) {
317
- this.verificarLadoBinario(binario.direita);
318
- this.verificarLadoBinario(binario.esquerda);
336
+ this.verificarExistenciaConstruto(binario.direita);
337
+ this.verificarExistenciaConstruto(binario.esquerda);
319
338
  this.verificarOperadorBinario(binario);
320
339
  return Promise.resolve();
321
340
  }
322
341
  verificarOperadorBinario(binario) {
323
- var _a, _b, _c;
342
+ if (binario.esquerda instanceof construtos_1.Binario) {
343
+ this.verificarOperadorBinario(binario.esquerda);
344
+ }
345
+ if (binario.direita instanceof construtos_1.Binario) {
346
+ this.verificarOperadorBinario(binario.direita);
347
+ }
324
348
  const operadoresMatematicos = ['ADICAO', 'SUBTRACAO', 'MULTIPLICACAO', 'DIVISAO', 'MODULO'];
325
349
  if (operadoresMatematicos.includes(binario.operador.tipo)) {
326
- if (binario.direita instanceof construtos_1.Variavel && binario.esquerda instanceof construtos_1.Variavel) {
327
- const tipoDireita = typeof ((_a = this.variaveis[binario.direita.simbolo.lexema]) === null || _a === void 0 ? void 0 : _a.valor);
328
- const tipoEsquerda = typeof ((_b = this.variaveis[binario.esquerda.simbolo.lexema]) === null || _b === void 0 ? void 0 : _b.valor);
329
- if (tipoDireita !== tipoEsquerda) {
330
- this.erro(binario.operador, `Operação inválida, tipos diferentes.`);
331
- }
332
- }
350
+ this.verificarTiposOperandos(binario);
333
351
  }
334
352
  if (binario.operador.tipo === 'DIVISAO') {
335
- switch (binario.direita.constructor) {
336
- case construtos_1.Variavel:
337
- const operadorDireitoLiteral = binario.direita;
338
- if (((_c = this.variaveis[operadorDireitoLiteral.simbolo.lexema]) === null || _c === void 0 ? void 0 : _c.valor) === 0) {
339
- this.erro(binario.operador, `Divisão por zero.`);
340
- }
341
- break;
353
+ this.verificarDivisaoPorZero(binario);
354
+ }
355
+ }
356
+ /**
357
+ * Verifica se os tipos dos operandos são compatíveis
358
+ */
359
+ verificarTiposOperandos(binario) {
360
+ const tipoEsquerda = this.obterTipoExpressao(binario.esquerda);
361
+ const tipoDireita = this.obterTipoExpressao(binario.direita);
362
+ if (tipoEsquerda && tipoDireita && tipoEsquerda !== tipoDireita) {
363
+ // Verificar se são tipos numéricos compatíveis
364
+ const tiposNumericos = ['inteiro', 'número', 'real'];
365
+ const ambosNumericos = tiposNumericos.includes(tipoEsquerda) &&
366
+ tiposNumericos.includes(tipoDireita);
367
+ if (!ambosNumericos) {
368
+ this.aviso(binario.operador, `Operação entre tipos diferentes: tipo esquerdo '${tipoEsquerda}' e tipo direito '${tipoDireita}'. O resultado será resolvido implicitamente.`);
342
369
  }
343
370
  }
344
371
  }
345
- verificarLadoBinario(lado) {
346
- if (lado instanceof construtos_1.Variavel && !this.variaveis[lado.simbolo.lexema]) {
347
- this.erro(lado.simbolo, `Variável ${lado.simbolo.lexema} ainda não foi declarada até este ponto.`);
348
- return;
372
+ /**
373
+ * Verifica divisão por zero recursivamente
374
+ */
375
+ verificarDivisaoPorZero(binario) {
376
+ const valorDireita = this.avaliarExpressaoConstante(binario.direita);
377
+ if (valorDireita === 0) {
378
+ this.erro(binario.operador, `Divisão por zero.`);
379
+ }
380
+ }
381
+ /**
382
+ * Tenta avaliar uma expressão em tempo de compilação para detectar valores constantes
383
+ * Retorna o valor se puder ser determinado, ou null caso contrário
384
+ */
385
+ avaliarExpressaoConstante(expressao) {
386
+ if (expressao instanceof construtos_1.Literal) {
387
+ return expressao.valor;
388
+ }
389
+ if (expressao instanceof construtos_1.Variavel) {
390
+ const variavel = this.gerenciadorEscopos.buscar(expressao.simbolo.lexema);
391
+ if (!variavel) {
392
+ return null;
393
+ }
394
+ if (variavel.imutavel && variavel.inicializada) {
395
+ return variavel.valor;
396
+ }
397
+ if (variavel.inicializada && variavel.valor !== undefined) {
398
+ return variavel.valor;
399
+ }
400
+ return null;
401
+ }
402
+ if (expressao instanceof construtos_1.Binario) {
403
+ const esquerda = this.avaliarExpressaoConstante(expressao.esquerda);
404
+ const direita = this.avaliarExpressaoConstante(expressao.direita);
405
+ if (esquerda !== null && direita !== null) {
406
+ return this.calcularOperacaoBinaria(expressao.operador.tipo, esquerda, direita);
407
+ }
408
+ }
409
+ if (expressao instanceof construtos_1.Agrupamento) {
410
+ return this.avaliarExpressaoConstante(expressao.expressao);
411
+ }
412
+ return null;
413
+ }
414
+ /**
415
+ * Calcula o resultado de uma operação binária em tempo de compilação
416
+ */
417
+ calcularOperacaoBinaria(operador, esquerda, direita) {
418
+ try {
419
+ switch (operador) {
420
+ case 'ADICAO':
421
+ return esquerda + direita;
422
+ case 'SUBTRACAO':
423
+ return esquerda - direita;
424
+ case 'MULTIPLICACAO':
425
+ return esquerda * direita;
426
+ case 'DIVISAO':
427
+ return esquerda / direita;
428
+ case 'MODULO':
429
+ return esquerda % direita;
430
+ case 'MAIOR':
431
+ return esquerda > direita;
432
+ case 'MAIOR_IGUAL':
433
+ return esquerda >= direita;
434
+ case 'MENOR':
435
+ return esquerda < direita;
436
+ case 'MENOR_IGUAL':
437
+ return esquerda <= direita;
438
+ case 'IGUAL':
439
+ return esquerda === direita;
440
+ case 'DIFERENTE':
441
+ return esquerda !== direita;
442
+ default:
443
+ return null;
444
+ }
445
+ }
446
+ catch (e) {
447
+ return null;
448
+ }
449
+ }
450
+ /**
451
+ * Obtém o tipo de uma expressão (pode ser Literal, Variavel, ou Binario)
452
+ */
453
+ obterTipoExpressao(expressao) {
454
+ if (expressao instanceof construtos_1.Literal) {
455
+ return expressao.tipo;
456
+ }
457
+ if (expressao instanceof construtos_1.Variavel) {
458
+ const variavel = this.gerenciadorEscopos.buscar(expressao.simbolo.lexema);
459
+ return (variavel === null || variavel === void 0 ? void 0 : variavel.tipo) || null;
460
+ }
461
+ if (expressao instanceof construtos_1.Binario) {
462
+ // Para binários, tentamos inferir o tipo baseado nos operandos
463
+ return this.inferirTipoBinario(expressao);
464
+ }
465
+ if (expressao instanceof construtos_1.Agrupamento) {
466
+ return this.obterTipoExpressao(expressao.expressao);
467
+ }
468
+ return null;
469
+ }
470
+ /**
471
+ * Infere o tipo de resultado de uma operação binária
472
+ */
473
+ inferirTipoBinario(binario) {
474
+ const tipoEsquerda = this.obterTipoExpressao(binario.esquerda);
475
+ const tipoDireita = this.obterTipoExpressao(binario.direita);
476
+ if (!tipoEsquerda || !tipoDireita) {
477
+ return null;
478
+ }
479
+ const operadoresMatematicos = ['ADICAO', 'SUBTRACAO', 'MULTIPLICACAO', 'DIVISAO', 'MODULO'];
480
+ const operadoresComparacao = ['MAIOR', 'MAIOR_IGUAL', 'MENOR', 'MENOR_IGUAL', 'IGUAL', 'DIFERENTE'];
481
+ if (operadoresComparacao.includes(binario.operador.tipo)) {
482
+ return 'lógico';
349
483
  }
350
- if (lado instanceof construtos_1.Binario) {
351
- this.verificarBinario(lado);
484
+ if (operadoresMatematicos.includes(binario.operador.tipo)) {
485
+ const tiposNumericos = ['inteiro', 'número', 'real'];
486
+ if (tiposNumericos.includes(tipoEsquerda) && tiposNumericos.includes(tipoDireita)) {
487
+ // Se um dos lados é 'real', o resultado é 'real'
488
+ if (tipoEsquerda === 'real' || tipoDireita === 'real') {
489
+ return 'real';
490
+ }
491
+ return 'número';
492
+ }
493
+ // Concatenação de textos
494
+ if (tipoEsquerda === 'texto' || tipoDireita === 'texto') {
495
+ return 'texto';
496
+ }
497
+ }
498
+ return 'qualquer';
499
+ }
500
+ verificarExistenciaConstruto(construto) {
501
+ if (construto instanceof construtos_1.Variavel) {
502
+ if (!this.gerenciadorEscopos.buscar(construto.simbolo.lexema)) {
503
+ this.erro(construto.simbolo, `Variável ${construto.simbolo.lexema} ainda não foi declarada até este ponto.`);
504
+ return;
505
+ }
506
+ this.gerenciadorEscopos.marcarComoUsada(construto.simbolo.lexema);
352
507
  return;
353
508
  }
509
+ if (construto instanceof construtos_1.Binario) {
510
+ this.verificarBinario(construto);
511
+ }
354
512
  }
355
- verificarLogico(logio) {
356
- this.verificarLadoLogico(logio.direita);
357
- this.verificarLadoLogico(logio.esquerda);
513
+ verificarLogico(logico) {
514
+ this.verificarLadoLogico(logico.direita);
515
+ this.verificarLadoLogico(logico.esquerda);
358
516
  return Promise.resolve();
359
517
  }
360
518
  verificarChamada(chamada) {
@@ -374,6 +532,43 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
374
532
  this.verificarVariavelBinaria(variavel);
375
533
  }
376
534
  }
535
+ /**
536
+ * Verifica interpolações de texto e marca variáveis como usadas
537
+ */
538
+ verificarInterpolacaoTexto(texto, literal) {
539
+ // Regex para encontrar ${identificador}
540
+ const regexInterpolacao = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
541
+ let match;
542
+ while ((match = regexInterpolacao.exec(texto)) !== null) {
543
+ const nomeVariavel = match[1];
544
+ // Verifica se a variável existe
545
+ const variavel = this.gerenciadorEscopos.buscar(nomeVariavel);
546
+ const funcao = this.funcoes[nomeVariavel];
547
+ if (!variavel && !funcao) {
548
+ this.erro({
549
+ lexema: nomeVariavel,
550
+ tipo: 'IDENTIFICADOR',
551
+ linha: literal.linha,
552
+ hashArquivo: literal.hashArquivo,
553
+ literal: null
554
+ }, `Variável ou função '${nomeVariavel}' usada em interpolação não existe.`);
555
+ }
556
+ else if (variavel) {
557
+ // Marca como usada
558
+ this.gerenciadorEscopos.marcarComoUsada(nomeVariavel);
559
+ // Verifica se foi inicializada
560
+ if (!variavel.inicializada) {
561
+ this.aviso({
562
+ lexema: nomeVariavel,
563
+ tipo: 'IDENTIFICADOR',
564
+ linha: literal.linha,
565
+ hashArquivo: literal.hashArquivo,
566
+ literal: null
567
+ }, `Variável '${nomeVariavel}' usada em interpolação pode não ter sido inicializada.`);
568
+ }
569
+ }
570
+ }
571
+ }
377
572
  visitarDeclaracaoEscreva(declaracao) {
378
573
  if (declaracao.argumentos.length === 0) {
379
574
  const { linha, hashArquivo } = declaracao;
@@ -387,53 +582,53 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
387
582
  this.erro(simbolo, `É preciso ter um ou mais parametros para 'escreva(...)'`);
388
583
  return Promise.resolve();
389
584
  }
390
- const variaveis = declaracao.argumentos.filter((arg) => arg instanceof construtos_1.Variavel);
391
- for (let variavel of variaveis) {
392
- // TODO: Funções também são consideradas "variáveis" até aqui, mas isso deve mudar futuramente.
393
- const possivelVariavel = this.variaveis[variavel.simbolo.lexema];
394
- const possivelFuncao = this.funcoes[variavel.simbolo.lexema];
395
- if (!possivelVariavel && !possivelFuncao) {
396
- this.erro(variavel.simbolo, `Variável ou função '${variavel.simbolo.lexema}' não existe.`);
397
- continue;
585
+ for (const argumento of declaracao.argumentos) {
586
+ this.marcarVariaveisUsadasEmExpressao(argumento);
587
+ if (argumento instanceof construtos_1.Literal && argumento.tipo === 'texto') {
588
+ this.verificarInterpolacaoTexto(argumento.valor, argumento);
398
589
  }
399
- if (possivelVariavel && possivelVariavel.valor === undefined) {
400
- this.aviso(variavel.simbolo, `Variável '${variavel.simbolo.lexema}' não foi inicializada.`);
590
+ if (argumento instanceof construtos_1.Variavel) {
591
+ const possivelVariavel = this.gerenciadorEscopos.buscar(argumento.simbolo.lexema);
592
+ const possivelFuncao = this.funcoes[argumento.simbolo.lexema];
593
+ if (!possivelVariavel && !possivelFuncao) {
594
+ this.erro(argumento.simbolo, `Variável ou função '${argumento.simbolo.lexema}' não existe.`);
595
+ continue;
596
+ }
597
+ if (possivelVariavel && possivelVariavel.valor === undefined) {
598
+ this.aviso(argumento.simbolo, `Variável '${argumento.simbolo.lexema}' não foi inicializada.`);
599
+ }
401
600
  }
402
601
  }
403
602
  return Promise.resolve();
404
603
  }
405
604
  visitarDeclaracaoConst(declaracao) {
605
+ var _a;
406
606
  this.verificarTipoAtribuido(declaracao);
407
- if (this.variaveis.hasOwnProperty(declaracao.simbolo.lexema)) {
408
- this.erro(declaracao.simbolo, 'Declaração de constante já feita.');
409
- }
410
- else {
411
- this.variaveis[declaracao.simbolo.lexema] = {
412
- imutavel: true,
413
- tipo: declaracao.tipo,
414
- valor: declaracao.inicializador.valor,
415
- };
607
+ if (declaracao.inicializador) {
608
+ this.marcarVariaveisUsadasEmExpressao(declaracao.inicializador);
416
609
  }
417
- this.virificarTipoDeclaracaoConst(declaracao);
418
- return Promise.resolve();
419
- }
420
- virificarTipoDeclaracaoConst(declaracao) {
421
- if (declaracao.inicializador instanceof construtos_1.Binario) {
422
- // verificar tipos iguais no lado esquerdo e direito
423
- const binario = declaracao.inicializador;
424
- this.verificarLadoBinario(binario.direita);
425
- this.verificarLadoBinario(binario.esquerda);
426
- const tipoDireita = typeof binario.direita.valor;
427
- const tipoEsquerda = typeof binario.esquerda.valor;
428
- if (tipoDireita !== tipoEsquerda) {
429
- this.aviso(declaracao.simbolo, 'Declaração de constante com tipos diferentes.');
430
- }
610
+ const constanteCorrespondente = this.gerenciadorEscopos.buscarNoEscopoAtual(declaracao.simbolo.lexema);
611
+ if (constanteCorrespondente) {
612
+ this.erro(declaracao.simbolo, 'Declaração de constante já feita.');
613
+ return Promise.resolve();
431
614
  }
615
+ this.gerenciadorEscopos.declarar(declaracao.simbolo.lexema, {
616
+ nome: declaracao.simbolo.lexema,
617
+ tipo: declaracao.tipo || 'qualquer',
618
+ imutavel: true,
619
+ valor: (_a = declaracao.inicializador) === null || _a === void 0 ? void 0 : _a.valor,
620
+ inicializada: true,
621
+ usada: false,
622
+ hashArquivo: declaracao.simbolo.hashArquivo,
623
+ linha: declaracao.simbolo.linha
624
+ });
625
+ // TODO: Verificar inicializador.
432
626
  return Promise.resolve();
433
627
  }
434
628
  visitarDeclaracaoVar(declaracao) {
435
629
  this.verificarTipoAtribuido(declaracao);
436
630
  if (declaracao.inicializador) {
631
+ this.marcarVariaveisUsadasEmExpressao(declaracao.inicializador);
437
632
  switch (declaracao.inicializador.constructor) {
438
633
  case construtos_1.FuncaoConstruto:
439
634
  const funcaoConstruto = declaracao.inicializador;
@@ -443,14 +638,42 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
443
638
  break;
444
639
  }
445
640
  }
446
- const valorInicializador = declaracao.inicializador && declaracao.inicializador.valor
447
- ? declaracao.inicializador.valor
448
- : declaracao.inicializador;
449
- this.variaveis[declaracao.simbolo.lexema] = {
641
+ let valorInicializador = undefined;
642
+ if (declaracao.inicializador) {
643
+ if (declaracao.inicializador.hasOwnProperty('valor')) {
644
+ valorInicializador = declaracao.inicializador.valor;
645
+ }
646
+ else {
647
+ valorInicializador = declaracao.inicializador;
648
+ }
649
+ }
650
+ const variavel = {
651
+ nome: declaracao.simbolo.lexema,
652
+ tipo: declaracao.tipo || 'qualquer',
450
653
  imutavel: false,
451
- tipo: declaracao.tipo,
452
- valor: valorInicializador || undefined,
654
+ valor: valorInicializador,
655
+ inicializada: declaracao.inicializador !== null && declaracao.inicializador !== undefined,
656
+ usada: false,
657
+ hashArquivo: declaracao.simbolo.hashArquivo,
658
+ linha: declaracao.simbolo.linha
453
659
  };
660
+ const declaradaComSucesso = this.gerenciadorEscopos.declarar(declaracao.simbolo.lexema, variavel);
661
+ if (!declaradaComSucesso) {
662
+ const variavelExistente = this.gerenciadorEscopos.buscarNoEscopoAtual(declaracao.simbolo.lexema);
663
+ this.aviso(declaracao.simbolo, `Variável '${declaracao.simbolo.lexema}' já foi declarada na linha ${variavelExistente === null || variavelExistente === void 0 ? void 0 : variavelExistente.linha}.`);
664
+ }
665
+ return Promise.resolve();
666
+ }
667
+ visitarExpressaoFormatacaoEscrita(expressao) {
668
+ if (expressao.expressao) {
669
+ /* for (const expressao of expressao.expressoes) {
670
+ if (expressao instanceof Variavel) {
671
+ this.gerenciadorEscopos.marcarComoUsada(expressao.simbolo.lexema);
672
+ }
673
+ // Recursively check for variables in more complex expressions
674
+ this.marcarVariaveisUsadasEmExpressao(expressao);
675
+ } */
676
+ }
454
677
  return Promise.resolve();
455
678
  }
456
679
  visitarExpressaoRetornar(declaracao) {
@@ -471,45 +694,63 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
471
694
  }
472
695
  let tipoRetornoFuncao = declaracao.funcao.tipo;
473
696
  if (tipoRetornoFuncao) {
697
+ if (!['vazio', 'qualquer'].includes(tipoRetornoFuncao)) {
698
+ const todosOsCaminhosRetornam = this.todosOsCaminhosRetornam(declaracao.funcao.corpo);
699
+ if (!todosOsCaminhosRetornam) {
700
+ this.erro(declaracao.simbolo, `Função '${declaracao.simbolo.lexema}' deve retornar '${tipoRetornoFuncao}' em todos os caminhos de execução.`);
701
+ }
702
+ }
474
703
  let funcaoContemRetorno = declaracao.funcao.corpo.find((c) => c instanceof declaracoes_1.Retorna);
475
- if (funcaoContemRetorno) {
704
+ if (funcaoContemRetorno && funcaoContemRetorno.valor) {
476
705
  if (tipoRetornoFuncao === 'vazio') {
477
706
  this.erro(declaracao.simbolo, `A função não pode ter nenhum tipo de retorno.`);
478
707
  }
479
- const tipoValor = typeof funcaoContemRetorno.valor.valor;
480
- if (!['qualquer'].includes(tipoRetornoFuncao)) {
481
- if (tipoValor === 'string') {
482
- if (tipoRetornoFuncao != 'texto') {
708
+ else {
709
+ const tipoValor = typeof funcaoContemRetorno.valor.valor;
710
+ if (!['qualquer'].includes(tipoRetornoFuncao)) {
711
+ if (tipoValor === 'string' && tipoRetornoFuncao !== 'texto') {
483
712
  this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`);
484
713
  }
485
- }
486
- if (tipoValor === 'number') {
487
- if (!['inteiro', 'real'].includes(tipoRetornoFuncao)) {
714
+ if (tipoValor === 'number' && !['inteiro', 'real', 'número'].includes(tipoRetornoFuncao)) {
488
715
  this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`);
489
716
  }
490
717
  }
491
718
  }
492
719
  }
493
- else {
494
- if (declaracao.funcao.tipoExplicito &&
495
- !['vazio', 'qualquer'].includes(tipoRetornoFuncao)) {
496
- this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`);
497
- }
498
- }
499
720
  }
500
721
  this.funcoes[declaracao.simbolo.lexema] = {
501
722
  valor: declaracao.funcao,
502
723
  };
503
724
  return Promise.resolve();
504
725
  }
726
+ verificarVariaveisNaoUsadas() {
727
+ const naoUsadas = this.gerenciadorEscopos.obterVariaveisNaoUsadas();
728
+ for (let variavel of naoUsadas) {
729
+ // Verifica se já existe um erro associado à variável.
730
+ const temErro = this.diagnosticos.some(d => d.severidade === erros_1.DiagnosticoSeveridade.ERRO &&
731
+ d.simbolo.lexema === variavel.nome);
732
+ // Se a variável já tem um erro associado, não emitir aviso de não usada.
733
+ if (temErro) {
734
+ continue;
735
+ }
736
+ this.aviso({
737
+ lexema: variavel.nome,
738
+ linha: variavel.linha,
739
+ tipo: variavel.tipo,
740
+ hashArquivo: variavel.hashArquivo
741
+ }, `Variável '${variavel.nome}' foi declarada mas nunca usada.`);
742
+ }
743
+ }
505
744
  analisar(declaracoes) {
506
- this.variaveis = {};
745
+ this.gerenciadorEscopos = new gerenciador_escopos_1.GerenciadorEscopos();
507
746
  this.atual = 0;
508
747
  this.diagnosticos = [];
509
748
  while (this.atual < declaracoes.length) {
510
749
  declaracoes[this.atual].aceitar(this);
511
750
  this.atual++;
512
751
  }
752
+ // Verifica variáveis não usadas ao final
753
+ this.verificarVariaveisNaoUsadas();
513
754
  return {
514
755
  diagnosticos: this.diagnosticos,
515
756
  };