Repository: DesignLiquido/delegua
Pull request #535 (2023-10-28 19:34:50)

#376 Implementação regex
Resolve: https://github.com/DesignLiquido/delegua/issues/376


Patch:
diff --git a/fontes/analisador-semantico/analisador-semantico.ts b/fontes/analisador-semantico/analisador-semantico.ts
index 3ed04d06..93f46699 100644
--- a/fontes/analisador-semantico/analisador-semantico.ts
+++ b/fontes/analisador-semantico/analisador-semantico.ts
@@ -1,4 +1,4 @@
-import { Atribuir, Chamada, FimPara, FormatacaoEscrita, FuncaoConstruto, Literal, Super, TipoDe, Variavel, Vetor } from '../construtos';
+import { Atribuir, Chamada, ExpressaoRegular, FimPara, FormatacaoEscrita, FuncaoConstruto, Literal, Super, TipoDe, Variavel, Vetor } from '../construtos';
 import {
     Bloco,
     Classe,
@@ -58,6 +58,9 @@ export class AnalisadorSemantico implements AnalisadorSemanticoInterface {
         this.atual = 0;
         this.erros = [];
     }
+    visitarExpressaoExpressaoRegular(expressao: ExpressaoRegular): Promise<any> {
+        return Promise.resolve();
+    }
 
     erro(simbolo: SimboloInterface, mensagemDeErro: string): void {
         this.erros.push({
diff --git a/fontes/analisador-semantico/dialetos/analisador-semantico-birl.ts b/fontes/analisador-semantico/dialetos/analisador-semantico-birl.ts
index d45dffd1..8356efc5 100644
--- a/fontes/analisador-semantico/dialetos/analisador-semantico-birl.ts
+++ b/fontes/analisador-semantico/dialetos/analisador-semantico-birl.ts
@@ -1,4 +1,4 @@
-import { Atribuir, FimPara, FormatacaoEscrita, Literal, Super, TipoDe, Variavel } from '../../construtos';
+import { Atribuir, ExpressaoRegular, FimPara, FormatacaoEscrita, Literal, Super, TipoDe, Variavel } from '../../construtos';
 import {
     Bloco,
     Classe,
@@ -60,9 +60,12 @@ export class AnalisadorSemanticoBirl implements AnalisadorSemanticoInterface {
         this.atual = 0;
         this.erros = [];
     }
+    visitarExpressaoExpressaoRegular(expressao: ExpressaoRegular): Promise<any> {
+        return Promise.resolve();
+    }
 
     visitarExpressaoTipoDe(expressao: TipoDe): Promise<any> {
-        throw new Error('Método não implementado.');
+        return Promise.resolve();
     }
 
     visitarDeclaracaoClasse(declaracao: Classe) {
diff --git a/fontes/avaliador-sintatico/avaliador-sintatico.ts b/fontes/avaliador-sintatico/avaliador-sintatico.ts
index e3fd3fba..f282e596 100644
--- a/fontes/avaliador-sintatico/avaliador-sintatico.ts
+++ b/fontes/avaliador-sintatico/avaliador-sintatico.ts
@@ -26,6 +26,7 @@ import {
     Variavel,
     Vetor,
     Isto,
+    ExpressaoRegular,
 } from '../construtos';
 
 import { ErroAvaliadorSintatico } from './erro-avaliador-sintatico';
@@ -271,6 +272,20 @@ export class AvaliadorSintatico implements AvaliadorSintaticoInterface<SimboloIn
                     simboloAtual,
                     _expressao instanceof Literal ? _expressao.valor : _expressao
                 );
+
+            case tiposDeSimbolos.EXPRESSAO_REGULAR:
+                let valor: string = '';
+                this.avancarEDevolverAnterior();
+                while (!this.verificarTipoSimboloAtual(tiposDeSimbolos.EXPRESSAO_REGULAR)) {
+                    valor += this.simboloAtual().lexema;
+                    this.avancarEDevolverAnterior();
+                }
+                this.avancarEDevolverAnterior();
+                return new ExpressaoRegular(
+                    this.hashArquivo,
+                    simboloAtual,
+                    valor
+                );
         }
 
         throw this.erro(this.simbolos[this.atual], 'Esperado expressão.');
diff --git a/fontes/construtos/expressao-regular.ts b/fontes/construtos/expressao-regular.ts
new file mode 100644
index 00000000..7f2613e1
--- /dev/null
+++ b/fontes/construtos/expressao-regular.ts
@@ -0,0 +1,21 @@
+import { VisitanteComumInterface, SimboloInterface, VariavelInterface } from '../interfaces';
+import { Construto } from './construto';
+
+export class ExpressaoRegular implements Construto {
+    linha: number;
+    hashArquivo: number;
+    valor: any;
+
+    simbolo: SimboloInterface;
+
+    constructor(hashArquivo: number, simbolo: SimboloInterface, valor: any) {
+        this.linha = Number(simbolo.linha);
+        this.hashArquivo = hashArquivo;
+        this.valor = valor;
+        this.simbolo = simbolo;
+    }
+
+    async aceitar(visitante: VisitanteComumInterface): Promise<RegExp> {
+        return Promise.resolve(visitante.visitarExpressaoExpressaoRegular(this));
+    }
+}
diff --git a/fontes/construtos/index.ts b/fontes/construtos/index.ts
index 804b0689..e0914ff2 100644
--- a/fontes/construtos/index.ts
+++ b/fontes/construtos/index.ts
@@ -1,21 +1,22 @@
+export * from './acesso-indice-variavel';
+export * from './acesso-metodo';
+export * from './agrupamento';
 export * from './atribuicao-por-indice';
 export * from './atribuir';
 export * from './binario';
 export * from './chamada';
 export * from './constante';
 export * from './constante-ou-variavel';
+export * from './construto';
 export * from './definir-valor';
 export * from './dicionario';
-export * from './construto';
+export * from './expressao-regular';
 export * from './fim-para';
 export * from './formatacao-escrita';
 export * from './funcao';
-export * from './acesso-metodo';
-export * from './agrupamento';
 export * from './isto';
 export * from './literal';
 export * from './logico';
-export * from './acesso-indice-variavel';
 export * from './super';
 export * from './tipo-de';
 export * from './unario';
diff --git a/fontes/interfaces/visitante-comum-interface.ts b/fontes/interfaces/visitante-comum-interface.ts
index 44349559..00276d81 100644
--- a/fontes/interfaces/visitante-comum-interface.ts
+++ b/fontes/interfaces/visitante-comum-interface.ts
@@ -1,4 +1,4 @@
-import { Atribuir, FimPara, Literal, Super, TipoDe } from '../construtos';
+import { Atribuir, ExpressaoRegular, FimPara, Literal, Super, TipoDe } from '../construtos';
 import {
     Bloco,
     Classe,
@@ -58,6 +58,7 @@ export interface VisitanteComumInterface {
     visitarExpressaoDeleguaFuncao(expressao: any): any;
     visitarExpressaoDeVariavel(expressao: any): any;
     visitarExpressaoDicionario(expressao: any): any;
+    visitarExpressaoExpressaoRegular(expressao: ExpressaoRegular): Promise<RegExp>;
     visitarExpressaoEscrevaMesmaLinha(declaracao: EscrevaMesmaLinha): any;
     visitarExpressaoFalhar(expressao: any): Promise<any>;
     visitarExpressaoFimPara(declaracao: FimPara): any;
diff --git a/fontes/interpretador/dialetos/egua-classico/interpretador-egua-classico.ts b/fontes/interpretador/dialetos/egua-classico/interpretador-egua-classico.ts
index 150c6a2f..a37cd048 100644
--- a/fontes/interpretador/dialetos/egua-classico/interpretador-egua-classico.ts
+++ b/fontes/interpretador/dialetos/egua-classico/interpretador-egua-classico.ts
@@ -11,6 +11,7 @@ import {
     AcessoIndiceVariavel,
     Atribuir,
     Construto,
+    ExpressaoRegular,
     FimPara,
     FormatacaoEscrita,
     Literal,
@@ -89,6 +90,9 @@ export class InterpretadorEguaClassico implements InterpretadorInterface {
 
         carregarBibliotecaGlobal(this, this.pilhaEscoposExecucao);
     }
+    visitarExpressaoExpressaoRegular(expressao: ExpressaoRegular): Promise<any> {
+        throw new Error('Método não implementado.');
+    }
 
     visitarExpressaoTipoDe(expressao: TipoDe): Promise<any> {
         throw new Error('Método não implementado.');
diff --git a/fontes/interpretador/dialetos/egua-classico/resolvedor/resolvedor.ts b/fontes/interpretador/dialetos/egua-classico/resolvedor/resolvedor.ts
index edc922fe..ae418711 100644
--- a/fontes/interpretador/dialetos/egua-classico/resolvedor/resolvedor.ts
+++ b/fontes/interpretador/dialetos/egua-classico/resolvedor/resolvedor.ts
@@ -1,4 +1,4 @@
-import { AcessoMetodo, Construto, FimPara, FormatacaoEscrita, Super, TipoDe, Variavel } from '../../../../construtos';
+import { AcessoMetodo, Construto, ExpressaoRegular, FimPara, FormatacaoEscrita, Super, TipoDe, Variavel } from '../../../../construtos';
 import {
     Bloco,
     Const,
@@ -75,6 +75,10 @@ export class ResolvedorEguaClassico implements ResolvedorInterface, Interpretado
         this.classeAtual = TipoClasse.NENHUM;
         this.cicloAtual = TipoClasse.NENHUM;
     }
+    
+    visitarExpressaoExpressaoRegular(expressao: ExpressaoRegular): Promise<any> {
+        throw new Error('Método não implementado.');
+    }
 
     visitarExpressaoTipoDe(expressao: TipoDe): Promise<any> {
         throw new Error('Método não implementado.');
diff --git a/fontes/interpretador/dialetos/mapler/interpretador-mapler.ts b/fontes/interpretador/dialetos/mapler/interpretador-mapler.ts
index df9413c7..d64f7b75 100644
--- a/fontes/interpretador/dialetos/mapler/interpretador-mapler.ts
+++ b/fontes/interpretador/dialetos/mapler/interpretador-mapler.ts
@@ -1,4 +1,4 @@
-import { Binario, Construto, Logico, Variavel } from '../../../construtos';
+import { Binario, Construto, ExpressaoRegular, Logico, Variavel } from '../../../construtos';
 import { Const, Escreva, EscrevaMesmaLinha, Fazer, Leia } from '../../../declaracoes';
 import { InterpretadorBase } from '../..';
 import { ContinuarQuebra, Quebra } from '../../../quebras';
@@ -18,6 +18,10 @@ export class InterpretadorMapler extends InterpretadorBase {
         this.mensagemPrompt = '> ';
     }
 
+    visitarExpressaoExpressaoRegular(expressao: ExpressaoRegular): Promise<any> {
+        throw new Error('Método não implementado.');
+    }
+    
     visitarDeclaracaoConst(declaracao: Const): Promise<any> {
         throw new Error('Método não implementado.');
     }
diff --git a/fontes/interpretador/dialetos/portugol-ipt/interpretador-portugol-ipt.ts b/fontes/interpretador/dialetos/portugol-ipt/interpretador-portugol-ipt.ts
index 826d5322..49a575c9 100644
--- a/fontes/interpretador/dialetos/portugol-ipt/interpretador-portugol-ipt.ts
+++ b/fontes/interpretador/dialetos/portugol-ipt/interpretador-portugol-ipt.ts
@@ -1,4 +1,4 @@
-import { Atribuir, Construto, FimPara, FormatacaoEscrita, Literal, Super, TipoDe, Variavel } from '../../../construtos';
+import { Atribuir, Construto, ExpressaoRegular, FimPara, FormatacaoEscrita, Literal, Super, TipoDe, Variavel } from '../../../construtos';
 import {
     Bloco,
     Classe,
@@ -73,6 +73,9 @@ export class InterpretadorPortugolIpt implements InterpretadorInterface {
         };
         this.pilhaEscoposExecucao.empilhar(escopoExecucao);
     }
+    visitarExpressaoExpressaoRegular(expressao: ExpressaoRegular): Promise<any> {
+        throw new Error('Método não implementado.');
+    }
 
     visitarExpressaoTipoDe(expressao: TipoDe): Promise<any> {
         throw new Error('Método não implementado.');
diff --git a/fontes/interpretador/interpretador-base.ts b/fontes/interpretador/interpretador-base.ts
index 7b3b6daa..65a990e7 100644
--- a/fontes/interpretador/interpretador-base.ts
+++ b/fontes/interpretador/interpretador-base.ts
@@ -50,6 +50,7 @@ import {
     Binario,
     Chamada,
     Construto,
+    ExpressaoRegular,
     FimPara,
     FormatacaoEscrita,
     Literal,
@@ -136,6 +137,24 @@ export class InterpretadorBase implements InterpretadorInterface {
         carregarBibliotecasGlobais(this, this.pilhaEscoposExecucao);
     }
 
+    //https://stackoverflow.com/a/66751666/9043143
+    textoParaRegex(texto): any {
+        const match = texto.match(/^([\/~@;%#'])(.*?)\1([gimsuy]*)$/);
+        return match ? 
+          new RegExp(
+            match[2],
+            match[3]
+              .split('')
+              .filter((char, pos, flagArr) => flagArr.indexOf(char) === pos)
+              .join('')
+          ) 
+          : new RegExp(texto);
+    }
+
+    visitarExpressaoExpressaoRegular(expressao: ExpressaoRegular): Promise<RegExp> {
+        return this.textoParaRegex(expressao.valor);
+    }
+
     async visitarExpressaoTipoDe(expressao: TipoDe): Promise<string> {
         let tipoDe = expressao.valor;
 
@@ -700,10 +719,9 @@ export class InterpretadorBase implements InterpretadorInterface {
     }
 
     async visitarDeclaracaoPara(declaracao: Para): Promise<any> {
-        const declaracaoInicializador = Array.isArray(declaracao.inicializador)
+        const declaracaoInicializador = declaracao.inicializador?.length
             ? declaracao.inicializador[0]
             : declaracao.inicializador;
-            
         if (declaracaoInicializador !== null) {
             await this.avaliar(declaracaoInicializador);
         }
diff --git a/fontes/interpretador/interpretador-com-depuracao.ts b/fontes/interpretador/interpretador-com-depuracao.ts
index a5a216d6..8857fb60 100644
--- a/fontes/interpretador/interpretador-com-depuracao.ts
+++ b/fontes/interpretador/interpretador-com-depuracao.ts
@@ -201,12 +201,8 @@ export class InterpretadorComDepuracao extends InterpretadorBase implements Inte
 
     async visitarDeclaracaoPara(declaracao: Para): Promise<any> {
         const corpoExecucao = declaracao.corpo as Bloco;
-        const declaracaoInicializador = Array.isArray(declaracao.inicializador)
-            ? declaracao.inicializador[0]
-            : declaracao.inicializador;
-
-        if (declaracaoInicializador !== null && !declaracao.inicializada) {
-            await this.avaliar(declaracaoInicializador);
+        if (declaracao.inicializador !== null && !declaracao.inicializada) {
+            await this.avaliar(declaracao.inicializador);
             // O incremento vai ao final do bloco de escopo.
             if (declaracao.incrementar !== null) {
                 corpoExecucao.declaracoes.push(declaracao.incrementar);
diff --git a/fontes/lexador/lexador.ts b/fontes/lexador/lexador.ts
index 297c5896..0ebf7b9d 100644
--- a/fontes/lexador/lexador.ts
+++ b/fontes/lexador/lexador.ts
@@ -323,8 +323,13 @@ export class Lexador implements LexadorInterface<SimboloInterface> {
                 break;
 
             case '|':
-                this.adicionarSimbolo(tiposDeSimbolos.BIT_OR);
                 this.avancar();
+                if (this.simboloAtual() === '|') {
+                    this.adicionarSimbolo(tiposDeSimbolos.EXPRESSAO_REGULAR, '||');
+                    this.avancar();
+                } else {
+                    this.adicionarSimbolo(tiposDeSimbolos.BIT_OR);
+                }
                 break;
 
             case '^':
diff --git a/fontes/tipos-de-simbolos/delegua.ts b/fontes/tipos-de-simbolos/delegua.ts
index d8c3fc91..618d0db3 100644
--- a/fontes/tipos-de-simbolos/delegua.ts
+++ b/fontes/tipos-de-simbolos/delegua.ts
@@ -29,6 +29,7 @@ export default {
     ESCOLHA: 'ESCOLHA',
     ESCREVA: 'ESCREVA',
     EXPONENCIACAO: 'EXPONENCIACAO',
+    EXPRESSAO_REGULAR: 'EXPRESSAO_REGULAR',
     FALHAR: 'FALHAR',
     FALSO: 'FALSO',
     FAZER: 'FAZER',


Test patch:
diff --git a/testes/interpretador.test.ts b/testes/interpretador.test.ts
index eaaaff00..3ff8ab24 100644
--- a/testes/interpretador.test.ts
+++ b/testes/interpretador.test.ts
@@ -994,6 +994,25 @@ describe('Interpretador', () => {
                     expect(retornoInterpretador.erros).toHaveLength(0);
                 });
             })
+
+            describe('Expressão Regular', () => {
+                it('Método substituir()', async () => {
+                    const retornoLexador = lexador.mapear([
+                        "var str = \"olá mundo, olá universo\";",
+                        "var novaStr = str.substituir(||/olá/g||, \"oi\");",
+                        "escreva(novaStr);",
+                    ], -1);
+                    const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+
+                    interpretador.funcaoDeRetorno = (saida: any) => {
+                        expect(saida).toEqual('oi mundo, oi universo');
+                    };
+
+                    const retornoInterpretador = await interpretador.interpretar(retornoAvaliadorSintatico.declaracoes);
+
+                    expect(retornoInterpretador.erros).toHaveLength(0);
+                });
+            })
         });
 
         describe('Cenários de falha', () => {