Repository: DesignLiquido/delegua
Pull request #498 (2023-09-18 20:32:55)

Análise sintática para `leia_inteiros`, `leia_reais` e `leia_textos`.


Patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index a1d80845..5f8d2660 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -724,7 +724,7 @@
             "runtimeArgs": [
                 "--inspect-brk",
                 "${workspaceRoot}/node_modules/jest/bin/jest.js",
-                "potigol/interpretador.test",
+                "potigol/avaliador-sintatico.test",
                 "--runInBand"
             ],
             "skipFiles": [
diff --git a/fontes/avaliador-sintatico/dialetos/potigol/avaliador-sintatico-potigol.ts b/fontes/avaliador-sintatico/dialetos/potigol/avaliador-sintatico-potigol.ts
index 785dcc7a..3ed5914b 100644
--- a/fontes/avaliador-sintatico/dialetos/potigol/avaliador-sintatico-potigol.ts
+++ b/fontes/avaliador-sintatico/dialetos/potigol/avaliador-sintatico-potigol.ts
@@ -338,13 +338,23 @@ export class AvaliadorSintaticoPotigol extends AvaliadorSintaticoBase {
                     simboloVerdadeiroFalso.tipo === tiposDeSimbolos.VERDADEIRO
                 );
             case tiposDeSimbolos.LEIA_INTEIRO:
-            case tiposDeSimbolos.LEIA_INTEIROS:
-            case tiposDeSimbolos.LEIA_REAIS:
             case tiposDeSimbolos.LEIA_REAL:
             case tiposDeSimbolos.LEIA_TEXTO:
-            case tiposDeSimbolos.LEIA_TEXTOS:
                 const simboloLeia: SimboloInterface = this.avancarEDevolverAnterior();
                 return new Leia(simboloLeia, []);
+            case tiposDeSimbolos.LEIA_INTEIROS:
+            case tiposDeSimbolos.LEIA_REAIS:
+            case tiposDeSimbolos.LEIA_TEXTOS:
+                const simboloLeiaDefinido: SimboloInterface = this.avancarEDevolverAnterior();
+                this.consumir(tiposDeSimbolos.PARENTESE_ESQUERDO, `Esperado parêntese esquerdo após ${simboloLeiaDefinido.lexema}.`);
+                if (!this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.INTEIRO, tiposDeSimbolos.REAL)) {
+                    throw this.erro(this.simbolos[this.atual], `Esperado número de argumentos como inteiro ou real em ${simboloLeiaDefinido.lexema}`);
+                }
+                let numeroArgumentosLeia = this.simbolos[this.atual - 1];
+                this.consumir(tiposDeSimbolos.PARENTESE_DIREITO, `Esperado parêntese direito após número de parâmetros em chamada de ${simboloLeiaDefinido.lexema}.`);
+                const leiaDefinido = new Leia(simboloLeiaDefinido, []);
+                leiaDefinido.numeroArgumentosEsperados = parseInt(numeroArgumentosLeia.literal);
+                return leiaDefinido;
             default:
                 const simboloIdentificador: SimboloInterface = this.avancarEDevolverAnterior();
                 return new ConstanteOuVariavel(this.hashArquivo, simboloIdentificador);
@@ -744,8 +754,13 @@ export class AvaliadorSintaticoPotigol extends AvaliadorSintaticoBase {
         do {
             let inicializador = this.expressao();
             if (inicializador instanceof Leia && identificadores.length > 1) {
-                inicializador = new LeiaMultiplo(inicializador.simbolo, inicializador.argumentos);
+                inicializador = new LeiaMultiplo(
+                    inicializador.simbolo, 
+                    inicializador.argumentos, 
+                    inicializador.numeroArgumentosEsperados
+                );
             }
+
             inicializadores.push(inicializador);
         } while (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.VIRGULA));
 
@@ -758,12 +773,29 @@ export class AvaliadorSintaticoPotigol extends AvaliadorSintaticoBase {
                     'Quantidade de identificadores à esquerda do igual é diferente da quantidade de valores à direita.'
                 );
             }
+
+            // `leia_inteiros`, `leia_reais` e `leia_textos` pedem um inteiro como argumento, 
+            // que pode ser usado para verificar se a expressão faz sentido ou não aqui.
+            const inicializadorLeia = <LeiaMultiplo>inicializadores[0];
+            if (inicializadorLeia.numeroArgumentosEsperados > 0) {
+                if (identificadores.length !== inicializadorLeia.numeroArgumentosEsperados) {
+                    throw this.erro(
+                        this.simbolos[this.atual],
+                        `Quantidade de identificadores à esquerda do igual é diferente da quantidade de valores passada por parâmetro à direita em ${inicializadorLeia.simbolo.lexema}.`
+                    );
+                }
+            }
             
             let tipoConversao: TiposDadosInterface;
-            switch (inicializadores[0].simbolo.tipo) {
+            switch (inicializadorLeia.simbolo.tipo) {
                 case tiposDeSimbolos.LEIA_INTEIRO:
+                case tiposDeSimbolos.LEIA_INTEIROS:
                     tipoConversao = 'inteiro';
                     break;
+                case tiposDeSimbolos.LEIA_REAL:
+                case tiposDeSimbolos.LEIA_REAIS:
+                    tipoConversao = 'real';
+                    break;
                 default:
                     tipoConversao = 'texto';
                     break;
diff --git a/fontes/declaracoes/leia-multiplo.ts b/fontes/declaracoes/leia-multiplo.ts
index 0bde1d46..80ba2182 100644
--- a/fontes/declaracoes/leia-multiplo.ts
+++ b/fontes/declaracoes/leia-multiplo.ts
@@ -12,12 +12,14 @@ export class LeiaMultiplo extends Declaracao {
     id: string;
     argumentos: Construto[];
     tipo?: string;
+    numeroArgumentosEsperados?: number;
 
-    constructor(simbolo: SimboloInterface, argumentos: Construto[]) {
+    constructor(simbolo: SimboloInterface, argumentos: Construto[], numeroArgumentosEsperados?: number) {
         super(simbolo.linha, simbolo.hashArquivo);
         this.simbolo = simbolo;
         this.id = uuidv4();
         this.argumentos = argumentos;
+        this.numeroArgumentosEsperados = numeroArgumentosEsperados;
     }
 
     async aceitar(visitante: VisitanteComumInterface): Promise<any> {
diff --git a/fontes/declaracoes/leia.ts b/fontes/declaracoes/leia.ts
index 495bf10f..49373812 100644
--- a/fontes/declaracoes/leia.ts
+++ b/fontes/declaracoes/leia.ts
@@ -12,6 +12,7 @@ export class Leia extends Declaracao {
     id: string;
     argumentos: Construto[];
     tipo?: string;
+    numeroArgumentosEsperados?: number;
 
     constructor(simbolo: SimboloInterface, argumentos: Construto[]) {
         super(simbolo.linha, simbolo.hashArquivo);


Test patch:
diff --git a/testes/potigol/avaliador-sintatico.test.ts b/testes/potigol/avaliador-sintatico.test.ts
index 15233ad9..1c73505d 100644
--- a/testes/potigol/avaliador-sintatico.test.ts
+++ b/testes/potigol/avaliador-sintatico.test.ts
@@ -206,6 +206,24 @@ describe('Avaliador sintático', () => {
                     expect(retornoAvaliadorSintatico).toBeTruthy();
                     expect(retornoAvaliadorSintatico.declaracoes).toHaveLength(1);
                 });
+
+                it('Sucesso - Declaração de múltiplas constantes, lado direito usando `leia_inteiro`', () => {
+                    const retornoLexador = lexador.mapear(['a, b, c = leia_inteiro'], -1);
+                    const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+        
+                    expect(retornoAvaliadorSintatico).toBeTruthy();
+                    expect(retornoAvaliadorSintatico.erros).toHaveLength(0);
+                    expect(retornoAvaliadorSintatico.declaracoes).toHaveLength(1);
+                });
+
+                it('Sucesso - Declaração de múltiplas constantes, lado direito usando `leia_inteiros`', () => {
+                    const retornoLexador = lexador.mapear(['a, b, c = leia_inteiros(3)'], -1);
+                    const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+        
+                    expect(retornoAvaliadorSintatico).toBeTruthy();
+                    expect(retornoAvaliadorSintatico.erros).toHaveLength(0);
+                    expect(retornoAvaliadorSintatico.declaracoes).toHaveLength(1);
+                });
             });
             
             describe('Estruturas de decisão', () => {
@@ -410,6 +428,16 @@ describe('Avaliador sintático', () => {
                     })
                 );
             });
+
+            it('Sucesso - Declaração de múltiplas constantes, lado direito usando `leia_inteiros` com número errado de parâmetros', () => {
+                const retornoLexador = lexador.mapear(['a, b, c = leia_inteiros(1)'], -1);
+                expect(() => avaliadorSintatico.analisar(retornoLexador, -1)).toThrow(ErroAvaliadorSintatico);
+                expect(() => avaliadorSintatico.analisar(retornoLexador, -1)).toThrow(
+                    expect.objectContaining({
+                        message: "Quantidade de identificadores à esquerda do igual é diferente da quantidade de valores passada por parâmetro à direita em leia_inteiros.",
+                    })
+                );
+            });
         });
     });
 });