So the task is to create our own parser for a expression calculator. For Example:
Input: 3+2*1-6/3
Output: 3
Input: 3++2
Output: Invalid Expression
Input: -5+2
Output: -3
Input: 5--2
Output: 7
The code here solves a part of the problem except that it has a fixed input and negative values cannot be solved, And I'm not quite sure yet if it really does solve the expression with operator precedence.
but I already modified it to get an input expression from the user.
and I've been wondering for hours how to implement the solving for negative values. help anyone?
NO JAVASCRIPT ENGINE PLEASE.
here's the current code
import java.util.*;
public class ExpressionParser
{
// Associativity constants for operators
private static final int LEFT_ASSOC = 0;
private static final int RIGHT_ASSOC = 1;
// Operators
private static final Map<String, int[]> OPERATORS = new HashMap<String, int[]>();
static
{
// Map<"token", []{precendence, associativity}>
OPERATORS.put("+", new int[] { 0, LEFT_ASSOC });
OPERATORS.put("-", new int[] { 0, LEFT_ASSOC });
OPERATORS.put("*", new int[] { 5, LEFT_ASSOC });
OPERATORS.put("/", new int[] { 5, LEFT_ASSOC });
}
// Test if token is an operator
private static boolean isOperator(String token)
{
return OPERATORS.containsKey(token);
}
// Test associativity of operator token
private static boolean isAssociative(String token, int type)
{
if (!isOperator(token))
{
throw new IllegalArgumentException("Invalid token: " + token);
}
if (OPERATORS.get(token)[1] == type) {
return true;
}
return false;
}
// Compare precedence of operators.
private static final int cmpPrecedence(String token1, String token2)
{
if (!isOperator(token1) || !isOperator(token2))
{
throw new IllegalArgumentException("Invalid tokens: " + token1
+ " " + token2);
}
return OPERATORS.get(token1)[0] - OPERATORS.get(token2)[0];
}
// Convert infix expression format into reverse Polish notation
public static String[] expToRPN(String[] inputTokens)
{
ArrayList<String> out = new ArrayList<String>();
Stack<String> stack = new Stack<String>();
// For each token
for (String token : inputTokens)
{
// If token is an operator
if (isOperator(token))
{
// While stack not empty AND stack top element
// is an operator
while (!stack.empty() && isOperator(stack.peek()))
{
if ((isAssociative(token, LEFT_ASSOC) &&
cmpPrecedence(token, stack.peek()) <= 0) ||
(isAssociative(token, RIGHT_ASSOC) &&
cmpPrecedence(token, stack.peek()) < 0))
{
out.add(stack.pop());
continue;
}
break;
}
// Push the new operator on the stack
stack.push(token);
}
// If token is a left bracket '('
else if (token.equals("("))
{
stack.push(token); //
}
// If token is a right bracket ')'
else if (token.equals(")"))
{
while (!stack.empty() && !stack.peek().equals("("))
{
out.add(stack.pop());
}
stack.pop();
}
// If token is a number
else
{
// if(!isOperator(stack.peek())){
// out.add(String.valueOf(token*10));
// }
out.add(token);
}
}
while (!stack.empty())
{
out.add(stack.pop());
}
String[] output = new String[out.size()];
return out.toArray(output);
}
public static double RPNtoDouble(String[] tokens)
{
Stack<String> stack = new Stack<String>();
// For each token
for (String token : tokens) //for each
{
// If the token is a value push it onto the stack
if (!isOperator(token))
{
stack.push(token);
}
else
{
// Token is an operator: pop top two entries
Double d2 = Double.valueOf( stack.pop() );
Double d1 = Double.valueOf( stack.pop() );
//Get the result
Double result = token.compareTo("*") == 0 ? d1 * d2 :
token.compareTo("/") == 0 ? d1 / d2 :
token.compareTo("+") == 0 ? d1 + d2 :
d1 - d2;
// Push result onto stack
stack.push( String.valueOf( result ));
}
}
return Double.valueOf(stack.pop());
}
public static void main(String[] args) throws Exception{
Scanner in = new Scanner(System.in);
String reg = "((?<=[<=|>=|==|\\+|\\*|\\-|<|>|/|=])|(?=[<=|>=|==|\\+|\\*|\\-|<|>|/|=]))";
while(true){
try{
System.out.println("Enter Your Expression");
//String[] input = "( 1 + 2 ) * ( 3 / 4 ) - ( 5 + 6 )".split(" ");
String[] input = in.nextLine() .split(reg);
String[] output = expToRPN(input);
// Build output RPN string minus the commas
System.out.print("Stack: ");
for (String token : output) {
System.out.print("[ ");System.out.print(token + " "); System.out.print("]");
}
System.out.println(" ");
// Feed the RPN string to RPNtoDouble to give result
Double result = RPNtoDouble( output );
System.out.println("Answer= " + result);
}catch (NumberFormatException | EmptyStackException nfe){
System.out.println("INVALID EXPRESSION"); }
}
}
}
UPDATED CODE:
Added: unaryToexp() function.
what I wanted to do was that everytime a " - " occurs, the code treats it as a binary by changing it to " _ " as another operator and this operator solves multiplies thing by -1 (what I wanted first was to add [-1] and [*] to the rpn stack). still got problems here.
compiler says:
Enter Your Expression
-5+3
Stack: [ ][ 5 ][ - ][ 3 ][ + ]
Exception in thread "main" java.lang.NumberFormatException: empty String
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:10 11)
at java.lang.Double.valueOf(Double.java:504)
at ExpressionParser.RPNtoDouble(ExpressionParser.java:160)
at ExpressionParser.main(ExpressionParser.java:194)*
I think it has something to do with the Double d1 = Double.valueOf( stack.pop() ); cause it still pops another two values, where I only need one for a solving a unary operator. any help?
public class ExpressionParser
{
// Associativity constants for operators
private static final int LEFT_ASSOC = 0;
private static final int RIGHT_ASSOC = 1;
// Operators
private static final Map<String, int[]> OPERATORS = new HashMap<String, int[]>();
static
{
// Map<"token", []{precendence, associativity}>
OPERATORS.put("-", new int[] { 0, LEFT_ASSOC });
OPERATORS.put("+", new int[] { 0, LEFT_ASSOC });
OPERATORS.put("*", new int[] { 5, LEFT_ASSOC });
OPERATORS.put("/", new int[] { 5, LEFT_ASSOC });
OPERATORS.put("_", new int[] { 5, RIGHT_ASSOC });
}
// Test if token is an operator
private static boolean isOperator(String token)
{
return OPERATORS.containsKey(token);
}
// Test associativity of operator token
private static boolean isAssociative(String token, int type)
{
if (!isOperator(token))
{
throw new IllegalArgumentException("Invalid token: " + token);
}
if (OPERATORS.get(token)[1] == type) {
return true;
}
return false;
}
// Compare precedence of operators.
private static final int cmpPrecedence(String token1, String token2)
{
if (!isOperator(token1) || !isOperator(token2))
{
throw new IllegalArgumentException("Invalid tokens: " + token1
+ " " + token2);
}
return OPERATORS.get(token1)[0] - OPERATORS.get(token2)[0];
}
// CONVERT UNARY OPERATORS
public static String[] unaryToexp(String[] inputTokens)
{
ArrayList<String> out = new ArrayList<String>();
Stack<String> stack = new Stack<String>();
//if token is an unary minus
for (String token : inputTokens)
{
if( ((token == "-") && (isOperator(stack.peek()) || stack.empty() ))){ //
token = "_";
}
else if (token == "-"){
token = "-";
}
out.add(token);
while (!stack.empty())
{
out.add(stack.pop());
}
}
String[] output = new String[out.size()];
return out.toArray(output);
}
// Convert infix expression format into reverse Polish notation
public static String[] expToRPN(String[] inputTokens)
{
ArrayList<String> out = new ArrayList<String>();
Stack<String> stack = new Stack<String>();
// For each token
for (String token : inputTokens)
{
// If token is an operator
if (isOperator(token))
{
// While stack not empty AND stack top element
// is an operator
while (!stack.empty() && isOperator(stack.peek()))
{
if ((isAssociative(token, LEFT_ASSOC) &&
cmpPrecedence(token, stack.peek()) <= 0) ||
(isAssociative(token, RIGHT_ASSOC) &&
cmpPrecedence(token, stack.peek()) < 0))
{
out.add(stack.pop());
continue;
}
break;
}
// Push the new operator on the stack
stack.push(token);
}
// If token is a left bracket '('
else if (token.equals("("))
{
stack.push(token); //
}
// If token is a right bracket ')'
else if (token.equals(")"))
{
while (!stack.empty() && !stack.peek().equals("("))
{
out.add(stack.pop());
}
stack.pop();
}
// If token is a number
else
{
out.add(token);
}
}
while (!stack.empty())
{
out.add(stack.pop());
}
String[] output = new String[out.size()];
return out.toArray(output);
}
public static double RPNtoDouble(String[] tokens)
{
Stack<String> stack = new Stack<String>();
// For each token
for (String token : tokens)
{
// If the token is a value push it onto the stack
if (!isOperator(token))
{
stack.push(token);
}
else
{
// Token is an operator: pop top two entries
Double d2 = Double.valueOf( stack.pop() );
Double d1 = Double.valueOf( stack.pop() );
//Get the result
Double result = token.compareTo("_") == 0 ? d2 * -1 :
token.compareTo("*") == 0 ? d1 * d2 :
token.compareTo("/") == 0 ? d1 / d2 :
token.compareTo("+") == 0 ? d1 + d2 :
d1 - d2;
// Push result onto stack
stack.push( String.valueOf( result ));
}
}
return Double.valueOf(stack.pop());
}
public static void main(String[] args) throws Exception{
Scanner in = new Scanner(System.in);
String reg = "((?<=[<=|>=|==|\\+|\\*|\\-|\\_|<|>|/|=])|(?=[<=|>=|==|\\+|\\*|\\-|<|>|/|=]))";
while(true){
//try{
System.out.println("Enter Your Expression");
//String[] input = "( 1 + 2 ) * ( 3 / 4 ) - ( 5 + 6 )".split(" ");
String[] input = in.nextLine() .split(reg);
String[] unary = unaryToexp(input); //.split(reg);
String[] output = expToRPN(unary);
// Build output RPN string minus the commas
System.out.print("Stack: ");
for (String token : output) {
System.out.print("[ ");System.out.print(token); System.out.print(" ]");
}
System.out.println(" ");
// Feed the RPN string to RPNtoDouble to give result
Double result = RPNtoDouble( output );
System.out.println("Answer= " + result);
//}catch (){
//System.out.println("INVALID EXPRESSION"); }
}
}
}
Here you are:
private static final ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
public static String eval(String matlab_expression){
if(matlab_expression == null){
return "NULL";
}
String js_parsable_expression = matlab_expression
.replaceAll("\\((\\-?\\d+)\\)\\^(\\-?\\d+)", "(Math.pow($1,$2))")
.replaceAll("(\\d+)\\^(\\-?\\d+)", "Math.pow($1,$2)");
try{
return engine.eval(js_parsable_expression).toString();
}catch(javax.script.ScriptException e1){
return null; // Invalid Expression
}
}
Couldn't you use the javascript scripting engine? (you would need a bit of tweaking for the 5--2 expression) The code below outputs:
3+2*1-6/3 = 3.0
3++2 = Invalid Expression
-5+2 = -3.0
5--2 = 7.0
Code:
public class Test1 {
static ScriptEngine engine;
public static void main(String[] args) throws Exception {
engine = new ScriptEngineManager().getEngineByName("JavaScript");
printValue("3+2*1-6/3");
printValue("3++2");
printValue("-5+2");
printValue("5--2");
}
private static void printValue(String expression) {
String adjustedExpression = expression.replaceAll("--", "- -");
try {
System.out.println(expression + " = " + engine.eval(adjustedExpression));
} catch (ScriptException e) {
System.out.println(expression + " = Invalid Expression");
}
}
}
Rather than re-invent the wheel you could use a parser generator such as JavaCC or antlr, which is specifically designed for this kind of task. This is a nice example of a simple expression parser and evaluator in a couple of dozen lines of JavaCC.
Take a look at some examples and try to find a rule how to distinguish negative values from operators.
A rule like:
if (token is + or -) and next token is a number
and
(the previous token was empty
or the prvious token was ')' or another operator)
then it is a sign to the current value.
You could iterate through your original token list and create a new token list based on this rules.
I have just written such an expression evaluator and have an iterator for tokenizing expressions at hand. plan to publish it after some extensions on GitHub.
EDIT: Here is the iterator, the references and calls should be clear, it is a bit more complex because of support for variables/functions and multi-character operators:
private class Tokenizer implements Iterator<String> {
private int pos = 0;
private String input;
private String previousToken;
public Tokenizer(String input) {
this.input = input;
}
#Override
public boolean hasNext() {
return (pos < input.length());
}
private char peekNextChar() {
if (pos < (input.length() - 1)) {
return input.charAt(pos + 1);
} else {
return 0;
}
}
#Override
public String next() {
StringBuilder token = new StringBuilder();
if (pos >= input.length()) {
return previousToken = null;
}
char ch = input.charAt(pos);
while (Character.isWhitespace(ch) && pos < input.length()) {
ch = input.charAt(++pos);
}
if (Character.isDigit(ch)) {
while ((Character.isDigit(ch) || ch == decimalSeparator)
&& (pos < input.length())) {
token.append(input.charAt(pos++));
ch = pos == input.length() ? 0 : input.charAt(pos);
}
} else if (ch == minusSign
&& Character.isDigit(peekNextChar())
&& ("(".equals(previousToken) || ",".equals(previousToken)
|| previousToken == null || operators
.containsKey(previousToken))) {
token.append(minusSign);
pos++;
token.append(next());
} else if (Character.isLetter(ch)) {
while (Character.isLetter(ch) && (pos < input.length())) {
token.append(input.charAt(pos++));
ch = pos == input.length() ? 0 : input.charAt(pos);
}
} else if (ch == '(' || ch == ')' || ch == ',') {
token.append(ch);
pos++;
} else {
while (!Character.isLetter(ch) && !Character.isDigit(ch)
&& !Character.isWhitespace(ch) && ch != '('
&& ch != ')' && ch != ',' && (pos < input.length())) {
token.append(input.charAt(pos));
pos++;
ch = pos == input.length() ? 0 : input.charAt(pos);
if (ch == minusSign) {
break;
}
}
if (!operators.containsKey(token.toString())) {
throw new ExpressionException("Unknown operator '" + token
+ "' at position " + (pos - token.length() + 1));
}
}
return previousToken = token.toString();
}
#Override
public void remove() {
throw new ExpressionException("remove() not supported");
}
}
Related
Given an arbitrary number of TextField inputs (t1, t2, t3, ...) and a custom boolean string input from a JtextArea, I need to check if lines in a file match the custom boolean expression. It needs to support nested parentheses.
Example:
User enters "str1" into t1 , "str2" into t2, "str3" into t3, "str4" into t4, "str5" into t5.
User enters the following into the JTextArea for the custom boolean:
"not ((t1 and not t3) or (t4 and t2)) or t5"
Then based on these inputs, I must filter a file and return lines in the file that match the custom boolean based on a "contains" relationship (e.g. "t1 and not t3" means a line must contain the string t1 and not contain the string t3).
For example a file with the following two lines:
str 5
str4 str2
The filter would only return str5 because it is the only line that matches the custom boolean.
I am having trouble even getting started. I have tried to think of a recursive solution but couldn't come up with anything. Also I tried non-recursive solutions but can't come up with anything either.
There is also the problem of the end result boolean needing to take in a parameter (each line in the file). I thought of maybe producing a sequence of operations to perform rather than a boolean that somehow takes in a parameter. But I can't figure out how to get this sequence in the first place.
Here is what I have now. It is very bad and I am thinking of scrapping this approach.
public class CustomInputParser {
private ArrayList<String> pairs;
private String inp;
private HashMap<Integer,String> atomMap;
public CustomInputParser() {
this.pairs = null;
this.inp = "";
this.atomMap = new HashMap<Integer,String>();
}
public void findAtoms() {
int i = 0;
for(String s : this.pairs) {
String[] indices = s.split(",");
int begin = Integer.valueOf(indices[0]);
int end = Integer.valueOf(indices[1]);
if(!inp.substring(begin+1, end).contains("(")) {
this.pairs.set(i, this.pairs.get(i) + ",#");
}
i++;
}
}
public void computeAtoms() {
int i = 0;
for(String s : this.pairs) {
if(s.contains(",#")) {
String[] indices = s.split(",");
int begin = Integer.valueOf(indices[0]);
int end = Integer.valueOf(indices[1]);
//this.pairs.set(i,this.pairs.get(i).replace(",a", ""));
this.pairs.set(i, this.pairs.get(i) + ","+inp.substring(begin+1, end));
this.atomMap.put(begin,this.pairs.get(i).split(",")[3]+"#"+String.valueOf(end));
}
i++;
}
System.out.println(this.pairs.toString());
System.out.println(this.atomMap.toString());
}
public void replaceAtoms() {
int i = 0;
for(String s : this.pairs) {
if(!(s.contains("o") || s.contains("a") || s.contains("n"))) {
String[] indices = s.split(",");
int begin = Integer.valueOf(indices[0])+1;
int end = Integer.valueOf(indices[1]);
for(int j = begin; j < end; j++) {
if(inp.charAt(j) == '(') {
if(atomMap.containsKey(j)) {
this.pairs.set(i, this.pairs.get(i) + ","+j+"#"+atomMap.get(j).split("#")[1]+">"+atomMap.get(j).split("#")[0]);
}
else {
this.pairs.set(i,"!"+ this.pairs.get(i));
}
}
}
}
i++;
}
System.out.println(this.pairs.toString());
}
public ArrayList<String> getPairs(String str){
this.inp = str;
ArrayList<String> res = new ArrayList<String>();
char[] arr = str.toCharArray();
Stack<Integer> s = new Stack<Integer>();
for(int i = 0; i < arr.length; i++) {
if(arr[i] == '(') {
s.push(i);
}
if(arr[i] == ')') {
if(s.empty()) {
return null;
}
else {
Integer start = s.pop();
Integer end = Integer.valueOf(i);
res.add(start.toString() + "," + end.toString());
}
}
}
if(!s.empty()) {
return null;
}
this.pairs = res;
return res;
}
public static void main(String[] args) {
String x = "((not t1 and ((not t2 or t4) or (t3 or t4))) or (t5 and not t6)) and t7";
x = x.replace("not", "n").replace("and","a").replace("or", "o").replace("t", "").replace(" ", "");
System.out.println(x);
CustomInputParser c = new CustomInputParser();
System.out.println(c.getPairs(x).toString());
c.findAtoms();
c.computeAtoms();
c.replaceAtoms();
}
}
The first step is to tokenize the input. Define
enum Token {VAR, LP, RP, NOT, AND, OR, END}
LP and RP are parentheses. Now define a tokenizer class that looks something like this:
class Tokenizer {
Tokenizer(String input) {...}
void reset() {...}
Token getNext() {...}
String getVarName() {...}
}
Calling getNext() on your example in a loop should return
LP LP NOT VAR AND LP LP NOT VAR OR VAR RP OR LP VAR OR VAR RP RP RP OR LP VAR AND NOT VAR RP RP AND VAR END
Calling getVarName() immediately after a VAR has been returned by getNext() gives you the name of the variable (e.g. "t42").
There are many ways to implement little scanners like this. You should do this first and make sure it's bulletproof by testing. Trying to build a parser on top of a flaky scanner is torture.
As I said in comments, I'd consider recursive descent parsing. If you have a suitable grammar, writing an RD parser is a very short step as the Dragon Book (also mentioned above) shows.
A reasonable grammar (using tokens as above) is
Expr -> Term AND Term
| Term OR Term
| Term END
Term -> NOT Term
| Opnd
Opnd -> VAR
| LP Expr RP
For example, here is how you'd get started. It shows the first rule converted to a function:
class Evaluator {
final Tokenizer tokenizer = ...; // Contains the expression text.
final Map<String, Boolean> env = ... // Environment: variables to values.
Token lookAhead; // Holds the token we're parsing right now.
Evaluator(Tokenizer tokenizer, Map<String, Boolean> env) { ... }
void advance() {
lookAhead = tokenizer.getNext();
}
boolean expr() {
boolean leftHandSide = term(); // Parse the left hand side recursively.
Token op = lookAhead; // Remember the operator.
if (op == Token.END) return leftHandSide; // Oops. That's all.
advance(); // Skip past the operator.
boolean rightHandSide = term(); // Parse the right hand side recursively.
if (op == Token.AND) return leftHandSide && rightHandSide; // Evaluate!
if (op == Token.OR) return leftHandSide || rightHandSide;
dieWithSyntaxError("Expected op, found " + op);
}
boolean term() {...}
boolean opnd() {...}
}
The environment is used when a VAR is parsed. Its boolean value is env.get(tokenizer.getVarName()).
So to process the file, you'll
For each line
For each variable tX in the expression
See if the line contains the string tX is bound to in its text field.
If so, put the mapping tX -> true in the environment
else put tX -> false
Reset the tokenizer
Call Evaluator.evaluate(tokenizer, environment)
If it returns true, print the line, else skip it.
This is the simplest approach I can think of. About 150 lines. Many optimizations are possible.
Added
Well since I can no longer take away the thrill of discovery, here is my version:
import static java.lang.Character.isDigit;
import static java.lang.Character.isWhitespace;
import java.util.HashMap;
import java.util.Map;
import static java.util.stream.Collectors.toMap;
public class TextExpressionSearch {
enum Token { VAR, LP, RP, NOT, AND, OR, END }
static class Tokenizer {
final String input;
int pos = 0;
String var;
Tokenizer(String input) {
this.input = input;
}
void reset() {
pos = 0;
var = null;
}
String getRead() {
return input.substring(0, pos);
}
Token getNext() {
var = null;
while (pos < input.length() && isWhitespace(input.charAt(pos))) {
++pos;
}
if (pos >= input.length()) {
return Token.END;
}
int start = pos++;
switch (input.charAt(start)) {
case 't':
while (pos < input.length() && isDigit(input.charAt(pos))) {
++pos;
}
var = input.substring(start, pos);
return Token.VAR;
case '(':
return Token.LP;
case ')':
return Token.RP;
case 'n':
if (input.startsWith("ot", pos)) {
pos += 2;
return Token.NOT;
}
break;
case 'a':
if (input.startsWith("nd", pos)) {
pos += 2;
return Token.AND;
}
break;
case 'o':
if (input.startsWith("r", pos)) {
pos += 1;
return Token.OR;
}
break;
}
throw new AssertionError("Can't tokenize: " + input.substring(start));
}
}
static class Evaluator {
final Tokenizer tokenizer;
final Map<String, Boolean> env;
Token lookAhead;
Evaluator(Tokenizer tokenizer, Map<String, Boolean> env) {
this.tokenizer = tokenizer;
this.env = env;
advance();
}
boolean die(String msg) {
throw new AssertionError(msg + "\nRead: " + tokenizer.getRead());
}
void advance() {
lookAhead = tokenizer.getNext();
}
void match(Token token) {
if (lookAhead != token) {
die("Expected " + token + ", found " + lookAhead);
}
advance();
}
boolean evaluate() {
boolean exprVal = expr();
match(Token.END);
return exprVal;
}
boolean expr() {
boolean lhs = negated();
switch (lookAhead) {
case AND:
advance();
return negated() && lhs;
case OR:
advance();
return negated() || lhs;
case END:
return lhs;
}
return die("Expected expr, found " + lookAhead);
}
boolean negated() {
switch (lookAhead) {
case NOT:
advance();
return !negated();
default:
return operand();
}
}
boolean operand() {
switch (lookAhead) {
case VAR:
if (!env.containsKey(tokenizer.var)) {
die("Undefined variable: " + tokenizer.var);
}
boolean varVal = env.get(tokenizer.var);
advance();
return varVal;
case LP:
advance();
boolean exprVal = expr();
match(Token.RP);
return exprVal;
}
return die("Expected operand, found " + lookAhead);
}
}
public static void main(String [] args) {
String expr = "((not t1 and ((not t2 or t4) or (t3 or t4))) or (t5 and not t6)) and t7";
Map<String, String> bindings = new HashMap<>();
bindings.put("t1", "str1");
bindings.put("t2", "str2");
bindings.put("t3", "str3");
bindings.put("t4", "str4");
bindings.put("t5", "str5");
bindings.put("t6", "str6");
bindings.put("t7", "str7");
String [] lines = {"str5 str7", "str4 str2"};
Tokenizer tokenizer = new Tokenizer(expr);
for (String line : lines) {
Map<String, Boolean> env =
bindings.entrySet().stream()
.collect(toMap(e -> e.getKey(), e -> line.contains(e.getValue())));
tokenizer.reset();
if (new Evaluator(tokenizer, env).evaluate()) {
System.out.println(line);
}
}
}
}
You can define a parser that returns a Predicate<String> that tests if a given string satisfies a conditional expression.
static Predicate<String> parse(String s, Map<String, String> map) {
return new Object() {
String[] tokens = Pattern.compile("[()]|[a-z][a-z0-9]*")
.matcher(s).results()
.map(MatchResult::group)
.toArray(String[]::new);
int length = tokens.length;
int index = 0;
String token = get();
String get() {
return token = index < length ? tokens[index++] : null;
}
boolean eat(String expect) {
if (expect.equals(token)) {
get();
return true;
}
return false;
}
Predicate<String> identifier() {
String id = token;
return s -> {
String value = map.get(id);
if (value == null)
throw new RuntimeException(
"identifier '" + id + "' undefined");
return s.contains(value);
};
}
Predicate<String> factor() {
boolean not = false;
Predicate<String> p;
if (eat("not"))
not = true;
switch (token) {
case "(":
get();
p = expression();
if (!eat(")"))
throw new RuntimeException("')' expected");
break;
case ")": case "not": case "and": case "or":
throw new RuntimeException("syntax error at '" + token + "'");
default:
p = identifier();
get();
break;
}
if (not)
p = p.negate();
return p;
}
Predicate<String> term() {
Predicate<String> p = factor();
while (eat("and"))
p = p.and(factor());
return p;
}
Predicate<String> expression() {
Predicate<String> p = term();
while (eat("or"))
p = p.or(term());
return p;
}
Predicate<String> parse() {
Predicate<String> p = expression();
if (token != null)
throw new RuntimeException("extra tokens string");
return p;
}
}.parse();
}
test case:
#Test
public void testParse() {
String s = "not ((t1 and not t3) or (t4 and t2)) or t5";
Map<String, String> map = new HashMap<>(Map.of(
"t1", "str1",
"t2", "str2",
"t3", "str3",
"t4", "str4",
"t5", "str5"));
Predicate<String> p = parse(s, map);
assertTrue(p.test("str5"));
assertTrue(p.test("str3"));
assertTrue(p.test("str1 str3"));
assertFalse(p.test("str1"));
assertFalse(p.test("str2 str4"));
// you can change value of variables.
assertFalse(p.test("str1 FOO"));
map.put("t5", "FOO");
assertTrue(p.test("str1 FOO"));
}
syntax:
expression = term { "or" term }
term = factor { "and" factor }
factor = [ "not" ] ( "(" expression ")" | identifier )
identifier = letter { letter | digit }
letter = "a" | "b" | ... | "z"
digit = "0" | "1" | ... | "9"
For posterity, here is my shunting yard solution which includes input validation:
public class CustomInputParser {
private Stack<Character> ops;
private LinkedList<Character> postFix;
private HashMap<Character, Integer> precedence;
private Stack<Boolean> eval;
private HashMap<Integer, String> termsMap;
private String customBool;
public CustomInputParser(HashMap<Integer, String> tMap, String custBool) {
this.ops = new Stack<Character>();
this.eval = new Stack<Boolean>();
this.postFix = new LinkedList<Character>();
this.termsMap = tMap;
this.customBool = custBool;
this.precedence = new HashMap<Character, Integer>();
precedence.put('n', 1);
precedence.put('a', 2);
precedence.put('o',3);
precedence.put('(', 4);
}
private int inToPost() {
char[] expr = convertToArr(this.customBool);
char c;
for(int i = 0; i < expr.length; i++) {
c = expr[i];
if(isOp(c)) {
if(processOp(c) != 0) return -1;
}
else {
if(!Character.isDigit(c)) {
return -1;
}
//I made the mistake of using a queue of Characters for postfix initially
//This only worked for up to 9 operands (multi digit would add mutiple chars to
// postfix for a single reference.
//This loops is a lazy workaround:
// 1. get the string of the reference (e.g. "11")
// 2. convert it to int
// 3. store the char value of the int in postfix
// 4. when evaluating operands in postfix eval, convert char back to int to get the termsMap key
String num = "";
while(i < expr.length) {
if(!Character.isDigit(expr[i])) {
i--;
break;
}
c = expr[i];
num += c;
i++;
}
int j = Integer.valueOf(num);
c = (char) j;
postFix.offer(c); //enqueue
}
}
while(!ops.empty()) {
if(ops.peek() == '(')return -1; //no matching close paren for the open paren
postFix.offer(ops.pop()); //pop and enqueue all remaining ops from stack
}
return 0;
}
private boolean isOp(char c) {
if(c == '(' || c == ')' || c =='n' || c=='a' || c=='o') {
return true;
}
return false;
}
private int processOp(char c) {
if (ops.empty() || c == '(') {
ops.push(c);
}
else if(c == ')') {
while(ops.peek() != '(') {
postFix.offer(ops.pop()); //pop and equeue ops wrapped in parens
if(ops.empty()) return -1; //no matching open paren for the close paren
}
ops.pop(); // don't enqueue open paren, just remove it from stack
}
else if(precedence.get(c) > precedence.get(ops.peek())) {
postFix.offer(ops.pop()); //pop and enqueue the higher precedence op
ops.push(c);
}
else {
ops.push(c);
}
return 0;
}
public boolean evaluate(String s) {
while(!postFix.isEmpty()) {
char c = postFix.poll();
boolean op1, op2;
switch(c) {
case 'n':
op1 = eval.pop();
eval.push(!op1);
break;
case 'a':
op1 = eval.pop();
op2 = eval.pop();
eval.push(op1 && op2);
break;
case 'o':
op1 = eval.pop();
op2 = eval.pop();
eval.push(op1 || op2);
break;
default:
int termKey = (int) c;
String term = this.termsMap.get(termKey);
eval.push(s.contains(String.valueOf(term)));
break;
}
}
return eval.pop();
}
private char[] convertToArr(String x) {
x = x.replace("not", "n").replace("and","a").replace("or", "o").replace("t", "").replace(" ", "");
return x.toCharArray();
}
public static void main(String[] args) {
String customBool = "(t1 and not (t2 and t3)) or (t4 and not t5)";
HashMap<Integer,String> termsMap = new HashMap<Integer, String>();
termsMap.put(1,"str1");
termsMap.put(2,"str2");
termsMap.put(3,"str3");
termsMap.put(4,"str4");
termsMap.put(5,"str5");
CustomInputParser c = new CustomInputParser(termsMap, customBool);
if(c.inToPost() != 0) {
System.out.println("invalid custom boolean");
}
else {
System.out.println(c.evaluate("str1str5"));
}
}
}
I have the shunting yard algorithm that I found online:
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
public class ShuntingYardAlgorithm {
private enum Operator {
ADD(1), SUBTRACT(2), MULTIPLY(3), DIVIDE(4);
final int precedence;
Operator(int p) {
precedence = p;
}
}
private Map<String, Operator> operatorMap = new HashMap<String, Operator>() {/**
*
*/
private static final long serialVersionUID = 1L;
{
put("+", Operator.ADD);
put("-", Operator.SUBTRACT);
put("*", Operator.MULTIPLY);
put("/", Operator.DIVIDE);
}};
private boolean isHigherPrec(String op, String sub) {
return (operatorMap.containsKey(sub) &&
operatorMap.get(sub).precedence >= operatorMap.get(op).precedence);
}
public String shuntingYard(String infix) {
StringBuilder output = new StringBuilder();
Stack<String> stack = new Stack<String>();
for (String token : infix.split("")) {
//operator
if (operatorMap.containsKey(token)) {
while ( ! stack.isEmpty() && isHigherPrec(token, stack.peek())) {
output.append(stack.pop()).append(' ');
}
stack.push(token);
}
//left parenthesis
else if (token.equals("(")) {
stack.push(token);
}
//right parenthesis
else if (token.equals(")")) {
while ( ! stack.peek().equals("(")) {
output.append(stack.pop()).append(' ');
}
stack.pop();
}
//digit
else {
output.append(token).append(' ');
}
}
while ( ! stack.isEmpty()) {
output.append(stack.pop()).append(' ');
}
return output.toString();
}
}
And the evaluator:
private static int evalRPN(String[] tokens) {
int returnValue = 0;
String operators = "+-*/";
Stack<String> stack = new Stack<String>();
for (String t : tokens) {
if (!operators.contains(t)) {
stack.push(t);
} else {
int a = Integer.valueOf(stack.pop());
int b = Integer.valueOf(stack.pop());
switch (t) {
case "+":
stack.push(String.valueOf(a + b));
break;
case "-":
stack.push(String.valueOf(b - a));
break;
case "*":
stack.push(String.valueOf(a * b));
break;
case "/":
stack.push(String.valueOf(b / a));
break;
}
}
}
returnValue = Integer.valueOf(stack.pop());
return returnValue;
}
And they work good so far but I have a problem with the evaluation where the delimiter is split by "", which does not allow two digit numbers, such as 23, or above. What can you suggest that I can do to improve the evaluation method?
String output = new ShuntingYardAlgorithm().shuntingYard(algExp);
algExp = output.replaceAll(" ", "");
String[] outputArray = algExp.split("");
return evalRPN(outputArray);
Such as I input: 256+3
result: 2 5 6 3 +
Evaluation: 6 + 3 = 9, ignores 2 and 5
Your shuntingYard function is discarding the contents of output when an operator or a parenthesis is encountered.
You need to add checks for contents of output before processing the current character.
if (operatorMap.containsKey(token)) {
// TODO: Check output here first, and create a token as necessary
while ( ! stack.isEmpty() && isHigherPrec(token, stack.peek())) {
output.append(stack.pop()).append(' ');
}
stack.push(token);
}
//left parenthesis
else if (token.equals("(")) {
// TODO: Check output here first, and create a token as necessary
stack.push(token);
}
//right parenthesis
else if (token.equals(")")) {
// TODO: Check output here first, and create a token as necessary
while ( ! stack.peek().equals("(")) {
output.append(stack.pop()).append(' ');
}
stack.pop();
}
Also, splitting using the empty string is equivalent to just iterating the String one character at a time. Iterating infix using toCharArray() might be more readable
for (char c : infix.toCharArray())
I need to write a Java program that takes from the standard input a valid Right Parenthesized Infix Expression (RPIE) and outputs the equivalent Full Parenthesized Infix Expression (FPIE). For example, if the input is: a+20)/b-c)53.4-d))) , the output should be ((a+20)/((b-c)(53.4-d))).
I have tried to implement as follows but did not reach the solution. Could anyone help me?
import java.util.Scanner;
import java.util.Stack;
public class ParenthesisCreator {
static private String expression;
private Stack<Character> stack = new Stack<Character>();
public ParenthesisCreator(String input) {
expression = input;
}
public String rtParenthesisInfixToFullParenthesis() {
String postfixString = "";
for (int index = 0; index < expression.length(); ++index) {
char value = expression.charAt(index);
if (value == ')') {
stack.push(')');
stack.push('(');
Character oper = stack.peek();
while (!stack.isEmpty()) {
stack.pop();
postfixString += oper.charValue();
if (!stack.isEmpty())
oper = stack.peek();
}
} else {
postfixString += value;
}
}
return postfixString;
}
public static void main(String[] args) {
System.out.println("Type an expression written in right parenthesized infix: ");
Scanner input = new Scanner(System.in);
String expression = input.next();
// Input: a+20)/b-c)*53.4-d)))
// Desired output is: ((a+20)/((b-c)*(53.4-d)))
ParenthesisCreator convert = new ParenthesisCreator(expression);
System.out.println("This expression writtien in full parenthesized is: \n" + convert.rtParenthesisInfixToFullParenthesis());
}
}
public final class ParenthesisCreator implements Function<String, String> {
private final IntPredicate isOperator;
public ParenthesisCreator() {
this(ch -> ch == '/' || ch == '*' || ch == '+' || ch == '-');
}
public ParenthesisCreator(IntPredicate isOperator) {
this.isOperator = isOperator;
}
#Override
public String apply(String expr) {
Deque<String> stack = new LinkedList<>();
StringBuilder buf = null;
for (int i = 0; i < expr.length(); i++) {
char ch = expr.charAt(i);
if (ch == ')') {
if (buf != null) {
stack.push(buf.insert(0, '(').append(')').toString());
buf = null;
} else if (stack.size() >= 2) {
String two = stack.pop();
String one = stack.pop();
stack.push('(' + one + two + ')');
} else
throw new IllegalArgumentException();
} else if (isOperator.test(ch) && buf == null && !stack.isEmpty())
stack.push(stack.pop() + ch);
else
(buf = buf == null ? new StringBuilder() : buf).append(ch);
}
return String.join("", stack);
}
}
Demo
System.out.println(new ParenthesisCreator().apply("a+20)/b-c)53.4-d)))")); // ((a+20)/((b-c)(53.4-d)))
public class FixExpressionParentheses {
public String fixExpression(String expression) {
String[] tokenArray = expression.split(" ");
Stack<String> operators = new Stack<>();
Stack<String> operands = new Stack<>();
for (String token: tokenArray) {
switch (token) {
case "+", "-", "*", "/", "sqrt" -> operators.push(token);
case ")" -> {
String operator = operators.pop();
String operandTwo = operands.pop();
String operandOne = operands.pop();
String newToken = "( " + operandOne + " " + operator + " "
+ operandTwo + " )";
operands.push(newToken);
}
default -> operands.push(token);
}
}
return operands.pop();
}
}
I am having problems getting my toString() method to work and print out parenthesis. Within my infix notation. For example, right now if I enter 12+3* it will print out 1 + 2 * 3. I would like it to print out ((1+2) *3).
Also, I would like my expression tree to be built when it contains a space within the input. For example, right now if I enter 12+ it works, but I want to be able to enter 1 2 + and it still work. Any thoughts?
P.S. Ignore my evaluate method I haven't implemented it yet!
// Java program to construct an expression tree
import java.util.EmptyStackException;
import java.util.Scanner;
import java.util.Stack;
import javax.swing.tree.TreeNode;
// Java program for expression tree
class Node {
char ch;
Node left, right;
Node(char item) {
ch = item;
left = right = null;
}
public String toString() {
return (right == null && left == null) ? Character.toString(ch) : "(" + left.toString()+ ch + right.toString() + ")";
}
}
class ExpressionTree {
static boolean isOperator(char c) {
if ( c == '+' ||
c == '-' ||
c == '*' ||
c == '/'
) {
return true;
}
return false;
}
// Utility function to do inorder traversal
public void inorder(Node t) {
if (t != null) {
inorder(t.left);
System.out.print(t.ch + " ");
inorder(t.right);
}
}
// Returns root of constructed tree for given
// postfix expression
Node constructTree(char postfix[]) {
Stack<Node> st = new Stack();
Node t, t1, t2;
for (int i = 0; i < postfix.length; i++) {
// If operand, simply push into stack
if (!isOperator(postfix[i])) {
t = new Node(postfix[i]);
st.push(t);
} else // operator
{
t = new Node(postfix[i]);
// Pop two top nodes
// Store top
t1 = st.pop(); // Remove top
t2 = st.pop();
// make them children
t.right = t1;
t.left = t2;
// System.out.println(t1 + "" + t2);
// Add this subexpression to stack
st.push(t);
}
}
// only element will be root of expression
// tree
t = st.peek();
st.pop();
return t;
}
public static void main(String args[]) {
Scanner input = new Scanner(System.in);
/*boolean keepgoing = true;
while (keepgoing) {
String line = input.nextLine();
if (line.isEmpty()) {
keepgoing = false;
} else {
Double answer = calculate(line);
System.out.println(answer);
}
}*/
ExpressionTree et = new ExpressionTree();
String postfix = input.nextLine();
char[] charArray = postfix.toCharArray();
Node root = et.constructTree(charArray);
System.out.println("infix expression is");
et.inorder(root);
}
public double evaluate(Node ptr)
{
if (ptr.left == null && ptr.right == null)
return toDigit(ptr.ch);
else
{
double result = 0.0;
double left = evaluate(ptr.left);
double right = evaluate(ptr.right);
char operator = ptr.ch;
switch (operator)
{
case '+' : result = left + right; break;
case '-' : result = left - right; break;
case '*' : result = left * right; break;
case '/' : result = left / right; break;
default : result = left + right; break;
}
return result;
}
}
private boolean isDigit(char ch)
{
return ch >= '0' && ch <= '9';
}
private int toDigit(char ch)
{
return ch - '0';
}
}
Why you use inorder()? root.toString() returns exactly what you want, "((1+2)*3)"
Spaces you can skip at start of loop:
for (int i = 0; i < postfix.length; i++) {
if (postfix[i] == ' ')
continue;
...
Change main like this.
Scanner input = new Scanner(System.in);
String postfix = input.nextLine();
char[] charArray = postfix.replace(" ", "").toCharArray();
Node root = constructTree(charArray);
System.out.println("infix expression is");
System.out.println(root);
This question already has answers here:
Handling parenthesis while converting infix expressions to postfix expressions
(2 answers)
Closed 5 years ago.
I have to make a program that changes an expression written in Infix notation to Postfix notation. I am running into a problem when I start using parentheses. For example, when I put in "a + (c - h) / (b * d)" is comes out as "ac+h-b/d*" when it should come out as "a c h - b d * / +." Would really appreciate the help. Thanks.
import java.util.Scanner;
import java.util.Stack;
public class PostfixConverter {
static private String expression;
private Stack<Character> stack = new Stack<Character>();
public PostfixConverter(String infixExpression) {
expression = infixExpression;
}
public String infixToPostfix() {
String postfixString = "";
for (int index = 0; index < expression.length(); ++index) {
char value = expression.charAt(index);
if (value == '(') {
} else if (value == ')') {
Character oper = stack.peek();
while (!(oper.equals('(')) && !(stack.isEmpty())) {
stack.pop();
postfixString += oper.charValue();
}
} else if (value == '+' || value == '-') {
if (stack.isEmpty()) {
stack.push(value);
} else {
Character oper = stack.peek();
while (!(stack.isEmpty() || oper.equals(('(')) || oper.equals((')')))) {
stack.pop();
postfixString += oper.charValue();
}
stack.push(value);
}
} else if (value == '*' || value == '/') {
if (stack.isEmpty()) {
stack.push(value);
} else {
Character oper = stack.peek();
while (!oper.equals(('+')) && !oper.equals(('-')) && !stack.isEmpty()) {
stack.pop();
postfixString += oper.charValue();
}
stack.push(value);
}
} else {
postfixString += value;
}
}
while (!stack.isEmpty()) {
Character oper = stack.peek();
if (!oper.equals(('('))) {
stack.pop();
postfixString += oper.charValue();
}
}
return postfixString;
}
public static void main(String[] args) {
System.out.println("Type an expression written in Infix notation: ");
Scanner input = new Scanner(System.in);
String expression = input.next();
PostfixConverter convert = new PostfixConverter(expression);
System.out.println("This expression writtien in Postfix notation is: \n" + convert.infixToPostfix());
}
}
You provided code is similar to this. But that code also does not work.
I have updated your code and added the comments of the changes.
import java.util.Scanner;
import java.util.Stack;
public class PostfixConverter {
static private String expression;
private Stack<Character> stack = new Stack<Character>();
public PostfixConverter(String infixExpression) {
expression = infixExpression;
}
public String infixToPostfix() {
String postfixString = "";
for (int index = 0; index < expression.length(); ++index) {
char value = expression.charAt(index);
if (value == '(') {
stack.push('('); // Code Added
} else if (value == ')') {
Character oper = stack.peek();
while (!(oper.equals('(')) && !(stack.isEmpty())) {
stack.pop();
postfixString += oper.charValue();
if (!stack.isEmpty()) // Code Added
oper = stack.peek(); // Code Added
}
stack.pop(); // Code Added
} else if (value == '+' || value == '-') {
if (stack.isEmpty()) {
stack.push(value);
} else {
Character oper = stack.peek();
while (!(stack.isEmpty() || oper.equals(('(')) || oper.equals((')')))) {
oper = stack.pop(); // Code Updated
postfixString += oper.charValue();
}
stack.push(value);
}
} else if (value == '*' || value == '/') {
if (stack.isEmpty()) {
stack.push(value);
} else {
Character oper = stack.peek();
// while condition updated
while (!oper.equals(('(')) && !oper.equals(('+')) && !oper.equals(('-')) && !stack.isEmpty()) {
oper = stack.pop(); // Code Updated
postfixString += oper.charValue();
}
stack.push(value);
}
} else {
postfixString += value;
}
}
while (!stack.isEmpty()) {
Character oper = stack.peek();
if (!oper.equals(('('))) {
stack.pop();
postfixString += oper.charValue();
}
}
return postfixString;
}
public static void main(String[] args) {
System.out.println("Type an expression written in Infix notation: ");
Scanner input = new Scanner(System.in);
String expression = input.next();
PostfixConverter convert = new PostfixConverter(expression);
System.out.println("This expression writtien in Postfix notation is: \n" + convert.infixToPostfix());
}
}
You need to use associativity and compare operator precedence. I have mostly covered all the operators.
Pre-requiste - Expression should be splitted by space ' '.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
public class Test{
public static final int LEFT_ASSOC = 0;
public static final int RIGHT_ASSOC = 1;
public static final Map<String, int[]> ARITH_OPERATORS = new HashMap<String, int[]>();
public static final Map<String, int[]> REL_OPERATORS = new HashMap<String, int[]>();
public static final Map<String, int[]> LOG_OPERATORS = new HashMap<String, int[]>();
public static final Map<String, int[]> OPERATORS = new HashMap<String, int[]>();
static {
ARITH_OPERATORS.put("+", new int[] { 25, LEFT_ASSOC });
ARITH_OPERATORS.put("-", new int[] { 25, LEFT_ASSOC });
ARITH_OPERATORS.put("*", new int[] { 30, LEFT_ASSOC });
ARITH_OPERATORS.put("/", new int[] { 30, LEFT_ASSOC });
ARITH_OPERATORS.put("%", new int[] { 30, LEFT_ASSOC });
ARITH_OPERATORS.put("^", new int[] { 35, RIGHT_ASSOC });
ARITH_OPERATORS.put("**", new int[] { 30, LEFT_ASSOC });
REL_OPERATORS.put("<", new int[] { 20, LEFT_ASSOC });
REL_OPERATORS.put("<=", new int[] { 20, LEFT_ASSOC });
REL_OPERATORS.put(">", new int[] { 20, LEFT_ASSOC });
REL_OPERATORS.put(">=", new int[] { 20, LEFT_ASSOC });
REL_OPERATORS.put("==", new int[] { 20, LEFT_ASSOC });
REL_OPERATORS.put("!=", new int[] { 20, RIGHT_ASSOC });
LOG_OPERATORS.put("!", new int[] { 15, RIGHT_ASSOC });
LOG_OPERATORS.put("&&", new int[] { 10, LEFT_ASSOC });
LOG_OPERATORS.put("||", new int[] { 5, LEFT_ASSOC });
LOG_OPERATORS.put("EQV", new int[] { 0, LEFT_ASSOC });
LOG_OPERATORS.put("NEQV", new int[] { 0, LEFT_ASSOC });
OPERATORS.putAll(ARITH_OPERATORS);
OPERATORS.putAll(REL_OPERATORS);
OPERATORS.putAll(LOG_OPERATORS);
}
public static void main(String args[]) {
String inputExpression = "a + ( c - h ) / ( b * d )";
String[] input = inputExpression.split(" ");
List<String> output = infixToRPN(input);
System.out.println(output.toString());
}
private static boolean isAssociative(String token, int type) {
if (!isOperator(token)) {
System.out.println("");
}
if (OPERATORS.get(token)[1] == type) {
return true;
}
return false;
}
private static boolean isOperator(String token) {
return OPERATORS.containsKey(token);
}
private static int cmpPrecedence(String token1, String token2) {
if (!isOperator(token1) || !isOperator(token2)) {
System.out.println("");
}
return OPERATORS.get(token1)[0] - OPERATORS.get(token2)[0];
}
private static ArrayList<String> infixToRPN(String[] inputTokens) {
ArrayList<String> out = new ArrayList<String>();
Stack<String> stack = new Stack<String>();
// For all the input tokens [S1] read the next token [S2]
for (String token : inputTokens) {
if (isOperator(token)) {
// If token is an operator (x) [S3]
while (!stack.empty() && isOperator(stack.peek())) {
// [S4]
if ((isAssociative(token, LEFT_ASSOC) && cmpPrecedence(token, stack.peek()) <= 0)
|| (isAssociative(token, RIGHT_ASSOC) && cmpPrecedence(token, stack.peek()) < 0)) {
out.add(stack.pop()); // [S5] [S6]
continue;
}
break;
}
// Push the new operator on the stack [S7]
stack.push(token);
} else if (token.equals("(")) {
stack.push(token); // [S8]
} else if (token.equals(")")) {
// [S9]
while (!stack.empty() && !stack.peek().equals("(")) {
out.add(stack.pop()); // [S10]
}
stack.pop(); // [S11]
} else {
out.add(token); // [S12]
}
}
while (!stack.empty()) {
out.add(stack.pop()); // [S13]
}
return out;
}
}
output
[a, c, h, -, b, d, *, /, +]