package tirateima.controlador;
import java.util.Stack;
/**
* Avalia expressões utilizando o método criado por Jan Lukasienwicz, o qual se
* baseia em transformar uma expressão infixada em uma expressão pós fixada e
* então efetuar o cálculo da expressão nessa forma.
*
* @author Vinícius
*/
public class AvaliadorDeExpressao {
/**
* Avalia uma expressão retornando seu cálculo.
*
* Para isso recebe uma pilha de símbolos que representam a expressão lida
* no roteiro. Copia a pilha antes de computar o valor da expressão para
* caso seja necessário re-executar o comado que chamou a avaliação.
*
* Para tanto:
* 1 Converte a expressão para a forma pós-fixada.
* 2 Calcula o valor da expressão pós-fixada
*
* Exemplo:
* a + b * c - d //expressão original
* abc*+d- // expressão na notação pós fixada
* ar+d- //r <- b*c
* xd- //x <- a+r;
* resultado //resultado <- x-d
*
* Para explicação detalhada do algoritmo, consultar o arquivo
* avaliacao_de_expressoes_em_estrutura_de_dados.pdf
* localizado nas referências da pasta doc.
*
* @param pilhaSimbolos
* @return
*/
public static Object avaliar(Stack<Object> pilhaSimbolos) {
Stack<Object> pilhaAuxiliar = new Stack<Object>();
copiaPilha(pilhaAuxiliar,pilhaSimbolos);
converteExpressaoParaPosFixa(pilhaAuxiliar);
return calculaValorExpressaoPosFixa(pilhaAuxiliar);
}
/**
* Converte uma expressão para pós-fixada.
*
* @param pilhaSimbolos
*/
private static void converteExpressaoParaPosFixa(Stack<Object> pilhaSimbolos) {
invertePilha(pilhaSimbolos);
Stack<Operador> pilhaEsperaOperadores = new Stack<Operador>();
Stack<Object> pilhaSaida = new Stack<Object>();
//para cada elemento
Object elemento;
while(!pilhaSimbolos.empty()){
elemento = pilhaSimbolos.pop();
//caso o elemento seja um operador
if(elemento instanceof Operador){
//se for um parênteses de abertura
if((Operador)elemento == Operador.OPEN_PAR){
//empilha-o na pilha de operadores
pilhaEsperaOperadores.push((Operador)elemento);
}
//se for um parênteses de fechamento
else if((Operador)elemento == Operador.CLOSE_PAR){
/*remove os símbolos da pilha de operadores e os copia para a
* saída até encontrar um parênteses de abertura
*/
while(pilhaEsperaOperadores.peek() != Operador.OPEN_PAR){
pilhaSaida.push(pilhaEsperaOperadores.pop());
}
//tira o parênteses de abertura encontrado da pilha
pilhaEsperaOperadores.pop();
} else {
/*enquanto a pilha de operadores estiver vazia e sua
* precedência for menor que os mais recentemente acessados*/
while(!pilhaEsperaOperadores.empty() &&
(pilhaEsperaOperadores.peek().getPrecedencia() >=
((Operador)elemento).getPrecedencia())){
pilhaSaida.push(pilhaEsperaOperadores.pop());
}
//empilhe o operador encontrado
pilhaEsperaOperadores.push((Operador)elemento);
}
}
//caso o elemento seja um operando
else{
//empilha-o
pilhaSaida.push(elemento);
}
}
//coloca os operadores restantes na pilha de saída
while(!pilhaEsperaOperadores.empty()){
pilhaSaida.push(pilhaEsperaOperadores.pop());
}
//atualiza a pilha de símbolos com sua versão pós fixada
copiaPilha(pilhaSimbolos, pilhaSaida);
}
/**
* Calcula o valor da expressão pós-fixada
* @param pilhaSimbolos
* @return
*/
private static Object calculaValorExpressaoPosFixa(
Stack<Object> pilhaSimbolos) {
invertePilha(pilhaSimbolos);
Stack<Object> pilhaAuxiliar = new Stack<Object>();
Object operandoEsquerda, operandoDireita, resultado;
//para cada elemento da expressão, verifica seu tipo
while(!pilhaSimbolos.empty()){
Object elemento = pilhaSimbolos.pop();
//se for um operando
if(!(elemento instanceof Operador)){
//empilha-o na pilha temporária
pilhaAuxiliar.push(elemento);
}
//se for um operador
else{
//desempilha o(s) operando(s) e calcular o valor
operandoDireita = pilhaAuxiliar.pop();
if(!isOperadorUnario((Operador)elemento)){
operandoEsquerda = pilhaAuxiliar.pop();
resultado = opera(operandoEsquerda,(Operador)elemento,operandoDireita);
}
else{
resultado = opera(null,(Operador)elemento,operandoDireita);
}
pilhaAuxiliar.push(resultado);
}
}
//o valor que sobrou na pilha é o resultado da expressão
return pilhaAuxiliar.pop();
}
/**
* Opera um ou dois elementos de acordo coma a operação passada.
*
* @param operandoEsquerda
* @param elemento
* @param operandoDireita
* @return
*/
private static Object opera(Object operandoEsquerda, Operador operador,
Object operandoDireita) {
switch (operador) {
case NOT_OP:
return (!(Boolean)operandoDireita);
case MULT_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Integer)operandoEsquerda * (Integer)operandoDireita;
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return (Double) operandoEsquerda * (Double)operandoDireita;
case DIV_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Integer)operandoEsquerda / (Integer)operandoDireita;
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return Math.round(((Double) operandoEsquerda / (Double)operandoDireita)*1000)/1000d;
case MOD_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Integer)operandoEsquerda % (Integer)operandoDireita;
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return (Double) operandoEsquerda % (Double)operandoDireita;
case ADD_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Integer)operandoEsquerda + (Integer)operandoDireita;
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return (Double) operandoEsquerda + (Double)operandoDireita;
case SUB_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Integer)operandoEsquerda - (Integer)operandoDireita;
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return (Double) operandoEsquerda - (Double)operandoDireita;
case LT_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Boolean)((Integer)operandoEsquerda < (Integer)operandoDireita);
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return (Boolean)((Double) operandoEsquerda < (Double)operandoDireita);
case GT_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Boolean)((Integer)operandoEsquerda > (Integer)operandoDireita);
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return (Boolean)((Double) operandoEsquerda > (Double)operandoDireita);
case LE_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Boolean)((Integer)operandoEsquerda <= (Integer)operandoDireita);
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return (Boolean)((Double) operandoEsquerda <= (Double)operandoDireita);
case GE_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Boolean)((Integer)operandoEsquerda >= (Integer)operandoDireita);
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return (Boolean)((Double) operandoEsquerda >= (Double)operandoDireita);
case EQ_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Boolean)((Integer)operandoEsquerda == (Integer)operandoDireita);
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return (Boolean)((Double) operandoEsquerda == (Double)operandoDireita);
else if(operandoEsquerda instanceof String && operandoDireita instanceof String)
return (Boolean)(((String)operandoEsquerda).equals((String)operandoDireita));
else if(operandoEsquerda instanceof Character && operandoDireita instanceof Character)
return (Boolean)(((Character) operandoEsquerda).equals((Character)operandoDireita));
case NE_OP:
if(operandoEsquerda instanceof Integer && operandoDireita instanceof Integer)
return (Boolean)((Integer)operandoEsquerda != (Integer)operandoDireita);
else if(operandoEsquerda instanceof Double && operandoDireita instanceof Double)
return (Boolean)((Double) operandoEsquerda != (Double)operandoDireita);
else if(operandoEsquerda instanceof String && operandoDireita instanceof String)
return (Boolean)!(((String) operandoEsquerda).equals((String)operandoDireita));
else if(operandoEsquerda instanceof Character && operandoDireita instanceof Character)
return (Boolean)!(((Character) operandoEsquerda).equals((Character)operandoDireita));
case AND_OP:
if(operandoEsquerda instanceof Boolean && operandoDireita instanceof Boolean)
return (Boolean)operandoEsquerda && (Boolean)operandoDireita;
case OR_OP:
if(operandoEsquerda instanceof Boolean || operandoDireita instanceof Boolean)
return (Boolean)operandoEsquerda || (Boolean)operandoDireita;
default:
break;
}
throw new RuntimeException("Problema ao avaliar a expressão. Possivelmente expressão mal formada.");
}
/**
* Copia a pilha de símbolos para não alterar seu valor e poder ser
* re-utilizada.
*
* @param pilhaAuxiliar
* @param pilhaSimbolos
*/
private static void copiaPilha(Stack<Object> pilhaAuxiliar,
Stack<Object> pilhaSimbolos) {
Stack<Object> pilhaIntermediaria = new Stack<Object>();
//Empilha elementos na pilha intermediária
while(!pilhaSimbolos.empty()){
pilhaIntermediaria.push(pilhaSimbolos.pop());
}
//Empilha elementos na pilha auxiliar e na pilha de símbolos de volta.
Object o;
while(!pilhaIntermediaria.empty()){
o = pilhaIntermediaria.pop();
pilhaAuxiliar.push(o);
pilhaSimbolos.push(o);
}
}
/**
* Inverte uma pilha.
*
* @param pilhaSimbolos
*/
private static void invertePilha(Stack<Object> pilhaSimbolos) {
Stack<Object> pilhaIntermediaria = new Stack<Object>();
Stack<Object> pilhaAuxiliar = new Stack<Object>();
while(!pilhaSimbolos.empty()){
pilhaIntermediaria.push(pilhaSimbolos.pop());
}
while(!pilhaIntermediaria.empty()){
pilhaAuxiliar.push(pilhaIntermediaria.pop());
}
while(!pilhaAuxiliar.empty()){
pilhaSimbolos.push(pilhaAuxiliar.pop());
}
}
/**
* Verifica se o operador é unário.
*
* @param elemento
* @return
*/
private static boolean isOperadorUnario(Operador operador) {
if(operador == Operador.NOT_OP){
return true;
}
else{
return false;
}
}
}