-->

Java的表达式解析器和计算器调度场算法(Java Expression Parser & Calc

2019-08-03 18:43发布

所以任务是创造我们自己的解析器的表达式计算器。 例如:

输入:3 + 2 * 1-6 / 3输出:3

输入:3 ++ 2输出:无效表达

输入:-5 + 2输出:-3

输入:5--2输出:7

这里的代码解决了,除了它有一个固定的输入负值不能得到解决问题的一部分,我不是很确定但如果它确实解决运算符优先级的表达式。 但我已经修改了它获取用户输入表达式。 我一直在想了好几个小时如何实现负值的解决。 帮助任何人吗?

NO JavaScript引擎PLEASE。

这里是当前的代码

    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"); }         
        }
    }
}  

更新后的代码:补充:unaryToexp()函数。 我想要做的是,每次一个“ - ”时,代码通过改变它把它作为一个二进制“_”作为另一家运营商和该运营商-1解决乘法的事情(我想要的东西首先是添加[ - 1]和[*]至RPN栈)。 仍然有问题在这里。

编译器说:

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)* 

我认为这事做的Double d1 = Double.valueOf( stack.pop() ); 导致它仍然弹出另外两个值,在这里我只需要一个用于求解一元运算符。 任何帮助吗?

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"); }           
        }
    }   
} 

Answer 1:

这个给你:

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
    }
}


Answer 2:

你不能使用JavaScript脚本引擎? (你将需要一个位调整为的5--2表达)下面输出的代码:

3+2*1-6/3 = 3.0
3++2 = Invalid Expression
-5+2 = -3.0
5--2 = 7.0

码:

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");
        }
    }
}


Answer 3:

而不是重新创造,你可以使用一个解析器生成如JavaCC的或ANTLR,这是专门为这样的任务而设计的车轮。 这是一个很好的例子是简单表达式解析器和评估器在几个的JavaCC的十几行的。



Answer 4:

看看一些例子,试图找到一条规则如何从运营商区分负值。 规则,如:

 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.

你可以通过你的原始凭证列表迭代和创建基于此规则的新的令牌列表。 我刚才写这样的表达式求值,并有手头令牌化表达的迭代器。 计划在GitHub上一些扩展之后发布。

编辑:这是迭代器,引用和调用应该是清楚的,那是因为变量/功能和多字符运营商的支持更复杂一点:

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");
    }

}


文章来源: Java Expression Parser & Calculator Shunting Yard Algorithm