提交 2a5cc84e 编写于 作者: T tianqiao

完善文档

上级 4b337107
# QLExpress基本语法
## 背景介绍
# 一、背景介绍
由阿里的电商业务规则、表达式(布尔组合)、特殊数学公式计算(高精度)、语法分析、脚本二次定制等强需求而设计的一门动态脚本引擎解析工具。
在阿里集团有很强的影响力,同时为了自身不断优化、发扬开源贡献精神,于2012年开源。
## 依赖和调用说明
# 二、依赖和调用说明
```xml
<dependency>
......@@ -25,7 +25,7 @@ String express = "a+b*c";
Object r = runner.execute(express, context, null, true, false);
System.out.println(r);
```
# 三、语法介绍
## 1、操作符和java对象操作
### 普通java语法
```
......@@ -270,4 +270,189 @@ var : 语文
System.out.println(map.get(key));
}
```
# 四、运行参数和API列表介绍
QLExpressRunner如下图所示,从语法树分析、上下文、执行过程三个方面提供二次定制的功能扩展。
![QlExpress-detail.jpg](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/dec904b003aba15cbf1af2726914ddee.jpg)
## 1、属性开关
### isPrecise
```java
/**
* 是否需要高精度计算
*/
private boolean isPrecise = false;
```
> 高精度计算在会计财务中非常重要,java的float、double、int、long存在很多隐式转换,做四则运算和比较的时候其实存在非常多的安全隐患。
> 所以类似汇金的系统中,会有很多BigDecimal转换代码。而使用QLExpress,你只要关注数学公式本身 _订单总价 = 单价 * 数量 + 首重价格 + ( 总重量 - 首重) * 续重单价_ ,然后设置这个属性即可,所有的中间运算过程都会保证不丢失精度。
### isShortCircuit
```java
/**
* 是否使用逻辑短路特性
*/
private boolean isShortCircuit = true;
```
在很多业务决策系统中,往往需要对布尔条件表达式进行分析输出,普通的java运算一般会通过逻辑短路来减少性能的消耗。例如规则公式:
_star>10000 and shoptype in('tmall','juhuasuan') and price between (100,900)_
假设第一个条件 _star>10000_ 不满足就停止运算。但业务系统却还是希望把后面的逻辑都能够运算一遍,并且输出中间过程,保证更快更好的做出决策。
### isTrace
```java
/**
* 是否输出所有的跟踪信息,同时还需要log级别是DEBUG级别
*/
private boolean isTrace = false;
```
这个主要是是否输出脚本的编译解析过程,一般对于业务系统来说关闭之后会提高性能。
## 2、调用入参
```java
/**
* 执行一段文本
* @param expressString 程序文本
* @param context 执行上下文,可以扩展为包含ApplicationContext
* @param errorList 输出的错误信息List
* @param isCache 是否使用Cache中的指令集,建议为true
* @param isTrace 是否输出详细的执行指令信息,建议为false
* @param aLog 输出的log
* @return
* @throws Exception
*/
Object execute(String expressString, IExpressContext<String,Object> context,List<String> errorList, boolean isCache, boolean isTrace, Log aLog);
```
## 3、功能扩展API列表
QLExpress主要通过子类实现Operator.java提供的以下方法来最简单的操作符定义,然后可以被通过addFunction或者addOperator的方式注入到ExpressRunner中。
```java
public abstract Object executeInner(Object[] list) throws Exception;
```
比如我们几行代码就可以实现一个功能超级强大、非常好用的join操作符:
_list = 1 join 2 join 3;_ -> [1,2,3]
_list = join(list,4,5,6);_ -> [1,2,3,4,5,6]
```java
public class JoinOperator extends Operator{
public Object executeInner(Object[] list) throws Exception {
java.util.List result = new java.util.ArrayList();
Object opdata1 = list[0];
if(opdata1 instanceof java.util.List){
result.addAll((java.util.List)opdata1);
}else{
result.add(opdata1);
}
for(int i=1;i<list.length;i++){
result.add(list[i]);
}
return result;
}
}
```
如果你使用Operator的基类OperatorBase.java将获得更强大的能力,基本能够满足所有的要求。
### (1)function相关API
```java
//通过name获取function的定义
OperatorBase getFunciton(String name);
//通过自定义的Operator来实现类似:fun(a,b,c)
void addFunction(String name, OperatorBase op);
//fun(a,b,c) 绑定 object.function(a,b,c)对象方法
void addFunctionOfServiceMethod(String name, Object aServiceObject,
String aFunctionName, Class<?>[] aParameterClassTypes,
String errorInfo);
//fun(a,b,c) 绑定 Class.function(a,b,c)类方法
void addFunctionOfClassMethod(String name, String aClassName,
String aFunctionName, Class<?>[] aParameterClassTypes,
String errorInfo);
//给Class增加或者替换method,同时 支持a.fun(b) ,fun(a,b) 两种方法调用
//比如扩展String.class的isBlank方法:“abc”.isBlank()和isBlank("abc")都可以调用
void addFunctionAndClassMethod(String name,Class<?>bindingClass, OperatorBase op);
```
### (2)Operator相关API
提到脚本语言的操作符,优先级、运算的目数、覆盖原始的操作符(+,-,*,/等等)都是需要考虑的问题,QLExpress统统帮你搞定了。
```java
//添加操作符号,可以设置优先级
void addOperator(String name,Operator op);
void addOperator(String name,String aRefOpername,Operator op);
//替换操作符处理
OperatorBase replaceOperator(String name,OperatorBase op);
//添加操作符和关键字的别名,比如 if..then..else -> 如果。。那么。。否则。。
void addOperatorWithAlias(String keyWordName, String realKeyWordName,
String errorInfo);
```
### (3)宏定义相关API
QLExpress的宏定义比较简单,就是简单的用一个变量替换一段文本,和传统的函数替换有所区别。
```java
//比如addMacro("天猫卖家","userDO.userTag &1024 ==1024")
void addMacro(String macroName,String express)
```
### (4)java class的相关api
QLExpress可以通过给java类增加或者改写一些method和field,比如 链式调用:"list.join("1").join("2")",比如中文属性:"list.长度"。
```java
//添加类的属性字段
void addClassField(String field,Class<?>bindingClass,Class<?>returnType,Operator op);
//添加类的方法
void addClassMethod(String name,Class<?>bindingClass,OperatorBase op);
```
> 注意,这些类的字段和方法是执行器通过解析语法执行的,而不是通过字节码增强等技术,所以只在脚本运行期间生效,不会对jvm整体的运行产生任何影响,所以是绝对安全的。
### (4)语法树解析变量、函数的API
> 这些接口主要是对一个脚本内容的静态分析,可以作为上下文创建的依据,也可以用于系统的业务处理。
> 比如:计算 “a+fun1(a)+fun2(a+b)+c.getName()”
> 包含的变量:a,b,c
> 包含的函数:fun1,fun2
```java
//获取一个表达式需要的外部变量名称列表
String[] getOutVarNames(String express);
String[] getOutFunctionNames(String express);
```
### (5)语法解析校验api
脚本语法是否正确,可以通过ExpressRunner编译指令集的接口来完成。
```java
String expressString = "for(i=0;i<10;i++){sum=i+1}return sum;";
InstructionSet instructionSet = expressRunner.parseInstructionSet(expressString);
//如果调用过程不出现异常,指令集instructionSet就是可以被加载运行(execute)了!
```
### (6)指令集缓存相关的api
因为QLExpress对文本到指令集做了一个本地HashMap缓存,通常情况下一个设计合理的应用脚本数量应该是有限的,缓存是安全稳定的,但是也提供了一些接口进行管理。
```java
//优先从本地指令集缓存获取指令集,没有的话生成并且缓存在本地
InstructionSet getInstructionSetFromLocalCache(String expressString);
//清除缓存
void clearExpressCache();
```
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册