sexta-feira, 6 de outubro de 2017

Construindo um PARSER em javascript - IX - Notação científica e Funções

Nos posts anteriores, apresentamos a estrutura e o código de um Parser de expressões aritméticas, com as operações triviais e tratamento dos níveis de parênteses. O leitor pode ter notado que ele possuía algumas deficiências em torno do tratamento de valores cujo resultado atingisse uma ordem de grandeza que tornasse necessário lidar com a notação científica. Nesta versão contemplaremos esta deficiência com uma solução simples. Também incluímos a manipulação de funções com um parâmetro, para que o leitor possa implementar Fatorial, Seno, Cosseno, logarítmo decimal, logarítmo neperiano, função Inversa e tantas outras que necessitar. Também fizemos a generalização do tratamento das funções aritméticas.

Diagrama hierárquico das funções



Em relação ao modelo anterior, a lógica comum de equacionamento de um par de operandos foi concentrada na função equateOper. Para tratamento da notação científica foi criada a função parseCientific. A quebra dos valores da notação científica é feita pela função parseValue. A função parseVariables teve que ser modificada para contemplar a possibilidade de que resultados obtidos pudessem vir no formato de notação científica.

A nova função parseVariables

A diferença para a versão anterior é a preocupação com o sinal e valor do expoente da potência de 10 (dez) do valor, quando este vem na forma de notação científica.


Os números da expressão podem ocorrer no formato mantissa, letra "e" (apontando que em seguida virá um expoente), o sinal do expoente e o próprio expoente.

Esta possibilidade de haver expoente em ALGUNS operandos da expressão traz a necessidade de se ter os dois novos arrays: SIGNAL e EXPONENT. Isto é necessário pelo fato de que ao ocorrer um SINAL de expoente, ainda não temos a ciência do valor do expoente, que só será conhecido no momento em que um novo operador da expressão, ou o fim desta.

Fluxograma básico

Neste fluxograma algumas variáveis temporárias não foram utilizadas, preferindo-se a abordagem esquemática:


E a continuação do label A:


Código da função

function parseVariables(texto){
var tamTexto = texto.length;
var VR = [];
var carctr = "";
var ind = 0;
// Próximo caracter
var proxCarctr = "";
// Anterior caracter
var antCarctr = "";
var S = "";
var SE = "";
var Variable;
var SINAL, PROX_SINAL;
var tmp = "";
var tmpe = "";
var tmpx = "";
SIGNAL["v01"] = "";
EXPONENT["v01"] = 0;
for(i=0;i<tamTexto;i++){
carctr = texto[i];
SINAL = "";
tmp = "";
tmpe = "";
// Caracter ATUAL é o OPERADOR de expoente "e" ou "E"
//EXPONENT[Variable] = 0;
if( isExponent(carctr) ){
// ind não precisa ser incrementado, pois o expoente se refere à uma mantissa anteriormente lida
// próximo caracter é um sinal "+" ou "-" do expoente
i++;
Variable = adjustZero((ind+1).toString());
EXPONENT[Variable] = parseFloat(texto[i]+"1");
i++;
j=0;
SE = "";
for(j=i;j<texto.length;j++){
if( isOperator(texto[j])){
break;
}
SE = SE + texto[j];
}
EXPONENT[Variable] = EXPONENT[Variable] * parseFloat(SE);
} else {
// Caracter ATUAL é OPERADOR
if( isOperator(carctr) ){
// Índice das variáveis armazenadas
ind++;
Variable = adjustZero((ind+1).toString());
SIGNAL[Variable] = "";
EXPONENT[Variable] = 0;
// Verifica se o operador do caracter atual é "-" (subtração)
if( carctr == "-" ){
Variable = adjustZero((ind+1).toString());
SIGNAL[Variable] = "-";
}
// Obtem próximo caracter
proxCarctr = texto[i+1]; //
// Caracter seguinte é OPERADOR também (^-  OU *- OU /-)
Variable = adjustZero((ind+1).toString());
if( proxCarctr == "-" ){
SIGNAL[Variable] = "-";
i++; // OBSERVAR
}
// Vai lidar com o sinal no fim do Parse
tmp = SIGNAL[Variable];
// Alimenta o array VARIABLES **
VARIABLES[ind] = parseFloat(S);
Variable = adjustZero(ind.toString());
VARIABLES[Variable] = parseFloat(S);
// Refaz a expressão
if( carctr == "-" ) { carctr = "+"; } //**
textoConv = textoConv + Variable + carctr;
S = "";
// Númeral, e não operador
} else {
S = S + carctr;
} // fim do if isOperator
}
}
// Lida com o sinal
Variable = adjustZero((ind+1).toString());
tmp = SIGNAL[Variable];
ind++;
// S vem com o resultado
tmpe = parseFloat(S);
if( findExpoent(S) >= 0 ){
VR = parseCientific(S);
Resultado = VR.semExpoente;
tmpx = parseInt(VR.expoente)+EXPONENT[Variable];
tmpe = parseFloat(VR.mantissa).toPrecision(8);
}
VARIABLES[ind] = tmpe; //*
Variable = adjustZero(ind.toString());
VARIABLES[Variable] = tmpe;
textoConv = textoConv + Variable;
showExpression(texto);
S = ""; //**
// Concatena os sinais no array VARIABLES
for(i=1;i<=ind;i++){
Variable = adjustZero(i.toString());
S = SIGNAL[Variable]+VARIABLES[Variable].toString();
VARIABLES[Variable] = parseFloat(S);
}
return tamTexto;
}

Conclusão

Os três arrays, VARIABLES, SIGNAL e EXPONENT, são alimentados a contento, indexados pelos índices "v01", "v02", ..., "vnn". Eles serão aproveitados na próxima etapa, que resolve as operações segundo a sua ordem de prioridade.