From 71492b12263ec903a34ed886f2c017583a6fa417 Mon Sep 17 00:00:00 2001 From: Leonel Sanches da Silva <53848829+leonelsanchesdasilva@users.noreply.github.com> Date: Sun, 28 Apr 2024 21:16:09 -0700 Subject: [PATCH] =?UTF-8?q?Construto=20de=20coment=C3=A1rio=20(#678)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Construto de comentário e impactos iniciais. * Implementações do construto de comentário no Lexador e no Avaliador Sintático. --- .../analisador-semantico-base.ts | 23 +- .../analisador-semantico.ts | 25 +- .../avaliador-sintatico.ts | 369 ++++++++++-------- fontes/construtos/atribuir.ts | 3 + fontes/construtos/comentario.ts | 25 ++ fontes/construtos/index.ts | 1 + fontes/estruturas/chamavel.ts | 3 +- fontes/estruturas/classe-padrao.ts | 3 +- fontes/estruturas/funcao-padrao.ts | 16 +- fontes/formatadores/formatador-delegua.ts | 17 + fontes/formatadores/formatador-potigol.ts | 12 +- .../interfaces/visitante-comum-interface.ts | 3 + .../interpretador-egua-classico.ts | 8 +- .../egua-classico/resolvedor/resolvedor.ts | 5 + .../interpretador-portugol-ipt.ts | 5 + fontes/interpretador/interpretador-base.ts | 132 ++++--- fontes/lexador/lexador.ts | 53 ++- fontes/tipos-de-simbolos/delegua.ts | 2 + .../interpretador-com-depuracao.test.ts | 43 ++ testes/lexador.test.ts | 10 +- 20 files changed, 470 insertions(+), 288 deletions(-) create mode 100644 fontes/construtos/comentario.ts diff --git a/fontes/analisador-semantico/analisador-semantico-base.ts b/fontes/analisador-semantico/analisador-semantico-base.ts index cb66f3d5..da0ad313 100644 --- a/fontes/analisador-semantico/analisador-semantico-base.ts +++ b/fontes/analisador-semantico/analisador-semantico-base.ts @@ -22,6 +22,7 @@ import { TipoDe, Unario, Vetor, + Comentario, } from '../construtos'; import { Declaracao, @@ -54,7 +55,6 @@ import { Retorna, Sustar, } from '../declaracoes'; -import { DeleguaFuncao } from '../estruturas'; import { AnalisadorSemanticoInterface } from '../interfaces/analisador-semantico-interface'; import { RetornoAnalisadorSemantico } from '../interfaces/retornos/retorno-analisador-semantico'; import { ContinuarQuebra, RetornoQuebra, SustarQuebra } from '../quebras'; @@ -65,16 +65,9 @@ import { ContinuarQuebra, RetornoQuebra, SustarQuebra } from '../quebras'; * simplesmente passa por ele (`return Promise.resolve()`). */ export abstract class AnalisadorSemanticoBase implements AnalisadorSemanticoInterface { + abstract analisar(declaracoes: Declaracao[]): RetornoAnalisadorSemantico; - visitarDeclaracaoTendoComo(declaracao: TendoComo): Promise { - return Promise.resolve(); - } - - visitarDeclaracaoInicioAlgoritmo(declaracao: InicioAlgoritmo): Promise { - return Promise.resolve(); - } - visitarDeclaracaoAleatorio(declaracao: Aleatorio): Promise { return Promise.resolve(); } @@ -87,6 +80,10 @@ export abstract class AnalisadorSemanticoBase implements AnalisadorSemanticoInte return Promise.resolve(); } + visitarDeclaracaoComentario(declaracao: Comentario): void | Promise { + return Promise.resolve(); + } + visitarDeclaracaoConst(declaracao: Const): Promise { return Promise.resolve(); } @@ -127,6 +124,10 @@ export abstract class AnalisadorSemanticoBase implements AnalisadorSemanticoInte return Promise.resolve(); } + visitarDeclaracaoInicioAlgoritmo(declaracao: InicioAlgoritmo): Promise { + return Promise.resolve(); + } + visitarDeclaracaoPara(declaracao: Para): Promise { return Promise.resolve(); } @@ -139,6 +140,10 @@ export abstract class AnalisadorSemanticoBase implements AnalisadorSemanticoInte return Promise.resolve(); } + visitarDeclaracaoTendoComo(declaracao: TendoComo): Promise { + return Promise.resolve(); + } + visitarDeclaracaoTente(declaracao: Tente): Promise { return Promise.resolve(); } diff --git a/fontes/analisador-semantico/analisador-semantico.ts b/fontes/analisador-semantico/analisador-semantico.ts index 602a3340..7b9b54d4 100644 --- a/fontes/analisador-semantico/analisador-semantico.ts +++ b/fontes/analisador-semantico/analisador-semantico.ts @@ -4,54 +4,31 @@ import { Binario, Chamada, Construto, - ExpressaoRegular, - FimPara, - FormatacaoEscrita, FuncaoConstruto, Literal, Logico, - Super, TipoDe, - Tupla, Variavel, Vetor, } from '../construtos'; import { - Aleatorio, - Bloco, - CabecalhoPrograma, - 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 { InicioAlgoritmo } from '../declaracoes/inicio-algoritmo'; 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 { TipoDadosElementar } from '../tipo-dados-elementar'; -import { ContinuarQuebra, RetornoQuebra, SustarQuebra } from '../quebras'; +import { RetornoQuebra } from '../quebras'; import { AnalisadorSemanticoBase } from './analisador-semantico-base'; import { PilhaVariaveis } from './pilha-variaveis'; diff --git a/fontes/avaliador-sintatico/avaliador-sintatico.ts b/fontes/avaliador-sintatico/avaliador-sintatico.ts index 319c98a5..b3da413b 100644 --- a/fontes/avaliador-sintatico/avaliador-sintatico.ts +++ b/fontes/avaliador-sintatico/avaliador-sintatico.ts @@ -25,6 +25,7 @@ import { Isto, ExpressaoRegular, Decorador, + Comentario, } from '../construtos'; import { ErroAvaliadorSintatico } from './erro-avaliador-sintatico'; @@ -112,14 +113,6 @@ export class AvaliadorSintatico implements AvaliadorSintaticoInterface { + let declaracoes: Array = []; + + while (!this.verificarTipoSimboloAtual(tiposDeSimbolos.CHAVE_DIREITA) && !this.estaNoFinal()) { + const retornoDeclaracao = this.resolverDeclaracaoForaDeBloco(); + if (Array.isArray(retornoDeclaracao)) { + declaracoes = declaracoes.concat(retornoDeclaracao); + } else { + declaracoes.push(retornoDeclaracao as Declaracao); + } + } + + this.consumir(tiposDeSimbolos.CHAVE_DIREITA, "Esperado '}' após o bloco."); + + this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.PONTO_E_VIRGULA); + + return declaracoes; + } + + declaracaoEnquanto(): Enquanto { + try { + this.blocos += 1; + + const condicao = this.expressao(); + const corpo = this.resolverDeclaracao(); + + return new Enquanto(condicao, corpo); + } finally { + this.blocos -= 1; + } + } + declaracaoEscreva(): Escreva { const simboloAtual = this.simbolos[this.atual]; @@ -670,6 +695,145 @@ export class AvaliadorSintatico implements AvaliadorSintaticoInterface { - let declaracoes: Array = []; - - while (!this.verificarTipoSimboloAtual(tiposDeSimbolos.CHAVE_DIREITA) && !this.estaNoFinal()) { - const retornoDeclaracao = this.resolverDeclaracaoForaDeBloco(); - if (Array.isArray(retornoDeclaracao)) { - declaracoes = declaracoes.concat(retornoDeclaracao); - } else { - declaracoes.push(retornoDeclaracao as Declaracao); - } - } - - this.consumir(tiposDeSimbolos.CHAVE_DIREITA, "Esperado '}' após o bloco."); - - this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.PONTO_E_VIRGULA); - - return declaracoes; - } - - declaracaoSe(): Se { - const condicao = this.expressao(); - - const caminhoEntao = this.resolverDeclaracao(); - - let caminhoSenao = null; - if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.SENAO, tiposDeSimbolos.SENÃO)) { - caminhoSenao = this.resolverDeclaracao(); - } - - return new Se(condicao, caminhoEntao, [], caminhoSenao); - } - - declaracaoEnquanto(): Enquanto { + declaracaoPara(): Para | ParaCada { try { + const simboloPara: SimboloInterface = this.simbolos[this.atual - 1]; this.blocos += 1; - const condicao = this.expressao(); - const corpo = this.resolverDeclaracao(); + if (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.CADA)) { + return this.declaracaoParaCada(simboloPara); + } - return new Enquanto(condicao, corpo); + return this.declaracaoParaTradicional(simboloPara); } finally { this.blocos -= 1; } @@ -796,41 +930,6 @@ export class AvaliadorSintatico implements AvaliadorSintaticoInterface implements Construto { linha: number; hashArquivo: number; diff --git a/fontes/construtos/comentario.ts b/fontes/construtos/comentario.ts new file mode 100644 index 00000000..72331cc6 --- /dev/null +++ b/fontes/construtos/comentario.ts @@ -0,0 +1,25 @@ +import { VisitanteComumInterface } from "../interfaces"; +import { Construto } from "./construto"; + +/** + * Como construto, um comentário é normalmente útil para formatadores de código. + * Pode ser que em alguns casos ter um conteúdo dentro de um comentário possa ser + * importante. Por exemplo, uma ferramenta de testes ou de auto-documentação. + */ +export class Comentario implements Construto { + linha: number; + hashArquivo: number; + conteudo: string | string[]; + multilinha: boolean; + + constructor(hashArquivo: number, linha: number, conteudo: string | string[], multilinha: boolean) { + this.hashArquivo = hashArquivo; + this.linha = linha; + this.conteudo = conteudo; + this.multilinha = multilinha; + } + + async aceitar(visitante: VisitanteComumInterface): Promise { + return await visitante.visitarDeclaracaoComentario(this); + } +} diff --git a/fontes/construtos/index.ts b/fontes/construtos/index.ts index ff6def93..bae9e9fe 100644 --- a/fontes/construtos/index.ts +++ b/fontes/construtos/index.ts @@ -7,6 +7,7 @@ export * from './atribuicao-por-indices-matriz'; export * from './atribuir'; export * from './binario'; export * from './chamada'; +export * from './comentario'; export * from './constante'; export * from './constante-ou-variavel'; export * from './construto'; diff --git a/fontes/estruturas/chamavel.ts b/fontes/estruturas/chamavel.ts index 58f4d734..245f8050 100644 --- a/fontes/estruturas/chamavel.ts +++ b/fontes/estruturas/chamavel.ts @@ -1,3 +1,4 @@ +import { SimboloInterface, VisitanteComumInterface } from '../interfaces'; import { ArgumentoInterface } from '../interpretador/argumento-interface'; export abstract class Chamavel { @@ -7,7 +8,7 @@ export abstract class Chamavel { return this.valorAridade; } - async chamar(interpretador?: any, argumentos?: ArgumentoInterface[], simbolo?: any): Promise { + async chamar(visitante?: VisitanteComumInterface, argumentos?: ArgumentoInterface[], simbolo?: SimboloInterface): Promise { return Promise.reject(new Error('Este método não deveria ser chamado.')); } } diff --git a/fontes/estruturas/classe-padrao.ts b/fontes/estruturas/classe-padrao.ts index fff93e9a..53a315ed 100644 --- a/fontes/estruturas/classe-padrao.ts +++ b/fontes/estruturas/classe-padrao.ts @@ -1,3 +1,4 @@ +import { SimboloInterface, VisitanteComumInterface } from '../interfaces'; import { Chamavel } from './chamavel'; /** @@ -37,7 +38,7 @@ export class ClassePadrao extends Chamavel { * @param argumentos * @param simbolo */ - chamar(argumentos: any[], simbolo: any): any { + chamar(visitante: VisitanteComumInterface, argumentos: any[], simbolo: SimboloInterface): any { const novoObjeto: any = new this.funcaoDeClasse(argumentos); return novoObjeto; } diff --git a/fontes/estruturas/funcao-padrao.ts b/fontes/estruturas/funcao-padrao.ts index 4c58524f..514b7c68 100644 --- a/fontes/estruturas/funcao-padrao.ts +++ b/fontes/estruturas/funcao-padrao.ts @@ -1,12 +1,14 @@ +import { SimboloInterface, VisitanteComumInterface } from '../interfaces'; import { Chamavel } from './chamavel'; /** - * Uma `FuncaoPadrao` normalmente é uma função em JavaScript. + * Uma `FuncaoPadrao` normalmente é uma função em JavaScript que representa um + * método de uma biblioteca global, mas que pode ser usada para outros casos. */ export class FuncaoPadrao extends Chamavel { valorAridade: number; funcao: Function; - simbolo: any; + simbolo: SimboloInterface; constructor(valorAridade: number, funcao: Function) { super(); @@ -14,7 +16,11 @@ export class FuncaoPadrao extends Chamavel { this.funcao = funcao; } - async chamar(argumentos: any[], simbolo: any): Promise { + async chamar(visitante: VisitanteComumInterface, argumentos: any[], simbolo: SimboloInterface): Promise { + return this.chamarInterno(argumentos, simbolo); + } + + protected async chamarInterno(argumentos: any[], simbolo: SimboloInterface): Promise { this.simbolo = simbolo; return await this.funcao.apply(this, argumentos); } @@ -24,7 +30,7 @@ export class FuncaoPadrao extends Chamavel { * @returns {string} A representação da função como texto. */ paraTexto(): string { - return ''; + return ``; } /** @@ -32,6 +38,6 @@ export class FuncaoPadrao extends Chamavel { * @returns {string} A representação da classe como texto. */ toString(): string { - return ''; + return this.paraTexto(); } } diff --git a/fontes/formatadores/formatador-delegua.ts b/fontes/formatadores/formatador-delegua.ts index 2a864688..ae2cd671 100644 --- a/fontes/formatadores/formatador-delegua.ts +++ b/fontes/formatadores/formatador-delegua.ts @@ -6,6 +6,7 @@ import { Atribuir, Binario, Chamada, + Comentario, Construto, DefinirValor, Dicionario, @@ -80,6 +81,22 @@ export class FormatadorDelegua implements VisitanteComumInterface { this.deveIndentar = true; } + visitarDeclaracaoComentario(declaracao: Comentario): void | Promise { + if (declaracao.multilinha) { + this.codigoFormatado += `${' '.repeat(this.indentacaoAtual)}/* `; + + for (let linhaConteudo of (declaracao.conteudo as string[])) { + this.codigoFormatado += `${' '.repeat(this.indentacaoAtual)} ${linhaConteudo.replace(/\s+/g, " ")}${this.quebraLinha}`; + } + + this.codigoFormatado += `${' '.repeat(this.indentacaoAtual)} */${this.quebraLinha}`; + } else { + this.codigoFormatado += `${' '.repeat(this.indentacaoAtual)}// `; + this.codigoFormatado += (declaracao.conteudo as string).replace(/\s+/g, " "); + this.codigoFormatado += `${this.quebraLinha}`; + } + } + visitarDeclaracaoTendoComo(declaracao: TendoComo): void { this.codigoFormatado += `${' '.repeat(this.indentacaoAtual)}tendo `; this.formatarDeclaracaoOuConstruto(declaracao.inicializacaoVariavel); diff --git a/fontes/formatadores/formatador-potigol.ts b/fontes/formatadores/formatador-potigol.ts index 2adc0831..6f9e60c4 100644 --- a/fontes/formatadores/formatador-potigol.ts +++ b/fontes/formatadores/formatador-potigol.ts @@ -6,6 +6,7 @@ import { Atribuir, Binario, Chamada, + Comentario, Constante, Construto, Deceto, @@ -68,7 +69,7 @@ import { InicioAlgoritmo } from '../declaracoes/inicio-algoritmo'; import { VisitanteComumInterface } from '../interfaces'; import { ContinuarQuebra, RetornoQuebra, SustarQuebra } from '../quebras'; import tiposDeSimbolos from '../tipos-de-simbolos/potigol'; -import { inferirTipoVariavel } from '../interpretador/inferenciador'; + export class FormatadorPotigol implements VisitanteComumInterface { indentacaoAtual: number; quebraLinha: string; @@ -87,6 +88,14 @@ export class FormatadorPotigol implements VisitanteComumInterface { this.deveIndentar = true; } + /** + * Aparentemente só existe comentário de uma linha só em Potigol. + * @param declaracao A declaração de comentário. + */ + visitarDeclaracaoComentario(declaracao: Comentario): void { + this.codigoFormatado += `${" ".repeat(this.indentacaoAtual)}# ${declaracao.conteudo}${this.quebraLinha}`; + } + visitarDeclaracaoTendoComo(declaracao: TendoComo): void | Promise { throw new Error('Método não implementado.'); } @@ -102,6 +111,7 @@ export class FormatadorPotigol implements VisitanteComumInterface { visitarExpressaoTupla(expressao: Tupla): Promise { throw new Error('Método não implementado'); } + visitarDeclaracaoClasse(declaracao: Classe) { this.codigoFormatado += `${" ".repeat(this.indentacaoAtual)}tipo ${declaracao.simbolo.lexema}${this.quebraLinha}` this.formatarBlocoOuVetorDeclaracoes(declaracao.propriedades) diff --git a/fontes/interfaces/visitante-comum-interface.ts b/fontes/interfaces/visitante-comum-interface.ts index fac0d81a..ae08487e 100644 --- a/fontes/interfaces/visitante-comum-interface.ts +++ b/fontes/interfaces/visitante-comum-interface.ts @@ -8,6 +8,7 @@ import { Atribuir, Binario, Chamada, + Comentario, Constante, DefinirValor, Dicionario, @@ -25,6 +26,7 @@ import { Variavel, Vetor, } from '../construtos'; + import { Aleatorio, Bloco, @@ -61,6 +63,7 @@ export interface VisitanteComumInterface { visitarDeclaracaoAleatorio(declaracao: Aleatorio): Promise; visitarDeclaracaoCabecalhoPrograma(declaracao: CabecalhoPrograma): Promise; visitarDeclaracaoClasse(declaracao: Classe): Promise | void; + visitarDeclaracaoComentario(declaracao: Comentario): Promise | void; visitarDeclaracaoConst(declaracao: Const): Promise; visitarDeclaracaoConstMultiplo(declaracao: ConstMultiplo): Promise; visitarDeclaracaoDeExpressao(declaracao: Expressao): Promise | void; diff --git a/fontes/interpretador/dialetos/egua-classico/interpretador-egua-classico.ts b/fontes/interpretador/dialetos/egua-classico/interpretador-egua-classico.ts index 1db1ad52..43eda7bf 100644 --- a/fontes/interpretador/dialetos/egua-classico/interpretador-egua-classico.ts +++ b/fontes/interpretador/dialetos/egua-classico/interpretador-egua-classico.ts @@ -10,6 +10,7 @@ import { ObjetoDeleguaClasse } from '../../../estruturas/objeto-delegua-classe'; import { AcessoIndiceVariavel, Atribuir, + Comentario, Construto, ExpressaoRegular, FimPara, @@ -104,6 +105,10 @@ export class InterpretadorEguaClassico implements InterpretadorInterface { carregarBibliotecaGlobal(this, this.pilhaEscoposExecucao); } + visitarDeclaracaoComentario(declaracao: Comentario): Promise { + return Promise.resolve(); + } + visitarDeclaracaoTendoComo(declaracao: TendoComo): Promise { throw new Error('Método não implementado.'); } @@ -407,8 +412,9 @@ export class InterpretadorEguaClassico implements InterpretadorInterface { if (entidadeChamada instanceof FuncaoPadrao) { return entidadeChamada.chamar( + undefined, argumentos.map((a) => (a !== null && a.hasOwnProperty('valor') ? a.valor : a)), - expressao.entidadeChamada.nome + expressao.entidadeChamada.simbolo ); } diff --git a/fontes/interpretador/dialetos/egua-classico/resolvedor/resolvedor.ts b/fontes/interpretador/dialetos/egua-classico/resolvedor/resolvedor.ts index 694f6028..45f63600 100644 --- a/fontes/interpretador/dialetos/egua-classico/resolvedor/resolvedor.ts +++ b/fontes/interpretador/dialetos/egua-classico/resolvedor/resolvedor.ts @@ -1,5 +1,6 @@ import { AcessoMetodoOuPropriedade, + Comentario, Construto, ExpressaoRegular, FimPara, @@ -91,6 +92,10 @@ export class ResolvedorEguaClassico implements ResolvedorInterface, Interpretado this.cicloAtual = TipoClasse.NENHUM; } + visitarDeclaracaoComentario(declaracao: Comentario): Promise { + return Promise.resolve(); + } + visitarDeclaracaoTendoComo(declaracao: TendoComo): Promise { 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 7f28a88e..86fbe8f3 100644 --- a/fontes/interpretador/dialetos/portugol-ipt/interpretador-portugol-ipt.ts +++ b/fontes/interpretador/dialetos/portugol-ipt/interpretador-portugol-ipt.ts @@ -1,5 +1,6 @@ import { Atribuir, + Comentario, Construto, ExpressaoRegular, FimPara, @@ -89,6 +90,10 @@ export class InterpretadorPortugolIpt implements InterpretadorInterface { }; this.pilhaEscoposExecucao.empilhar(escopoExecucao); } + + visitarDeclaracaoComentario(declaracao: Comentario): Promise { + return Promise.resolve(); + } visitarDeclaracaoTendoComo(declaracao: TendoComo): Promise { throw new Error('Método não implementado.'); diff --git a/fontes/interpretador/interpretador-base.ts b/fontes/interpretador/interpretador-base.ts index 57a8f461..81883a77 100644 --- a/fontes/interpretador/interpretador-base.ts +++ b/fontes/interpretador/interpretador-base.ts @@ -51,6 +51,7 @@ import { Atribuir, Binario, Chamada, + Comentario, Construto, Dicionario, ExpressaoRegular, @@ -172,6 +173,15 @@ export class InterpretadorBase implements InterpretadorInterface { carregarBibliotecasGlobais(this, this.pilhaEscoposExecucao); } + /** + * Construtos de comentários não têm utilidade para o Interpretador. + * Apenas retornamos `Promise.resolve()` para não termos erros. + * @param declaracao A declaração de comentário. + */ + visitarDeclaracaoComentario(declaracao: Comentario): Promise { + return Promise.resolve(); + } + async visitarDeclaracaoTendoComo(declaracao: TendoComo): Promise { const retornoInicializacao = await this.avaliar(declaracao.inicializacaoVariavel); this.pilhaEscoposExecucao.definirConstante(declaracao.simboloVariavel.lexema, retornoInicializacao); @@ -632,12 +642,68 @@ export class InterpretadorBase implements InterpretadorInterface { } } + /** + * Faz a chamada do método de uma primitiva (por exemplo, número, texto, etc.) com seus + * respectivos argumentos. + * @param {Chamada} expressao A expressão de chamada. + * @param {MetodoPrimitiva} entidadeChamada O método da primitiva chamado. + * @returns O resultado da chamada do método da primitiva. + */ + protected async chamarMetodoPrimitiva(expressao: Chamada, entidadeChamada: MetodoPrimitiva): Promise { + const argumentosResolvidos: any[] = []; + + for (const argumento of expressao.argumentos) { + const valorResolvido: any = await this.avaliar(argumento); + argumentosResolvidos.push( + valorResolvido?.hasOwnProperty('valor') ? valorResolvido.valor : valorResolvido + ); + } + + return await entidadeChamada.chamar(this, argumentosResolvidos); + } + + protected async resolverArgumentosChamada(expressao: Chamada): Promise { + const argumentos: ArgumentoInterface[] = []; + for (let i = 0; i < expressao.argumentos.length; i++) { + const variavelArgumento = expressao.argumentos[i]; + const nomeArgumento = variavelArgumento.hasOwnProperty('simbolo') + ? variavelArgumento.simbolo.lexema + : undefined; + + argumentos.push({ + nome: nomeArgumento, + valor: await this.avaliar(variavelArgumento), + }); + } + + return argumentos; + } + + /** + * Resolve paraâmetros da chamada de acordo com o tipo da entidade chamada. + * @param {Chamavel} entidadeChamada A entidade chamada. + * @returns Os parâmetros resolvidos. + */ + protected resolverParametrosChamada(entidadeChamada: Chamavel): ParametroInterface[] { + if (entidadeChamada instanceof DeleguaFuncao) { + return entidadeChamada.declaracao.parametros; + } + + if (entidadeChamada instanceof DeleguaClasse) { + return entidadeChamada.metodos.construtor + ? entidadeChamada.metodos.construtor.declaracao.parametros + : []; + } + + return []; + } + /** * Executa uma chamada de função, método ou classe. * @param expressao A expressão chamada. * @returns O resultado da chamada. */ - async visitarExpressaoDeChamada(expressao: any): Promise { + async visitarExpressaoDeChamada(expressao: Chamada | any): Promise { try { const variavelEntidadeChamada: VariavelInterface | any = await this.avaliar(expressao.entidadeChamada); @@ -655,19 +721,6 @@ export class InterpretadorBase implements InterpretadorInterface { ? variavelEntidadeChamada.valor : variavelEntidadeChamada; - let argumentos: ArgumentoInterface[] = []; - for (let i = 0; i < expressao.argumentos.length; i++) { - const variavelArgumento = expressao.argumentos[i]; - const nomeArgumento = variavelArgumento.hasOwnProperty('simbolo') - ? variavelArgumento.simbolo.lexema - : undefined; - - argumentos.push({ - nome: nomeArgumento, - valor: await this.avaliar(variavelArgumento), - }); - } - if (entidadeChamada instanceof DeleguaModulo) { return Promise.reject( new ErroEmTempoDeExecucao( @@ -679,45 +732,14 @@ export class InterpretadorBase implements InterpretadorInterface { } if (entidadeChamada instanceof MetodoPrimitiva) { - const argumentosResolvidos: any[] = []; - - // if (expressao instanceof Chamada) { - // if (expressao.entidadeChamada instanceof AcessoMetodoOuPropriedade) { - // if (expressao.entidadeChamada.objeto instanceof Variavel) { - // let eTupla = this.procurarVariavel(expressao.entidadeChamada.objeto.simbolo) - // if (eTupla.tipo === tipoDeDadosDelegua.TUPLA) { - // return Promise.reject( - // new ErroEmTempoDeExecucao( - // expressao.entidadeChamada.objeto.simbolo, - // 'Tupla é imutável, seus elementos não podem ser alterados, adicionados ou removidos.', - // expressao.linha - // ) - // ); - // } - // } - // } - // } - - for (const argumento of expressao.argumentos) { - const valorResolvido: any = await this.avaliar(argumento); - argumentosResolvidos.push( - valorResolvido?.hasOwnProperty('valor') ? valorResolvido.valor : valorResolvido - ); - } - - return await entidadeChamada.chamar(this, argumentosResolvidos); + return await this.chamarMetodoPrimitiva(expressao, entidadeChamada); } - let parametros: ParametroInterface[]; - if (entidadeChamada instanceof DeleguaFuncao) { - parametros = entidadeChamada.declaracao.parametros; - } else if (entidadeChamada instanceof DeleguaClasse) { - parametros = entidadeChamada.metodos.construtor - ? entidadeChamada.metodos.construtor.declaracao.parametros - : []; - } else { - parametros = []; - } + const argumentos: ArgumentoInterface[] = await this.resolverArgumentosChamada(expressao); + // TODO: Aparentemente isso nunca é usado se o bloco de resolução de parâmetros, + // mais abaixo, também não é. + // Estudar remoção mais adiante. + const parametros: ParametroInterface[] = this.resolverParametrosChamada(entidadeChamada); const aridade = entidadeChamada.aridade ? entidadeChamada.aridade() : entidadeChamada.length; @@ -727,7 +749,7 @@ export class InterpretadorBase implements InterpretadorInterface { for (let i = 0; i < diferenca; i++) { argumentos.push(null); } - } else { + } /* else { // TODO: Aparentemente isso aqui nunca funcionou. // Avaliar de simplesmente apagar este código, e usar o que foi // implementado em `DeleguaFuncao.chamar`. @@ -742,15 +764,16 @@ export class InterpretadorBase implements InterpretadorInterface { ); argumentos = novosArgumentos; } - } + } */ if (entidadeChamada instanceof FuncaoPadrao) { try { return entidadeChamada.chamar( + undefined, argumentos.map((a) => a && a.valor && a.valor.hasOwnProperty('valor') ? a.valor.valor : a?.valor ), - expressao.entidadeChamada.nome + expressao.entidadeChamada.simbolo ); } catch (erro: any) { this.erros.push({ @@ -771,6 +794,7 @@ export class InterpretadorBase implements InterpretadorInterface { // A função chamada pode ser de uma biblioteca JavaScript. // Neste caso apenas testamos se o tipo é uma função. + // TODO: Descobrir qual caso exatamente passa aqui. if (typeof entidadeChamada === tipoDeDadosPrimitivos.FUNCAO) { let objeto = null; if (expressao.entidadeChamada.objeto) { diff --git a/fontes/lexador/lexador.ts b/fontes/lexador/lexador.ts index 38e60fda..f8f1d5e8 100644 --- a/fontes/lexador/lexador.ts +++ b/fontes/lexador/lexador.ts @@ -118,9 +118,41 @@ export class Lexador implements LexadorInterface { return this.codigo[this.linha].charAt(this.atual); } - avancarParaProximaLinha(): void { - this.linha++; - this.atual = 0; + comentarioMultilinha(): void { + let conteudo = ''; + while (!this.eFinalDoCodigo()) { + this.avancar(); + conteudo += this.codigo[this.linha].charAt(this.atual); + if (this.simboloAtual() === '*' && this.proximoSimbolo() === '/') { + const linhas = conteudo.split('\0'); + for (let linha of linhas) { + this.adicionarSimbolo(tiposDeSimbolos.LINHA_COMENTARIO, linha.trim()); + } + + // Remove o asterisco da última linha + let lexemaUltimaLinha = this.simbolos[this.simbolos.length - 1].lexema; + lexemaUltimaLinha = lexemaUltimaLinha.substring(0, lexemaUltimaLinha.length - 1); + this.simbolos[this.simbolos.length - 1].lexema = lexemaUltimaLinha; + this.simbolos[this.simbolos.length - 1].literal = lexemaUltimaLinha; + + this.avancar(); + this.avancar(); + break; + } + } + } + + comentarioUmaLinha(): void { + this.avancar(); + const linhaAtual = this.linha; + let ultimoAtual = this.atual; + while (linhaAtual === this.linha && !this.eFinalDoCodigo()) { + ultimoAtual = this.atual; + this.avancar(); + } + + const conteudo = this.codigo[linhaAtual].substring(this.inicioSimbolo + 2, ultimoAtual); + this.adicionarSimbolo(tiposDeSimbolos.COMENTARIO, conteudo.trim()); } proximoSimbolo(): string { @@ -179,17 +211,6 @@ export class Lexador implements LexadorInterface { this.adicionarSimbolo(tipo); } - encontrarFimComentarioAsterisco(): void { - while (!this.eFinalDoCodigo()) { - this.avancar(); - if (this.simboloAtual() === '*' && this.proximoSimbolo() === '/') { - this.avancar(); - this.avancar(); - break; - } - } - } - analisarToken(): void { const caractere = this.simboloAtual(); @@ -371,10 +392,10 @@ export class Lexador implements LexadorInterface { this.avancar(); switch (this.simboloAtual()) { case '/': - this.avancarParaProximaLinha(); + this.comentarioUmaLinha(); break; case '*': - this.encontrarFimComentarioAsterisco(); + this.comentarioMultilinha(); break; case '=': this.adicionarSimbolo(tiposDeSimbolos.DIVISAO_IGUAL); diff --git a/fontes/tipos-de-simbolos/delegua.ts b/fontes/tipos-de-simbolos/delegua.ts index 7738b99d..468de7da 100644 --- a/fontes/tipos-de-simbolos/delegua.ts +++ b/fontes/tipos-de-simbolos/delegua.ts @@ -12,6 +12,7 @@ export default { CLASSE: 'CLASSE', COLCHETE_DIREITO: 'COLCHETE_DIREITO', COLCHETE_ESQUERDO: 'COLCHETE_ESQUERDO', + COMENTARIO: 'COMENTARIO', COMO: 'COMO', CONSTANTE: 'CONSTANTE', CONSTRUTOR: 'CONSTRUTOR', @@ -46,6 +47,7 @@ export default { INCREMENTAR: 'INCREMENTAR', ISTO: 'ISTO', LEIA: 'LEIA', + LINHA_COMENTARIO: 'LINHA_COMENTARIO', MAIOR: 'MAIOR', MAIOR_IGUAL: 'MAIOR_IGUAL', MAIOR_MAIOR: 'MAIOR_MAIOR', diff --git a/testes/interpretador/interpretador-com-depuracao.test.ts b/testes/interpretador/interpretador-com-depuracao.test.ts index bbc84593..6eda2b55 100644 --- a/testes/interpretador/interpretador-com-depuracao.test.ts +++ b/testes/interpretador/interpretador-com-depuracao.test.ts @@ -73,5 +73,48 @@ describe('Interpretador com Depuração', () => { expect(interpretador.pontoDeParadaAtivo).toBe(true); }); }); + + describe('Issue 677', () => { + beforeEach(() => { + interpretador = new InterpretadorComDepuracao( + process.cwd(), + console.log, + process.stdout.write.bind(process.stdout) + ); + }); + + it('Problema na inicialização', async () => { + const retornoLexador = lexador.mapear([ + "var numeros = []", + "funcao adicionarNumeros(numero, quantidadeDeVezes) {", + " para (var i = 0; i < quantidadeDeVezes; i = i + 1) {", + " numeros.adicionar(numero)", + " }", + "}", + "adicionarNumeros(10, 5)", + "adicionarNumeros(2, 5)", + "// adicionarNumeros(0, 5)", + "// adicionarNumeros(14, 5)", + "// adicionarNumeros(15, 5)", + "// adicionarNumeros(20, 5)", + "escreva(numeros)", + "escreva(numeros.ordenar())", + "escreva(numeros.filtrarPor)", + ], -1); + + let execucaoFinalizada: boolean = false; + interpretador.finalizacaoDaExecucao = () => { + execucaoFinalizada = true; + } + + const retornoAvaliadorSintatico = avaliadorSintatico.analisar(retornoLexador, -1); + + interpretador.prepararParaDepuracao(retornoAvaliadorSintatico.declaracoes); + await interpretador.instrucaoContinuarInterpretacao(); + + // expect(interpretador.pontoDeParadaAtivo).toBe(true); + expect(true).toBe(true); + }); + }); }); }); diff --git a/testes/lexador.test.ts b/testes/lexador.test.ts index 34d1d9da..2c01d4b3 100644 --- a/testes/lexador.test.ts +++ b/testes/lexador.test.ts @@ -138,14 +138,18 @@ describe('Lexador', () => { const resultado = lexador.mapear(["/* comentário ", "outro comentário*/"], -1); expect(resultado).toBeTruthy(); - expect(resultado.simbolos).toHaveLength(0); + expect(resultado.simbolos).toHaveLength(2); + expect(resultado.simbolos[0].tipo).toBe(tiposDeSimbolos.LINHA_COMENTARIO); + expect(resultado.simbolos[1].tipo).toBe(tiposDeSimbolos.LINHA_COMENTARIO); }); - it('Sucesso - Comentários uma linha', () => { + it('Sucesso - Comentários de uma linha', () => { const resultado = lexador.mapear(["// comentário ", "// outro comentário"], -1); expect(resultado).toBeTruthy(); - expect(resultado.simbolos).toHaveLength(0); + expect(resultado.simbolos).toHaveLength(2); + expect(resultado.simbolos[0].tipo).toBe(tiposDeSimbolos.COMENTARIO); + expect(resultado.simbolos[1].tipo).toBe(tiposDeSimbolos.COMENTARIO); }); it('Sucesso - Se', () => {