Skip to content

Commit

Permalink
Lógica mais robusta de chamada de funções e procedimentos sem parênte…
Browse files Browse the repository at this point in the history
…ses.
  • Loading branch information
leonelsanchesdasilva committed Jun 29, 2024
1 parent 6d1234b commit ce4a738
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 32 deletions.
47 changes: 47 additions & 0 deletions fontes/avaliador-sintatico/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Avaliador Sintático para dialeto VisuAlg

Aqui temos toda a implementação da avaliação sintática para VisuAlg, separada em dois arquivos:

- `avaliador-sintatico-visualg.ts`: O avaliador sintático em si;
- `parametro-visualg.ts`: Uma declaração de tipo de parâmetro. Ao analisar a seção `var` de um fonte com extensão `.alg`, temos a declaração de várias variáveis com o mesmo tipo. Essa estrutura ajuda a mapear essa condensação de variáveis.

## Características do VisuAlg

- Quebras de linha são significativas;
- Durante a avaliação sintática, o avaliador sintático mantém em memória os tipos conhecidos (normalmente registros) e funções e procedimentos conhecidos. Os tipos são usados tanto para indicar quando é necessário uma tipagem mais criteriosa - assim como sua respectiva inicialização, já que um registro é uma composição de outras variáveis de tipos primitivos ou complexos - quanto para validar se o acesso a elementos do registro fazem sentido ou não. Por exemplo, se meu registro é declarado da seguinte forma:

```
tipo teste = registro
campo1: inteiro
campo2: caractere
fimregistro
```

Tentar um acesso como o abaixo precisa causar um erro de avaliação sintática, já que o avaliador sabe que a propriedade `campo3` não existe:

```
algoritmo "Acesso a propriedade que não existe"
var
t: teste
inicio
leia(t.campo3) // Isso deve causar um erro de avaliação sintática
fimalgoritmo
```

Já para procedimentos e funções, simplesmente mencionar um procedimento ou função causa a sua execução. Por exemplo:

```
algoritmo "Chamada a procedimento sem parênteses"
var
procedimento escreva123 ()
inicio
escreval(123)
fimprocedimento
inicio
escreva123 // Isso chama o procedimento, ainda que não tenha parênteses.
fimalgoritmo
```

No ecossistema de Delégua, procedimentos e funções são mantidos no ambiente como variáveis. Delégua e alguns outros dialetos decidem se uma variável é uma chamada ou não com a inclusão de parênteses logo após o nome da função chamada.

Para decidir se uma expressão é uma chamada sem parênteses ou uma menção a uma variável, o analisador sintático precisa saber os nomes de procedimentos e funções declarados anteriormente.
14 changes: 13 additions & 1 deletion fontes/avaliador-sintatico/avaliador-sintatico-visualg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,14 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
blocoPrincipalIniciado: boolean;
fimAlgoritmoEncontrado: boolean;
tiposConhecidos: string[];
funcoesProcedimentosConhecidos: string[];

constructor() {
super();
this.blocoPrincipalIniciado = false;
this.fimAlgoritmoEncontrado = false;
this.tiposConhecidos = [];
this.funcoesProcedimentosConhecidos = [];
}

private validarSegmentoAlgoritmo(): SimboloInterface {
Expand Down Expand Up @@ -196,6 +198,7 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
case tiposDeSimbolos.FUNCAO:
case tiposDeSimbolos.FUNÇÃO:
const dadosFuncao = this.funcao('funcao');
this.funcoesProcedimentosConhecidos.push(dadosFuncao.simbolo.lexema);
inicializacoes.push(dadosFuncao);
break;
case tiposDeSimbolos.PROCEDIMENTO:
Expand Down Expand Up @@ -371,7 +374,14 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
if (
this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.IDENTIFICADOR, tiposDeSimbolos.METODO_BIBLIOTECA_GLOBAL)
) {
return new Variavel(this.hashArquivo, this.simbolos[this.atual - 1]);
const simboloIdentificadorOuMetodo = this.simbolos[this.atual - 1];
const variavel = new Variavel(this.hashArquivo, simboloIdentificadorOuMetodo);
// Chamada de função ou procedimento sem parâmetros.
if (this.funcoesProcedimentosConhecidos.includes(simboloIdentificadorOuMetodo.lexema)) {
return new Chamada(this.hashArquivo, variavel, undefined, []);
}

return variavel;
}

if (
Expand Down Expand Up @@ -1099,6 +1109,7 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
this.validarSegmentoInicio('procedimento');

const corpo: any[] = (inicializacoes as any[]).concat(this.blocoEscopo());
this.funcoesProcedimentosConhecidos.push(nomeProcedimento.lexema);

return new FuncaoDeclaracao(
nomeProcedimento,
Expand Down Expand Up @@ -1451,6 +1462,7 @@ export class AvaliadorSintaticoVisuAlg extends AvaliadorSintaticoBase {
this.blocoPrincipalIniciado = false;
this.fimAlgoritmoEncontrado = false;
this.tiposConhecidos = [];
this.funcoesProcedimentosConhecidos = [];

this.hashArquivo = hashArquivo || 0;
this.simbolos = retornoLexador?.simbolos || [];
Expand Down
169 changes: 151 additions & 18 deletions fontes/interpretador/comum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
AcessoElementoMatriz,
AcessoIndiceVariavel,
AcessoMetodoOuPropriedade,
AtribuicaoPorIndice,
AtribuicaoPorIndicesMatriz,
Binario,
Construto,
Expand All @@ -11,6 +12,7 @@ import {
Logico,
Unario,
Variavel,
Vetor,
} from '@designliquido/delegua/construtos';
import {
Aleatorio,
Expand Down Expand Up @@ -145,7 +147,7 @@ export async function atribuirVariavel(

let alvo = promises[0];
let indice = promises[1];
let valorAlvo: any;
let valorAlvo: Vetor | Array<any>;
let valorIndice: any;

if (alvo.hasOwnProperty('valor')) {
Expand All @@ -163,7 +165,12 @@ export async function atribuirVariavel(
const subtipo = String(alvo.tipo).replace('[]', '');
const valorResolvido: any = converterValor(valor, subtipo);

valorAlvo[valorIndice] = valorResolvido;
if (valorAlvo instanceof Vetor) {
valorAlvo.valores[valorIndice] = valorResolvido;
} else {
valorAlvo[valorIndice] = valorResolvido;
}

return;
}

Expand Down Expand Up @@ -233,6 +240,148 @@ function verificarOperandosNumeros(
throw new ErroEmTempoDeExecucao(operador, 'Operadores precisam ser números.', operador.linha);
}

export async function visitarExpressaoAcessoIndiceVariavel(
interpretador: InterpretadorVisuAlgInterface,
expressao: AcessoIndiceVariavel | any
): Promise<any> {
const promises = await Promise.all([
interpretador.avaliar(expressao.entidadeChamada),
interpretador.avaliar(expressao.indice)
]);

const variavelObjeto: VariavelInterface = promises[0];
const indice = promises[1];

const objeto = variavelObjeto.hasOwnProperty('valor') ? variavelObjeto.valor : variavelObjeto;
let valorIndice = indice.hasOwnProperty('valor') ? indice.valor : indice;

if (Array.isArray(objeto)) {
if (!Number.isInteger(valorIndice)) {
return Promise.reject(
new ErroEmTempoDeExecucao(
expressao.simboloFechamento,
'Somente inteiros podem ser usados para indexar um vetor.',
expressao.linha
)
);
}

if (valorIndice < 0 && objeto.length !== 0) {
while (valorIndice < 0) {
valorIndice += objeto.length;
}
}

if (valorIndice >= objeto.length) {
return Promise.reject(
new ErroEmTempoDeExecucao(
expressao.simboloFechamento,
'Índice do vetor fora do intervalo.',
expressao.linha
)
);
}

return objeto[valorIndice];
}

if (objeto instanceof Vetor) {
return objeto.valores[valorIndice];
}

if (
objeto.constructor === Object ||
objeto instanceof ObjetoDeleguaClasse ||
objeto instanceof DeleguaFuncao ||
objeto instanceof DeleguaClasse
) {
return objeto[valorIndice] || null;
}

if (typeof objeto === 'string') {
if (!Number.isInteger(valorIndice)) {
return Promise.reject(
new ErroEmTempoDeExecucao(
expressao.simboloFechamento,
'Somente inteiros podem ser usados para indexar um vetor.',
expressao.linha
)
);
}

if (valorIndice < 0 && objeto.length !== 0) {
while (valorIndice < 0) {
valorIndice += objeto.length;
}
}

if (valorIndice >= objeto.length) {
return Promise.reject(
new ErroEmTempoDeExecucao(expressao.simboloFechamento, 'Índice fora do tamanho.', expressao.linha)
);
}

return objeto.charAt(valorIndice);
}

return Promise.reject(
new ErroEmTempoDeExecucao(
expressao.entidadeChamada.nome,
'Somente listas, dicionários, classes e objetos podem ter seus valores indexados.',
expressao.linha
)
);
}

export async function visitarExpressaoAtribuicaoPorIndice(
interpretador: InterpretadorVisuAlgInterface,
expressao: AtribuicaoPorIndice
): Promise<any> {
const promises = await Promise.all([
interpretador.avaliar(expressao.objeto),
interpretador.avaliar(expressao.indice),
interpretador.avaliar(expressao.valor),
]);

let objeto = promises[0];
let indice = promises[1];
const valor = promises[2];

objeto = objeto.hasOwnProperty('valor') ? objeto.valor : objeto;
indice = indice.hasOwnProperty('valor') ? indice.valor : indice;

if (Array.isArray(objeto)) {
if (indice < 0 && objeto.length !== 0) {
while (indice < 0) {
indice += objeto.length;
}
}

while (objeto.length < indice) {
objeto.push(null);
}

objeto[indice] = valor;
} else if (objeto instanceof Vetor) {
objeto.valores[indice] = valor;
} else if (
objeto.constructor === Object ||
objeto instanceof ObjetoDeleguaClasse ||
objeto instanceof DeleguaFuncao ||
objeto instanceof DeleguaClasse
) {
objeto[indice] = valor;
} else {
return Promise.reject(
new ErroEmTempoDeExecucao(
expressao.objeto.nome,
'Somente listas, dicionários, classes e objetos podem ser mudados por sobrescrita.',
expressao.linha
)
);
}
}

/**
* Método de visita de expressão binária.
* Reintroduzido pelas particularidades do VisuAlg.
Expand Down Expand Up @@ -540,22 +689,6 @@ export async function visitarExpressaoAtribuicaoPorIndicesMatriz(
);
}

export function visitarExpressaoDeVariavel(
interpretador: InterpretadorVisuAlgInterface,
expressao: Variavel
): any {
const variavel = (interpretador as any).procurarVariavel(expressao.simbolo);
// Este caso abaixo ocorre quando uma função é chamada sem parâmetros.
if (variavel.tipo === 'função') {
const funcao: DeleguaFuncao = variavel.valor;
if (funcao.declaracao.parametros.length === 0) {
return funcao.chamar(interpretador, []);
}
}

return variavel;
}

async function encontrarLeiaNoAleatorio(
interpretador: InterpretadorVisuAlgInterface,
declaracao: Declaracao,
Expand Down
14 changes: 9 additions & 5 deletions fontes/interpretador/interpretador-visualg-com-depuracao.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as _ from 'lodash';

import { AcessoElementoMatriz, AtribuicaoPorIndicesMatriz, Binario, Construto, FimPara, FormatacaoEscrita, Logico, Variavel } from '@designliquido/delegua/construtos';
import { AcessoElementoMatriz, AcessoIndiceVariavel, AtribuicaoPorIndice, AtribuicaoPorIndicesMatriz, Binario, Construto, FimPara, FormatacaoEscrita, Logico, Variavel } from '@designliquido/delegua/construtos';
import { EscrevaMesmaLinha, Escreva, Fazer, Leia, Const, Para, Bloco, Aleatorio, CabecalhoPrograma, Classe } from '@designliquido/delegua/declaracoes';
import { ContinuarQuebra, Quebra, SustarQuebra } from '@designliquido/delegua/quebras';
import { InterpretadorComDepuracao } from '@designliquido/delegua/interpretador/interpretador-com-depuracao';
Expand Down Expand Up @@ -243,12 +243,16 @@ export class InterpretadorVisuAlgComDepuracao extends InterpretadorComDepuracao
}
}

override async visitarExpressaoBinaria(expressao: Binario | any): Promise<any> {
return comum.visitarExpressaoBinaria(this, expressao);
override async visitarExpressaoAcessoIndiceVariavel(expressao: AcessoIndiceVariavel): Promise<any> {
return comum.visitarExpressaoAcessoIndiceVariavel(this, expressao);
}

override async visitarExpressaoAtribuicaoPorIndice(expressao: AtribuicaoPorIndice): Promise<any> {
return comum.visitarExpressaoAtribuicaoPorIndice(this, expressao);
}

override visitarExpressaoDeVariavel(expressao: Variavel): any {
return comum.visitarExpressaoDeVariavel(this, expressao);
override async visitarExpressaoBinaria(expressao: Binario | any): Promise<any> {
return comum.visitarExpressaoBinaria(this, expressao);
}

override async visitarExpressaoLogica(expressao: Logico): Promise<any> {
Expand Down
14 changes: 9 additions & 5 deletions fontes/interpretador/interpretador-visualg.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AcessoElementoMatriz, AtribuicaoPorIndicesMatriz, Binario, Construto, FimPara, FormatacaoEscrita, Logico, Variavel } from '@designliquido/delegua/construtos';
import { AcessoElementoMatriz, AcessoIndiceVariavel, AtribuicaoPorIndice, AtribuicaoPorIndicesMatriz, Binario, Construto, FimPara, FormatacaoEscrita, Logico, Variavel } from '@designliquido/delegua/construtos';
import { Aleatorio, CabecalhoPrograma, Classe, Const, Escreva, EscrevaMesmaLinha, Fazer, Leia, Para } from '@designliquido/delegua/declaracoes';
import { InterpretadorBase } from '@designliquido/delegua/interpretador';
import { ContinuarQuebra, Quebra, SustarQuebra } from '@designliquido/delegua/quebras';
Expand Down Expand Up @@ -63,6 +63,14 @@ export class InterpretadorVisuAlg extends InterpretadorBase implements Interpret
return await comum.visitarExpressaoAcessoElementoMatriz(this, expressao);
}

override async visitarExpressaoAtribuicaoPorIndice(expressao: AtribuicaoPorIndice): Promise<any> {
return comum.visitarExpressaoAtribuicaoPorIndice(this, expressao);
}

override async visitarExpressaoAcessoIndiceVariavel(expressao: AcessoIndiceVariavel): Promise<any> {
return comum.visitarExpressaoAcessoIndiceVariavel(this, expressao);
}

override async visitarExpressaoAtribuicaoPorIndicesMatriz(expressao: AtribuicaoPorIndicesMatriz): Promise<any> {
return await comum.visitarExpressaoAtribuicaoPorIndicesMatriz(this, expressao);
}
Expand Down Expand Up @@ -222,10 +230,6 @@ export class InterpretadorVisuAlg extends InterpretadorBase implements Interpret
return comum.visitarExpressaoBinaria(this, expressao);
}

override visitarExpressaoDeVariavel(expressao: Variavel): any {
return comum.visitarExpressaoDeVariavel(this, expressao);
}

override async visitarExpressaoLogica(expressao: Logico): Promise<any> {
return comum.visitarExpressaoLogica(this, expressao);
}
Expand Down
2 changes: 1 addition & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default async (): Promise<Config.InitialOptions> => {
coverageReporters: ['json-summary', 'lcov', 'text', 'text-summary'],
moduleNameMapper: {
// Se for utilizar módulos linkados, comentar a linha abaixo:
'@designliquido/delegua/(.*)': '<rootDir>/node_modules/@designliquido/delegua/$1'
'@designliquido/delegua/(.*)': '<rootDir>/node_modules/@designliquido/delegua/dist/$1'
// E descomentar a linha abaixo:
// '@designliquido/delegua/(.*)': '<rootDir>/node_modules/@designliquido/delegua/fontes/$1'
},
Expand Down
1 change: 0 additions & 1 deletion testes/avaliador-sintatico.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ describe('Avaliador sintático', () => {
`Fimregistro`,
`Var`,
` produto: vetor[1..50] de deposito`,
` i: inteiro`,
` maisCaro, maisBarato: deposito`,
` Procedimento lerProdutos()`,
` Inicio`,
Expand Down
Loading

0 comments on commit ce4a738

Please sign in to comment.