diff --git a/fontes/analisador-semantico/analisador-semantico.ts b/fontes/analisador-semantico/analisador-semantico.ts index c56bd34c..8f393ff1 100644 --- a/fontes/analisador-semantico/analisador-semantico.ts +++ b/fontes/analisador-semantico/analisador-semantico.ts @@ -61,6 +61,49 @@ export class AnalisadorSemantico implements AnalisadorSemanticoInterface { }); } + verificarTipoAtribuido(declaracao: Var | Const) { + if (declaracao.tipo) { + if (['vetor', 'qualquer[]', 'inteiro[]', 'texto[]'].includes(declaracao.tipo)) { + if (declaracao.inicializador instanceof Vetor) { + const vetor = declaracao.inicializador as Vetor; + if (declaracao.tipo === 'inteiro[]') { + const v = vetor.valores.find(v => typeof v?.valor !== 'number') + if(v) { + this.erro( + declaracao.simbolo, + `Atribuição inválida para '${declaracao.simbolo.lexema}', é esperado um vetor de 'inteiros' ou 'real'.` + ); + } + } + if (declaracao.tipo === 'texto[]') { + const v = vetor.valores.find(v => typeof v?.valor !== 'string') + if(v) { + this.erro( + declaracao.simbolo, + `Atribuição inválida para '${declaracao.simbolo.lexema}', é esperado um vetor de 'texto'.` + ); + } + } + } else { + this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}', é esperado um vetor de elementos.`); + } + } + if (declaracao.inicializador instanceof Literal) { + const literal = declaracao.inicializador as Literal; + if (declaracao.tipo === 'texto') { + if (typeof literal.valor !== 'string') { + this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}', é esperado um 'texto'.`); + } + } + if (['inteiro', 'real'].includes(declaracao.tipo)) { + if (typeof literal.valor !== 'number') { + this.erro(declaracao.simbolo, `Atribuição inválida '${declaracao.simbolo.lexema}', é esperado um 'número'.`); + } + } + } + } + } + visitarExpressaoTipoDe(expressao: TipoDe): Promise { return Promise.resolve(); } @@ -223,6 +266,9 @@ export class AnalisadorSemantico implements AnalisadorSemanticoInterface { } visitarDeclaracaoConst(declaracao: Const): Promise { + + this.verificarTipoAtribuido(declaracao); + if (this.variaveis.hasOwnProperty(declaracao.simbolo.lexema)) { this.erros.push({ simbolo: declaracao.simbolo, @@ -245,6 +291,9 @@ export class AnalisadorSemantico implements AnalisadorSemanticoInterface { } visitarDeclaracaoVar(declaracao: Var): Promise { + + this.verificarTipoAtribuido(declaracao); + this.variaveis[declaracao.simbolo.lexema] = { imutavel: false, tipo: declaracao.tipo, @@ -286,6 +335,47 @@ export class AnalisadorSemantico implements AnalisadorSemanticoInterface { } visitarDeclaracaoDefinicaoFuncao(declaracao: FuncaoDeclaracao) { + for (let parametro of declaracao.funcao.parametros) { + if(parametro.hasOwnProperty('tipoDado') && !parametro.tipoDado.tipo) { + this.erro(declaracao.simbolo, `O tipo '${parametro.tipoDado.nome}' não é válido.`); + } + } + + let tipoRetornoFuncao = declaracao.funcao.tipoRetorno; + if (tipoRetornoFuncao) { + let funcaoContemRetorno = declaracao.funcao.corpo.find((c) => c instanceof Retorna) as Retorna; + if (funcaoContemRetorno) { + if (tipoRetornoFuncao === 'vazio') { + this.erro(declaracao.simbolo, `A função não pode ter nenhum tipo de retorno.`); + return Promise.resolve(); + } + + const tipoValor = typeof funcaoContemRetorno.valor.valor; + if (!['qualquer'].includes(tipoRetornoFuncao)) { + if (tipoValor === 'string') { + if (tipoRetornoFuncao != 'texto') { + this.erro( + declaracao.simbolo, + `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.` + ); + } + } + if (tipoValor === 'number') { + if (!['inteiro', 'real'].includes(tipoRetornoFuncao)) { + this.erro( + declaracao.simbolo, + `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.` + ); + } + } + } + } else { + if (tipoRetornoFuncao !== 'vazio') { + this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`); + } + } + } + return Promise.resolve(); } diff --git a/fontes/avaliador-sintatico/avaliador-sintatico.ts b/fontes/avaliador-sintatico/avaliador-sintatico.ts index 29566e30..5804ed4f 100644 --- a/fontes/avaliador-sintatico/avaliador-sintatico.ts +++ b/fontes/avaliador-sintatico/avaliador-sintatico.ts @@ -1,7 +1,11 @@ import tiposDeSimbolos from '../tipos-de-simbolos/delegua'; import hrtime from 'browser-process-hrtime'; -import { AvaliadorSintaticoInterface, ParametroInterface, SimboloInterface } from '../interfaces'; +import { + AvaliadorSintaticoInterface, + ParametroInterface, + SimboloInterface, +} from '../interfaces'; import { AcessoMetodo as AcessoMetodo, Agrupamento, @@ -100,7 +104,7 @@ export class AvaliadorSintatico implements AvaliadorSintaticoInterface c instanceof Retorna) as Retorna; - if (funcaoContemRetorno) { - if (tipoRetorno === 'vazio') { - throw this.erro(this.simboloAtual(), `A função não pode ter nenhum tipo de retorno.`); - } - - const tipoValor = typeof funcaoContemRetorno.valor.valor; - if (!['qualquer'].includes(tipoRetorno)) { - if (tipoValor === 'string') { - if (tipoRetorno != 'texto') { - this.erro( - this.simboloAtual(), - `Esperado retorno do tipo '${tipoRetorno}' dentro da função.` - ); - } - } - if (tipoValor === 'number') { - if (!['inteiro', 'real'].includes(tipoRetorno)) { - this.erro( - this.simboloAtual(), - `Esperado retorno do tipo '${tipoRetorno}' dentro da função.` - ); - } - } - } - } else { - if (tipoRetorno !== 'vazio') { - this.erro(this.simboloAtual(), `Esperado retorno do tipo '${tipoRetorno}' dentro da função.`); - } - } - } - return new FuncaoConstruto(this.hashArquivo, Number(parenteseEsquerdo.linha), parametros, corpo, tipoRetorno); } diff --git a/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-birl.ts b/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-birl.ts index 1ec2b137..13a19476 100644 --- a/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-birl.ts +++ b/fontes/avaliador-sintatico/dialetos/avaliador-sintatico-birl.ts @@ -712,8 +712,11 @@ export class AvaliadorSintaticoBirl extends AvaliadorSintaticoBase { }; const tipo = this.resolveTipo(this.simbolos[this.atual].tipo); - - parametro.tipo = this.resolveSimboloInterfaceParaTiposDadosInterface(tipo); + const resolucaoTipo = this.resolveSimboloInterfaceParaTiposDadosInterface(tipo); + parametro.tipoDado = { + nome: this.simbolos[this.atual].lexema, + tipo: resolucaoTipo + } this.avancarEDevolverAnterior(); parametro.nome = this.simbolos[this.atual]; diff --git a/fontes/avaliador-sintatico/dialetos/potigol/avaliador-sintatico-potigol.ts b/fontes/avaliador-sintatico/dialetos/potigol/avaliador-sintatico-potigol.ts index ab4fc705..d9f378e9 100644 --- a/fontes/avaliador-sintatico/dialetos/potigol/avaliador-sintatico-potigol.ts +++ b/fontes/avaliador-sintatico/dialetos/potigol/avaliador-sintatico-potigol.ts @@ -273,7 +273,11 @@ export class AvaliadorSintaticoPotigol extends AvaliadorSintaticoBase { ); const tipoParametro = simbolos[indice]; - parametro.tipo = this.tiposPotigolParaDelegua[tipoParametro.lexema]; + const resolucaoTipo = this.tiposPotigolParaDelegua[tipoParametro.lexema]; + parametro.tipoDado = { + nome: simbolos[indice - 2].lexema, + tipo: resolucaoTipo + } tipagemDefinida = true; } diff --git a/fontes/interfaces/parametro-interface.ts b/fontes/interfaces/parametro-interface.ts index 0f2a2b16..1754864e 100644 --- a/fontes/interfaces/parametro-interface.ts +++ b/fontes/interfaces/parametro-interface.ts @@ -1,10 +1,14 @@ import { SimboloInterface } from './simbolo-interface'; import { TiposDadosInterface } from './tipos-dados-interface'; +interface TipoDadoParametroInterface { + nome: string; + tipo: TiposDadosInterface; +} export interface ParametroInterface { abrangencia: 'padrao' | 'multiplo'; nome: SimboloInterface; - tipo?: TiposDadosInterface; + tipoDado?: TipoDadoParametroInterface; valorPadrao?: any; referencia?: boolean; } diff --git a/fontes/tradutores/tradutor-assemblyscript.ts b/fontes/tradutores/tradutor-assemblyscript.ts index 6f96e5b9..fe53a482 100644 --- a/fontes/tradutores/tradutor-assemblyscript.ts +++ b/fontes/tradutores/tradutor-assemblyscript.ts @@ -540,7 +540,7 @@ export class TradutorAssemblyScript { traduzirFuncaoConstruto(funcaoConstruto: FuncaoConstruto): string { let resultado = 'function('; for (const parametro of funcaoConstruto.parametros) { - const tipoParametro = this.resolveTipoDeclaracaoVarEContante(parametro.tipo); + const tipoParametro = this.resolveTipoDeclaracaoVarEContante(parametro.tipoDado.tipo); resultado += `${parametro.nome.lexema}${tipoParametro}, `; } diff --git a/testes/analisador-semantico.test.ts b/testes/analisador-semantico.test.ts index 4bd68fcf..b658055f 100644 --- a/testes/analisador-semantico.test.ts +++ b/testes/analisador-semantico.test.ts @@ -24,6 +24,24 @@ describe('Analisador semântico', () => { expect(retornoAnalisadorSemantico.erros).toHaveLength(0); }); + it('Sucesso - Definição Tipo Variável', () => { + const retornoLexador = lexador.mapear([ + "var t: texto = \"Variável com tipo\"", + "const n: inteiro = 10", + "var v1: texto[] = [\'oi\']", + "var v2: inteiro[] = [1]", + "const a: vetor = ['1', '2', '3']", + "const b: vetor = [1, 2, 3]", + "const c: qualquer[] = [1, 2, 3]", + "const d: qualquer[] = [1, '2', 3, 'Olá Mundo']", + "const f: qualquer = [1, '2', 3, 'Olá Mundo']", + ], -1); + const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); + + expect(retornoAvaliadorSintatico).toBeTruthy(); + expect(retornoAvaliadorSintatico.declaracoes).toHaveLength(9); + }); + it('Sucesso - Atribuindo tipos válidos para variáveis', () => { const retornoLexador = lexador.mapear([ "var a: inteiro = 1", @@ -112,7 +130,26 @@ describe('Analisador semântico', () => { expect(retornoAnalisadorSemantico.erros).toHaveLength(8); }); - it.skip('Retorno vazio', () => { + it('Declaração de variáveis inválidas', () => { + const retornoLexador = lexador.mapear([ + "var t: texto = 1", + "const n: inteiro = \"10\"", + "var v1: texto[] = [1, 2]", + "var v2: inteiro[] = ['1']", + "var v3: inteiro[] = 1" + ], -1); + const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); + const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes); + + expect(retornoAnalisadorSemantico).toBeTruthy(); + expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('Atribuição inválida para \'t\', é esperado um \'texto\'.'); + expect(retornoAnalisadorSemantico.erros[1].mensagem).toBe('Atribuição inválida \'n\', é esperado um \'número\'.'); + expect(retornoAnalisadorSemantico.erros[2].mensagem).toBe('Atribuição inválida para \'v1\', é esperado um vetor de \'texto\'.'); + expect(retornoAnalisadorSemantico.erros[3].mensagem).toBe('Atribuição inválida para \'v2\', é esperado um vetor de \'inteiros\' ou \'real\'.'); + expect(retornoAnalisadorSemantico.erros[4].mensagem).toBe('Atribuição inválida para \'v3\', é esperado um vetor de elementos.'); + }); + + it('Retorno vazio', () => { const retornoLexador = lexador.mapear([ "funcao olaMundo (): vazio {", " retorna \"Olá Mundo!!!\"", @@ -122,7 +159,75 @@ describe('Analisador semântico', () => { const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes); expect(retornoAnalisadorSemantico).toBeTruthy(); - expect(retornoAnalisadorSemantico.erros).toHaveLength(1); + expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('A função não pode ter nenhum tipo de retorno.'); + }); + + it('Não retornando o tipo que a função definiu - texto', () => { + const retornoLexador = lexador.mapear([ + "funcao executar(valor1, valor2): texto {", + " var resultado = valor1 + valor2", + " retorna 10", + "}", + ], -1); + const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); + const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes); + + expect(retornoAnalisadorSemantico).toBeTruthy(); + expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('Esperado retorno do tipo \'texto\' dentro da função.'); + }); + + it('Não retornando o tipo que a função definiu - inteiro', () => { + const retornoLexador = lexador.mapear([ + "funcao executar(valor1, valor2): inteiro {", + " var resultado = valor1 + valor2", + " retorna 'a'", + "}", + ], -1); + const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); + const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes); + + expect(retornoAnalisadorSemantico).toBeTruthy(); + expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('Esperado retorno do tipo \'inteiro\' dentro da função.'); + }); + + it('Retorno vazio mas com retorno de valor', () => { + const retornoLexador = lexador.mapear([ + "funcao executar(valor1, valor2): vazio {", + " var resultado = valor1 + valor2", + " retorna resultado", + "}", + ], -1); + const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); + const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes); + + expect(retornoAnalisadorSemantico).toBeTruthy(); + expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('A função não pode ter nenhum tipo de retorno.'); + }); + + it('Função sem retorno de valor', () => { + const retornoLexador = lexador.mapear([ + "funcao executar(valor1, valor2): texto {", + " var resultado = valor1 + valor2", + "}", + ], -1); + const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); + const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes); + + expect(retornoAnalisadorSemantico).toBeTruthy(); + expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('Esperado retorno do tipo \'texto\' dentro da função.'); + }); + + it('Parametro com definição de tipo inválido', () => { + const retornoLexador = lexador.mapear([ + "funcao executar(valor1: algum, valor2): texto {", + " retorna \'Olá Mundo!!!\'", + "}", + ], -1); + const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); + const retornoAnalisadorSemantico = analisadorSemantico.analisar(retornoAvaliadorSintatico.declaracoes); + + expect(retornoAnalisadorSemantico).toBeTruthy(); + expect(retornoAnalisadorSemantico.erros[0].mensagem).toBe('O tipo \'algum\' não é válido.'); }); }); }); diff --git a/testes/avaliador-sintatico.test.ts b/testes/avaliador-sintatico.test.ts index 9a1d7472..f051e24e 100644 --- a/testes/avaliador-sintatico.test.ts +++ b/testes/avaliador-sintatico.test.ts @@ -14,24 +14,6 @@ describe('Avaliador sintático', () => { expect(retornoAvaliadorSintatico).toBeTruthy(); expect(retornoAvaliadorSintatico.declaracoes).toHaveLength(1); }); - - it('Sucesso - Definição Tipo Variável', () => { - const retornoLexador = lexador.mapear([ - "var t: texto = \"Variável com tipo\"", - "const n: inteiro = 10", - "var v1: texto[] = [\'oi\']", - "var v2: inteiro[] = [1]", - "const a: vetor = ['1', '2', '3']", - "const b: vetor = [1, 2, 3]", - "const c: qualquer[] = [1, 2, 3]", - "const d: qualquer[] = [1, '2', 3, 'Olá Mundo']", - "const f: qualquer = [1, '2', 3, 'Olá Mundo']", - ], -1); - const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); - - expect(retornoAvaliadorSintatico).toBeTruthy(); - expect(retornoAvaliadorSintatico.declaracoes).toHaveLength(9); - }); it('Sucesso - Vetor vazio', () => { const retornoLexador = lexador.mapear(['var vetorVazio = []'], -1); @@ -230,44 +212,6 @@ describe('Avaliador sintático', () => { expect(retornoAvaliadorSintatico.erros[0].message).toBe('Não pode haver mais de 255 parâmetros'); }); - it('Retorno vazio mas com retorno de valor', () => { - const retornoLexador = lexador.mapear([ - "funcao executar(valor1, valor2): vazio {", - " var resultado = valor1 + valor2", - " retorna resultado", - "}", - ], -1); - const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); - - expect(retornoAvaliadorSintatico).toBeTruthy(); - expect(retornoAvaliadorSintatico.erros[0].message).toBe('A função não pode ter nenhum tipo de retorno.'); - }); - - it('Não retornando o tipo que a função definiu', () => { - const retornoLexador = lexador.mapear([ - "funcao executar(valor1, valor2): texto {", - " var resultado = valor1 + valor2", - " retorna 10", - "}", - ], -1); - const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); - - expect(retornoAvaliadorSintatico).toBeTruthy(); - expect(retornoAvaliadorSintatico.erros[0].message).toBe('Esperado retorno do tipo \'texto\' dentro da função.'); - }); - - it('Parametro com definição de tipo inválido', () => { - const retornoLexador = lexador.mapear([ - "funcao executar(valor1: algum, valor2): texto {", - " retorna \'Olá Mundo!!!\'", - "}", - ], -1); - const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); - - expect(retornoAvaliadorSintatico).toBeTruthy(); - expect(retornoAvaliadorSintatico.erros[0].message).toBe('O tipo \'algum\' não é válido.'); - }); - it('Função com retorno de vetor', () => { const retornoLexador = lexador.mapear([ "funcao executar(): texto[] {",