@designliquido/delegua 0.64.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/analisador-semantico/analisador-semantico-base.d.ts +18 -1
- package/analisador-semantico/analisador-semantico-base.d.ts.map +1 -1
- package/analisador-semantico/analisador-semantico-base.js +129 -0
- package/analisador-semantico/analisador-semantico-base.js.map +1 -1
- package/analisador-semantico/analisador-semantico.d.ts +38 -19
- package/analisador-semantico/analisador-semantico.d.ts.map +1 -1
- package/analisador-semantico/analisador-semantico.js +353 -112
- package/analisador-semantico/analisador-semantico.js.map +1 -1
- package/analisador-semantico/escopo-variavel.d.ts +11 -0
- package/analisador-semantico/escopo-variavel.d.ts.map +1 -0
- package/analisador-semantico/escopo-variavel.js +3 -0
- package/analisador-semantico/escopo-variavel.js.map +1 -0
- package/analisador-semantico/funcao-hipotetica-interface.d.ts +5 -0
- package/analisador-semantico/funcao-hipotetica-interface.d.ts.map +1 -0
- package/analisador-semantico/funcao-hipotetica-interface.js +3 -0
- package/analisador-semantico/funcao-hipotetica-interface.js.map +1 -0
- package/analisador-semantico/gerenciador-escopos.d.ts +15 -0
- package/analisador-semantico/gerenciador-escopos.d.ts.map +1 -0
- package/analisador-semantico/gerenciador-escopos.js +77 -0
- package/analisador-semantico/gerenciador-escopos.js.map +1 -0
- package/analisador-semantico/index.d.ts +4 -0
- package/analisador-semantico/index.d.ts.map +1 -1
- package/analisador-semantico/index.js +4 -0
- package/analisador-semantico/index.js.map +1 -1
- package/analisador-semantico/variavel-hipotetica-interface.d.ts +7 -0
- package/analisador-semantico/variavel-hipotetica-interface.d.ts.map +1 -0
- package/analisador-semantico/variavel-hipotetica-interface.js +3 -0
- package/analisador-semantico/variavel-hipotetica-interface.js.map +1 -0
- package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.d.ts.map +1 -1
- package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.js +111 -48
- package/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues.js.map +1 -1
- package/bin/package.json +1 -1
- package/interpretador/depuracao/avaliador-expressao-depuracao.d.ts +66 -0
- package/interpretador/depuracao/avaliador-expressao-depuracao.d.ts.map +1 -0
- package/interpretador/depuracao/avaliador-expressao-depuracao.js +151 -0
- package/interpretador/depuracao/avaliador-expressao-depuracao.js.map +1 -0
- package/interpretador/depuracao/index.d.ts +1 -0
- package/interpretador/depuracao/index.d.ts.map +1 -1
- package/interpretador/depuracao/index.js +1 -0
- package/interpretador/depuracao/index.js.map +1 -1
- package/package.json +1 -1
- package/umd/delegua.js +111 -48
|
@@ -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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
311
|
-
if (!
|
|
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.
|
|
318
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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 (
|
|
351
|
-
|
|
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(
|
|
356
|
-
this.verificarLadoLogico(
|
|
357
|
-
this.verificarLadoLogico(
|
|
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
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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 (
|
|
400
|
-
this.
|
|
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 (
|
|
408
|
-
this.
|
|
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.
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
|
|
452
|
-
|
|
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
|
-
|
|
480
|
-
|
|
481
|
-
if (
|
|
482
|
-
if (tipoRetornoFuncao
|
|
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.
|
|
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
|
};
|