java - 如何使用 "varchars,custom functions,numbers and dates"执行(eval)表达式

标签 java eval formula varchar

评估可以包含操作、varchars、数字、自定义函数、日期的自定义表达式的正确方法是什么。

一开始我使用“Udo Klimaschewski”Expression utility

一切都很好,但该实用程序不适用于 varchars,仅适用于数字。 但需要执行的公式也可以包含varchar,预览: =((1=2) and ('abc'= 'abc'))

对于日期,我做了一件棘手的事情,将日期表达式转换为长整型,然后进行比较。 修改后的实用程序的示例代码:

eval()

    public BigDecimal eval() {

    Stack<BigDecimal> stack = new Stack<BigDecimal>();

    for (String token : getRPN()) {
        mylog.pl("Reverse polish notation TOKEN : " + token + " RPN size: " + getRPN().size() );
        if (operators.containsKey(token)) {
            BigDecimal v1 = stack.pop();
            BigDecimal v2 = stack.pop();
            stack.push(operators.get(token).eval(v2, v1));
        } else if (variables.containsKey(token)) {
            stack.push(variables.get(token).round(mc));
        } else if (functions.containsKey(token.toUpperCase())) {
            Function f = functions.get(token.toUpperCase());
            ArrayList<BigDecimal> p = new ArrayList<BigDecimal>(f.getNumParams());
            for (int i = 0; i < f.numParams; i++) {
                p.add(0, stack.pop());
            }
            BigDecimal fResult = f.eval(p);
            stack.push(fResult);
        } else if (isDate(token)) {
            Long date = null;
            try {
                date = SU.sdf.parse(token).getTime();
            } catch (ParseException e) {/* IGNORE! */
            }
            stack.push(new BigDecimal(date, mc));
        } else {
//Here now is error because expresion is varchar not a number 
//java.lang.NumberFormatException
                stack.push(new BigDecimal(token, mc));
            }
        }
        return stack.pop().stripTrailingZeros();
    }

反转符号

    private List<String> getRPN() {
    if (rpn == null) {
        rpn = shuntingYard(this.expression);
    }
    return rpn;
}

调车场

private List<String> shuntingYard(String expression) {
    List<String> outputQueue = new ArrayList<String>();
    Stack<String> stack = new Stack<String>();

    Tokenizer tokenizer = new Tokenizer(expression);

    String lastFunction = null;
    while (tokenizer.hasNext()) {
        String token = tokenizer.next();
        if (isNumber(token)) {
            outputQueue.add(token);                     
        } else if (variables.containsKey(token)) {
            outputQueue.add(token);
        } else if (functions.containsKey(token.toUpperCase())) {
            stack.push(token);
            lastFunction = token;
        } else if (Character.isLetter(token.charAt(0))) {
            if (BusinessStrategy.PREFIX_X.equals(Character.toString(token.charAt(0)))){
                //HERE can catch all varchar's prefix is added before this utility
                outputQueue.add(token);                    
            } else {
                stack.push(token);                    
            }
        } else if (",".equals(token)) {
            while (!stack.isEmpty() && !"(".equals(stack.peek())) {
                outputQueue.add(stack.pop());
            }
            if (stack.isEmpty()) {
                throw new ExpressionException("Parse error for function '"
                        + lastFunction + "'");
            }
        } else if (operators.containsKey(token)) {
            Operator o1 = operators.get(token);
            String token2 = stack.isEmpty() ? null : stack.peek();
            while (operators.containsKey(token2)
                    && ((o1.isLeftAssoc() && o1.getPrecedence() <= operators
                            .get(token2).getPrecedence()) || (o1
                            .getPrecedence() < operators.get(token2)
                            .getPrecedence()))) {
                outputQueue.add(stack.pop());
                token2 = stack.isEmpty() ? null : stack.peek();
            }
            stack.push(token);
        } else if ("(".equals(token)) {
            stack.push(token);
        } else if (")".equals(token)) {
            while (!stack.isEmpty() && !"(".equals(stack.peek())) {
                outputQueue.add(stack.pop());
            }
            if (stack.isEmpty()) {
                throw new RuntimeException("Mismatched parentheses");
            }
            stack.pop();
            if (!stack.isEmpty()
                    && functions.containsKey(stack.peek().toUpperCase())) {
                outputQueue.add(stack.pop());
            }
        }
    }
    while (!stack.isEmpty()) {
        String element = stack.pop();
        if ("(".equals(element) || ")".equals(element)) {
            throw new RuntimeException("Mismatched parentheses");
        }
        if (!operators.containsKey(element)) {
            throw new RuntimeException("Unknown operator or function: "
                    + element);
        }
        outputQueue.add(element);
    }
    return outputQueue;
}

也许有人已经做过类似的事情并且可以提供建议? 谢谢!

编辑1 我像这样编辑了 eval 现在我不知道如何处理自定义函数:(

public Object eval() {

    Stack<Object> stack = new Stack<Object>();

    for (String token : getRPN()) {
        mylog.pl("Reverse polish notation TOKEN : " + token + " RPN size: " + getRPN().size() );
        if (operators.containsKey(token)) {
            Object v1 = stack.pop();
            Object v2 = stack.pop();
            stack.push(operators.get(token).eval(v2, v1));
        } else if (variables.containsKey(token)) {
            stack.push(variables.get(token).round(mc));
        } else if (functions.containsKey(token.toUpperCase())) {
           /* Function f = functions.get(token.toUpperCase());
            ArrayList<BigDecimal> p = new ArrayList<BigDecimal>(f.getNumParams());
            for (int i = 0; i < f.numParams; i++) {
                p.add(0, stack.pop());
            }
            BigDecimal fResult = f.eval(p);
            stack.push(fResult);*/
        } else if (isDate(token)) {
            Long date = null;
            try {
                date = SU.sdf.parse(token).getTime();
            } catch (ParseException e) {/* IGNORE! */
            }
            stack.push(new BigDecimal(date, mc));
        } else {
            //stack.push(new BigDecimal(token, mc));
            if (BusinessStrategy.PREFIX_X.equals(Character.toString(token.charAt(0)))) {
                //Push the string without quotes.
                mylog.pl("VARCHAR PUSH");
                stack.push(token.substring(0, token.length()));
            } else {
                stack.push(new BigDecimal(token, mc));
            }
        }
    }
    return stack.pop();//.stripTrailingZeros();
}

运营商 也许有什么方法可以标准化这段代码?

       addOperator(new Operator("=", 7, false) {
        @Override
        public BigDecimal eval(Object v1, Object v2) {
            if (v1.getClass() == v2.getClass()){
               if (v1 instanceof String){
                   String s1 = (String) v1;
                   String s2 = (String) v2;
                   return s1.equals(s2) == true ? BigDecimal.ONE : BigDecimal.ZERO;
               } else if (v1 instanceof BigDecimal){
                   BigDecimal b1 = (BigDecimal) v1;
                   BigDecimal b2 = (BigDecimal) v2;
                   return b1.compareTo(b2) == 0 ? BigDecimal.ONE : BigDecimal.ZERO;
               }
            } else {
                // TODO Throw something
                mylog.pl("Wrong types");
                return null;
            }
            //How to avoid this return ? 
            return null;
        }
    });
    addOperator(new Operator("==", 7, false) {
        @Override
        public BigDecimal eval(Object v1, Object v2) {
            return operators.get("=").eval(v1, v2);
        }
    });

    addOperator(new Operator("!=", 7, false) {
        @Override
        public BigDecimal eval(Object v1, Object v2) {
            if (v1.getClass() == v2.getClass()){
                if (v1 instanceof String){
                    String s1 = (String) v1;
                    String s2 = (String) v2;
                    return s1.equals(s2) == false ? BigDecimal.ONE : BigDecimal.ZERO;
                } else if (v1 instanceof BigDecimal){
                    BigDecimal b1 = (BigDecimal) v1;
                    BigDecimal b2 = (BigDecimal) v2;
                    return b1.compareTo(b2) != 0 ? BigDecimal.ONE : BigDecimal.ZERO;
                }
             } else {
                 // TODO Throw something
                 mylog.pl("Wrong types");
                 return null;
             }
             //How to avoid this return ? 
             return null;
        }
    });
    addOperator(new Operator("<>", 7, false) {
        @Override
        public BigDecimal eval(Object v1, Object v2) {
            return operators.get("!=").eval(v1, v2);
        }
    });

如何处理这些运算符? 对于 varchars 只需要相等或不需要如何轻松且正常地重写这些?

    addOperator(new Operator("<=", 10, false) {
        @Override
        public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
            return v1.compareTo(v2) <= 0 ? BigDecimal.ONE : BigDecimal.ZERO;
        }
    });

        addOperator(new Operator("and", 4, false) {
        @Override
        public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
            boolean b1 = !v1.equals(BigDecimal.ZERO);
            boolean b2 = !v2.equals(BigDecimal.ZERO);
            return b1 && b2 ? BigDecimal.ONE : BigDecimal.ZERO;
        }
    });

自定义函数

        addFunction(new Function("SalStrLeftX", 2) {
        @Override
        public BigDecimal eval(List<BigDecimal> parameters) {
            Object o = null;
            try {
                Class<?> c = Class.forName("com.disnet.business.Functions");
                Method  method = c.getDeclaredMethod ("SalStrLeftX", String.class, int.class);
                o = method.invoke (c.newInstance(), parameters.get(0).toPlainString(), parameters.get(1).intValueExact());
                System.out.println("Value from SalStrLeftX "+o);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //TODO for Varchar
            return SU.getBigDecimal(o);
        }
    });

最佳答案

首先您需要不止一种类型。您可以引入一个新类 Value,但 Object 也可以。

public Object eval() {

Stack<Object> stack = new Stack<>();


    } else {
        if (token.matches("(?s)'.*'") {
            stack.push(token.substring(1, token.length() - 1);
        } else {
            stack.push(new BigDecimal(token, mc));
        }
    }

推送不带引号的字符串。

然后运算符也必须处理字符串。最简单的是进行运行时转换:

在伪代码中:

Object rhs = stack.pop();
Object lhs = stack.pop();
TypeCasted casted = operator.typeCast(lhs, rhs);
lhs = casted.lhs;
rhs = casted.rhs;
stack.push(operator.eval(lhs, rhs));

最简单的类型转换,即 = 是:

if (lhs.getClass() != rhs.getClass() {
    if (lhs instanceof String) {
        rhs = String.valueOf(rhs);
    }
    ...
}
return new TypeCasted(lhs, rhs);
<小时/>

编辑问题后:

        if (v1.getClass() == v2.getClass()){
           if (v1 instanceof Comparable){
               Comparable b1 = (Comparable) v1;
               Comparable b2 = (Comparable) v2;
               return b1.compareTo(b2) == 0 ? BigDecimal.ONE : BigDecimal.ZERO;
           } else {
               throw new IllegalStateException("Comparable expected instead of: "
                   + v1.getClass().getName());
           }
       } else {

顺便说一句,您可能会引入许多类型:BigDecimal、Integer等,所有实现Number,都可以用作((Number)obj).longValue().

关于java - 如何使用 "varchars,custom functions,numbers and dates"执行(eval)表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25154197/

相关文章:

java - 无法运行程序 "mvn"错误=2,没有这样的文件或目录

java - 如何分离Log4j配置?

javascript - 将字符串中的点从charcode转换为十六进制(javascript)

python - 在 Python 中使用 eval 或 exec 设置全局变量

lambda - Lisp 奇怪的段错误可能是 eval 错误

c++ - 公式序列

java - java中数据库中的语句

java - 源附件不包含文件 SunPKCS11.class 的源

google-sheets - 谷歌表格: Sum if value is round number

r - 从混合模型 (lme4) 公式中提取成分