Repository: DesignLiquido/delegua (ts)

Issue:
Listas aninhadas utilizadas como chaves de dicionários não são imprimidas como listas.
```js
escreva({
    [1, 2, [1, 2, 3]]: 'valor',
})
```


![Image](https://github.com/user-attachments/assets/4f6cc5a1-878f-438f-b2ae-02d2c2b1e84f)

Pull request description:
Restrição de tipos aceitos como chaves de dicionários.
Apenas os seguintes tipos elementares serão aceitos:

    Número
    Texto
    Lógico
    Tupla

Os tipos abaixo não serão aceitos:

    Vetor
    Nulo
    Dicionário

Resolve #711. 


Patch:
diff --git a/fontes/avaliador-sintatico/avaliador-sintatico.ts b/fontes/avaliador-sintatico/avaliador-sintatico.ts
index 0bfee6c3..e39243c7 100644
--- a/fontes/avaliador-sintatico/avaliador-sintatico.ts
+++ b/fontes/avaliador-sintatico/avaliador-sintatico.ts
@@ -1,8 +1,5 @@
 import hrtime from 'browser-process-hrtime';
 
-import tipoDeDadosDelegua from '../tipos-de-dados/delegua';
-import tiposDeSimbolos from '../tipos-de-simbolos/delegua';
-
 import {
     AcessoIndiceVariavel,
     AcessoMetodo,
@@ -70,6 +67,9 @@ import { TipoInferencia } from '../inferenciador';
 import { PilhaEscopos } from './pilha-escopos';
 import { InformacaoEscopo } from './informacao-escopo';
 
+import tipoDeDadosDelegua from '../tipos-de-dados/delegua';
+import tiposDeSimbolos from '../tipos-de-simbolos/delegua';
+
 import primitivasDicionario from '../bibliotecas/primitivas-dicionario';
 import primitivasNumero from '../bibliotecas/primitivas-numero';
 import primitivasTexto from '../bibliotecas/primitivas-texto';
@@ -152,33 +152,83 @@ export class AvaliadorSintatico
         return tipoElementarResolvido as TipoDadosElementar;
     }
 
-    override primario(): Construto {
-        const simboloAtual = this.simbolos[this.atual];
-        let valores = [];
-        switch (simboloAtual.tipo) {
-            case tiposDeSimbolos.CHAVE_ESQUERDA:
-                this.avancarEDevolverAnterior();
-                const chaves = [];
-                valores = [];
+    protected obterChaveDicionario(): Construto {
+        switch (this.simbolos[this.atual].tipo) {
+            case tiposDeSimbolos.NUMERO:
+            case tiposDeSimbolos.TEXTO:
+            case tiposDeSimbolos.FALSO:
+            case tiposDeSimbolos.VERDADEIRO:
+                return this.primario();
+            case tiposDeSimbolos.IDENTIFICADOR:
+                const simboloIdentificador: SimboloInterface = this.avancarEDevolverAnterior();
+                let tipoOperando: string;
+                if (simboloIdentificador.lexema in this.tiposDefinidosEmCodigo) {
+                    tipoOperando = simboloIdentificador.lexema;
+                } else {
+                    tipoOperando = this.pilhaEscopos.obterTipoVariavelPorNome(simboloIdentificador.lexema);
+                }
 
-                if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.CHAVE_DIREITA)) {
-                    return new Dicionario(this.hashArquivo, Number(simboloAtual.linha), [], []);
+                if (!['numero', 'número', 'texto', 'lógico'].includes(tipoOperando)) {
+                    throw this.erro(simboloIdentificador, `Tipo ${tipoOperando} de identificador ${simboloIdentificador.lexema} não é válido como chave de dicionário.`);
                 }
 
-                while (!this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.CHAVE_DIREITA)) {
-                    const chave = this.atribuir();
-                    this.consumir(tiposDeSimbolos.DOIS_PONTOS, "Esperado ':' entre chave e valor.");
-                    const valor = this.atribuir();
+                return new Variavel(this.hashArquivo, simboloIdentificador, tipoOperando);
+            case tiposDeSimbolos.COLCHETE_ESQUERDO:
+                this.avancarEDevolverAnterior();
+                if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.PARENTESE_ESQUERDO)) {
+                    return this.construtoTupla();
+                }
 
-                    chaves.push(chave);
-                    valores.push(valor);
+                throw this.erro(this.simbolos[this.atual], `Esperado parêntese esquerdo após colchete esquerdo para definição de chave de dicionário. Atual: ${this.simbolos[this.atual].tipo}.`);
+            default:
+                throw this.erro(this.simbolos[this.atual], `Símbolo ${this.simbolos[this.atual].tipo} inesperado ou inválido como chave de dicionário.`);
+        }
+    }
 
-                    if (this.simbolos[this.atual].tipo !== tiposDeSimbolos.CHAVE_DIREITA) {
-                        this.consumir(tiposDeSimbolos.VIRGULA, 'Esperado vírgula antes da próxima expressão.');
-                    }
-                }
+    protected construtoDicionario(simboloChaveEsquerda: SimboloInterface): Dicionario {
+        this.avancarEDevolverAnterior();
+        const chaves = [];
+        const valores = [];
+
+        if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.CHAVE_DIREITA)) {
+            return new Dicionario(this.hashArquivo, Number(simboloChaveEsquerda.linha), [], []);
+        }
+
+        while (!this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.CHAVE_DIREITA)) {
+            const chave = this.obterChaveDicionario();
+            this.consumir(tiposDeSimbolos.DOIS_PONTOS, "Esperado ':' entre chave e valor.");
+            const valor = this.atribuir();
+
+            chaves.push(chave);
+            valores.push(valor);
+
+            if (this.simbolos[this.atual].tipo !== tiposDeSimbolos.CHAVE_DIREITA) {
+                this.consumir(tiposDeSimbolos.VIRGULA, 'Esperado vírgula antes da próxima expressão.');
+            }
+        }
+
+        return new Dicionario(this.hashArquivo, Number(simboloChaveEsquerda.linha), chaves, valores);
+    }
+
+    protected construtoTupla(): Tupla {
+        const expressao = this.expressao();
+        const argumentos = [expressao];
+        while (this.simbolos[this.atual].tipo === tiposDeSimbolos.VIRGULA) {
+            this.avancarEDevolverAnterior();
+            argumentos.push(this.expressao());
+        }
 
-                return new Dicionario(this.hashArquivo, Number(simboloAtual.linha), chaves, valores);
+        this.consumir(tiposDeSimbolos.PARENTESE_DIREITO, "Esperado ')' após a expressão.");
+        this.consumir(tiposDeSimbolos.COLCHETE_DIREITO, "Esperado ']' após a expressão.");
+        return new SeletorTuplas(...argumentos) as Tupla;
+    }
+
+    override primario(): Construto {
+        const simboloAtual = this.simbolos[this.atual];
+        let valores = [];
+        switch (simboloAtual.tipo) {
+            case tiposDeSimbolos.CHAVE_ESQUERDA:
+                return this.construtoDicionario(simboloAtual);
 
             case tiposDeSimbolos.COLCHETE_ESQUERDO:
                 this.avancarEDevolverAnterior();
@@ -189,21 +239,11 @@ export class AvaliadorSintatico
                 }
 
                 while (!this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.COLCHETE_DIREITO)) {
-                    let valor: any = null;
                     if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.PARENTESE_ESQUERDO)) {
-                        const expressao = this.expressao();
-                        const argumentos = [expressao];
-                        while (this.simbolos[this.atual].tipo === tiposDeSimbolos.VIRGULA) {
-                            this.avancarEDevolverAnterior();
-                            argumentos.push(this.expressao());
-                        }
-
-                        this.consumir(tiposDeSimbolos.PARENTESE_DIREITO, "Esperado ')' após a expressão.");
-                        this.consumir(tiposDeSimbolos.COLCHETE_DIREITO, "Esperado ']' após a expressão.");
-                        return new SeletorTuplas(...argumentos) as Tupla;
+                        return this.construtoTupla();
                     }
 
-                    valor = this.atribuir();
+                    const valor = this.atribuir();
                     valores.push(valor);
                     if (this.simbolos[this.atual].tipo !== tiposDeSimbolos.COLCHETE_DIREITO) {
                         this.consumir(tiposDeSimbolos.VIRGULA, 'Esperado vírgula antes da próxima expressão.');


Test patch:
diff --git a/testes/avaliador-sintatico/avaliador-sintatico.test.ts b/testes/avaliador-sintatico/avaliador-sintatico.test.ts
index 89183cfa..ed3cc2b6 100644
--- a/testes/avaliador-sintatico/avaliador-sintatico.test.ts
+++ b/testes/avaliador-sintatico/avaliador-sintatico.test.ts
@@ -807,6 +807,24 @@ describe('Avaliador sintático', () => {
                 );
             });
 
+            describe('Dicionários', () => {
+                it('Tipo de chave de dicionário inválida', async () => {
+                    const retornoLexador = lexador.mapear(
+                        [
+                            'var dicionarioInvalido = {',
+                            '    [1, 2, [1, 2, 3]]: "valor",',
+                            '}'
+                        ],
+                        -1
+                    );
+                    const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);
+
+                    expect(retornoAvaliadorSintatico.erros.length).toBeGreaterThan(0);
+                    const erro = retornoAvaliadorSintatico.erros[0];
+                    expect(erro.message).toBe('Esperado parêntese esquerdo após colchete esquerdo para definição de chave de dicionário. Atual: NUMERO.');
+                });
+            });
+
             describe('Funções', () => {
                 it('Função retorna vazio mas tem retorno de valores', async () => {
                     const retornoLexador = lexador.mapear(
diff --git a/testes/interpretador/interpretador.test.ts b/testes/interpretador/interpretador.test.ts
index 57f88c13..fef2ee05 100644
--- a/testes/interpretador/interpretador.test.ts
+++ b/testes/interpretador/interpretador.test.ts
@@ -18,7 +18,11 @@ describe('Interpretador', () => {
             describe('Acesso a operações matemáticas em posições de array', () => {
                 it('Espera-se que atribuição com acumulador seja bem sucedida', async () => {
                     const retornoLexador = lexador.mapear(
-                        ['var pilha = [1, 2, 3, 4]', 'pilha[0] += 8', 'escreva(pilha[0])'],
+                        [
+                            'var pilha = [1, 2, 3, 4]', 
+                            'pilha[0] += 8', 
+                            'escreva(pilha[0])'
+                        ],
                         -1
                     );
                     const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1);