Repository: DesignLiquido/delegua
Pull request #520 (2023-09-29 01:21:13)

Analisador semântico melhorias - Parte 1


Patch:
diff --git a/fontes/analisador-semantico/analisador-semantico.ts b/fontes/analisador-semantico/analisador-semantico.ts
index d4f29564..d788f750 100644
--- a/fontes/analisador-semantico/analisador-semantico.ts
+++ b/fontes/analisador-semantico/analisador-semantico.ts
@@ -1,4 +1,4 @@
-import { Atribuir, FimPara, FormatacaoEscrita, Literal, Super, TipoDe, Vetor } from '../construtos';
+import { Atribuir, Chamada, FimPara, FormatacaoEscrita, FuncaoConstruto, Literal, Super, TipoDe, Variavel, Vetor } from '../construtos';
 import {
     Bloco,
     Classe,
@@ -40,15 +40,21 @@ interface VariavelHipoteticaInterface {
     valor?: any
 }
 
+interface FuncaoHipoteticaInterface {
+    valor: any
+}
+
 export class AnalisadorSemantico implements AnalisadorSemanticoInterface {
     pilhaVariaveis: PilhaVariaveis;
     variaveis: { [nomeVariavel: string]: VariavelHipoteticaInterface };
+    funcoes: { [nomeFuncao: string]: FuncaoHipoteticaInterface }
     atual: number;
     erros: ErroAnalisadorSemantico[];
 
     constructor() {
         this.pilhaVariaveis = new PilhaVariaveis();
         this.variaveis = {};
+        this.funcoes = {};
         this.atual = 0;
         this.erros = [];
     }
@@ -98,10 +104,15 @@ export class AnalisadorSemantico implements AnalisadorSemanticoInterface {
                 }
                 if (['inteiro', 'real'].includes(declaracao.tipo)) {
                     if (typeof literal.valor !== 'number') {
-                        this.erro(declaracao.simbolo, `Atribuição inválida '${declaracao.simbolo.lexema}', é esperado um 'número'.`);
+                        this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}', é esperado um 'número'.`);
                     }
                 }
             }
+            if (declaracao.inicializador instanceof Leia) {
+                if (declaracao.tipo !== 'texto') {
+                    this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}', Leia só pode receber tipo 'texto'.`);
+                }
+            }
         }
     }
 
@@ -129,7 +140,26 @@ export class AnalisadorSemantico implements AnalisadorSemanticoInterface {
         return Promise.resolve();
     }
 
-    visitarExpressaoDeChamada(expressao: any) {
+    visitarExpressaoDeChamada(expressao: Chamada) {
+        if (expressao.entidadeChamada instanceof Variavel) {
+            const variavel = expressao.entidadeChamada as Variavel;
+            const funcaoChamada = this.variaveis[variavel.simbolo.lexema] || this.funcoes[variavel.simbolo.lexema];
+            if (!funcaoChamada) {
+                this.erro(
+                    expressao.entidadeChamada.simbolo,
+                    `Chamada da função '${expressao.entidadeChamada.simbolo.lexema}' não existe.`
+                );
+                return Promise.resolve();
+            }
+            const funcao = funcaoChamada.valor as FuncaoConstruto;
+            if (funcao.parametros.length !== expressao.argumentos.length) {
+                this.erro(
+                    expressao.entidadeChamada.simbolo,
+                    `Função '${expressao.entidadeChamada.simbolo.lexema}' espera ${funcao.parametros.length} parametros.`
+                );
+            }
+        }
+
         return Promise.resolve();
     }
 
@@ -270,7 +300,15 @@ export class AnalisadorSemantico implements AnalisadorSemanticoInterface {
     }
 
     visitarDeclaracaoEscreva(declaracao: Escreva) {
-        // return Promise.resolve();
+        const variaveis = declaracao.argumentos.filter(arg => arg instanceof Variavel)
+
+        for (let variavel of variaveis as Variavel[]) {
+            if(!this.variaveis[variavel.simbolo.lexema]) {
+                this.erro(variavel.simbolo, `Variável '${variavel.simbolo.lexema}' não existe.`)
+            }
+        }
+
+        return Promise.resolve();
     }
 
     visitarExpressaoEscrevaMesmaLinha(declaracao: EscrevaMesmaLinha) {
@@ -369,7 +407,6 @@ export class AnalisadorSemantico implements AnalisadorSemanticoInterface {
             if (funcaoContemRetorno) {
                 if (tipoRetornoFuncao === 'vazio') {
                     this.erro(declaracao.simbolo, `A função não pode ter nenhum tipo de retorno.`);
-                    return Promise.resolve();
                 }
 
                 const tipoValor = typeof funcaoContemRetorno.valor.valor;
@@ -398,6 +435,10 @@ export class AnalisadorSemantico implements AnalisadorSemanticoInterface {
             }
         }
 
+        this.funcoes[declaracao.simbolo.lexema] = {
+            valor: declaracao.funcao
+        }
+
         return Promise.resolve();
     }
 


Test patch:
diff --git a/testes/analisador-semantico.test.ts b/testes/analisador-semantico.test.ts
index 0b786a74..8ed124fe 100644
--- a/testes/analisador-semantico.test.ts
+++ b/testes/analisador-semantico.test.ts
@@ -19,7 +19,7 @@ describe('Analisador semântico', () => {
                 const retornoLexador = lexador.mapear(["escreva('Olá mundo')"], -1);
                 const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
                 const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
-    
+
                 expect(retornoAnalisadorSemantico).toBeTruthy();
                 expect(retornoAnalisadorSemantico.erros).toHaveLength(0);
             });
@@ -37,7 +37,7 @@ describe('Analisador semântico', () => {
                     "const f: qualquer = [1, '2', 3, 'Olá Mundo']",
                 ], -1);
                 const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
-    
+
                 expect(retornoAvaliadorSintatico).toBeTruthy();
                 expect(retornoAvaliadorSintatico.declaracoes).toHaveLength(9);
             });
@@ -55,7 +55,7 @@ describe('Analisador semântico', () => {
                 ], -1);
                 const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
                 const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
-    
+
                 expect(retornoAnalisadorSemantico).toBeTruthy();
                 expect(retornoAnalisadorSemantico.erros).toHaveLength(0);
             });
@@ -74,12 +74,12 @@ describe('Analisador semântico', () => {
                 ], -1);
                 const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
                 const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
-    
+
                 expect(retornoAnalisadorSemantico).toBeTruthy();
                 expect(retornoAnalisadorSemantico.erros).toHaveLength(0);
             });
         });
-        
+
         describe('Cenários de falha', () => {
             it('Atribuindo variável que não existe', () => {
                 const retornoLexador = lexador.mapear([
@@ -87,7 +87,7 @@ describe('Analisador semântico', () => {
                 ], -1);
                 const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
                 const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
-    
+
                 expect(retornoAnalisadorSemantico).toBeTruthy();
                 expect(retornoAnalisadorSemantico.erros).toHaveLength(1);
             });
@@ -99,7 +99,7 @@ describe('Analisador semântico', () => {
                 ], -1);
                 const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
                 const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
-    
+
                 expect(retornoAnalisadorSemantico).toBeTruthy();
                 expect(retornoAnalisadorSemantico.erros).toHaveLength(1);
             });
@@ -125,7 +125,7 @@ describe('Analisador semântico', () => {
                 ], -1);
                 const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
                 const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
-    
+
                 expect(retornoAnalisadorSemantico).toBeTruthy();
                 expect(retornoAnalisadorSemantico.erros).toHaveLength(8);
             });
@@ -140,10 +140,10 @@ describe('Analisador semântico', () => {
                 ], -1);
                 const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
                 const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
-    
+
                 expect(retornoAnalisadorSemantico).toBeTruthy();
                 expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('Atribuição inválida para \'t\', é esperado um \'texto\'.');
-                expect(retornoAnalisadorSemantico.erros[1].mensagem).toBe('Atribuição inválida \'n\', é esperado um \'número\'.');
+                expect(retornoAnalisadorSemantico.erros[1].mensagem).toBe('Atribuição inválida para \'n\', é esperado um \'número\'.');
                 expect(retornoAnalisadorSemantico.erros[2].mensagem).toBe('Atribuição inválida para \'v1\', é esperado um vetor de \'texto\'.');
                 expect(retornoAnalisadorSemantico.erros[3].mensagem).toBe('Atribuição inválida para \'v2\', é esperado um vetor de \'inteiros\' ou \'real\'.');
                 expect(retornoAnalisadorSemantico.erros[4].mensagem).toBe('Atribuição inválida para \'v3\', é esperado um vetor de elementos.');
@@ -157,7 +157,7 @@ describe('Analisador semântico', () => {
                 ], -1);
                 const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
                 const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
-    
+
                 expect(retornoAnalisadorSemantico).toBeTruthy();
                 expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('A função não pode ter nenhum tipo de retorno.');
             });
@@ -256,11 +256,96 @@ describe('Analisador semântico', () => {
                 ], -1);
                 const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
                 const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
-    
+
                 expect(retornoAnalisadorSemantico).toBeTruthy();
                 expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('\'caso 0:\' não é do mesmo tipo esperado em \'escolha\'');
                 expect(retornoAnalisadorSemantico.erros[1].mensagem).toBe('\'caso 1:\' não é do mesmo tipo esperado em \'escolha\'');
             });
+
+            it.skip('Escolha com tipos diferentes em \'caso\'', () => {
+                const retornoLexador = lexador.mapear([
+                    'var opcao = leia(\'Digite a opção desejada: \')',
+                    'escolha opcao {',
+                        // 'cas:x1 ako',
+                        'caso 1: // Avisar aqui que tipo de `opcao` não é o mesmo tipo do literal 1',
+                            'facaAlgumaCoisa()',
+                        'cas:x1 ako',
+                        'caso \'1\': // Aqui o tipo está correto, então não precisa avisar.',
+                            'facaAlgumaCoisa()',
+                    '}',
+                ], -1);
+                const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+                const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
+
+                expect(retornoAnalisadorSemantico).toBeTruthy();
+                // expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('\'caso 0:\' não é do mesmo tipo esperado em \'escolha\'');
+                // expect(retornoAnalisadorSemantico.erros[1].mensagem).toBe('\'caso 1:\' não é do mesmo tipo esperado em \'escolha\'');
+            });
+
+            it('Leia só pode receber texto', () => {
+                const retornoLexador = lexador.mapear([
+                    'var opcao: inteiro = leia(\'Digite a opção desejada: \')',
+                ], -1);
+                const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+                const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
+
+                expect(retornoAnalisadorSemantico).toBeTruthy();
+                expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('Atribuição inválida para \'opcao\', Leia só pode receber tipo \'texto\'.');
+            });
+
+            it('Escreva com variável que não existe', () => {
+                const retornoLexador = lexador.mapear([
+                    'var t = \'teste\'',
+                    'escreva(XXX,\' outro teste\')',
+                ], -1);
+                const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+                const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
+
+                expect(retornoAnalisadorSemantico).toBeTruthy();
+                expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('Variável \'XXX\' não existe.');
+            });
+
+            it('Atribuição de função', () => {
+                const retornoLexador = lexador.mapear([
+                    'var f = função(a, b) {',
+                    '   escreva(a + b)',
+                    '}',
+                    'f(1)',
+                ], -1);
+                const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+                const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
+
+                expect(retornoAnalisadorSemantico).toBeTruthy();
+                expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('Função \'f\' espera 2 parametros.');
+            });
+
+            it('Chamada de função direta', () => {
+                const retornoLexador = lexador.mapear([
+                    'função f(a, b) {',
+                    '   escreva(a + b)',
+                    '}',
+                    'f(1)',
+                ], -1);
+                const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+                const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
+
+                expect(retornoAnalisadorSemantico).toBeTruthy();
+                expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('Função \'f\' espera 2 parametros.');
+            });
+
+            it('Chamada de função que não existe', () => {
+                const retornoLexador = lexador.mapear([
+                    'função f(a, b) {',
+                    '   escreva(a + b)',
+                    '}',
+                    'x()',
+                ], -1);
+                const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+                const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes);
+
+                expect(retornoAnalisadorSemantico).toBeTruthy();
+                expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('Chamada da função \'x\' não existe.');
+            });
         });
     });
 });