任何使用 5.x 版本的朋友,请升级到 5.2.6 及以上版本。
5.0 有哪些特性?
5.0 这个大版本最大的改变是将 aviator 从表达式引擎升级为 AviatorScript 通用脚本语言,主要的语法特性包括:
大括号 { ... }
括起来的词法作用域。let
语句用于定义局部变量。- 条件语句
if/elsif/else
。 - 循环语句
for
和while
,以及相应的break
和continue
语句支持。 return
语句用于从脚本或者函数中返回值。fn hello() { println("hello"); }
新的 fn 语法用于定义命名函数。## 单行注释
注释支持- 模块系统
new
语法用于创建对象- 异常处理
- 命令行工具 aviator
学习文档可以阅读《AviatorScript 编程指南》,大量例子可以参见 examples 下的脚本。
除了新的语法特性之外,还做了大量的重构和优化,以及新的功能支持,主要包括:
- 编译结果支持 LRU 缓存,通过
AviatorEvaluatorInstance#useLRUExpressionCache(int)
即可启用。 - 所有解析 token 和内部对象如 AviatorString 等都实现了
Serializable
接口,方便序列化。 - 彻底重构了 Sequence 抽象及各个高阶函数,现在你可以自定义 Sequence。
- 完整支持 Java 脚本 API,包括
Invocable
接口等,还提供了 SPI 实现,让 Java 调用 AviatorScript 更容易。 - 优化报错信息,减少不必要的堆栈。
- 函数
AviatorFunction
继承Runnable
和Callable
接口,两个接口实现都将调用无参函数分支。这样函数可以直接作为Runnable
或者Callable
使用。 - 大量的内部函数重构和优化,包括:
seq.get(seq, x)
支持Set
,如果存在元素 x,就返回元素自身,否则返回 nilseq.contains_key(seq, index)
除了map 之外还支持数组和链表,如果 index 这个索引位置在数组或者链表有效范围内就返回 true,否则返回 false。seq.add
、filter
、some
等函数都支持了所有 sequence 集合,特别是 map,元素类型为Map.Entry
。- 现在字符串 String 也是一个 sequence,因此你可以用 map/reduce 等函数以及 for 循环去操作字符串,它的每个元素是单个字符组成的字符串,暂不支持字符类型。
- 新增函数:
type(x)
获取值的类型,is_def(x)
返回 x 是否被定义,undef(x)
取消 x 的定义(或者说遗忘 x)。assert(x)
断言函数,当 x 不为 true,抛出异常。cmp(x, y)
用于对比 x 和 y,返回整数,当x == y
返回 0,x > y
返回正整数,否则返回负数。range(start, end, [step])
用于创建[start, end)
的整数集合。seq.put(seq, key, value)
用于设置 seq 在 key 位置的值为 value,seq 可以是 map ,数组或者 List。 map 就是键值对, 数组或者 List 的时候, key 为索引位置整数,value 即为想要放入该索引位置的值。decimal(x)
将其他类型的值转为 decimal 类型。bigint(x)
将其他类型的值转为 bigint 类型。seq.entry(key, value)
用于创建Map.Entry
对象。into(to_seq, from_seq)
用于 sequence 转换,将 from sequence 的元素使用seq.add
函数逐一添加到了 to sequence 并返回最终的 to_seq。
- 性能优化:
include(seq, x)
对于Set
现在是 O(1) 时间复杂度- 消除 BeanUtils 的全局锁,提升嵌套变量的并发访问和写入性能。
- 通过脚本内符号表,提升变量访问性能
- 优化字符串拼接性能,内部使用 StringBuilder 替代原先的加法运算。
- 其他:
- 新增
AviatorEvaluatorInstance
管理 classloader 的 API,如resetClassLoader
、setAviatorClassLoader
等。 - 编译脚本使用的
compileScript
系列方法 - 编译结果缓存允许指定 caching key,减少内存占用,只要保证唯一即可。
Feature
API,用于指定脚本引擎支持的语法特性。
- 新增
5.0 的 breaking change 不兼容点有哪些?
已知的不兼容点包括:
- 废弃的:
AviatorRuntimeJavaType(Object)
构造函数,请用AviatorRuntimeJavaType.valueOf(Object)
替代。 - 移除数个选项:
- 删除
Options.ALWAYS_USE_DOUBLE_AS_DECIMAL
,请用Options.ALWAYS_USE_DECIMAL_AS_FLOATING_POINT_NUMBER
替代。 - 删除
Options.TRACE
选项,不再支持。 - 删除
Options.DISABLE_ASSIGNMENT
选项,使用Feature.Assignment
来替代用于启用或者禁用。
- 删除
- 默认启用全脚本语法特性支持,包括赋值、
return
语句、fn 语法、let
、if/else
以及for/while
等默认都支持,不过可修改,见下一节。 - 变量被闭包捕获后,仍然可修改,这跟原来设定不符。
- 新增关键字:if, elsif, else, for, in, while, break, continue, fn, return, try, catch, finally, throw, new。如果你原来使用了这些关键字做函数或者变量名,可能会遇到问题。
- 部分函数修改了行为,特别是 seq 系列函数:
nil =~/[a-zA-Z]+/; ## returns false when matching nil with regular pattern .
count(nil); ## returns 0
include(nil, element); ## returns false
map(nil, lambda); ## returns nil
sort(nil); ## returns nil
reduce(nil, lambda, init); ## returns the init value
filter(nil, lambda); ## returns nil
seq.every(nil, lambda); ## returns true
seq.not_any(nil, lambda); ## returns true
seq.some(nil, lambda); ## returns nil
如果你发现有新的不兼容点,请在评论里回复,谢谢。
如何无缝升级到 5.0 ?
首先,请仔细阅读上一节的不兼容点,做针对性的修改。
其次,如果你想继续将 AviatorScript 保持在 5.0 之前的 EL 模式,你可以通过下列代码设置语法特性集为兼容模式:
AviatorEvaluator.getInstance()
.setOption(Options.FEATURE_SET, Feature.getCompatibleFeatures())
我们看下 Feature.getCompatibleFeatures()
返回的是什么:
/**
* Returns the feature set that is compatible with aviator early versions(before 5.0).
*
* @return
*/
public static Set<Feature> getCompatibleFeatures() {
return asSet(Feature.Assignment, Feature.Lambda, Feature.InternalVars);
}
返回的仅有赋值、lambda 语法和内部变量特性( __env__
、 __intance__
等内部变量),其他语法特性都不启用,这就跟 5.0 之前版本保持一致。
如果你想关闭赋值,你可以单独调用 disableFeature
方法:
AviatorEvaluator.getInstance().disableFeature(Feature.Assignment);
启用就是 enableFeature
方法,比如我们想单独启用 return
语句:
AviatorEvaluator.getInstance().enableFeature(Feature.Return);
关于 Opitons.FEATURE_SET
的完整说明,请阅读选项文档。
交流支持请到 github 或者加入钉钉群: 30739878