HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Infix to Postfix Formula Parser Java

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
parserpostfixinfixjavaformula

Problem

I've written a program that takes a mathematical formula as an user input and converts it from infix to postfix.

It doesn't support negative numbers.

The program doesn't depend on the user having to pay attention on how to use spaces, so '1+23' and '1 + 2 3' are the same. (formulas like 'x^2^3' however must be written like 'x^(2^3)')

It provides support for the following operators: +, -, *, /, ^

Originally I had written a stack class completely myself, but this one (credits for stack class go to users of that post) is much more useful/reusable so I updated mine.

import java.util.EmptyStackException;

public class MyStack {
    private T[] data;
    private int numElements;

    public MyStack(int capacity) {
        data = newArray(capacity);
        numElements = 0;
    }

    private final T[] newArray(int capacity) {
        @SuppressWarnings("unchecked")
        T[] tmp = (T[]) new Object[capacity];
        return tmp;
    }

    private void enlarge() {
        T[] temp = data;
        data = newArray(temp.length * 2);
        System.arraycopy(temp, 0, data, 0, temp.length);
    }

    public void push(T element) {
        if (numElements == data.length) {
            enlarge();
        }
        else {
            data[numElements++] = element;
        }
    }

    public T peek() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }
        return data[numElements - 1];
    }

    public boolean isEmpty() {
        return (numElements == 0);
    }

    public T pop() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }
        return data[--numElements];
    }
}


Here is the InfixToPostfixclass itself:

```
import java.util.Scanner;

public class InfixToPostfix {

//checks whether c is operator
private static boolean isOperator(char c) {
if (c == '+' || c == '-' || c == '*' || c == '/' ||
c == '^' || c == '(' || c == ')') {
return true;

Solution

Standard Stack

Try to use the standard implementation of a stack like java.util.ArrayDeque. You have reinvented the wheel here.

State Pattern

Introduce the state pattern. I here provide you an example.

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;

public class NumberParser {

    public static void main(String[] args) {

        List parse = new NumberParser("   10   22  32  ").parse();

        for (BigInteger bigInteger : parse) {
            System.out.println(bigInteger);
        }

    }

    private List numbers;

    private final StringBuffer stringToParse;
    private final StringBuffer buffer;

    private State state;

    public NumberParser(String string) {
        this.stringToParse = new StringBuffer(string);
        this.buffer = new StringBuffer();
        this.setState(new Unknown());
    }

    private boolean hasCurrentChar() {
        return this.stringToParse.length() > 0;
    }

    private char removeCurrentChar() {
        if (hasCurrentChar()) {
            char ch = this.stringToParse.charAt(0);
            this.stringToParse.deleteCharAt(0);
            return ch;
        } else {
            throw new NoSuchElementException();
        }
    }

    private char currentChar() {
        if (this.stringToParse.length() > 0) {
            return this.stringToParse.charAt(0);
        } else {
            throw new NoSuchElementException();
        }
    }

    private void clearBuffer() {
        buffer.setLength(0);
    }

    private void recognizeNumber() {
        numbers.add(new BigInteger(buffer.toString()));
        clearBuffer();
    }

    public List parse() {

        if (numbers == null) {

            this.numbers = new ArrayList<>();

            while (!(getState() instanceof End)) {
                getState().parse();
            }

        }

        return this.numbers;

    }

    private State getState() {
        return state;
    }

    private void setState(State state) {
        System.out.println(state.getStateInfo());
        this.state = state;
    }

    private interface State {
        public String getStateInfo();
        public void parse();
    }

    private interface End extends State {
    }

    private class Error implements End {

        @Override
        public String getStateInfo() {
            return "Something went wrong ...";
        }

        @Override
        public void parse() {
        }

    }

    private class NoMoreChars implements End {

        @Override
        public String getStateInfo() {
            return "No chars left.";
        }

        @Override
        public void parse() {
        }

    }

    private class RemoveWhiteSpaces implements State {

        @Override
        public String getStateInfo() {
            return "Removing white spaces.";
        }

        @Override
        public void parse() {

            if (hasCurrentChar()) {

                if (Character.isWhitespace(currentChar())) {
                    removeCurrentChar();
                } else {
                    setState(new Unknown());
                }

            } else {
                setState(new NoMoreChars());
            }

        }

    }

    private class Number implements State {

        @Override
        public String getStateInfo() {
            return "Parse digits.";
        }

        @Override
        public void parse() {

            if (hasCurrentChar()) {

                if (Character.isDigit(currentChar())) {
                    buffer.append(currentChar());
                    removeCurrentChar();
                } else {
                    recognizeNumber();
                    setState(new Unknown());
                }

            } else {
                recognizeNumber();
                setState(new NoMoreChars());
            }

        }

    }

    private class Unknown implements State {

        @Override
        public String getStateInfo() {
            return "Search ...";
        }

        @Override
        public void parse() {

            if (hasCurrentChar()) {

                if (Character.isWhitespace(currentChar())) {
                    setState(new RemoveWhiteSpaces());
                } else if (Character.isDigit(currentChar())){
                    setState(new Number());
                } else {
                    setState(new Error());
                }

            } else {
                setState(new NoMoreChars());
            }

        }

    }

}


This parser searches for numbers and returns them. Whitespaces separate numbers as whitespaces are allowed to occur multiple times. If you input alphanumeric characters the machine goes into the Error-State.

The main point is to avoid nested if-statements and have clear responsibilities during parsing. Furthermore your solution lacks extendability. If new requirements occur the state pattern helps you to manage them in a well-defined way.

Fir

Code Snippets

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;

public class NumberParser {


    public static void main(String[] args) {

        List<BigInteger> parse = new NumberParser("   10   22  32  ").parse();

        for (BigInteger bigInteger : parse) {
            System.out.println(bigInteger);
        }

    }


    private List<BigInteger> numbers;


    private final StringBuffer stringToParse;
    private final StringBuffer buffer;


    private State state;


    public NumberParser(String string) {
        this.stringToParse = new StringBuffer(string);
        this.buffer = new StringBuffer();
        this.setState(new Unknown());
    }


    private boolean hasCurrentChar() {
        return this.stringToParse.length() > 0;
    }


    private char removeCurrentChar() {
        if (hasCurrentChar()) {
            char ch = this.stringToParse.charAt(0);
            this.stringToParse.deleteCharAt(0);
            return ch;
        } else {
            throw new NoSuchElementException();
        }
    }

    private char currentChar() {
        if (this.stringToParse.length() > 0) {
            return this.stringToParse.charAt(0);
        } else {
            throw new NoSuchElementException();
        }
    }


    private void clearBuffer() {
        buffer.setLength(0);
    }


    private void recognizeNumber() {
        numbers.add(new BigInteger(buffer.toString()));
        clearBuffer();
    }


    public List<BigInteger> parse() {

        if (numbers == null) {

            this.numbers = new ArrayList<>();

            while (!(getState() instanceof End)) {
                getState().parse();
            }

        }

        return this.numbers;

    }


    private State getState() {
        return state;
    }


    private void setState(State state) {
        System.out.println(state.getStateInfo());
        this.state = state;
    }


    private interface State {
        public String getStateInfo();
        public void parse();
    }


    private interface End extends State {
    }


    private class Error implements End {

        @Override
        public String getStateInfo() {
            return "Something went wrong ...";
        }

        @Override
        public void parse() {
        }

    }


    private class NoMoreChars implements End {

        @Override
        public String getStateInfo() {
            return "No chars left.";
        }

        @Override
        public void parse() {
        }

    }


    private class RemoveWhiteSpaces implements State {

        @Override
        public String getStateInfo() {
            return "Removing white spaces.";
        }

        @Override
        public void parse() {

            if (hasCurrentChar()) {

                if (Character.isWhitespace(currentChar())) {
                    removeCurrentChar();
                } else {
                    setState(new Unknown());
               

Context

StackExchange Code Review Q#112091, answer score: 2

Revisions (0)

No revisions yet.