Repository: DesignLiquido/delegua
Pull request #282 (2023-02-17 23:46:11)

Portugol IPT - Lexador

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.64%">73.61% (-0.03% 🔻)</div> |    4315/5862    |
|                                                                           🟡                                                                           | Branches   | <div title="Base coverage is: 61.82%">61.93% (+0.11% 🔼)</div> |    1329/2146    |
|                                                                           🟡                                                                           | Functions  | <div title="Base coverage is: 73.91%">73.93% (+0.02% 🔼)</div> |     641/867     |
|                                                                           🟡                                                                           | Lines      | <div title="Base coverage is: 74%">73.97% (-0.02% 🔻)</div>    |    4167/5633    |

<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-portugol-ipt.ts">`...` / lexador-portugol-ipt.ts</div>     | 70.37%     | 66.67%   | 75%       | 71.43% |
|                                                                           🟢                                                                           | <div title="lexador/dialetos/palavras-reservadas/portugol-ipt.ts">`...` / portugol-ipt.ts</div> | 100%       | 100%     | 100%      | 100%   |
|                                                                           🟢                                                                           | <div title="tipos-de-simbolos/portugol-ipt.ts">`...` / portugol-ipt.ts</div>                    | 100%       | 100%     | 100%      | 100%   |

</details>


## Test suite run success
294 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 f951effd03ad8997bc2ba44c7fbb0cc9d5622f6c</p>
<!-- Sticky Pull Request Comment -->

Patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 69431e84..b0279047 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -419,7 +419,7 @@
             "runtimeArgs": [
                 "--inspect-brk",
                 "${workspaceRoot}/node_modules/jest/bin/jest.js",
-                "portugol-studio",
+                "portugol-ipt",
                 "--runInBand"
             ],
             "skipFiles": ["<node_internals>/**", "node_modules/**"],
diff --git a/fontes/lexador/dialetos/lexador-portugol-ipt.ts b/fontes/lexador/dialetos/lexador-portugol-ipt.ts
new file mode 100644
index 00000000..24db8e22
--- /dev/null
+++ b/fontes/lexador/dialetos/lexador-portugol-ipt.ts
@@ -0,0 +1,247 @@
+import { LexadorInterface, SimboloInterface } from "../../interfaces";
+import { RetornoLexador } from "../../interfaces/retornos";
+import { ErroLexador } from "../erro-lexador";
+import { Simbolo } from "../simbolo";
+
+import palavrasReservadas from "./palavras-reservadas/portugol-ipt";
+import tiposDeSimbolos from "../../tipos-de-simbolos/portugol-ipt";
+
+export class LexadorPortugolIpt implements LexadorInterface {
+    simbolos: SimboloInterface[];
+    erros: ErroLexador[];
+    hashArquivo: number;
+    
+    codigo: string | string[];
+    inicioSimbolo: number;
+    atual: number;
+    linha: number;
+
+    eDigito(caractere: string): boolean {
+        return caractere >= '0' && caractere <= '9';
+    }
+
+    eAlfabeto(caractere: string): boolean {
+        const acentuacoes = [
+            'á',
+            'Á',
+            'ã',
+            'Ã',
+            'â',
+            'Â',
+            'à',
+            'À',
+            'é',
+            'É',
+            'ê',
+            'Ê',
+            'í',
+            'Í',
+            'ó',
+            'Ó',
+            'õ',
+            'Õ',
+            'ô',
+            'Ô',
+            'ú',
+            'Ú',
+            'ç',
+            'Ç',
+            '_',
+        ];
+
+        return (
+            (caractere >= 'a' && caractere <= 'z') ||
+            (caractere >= 'A' && caractere <= 'Z') ||
+            acentuacoes.includes(caractere)
+        );
+    }
+    eAlfabetoOuDigito(caractere: string): boolean {
+        return this.eDigito(caractere) || this.eAlfabeto(caractere);
+    }
+
+    eFinalDoCodigo(): boolean {
+        if (this.linha > this.codigo.length - 1) 
+            return true;
+
+        return this.linha == this.codigo.length - 1 && 
+            this.codigo[this.codigo.length - 1].length <= this.atual;
+    }
+
+    /**
+     * Indica se o código está na última linha.
+     * @returns Verdadeiro se contador de linhas está na última linha.
+     *          Falso caso contrário.
+     */
+    private eUltimaLinha(): boolean {
+        return this.linha >= this.codigo.length - 1;
+    }
+
+    avancar(): string | void {
+        this.atual += 1;
+
+        if (this.eFinalDaLinha() && !this.eUltimaLinha()) {
+            this.linha++;
+            this.atual = 0;
+        }
+    }
+
+    adicionarSimbolo(tipo: any, literal: any): void {
+        throw new Error("Método não implementado.");
+    }
+
+    private eFinalDaLinha(): boolean {
+        return this.atual >= this.codigo[this.linha].length;
+    }
+
+    simboloAtual(): string {
+        if (this.eFinalDaLinha()) return '\0';
+        if (this.linha > this.codigo.length - 1) return '\0';
+        return this.codigo[this.linha].charAt(this.atual);
+    }
+
+    proximoSimbolo(): string {
+        if (this.atual + 1 >= this.codigo[this.linha].length) 
+            return '\0';
+        return this.codigo[this.linha].charAt(this.atual + 1);
+    }
+
+    simboloAnterior(): string {
+        return this.codigo[this.linha].charAt(this.atual - 1);
+    }
+
+    analisarTexto(delimitador: string): void {
+        const linhaPrimeiroCaracter: number = this.linha;
+        while (this.simboloAtual() !== delimitador && !this.eFinalDoCodigo()) {
+            this.avancar();
+        }
+
+        if (this.eFinalDoCodigo()) {
+            this.erros.push({
+                linha: this.linha + 1,
+                caractere: this.simboloAnterior(),
+                mensagem: 'Texto não finalizado.',
+            } as ErroLexador);
+            return;
+        }
+
+        const textoCompleto = this.codigo[this.linha].substring(this.inicioSimbolo + 1, this.atual);
+
+        this.simbolos.push(
+            new Simbolo(
+                tiposDeSimbolos.TEXTO,
+                textoCompleto,
+                textoCompleto,
+                linhaPrimeiroCaracter + 1,
+                this.hashArquivo
+            )
+        );
+    }
+    
+    analisarNumero(): void {
+        const linhaPrimeiroDigito: number = this.linha;
+        while (this.eDigito(this.simboloAtual()) && this.linha === linhaPrimeiroDigito) {
+            this.avancar();
+        }
+
+        if (this.simboloAtual() == '.' && this.eDigito(this.proximoSimbolo())) {
+            this.avancar();
+
+            while (this.eDigito(this.simboloAtual())) {
+                this.avancar();
+            }
+        }
+
+        let numeroCompleto: string;
+        if (linhaPrimeiroDigito < this.linha) {
+            const linhaNumero: string = this.codigo[linhaPrimeiroDigito];
+            numeroCompleto = linhaNumero.substring(this.inicioSimbolo, linhaNumero.length);
+        } else {
+            numeroCompleto = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual);
+        }
+
+        this.simbolos.push(
+            new Simbolo(
+                tiposDeSimbolos.NUMERO,
+                numeroCompleto,
+                parseFloat(numeroCompleto),
+                linhaPrimeiroDigito + 1,
+                this.hashArquivo
+            )
+        );
+    }
+
+    identificarPalavraChave(): void {
+        const linhaPrimeiroCaracter: number = this.linha;
+        while (this.eAlfabetoOuDigito(this.simboloAtual()) && this.linha === linhaPrimeiroCaracter) {
+            this.avancar();
+        }
+
+        let textoPalavraChave: string;
+        if (linhaPrimeiroCaracter < this.linha) {
+            const linhaPalavraChave: string = this.codigo[linhaPrimeiroCaracter];
+            textoPalavraChave = linhaPalavraChave.substring(this.inicioSimbolo, linhaPalavraChave.length);
+        } else {
+            textoPalavraChave = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual);
+        }
+
+        const tipo: string =
+            textoPalavraChave in palavrasReservadas
+                ? palavrasReservadas[textoPalavraChave]
+                : tiposDeSimbolos.IDENTIFICADOR;
+
+        this.simbolos.push(new Simbolo(tipo, textoPalavraChave, null, linhaPrimeiroCaracter + 1, this.hashArquivo));
+    }
+
+    analisarToken(): void {
+        const caractere = this.simboloAtual();
+
+        switch (caractere) {
+            case ' ':
+            case '\t':
+            case '\r':
+            case '\n':
+            case '\0':
+            case ';':
+                this.avancar();
+                break;
+            case '"':
+                this.avancar();
+                this.analisarTexto('"');
+                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.simbolos = [];
+        this.erros = [];
+
+        this.inicioSimbolo = 0;
+        this.atual = 0;
+        this.linha = 0;
+
+        this.codigo = codigo || [''];
+        this.hashArquivo = hashArquivo;
+
+        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/portugol-ipt.ts b/fontes/lexador/dialetos/palavras-reservadas/portugol-ipt.ts
new file mode 100644
index 00000000..c8785c53
--- /dev/null
+++ b/fontes/lexador/dialetos/palavras-reservadas/portugol-ipt.ts
@@ -0,0 +1,7 @@
+import tiposDeSimbolos from '../../../tipos-de-simbolos/portugol-ipt';
+
+export default {
+    escrever: tiposDeSimbolos.ESCREVER,
+    fim: tiposDeSimbolos.FIM,
+    inicio: tiposDeSimbolos.INICIO
+};
\ No newline at end of file
diff --git a/fontes/tipos-de-simbolos/portugol-ipt.ts b/fontes/tipos-de-simbolos/portugol-ipt.ts
new file mode 100644
index 00000000..09ee81b5
--- /dev/null
+++ b/fontes/tipos-de-simbolos/portugol-ipt.ts
@@ -0,0 +1,27 @@
+export default {
+    ADICAO: 'ADICAO',
+    DIFERENTE: 'DIFERENTE',
+    DIVISAO: 'DIVISAO',
+    DIVISAO_INTEIRA: 'DIVISAO_INTEIRA',
+    E: 'E',
+    ESCREVER: 'ESCREVER',
+    EXPONENCIACAO: 'EXPONENCIACAO',
+    FIM: 'FIM',
+    IDENTIFICADOR: 'IDENTIFICADOR',
+    IGUAL: 'IGUAL',
+    INICIO: 'INICIO',
+    MAIOR: 'MAIOR',
+    MAIOR_IGUAL: 'MAIOR_IGUAL',
+    MENOR: 'MENOR',
+    MENOR_IGUAL: 'MENOR_IGUAL',
+    MODULO: 'MODULO',
+    MULTIPLICACAO: 'MULTIPLICACAO',
+    NEGACAO: 'NEGACAO',
+    NUMERO: 'NUMERO',
+    OU: 'OU',
+    PARENTESE_ESQUERDO: 'PARENTESE_ESQUERDO',
+    PARENTESE_DIREITO: 'PARENTESE_DIREITO',
+    SUBTRACAO: 'SUBTRACAO',
+    TEXTO: 'TEXTO',
+    VIRGULA: 'VIRGULA',
+};
\ No newline at end of file


Test patch:
diff --git a/testes/portugol-ipt/lexador.test.ts b/testes/portugol-ipt/lexador.test.ts
new file mode 100644
index 00000000..8a1a9220
--- /dev/null
+++ b/testes/portugol-ipt/lexador.test.ts
@@ -0,0 +1,44 @@
+import { LexadorPortugolIpt } from '../../fontes/lexador/dialetos/lexador-portugol-ipt';
+
+import tiposDeSimbolos from '../../fontes/tipos-de-simbolos/portugol-ipt';
+
+describe('Lexador (Portugol IPT)', () => {
+    describe('mapear()', () => {
+        let lexador: LexadorPortugolIpt;
+
+        beforeEach(() => {
+            lexador = new LexadorPortugolIpt();
+        });
+
+        describe('Cenários de sucesso', () => {
+            it('Sucesso - Código vazio', () => {
+                const resultado = lexador.mapear([''], -1);
+
+                expect(resultado).toBeTruthy();
+                expect(resultado.simbolos).toHaveLength(0);
+            });
+        });
+
+        describe('Cenários de sucesso', () => {
+            it('Sucesso - Código apenas com `inicio` e `fim`', () => {
+                const resultado = lexador.mapear(['inicio', 'fim'], -1);
+
+                expect(resultado).toBeTruthy();
+                expect(resultado.simbolos).toHaveLength(2);
+            });
+        });
+
+        describe('Cenários de sucesso', () => {
+            it('Sucesso - Olá Mundo', () => {
+                const resultado = lexador.mapear([
+                    'inicio', 
+                    'escrever "Olá mundo"',
+                    'fim'
+                ], -1);
+
+                expect(resultado).toBeTruthy();
+                expect(resultado.simbolos).toHaveLength(4);
+            });
+        });
+    });
+});