From 36f19c62c2266973818908a10d7962f055714db2 Mon Sep 17 00:00:00 2001 From: krolxon Date: Sat, 17 Feb 2024 20:45:28 +0530 Subject: add build script --- .gitignore | 1 + Calculator.java | 30 ------- GFrame.java | 219 ---------------------------------------------------- History.java | 17 ---- Parser.java | 158 ------------------------------------- README.md | 2 +- build.sh | 4 + manifest.mf | 1 + run.sh | 5 +- src/Calculator.java | 30 +++++++ src/GFrame.java | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/History.java | 17 ++++ src/Parser.java | 158 +++++++++++++++++++++++++++++++++++++ 13 files changed, 434 insertions(+), 427 deletions(-) delete mode 100644 Calculator.java delete mode 100644 GFrame.java delete mode 100644 History.java delete mode 100644 Parser.java create mode 100644 build.sh create mode 100644 manifest.mf create mode 100644 src/Calculator.java create mode 100644 src/GFrame.java create mode 100644 src/History.java create mode 100644 src/Parser.java diff --git a/.gitignore b/.gitignore index 36f971e..b86f7b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ bin/* +release/* diff --git a/Calculator.java b/Calculator.java deleted file mode 100644 index c43ca9f..0000000 --- a/Calculator.java +++ /dev/null @@ -1,30 +0,0 @@ -import java.util.Scanner; - -public class Calculator { - public static void main(String[] args) { - // String expr = "(84 / 4 * 3 - 9) * 2 + 1 / 5"; // 108.2 - String expr = new String(" "); - Scanner sc = new Scanner(System.in); - Parser p; - - if (args.length == 0) { - new GFrame("Calculator"); - } else { - if (args[0].equals("-n")) { - System.out.println("Type \"exit\" or Ctrl+C to exit."); - while (true) { - System.out.print(">>> "); - expr = sc.nextLine(); - if (!expr.equals("exit")) { - p = new Parser(expr); - System.out.println(p.eval()); - } else { - sc.close(); - break; - } - } - } else { - } - } - } -} diff --git a/GFrame.java b/GFrame.java deleted file mode 100644 index f03c154..0000000 --- a/GFrame.java +++ /dev/null @@ -1,219 +0,0 @@ -/* -* TODO: -* Item positions are hard coded, make them dynamic -* so that window can be set to resizable. -*/ - -import java.util.Vector; -import javax.swing.*; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Insets; -import java.awt.event.*; -import java.awt.*; -import javax.swing.border.Border; -import java.text.DecimalFormat; - -class RoundBtn implements Border { - private int r; - - RoundBtn(int r) { - this.r = r; - } - - public Insets getBorderInsets(Component c) { - return new Insets(this.r + 1, this.r + 1, this.r + 2, this.r); - } - - public boolean isBorderOpaque() { - return true; - } - - public void paintBorder(Component c, Graphics g, int x, int y, - int width, int height) { - g.drawRoundRect(x, y, width - 1, height - 1, r, r); - } -} - -public class GFrame extends JFrame { - Parser p; - JTextField tf; - - private JButton newButton(String text, int d1, int d2, int d3, int d4) { - JButton b = new JButton(); - // b.setBackground(Color.decode("#F78361")); - b.setBackground(Color.decode("#2B2B2B")); - b.setForeground(Color.decode("#e5e5e5")); - b.setFont((new Font("Times New Roman", Font.PLAIN, 20))); - b.setBounds(d1, d2, d3, d4); - b.setBorder(new RoundBtn(15)); - b.setText(text); - return b; - } - - private void actionAdderForTextField(JButton b, String val) { - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - tf.setText(tf.getText() + val); - } - }); - - } - - GFrame(String title) { - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - this.setTitle(title); - this.getContentPane().setBackground(Color.decode("#111111")); - this.setResizable(false); - - // Text Field - tf = new JTextField(); - tf.setBackground(Color.decode("#2B2B2B")); - tf.setForeground(Color.decode("#e5e5e5")); - tf.setMargin(new Insets(0, 10, 0, 10)); - tf.setBounds(40, 50, 310, 40); - tf.setFont((new Font("Times New Roman", Font.PLAIN, 20))); - tf.setBorder(new RoundBtn(5)); - - // History Text Field - History history = new History(); - JTextArea tHist = new JTextArea(); - tHist.setBounds(380, 50, 200, 240); - tHist.setEditable(false); - tHist.setBorder(new RoundBtn(5)); - tHist.setBackground(Color.decode("#2B2B2B")); - tHist.setForeground(Color.decode("#e5e5e5")); - tHist.setFont((new Font("Times New Roman", Font.PLAIN, 20))); - tHist.setBorder(new RoundBtn(5)); - tHist.setMargin(new Insets(0, 10, 0, 10)); - - // Buttons - JButton bEval = newButton("=", 250, 100, 100, 40); - JButton bClear = newButton("CL", 180, 100, 60, 40); - JButton bClearHistory = newButton("Clear History", 380, 300, 200, 40); - JButton bAdd = newButton("+", 250, 300, 100, 40); - JButton bSub = newButton("-", 250, 250, 100, 40); - JButton bMul = newButton("x", 250, 200, 100, 40); - JButton bDiv = newButton("%", 250, 150, 100, 40); - JButton bCut = newButton("", 180, 300, 60, 40); - bCut.setFont(new Font("JetBrainsMono Nerd Font", Font.PLAIN, 20)); - JButton bRightPar = newButton(")", 110, 100, 60, 40); - JButton bLeftPar = newButton("(", 40, 100, 60, 40); - JButton bDoubleZero = newButton("00", 40, 300, 60, 40); - JButton bZero = newButton("0", 110, 300, 60, 40); - JButton bOne = newButton("1", 40, 250, 60, 40); - JButton bTwo = newButton("2", 110, 250, 60, 40); - JButton bThree = newButton("3", 180, 250, 60, 40); - JButton bFour = newButton("4", 40, 200, 60, 40); - JButton bFive = newButton("5", 110, 200, 60, 40); - JButton bSix = newButton("6", 180, 200, 60, 40); - JButton bSeven = newButton("7", 40, 150, 60, 40); - JButton bEight = newButton("8", 110, 150, 60, 40); - JButton bNine = newButton("9", 180, 150, 60, 40); - - // Add the buttons to the Frame - this.add(bEval); - this.add(bClear); - this.add(bClearHistory); - this.add(bAdd); - this.add(bSub); - this.add(bMul); - this.add(bDiv); - this.add(bCut); - this.add(bRightPar); - this.add(bLeftPar); - this.add(bDoubleZero); - this.add(bZero); - this.add(bOne); - this.add(bTwo); - this.add(bThree); - this.add(bFour); - this.add(bFive); - this.add(bSix); - this.add(bSeven); - this.add(bEight); - this.add(bNine); - this.add(tf); - this.add(tHist); - this.setSize(600, 400); - this.setLayout(null); - this.setVisible(true); - - JLabel label = new JLabel("Enter Expression: "); - label.setFont((new Font("Times New Roman", Font.PLAIN, 20))); - label.setBounds(40, 20, 150, 40); - label.setForeground(Color.decode("#e5e5e5")); - this.add(label); - - // ActionsListeners - bEval.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (tf.getText().length() != 0) { - String expression = tf.getText(); - DecimalFormat format = new DecimalFormat(); - p = new Parser(tf.getText()); - String formattedResult = format.format(p.eval()); - tf.setText(formattedResult); - - tHist.setText(""); - Vector s = history.getHistory(); - s.add(expression + " = " + formattedResult); - for (int i = 0; i < s.size(); i++) { - // tHist.setText(new String(s.get(i).concat(tHist.getText())).concat("\n")); - tHist.append(s.get(i)); - tHist.append("\n"); - } - - } else { - tf.setText("No input"); - } - } - }); - - // Common actions that just appends the symbols to the text field - actionAdderForTextField(bAdd, "+"); - actionAdderForTextField(bSub, "-"); - actionAdderForTextField(bMul, "*"); - actionAdderForTextField(bDiv, "/"); - actionAdderForTextField(bRightPar, ")"); - actionAdderForTextField(bLeftPar, "("); - actionAdderForTextField(bDoubleZero, "00"); - actionAdderForTextField(bZero, "0"); - actionAdderForTextField(bOne, "1"); - actionAdderForTextField(bTwo, "2"); - actionAdderForTextField(bThree, "3"); - actionAdderForTextField(bFour, "4"); - actionAdderForTextField(bFive, "5"); - actionAdderForTextField(bSix, "6"); - actionAdderForTextField(bSeven, "7"); - actionAdderForTextField(bEight, "8"); - actionAdderForTextField(bNine, "9"); - - bClear.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - tf.setText(""); - } - }); - - bClearHistory.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - history.clearHistory(); - tHist.setText(""); - } - }); - - bCut.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - String text = tf.getText(); - tf.setText(text.substring(0, text.length() - 1)); - } - }); - - // bOne.addActionListener(new ActionListener() { - // public void actionPerformed(ActionEvent e) { - // tf.setText(tf.getText() + "1"); - // } - // }); - } -} diff --git a/History.java b/History.java deleted file mode 100644 index ef2807b..0000000 --- a/History.java +++ /dev/null @@ -1,17 +0,0 @@ -import java.util.Vector; - -public class History { - private Vector hist; - - History() { - hist = new Vector(); - } - - public void clearHistory() { - hist.clear(); - } - - public Vector getHistory() { - return hist; - } -} diff --git a/Parser.java b/Parser.java deleted file mode 100644 index bd2cde9..0000000 --- a/Parser.java +++ /dev/null @@ -1,158 +0,0 @@ -/* Todo -* - Add support for trigonometric functions -* - Remove all edge cases -*/ - -import java.util.Stack; -import java.util.StringJoiner; - -public class Parser { - private String expr; - private String postfix; - private Stack operatorStack; - - public Parser(String infixExpr) { - expr = infixExpr.trim().replaceAll("\\s", ""); // remove whitespaces - postfix = toPostFix(); - } - - private boolean isOperand(char c) { - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return true; - default: - return false; - } - } - - private boolean isOperand(String c) { - return isOperand(c.charAt(0)); - } - - private int getPresedence(char c) { - switch (c) { - case '^': - return 3; - case '/': - return 2; - case '*': - return 2; - case '+': - return 1; - case '-': - return 1; - default: - return -1; - } - } - - private boolean hasLeftAssociativity(char c) { - if (c == '^') { - return false; - } else { - return true; - } - } - - private String toPostFix() { - operatorStack = new Stack<>(); - StringJoiner output = new StringJoiner(" "); - StringBuilder operand = new StringBuilder(); - char[] infix = expr.replaceAll(" ", "").toCharArray(); - - // Main Expression Iteration - for (int i = 0; i < infix.length; i++) { - char c = infix[i]; - - // Keep appending digits to the operand StringBuilder and skip iterations till - // we find a operator. - if (isOperand(c)) { - operand.append(infix[i]); - if (i < infix.length - 1) { - continue; - } - } - - if (operand.length() > 0) { - output.add(operand.toString()); - operand = new StringBuilder(); - } - - if (c == '(') { - operatorStack.push(c); - } else if (c == ')') { - while (!operatorStack.isEmpty() && operatorStack.peek() != '(') { - output.add(operatorStack.pop().toString()); - } - operatorStack.pop(); - } else { - // Manage precedence order of operatorStack operators - while (!operatorStack.isEmpty() && getPresedence(c) <= getPresedence((char) operatorStack.peek()) - && hasLeftAssociativity(c)) { - output.add(operatorStack.pop().toString()); - } - - // For whatever reason, the last digit gets added to the operatorStack, leading - // to a duplicate of last digit in the expression - // To fix this, we again make sure that the current char is not a operand. - if (!isOperand(c)) { - operatorStack.push(c); - } - } - } - - // pop all the operators left in the operatorStack after the loop is done. - while (!operatorStack.isEmpty()) { - if (operatorStack.peek() == '(') { - return "This expression is invalid"; - } - output.add(operatorStack.pop().toString()); - } - - return output.toString(); - } - - private Double evaluate(String operator, Double op1, Double op2) { - switch (operator.charAt(0)) { - case '+': - return op2 + op1; - case '-': - return op1 - op2; - case '*': - return op2 * op1; - case '/': - return op1 / op2; - case '^': - return (Math.pow(op2, op1)); - default: - return 0.0; - } - } - - public double eval() { - Stack stack = new Stack(); - for (String c : postfix.split(" ")) { - if (isOperand(c)) { - stack.push(Double.parseDouble(c)); - } else { - Double op1 = (Double) (stack.pop()); - Double op2 = (Double) (stack.pop()); - stack.push(evaluate(c, op2, op1)); - } - } - return stack.pop(); - } - - public String getPostfix() { - return postfix; - } -} diff --git a/README.md b/README.md index 6f295d5..6253709 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Simple GUI calculator with complex infix expression evaluation made in Java usin ```bash $ git clone --depth=1 https://github.com/krolyxon/JavaCalc --branch=master $ cd JavaCalc - $ ./run.sh + $ ./run.sh OR ./build.sh ``` ## JavaCalc in action diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..3a9aea7 --- /dev/null +++ b/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh +mkdir -p release +javac -d ./bin src/*.java +jar cvmf manifest.mf ./release/JavaCalc.jar -C ./bin . diff --git a/manifest.mf b/manifest.mf new file mode 100644 index 0000000..435cb75 --- /dev/null +++ b/manifest.mf @@ -0,0 +1 @@ +Main-Class: Calculator diff --git a/run.sh b/run.sh index bc08127..f9e3394 100644 --- a/run.sh +++ b/run.sh @@ -1,5 +1,6 @@ #!/bin/sh mkdir -p ./bin ClASSNAME="Calculator" -javac -d ./bin/ "./$ClASSNAME.java" -java -cp ./bin/ "$ClASSNAME" $* +cd src +javac -d ../bin/ "./$ClASSNAME.java" +java -cp ../bin/ "$ClASSNAME" $* diff --git a/src/Calculator.java b/src/Calculator.java new file mode 100644 index 0000000..c43ca9f --- /dev/null +++ b/src/Calculator.java @@ -0,0 +1,30 @@ +import java.util.Scanner; + +public class Calculator { + public static void main(String[] args) { + // String expr = "(84 / 4 * 3 - 9) * 2 + 1 / 5"; // 108.2 + String expr = new String(" "); + Scanner sc = new Scanner(System.in); + Parser p; + + if (args.length == 0) { + new GFrame("Calculator"); + } else { + if (args[0].equals("-n")) { + System.out.println("Type \"exit\" or Ctrl+C to exit."); + while (true) { + System.out.print(">>> "); + expr = sc.nextLine(); + if (!expr.equals("exit")) { + p = new Parser(expr); + System.out.println(p.eval()); + } else { + sc.close(); + break; + } + } + } else { + } + } + } +} diff --git a/src/GFrame.java b/src/GFrame.java new file mode 100644 index 0000000..f03c154 --- /dev/null +++ b/src/GFrame.java @@ -0,0 +1,219 @@ +/* +* TODO: +* Item positions are hard coded, make them dynamic +* so that window can be set to resizable. +*/ + +import java.util.Vector; +import javax.swing.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Insets; +import java.awt.event.*; +import java.awt.*; +import javax.swing.border.Border; +import java.text.DecimalFormat; + +class RoundBtn implements Border { + private int r; + + RoundBtn(int r) { + this.r = r; + } + + public Insets getBorderInsets(Component c) { + return new Insets(this.r + 1, this.r + 1, this.r + 2, this.r); + } + + public boolean isBorderOpaque() { + return true; + } + + public void paintBorder(Component c, Graphics g, int x, int y, + int width, int height) { + g.drawRoundRect(x, y, width - 1, height - 1, r, r); + } +} + +public class GFrame extends JFrame { + Parser p; + JTextField tf; + + private JButton newButton(String text, int d1, int d2, int d3, int d4) { + JButton b = new JButton(); + // b.setBackground(Color.decode("#F78361")); + b.setBackground(Color.decode("#2B2B2B")); + b.setForeground(Color.decode("#e5e5e5")); + b.setFont((new Font("Times New Roman", Font.PLAIN, 20))); + b.setBounds(d1, d2, d3, d4); + b.setBorder(new RoundBtn(15)); + b.setText(text); + return b; + } + + private void actionAdderForTextField(JButton b, String val) { + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + tf.setText(tf.getText() + val); + } + }); + + } + + GFrame(String title) { + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setTitle(title); + this.getContentPane().setBackground(Color.decode("#111111")); + this.setResizable(false); + + // Text Field + tf = new JTextField(); + tf.setBackground(Color.decode("#2B2B2B")); + tf.setForeground(Color.decode("#e5e5e5")); + tf.setMargin(new Insets(0, 10, 0, 10)); + tf.setBounds(40, 50, 310, 40); + tf.setFont((new Font("Times New Roman", Font.PLAIN, 20))); + tf.setBorder(new RoundBtn(5)); + + // History Text Field + History history = new History(); + JTextArea tHist = new JTextArea(); + tHist.setBounds(380, 50, 200, 240); + tHist.setEditable(false); + tHist.setBorder(new RoundBtn(5)); + tHist.setBackground(Color.decode("#2B2B2B")); + tHist.setForeground(Color.decode("#e5e5e5")); + tHist.setFont((new Font("Times New Roman", Font.PLAIN, 20))); + tHist.setBorder(new RoundBtn(5)); + tHist.setMargin(new Insets(0, 10, 0, 10)); + + // Buttons + JButton bEval = newButton("=", 250, 100, 100, 40); + JButton bClear = newButton("CL", 180, 100, 60, 40); + JButton bClearHistory = newButton("Clear History", 380, 300, 200, 40); + JButton bAdd = newButton("+", 250, 300, 100, 40); + JButton bSub = newButton("-", 250, 250, 100, 40); + JButton bMul = newButton("x", 250, 200, 100, 40); + JButton bDiv = newButton("%", 250, 150, 100, 40); + JButton bCut = newButton("", 180, 300, 60, 40); + bCut.setFont(new Font("JetBrainsMono Nerd Font", Font.PLAIN, 20)); + JButton bRightPar = newButton(")", 110, 100, 60, 40); + JButton bLeftPar = newButton("(", 40, 100, 60, 40); + JButton bDoubleZero = newButton("00", 40, 300, 60, 40); + JButton bZero = newButton("0", 110, 300, 60, 40); + JButton bOne = newButton("1", 40, 250, 60, 40); + JButton bTwo = newButton("2", 110, 250, 60, 40); + JButton bThree = newButton("3", 180, 250, 60, 40); + JButton bFour = newButton("4", 40, 200, 60, 40); + JButton bFive = newButton("5", 110, 200, 60, 40); + JButton bSix = newButton("6", 180, 200, 60, 40); + JButton bSeven = newButton("7", 40, 150, 60, 40); + JButton bEight = newButton("8", 110, 150, 60, 40); + JButton bNine = newButton("9", 180, 150, 60, 40); + + // Add the buttons to the Frame + this.add(bEval); + this.add(bClear); + this.add(bClearHistory); + this.add(bAdd); + this.add(bSub); + this.add(bMul); + this.add(bDiv); + this.add(bCut); + this.add(bRightPar); + this.add(bLeftPar); + this.add(bDoubleZero); + this.add(bZero); + this.add(bOne); + this.add(bTwo); + this.add(bThree); + this.add(bFour); + this.add(bFive); + this.add(bSix); + this.add(bSeven); + this.add(bEight); + this.add(bNine); + this.add(tf); + this.add(tHist); + this.setSize(600, 400); + this.setLayout(null); + this.setVisible(true); + + JLabel label = new JLabel("Enter Expression: "); + label.setFont((new Font("Times New Roman", Font.PLAIN, 20))); + label.setBounds(40, 20, 150, 40); + label.setForeground(Color.decode("#e5e5e5")); + this.add(label); + + // ActionsListeners + bEval.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (tf.getText().length() != 0) { + String expression = tf.getText(); + DecimalFormat format = new DecimalFormat(); + p = new Parser(tf.getText()); + String formattedResult = format.format(p.eval()); + tf.setText(formattedResult); + + tHist.setText(""); + Vector s = history.getHistory(); + s.add(expression + " = " + formattedResult); + for (int i = 0; i < s.size(); i++) { + // tHist.setText(new String(s.get(i).concat(tHist.getText())).concat("\n")); + tHist.append(s.get(i)); + tHist.append("\n"); + } + + } else { + tf.setText("No input"); + } + } + }); + + // Common actions that just appends the symbols to the text field + actionAdderForTextField(bAdd, "+"); + actionAdderForTextField(bSub, "-"); + actionAdderForTextField(bMul, "*"); + actionAdderForTextField(bDiv, "/"); + actionAdderForTextField(bRightPar, ")"); + actionAdderForTextField(bLeftPar, "("); + actionAdderForTextField(bDoubleZero, "00"); + actionAdderForTextField(bZero, "0"); + actionAdderForTextField(bOne, "1"); + actionAdderForTextField(bTwo, "2"); + actionAdderForTextField(bThree, "3"); + actionAdderForTextField(bFour, "4"); + actionAdderForTextField(bFive, "5"); + actionAdderForTextField(bSix, "6"); + actionAdderForTextField(bSeven, "7"); + actionAdderForTextField(bEight, "8"); + actionAdderForTextField(bNine, "9"); + + bClear.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + tf.setText(""); + } + }); + + bClearHistory.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + history.clearHistory(); + tHist.setText(""); + } + }); + + bCut.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String text = tf.getText(); + tf.setText(text.substring(0, text.length() - 1)); + } + }); + + // bOne.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // tf.setText(tf.getText() + "1"); + // } + // }); + } +} diff --git a/src/History.java b/src/History.java new file mode 100644 index 0000000..ef2807b --- /dev/null +++ b/src/History.java @@ -0,0 +1,17 @@ +import java.util.Vector; + +public class History { + private Vector hist; + + History() { + hist = new Vector(); + } + + public void clearHistory() { + hist.clear(); + } + + public Vector getHistory() { + return hist; + } +} diff --git a/src/Parser.java b/src/Parser.java new file mode 100644 index 0000000..bd2cde9 --- /dev/null +++ b/src/Parser.java @@ -0,0 +1,158 @@ +/* Todo +* - Add support for trigonometric functions +* - Remove all edge cases +*/ + +import java.util.Stack; +import java.util.StringJoiner; + +public class Parser { + private String expr; + private String postfix; + private Stack operatorStack; + + public Parser(String infixExpr) { + expr = infixExpr.trim().replaceAll("\\s", ""); // remove whitespaces + postfix = toPostFix(); + } + + private boolean isOperand(char c) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return true; + default: + return false; + } + } + + private boolean isOperand(String c) { + return isOperand(c.charAt(0)); + } + + private int getPresedence(char c) { + switch (c) { + case '^': + return 3; + case '/': + return 2; + case '*': + return 2; + case '+': + return 1; + case '-': + return 1; + default: + return -1; + } + } + + private boolean hasLeftAssociativity(char c) { + if (c == '^') { + return false; + } else { + return true; + } + } + + private String toPostFix() { + operatorStack = new Stack<>(); + StringJoiner output = new StringJoiner(" "); + StringBuilder operand = new StringBuilder(); + char[] infix = expr.replaceAll(" ", "").toCharArray(); + + // Main Expression Iteration + for (int i = 0; i < infix.length; i++) { + char c = infix[i]; + + // Keep appending digits to the operand StringBuilder and skip iterations till + // we find a operator. + if (isOperand(c)) { + operand.append(infix[i]); + if (i < infix.length - 1) { + continue; + } + } + + if (operand.length() > 0) { + output.add(operand.toString()); + operand = new StringBuilder(); + } + + if (c == '(') { + operatorStack.push(c); + } else if (c == ')') { + while (!operatorStack.isEmpty() && operatorStack.peek() != '(') { + output.add(operatorStack.pop().toString()); + } + operatorStack.pop(); + } else { + // Manage precedence order of operatorStack operators + while (!operatorStack.isEmpty() && getPresedence(c) <= getPresedence((char) operatorStack.peek()) + && hasLeftAssociativity(c)) { + output.add(operatorStack.pop().toString()); + } + + // For whatever reason, the last digit gets added to the operatorStack, leading + // to a duplicate of last digit in the expression + // To fix this, we again make sure that the current char is not a operand. + if (!isOperand(c)) { + operatorStack.push(c); + } + } + } + + // pop all the operators left in the operatorStack after the loop is done. + while (!operatorStack.isEmpty()) { + if (operatorStack.peek() == '(') { + return "This expression is invalid"; + } + output.add(operatorStack.pop().toString()); + } + + return output.toString(); + } + + private Double evaluate(String operator, Double op1, Double op2) { + switch (operator.charAt(0)) { + case '+': + return op2 + op1; + case '-': + return op1 - op2; + case '*': + return op2 * op1; + case '/': + return op1 / op2; + case '^': + return (Math.pow(op2, op1)); + default: + return 0.0; + } + } + + public double eval() { + Stack stack = new Stack(); + for (String c : postfix.split(" ")) { + if (isOperand(c)) { + stack.push(Double.parseDouble(c)); + } else { + Double op1 = (Double) (stack.pop()); + Double op2 = (Double) (stack.pop()); + stack.push(evaluate(c, op2, op1)); + } + } + return stack.pop(); + } + + public String getPostfix() { + return postfix; + } +} -- cgit v1.2.3