@designliquido/delegua 0.64.1 → 0.66.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 (38) hide show
  1. package/analisador-semantico/dialetos/analisador-semantico-pitugues.d.ts +83 -0
  2. package/analisador-semantico/dialetos/analisador-semantico-pitugues.d.ts.map +1 -0
  3. package/analisador-semantico/dialetos/analisador-semantico-pitugues.js +748 -0
  4. package/analisador-semantico/dialetos/analisador-semantico-pitugues.js.map +1 -0
  5. package/analisador-semantico/dialetos/index.d.ts +2 -0
  6. package/analisador-semantico/dialetos/index.d.ts.map +1 -0
  7. package/analisador-semantico/dialetos/index.js +18 -0
  8. package/analisador-semantico/dialetos/index.js.map +1 -0
  9. package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.d.ts +3 -0
  10. package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.d.ts.map +1 -1
  11. package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.js +155 -50
  12. package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.js.map +1 -1
  13. package/bin/package.json +1 -1
  14. package/interpretador/depuracao/avaliador-expressao-depuracao.d.ts +66 -0
  15. package/interpretador/depuracao/avaliador-expressao-depuracao.d.ts.map +1 -0
  16. package/interpretador/depuracao/avaliador-expressao-depuracao.js +145 -0
  17. package/interpretador/depuracao/avaliador-expressao-depuracao.js.map +1 -0
  18. package/interpretador/depuracao/comum.d.ts +11 -1
  19. package/interpretador/depuracao/comum.d.ts.map +1 -1
  20. package/interpretador/depuracao/comum.js +219 -3
  21. package/interpretador/depuracao/comum.js.map +1 -1
  22. package/interpretador/depuracao/index.d.ts +1 -0
  23. package/interpretador/depuracao/index.d.ts.map +1 -1
  24. package/interpretador/depuracao/index.js +1 -0
  25. package/interpretador/depuracao/index.js.map +1 -1
  26. package/interpretador/depuracao/interpretador-base-com-depuracao.d.ts +35 -3
  27. package/interpretador/depuracao/interpretador-base-com-depuracao.d.ts.map +1 -1
  28. package/interpretador/depuracao/interpretador-base-com-depuracao.js +50 -3
  29. package/interpretador/depuracao/interpretador-base-com-depuracao.js.map +1 -1
  30. package/interpretador/depuracao/interpretador-com-depuracao.d.ts +12 -3
  31. package/interpretador/depuracao/interpretador-com-depuracao.d.ts.map +1 -1
  32. package/interpretador/depuracao/interpretador-com-depuracao.js +27 -3
  33. package/interpretador/depuracao/interpretador-com-depuracao.js.map +1 -1
  34. package/interpretador/interpretador-base.d.ts.map +1 -1
  35. package/interpretador/interpretador-base.js +2 -0
  36. package/interpretador/interpretador-base.js.map +1 -1
  37. package/package.json +1 -1
  38. package/umd/delegua.js +157 -50
@@ -0,0 +1,748 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AnalisadorSemanticoPitugues = void 0;
4
+ const construtos_1 = require("../../construtos");
5
+ const declaracoes_1 = require("../../declaracoes");
6
+ const erros_1 = require("../../interfaces/erros");
7
+ const analisador_semantico_base_1 = require("../analisador-semantico-base");
8
+ const gerenciador_escopos_1 = require("../gerenciador-escopos");
9
+ const pilha_variaveis_1 = require("../pilha-variaveis");
10
+ /**
11
+ * O Analisador Semântico de Pituguês.
12
+ */
13
+ class AnalisadorSemanticoPitugues extends analisador_semantico_base_1.AnalisadorSemanticoBase {
14
+ constructor() {
15
+ super();
16
+ this.pilhaVariaveis = new pilha_variaveis_1.PilhaVariaveis();
17
+ this.gerenciadorEscopos = new gerenciador_escopos_1.GerenciadorEscopos();
18
+ this.funcoes = {};
19
+ this.atual = 0;
20
+ this.diagnosticos = [];
21
+ }
22
+ verificarTipoAtribuido(declaracao) {
23
+ if (declaracao.tipo) {
24
+ if (['vetor', 'qualquer[]', 'inteiro[]', 'texto[]'].includes(declaracao.tipo)) {
25
+ if (declaracao.inicializador instanceof construtos_1.Vetor) {
26
+ const vetor = declaracao.inicializador;
27
+ const vetorSemSeparadores = vetor.valores.filter((v) => v.constructor !== construtos_1.Separador);
28
+ if (declaracao.tipo === 'inteiro[]') {
29
+ const apenasValores = vetorSemSeparadores.find((v) => typeof (v === null || v === void 0 ? void 0 : v.valor) !== 'number');
30
+ if (apenasValores) {
31
+ this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}': é esperado um valor do tipo vetor de inteiro ou real. Atual: ${vetor.tipo}.`);
32
+ }
33
+ }
34
+ if (declaracao.tipo === 'texto[]') {
35
+ const apenasValores = vetorSemSeparadores.find((v) => typeof (v === null || v === void 0 ? void 0 : v.valor) !== 'string');
36
+ if (apenasValores) {
37
+ this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}': é esperado um valor do tipo vetor de texto. Atual: ${vetor.tipo}.`);
38
+ }
39
+ }
40
+ }
41
+ else {
42
+ this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}': é esperado um vetor de elementos.`);
43
+ }
44
+ }
45
+ if (declaracao.inicializador instanceof construtos_1.Literal) {
46
+ const literal = declaracao.inicializador;
47
+ if (declaracao.tipo === 'texto' && literal.tipo !== 'texto') {
48
+ this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}': é esperado um valor do tipo texto. Atual: ${literal.tipo}.`);
49
+ }
50
+ if (['inteiro', 'número', 'real'].includes(declaracao.tipo) &&
51
+ !['inteiro', 'número', 'real'].includes(literal.tipo)) {
52
+ this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}': é esperado um valor do tipo número. Atual: ${literal.tipo}.`);
53
+ }
54
+ }
55
+ if (declaracao.inicializador instanceof construtos_1.Leia) {
56
+ if (!['qualquer', 'texto'].includes(declaracao.tipo)) {
57
+ this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}', Função 'leia()' sempre retorna 'texto'.`);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ visitarExpressaoTipoDe(expressao) {
63
+ return this.verificarTipoDe(expressao.valor);
64
+ }
65
+ verificarTipoDe(valor) {
66
+ switch (valor.constructor) {
67
+ case construtos_1.Agrupamento:
68
+ const valorAgrupamento = valor;
69
+ return this.verificarTipoDe(valorAgrupamento.expressao);
70
+ case construtos_1.Binario:
71
+ const valorBinario = valor;
72
+ this.verificarTipoDe(valorBinario.direita);
73
+ this.verificarTipoDe(valorBinario.esquerda);
74
+ break;
75
+ case construtos_1.Variavel:
76
+ const valorVariavel = valor;
77
+ return this.verificarVariavel(valorVariavel);
78
+ }
79
+ return Promise.resolve();
80
+ }
81
+ visitarExpressaoFalhar(expressao) {
82
+ return this.verificarFalhar(expressao.explicacao);
83
+ }
84
+ verificarFalhar(valor) {
85
+ if (valor instanceof construtos_1.Binario) {
86
+ this.verificarFalhar(valor.direita);
87
+ this.verificarFalhar(valor.esquerda);
88
+ }
89
+ if (valor instanceof construtos_1.Agrupamento) {
90
+ return this.verificarFalhar(valor.expressao);
91
+ }
92
+ if (valor instanceof construtos_1.Variavel) {
93
+ return this.verificarVariavel(valor);
94
+ }
95
+ return Promise.resolve();
96
+ }
97
+ comparacaoArgumentosContraParametrosFuncao(simboloFuncao, parametros, argumentos) {
98
+ if (parametros.length !== argumentos.length) {
99
+ this.erro(simboloFuncao, `Função '${simboloFuncao.lexema}' espera ${parametros.length} parâmetros. Atual: ${argumentos.length}.`);
100
+ }
101
+ for (let [indice, parametro] of parametros.entries()) {
102
+ // TODO: `argumento` pode ser Literal (tipo já resolvido) ou variável (tipo inferido em outra etapa).
103
+ const argumento = argumentos[indice];
104
+ if (argumento) {
105
+ if (parametro.tipoDado === 'texto' && argumento.tipo !== 'texto') {
106
+ this.erro(simboloFuncao, `O valor passado para o parâmetro '${parametro.nome.lexema}' (${parametro.tipoDado}) é diferente do esperado pela função (${argumento.tipo}).`);
107
+ }
108
+ else if (['inteiro', 'número', 'real'].includes(parametro.tipoDado)) {
109
+ // Aqui, se houver diferença entre os tipos do parâmetro e do argumento, não há erro,
110
+ // porque Delégua pode trabalhar com conversões implícitas.
111
+ // Isso pode ou não mudar no futuro.
112
+ if (!['inteiro', 'número', 'real'].includes(argumento.tipo)) {
113
+ this.erro(simboloFuncao, `O valor passado para o parâmetro '${parametro.nome.lexema}' (${parametro.tipoDado}) é diferente do esperado pela função (${argumento.tipo}).`);
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ visitarChamadaPorArgumentoReferenciaFuncao(argumentoReferenciaFuncao, argumentos) {
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;
124
+ if (!variavelCorrespondente) {
125
+ return;
126
+ }
127
+ this.comparacaoArgumentosContraParametrosFuncao(argumentoReferenciaFuncao.simboloFuncao, variavelCorrespondente.parametros, argumentos);
128
+ }
129
+ visitarChamadaPorReferenciaFuncao(referenciaFuncao, argumentos) {
130
+ const funcaoCorrespondente = this.funcoes[referenciaFuncao.simboloFuncao.lexema];
131
+ if (!funcaoCorrespondente) {
132
+ return;
133
+ }
134
+ this.comparacaoArgumentosContraParametrosFuncao(referenciaFuncao.simboloFuncao, funcaoCorrespondente.valor.parametros, argumentos);
135
+ }
136
+ visitarChamadaPorVariavel(entidadeChamadaVariavel, argumentos) {
137
+ const variavel = entidadeChamadaVariavel;
138
+ const funcaoChamada = this.gerenciadorEscopos.buscar(variavel.simbolo.lexema) || this.funcoes[variavel.simbolo.lexema];
139
+ if (!funcaoChamada) {
140
+ this.erro(entidadeChamadaVariavel.simbolo, `Chamada da função '${entidadeChamadaVariavel.simbolo.lexema}' não existe.`);
141
+ return Promise.resolve();
142
+ }
143
+ const funcao = funcaoChamada.valor;
144
+ this.comparacaoArgumentosContraParametrosFuncao(entidadeChamadaVariavel.simbolo, funcao.parametros, argumentos);
145
+ }
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
+ }
152
+ switch (expressao.entidadeChamada.constructor) {
153
+ case construtos_1.ArgumentoReferenciaFuncao:
154
+ const entidadeChamadaArgumentoReferenciaFuncao = expressao.entidadeChamada;
155
+ this.visitarChamadaPorArgumentoReferenciaFuncao(entidadeChamadaArgumentoReferenciaFuncao, expressao.argumentos);
156
+ break;
157
+ case construtos_1.ReferenciaFuncao:
158
+ const entidadeChamadaReferenciaFuncao = expressao.entidadeChamada;
159
+ this.visitarChamadaPorReferenciaFuncao(entidadeChamadaReferenciaFuncao, expressao.argumentos);
160
+ break;
161
+ case construtos_1.Variavel:
162
+ const entidadeChamadaVariavel = expressao.entidadeChamada;
163
+ this.visitarChamadaPorVariavel(entidadeChamadaVariavel, expressao.argumentos);
164
+ break;
165
+ }
166
+ return Promise.resolve();
167
+ }
168
+ visitarExpressaoDeAtribuicao(expressao) {
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
+ // Marca como inicializada após atribuição
184
+ this.gerenciadorEscopos.marcarComoInicializada(simboloAlvo.lexema, expressao.valor);
185
+ // TODO: Readaptar para trabalhar com `expressao.alvo` sendo um construto.
186
+ switch (expressao.alvo.constructor) {
187
+ case construtos_1.Variavel:
188
+ const alvoVariavel = expressao.alvo;
189
+ simboloAlvo = alvoVariavel.simbolo;
190
+ break;
191
+ default:
192
+ // throw new Error(`Implementar atribuição para ${expressao.alvo.constructor}.`);
193
+ return Promise.resolve();
194
+ }
195
+ let valor = this.gerenciadorEscopos.buscar(simboloAlvo.lexema);
196
+ if (!valor) {
197
+ this.erro(simboloAlvo, `Variável ${simboloAlvo.lexema} ainda não foi declarada até este ponto.`);
198
+ return Promise.resolve();
199
+ }
200
+ if (valor.tipo) {
201
+ if (expressao.valor instanceof construtos_1.Literal && valor.tipo.includes('[]')) {
202
+ this.erro(simboloAlvo, `Atribuição inválida, esperado tipo '${valor.tipo}' na atribuição.`);
203
+ return Promise.resolve();
204
+ }
205
+ if (expressao.valor instanceof construtos_1.Vetor && !valor.tipo.includes('[]')) {
206
+ this.erro(simboloAlvo, `Atribuição inválida, esperado tipo '${valor.tipo}' na atribuição.`);
207
+ return Promise.resolve();
208
+ }
209
+ if (expressao.valor instanceof construtos_1.Literal) {
210
+ let valorLiteral = typeof expressao.valor.valor;
211
+ if (!['qualquer'].includes(valor.tipo)) {
212
+ if (valorLiteral === 'string') {
213
+ if (valor.tipo != 'texto') {
214
+ this.erro(simboloAlvo, `Esperado tipo '${valor.tipo}' na atribuição.`);
215
+ return Promise.resolve();
216
+ }
217
+ }
218
+ if (valorLiteral === 'number') {
219
+ if (!['inteiro', 'número', 'real'].includes(valor.tipo)) {
220
+ this.erro(simboloAlvo, `Esperado tipo '${valor.tipo}' na atribuição.`);
221
+ return Promise.resolve();
222
+ }
223
+ }
224
+ }
225
+ }
226
+ if (expressao.valor instanceof construtos_1.Vetor) {
227
+ let valoresSemSeparador = expressao.valor.valores.filter((v) => v.constructor !== construtos_1.Separador);
228
+ if (!['qualquer[]'].includes(valor.tipo)) {
229
+ if (valor.tipo === 'texto[]') {
230
+ if (!valoresSemSeparador.every((v) => typeof v.valor === 'string')) {
231
+ this.erro(simboloAlvo, `Esperado tipo '${valor.tipo}' na atribuição.`);
232
+ return Promise.resolve();
233
+ }
234
+ }
235
+ if (['inteiro[]', 'numero[]'].includes(valor.tipo)) {
236
+ if (!valoresSemSeparador.every((v) => typeof v.valor === 'number')) {
237
+ this.erro(simboloAlvo, `Esperado tipo '${valor.tipo}' na atribuição.`);
238
+ return Promise.resolve();
239
+ }
240
+ }
241
+ }
242
+ }
243
+ }
244
+ }
245
+ async visitarDeclaracaoDeExpressao(declaracao) {
246
+ return await declaracao.expressao.aceitar(this);
247
+ }
248
+ visitarDeclaracaoEscolha(declaracao) {
249
+ const identificadorOuLiteral = declaracao.identificadorOuLiteral;
250
+ const tipo = identificadorOuLiteral.tipo;
251
+ for (let caminho of declaracao.caminhos) {
252
+ for (let condicao of caminho.condicoes) {
253
+ switch (condicao.constructor) {
254
+ case construtos_1.Literal:
255
+ const condicaoLiteral = condicao;
256
+ if (condicaoLiteral.tipo !== tipo) {
257
+ this.erro({
258
+ lexema: condicaoLiteral.valor,
259
+ tipo: condicaoLiteral.tipo,
260
+ linha: condicaoLiteral.linha,
261
+ hashArquivo: condicaoLiteral.hashArquivo,
262
+ }, `'caso ${condicaoLiteral.valor}:' não é do mesmo tipo esperado em 'escolha' (esperado: ${tipo}, atual: ${condicaoLiteral.tipo}).`);
263
+ }
264
+ break;
265
+ case construtos_1.Variavel:
266
+ const condicaoVariavel = condicao;
267
+ this.verificarVariavel(condicaoVariavel);
268
+ const variavelHipotetica = this.gerenciadorEscopos.buscar(condicaoVariavel.simbolo.lexema);
269
+ if (variavelHipotetica && typeof variavelHipotetica.valor !== tipo) {
270
+ this.erro(condicaoVariavel.simbolo, `'caso ${condicaoVariavel.simbolo.lexema}:' não é do mesmo tipo esperado em 'escolha'`);
271
+ }
272
+ break;
273
+ }
274
+ }
275
+ }
276
+ return Promise.resolve();
277
+ }
278
+ visitarDeclaracaoEnquanto(declaracao) {
279
+ return this.verificarCondicao(declaracao.condicao);
280
+ }
281
+ verificarCondicao(condicao) {
282
+ if (condicao instanceof construtos_1.Agrupamento) {
283
+ return this.verificarCondicao(condicao.expressao);
284
+ }
285
+ if (condicao instanceof construtos_1.Variavel) {
286
+ return this.verificarVariavelBinaria(condicao);
287
+ }
288
+ if (condicao instanceof construtos_1.Binario) {
289
+ return this.verificarBinario(condicao);
290
+ }
291
+ if (condicao instanceof construtos_1.Logico) {
292
+ return this.verificarLogico(condicao);
293
+ }
294
+ if (condicao instanceof construtos_1.Chamada) {
295
+ return this.verificarChamada(condicao);
296
+ }
297
+ return Promise.resolve();
298
+ }
299
+ verificarVariavelBinaria(variavel) {
300
+ this.verificarVariavel(variavel);
301
+ const variavelHipotetica = this.gerenciadorEscopos.buscar(variavel.simbolo.lexema);
302
+ if (variavelHipotetica &&
303
+ !(variavelHipotetica.valor instanceof construtos_1.Binario) &&
304
+ typeof variavelHipotetica.valor !== 'boolean') {
305
+ this.erro(variavel.simbolo, `Esperado tipo 'lógico' na condição do 'enquanto'.`);
306
+ }
307
+ return Promise.resolve();
308
+ }
309
+ verificarVariavel(variavel) {
310
+ const variavelEscopo = this.gerenciadorEscopos.buscar(variavel.simbolo.lexema);
311
+ if (!variavelEscopo) {
312
+ this.erro(variavel.simbolo, `Variável '${variavel.simbolo.lexema}' ainda não foi declarada até este ponto.`);
313
+ return Promise.resolve();
314
+ }
315
+ // Marca como usada
316
+ this.gerenciadorEscopos.marcarComoUsada(variavel.simbolo.lexema);
317
+ // Verifica se foi inicializada
318
+ if (!variavelEscopo.inicializada) {
319
+ this.aviso(variavel.simbolo, `Variável '${variavel.simbolo.lexema}' pode não ter sido inicializada antes do uso.`);
320
+ }
321
+ return Promise.resolve();
322
+ }
323
+ verificarBinario(binario) {
324
+ this.verificarExistenciaConstruto(binario.direita);
325
+ this.verificarExistenciaConstruto(binario.esquerda);
326
+ this.verificarOperadorBinario(binario);
327
+ return Promise.resolve();
328
+ }
329
+ verificarOperadorBinario(binario) {
330
+ if (binario.esquerda instanceof construtos_1.Binario) {
331
+ this.verificarOperadorBinario(binario.esquerda);
332
+ }
333
+ if (binario.direita instanceof construtos_1.Binario) {
334
+ this.verificarOperadorBinario(binario.direita);
335
+ }
336
+ const operadoresMatematicos = ['ADICAO', 'SUBTRACAO', 'MULTIPLICACAO', 'DIVISAO', 'MODULO'];
337
+ if (operadoresMatematicos.includes(binario.operador.tipo)) {
338
+ this.verificarTiposOperandos(binario);
339
+ }
340
+ if (binario.operador.tipo === 'DIVISAO') {
341
+ this.verificarDivisaoPorZero(binario);
342
+ }
343
+ }
344
+ /**
345
+ * Verifica se os tipos dos operandos são compatíveis
346
+ */
347
+ verificarTiposOperandos(binario) {
348
+ const tipoEsquerda = this.obterTipoExpressao(binario.esquerda);
349
+ const tipoDireita = this.obterTipoExpressao(binario.direita);
350
+ if (tipoEsquerda && tipoDireita && tipoEsquerda !== tipoDireita) {
351
+ // Verificar se são tipos numéricos compatíveis
352
+ const tiposNumericos = ['inteiro', 'número', 'real'];
353
+ const ambosNumericos = tiposNumericos.includes(tipoEsquerda) &&
354
+ tiposNumericos.includes(tipoDireita);
355
+ if (!ambosNumericos) {
356
+ this.aviso(binario.operador, `Operação entre tipos diferentes: tipo esquerdo '${tipoEsquerda}' e tipo direito '${tipoDireita}'. O resultado será resolvido implicitamente.`);
357
+ }
358
+ }
359
+ }
360
+ /**
361
+ * Verifica divisão por zero recursivamente
362
+ */
363
+ verificarDivisaoPorZero(binario) {
364
+ const valorDireita = this.avaliarExpressaoConstante(binario.direita);
365
+ if (valorDireita === 0) {
366
+ this.erro(binario.operador, `Divisão por zero.`);
367
+ }
368
+ }
369
+ /**
370
+ * Tenta avaliar uma expressão em tempo de compilação para detectar valores constantes
371
+ * Retorna o valor se puder ser determinado, ou null caso contrário
372
+ */
373
+ avaliarExpressaoConstante(expressao) {
374
+ if (expressao instanceof construtos_1.Literal) {
375
+ return expressao.valor;
376
+ }
377
+ if (expressao instanceof construtos_1.Variavel) {
378
+ const variavel = this.gerenciadorEscopos.buscar(expressao.simbolo.lexema);
379
+ if (!variavel) {
380
+ return null;
381
+ }
382
+ if (variavel.imutavel && variavel.inicializada) {
383
+ return variavel.valor;
384
+ }
385
+ if (variavel.inicializada && variavel.valor !== undefined) {
386
+ return variavel.valor;
387
+ }
388
+ return null;
389
+ }
390
+ if (expressao instanceof construtos_1.Binario) {
391
+ const esquerda = this.avaliarExpressaoConstante(expressao.esquerda);
392
+ const direita = this.avaliarExpressaoConstante(expressao.direita);
393
+ if (esquerda !== null && direita !== null) {
394
+ return this.calcularOperacaoBinaria(expressao.operador.tipo, esquerda, direita);
395
+ }
396
+ }
397
+ if (expressao instanceof construtos_1.Agrupamento) {
398
+ return this.avaliarExpressaoConstante(expressao.expressao);
399
+ }
400
+ return null;
401
+ }
402
+ /**
403
+ * Calcula o resultado de uma operação binária em tempo de compilação
404
+ */
405
+ calcularOperacaoBinaria(operador, esquerda, direita) {
406
+ try {
407
+ switch (operador) {
408
+ case 'ADICAO':
409
+ return esquerda + direita;
410
+ case 'SUBTRACAO':
411
+ return esquerda - direita;
412
+ case 'MULTIPLICACAO':
413
+ return esquerda * direita;
414
+ case 'DIVISAO':
415
+ return esquerda / direita;
416
+ case 'MODULO':
417
+ return esquerda % direita;
418
+ case 'MAIOR':
419
+ return esquerda > direita;
420
+ case 'MAIOR_IGUAL':
421
+ return esquerda >= direita;
422
+ case 'MENOR':
423
+ return esquerda < direita;
424
+ case 'MENOR_IGUAL':
425
+ return esquerda <= direita;
426
+ case 'IGUAL':
427
+ return esquerda === direita;
428
+ case 'DIFERENTE':
429
+ return esquerda !== direita;
430
+ default:
431
+ return null;
432
+ }
433
+ }
434
+ catch (e) {
435
+ return null;
436
+ }
437
+ }
438
+ /**
439
+ * Obtém o tipo de uma expressão (pode ser Literal, Variavel, ou Binario)
440
+ */
441
+ obterTipoExpressao(expressao) {
442
+ if (expressao instanceof construtos_1.Literal) {
443
+ return expressao.tipo;
444
+ }
445
+ if (expressao instanceof construtos_1.Variavel) {
446
+ const variavel = this.gerenciadorEscopos.buscar(expressao.simbolo.lexema);
447
+ return (variavel === null || variavel === void 0 ? void 0 : variavel.tipo) || null;
448
+ }
449
+ if (expressao instanceof construtos_1.Binario) {
450
+ // Para binários, tentamos inferir o tipo baseado nos operandos
451
+ return this.inferirTipoBinario(expressao);
452
+ }
453
+ if (expressao instanceof construtos_1.Agrupamento) {
454
+ return this.obterTipoExpressao(expressao.expressao);
455
+ }
456
+ return null;
457
+ }
458
+ /**
459
+ * Infere o tipo de resultado de uma operação binária
460
+ */
461
+ inferirTipoBinario(binario) {
462
+ const tipoEsquerda = this.obterTipoExpressao(binario.esquerda);
463
+ const tipoDireita = this.obterTipoExpressao(binario.direita);
464
+ if (!tipoEsquerda || !tipoDireita) {
465
+ return null;
466
+ }
467
+ const operadoresMatematicos = ['ADICAO', 'SUBTRACAO', 'MULTIPLICACAO', 'DIVISAO', 'MODULO'];
468
+ const operadoresComparacao = ['MAIOR', 'MAIOR_IGUAL', 'MENOR', 'MENOR_IGUAL', 'IGUAL', 'DIFERENTE'];
469
+ if (operadoresComparacao.includes(binario.operador.tipo)) {
470
+ return 'lógico';
471
+ }
472
+ if (operadoresMatematicos.includes(binario.operador.tipo)) {
473
+ const tiposNumericos = ['inteiro', 'número', 'real'];
474
+ if (tiposNumericos.includes(tipoEsquerda) && tiposNumericos.includes(tipoDireita)) {
475
+ // Se um dos lados é 'real', o resultado é 'real'
476
+ if (tipoEsquerda === 'real' || tipoDireita === 'real') {
477
+ return 'real';
478
+ }
479
+ return 'número';
480
+ }
481
+ // Concatenação de textos
482
+ if (tipoEsquerda === 'texto' || tipoDireita === 'texto') {
483
+ return 'texto';
484
+ }
485
+ }
486
+ return 'qualquer';
487
+ }
488
+ verificarExistenciaConstruto(construto) {
489
+ if (construto instanceof construtos_1.Variavel) {
490
+ if (!this.gerenciadorEscopos.buscar(construto.simbolo.lexema)) {
491
+ this.erro(construto.simbolo, `Variável ${construto.simbolo.lexema} ainda não foi declarada até este ponto.`);
492
+ return;
493
+ }
494
+ this.gerenciadorEscopos.marcarComoUsada(construto.simbolo.lexema);
495
+ return;
496
+ }
497
+ if (construto instanceof construtos_1.Binario) {
498
+ this.verificarBinario(construto);
499
+ }
500
+ }
501
+ verificarLogico(logico) {
502
+ this.verificarLadoLogico(logico.direita);
503
+ this.verificarLadoLogico(logico.esquerda);
504
+ return Promise.resolve();
505
+ }
506
+ verificarChamada(chamada) {
507
+ switch (chamada.entidadeChamada.constructor) {
508
+ case construtos_1.Variavel:
509
+ let entidadeChamadaVariavel = chamada.entidadeChamada;
510
+ if (!this.funcoes[entidadeChamadaVariavel.simbolo.lexema]) {
511
+ this.erro(entidadeChamadaVariavel.simbolo, `Chamada da função '${entidadeChamadaVariavel.simbolo.lexema}' não existe.`);
512
+ }
513
+ break;
514
+ }
515
+ return Promise.resolve();
516
+ }
517
+ verificarLadoLogico(lado) {
518
+ if (lado instanceof construtos_1.Variavel) {
519
+ let variavel = lado;
520
+ this.verificarVariavelBinaria(variavel);
521
+ }
522
+ }
523
+ /**
524
+ * Verifica interpolações de texto e marca variáveis como usadas
525
+ */
526
+ verificarInterpolacaoTexto(texto, literal) {
527
+ // Regex para encontrar ${identificador}
528
+ const regexInterpolacao = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
529
+ let match;
530
+ while ((match = regexInterpolacao.exec(texto)) !== null) {
531
+ const nomeVariavel = match[1];
532
+ // Verifica se a variável existe
533
+ const variavel = this.gerenciadorEscopos.buscar(nomeVariavel);
534
+ const funcao = this.funcoes[nomeVariavel];
535
+ if (!variavel && !funcao) {
536
+ this.erro({
537
+ lexema: nomeVariavel,
538
+ tipo: 'IDENTIFICADOR',
539
+ linha: literal.linha,
540
+ hashArquivo: literal.hashArquivo,
541
+ literal: null
542
+ }, `Variável ou função '${nomeVariavel}' usada em interpolação não existe.`);
543
+ }
544
+ else if (variavel) {
545
+ // Marca como usada
546
+ this.gerenciadorEscopos.marcarComoUsada(nomeVariavel);
547
+ // Verifica se foi inicializada
548
+ if (!variavel.inicializada) {
549
+ this.aviso({
550
+ lexema: nomeVariavel,
551
+ tipo: 'IDENTIFICADOR',
552
+ linha: literal.linha,
553
+ hashArquivo: literal.hashArquivo,
554
+ literal: null
555
+ }, `Variável '${nomeVariavel}' usada em interpolação pode não ter sido inicializada.`);
556
+ }
557
+ }
558
+ }
559
+ }
560
+ visitarDeclaracaoEscreva(declaracao) {
561
+ if (declaracao.argumentos.length === 0) {
562
+ const { linha, hashArquivo } = declaracao;
563
+ const simbolo = {
564
+ literal: '',
565
+ tipo: '',
566
+ lexema: 'escreva',
567
+ linha,
568
+ hashArquivo,
569
+ };
570
+ this.erro(simbolo, `É preciso ter um ou mais parametros para 'escreva(...)'`);
571
+ return Promise.resolve();
572
+ }
573
+ for (const argumento of declaracao.argumentos) {
574
+ this.marcarVariaveisUsadasEmExpressao(argumento);
575
+ if (argumento instanceof construtos_1.Literal && argumento.tipo === 'texto') {
576
+ this.verificarInterpolacaoTexto(argumento.valor, argumento);
577
+ }
578
+ if (argumento instanceof construtos_1.Variavel) {
579
+ const possivelVariavel = this.gerenciadorEscopos.buscar(argumento.simbolo.lexema);
580
+ const possivelFuncao = this.funcoes[argumento.simbolo.lexema];
581
+ if (!possivelVariavel && !possivelFuncao) {
582
+ this.erro(argumento.simbolo, `Variável ou função '${argumento.simbolo.lexema}' não existe.`);
583
+ continue;
584
+ }
585
+ if (possivelVariavel && possivelVariavel.valor === undefined) {
586
+ this.aviso(argumento.simbolo, `Variável '${argumento.simbolo.lexema}' não foi inicializada.`);
587
+ }
588
+ }
589
+ }
590
+ return Promise.resolve();
591
+ }
592
+ visitarDeclaracaoConst(declaracao) {
593
+ var _a;
594
+ this.verificarTipoAtribuido(declaracao);
595
+ if (declaracao.inicializador) {
596
+ this.marcarVariaveisUsadasEmExpressao(declaracao.inicializador);
597
+ }
598
+ const constanteCorrespondente = this.gerenciadorEscopos.buscarNoEscopoAtual(declaracao.simbolo.lexema);
599
+ if (constanteCorrespondente) {
600
+ this.erro(declaracao.simbolo, 'Declaração de constante já feita.');
601
+ return Promise.resolve();
602
+ }
603
+ this.gerenciadorEscopos.declarar(declaracao.simbolo.lexema, {
604
+ nome: declaracao.simbolo.lexema,
605
+ tipo: declaracao.tipo || 'qualquer',
606
+ imutavel: true,
607
+ valor: (_a = declaracao.inicializador) === null || _a === void 0 ? void 0 : _a.valor,
608
+ inicializada: true,
609
+ usada: false,
610
+ hashArquivo: declaracao.simbolo.hashArquivo,
611
+ linha: declaracao.simbolo.linha
612
+ });
613
+ // TODO: Verificar inicializador.
614
+ return Promise.resolve();
615
+ }
616
+ visitarDeclaracaoVar(declaracao) {
617
+ this.verificarTipoAtribuido(declaracao);
618
+ if (declaracao.inicializador) {
619
+ this.marcarVariaveisUsadasEmExpressao(declaracao.inicializador);
620
+ switch (declaracao.inicializador.constructor) {
621
+ case construtos_1.FuncaoConstruto:
622
+ const funcaoConstruto = declaracao.inicializador;
623
+ if (funcaoConstruto.parametros.length >= 255) {
624
+ this.erro(declaracao.simbolo, 'Função não pode ter mais de 255 parâmetros.');
625
+ }
626
+ break;
627
+ }
628
+ }
629
+ let valorInicializador = undefined;
630
+ if (declaracao.inicializador) {
631
+ if (declaracao.inicializador.hasOwnProperty('valor')) {
632
+ valorInicializador = declaracao.inicializador.valor;
633
+ }
634
+ else {
635
+ valorInicializador = declaracao.inicializador;
636
+ }
637
+ }
638
+ const variavel = {
639
+ nome: declaracao.simbolo.lexema,
640
+ tipo: declaracao.tipo || 'qualquer',
641
+ imutavel: false,
642
+ valor: valorInicializador,
643
+ inicializada: declaracao.inicializador !== null && declaracao.inicializador !== undefined,
644
+ usada: false,
645
+ hashArquivo: declaracao.simbolo.hashArquivo,
646
+ linha: declaracao.simbolo.linha
647
+ };
648
+ const declaradaComSucesso = this.gerenciadorEscopos.declarar(declaracao.simbolo.lexema, variavel);
649
+ if (!declaradaComSucesso) {
650
+ const variavelExistente = this.gerenciadorEscopos.buscarNoEscopoAtual(declaracao.simbolo.lexema);
651
+ this.aviso(declaracao.simbolo, `Variável '${declaracao.simbolo.lexema}' já foi declarada na linha ${variavelExistente === null || variavelExistente === void 0 ? void 0 : variavelExistente.linha}.`);
652
+ }
653
+ return Promise.resolve();
654
+ }
655
+ visitarExpressaoFormatacaoEscrita(expressao) {
656
+ if (expressao.expressao) {
657
+ /* for (const expressao of expressao.expressoes) {
658
+ if (expressao instanceof Variavel) {
659
+ this.gerenciadorEscopos.marcarComoUsada(expressao.simbolo.lexema);
660
+ }
661
+ // Recursively check for variables in more complex expressions
662
+ this.marcarVariaveisUsadasEmExpressao(expressao);
663
+ } */
664
+ }
665
+ return Promise.resolve();
666
+ }
667
+ visitarExpressaoRetornar(declaracao) {
668
+ return Promise.resolve(null);
669
+ }
670
+ visitarExpressaoDeVariavel(expressao) {
671
+ if (expressao instanceof construtos_1.Variavel) {
672
+ return this.verificarVariavel(expressao);
673
+ }
674
+ return Promise.resolve();
675
+ }
676
+ visitarDeclaracaoDefinicaoFuncao(declaracao) {
677
+ if (declaracao.funcao.tipo === undefined) {
678
+ this.erro(declaracao.simbolo, `Declaração de retorno da função é inválido.`);
679
+ }
680
+ if (declaracao.funcao.parametros.length >= 255) {
681
+ this.erro(declaracao.simbolo, 'Função não pode ter mais de 255 parâmetros.');
682
+ }
683
+ let tipoRetornoFuncao = declaracao.funcao.tipo;
684
+ if (tipoRetornoFuncao) {
685
+ if (!['vazio', 'qualquer'].includes(tipoRetornoFuncao)) {
686
+ const todosOsCaminhosRetornam = this.todosOsCaminhosRetornam(declaracao.funcao.corpo);
687
+ if (!todosOsCaminhosRetornam) {
688
+ this.erro(declaracao.simbolo, `Função '${declaracao.simbolo.lexema}' deve retornar '${tipoRetornoFuncao}' em todos os caminhos de execução.`);
689
+ }
690
+ }
691
+ let funcaoContemRetorno = declaracao.funcao.corpo.find((c) => c instanceof declaracoes_1.Retorna);
692
+ if (funcaoContemRetorno && funcaoContemRetorno.valor) {
693
+ if (tipoRetornoFuncao === 'vazio') {
694
+ this.erro(declaracao.simbolo, `A função não pode ter nenhum tipo de retorno.`);
695
+ }
696
+ else {
697
+ const tipoValor = typeof funcaoContemRetorno.valor.valor;
698
+ if (!['qualquer'].includes(tipoRetornoFuncao)) {
699
+ if (tipoValor === 'string' && tipoRetornoFuncao !== 'texto') {
700
+ this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`);
701
+ }
702
+ if (tipoValor === 'number' && !['inteiro', 'real', 'número'].includes(tipoRetornoFuncao)) {
703
+ this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`);
704
+ }
705
+ }
706
+ }
707
+ }
708
+ }
709
+ this.funcoes[declaracao.simbolo.lexema] = {
710
+ valor: declaracao.funcao,
711
+ };
712
+ return Promise.resolve();
713
+ }
714
+ verificarVariaveisNaoUsadas() {
715
+ const naoUsadas = this.gerenciadorEscopos.obterVariaveisNaoUsadas();
716
+ for (let variavel of naoUsadas) {
717
+ // Verifica se já existe um erro associado à variável.
718
+ const temErro = this.diagnosticos.some(d => d.severidade === erros_1.DiagnosticoSeveridade.ERRO &&
719
+ d.simbolo.lexema === variavel.nome);
720
+ // Se a variável já tem um erro associado, não emitir aviso de não usada.
721
+ if (temErro) {
722
+ continue;
723
+ }
724
+ this.aviso({
725
+ lexema: variavel.nome,
726
+ linha: variavel.linha,
727
+ tipo: variavel.tipo,
728
+ hashArquivo: variavel.hashArquivo
729
+ }, `Variável '${variavel.nome}' foi declarada mas nunca usada.`);
730
+ }
731
+ }
732
+ analisar(declaracoes) {
733
+ this.gerenciadorEscopos = new gerenciador_escopos_1.GerenciadorEscopos();
734
+ this.atual = 0;
735
+ this.diagnosticos = [];
736
+ while (this.atual < declaracoes.length) {
737
+ declaracoes[this.atual].aceitar(this);
738
+ this.atual++;
739
+ }
740
+ // Verifica variáveis não usadas ao final
741
+ this.verificarVariaveisNaoUsadas();
742
+ return {
743
+ diagnosticos: this.diagnosticos,
744
+ };
745
+ }
746
+ }
747
+ exports.AnalisadorSemanticoPitugues = AnalisadorSemanticoPitugues;
748
+ //# sourceMappingURL=analisador-semantico-pitugues.js.map