Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
皇甫嗷嗷叫
qlexpress
提交
2a5cc84e
Q
qlexpress
项目概览
皇甫嗷嗷叫
/
qlexpress
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Q
qlexpress
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
2a5cc84e
编写于
1月 09, 2018
作者:
T
tianqiao
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
完善文档
上级
4b337107
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
188 addition
and
3 deletion
+188
-3
README.md
README.md
+188
-3
未找到文件。
README.md
浏览文件 @
2a5cc84e
# 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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录