Last active
September 8, 2015 20:42
-
-
Save joaorafaelm/3e9674757bca9a819489 to your computer and use it in GitHub Desktop.
Infix/postfix expression parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Stack; | |
import java.util.StringTokenizer; | |
import java.util.Scanner; | |
import java.util.EmptyStackException; | |
/** | |
* Classe para transformar infix para postfix e aplicar parser RPN. | |
* @author João Rafael | |
* Referências: | |
* [Infix to Postfix] http://people.cs.clemson.edu/~turner/courses/cs102/spring98/section2/assignments/asg4/InfixToPostfix.java | |
* [RPN evaluator] http://rosettacode.org/wiki/Parsing/RPN_calculator_algorithm#Java_2 | |
* [Shunting-yard] https://en.wikipedia.org/wiki/Shunting-yard_algorithm | |
*/ | |
public class RPNEvaluator { | |
/** | |
* Verifica se op1 tem prioridade maior que op2, | |
* onde op1 é um operador a esquerda e op2 um operador a direita. | |
* @param char op1 - (+|=|*|/|^) | |
* @param char op2 - (+|=|*|/|^) | |
* @return bool | |
*/ | |
private boolean lowerPrecedence(char op1, char op2) { | |
switch (op1) { | |
case '+': | |
case '-': return !(op2 == '+' || op2 == '-'); | |
case '*': | |
case '/': return op2 == '^' || op2 == '('; | |
case '^': return op2 == '('; | |
case '(': return true; | |
default: return false; // não deve acontecer | |
} | |
}// fim de lowerPrecedence | |
/** | |
* Converte expressão de infix pra postfix usando somente uma stack | |
* @param {String} infix - Expressão inicial. ex: "2 + 43 * ( 2 / 5)" | |
* @return String | |
*/ | |
public String convertToPostfix(String infix) { | |
Stack<String> operatorStack = new Stack<String>(); | |
StringBuffer postfix = new StringBuffer(infix.length()); | |
/* | |
* Separa a string $infix, | |
* os delimitedores são os operadores e espaço (' '). | |
* | |
* Usei look-ahead e look-behind no regex, | |
* para mander os operadores no array. | |
* | |
* Alternativa para classe StringTokenizer. | |
*/ | |
// StringTokenizer parser = new StringTokenizer(infix, "+-*/^() ", true); | |
String[] parser = infix.split( | |
"(?<=\\+)|(?=\\+)|" + | |
"(?<=\\-)|(?=\\-)|" + | |
"(?<=\\*)|(?=\\*)|" + | |
"(?<=\\/)|(?=\\/)|" + | |
"(?<=\\^)|(?=\\^)|" + | |
"(?<=\\()|(?=\\()|" + | |
"(?<=\\))|(?=\\))|" + | |
"(?<=\\s)|(?=\\s)" | |
); | |
for(int i = 0; i < parser.length; i++){ | |
// while( parser.hasMoreTokens() ){ // StringTokenizer | |
// String token = parser.nextToken(); // StringTokenizer | |
String token = parser[i]; | |
// pula para proximo token se este for vazio ou igual a espaço(' '); | |
if ( | |
token.length() == 0 || | |
token.length() == 1 && token.charAt(0) == ' ' | |
) continue; | |
char c = token.charAt(0); | |
if ( (token.length() == 1) && "+-*/^()".indexOf(c) >= 0 ) { | |
// se o token atual é um operador | |
while ( | |
!operatorStack.empty() && | |
!lowerPrecedence( | |
(operatorStack.peek()).charAt(0), | |
c | |
) | |
){ | |
/* | |
* Operador no stack não tem prioridade menor que token atual (char $c) , | |
* então ele vai antes que este operador. | |
*/ | |
postfix.append(" ").append(operatorStack.pop()); | |
} | |
if ( c == ')' ) { | |
// Appenda na string postfix os operadores na parte entre parenteses | |
String operator = operatorStack.pop(); | |
while ( operator.charAt(0) != '(' ) { | |
postfix.append(" ").append(operator); | |
operator = operatorStack.pop(); | |
} | |
} | |
else operatorStack.push(token); // coloca este operador na stack de operadores | |
} | |
// é um operando | |
else postfix.append(" ").append(token); | |
} | |
// appenda o resto dos operadores na Stack para a (string $postfix). | |
while (!operatorStack.empty()) | |
postfix.append(" ").append( operatorStack.pop() ); | |
// resultado | |
return postfix.toString(); | |
} // fim da convertToPostfix | |
/** | |
* Interpreta notação polonesa reversa | |
* @param {String} expression - interpreta expressão postfix | |
* @return Double | |
*/ | |
private Double evaluateRPN(String expression){ | |
Stack<Double> stack = new Stack<Double>(); | |
for(String token : expression.trim().split("\\s")){ | |
if( "+-*/^()".indexOf(token.charAt(0)) >= 0 ){ | |
// se token for um operador | |
double secondOperand = stack.pop(), | |
firstOperand = stack.pop(); | |
if (token.equals("*")) stack.push(firstOperand * secondOperand); | |
else if (token.equals("/")) stack.push(firstOperand / secondOperand); | |
else if (token.equals("-")) stack.push(firstOperand - secondOperand); | |
else if (token.equals("+")) stack.push(firstOperand + secondOperand); | |
else if (token.equals("^")) stack.push( Math.pow(firstOperand, secondOperand) ); | |
} | |
else stack.push( Double.parseDouble( token ) ); | |
} | |
return stack.pop(); | |
}// fim de evalRPN | |
public static void main(String[] args) { | |
RPNEvaluator expression = new RPNEvaluator(); | |
System.out.print("digite uma expressão infix ou postfix: "); | |
try{ | |
// expressão | |
String postfix = expression.convertToPostfix( | |
(new Scanner(System.in)).nextLine() | |
); | |
System.out.println( | |
"postfix: " + postfix | |
); | |
System.out.println( | |
"rpn eval: " + expression.evaluateRPN( | |
postfix | |
) | |
); | |
}catch(NumberFormatException e){ | |
// gambiarra pra tratar erros | |
System.out.println("caracteres inválidos"); | |
}catch(EmptyStackException e){ | |
// gambiarra pra tratar erros | |
System.out.println("sintaxe da expressão inválida"); | |
}catch(Exception e){ | |
// erro inesperado | |
System.out.println("erro inesperado"); | |
System.out.println(e); | |
} | |
System.exit(0); | |
}// fim de main | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment