segunda-feira, 25 de setembro de 2017

Construindo um PARSER em javascript - VI - Código

Neste post vamos fornecer o código da função equate, que resolve a expressão aritmética já transformada em variáveis pela função parseVariables, mostrada no post anterior.

function equate(Expressao){
// É NECESSÁRIO EXECUTAR parseVariables
var posExp = 0;
var aindaTem;
// Operador de expoente
OPERATOR = "Exp";
aindaTem = (0==0);
while ( aindaTem ){
posExp = FUNCOES[OPERATOR](textoConv);
if( posExp >= 0 ){
// Divide operandos à esquerda e à direita do operador
var OpDir = rightOperand(posExp);
var OpEsq = leftOperand(posExp);
// Concretiza a operação
Resultado = Math.pow(varValue(OpEsq),varValue(OpDir));
// Substitui o valor da variável à esquerda
VARIABLES[OpEsq] = Resultado;
// Zera a variável à direita
VARIABLES[OpDir] = 0;
// testa o fim e refaz a expressão
textoConv = updExpression(textoConv, posExp);
} else {
aindaTem = (0==1);
}
}
// Operador de multiplicação
OPERATOR = "Mul";
aindaTem = (0==0);
while ( aindaTem ){
posExp = FUNCOES[OPERATOR](textoConv);
if( posExp >= 0 ){
// Divide operandos à esquerda e à direita do operador
var OpDir = rightOperand(posExp);
var OpEsq = leftOperand(posExp);
// Concretiza a operação
Resultado = varValue(OpEsq)*varValue(OpDir);
// Substitui o valor da variável à esquerda
VARIABLES[OpEsq] = Resultado;
// Zera a variável à direita
VARIABLES[OpDir] = 0;
// testa o fim e refaz a expressão
textoConv = updExpression(textoConv, posExp);
} else {
aindaTem = (0==1);
}
}
// Operador de divisão
OPERATOR = "Div";
aindaTem = (0==0);
while ( aindaTem ){
posExp = FUNCOES[OPERATOR](textoConv);
if( posExp >= 0 ){
// Divide operandos à esquerda e à direita do operador
var OpDir = rightOperand(posExp);
var OpEsq = leftOperand(posExp);
// Concretiza a operação
Resultado = varValue(OpEsq)/varValue(OpDir);
// Substitui o valor da variável à esquerda
VARIABLES[OpEsq] = Resultado;
// Zera a variável à direita
VARIABLES[OpDir] = 0;
// testa o fim e refaz a expressão
textoConv = updExpression(textoConv, posExp);
} else {
aindaTem = (0==1);
}
}
// Operador de soma
OPERATOR = "Sum";
aindaTem = (0==0);
while ( aindaTem ){
posExp = FUNCOES[OPERATOR](textoConv);
if( posExp >= 0 ){
// Divide operandos à esquerda e à direita do operador
var OpDir = rightOperand(posExp);
var OpEsq = leftOperand(posExp);
// Concretiza a operação
Resultado = varValue(OpEsq)+varValue(OpDir);
// Substitui o valor da variável à esquerda
VARIABLES[OpEsq] = Resultado;
// Zera a variável à direita
VARIABLES[OpDir] = 0;
// testa o fim e refaz a expressão
textoConv = updExpression(textoConv, posExp);
} else {
aindaTem = (0==1);
}
}
return textoConv;
}

Esta função tem quatro partes. cada uma trata de um operador ("^", "*", "/" e "+"). E como já ressaltamos nos posts anteriores, a subtração foi transformada em soma, pois nosso parseVariables já providenciou a colocação dos sinais negativos como parte do valor do operando, dentro do array VARIABLES.

Vamos explicar a lógica de um operador, para explicar a resolução das expressões, pois ela valerá para todos. Seja o trecho que executa a multiplicação.

Iniciamos um loop while com a condição aindaTem em verdadeiro. Então chamamos a função redirecionada pelo array FUNCOES, índice "Mul" (Multiplicação). O array FUNCOES direciona esta função para findMultiplication:

// Acha uma operação de MULTIPLICAÇÃO em uma expressão
function findMultiplication(texto){
var posicao = findOperator("*", texto);
return posicao;
}

Esta função chama a função findOperator com o argumento "*" (operador de multiplicação):

// Acha um tipo de operador em um texto ou expressão
function findOperator(op,texto){
return texto.indexOf(op);
}

Que, por sua vez, utiliza a função indexOf do objeto string para achar a posição de um caracter, no caso o operador "*". Se este operador for achado, ocorrerá o tratamento da operação de multiplicação entre os dois operandos adjacentes, expressos por variáveis "Vnn".

Extração dos operandos

Uma vez achado um operador, o programa obtém o operando à sua esquerda (leftOperand) e à sua direita (rightOperand). Vamos mostrar os códigos destas funções:

// Obtém a variável à esquerda DE UMA POSIÇÃO (no caso um operador aritmético)
function leftOperand(pos){
return textoConv.substr(pos-3,3);
}
// Obtém a variável à direita DE UMA POSIÇÃO (no caso um operador aritmético)
function rightOperand(pos){
return textoConv.substr(pos+1,3);
}

Ambas não tem muito mistério. Apenas utilizam o tão conhecido método substr do objeto string.

No entanto, estes são apenas os nomes dos índices das variáveis, é preciso obter o valor de cada um para calcular o Resultado:

Resultado varValue(OpEsq)*varValue(OpDir);

Vejamos como funciona varValue:

// Obtém o valor armazenado em uma variável
function varValue(nomeVar){
return VARIABLES[nomeVar];
}

Ela se refere, através de um índice, ao array VARIABLES, e extrai seu valor. Exemplo:

VARIABLES["V01"] fornece o valor da variável V01, estabelecida pela função parseVariables.

A variável textoConv contém a última transformação da expressão em termos de variáveis, e não mais em termos de numerais.

Tratamento do Resultado

Uma vez obtido o Resultado, a variável do array indexada por OpEsq recebe este resultado, e variável do array indexada por OpDir recebe o valor 0 (zero).

Atualização da expressão

Uma vez obtido o resultado e armazenado no array de variaveis, é preciso refazer a expressão na forma de de variáveis em textoConv. Isto é feito pela função updExpression, cujo código apresentamos aqui:

// Faz o teste de finalização e atualiza a Expressão aritmética
function updExpression(textoConv, posExp){
if( left(textoConv,posExp) == left(right(textoConv,posExp+4),left(textoConv,posExp).length) ) {
textoConv  = left(textoConv,posExp);
} else {
textoConv  = left(textoConv,posExp)+right(textoConv,posExp+4);
}
return textoConv;
}

É feito um teste para verificar se o processamento da expressão, na forma de variáveis, após o ajuste da expressão, depois de uma operação aritmética. Este teste, em suma, averigua se a expressão colhida à esquerda é igual ao fragmento de mesmo comprimento na expressão da direita. Se isto for verdade, textoConv (a próxima expressão a ser interpretada) será apenas o conteúdo do lado esquerdo, senão haverá a concatenação do lado esquerdo com o direito, suprimindo o operador da última operação e o operando da direita (pois ele foi zerado, e o resultado da operação colocado no operando da esquerda).

Quando a interpretação de toda a expressão estiver terminada, a variável de índice "V01" estará armazenando o resultado final do nível corrente de parênteses..

Conclusão

 Nesta fase, as operações aritméticas são efetuadas, e temos o resultado final de uma expressão pura ou extraída do interior de um nível de parênteses.

No próximo post apresentaremos os detalhes da função solvExpression, que trata os níveis de parênteses.













Nenhum comentário:

Postar um comentário