Java Expression Parser & Calculator Shunting Yard Algorithm

前端 未结 4 1198
一个人的身影
一个人的身影 2021-01-23 19:29

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

相关标签:
4条回答
  • 2021-01-23 19:57

    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
        }
    }
    
    0 讨论(0)
  • 2021-01-23 20:01

    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.

    0 讨论(0)
  • 2021-01-23 20:06

    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");
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-23 20:22

    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");
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题