Repository: DesignLiquido/delegua
Pull request #270 (2023-02-09 00:44:04)

Demonstração de dialeto Guarani.

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: 67.25%">67.39% (+0.14% 🔼)</div> |    4135/6136    |
|                                                                           🔴                                                                           | Branches   | <div title="Base coverage is: 55.78%">55.91% (+0.13% 🔼)</div> |    1235/2209    |
|                                                                           🟡                                                                           | Functions  | <div title="Base coverage is: 67.9%">67.68% (-0.21% 🔻)</div>  |     599/885     |
|                                                                           🟡                                                                           | Lines      | <div title="Base coverage is: 67.58%">67.72% (+0.14% 🔼)</div> |    3996/5901    |

<details><summary>Show new covered files 🐣</summary>
<br/>

| <div title="Status of coverage:&#10; 🟢 - ok&#10; 🟡 - slightly more than threshold&#10; 🔴 - under the threshold">St.<sup>:grey_question:</sup></div> | File                                                                                                                  | Statements | Branches | Functions | Lines  |
| :----------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------- | :--------- | :------- | :-------- | :----- |
|                                                                           🟡                                                                           | <div title="lexador/dialetos/lexador-guarani.ts">`...` / lexador-guarani.ts</div>                                     | 79.03%     | 62.5%    | 80%       | 79.66% |
|                                                                           🟢                                                                           | <div title="tipos-de-simbolos/guarani.ts">`...` / guarani.ts</div>                                                    | 100%       | 100%     | 100%      | 100%   |
|                                                                           🟢                                                                           | <div title="lexador/dialetos/palavras-reservadas/guarani.ts">`...` / guarani.ts</div>                                 | 100%       | 100%     | 100%      | 100%   |
|                                                                           🟡                                                                           | <div title="avaliador-sintatico/dialetos/avaliador-sintatico-guarani.ts">`...` / avaliador-sintatico-guarani.ts</div> | 67.35%     | 66.67%   | 50%       | 67.35% |

</details>


## Test suite run success
279 tests passing in 28 suites.

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

Patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index bf9771cc..9190cc7a 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -435,7 +435,7 @@
             "runtimeArgs": [
                 "--inspect-brk",
                 "${workspaceRoot}/node_modules/jest/bin/jest.js",
-                "portugol-studio",
+                "guarani",
                 "--runInBand"
             ],
             "skipFiles": ["<node_internals>/**", "node_modules/**"],
diff --git a/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-guarani.ts b/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-guarani.ts
new file mode 100644
index 00000000..82981c3c
--- /dev/null
+++ b/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-guarani.ts
@@ -0,0 +1,179 @@
+import { AcessoIndiceVariavel, AcessoMetodo, AtribuicaoSobrescrita, Atribuir, Binario, Construto, DefinirValor, FuncaoConstruto, Literal, Variavel } from "../../construtos";
+import { Escreva, Declaracao, Se, Enquanto, Para, Escolha, Fazer } from "../../declaracoes";
+import { RetornoLexador, RetornoAvaliadorSintatico } from "../../interfaces/retornos";
+import { AvaliadorSintaticoBase } from "../avaliador-sintatico-base";
+
+import tiposDeSimbolos from '../../tipos-de-simbolos/guarani';
+import { SimboloInterface } from "../../interfaces";
+
+export class AvaliadorSintaticoGuarani extends AvaliadorSintaticoBase {
+    primario(): Construto {
+        const simboloAtual = this.simbolos[this.atual];
+        if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.NUMERO, tiposDeSimbolos.TEXTO)) {
+            const simboloAnterior: SimboloInterface = this.simbolos[this.atual - 1];
+            return new Literal(this.hashArquivo, Number(simboloAnterior.linha), simboloAnterior.literal);
+        }
+
+        throw this.erro(this.simbolos[this.atual], 'Esperado expressão.');
+    }
+
+    chamar(): Construto {
+        let expressao = this.primario();
+
+        /* while (true) {
+            if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.PARENTESE_ESQUERDO)) {
+                expressao = this.finalizarChamada(expressao);
+            } else if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.PONTO)) {
+                const nome = this.consumir(tiposDeSimbolos.IDENTIFICADOR, "Esperado nome do método após '.'.");
+                expressao = new AcessoMetodo(this.hashArquivo, expressao, nome);
+            } else if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.COLCHETE_ESQUERDO)) {
+                const indice = this.expressao();
+                const simboloFechamento = this.consumir(
+                    tiposDeSimbolos.COLCHETE_DIREITO,
+                    "Esperado ']' após escrita do indice."
+                );
+                expressao = new AcessoIndiceVariavel(this.hashArquivo, expressao, indice, simboloFechamento);
+            } else {
+                break;
+            }
+        } */
+
+        return expressao;
+    }
+
+    atribuir(): Construto {
+        const expressao = this.ou();
+
+        /* if (
+            expressao instanceof Binario &&
+            [
+                tiposDeSimbolos.MAIS_IGUAL,
+                tiposDeSimbolos.MENOS_IGUAL,
+                tiposDeSimbolos.MULTIPLICACAO_IGUAL,
+                tiposDeSimbolos.DIVISAO_IGUAL,
+                tiposDeSimbolos.DIVISAO_INTEIRA_IGUAL,
+                tiposDeSimbolos.MODULO_IGUAL,
+            ].includes(expressao.operador.tipo)
+        ) {
+            return new Atribuir(this.hashArquivo, (expressao.esquerda as Variavel).simbolo, expressao);
+        } else if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.IGUAL)) {
+            const igual = this.simbolos[this.atual - 1];
+            const valor = this.atribuir();
+
+            if (expressao instanceof Variavel) {
+                const simbolo = expressao.simbolo;
+                return new Atribuir(this.hashArquivo, simbolo, valor);
+            } else if (expressao instanceof AcessoMetodo) {
+                const get = expressao;
+                // return new Conjunto(this.hashArquivo, 0, get.objeto, get.simbolo, valor);
+                return new DefinirValor(this.hashArquivo, 0, get.objeto, get.simbolo, valor);
+            } else if (expressao instanceof AcessoIndiceVariavel) {
+                return new AtribuicaoSobrescrita(
+                    this.hashArquivo,
+                    0,
+                    expressao.entidadeChamada,
+                    expressao.indice,
+                    valor
+                );
+            }
+            this.erro(igual, 'Tarefa de atribuição inválida');
+        } */
+
+        return expressao;
+    }
+
+    declaracaoEscreva(): Escreva {
+        const simboloAtual = this.consumir(tiposDeSimbolos.HAI, '');
+
+        this.consumir(tiposDeSimbolos.PARENTESE_ESQUERDO, "Oñeha'arõ '(' valores mboyve jehaipyrépe.");
+
+        const argumentos: Construto[] = [];
+
+        do {
+            argumentos.push(this.expressao());
+        } while (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.VIRGULA));
+
+        this.consumir(tiposDeSimbolos.PARENTESE_DIREITO, "Oñeha'arõ ')' valores rire jehaipyrépe.");
+
+        return new Escreva(Number(simboloAtual.linha), simboloAtual.hashArquivo, argumentos);
+    }
+
+    blocoEscopo(): Declaracao[] {
+        throw new Error("Método não implementado.");
+    }
+
+    declaracaoSe(): Se {
+        throw new Error("Método não implementado.");
+    }
+
+    declaracaoEnquanto(): Enquanto {
+        throw new Error("Método não implementado.");
+    }
+
+    declaracaoPara(): Para {
+        throw new Error("Método não implementado.");
+    }
+
+    declaracaoEscolha(): Escolha {
+        throw new Error("Método não implementado.");
+    }
+
+    declaracaoFazer(): Fazer {
+        throw new Error("Método não implementado.");
+    }
+
+    corpoDaFuncao(tipo: string): FuncaoConstruto {
+        // O parêntese esquerdo é considerado o símbolo inicial para
+        // fins de pragma.
+        const parenteseEsquerdo = this.consumir(
+            tiposDeSimbolos.PARENTESE_ESQUERDO,
+            `Esperado '(' após o nome ${tipo}.`
+        );
+
+        let parametros = [];
+        if (!this.verificarTipoSimboloAtual(tiposDeSimbolos.PARENTESE_DIREITO)) {
+            parametros = this.logicaComumParametros();
+        }
+
+        this.consumir(tiposDeSimbolos.PARENTESE_DIREITO, "Esperado ')' após parâmetros.");
+        this.consumir(tiposDeSimbolos.CHAVE_ESQUERDA, `Esperado '{' antes do escopo do ${tipo}.`);
+
+        const corpo = this.blocoEscopo();
+
+        return new FuncaoConstruto(this.hashArquivo, Number(parenteseEsquerdo.linha), parametros, corpo);
+    }
+
+    expressao(): Construto {
+        // if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.LEIA)) return this.declaracaoLeia();
+        return this.atribuir();
+    }
+
+    declaracao(): Declaracao | Declaracao[] | Construto | Construto[] | any {
+        const simboloAtual = this.simbolos[this.atual];
+        switch (simboloAtual.tipo) {
+            case tiposDeSimbolos.HAI:
+                return this.declaracaoEscreva();
+            default:
+                return this.expressao();
+        }
+    }
+
+    analisar(retornoLexador: RetornoLexador, hashArquivo?: number): RetornoAvaliadorSintatico {
+        this.erros = [];
+        this.atual = 0;
+        this.blocos = 0;
+
+        this.hashArquivo = hashArquivo || 0;
+        this.simbolos = retornoLexador?.simbolos || [];
+
+        const declaracoes: Declaracao[] = [];
+        while (!this.estaNoFinal()) {
+            declaracoes.push(this.declaracao() as Declaracao);
+        }
+
+        return {
+            declaracoes: declaracoes,
+            erros: this.erros,
+        } as RetornoAvaliadorSintatico;
+    }
+}
\ No newline at end of file
diff --git a/fontes/lexador/dialetos/index.ts b/fontes/lexador/dialetos/index.ts
index 7fbf58e6..be16c56c 100644
--- a/fontes/lexador/dialetos/index.ts
+++ b/fontes/lexador/dialetos/index.ts
@@ -1,1 +1,2 @@
 export * from './lexador-egua-classico';
+export * from './lexador-guarani';
diff --git a/fontes/lexador/dialetos/lexador-guarani.ts b/fontes/lexador/dialetos/lexador-guarani.ts
new file mode 100644
index 00000000..6bb671b7
--- /dev/null
+++ b/fontes/lexador/dialetos/lexador-guarani.ts
@@ -0,0 +1,132 @@
+import { RetornoLexador } from "../../interfaces/retornos";
+import { LexadorBase } from "../lexador-base";
+
+import { ErroLexador } from "../erro-lexador";
+
+import tiposDeSimbolos from '../../tipos-de-simbolos/guarani';
+import palavrasReservadas from './palavras-reservadas/guarani';
+
+export class LexadorGuarani extends LexadorBase {
+    analisarTexto(delimitador: string): void {
+        while (this.simboloAtual() !== delimitador && !this.eFinalDoCodigo()) {
+            this.avancar();
+        }
+
+        if (this.eFinalDoCodigo()) {
+            this.erros.push({
+                linha: this.linha + 1,
+                caractere: this.simboloAnterior(),
+                mensagem: "Texto noñemohu'ãiva.",
+            } as ErroLexador);
+            return;
+        }
+
+        const valor = this.codigo[this.linha].substring(this.inicioSimbolo + 1, this.atual);
+        this.adicionarSimbolo(tiposDeSimbolos.TEXTO, valor);
+    }
+
+    analisarNumero(): void {
+        while (this.eDigito(this.simboloAtual())) {
+            this.avancar();
+        }
+
+        if (this.simboloAtual() == '.' && this.eDigito(this.proximoSimbolo())) {
+            this.avancar();
+
+            while (this.eDigito(this.simboloAtual())) {
+                this.avancar();
+            }
+        }
+
+        const numeroCompleto = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual);
+
+        this.adicionarSimbolo(tiposDeSimbolos.NUMERO, parseFloat(numeroCompleto));
+    }
+
+    identificarPalavraChave(): void {
+        while (this.eAlfabetoOuDigito(this.simboloAtual())) {
+            this.avancar();
+        }
+
+        const codigo: string = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual).toLowerCase();
+
+        const tipo: string = codigo in palavrasReservadas ? palavrasReservadas[codigo] : tiposDeSimbolos.IDENTIFICADOR;
+
+        this.adicionarSimbolo(tipo);
+    }
+
+    analisarToken(): void {
+        const caractere = this.simboloAtual();
+
+        switch (caractere) {
+            case '(':
+                this.adicionarSimbolo(tiposDeSimbolos.PARENTESE_ESQUERDO);
+                this.avancar();
+                break;
+            case ')':
+                this.adicionarSimbolo(tiposDeSimbolos.PARENTESE_DIREITO);
+                this.avancar();
+                break;
+
+            case '"':
+                this.avancar();
+                this.analisarTexto('"');
+                this.avancar();
+                break;
+
+            case "'":
+                this.avancar();
+                this.analisarTexto("'");
+                this.avancar();
+                break;
+
+            // Esta sessão ignora espaços em branco na tokenização.
+            // Ponto-e-vírgula é opcional em Delégua, então pode apenas ser ignorado.
+            case ' ':
+            case '\0':
+            case '\r':
+            case '\t':
+            case ';':
+                this.avancar();
+                break;
+
+            default:
+                if (this.eDigito(caractere)) this.analisarNumero();
+                else if (this.eAlfabeto(caractere)) this.identificarPalavraChave();
+                else {
+                    this.erros.push({
+                        linha: this.linha + 1,
+                        caractere: caractere,
+                        mensagem: 'Caractere inesperado.',
+                    } as ErroLexador);
+                    this.avancar();
+                }
+        }
+    }
+
+    mapear(codigo: string[], hashArquivo: number): RetornoLexador {
+        this.erros = [];
+        this.simbolos = [];
+        this.inicioSimbolo = 0;
+        this.atual = 0;
+        this.linha = 0;
+
+        this.codigo = codigo || [''];
+        this.hashArquivo = hashArquivo;
+
+        for (let iterador = 0; iterador < this.codigo.length; iterador++) {
+            this.codigo[iterador] += '\0';
+        }
+
+        while (!this.eFinalDoCodigo()) {
+            this.inicioSimbolo = this.atual;
+            this.analisarToken();
+        }
+
+        return {
+            simbolos: this.simbolos,
+            erros: this.erros,
+        } as RetornoLexador;
+    }
+    
+}
\ No newline at end of file
diff --git a/fontes/lexador/dialetos/palavras-reservadas/guarani.ts b/fontes/lexador/dialetos/palavras-reservadas/guarani.ts
new file mode 100644
index 00000000..4578b5fb
--- /dev/null
+++ b/fontes/lexador/dialetos/palavras-reservadas/guarani.ts
@@ -0,0 +1,5 @@
+import tiposDeSimbolos from '../../../tipos-de-simbolos/guarani';
+
+export default {
+    hai: tiposDeSimbolos.HAI
+}
\ No newline at end of file
diff --git a/fontes/lexador/dialetos/palavras-reservadas/rantiaquin.ts b/fontes/lexador/dialetos/palavras-reservadas/rantiaquin.ts
new file mode 100644
index 00000000..ff2dbcee
--- /dev/null
+++ b/fontes/lexador/dialetos/palavras-reservadas/rantiaquin.ts
@@ -0,0 +1,3 @@
+export default {
+    
+}
\ No newline at end of file
diff --git a/fontes/tipos-de-simbolos/guarani.ts b/fontes/tipos-de-simbolos/guarani.ts
new file mode 100644
index 00000000..479bd712
--- /dev/null
+++ b/fontes/tipos-de-simbolos/guarani.ts
@@ -0,0 +1,14 @@
+export default {
+    OI: 'OI', // E,
+    PE: 'PE', // Em
+    HAI: 'HAI', // Escreva
+
+    CHAVE_ESQUERDA: 'CHAVE_ESQUERDA',
+    CHAVE_DIREITA: 'CHAVE_DIREITA',
+    IDENTIFICADOR: 'IDENTIFICADOR',
+    NUMERO: 'NUMERO',
+    PARENTESE_DIREITO: 'PARENTESE_DIREITO',
+    PARENTESE_ESQUERDO: 'PARENTESE_ESQUERDO',
+    TEXTO: 'TEXTO',
+    VIRGULA: 'VIRGULA'
+}
\ No newline at end of file
diff --git a/fontes/tipos-de-simbolos/rantiaquin.ts b/fontes/tipos-de-simbolos/rantiaquin.ts
new file mode 100644
index 00000000..ff2dbcee
--- /dev/null
+++ b/fontes/tipos-de-simbolos/rantiaquin.ts
@@ -0,0 +1,3 @@
+export default {
+    
+}
\ No newline at end of file


Test patch:
diff --git a/testes/guarani/avaliador-sintatico.test.ts b/testes/guarani/avaliador-sintatico.test.ts
new file mode 100644
index 00000000..01cae3d2
--- /dev/null
+++ b/testes/guarani/avaliador-sintatico.test.ts
@@ -0,0 +1,29 @@
+import { ErroAvaliadorSintatico } from "../../fontes/avaliador-sintatico/erro-avaliador-sintatico";
+import { AvaliadorSintaticoGuarani } from "../../fontes/avaliador-sintatico/dialetos/avaliador-sintatico-guarani";
+import { LexadorGuarani } from "../../fontes/lexador/dialetos/lexador-guarani";
+
+describe('Avaliador sintático (Guarani)', () => {
+    describe('analisar()', () => {
+        let lexador: LexadorGuarani;
+        let avaliadorSintatico: AvaliadorSintaticoGuarani;
+
+        beforeEach(() => {
+            lexador = new LexadorGuarani();
+            avaliadorSintatico = new AvaliadorSintaticoGuarani();
+        });
+
+        it('Sucesso - Olá Mundo', () => {
+            const retornoLexador = lexador.mapear(
+                [
+                    'hai("Olá Mundo")'
+                ],
+                -1
+            );
+            const retornoAvaliadorSintatico =
+                avaliadorSintatico.analisar(retornoLexador);
+
+            expect(retornoAvaliadorSintatico).toBeTruthy();
+            expect(retornoAvaliadorSintatico.declaracoes).toHaveLength(1);
+        });
+    });
+});
diff --git a/testes/guarani/lexador.test.ts b/testes/guarani/lexador.test.ts
new file mode 100644
index 00000000..cf0e323a
--- /dev/null
+++ b/testes/guarani/lexador.test.ts
@@ -0,0 +1,43 @@
+import { LexadorGuarani } from '../../fontes/lexador/dialetos/lexador-guarani';
+import tiposDeSimbolos from '../../fontes/tipos-de-simbolos/guarani';
+
+describe('Lexador', () => {
+    describe('mapear()', () => {
+        let lexador: LexadorGuarani;
+
+        beforeEach(() => {
+            lexador = new LexadorGuarani();
+        });
+
+        describe('Cenários de sucesso', () => {
+            it('Sucesso - Código vazio', () => {
+                const resultado = lexador.mapear([''], -1);
+
+                expect(resultado).toBeTruthy();
+                expect(resultado.simbolos).toHaveLength(0);
+            });
+
+            it('Sucesso - Ponto-e-vírgula, opcional', () => {
+                const resultado = lexador.mapear([';;;;;;;;;;;;;;;;;;;;;'], -1);
+
+                expect(resultado).toBeTruthy();
+                expect(resultado.simbolos).toHaveLength(0);
+            });
+
+            it('Sucesso - Olá guaranis', () => {
+                const resultado = lexador.mapear(["hai('Olá guaranis')"], -1);
+
+                expect(resultado).toBeTruthy();
+                expect(resultado.simbolos).toHaveLength(4);
+                expect(resultado.simbolos).toEqual(
+                    expect.arrayContaining([
+                        expect.objectContaining({ tipo: tiposDeSimbolos.HAI }),
+                        expect.objectContaining({ tipo: tiposDeSimbolos.PARENTESE_ESQUERDO }),
+                        expect.objectContaining({ tipo: tiposDeSimbolos.TEXTO }),
+                        expect.objectContaining({ tipo: tiposDeSimbolos.PARENTESE_DIREITO }),
+                    ])
+                );
+            });
+        });
+    });
+});