Skip to content

Instantly share code, notes, and snippets.

@joaorafaelm
Last active September 8, 2015 20:42
Show Gist options
  • Save joaorafaelm/3e9674757bca9a819489 to your computer and use it in GitHub Desktop.
Save joaorafaelm/3e9674757bca9a819489 to your computer and use it in GitHub Desktop.
Infix/postfix expression parser
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