Repository: DesignLiquido/delegua (ts)

Issue:
[Delégua] Implementar primitivas `dicionario.chaves()` e `dicionario.valores()`
Exemplo:

```js
var meuDicionario = {"a": 1, "b": 2, "c": 3}
escreva(meuDicionario.chaves()) // ["a", "b", "c"]
escreva(meuDicionario.valores()) // [1, 2, 3]
```

Pull request:
Primitivas de dicionário `chaves()` e `valores()`. 
Resolve #618.

Patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 932db8cd..cd633de8 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -700,6 +700,22 @@
                 "ts-node/register/transpile-only"
             ]
         },
+        {
+            "name": "Testes unitários",
+            "type": "node",
+            "request": "launch",
+            "runtimeArgs": [
+                "--inspect-brk",
+                "${workspaceRoot}/node_modules/jest/bin/jest.js",
+                "--runInBand"
+            ],
+            "skipFiles": [
+                "<node_internals>/**",
+                "node_modules/**"
+            ],
+            "console": "integratedTerminal",
+            "internalConsoleOptions": "neverOpen"
+        },
         {
             "name": "Testes unitários - Birl",
             "type": "node",
@@ -724,7 +740,6 @@
             "runtimeArgs": [
                 "--inspect-brk",
                 "${workspaceRoot}/node_modules/jest/bin/jest.js",
-                // "interpretador",
                 "interpretador",
                 "--runInBand"
             ],
diff --git a/fontes/interfaces/visitante-comum-interface.ts b/fontes/interfaces/visitante-comum-interface.ts
index b2f51487..2e80d408 100644
--- a/fontes/interfaces/visitante-comum-interface.ts
+++ b/fontes/interfaces/visitante-comum-interface.ts
@@ -1,4 +1,4 @@
-import { Atribuir, ExpressaoRegular, FimPara, Literal, Super, TipoDe } from '../construtos';
+import { AcessoMetodoOuPropriedade, Atribuir, ExpressaoRegular, FimPara, Literal, Super, TipoDe } from '../construtos';
 import {
     Aleatorio,
     Bloco,
@@ -50,7 +50,7 @@ export interface VisitanteComumInterface {
     visitarDeclaracaoVarMultiplo(declaracao: VarMultiplo): Promise<any>;
     visitarExpressaoAcessoIndiceVariavel(expressao: any): any;
     visitarExpressaoAcessoElementoMatriz(expressao: any): any;
-    visitarExpressaoAcessoMetodo(expressao: any): any;
+    visitarExpressaoAcessoMetodo(expressao: AcessoMetodoOuPropriedade): any;
     visitarExpressaoAgrupamento(expressao: any): Promise<any>;
     visitarExpressaoAtribuicaoPorIndice(expressao: any): Promise<any>;
     visitarExpressaoAtribuicaoPorIndicesMatriz(expressao: any): Promise<any>;
diff --git a/fontes/interpretador/interpretador-base.ts b/fontes/interpretador/interpretador-base.ts
index 6c0318d8..39d10661 100644
--- a/fontes/interpretador/interpretador-base.ts
+++ b/fontes/interpretador/interpretador-base.ts
@@ -46,6 +46,7 @@ import {
 } from '../estruturas';
 import {
     AcessoIndiceVariavel,
+    AcessoMetodoOuPropriedade,
     Agrupamento,
     Atribuir,
     Binario,
@@ -71,15 +72,16 @@ import { ContinuarQuebra, Quebra, RetornoQuebra, SustarQuebra } from '../quebras
 import { PilhaEscoposExecucaoInterface } from '../interfaces/pilha-escopos-execucao-interface';
 import { inferirTipoVariavel } from './inferenciador';
 import { MetodoPrimitiva } from '../estruturas/metodo-primitiva';
+import { ArgumentoInterface } from './argumento-interface';
 
 import primitivasDicionario from '../bibliotecas/primitivas-dicionario';
 import primitivasNumero from '../bibliotecas/primitivas-numero';
 import primitivasTexto from '../bibliotecas/primitivas-texto';
 import primitivasVetor from '../bibliotecas/primitivas-vetor';
+
 import tiposDeSimbolos from '../tipos-de-simbolos/delegua';
-import { ArgumentoInterface } from './argumento-interface';
-import tipoDeDadosPrimitivos from '../tipos-de-dados/primitivos'
-import tipoDeDadosDelegua from '../tipos-de-dados/delegua'
+import tipoDeDadosPrimitivos from '../tipos-de-dados/primitivos';
+import tipoDeDadosDelegua from '../tipos-de-dados/delegua';
 
 /**
  * O Interpretador visita todos os elementos complexos gerados pelo avaliador sintático (_parser_),
@@ -287,11 +289,8 @@ export class InterpretadorBase implements InterpretadorInterface {
             if (elemento?.valor?.tipo === tipoDeDadosDelegua.LOGICO) {
                 textoFinal = textoFinal.replace('${' + elemento.variavel + '}', this.paraTexto(elemento?.valor?.valor));
             } else {
-                const valor = elemento?.valor?.hasOwnProperty('valor') ? elemento?.valor.valor : elemento?.valor
-                textoFinal = textoFinal.replace(
-                    '${' + elemento.variavel + '}',
-                    `${this.paraTexto(valor)}`
-                );
+                const valor = elemento?.valor?.hasOwnProperty('valor') ? elemento?.valor.valor : elemento?.valor;
+                textoFinal = textoFinal.replace('${' + elemento.variavel + '}', `${this.paraTexto(valor)}`);
             }
         });
 
@@ -422,7 +421,10 @@ export class InterpretadorBase implements InterpretadorInterface {
         const tipoConteudo: string = conteudo.hasOwnProperty('tipo') ? conteudo.tipo : typeof conteudo;
 
         resultado = valorConteudo;
-        if ([tipoDeDadosDelegua.NUMERO, tipoDeDadosPrimitivos.NUMERO].includes(tipoConteudo) && declaracao.casasDecimais > 0) {
+        if (
+            [tipoDeDadosDelegua.NUMERO, tipoDeDadosPrimitivos.NUMERO].includes(tipoConteudo) &&
+            declaracao.casasDecimais > 0
+        ) {
             resultado = valorConteudo.toLocaleString('pt', { maximumFractionDigits: declaracao.casasDecimais });
         }
 
@@ -452,16 +454,20 @@ export class InterpretadorBase implements InterpretadorInterface {
         direita: VariavelInterface | any,
         esquerda: VariavelInterface | any
     ): void {
-        const tipoDireita: string = direita.tipo ? direita.tipo : typeof direita === tipoDeDadosPrimitivos.NUMERO ? tipoDeDadosDelegua.NUMERO : String(NaN);
+        const tipoDireita: string = direita.tipo
+            ? direita.tipo
+            : typeof direita === tipoDeDadosPrimitivos.NUMERO
+            ? tipoDeDadosDelegua.NUMERO
+            : String(NaN);
         const tipoEsquerda: string = esquerda.tipo
             ? esquerda.tipo
             : typeof esquerda === tipoDeDadosPrimitivos.NUMERO
             ? tipoDeDadosDelegua.NUMERO
             : String(NaN);
-            
-        const tiposNumericos = [tipoDeDadosDelegua.INTEIRO, tipoDeDadosDelegua.NUMERO];
 
-        if(tiposNumericos.includes(tipoDireita) && tiposNumericos.includes(tipoEsquerda)) return;
+        const tiposNumericos = [tipoDeDadosDelegua.INTEIRO, tipoDeDadosDelegua.NUMERO, tipoDeDadosDelegua.NÚMERO];
+
+        if (tiposNumericos.includes(tipoDireita) && tiposNumericos.includes(tipoEsquerda)) return;
 
         throw new ErroEmTempoDeExecucao(operador, 'Operadores precisam ser números.', operador.linha);
     }
@@ -482,9 +488,9 @@ export class InterpretadorBase implements InterpretadorInterface {
             case tiposDeSimbolos.MAIOR:
                 if (tipoEsquerdo === tipoDeDadosDelegua.NUMERO && tipoDireito === tipoDeDadosDelegua.NUMERO) {
                     return Number(valorEsquerdo) > Number(valorDireito);
-                } else {
-                    return String(valorEsquerdo) > String(valorDireito);
-                }
+                } 
+                    
+                return String(valorEsquerdo) > String(valorDireito);
 
             case tiposDeSimbolos.MAIOR_IGUAL:
                 this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
@@ -493,9 +499,9 @@ export class InterpretadorBase implements InterpretadorInterface {
             case tiposDeSimbolos.MENOR:
                 if (tipoEsquerdo === tipoDeDadosDelegua.NUMERO && tipoDireito === tipoDeDadosDelegua.NUMERO) {
                     return Number(valorEsquerdo) < Number(valorDireito);
-                } else {
-                    return String(valorEsquerdo) < String(valorDireito);
-                }
+                } 
+                    
+                return String(valorEsquerdo) < String(valorDireito);
 
             case tiposDeSimbolos.MENOR_IGUAL:
                 this.verificarOperandosNumeros(expressao.operador, esquerda, direita);
@@ -508,11 +514,15 @@ export class InterpretadorBase implements InterpretadorInterface {
 
             case tiposDeSimbolos.ADICAO:
             case tiposDeSimbolos.MAIS_IGUAL:
-                if ([tipoDeDadosDelegua.NUMERO, tipoDeDadosDelegua.INTEIRO].includes(tipoEsquerdo) && [tipoDeDadosDelegua.NUMERO, tipoDeDadosDelegua.INTEIRO].includes(tipoDireito)) {
+                const tiposNumericos = [tipoDeDadosDelegua.INTEIRO, tipoDeDadosDelegua.NUMERO, tipoDeDadosDelegua.NÚMERO]
+                if (
+                    tiposNumericos.includes(tipoEsquerdo) &&
+                    tiposNumericos.includes(tipoDireito)
+                ) {
                     return Number(valorEsquerdo) + Number(valorDireito);
-                } else {
-                    return this.paraTexto(valorEsquerdo) + this.paraTexto(valorDireito);
-                }
+                } 
+    
+                return this.paraTexto(valorEsquerdo) + this.paraTexto(valorDireito);
 
             case tiposDeSimbolos.DIVISAO:
             case tiposDeSimbolos.DIVISAO_IGUAL:
@@ -1364,8 +1374,7 @@ export class InterpretadorBase implements InterpretadorInterface {
 
         deleguaClasse.dialetoRequerExpansaoPropriedadesEspacoVariaveis =
             this.expandirPropriedadesDeObjetosEmEspacoVariaveis;
-        deleguaClasse.dialetoRequerDeclaracaoPropriedades =
-            this.requerDeclaracaoPropriedades;
+        deleguaClasse.dialetoRequerDeclaracaoPropriedades = this.requerDeclaracaoPropriedades;
 
         // TODO: Recolocar isso se for necessário.
         /* if (superClasse !== null) {
@@ -1378,10 +1387,10 @@ export class InterpretadorBase implements InterpretadorInterface {
 
     /**
      * Executa um acesso a método, normalmente de um objeto de classe.
-     * @param expressao A expressão de acesso.
+     * @param {AcessoMetodoOuPropriedade} expressao A expressão de acesso.
      * @returns O resultado da execução.
      */
-    async visitarExpressaoAcessoMetodo(expressao: any): Promise<any> {
+    async visitarExpressaoAcessoMetodo(expressao: AcessoMetodoOuPropriedade): Promise<any> {
         const variavelObjeto: VariavelInterface = await this.avaliar(expressao.objeto);
         const objeto = variavelObjeto.hasOwnProperty('valor') ? variavelObjeto.valor : variavelObjeto;
 
@@ -1389,24 +1398,32 @@ export class InterpretadorBase implements InterpretadorInterface {
             return objeto.obter(expressao.simbolo) || null;
         }
 
-        // TODO: Possivelmente depreciar esta forma.
-        // Não parece funcionar em momento algum.
+        // Objeto simples do JavaScript, ou dicionário de Delégua. 
         if (objeto.constructor === Object) {
+            const metodoDePrimitivaDicionario: Function = primitivasDicionario[expressao.simbolo.lexema];
+            if (metodoDePrimitivaDicionario) {
+                return new MetodoPrimitiva(objeto, metodoDePrimitivaDicionario);
+            }
+            
             return objeto[expressao.simbolo.lexema] || null;
         }
 
-        // Função tradicional do JavaScript.
-        // Normalmente executa quando uma biblioteca é importada.
+        // Casos em que o objeto possui algum outro tipo que não o de objeto simples.
+        // Normalmente executam quando uma biblioteca é importada, e estamos tentando
+        // obter alguma propriedade ou método desse objeto.
+
+        // Caso 1: Função tradicional do JavaScript.
         if (typeof objeto[expressao.simbolo.lexema] === tipoDeDadosPrimitivos.FUNCAO) {
             return objeto[expressao.simbolo.lexema];
         }
 
-        // Objeto tradicional do JavaScript.
-        // Normalmente executa quando uma biblioteca é importada.
+        // Caso 2: Objeto tradicional do JavaScript.
         if (typeof objeto[expressao.simbolo.lexema] === tipoDeDadosPrimitivos.OBJETO) {
             return objeto[expressao.simbolo.lexema];
         }
 
+        // A partir daqui, presume-se que o objeto é uma das estruturas
+        // de Delégua.
         if (objeto instanceof DeleguaModulo) {
             return objeto.componentes[expressao.simbolo.lexema] || null;
         }
@@ -1416,16 +1433,13 @@ export class InterpretadorBase implements InterpretadorInterface {
             tipoObjeto = inferirTipoVariavel(variavelObjeto as any);
         }
 
+        // Como internamente um dicionário de Delégua é simplesmente um objeto de 
+        // JavaScript, as primitivas de dicionário, especificamente, são tratadas
+        // mais acima.
         switch (tipoObjeto) {
-            case tipoDeDadosDelegua.OBJETO:
-            case tipoDeDadosDelegua.DICIONARIO:
-                const metodoDeDicionario: Function = primitivasDicionario[expressao.simbolo.lexema];
-                if (metodoDeDicionario) {
-                    return new MetodoPrimitiva(objeto, metodoDeDicionario);
-                }
-                break;
             case tipoDeDadosDelegua.INTEIRO:
             case tipoDeDadosDelegua.NUMERO:
+            case tipoDeDadosDelegua.NÚMERO:
                 const metodoDePrimitivaNumero: Function = primitivasNumero[expressao.simbolo.lexema];
                 if (metodoDePrimitivaNumero) {
                     return new MetodoPrimitiva(objeto, metodoDePrimitivaNumero);
@@ -1437,7 +1451,7 @@ export class InterpretadorBase implements InterpretadorInterface {
                     return new MetodoPrimitiva(objeto, metodoDePrimitivaTexto);
                 }
                 break;
-            case 'vetor':
+            case tipoDeDadosDelegua.VETOR:
                 const metodoDePrimitivaVetor: Function = primitivasVetor[expressao.simbolo.lexema];
                 if (metodoDePrimitivaVetor) {
                     return new MetodoPrimitiva(objeto, metodoDePrimitivaVetor);
@@ -1447,7 +1461,7 @@ export class InterpretadorBase implements InterpretadorInterface {
 
         return Promise.reject(
             new ErroEmTempoDeExecucao(
-                expressao.nome,
+                expressao.simbolo,
                 `Método para objeto ou primitiva não encontrado: ${expressao.simbolo.lexema}.`,
                 expressao.linha
             )
@@ -1458,8 +1472,7 @@ export class InterpretadorBase implements InterpretadorInterface {
         return this.procurarVariavel(expressao.palavraChave);
     }
 
-
-    visitarDeclaracaoAleatorio(declaracao: Aleatorio): Promise<any>{
+    visitarDeclaracaoAleatorio(declaracao: Aleatorio): Promise<any> {
         return Promise.resolve();
     }
 
@@ -1564,7 +1577,6 @@ export class InterpretadorBase implements InterpretadorInterface {
      *                         pelo modo LAIR.
      */
     async executar(declaracao: Declaracao, mostrarResultado = false): Promise<any> {
-
         const resultado: any = await declaracao.aceitar(this);
         /* console.log("Resultado aceitar: " + resultado, this); */
         if (mostrarResultado) {
diff --git a/fontes/tipos-de-dados/delegua.ts b/fontes/tipos-de-dados/delegua.ts
index 0057f460..ed9942cb 100644
--- a/fontes/tipos-de-dados/delegua.ts
+++ b/fontes/tipos-de-dados/delegua.ts
@@ -1,9 +1,13 @@
 export default {
-    DICIONARIO: 'dicionário',
+    DICIONARIO: 'dicionario',
+    DICIONÁRIO: 'dicionário',
     INTEIRO: 'inteiro',
-    LOGICO: 'lógico',
-    NUMERO: 'número',
+    LOGICO: 'logico',
+    LÓGICO: 'lógico',
+    NUMERO: 'numero',
+    NÚMERO: 'número',
     NULO: 'nulo',
     OBJETO: 'objeto',
     TEXTO: 'texto',
+    VETOR: 'vetor'
 };
\ No newline at end of file


Test patch:
diff --git a/testes/interpretador.test.ts b/testes/interpretador.test.ts
index eff4ded8..9d38a6ee 100644
--- a/testes/interpretador.test.ts
+++ b/testes/interpretador.test.ts
@@ -1325,23 +1325,49 @@ describe('Interpretador', () => {
             });
 
             describe('Métodos de primitivas com dependência no Interpretador', () => {
-                it('ordenar() de vetor com parâmetro função', async () => {
-                    const retornoLexador = lexador.mapear([
-                        "var numeros = [4, 2, 12, 8];",
-                        "numeros.ordenar(funcao(a, b) {",
-                        "    retorna b - a;",
-                        "});",
-                        "escreva(numeros);"
-                    ], -1);
-                    const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
-
-                    interpretador.funcaoDeRetorno = (saida: string) => {
-                        expect(saida).toEqual('12,8,4,2');
-                    }
-
-                    const retornoInterpretador = await interpretador.interpretar(retornoAvaliadorSintatico.declaracoes);
-
-                    expect(retornoInterpretador.erros).toHaveLength(0);
+                describe('Dicionários', () => {
+                    it('chaves() e valores()', async () => {
+                        const retornoLexador = lexador.mapear([
+                            `var meuDicionario = {"a": 1, "b": 2, "c": 3}`,
+                            `escreva(meuDicionario.chaves())`,
+                            `escreva(meuDicionario.valores())`
+                        ], -1);
+
+                        const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+                        const saidas: string[] = [];
+    
+                        interpretador.funcaoDeRetorno = (saida: string) => {
+                            saidas.push(saida);
+                        }
+    
+                        const retornoInterpretador = await interpretador.interpretar(retornoAvaliadorSintatico.declaracoes);
+    
+                        expect(retornoInterpretador.erros).toHaveLength(0);
+                        expect(saidas).toHaveLength(2);
+                        expect(saidas[0]).toEqual('a,b,c');
+                        expect(saidas[1]).toEqual('1,2,3');
+                    });
+                });
+
+                describe('Vetores', () => {
+                    it('ordenar() de vetor com parâmetro função', async () => {
+                        const retornoLexador = lexador.mapear([
+                            "var numeros = [4, 2, 12, 8];",
+                            "numeros.ordenar(funcao(a, b) {",
+                            "    retorna b - a;",
+                            "});",
+                            "escreva(numeros);"
+                        ], -1);
+                        const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+    
+                        interpretador.funcaoDeRetorno = (saida: string) => {
+                            expect(saida).toEqual('12,8,4,2');
+                        }
+    
+                        const retornoInterpretador = await interpretador.interpretar(retornoAvaliadorSintatico.declaracoes);
+    
+                        expect(retornoInterpretador.erros).toHaveLength(0);
+                    });
                 });
             })