github: https://github.com/alibaba/QLExpress
优点:
@Test
public void testDemo() throws Exception {
String express = "10 * 10 + 1 + 2 * 3 + 5 * 2";
ExpressRunner runner = new ExpressRunner();
Object r = runner.execute(express, null, null, false, false);
Assert.assertTrue("表达式计算", r.toString().equalsIgnoreCase("117"));
System.out.println("表达式计算:" + express + " = " + r);
}
debug:
即10 * 10 + 1 + 2 * 3 + 5 * 2
转化为类似后缀表达式(逆波兰式): 10 10 * 1 + 2 3 * + 5 2 * +
1: STAT_BLOCK:STAT_BLOCK STAT_BLOCK
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: +:+ +
4: +:+ +
5: +:+ +
6: *:* *
7: 10:CONST_INTEGER CONST
7: 10:CONST_INTEGER CONST
6: 1:CONST_INTEGER CONST
5: *:* *
6: 2:CONST_INTEGER CONST
6: 3:CONST_INTEGER CONST
4: *:* *
5: 5:CONST_INTEGER CONST
5: 2:CONST_INTEGER CONST
即按照后缀表达式(逆波兰式): 10 10 * 1 + 2 3 * + 5 2 * +
执行
1:LoadData 10
2:LoadData 10
3:OP : * OPNUMBER[2]
4:LoadData 1
5:OP : + OPNUMBER[2]
6:LoadData 2
7:LoadData 3
8:OP : * OPNUMBER[2]
9:OP : + OPNUMBER[2]
10:LoadData 5
11:LoadData 2
12:OP : * OPNUMBER[2]
13:OP : + OPNUMBER[2]
private Object executeReentrant(InstructionSet sets, IExpressContext<String, Object> iExpressContext,
List<String> errorList, boolean isTrace, boolean isCatchException) throws Exception {
try {
int reentrantCount = threadReentrantCount.get() + 1;
threadReentrantCount.set(reentrantCount);
return reentrantCount > 1 ?
// 线程重入
InstructionSetRunner.execute(this, sets, this.loader, iExpressContext, errorList, isTrace,
isCatchException, true, false) :
InstructionSetRunner.executeOuter(this, sets, this.loader, iExpressContext, errorList, isTrace,
isCatchException, false);
} finally {
threadReentrantCount.set(threadReentrantCount.get() - 1);
}
}
得到结果,具体执行过程是
@Override
public void execute(RunEnvironment environment, List<String> errorList) throws Exception {
environment.push(this.operateData);
environment.programPointAddOne();
}
com.ql.util.express.RunEnvironment#push
public void push(OperateData data) {
this.point++;
if (this.point >= this.dataContainer.length) {
ensureCapacity(this.point + 1);
}
this.dataContainer[point] = data;
}
添加操作数
com.ql.util.express.instruction.detail.InstructionOperator#execute
按照特定指令,取数执行
@Override
public void execute(RunEnvironment environment, List<String> errorList) throws Exception {
InstructionSetContext instructionSetContext = environment.getContext();
ArraySwap parameters = environment.popArray(this.opDataNumber);
try {
OperateData result = this.operator.execute(instructionSetContext, parameters, errorList);
environment.push(result);
environment.programPointAddOne();
} catch (QLException e) {
throw new QLException(getExceptionPrefix(), e);
} catch (Throwable t) {
throw new QLBizException(getExceptionPrefix(), t);
}
}
com.ql.util.express.InstructionSetRunner#execute
特别说明:代码执行过程中会有各种缓存,避免指令的重复生成,可提高运行效率
@Test
public void testOperatorIn() throws Exception {
String express1 = "2 in (2, 3) ";
String express2 = "2 in a";
String express3 = "2 in b";
ExpressRunner runner = new ExpressRunner(true, true);
DefaultContext<String, Object> context = new DefaultContext<>();
int[] a = {1, 2, 3};
context.put("a", a);
List<Integer> b = new ArrayList<>();
b.add(2);
b.add(3);
context.put("b", b);
System.out.println(runner.execute(express1, context, null, false, false));
System.out.println(runner.execute(express2, context, null, false, false));
System.out.println(runner.execute(express3, context, null, false, false));
}
对于表达式"2 in b" 解析为的语法树
1: STAT_BLOCK:STAT_BLOCK STAT_BLOCK
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: in:in in
4: 2:CONST_INTEGER CONST
4: b:ID ID
执行指令:
在执行in操作符时候,
获取b参数则调用com.ql.util.express.instruction.opdata.OperateDataAttr#getObjectInner
, 从context中获取值:
最后正确执行in指令,得到结果