package com.servlet.demo.framework.utill;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
/**
* <b>表达式(JPEL)</b></br>
* 使用逆波兰算法,支持:
* <ul>
* <li><b>算数运算表达式:</b><br/>
* 加(+)、减(-)、乘(*)、除(/)、求余(%)、幂(^)运算</li>
* <li><b>关系表达式:</b><br/>
* 等于(==)、不等于(!=)、大于(>)、大于等于(>=)、小于(<)、小于等于(<=)</li>
* <li><b>逻辑表达式:</b><br/>
* 且(&&)、或( || )、非( ! )、true、false</li>
* <li><b>括号优先级表达式:</b><br/>
* 使用“(表达式)”构造,括号里的具有高优先级。</li>
* </ul>
* 不支持三目运算,不支持文本字符串
* @author Yuanqy
*/
public class ElEngine {
private final static HashMap<String, Integer> priority = new HashMap<>();
private static int MIN = 100;
private static String EOD = "#";
private static int getPriority(String key) {
if (priority.get(key) == null) return MIN;
return priority.get(key);
}
/**
* 执行表达式
* @param calc
* @return
*/
public static Object excute(String calc) {
Stack<String> s = createReversePolish(calc);
return calcReversePolish(s);
}
static {
// 加入优先级映射,可继续扩展,级别按百科填写
// priority.put("?:", 13);
priority.put("||", 12);
priority.put("&&", 11);
priority.put("!=", 7);
priority.put("==", 7);
priority.put("<=", 6);
priority.put(">=", 6);
priority.put("<", 6);
priority.put(">", 6);
priority.put("+", 4);
priority.put("-", 4);
priority.put("*", 3);
priority.put("/", 3);
priority.put("%", 3);
priority.put("!", 2);
priority.put("^", 2);// 幂运算,js引擎是不支持这么写的
priority.put("(", 1);
priority.put(")", 1);
}
// == 解析 ========================================================
public static Stack<String> createReversePolish(String source) {
String[] str = editExpress(source);
Stack<String> s1 = new Stack<String>();// 运算符
Stack<String> s2 = new Stack<String>();// 逆波兰
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length; i++) {
String t = str[i];
if (t.equals("")) continue;
if (t.equals(EOD)) {// 结束符
while (true) {
if (s1.size() == 0) break;
s2.push(s1.pop());
}
break;
}
sb.append(t);
if (isNumber(t)) {
// 若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈
if (!isNumber(str[i + 1])) RefreshTemp(s2, sb);
} else {
// 若取出的字符是“(”,则直接送入S1栈顶。
// 若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
if (getPriority(t) == 1) {
if (t.equals("(")) {
spush(s1, s2, sb.deleteCharAt(sb.length() - 1), sb.toString());
s1.push(t);
}
if (t.equals(")")) {
while (true) {
String s1t = s1.pop();
if (s1t.equals("(")) break;
s2.push(s1t);
}
}
sb.setLength(0);// ?
} else if (isNumber(str[i + 1])) {
// 若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级大于S1栈栈顶运算符优先级,则将该运算符进S1栈,
// 否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,最后将该运算符送入S1栈。
spush(s1, s2, sb, t);
}
}
}
s1.clear();
return s2;
}
private static String[] editExpress(String source) {
source = "(" + source.replaceAll("\\s*", "") + ")" + EOD;// 去除空格
source = source.replaceAll("true", "(1==1)");// 支持true/false
source = source.replaceAll("false", "(0==1)");
source = source.replaceAll("([^\\d|^)])([+|-])(\\d+)", "$1(0$2$3)");// 支持一元[+-]
return source.split("");
}
private static void spush(Stack<String> s1, Stack<String> s2, StringBuilder sb, String t) {
while (true) {
if (s1.size() == 0 || getPriority(t) < getPriority(s1.peek()) || getPriority(s1.peek()) == 1) {
RefreshTemp(s1, sb);
break;
} else {
s2.push(s1.pop());
}
}
}
private static void RefreshTemp(Stack<String> rpStack, StringBuilder sb) {
String str = sb.toString();
if (str.length() > 0) {
if ((isNumber(str) || getPriority(str) < MIN)) {
rpStack.push(sb.toString());
sb.setLength(0);
} else {
throw new ArithmeticException("错误的表达式:" + str);
}
}
}
private static boolean isNumber(String t) {
char i = t.charAt(0);// 值判断一个字符
return (i >= 48 && i <= 57) ? true : false;
}
// == 计算 ========================================================
public static Object calcReversePolish(Stack<String> s2) {
Stack<Object> no = new Stack<Object>();
for (String s : s2) {
if (isNumber(s)) {
no.push(Double.parseDouble(s));
} else {
Object d2 = no.pop();
Object d1 = 0.0;
if (!no.isEmpty()) d1 = no.pop();
switch (s) {
case "!":
no.push(!((boolean) d2));
break;
case "+":
no.push((double) d1 + (double) d2);
break;
case "-":
no.push((double) d1 - (double) d2);
break;
case "*":
no.push((double) d1 * (double) d2);
break;
case "/":
no.push((double) d1 / (double) d2);
break;
case "%":
no.push((double) d1 % (double) d2);
break;
case "==":
no.push((double) d1 == (double) d2 ? true : false);
break;
case "!=":
no.push((double) d1 != (double) d2 ? true : false);
break;
case ">=":
no.push(((double) d1 >= (double) d2 ? true : false));
break;
case "<=":
no.push(((double) d1 <= (double) d2 ? true : false));
break;
case ">":
no.push(((double) d1 > (double) d2 ? true : false));
break;
case "<":
no.push(((double) d1 < (double) d2 ? true : false));
break;
case "||":
no.push(((boolean) d1 || (boolean) d2 ? true : false));
break;
case "&&":
no.push(((boolean) d1 && (boolean) d2 ? true : false));
break;
case "^":
no.push(Math.pow((double) d1, (double) d2));
break;
}
}
}
return no.pop();
}
// == 附加 js 引擎
/**
* Must java version >=1.6
*/
private static ScriptEngine se;
public static ScriptEngine getSE() {
if (se == null) se = new ScriptEngineManager().getEngineByName("js");//
return se;
}
public static void main(String[] args) {
try {
int i = -10;
List<String> calcs = new ArrayList<>();
calcs.add("+11+-22-33 ");
calcs.add("-(-(-1))+(-3)*(-2)*3/4%(-5)+" + i++);
calcs.add("-1+2*(3+4)-5+(-2)*8/2+8+0 == 5*6/2-5 && 10%4*3>-1+2*3");
calcs.add("!false");
calcs.add("1!=-3-(-4)");
calcs.add("2>=-2+3");
calcs.add("1+1");
calcs.add("true");
for (String calc : calcs) {
System.out.println("\n中缀表达式:" + calc);
System.out.println("逆波兰表达式:" + createReversePolish(calc));
// == 逆波兰 ========================
long t1 = System.currentTimeMillis();
System.out.println("逆波兰=" + excute(calc) + "\ttime:" + (System.currentTimeMillis() - t1));
// // == JS引擎==========================
long t3 = System.currentTimeMillis();
System.out.println("JS引擎=" + getSE().eval(calc) + "\ttime:" + (System.currentTimeMillis() - t3));
Thread.sleep(500);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试结果:
中缀表达式:+11+-22-33
逆波兰表达式:[0, 11, +, 0, 22, -, +, 33, -]
逆波兰=-44.0 time:1
JS引擎=-44.0 time:122
中缀表达式:-(-(-1))+(-3)*(-2)*3/4%(-5)+-10
逆波兰表达式:[0, 1, -, -, -, 0, 3, -, 0, 2, -, *, 3, *, 4, /, 0, 5, -, %, +, 0, 10, -, +]
逆波兰=-6.5 time:1
JS引擎=-6.5 time:1
中缀表达式:-1+2*(3+4)-5+(-2)*8/2+8+0 == 5*6/2-5 && 10%4*3>-1+2*3
逆波兰表达式:[0, 1, -, 2, 3, 4, +, *, +, 5, -, 0, 2, -, 8, *, 2, /, +, 8, +, 0, +, 5, 6, *, 2, /, 5, -, ==, 10, 4, %, 3, *, 0, 1, -, 2, 3, *, +, >, &&]
逆波兰=false time:1
JS引擎=false time:3
中缀表达式:!false
逆波兰表达式:[0, 1, ==, !]
逆波兰=true time:0
JS引擎=true time:1
中缀表达式:1!=-3-(-4)
逆波兰表达式:[1, 0, 3, -, 0, 4, -, -, !=]
逆波兰=false time:0
JS引擎=false time:4
中缀表达式:2>=-2+3
逆波兰表达式:[2, 0, 2, -, 3, +, >=]
逆波兰=true time:0
JS引擎=true time:2
中缀表达式:1+1
逆波兰表达式:[1, 1, +]
逆波兰=2.0 time:0
JS引擎=2.0 time:1
中缀表达式:true
逆波兰表达式:[1, 1, ==]
逆波兰=true time:0
JS引擎=true time:4