Repository: DesignLiquido/delegua
Pull request #284 (2023-02-19 20:18:40)

Procedimentos em VisuAlg.

github-actions[bot]: <!-- jest coverage report action for options with hash 825fd481788efa7282e8569b20438413 -->

## Coverage report 



| <div title="Status of coverage:&#10; 🟢 - ok&#10; 🟡 - slightly more than threshold&#10; 🔴 - under the threshold">St.<sup>:grey_question:</sup></div> | Category   | Percentage                                                     | Covered / Total |
| :----------------------------------------------------------------------------------------------------------------------------------------------------: | :--------- | :------------------------------------------------------------- | :-------------: |
|                                                                           🟡                                                                           | Statements | <div title="Base coverage is: 73.59%">73.87% (+0.28% 🔼)</div> |    4359/5901    |
|                                                                           🟡                                                                           | Branches   | <div title="Base coverage is: 61.84%">62.14% (+0.3% 🔼)</div>  |    1341/2158    |
|                                                                           🟡                                                                           | Functions  | <div title="Base coverage is: 73.96%">74.2% (+0.23% 🔼)</div>  |     647/872     |
|                                                                           🟡                                                                           | Lines      | <div title="Base coverage is: 73.96%">74.22% (+0.27% 🔼)</div> |    4210/5672    |


## Test suite run success
296 tests passing in 27 suites.

<p align="right">Report generated by <a href="https://github.com/ArtiomTr/jest-coverage-report-action">🧪jest coverage report action</a> from acdcf0f362163b0bf18a417da4a76fbcdda833b9</p>
<!-- Sticky Pull Request Comment -->

Patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index b0279047..25e01b28 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -419,7 +419,7 @@
             "runtimeArgs": [
                 "--inspect-brk",
                 "${workspaceRoot}/node_modules/jest/bin/jest.js",
-                "portugol-ipt",
+                "visualg/interpretador",
                 "--runInBand"
             ],
             "skipFiles": ["<node_internals>/**", "node_modules/**"],
diff --git a/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-visualg.ts b/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-visualg.ts
index 8b459ed8..0bb96224 100644
--- a/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-visualg.ts
+++ b/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-visualg.ts
@@ -8,6 +8,7 @@ import {
     Escreva,
     EscrevaMesmaLinha,
     Fazer,
+    FuncaoDeclaracao,
     Leia,
     Para,
     Se,
@@ -27,7 +28,7 @@ import {
     Literal,
     Variavel,
 } from '../../construtos';
-import { SimboloInterface } from '../../interfaces';
+import { ParametroInterface, SimboloInterface } from '../../interfaces';
 import { Simbolo } from '../../lexador';
 
 import tiposDeSimbolos from '../../tipos-de-simbolos/visualg';
@@ -80,11 +81,43 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
         return dimensoes;
     }
 
+    private logicaComumParametroVisuAlg(): 
+        { identificadores: SimboloInterface[], tipo: string, simbolo: SimboloInterface }
+    {
+        const identificadores = [];
+        do {
+            identificadores.push(this.consumir(tiposDeSimbolos.IDENTIFICADOR, 'Esperado nome de variável.'));
+        } while (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.VIRGULA));
+
+        this.consumir(tiposDeSimbolos.DOIS_PONTOS, 'Esperado dois-pontos após nome de variável.');
+
+        if (
+            !this.verificarSeSimboloAtualEIgualA(
+                tiposDeSimbolos.CARACTERE,
+                tiposDeSimbolos.INTEIRO,
+                tiposDeSimbolos.LOGICO,
+                tiposDeSimbolos.REAL,
+                tiposDeSimbolos.VETOR
+            )
+        ) {
+            throw this.erro(this.simbolos[this.atual], 'Tipo de variável não conhecido.');
+        }
+
+        const simboloAnterior = this.simbolos[this.atual - 1];
+        const tipoVariavel: string = simboloAnterior.tipo;
+
+        return {
+            identificadores,
+            tipo: tipoVariavel,
+            simbolo: simboloAnterior
+        };
+    }
+
     /**
      * Validação do segmento de declaração de variáveis (opcional).
      * @returns Vetor de Construtos para inicialização de variáveis.
      */
-    private validarSegmentoVar(): Construto[] {
+    private validarSegmentoVar(): Construto[] | Declaracao[] {
         // Podem haver linhas de comentários acima de `var`, que geram
         // quebras de linha. 
         while (this.simbolos[this.atual].tipo === tiposDeSimbolos.QUEBRA_LINHA) {
@@ -99,7 +132,7 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
         this.avancarEDevolverAnterior(); // Var
 
         // Quebra de linha é opcional aqui.
-        this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.QUEBRA_LINHA);
+        // this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.QUEBRA_LINHA);
 
         while (!this.verificarTipoSimboloAtual(tiposDeSimbolos.INICIO)) {
             // Se ainda houver quebras de linha, volta para o começo do `while`.
@@ -107,89 +140,77 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
                 continue;
             }
 
-            const identificadores = [];
-            do {
-                identificadores.push(this.consumir(tiposDeSimbolos.IDENTIFICADOR, 'Esperado nome de variável.'));
-            } while (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.VIRGULA));
-
-            this.consumir(tiposDeSimbolos.DOIS_PONTOS, 'Esperado dois-pontos após nome de variável.');
-
-            if (
-                !this.verificarSeSimboloAtualEIgualA(
-                    tiposDeSimbolos.CARACTERE,
-                    tiposDeSimbolos.INTEIRO,
-                    tiposDeSimbolos.LOGICO,
-                    tiposDeSimbolos.REAL,
-                    tiposDeSimbolos.VETOR
-                )
-            ) {
-                throw this.erro(this.simbolos[this.atual], 'Tipo de variável não conhecido.');
-            }
-
-            const simboloAtual = this.simbolos[this.atual - 1];
-            const tipoVariavel: string = simboloAtual.tipo;
-
-            // Se chegou até aqui, variáveis são válidas.
-            // Devem ser declaradas com um valor inicial padrão.
-            for (let identificador of identificadores) {
-                switch (tipoVariavel) {
-                    case tiposDeSimbolos.CARACTERE:
-                        inicializacoes.push(
-                            new Var(identificador, new Literal(this.hashArquivo, Number(simboloAtual.linha), ''))
-                        );
-                        break;
-                    case tiposDeSimbolos.INTEIRO:
-                    case tiposDeSimbolos.REAL:
-                        inicializacoes.push(
-                            new Var(identificador, new Literal(this.hashArquivo, Number(simboloAtual.linha), 0))
-                        );
-                        break;
-                    case tiposDeSimbolos.LOGICO:
-                        inicializacoes.push(
-                            new Var(identificador, new Literal(this.hashArquivo, Number(simboloAtual.linha), false))
-                        );
-                        break;
-                    case tiposDeSimbolos.VETOR:
-                        // TODO: Validar vetor
-                        this.consumir(
-                            tiposDeSimbolos.COLCHETE_ESQUERDO,
-                            'Esperado colchete esquerdo após palavra reservada "vetor".'
-                        );
-                        const dimensoes = this.validarDimensoesVetor();
-                        this.consumir(
-                            tiposDeSimbolos.COLCHETE_DIREITO,
-                            'Esperado colchete direito após declaração de dimensões de vetor.'
-                        );
-                        this.consumir(
-                            tiposDeSimbolos.DE,
-                            'Esperado palavra reservada "de" após declaração de dimensões de vetor.'
-                        );
-                        if (
-                            !this.verificarSeSimboloAtualEIgualA(
-                                tiposDeSimbolos.CARACTERE,
-                                tiposDeSimbolos.INTEIRO,
-                                tiposDeSimbolos.LOGICO,
-                                tiposDeSimbolos.REAL,
-                                tiposDeSimbolos.VETOR
-                            )
-                        ) {
-                            throw this.erro(
-                                this.simbolos[this.atual],
-                                'Tipo de variável não conhecido para inicialização de vetor.'
-                            );
+            const simboloAtual = this.simbolos[this.atual];
+            switch (simboloAtual.tipo) {
+                case tiposDeSimbolos.PROCEDIMENTO:
+                    const dadosProcedimento = this.declaracaoProcedimento();
+                    inicializacoes.push(dadosProcedimento);
+                    break;
+                default:
+                    const dadosVariaveis = this.logicaComumParametroVisuAlg();
+                    // Se chegou até aqui, variáveis são válidas.
+                    // Devem ser declaradas com um valor inicial padrão.
+                    for (let identificador of dadosVariaveis.identificadores) {
+                        switch (dadosVariaveis.tipo) {
+                            case tiposDeSimbolos.CARACTERE:
+                                inicializacoes.push(
+                                    new Var(identificador, new Literal(this.hashArquivo, Number(dadosVariaveis.simbolo.linha), ''))
+                                );
+                                break;
+                            case tiposDeSimbolos.INTEIRO:
+                            case tiposDeSimbolos.REAL:
+                                inicializacoes.push(
+                                    new Var(identificador, new Literal(this.hashArquivo, Number(dadosVariaveis.simbolo.linha), 0))
+                                );
+                                break;
+                            case tiposDeSimbolos.LOGICO:
+                                inicializacoes.push(
+                                    new Var(identificador, new Literal(this.hashArquivo, Number(dadosVariaveis.simbolo.linha), false))
+                                );
+                                break;
+                            case tiposDeSimbolos.VETOR:
+                                // TODO: Validar vetor
+                                this.consumir(
+                                    tiposDeSimbolos.COLCHETE_ESQUERDO,
+                                    'Esperado colchete esquerdo após palavra reservada "vetor".'
+                                );
+                                const dimensoes = this.validarDimensoesVetor();
+                                this.consumir(
+                                    tiposDeSimbolos.COLCHETE_DIREITO,
+                                    'Esperado colchete direito após declaração de dimensões de vetor.'
+                                );
+                                this.consumir(
+                                    tiposDeSimbolos.DE,
+                                    'Esperado palavra reservada "de" após declaração de dimensões de vetor.'
+                                );
+                                if (
+                                    !this.verificarSeSimboloAtualEIgualA(
+                                        tiposDeSimbolos.CARACTERE,
+                                        tiposDeSimbolos.INTEIRO,
+                                        tiposDeSimbolos.LOGICO,
+                                        tiposDeSimbolos.REAL,
+                                        tiposDeSimbolos.VETOR
+                                    )
+                                ) {
+                                    throw this.erro(
+                                        this.simbolos[this.atual],
+                                        'Tipo de variável não conhecido para inicialização de vetor.'
+                                    );
+                                }
+                                inicializacoes.push(
+                                    new Var(
+                                        identificador,
+                                        new Literal(
+                                            this.hashArquivo,
+                                            Number(dadosVariaveis.simbolo.linha),
+                                            this.criarVetorNDimensional(dimensoes)
+                                        )
+                                    )
+                                );
+                                break;
                         }
-                        inicializacoes.push(
-                            new Var(
-                                identificador,
-                                new Literal(
-                                    this.hashArquivo,
-                                    Number(simboloAtual.linha),
-                                    this.criarVetorNDimensional(dimensoes)
-                                )
-                            )
-                        );
-                        break;
-                }
+                    }
+                    break;
             }
 
             this.consumir(tiposDeSimbolos.QUEBRA_LINHA, 'Esperado quebra de linha após declaração de variável.');
@@ -326,11 +347,17 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
     blocoEscopo(): any[] {
         const declaracoes = [];
 
-        while (!this.verificarTipoSimboloAtual(tiposDeSimbolos.FIM_FUNCAO) && !this.estaNoFinal()) {
+        while (![
+                tiposDeSimbolos.FIM_FUNCAO, 
+                tiposDeSimbolos.FIM_PROCEDIMENTO
+            ].includes(this.simbolos[this.atual].tipo) && !this.estaNoFinal()) 
+        {
             declaracoes.push(this.declaracao());
         }
 
-        this.consumir(tiposDeSimbolos.FIM_FUNCAO, "Esperado palavra-chave 'fimfuncao' após o bloco.");
+        // Se chegou até aqui, simplesmente consome o símbolo.
+        this.avancarEDevolverAnterior();
+        // this.consumir(tiposDeSimbolos.FIM_FUNCAO, "Esperado palavra-chave 'fimfuncao' após o bloco.");
         return declaracoes;
     }
 
@@ -710,6 +737,56 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
         );
     }
 
+    logicaComumParametros(): ParametroInterface[] {
+        const parametros: ParametroInterface[] = [];
+        if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.PARENTESE_ESQUERDO)) {
+            while (!this.verificarTipoSimboloAtual(tiposDeSimbolos.PARENTESE_DIREITO)) {
+                const dadosParametros = this.logicaComumParametroVisuAlg();
+                for (let parametro of dadosParametros.identificadores) {
+                    parametros.push({
+                        tipo: 'padrao',
+                        nome: parametro
+                    });
+                }
+            }
+
+            // Consumir parêntese direito
+            this.consumir(
+                tiposDeSimbolos.PARENTESE_DIREITO, 
+                "Esperado parêntese direito para finalização da leitura de parâmetros."
+            )
+        }
+
+        return parametros;
+    }
+
+    /**
+     * Procedimentos nada mais são do que funções que não retornam valor.
+     */
+    declaracaoProcedimento() {
+        const simboloProcedimento: SimboloInterface = this.avancarEDevolverAnterior();
+
+        const nomeProcedimento = this.consumir(tiposDeSimbolos.IDENTIFICADOR, 
+            "Esperado nome do procedimento após palavra-chave `procedimento`.");
+
+        // Parâmetros
+        const parametros = this.logicaComumParametros();
+
+        this.validarSegmentoVar();
+        this.validarSegmentoInicio('procedimento');
+
+        const corpo = this.blocoEscopo();
+
+        return new FuncaoDeclaracao(
+            nomeProcedimento, new FuncaoConstruto(
+                this.hashArquivo, 
+                Number(simboloProcedimento.linha), 
+                parametros, 
+                corpo.filter(d => d)
+            )
+        );
+    }
+
     declaracaoSe(): Se {
         const simboloSe: SimboloInterface = this.avancarEDevolverAnterior();
 
@@ -780,6 +857,8 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
                 return this.declaracaoPara();
             case tiposDeSimbolos.PARENTESE_DIREITO:
                 throw new Error('Não deveria estar caindo aqui.');
+            case tiposDeSimbolos.PROCEDIMENTO:
+                return this.declaracaoProcedimento();
             case tiposDeSimbolos.QUEBRA_LINHA:
                 this.avancarEDevolverAnterior();
                 return null;
diff --git a/fontes/lexador/dialetos/palavras-reservadas/visualg.ts b/fontes/lexador/dialetos/palavras-reservadas/visualg.ts
index 247368a2..c94151ea 100644
--- a/fontes/lexador/dialetos/palavras-reservadas/visualg.ts
+++ b/fontes/lexador/dialetos/palavras-reservadas/visualg.ts
@@ -26,6 +26,7 @@ export default {
     fimescolha: tiposDeSimbolos.FIM_ESCOLHA,
     fimfuncao: tiposDeSimbolos.FIM_FUNCAO,
     fimpara: tiposDeSimbolos.FIM_PARA,
+    fimprocedimento: tiposDeSimbolos.FIM_PROCEDIMENTO,
     fimrepita: tiposDeSimbolos.FIM_REPITA,
     fimse: tiposDeSimbolos.FIM_SE,
     funcao: tiposDeSimbolos.FUNCAO,
@@ -42,6 +43,7 @@ export default {
     outrocaso: tiposDeSimbolos.OUTRO_CASO,
     para: tiposDeSimbolos.PARA,
     pi: tiposDeSimbolos.METODO_BIBLIOTECA_GLOBAL,
+    procedimento: tiposDeSimbolos.PROCEDIMENTO,
     quad: tiposDeSimbolos.METODO_BIBLIOTECA_GLOBAL,
     radpgrau: tiposDeSimbolos.METODO_BIBLIOTECA_GLOBAL,
     raizq: tiposDeSimbolos.METODO_BIBLIOTECA_GLOBAL,
diff --git a/fontes/tipos-de-simbolos/visualg.ts b/fontes/tipos-de-simbolos/visualg.ts
index bd41ba58..13144902 100644
--- a/fontes/tipos-de-simbolos/visualg.ts
+++ b/fontes/tipos-de-simbolos/visualg.ts
@@ -25,6 +25,7 @@ export default {
     FIM_ESCOLHA: 'FIM_ESCOLHA',
     FIM_FUNCAO: 'FIM_FUNCAO',
     FIM_PARA: 'FIM_PARA',
+    FIM_PROCEDIMENTO: 'FIM_PROCEDIMENTO',
     FIM_REPITA: 'FIM_REPITA',
     FIM_SE: 'FIM_SE',
     FUNCAO: 'FUNCAO',
@@ -51,6 +52,7 @@ export default {
     PARENTESE_DIREITO: 'PARENTESE_DIREITO',
     PARENTESE_ESQUERDO: 'PARENTESE_ESQUERDO',
     PONTO: 'PONTO',
+    PROCEDIMENTO: 'PROCEDIMENTO',
     REAL: 'REAL',
     REPITA: 'REPITA',
     SE: 'SE',


Test patch:
diff --git a/testes/visualg/avaliador-sintatico.test.ts b/testes/visualg/avaliador-sintatico.test.ts
index d9ce896a..e2ce763b 100644
--- a/testes/visualg/avaliador-sintatico.test.ts
+++ b/testes/visualg/avaliador-sintatico.test.ts
@@ -144,6 +144,39 @@ describe('Avaliador sintático (VisuAlg)', () => {
             expect(retornoAvaliadorSintatico.declaracoes).toHaveLength(2);
         });
 
+        it('Sucesso - Procedimento', () => {
+            const retornoLexador = lexador.mapear([
+                'algoritmo "semnome"',
+                '// Função :',
+                '// Autor :',
+                '// Data : 27/02/2014',
+                '// Seção de Declarações ',
+                'var',
+                'a,b:inteiro',
+                'procedimento mostranumero (a:inteiro;b:inteiro)',
+                '',
+                'inicio',
+                '',
+                'se a > b entao',
+                '     escreval ("A variavel escolhida é ",a)',
+                'senao',
+                '     escreval ("A variavel escolhida é ",b)',
+                'fimse',
+                'fimprocedimento',
+                '',
+                'inicio',
+                'escreval ("Digite dois valores: ")',
+                'leia (a,b)',
+                'mostranumero (a,b)',
+                '',
+                'fimalgoritmo'
+            ], -1);
+            const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador);
+
+            expect(retornoAvaliadorSintatico).toBeTruthy();
+            expect(retornoAvaliadorSintatico.declaracoes).toHaveLength(6);
+        });
+
         it('Sucesso - Repita', () => {
             const retornoLexador = lexador.mapear([
                 'algoritmo "Números de 1 a 10 (com repita)"',
diff --git a/testes/visualg/interpretador.test.ts b/testes/visualg/interpretador.test.ts
index 48989dbf..c9e8d8c7 100644
--- a/testes/visualg/interpretador.test.ts
+++ b/testes/visualg/interpretador.test.ts
@@ -131,6 +131,50 @@ describe('Interpretador', () => {
 
                 expect(retornoInterpretador.erros).toHaveLength(0);
             });
+
+            it('Sucesso - Procedimento', async () => {
+                // Aqui vamos simular a resposta para duas variáveis de `leia()`.
+                const respostas = [
+                    2, 3
+                ];
+                interpretador.interfaceEntradaSaida = {
+                    question: (mensagem: string, callback: Function) => {
+                        callback(respostas.pop());
+                    }
+                };
+
+                const retornoLexador = lexador.mapear([
+                    'algoritmo "semnome"',
+                    '// Função :',
+                    '// Autor :',
+                    '// Data : 27/02/2014',
+                    '// Seção de Declarações ',
+                    'var',
+                    'a,b:inteiro',
+                    'procedimento mostranumero (a:inteiro;b:inteiro)',
+                    '',
+                    'inicio',
+                    '',
+                    'se a > b entao',
+                    '     escreval ("A variavel escolhida é ",a)',
+                    'senao',
+                    '     escreval ("A variavel escolhida é ",b)',
+                    'fimse',
+                    'fimprocedimento',
+                    '',
+                    'inicio',
+                    'escreval ("Digite dois valores: ")',
+                    'leia (a,b)',
+                    'mostranumero (a,b)',
+                    '',
+                    'fimalgoritmo'
+                ], -1);
+                const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador);
+    
+                const retornoInterpretador = await interpretador.interpretar(retornoAvaliadorSintatico.declaracoes);
+
+                expect(retornoInterpretador.erros).toHaveLength(0);
+            });
         });
     });
 });