Repository: DesignLiquido/delegua
Pull request #572 (2024-01-15 22:16:53)

Analisador Semântico Mapler


Patch:
diff --git a/fontes/analisador-semantico/dialetos/analisador-semantico-mapler.ts b/fontes/analisador-semantico/dialetos/analisador-semantico-mapler.ts
new file mode 100644
index 00000000..fcc07304
--- /dev/null
+++ b/fontes/analisador-semantico/dialetos/analisador-semantico-mapler.ts
@@ -0,0 +1,325 @@
+import { Atribuir, Binario, Chamada, Construto, ExpressaoRegular, FimPara, FormatacaoEscrita, FuncaoConstruto, Literal, Logico, Super, TipoDe, Variavel, Vetor } from '../../construtos';
+import {
+    Bloco,
+    Classe,
+    Const,
+    ConstMultiplo,
+    Continua,
+    Declaracao,
+    Enquanto,
+    Escolha,
+    Escreva,
+    EscrevaMesmaLinha,
+    Expressao,
+    Falhar,
+    Fazer,
+    FuncaoDeclaracao,
+    Importar,
+    Leia,
+    LeiaMultiplo,
+    Para,
+    ParaCada,
+    Retorna,
+    Se,
+    Sustar,
+    Tente,
+    Var,
+    VarMultiplo,
+} from '../../declaracoes';
+import { SimboloInterface } from '../../interfaces';
+import { AnalisadorSemanticoInterface } from '../../interfaces/analisador-semantico-interface';
+import { DiagnosticoAnalisadorSemantico, DiagnosticoSeveridade } from '../../interfaces/erros';
+import { RetornoAnalisadorSemantico } from '../../interfaces/retornos/retorno-analisador-semantico';
+import { TiposDadosInterface } from '../../interfaces/tipos-dados-interface';
+import { ContinuarQuebra, RetornoQuebra, SustarQuebra } from '../../quebras';
+import { PilhaVariaveis } from './../pilha-variaveis';
+
+interface VariavelHipoteticaInterface {
+    tipo: TiposDadosInterface;
+    subtipo?: 'texto' | 'número' | 'inteiro' | 'longo' | 'lógico';
+    imutavel: boolean;
+    valor?: any
+}
+
+interface FuncaoHipoteticaInterface {
+    valor: any
+}
+
+export class AnalisadorSemanticoMapler implements AnalisadorSemanticoInterface {
+    pilhaVariaveis: PilhaVariaveis;
+    variaveis: { [nomeVariavel: string]: VariavelHipoteticaInterface };
+    funcoes: { [nomeFuncao: string]: FuncaoHipoteticaInterface }
+    atual: number;
+    diagnosticos: DiagnosticoAnalisadorSemantico[];
+
+    constructor() {
+        this.pilhaVariaveis = new PilhaVariaveis();
+        this.variaveis = {};
+        this.funcoes = {};
+        this.atual = 0;
+        this.diagnosticos = [];
+    }
+    visitarExpressaoExpressaoRegular(expressao: ExpressaoRegular): Promise<any> {
+        return Promise.resolve();
+    }
+
+    erro(simbolo: SimboloInterface, mensagem: string): void {
+        this.diagnosticos.push({
+            simbolo: simbolo,
+            mensagem: mensagem,
+            hashArquivo: simbolo.hashArquivo,
+            linha: simbolo.linha,
+            severidade: DiagnosticoSeveridade.ERRO
+        });
+    }
+
+    aviso(simbolo: SimboloInterface, mensagem: string): void {
+        this.diagnosticos.push({
+            simbolo: simbolo,
+            mensagem: mensagem,
+            hashArquivo: simbolo.hashArquivo,
+            linha: simbolo.linha,
+            severidade: DiagnosticoSeveridade.AVISO
+        });
+    }
+
+    visitarExpressaoTipoDe(expressao: TipoDe): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoFalhar(expressao: Falhar): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoLiteral(expressao: Literal): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoAgrupamento(expressao: any): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoUnaria(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoBinaria(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoDeChamada(expressao: Chamada) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoDeAtribuicao(expressao: Atribuir) {
+        let valor = this.variaveis[expressao.simbolo.lexema];
+        if (!valor) {
+            this.erro(
+                expressao.simbolo,
+                `Variável ${expressao.simbolo.lexema} ainda não foi declarada até este ponto.`
+            );
+            return Promise.resolve();
+        }
+
+        if (valor.tipo) {
+            if (expressao.valor instanceof Literal && valor.tipo.includes('[]')) {
+                this.erro(expressao.simbolo, `Atribuição inválida, esperado tipo '${valor.tipo}' na atribuição.`);
+                return Promise.resolve();
+            }
+            if (expressao.valor instanceof Vetor && !valor.tipo.includes('[]')) {
+                this.erro(expressao.simbolo, `Atribuição inválida, esperado tipo '${valor.tipo}' na atribuição.`);
+                return Promise.resolve();
+            }
+            if (expressao.valor instanceof Literal) {
+                let valorLiteral = typeof (expressao.valor as Literal).valor;
+                if (!['qualquer'].includes(valor.tipo)) {
+                    if (valorLiteral === 'string') {
+                        if (valor.tipo != 'texto') {
+                            this.erro(expressao.simbolo, `Esperado tipo '${valor.tipo}' na atribuição.`);
+                            return Promise.resolve();
+                        }
+                    }
+                    if (valorLiteral === 'number') {
+                        if (!['inteiro', 'real'].includes(valor.tipo)) {
+                            this.erro(expressao.simbolo, `Esperado tipo '${valor.tipo}' na atribuição.`);
+                            return Promise.resolve();
+                        }
+                    }
+                }
+            }
+        }
+
+        if (this.variaveis[expressao.simbolo.lexema]) {
+            this.variaveis[expressao.simbolo.lexema].valor = expressao.valor;
+        }
+    }
+
+    visitarExpressaoDeVariavel(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoDeExpressao(declaracao: Expressao) {
+        return declaracao.expressao.aceitar(this);
+    }
+
+    visitarExpressaoLeia(expressao: Leia) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoLeiaMultiplo(expressao: LeiaMultiplo) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoLogica(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoPara(declaracao: Para): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoParaCada(declaracao: ParaCada): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoSe(declaracao: Se) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoFimPara(declaracao: FimPara) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoFazer(declaracao: Fazer) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoFormatacaoEscrita(declaracao: FormatacaoEscrita) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoEscolha(declaracao: Escolha) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoTente(declaracao: Tente) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoEnquanto(declaracao: Enquanto) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoImportar(declaracao: Importar) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoEscreva(declaracao: Escreva) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoEscrevaMesmaLinha(declaracao: EscrevaMesmaLinha) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoBloco(declaracao: Bloco): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoConst(declaracao: Const): Promise<any> {
+        return Promise.resolve();
+    }
+
+    virificarTipoDeclaracaoConst(declaracao: Const): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoConstMultiplo(declaracao: ConstMultiplo): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoVar(declaracao: Var): Promise<any> {
+        this.variaveis[declaracao.simbolo.lexema] = {
+            imutavel: false,
+            tipo: declaracao.tipo,
+            valor: declaracao.inicializador !== null ? declaracao.inicializador.valor !== undefined ? declaracao.inicializador.valor : declaracao.inicializador : undefined
+        };
+
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoVarMultiplo(declaracao: VarMultiplo): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoContinua(declaracao?: Continua): ContinuarQuebra {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoSustar(declaracao?: Sustar): SustarQuebra {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoRetornar(declaracao: Retorna): Promise<RetornoQuebra> {
+        return Promise.resolve(null);
+    }
+
+    visitarExpressaoDeleguaFuncao(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoAtribuicaoPorIndice(expressao: any): Promise<any> {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoAcessoIndiceVariavel(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoDefinirValor(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoDefinicaoFuncao(declaracao: FuncaoDeclaracao) {
+        return Promise.resolve();
+    }
+
+    visitarDeclaracaoClasse(declaracao: Classe) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoAcessoMetodo(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoIsto(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoDicionario(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoVetor(expressao: any) {
+        return Promise.resolve();
+    }
+
+    visitarExpressaoSuper(expressao: Super) {
+        return Promise.resolve();
+    }
+
+    analisar(declaracoes: Declaracao[]): RetornoAnalisadorSemantico {
+        this.variaveis = {};
+        this.atual = 0;
+        this.diagnosticos = [];
+
+        while (this.atual < declaracoes.length) {
+            declaracoes[this.atual].aceitar(this);
+            this.atual++;
+        }
+
+        return {
+            diagnosticos: this.diagnosticos,
+        } as RetornoAnalisadorSemantico;
+    }
+}
diff --git a/fontes/analisador-semantico/dialetos/index.ts b/fontes/analisador-semantico/dialetos/index.ts
index 55632e0b..6df675a9 100644
--- a/fontes/analisador-semantico/dialetos/index.ts
+++ b/fontes/analisador-semantico/dialetos/index.ts
@@ -1,1 +1,2 @@
 export * from './analisador-semantico-birl';
+export * from './analisador-semantico-mapler';
diff --git a/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-mapler.ts b/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-mapler.ts
index 4974fdc3..2c4509c0 100644
--- a/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-mapler.ts
+++ b/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-mapler.ts
@@ -133,7 +133,8 @@ export class AvaliadorSintaticoMapler extends AvaliadorSintaticoBase {
                                 inicializacoes.push(
                                     new Var(
                                         identificador,
-                                        new Literal(this.hashArquivo, Number(dadosVariaveis.simbolo.linha), '')
+                                        new Literal(this.hashArquivo, Number(dadosVariaveis.simbolo.linha), ''),
+                                        'texto'
                                     )
                                 );
                                 break;
@@ -142,7 +143,8 @@ export class AvaliadorSintaticoMapler extends AvaliadorSintaticoBase {
                                 inicializacoes.push(
                                     new Var(
                                         identificador,
-                                        new Literal(this.hashArquivo, Number(dadosVariaveis.simbolo.linha), 0)
+                                        new Literal(this.hashArquivo, Number(dadosVariaveis.simbolo.linha), 0),
+                                        'numero'
                                     )
                                 );
                                 break;
@@ -150,7 +152,8 @@ export class AvaliadorSintaticoMapler extends AvaliadorSintaticoBase {
                                 inicializacoes.push(
                                     new Var(
                                         identificador,
-                                        new Literal(this.hashArquivo, Number(dadosVariaveis.simbolo.linha), false)
+                                        new Literal(this.hashArquivo, Number(dadosVariaveis.simbolo.linha), false),
+                                        'lógico'
                                     )
                                 );
                                 break;
@@ -190,7 +193,8 @@ export class AvaliadorSintaticoMapler extends AvaliadorSintaticoBase {
                                             this.hashArquivo,
                                             Number(dadosVariaveis.simbolo.linha),
                                             this.criarVetorNDimensional(dimensoes)
-                                        )
+                                        ),
+                                        'vetor'
                                     )
                                 );
                                 break;
diff --git a/fontes/lexador/dialetos/lexador-mapler.ts b/fontes/lexador/dialetos/lexador-mapler.ts
index f11de2da..fda8fab0 100644
--- a/fontes/lexador/dialetos/lexador-mapler.ts
+++ b/fontes/lexador/dialetos/lexador-mapler.ts
@@ -47,7 +47,7 @@ export class LexadorMapler extends LexadorBaseLinhaUnica {
             this.avancar();
         }
 
-        const codigo = this.codigo.substring(this.inicioSimbolo, this.atual).toLowerCase();
+        const codigo = this.codigo.substring(this.inicioSimbolo, this.atual);
         if (codigo in palavrasReservadas) {
             this.adicionarSimbolo(palavrasReservadas[codigo], codigo);
         } else {


Test patch:
diff --git a/testes/mapler/analisador-semantico.test.ts b/testes/mapler/analisador-semantico.test.ts
new file mode 100644
index 00000000..a2e9e599
--- /dev/null
+++ b/testes/mapler/analisador-semantico.test.ts
@@ -0,0 +1,50 @@
+import { LexadorMapler } from '../../fontes/lexador/dialetos';
+import { AvaliadorSintaticoMapler } from '../../fontes/avaliador-sintatico/dialetos';
+import { AnalisadorSemanticoMapler } from '../../fontes/analisador-semantico/dialetos';
+import { DiagnosticoSeveridade } from '../../fontes/interfaces/erros';
+
+describe('Analisador semântico (Mapler)', () => {
+    let lexadorMapler: LexadorMapler;
+    let avaliadorSintaticoMapler: AvaliadorSintaticoMapler;
+    let analisadorSemanticoMapler: AnalisadorSemanticoMapler;
+
+    describe('analisar()', () => {
+        beforeEach(() => {
+            lexadorMapler = new LexadorMapler();
+            avaliadorSintaticoMapler = new AvaliadorSintaticoMapler();
+            analisadorSemanticoMapler = new AnalisadorSemanticoMapler();
+        });
+
+        describe('Cenários de falha', () => {
+            it('Atribuição inválida de número para texto', () => {
+                const retornoLexador = lexadorMapler.mapear([
+                    'variaveis',
+                    'idade: real;',
+                    'inicio',
+                    'idade <- "abc";',
+                    'fim'
+                ], -1);
+                const retornoAvaliadorSintatico = avaliadorSintaticoMapler.analisar(retornoLexador, -1);
+                const retornoAnalisadorSemantico = analisadorSemanticoMapler.analisar(retornoAvaliadorSintatico.declaracoes);
+
+                expect(retornoAnalisadorSemantico).toBeTruthy();
+                expect(retornoAnalisadorSemantico.diagnosticos).toHaveLength(1);
+            });
+
+            it('Variável indefinida, não declarada', () => {
+                const retornoLexador = lexadorMapler.mapear([
+                    'variaveis',
+                    'nome: cadeia;',
+                    'inicio',
+                    'NOME <- "abc";',
+                    'fim'
+                ], -1);
+                const retornoAvaliadorSintatico = avaliadorSintaticoMapler.analisar(retornoLexador, -1);
+                const retornoAnalisadorSemantico = analisadorSemanticoMapler.analisar(retornoAvaliadorSintatico.declaracoes);
+
+                expect(retornoAnalisadorSemantico).toBeTruthy();
+                expect(retornoAnalisadorSemantico.diagnosticos).toHaveLength(1);
+            });
+        })
+    })
+})
\ No newline at end of file