任何使用 5.x 版本的朋友,请升级到 5.2.6 及以上版本。

5.0 有哪些特性?

5.0 这个大版本最大的改变是将 aviator 从表达式引擎升级为 AviatorScript 通用脚本语言,主要的语法特性包括:

  • 大括号 { ... } 括起来的词法作用域。
  • let 语句用于定义局部变量。
  • 条件语句 if/elsif/else
  • 循环语句 forwhile ,以及相应的 breakcontinue 语句支持。
  • return 语句用于从脚本或者函数中返回值。
  • fn hello() { println("hello"); } 新的 fn 语法用于定义命名函数。
  • ## 单行注释 注释支持
  • 模块系统
  • new 语法用于创建对象
  • 异常处理
  • 命令行工具 aviator

学习文档可以阅读《AviatorScript 编程指南》,大量例子可以参见 examples 下的脚本。

除了新的语法特性之外,还做了大量的重构和优化,以及新的功能支持,主要包括:

  1. 编译结果支持 LRU 缓存,通过 AviatorEvaluatorInstance#useLRUExpressionCache(int) 即可启用。
  2. 所有解析 token 和内部对象如 AviatorString 等都实现了 Serializable 接口,方便序列化。
  3. 彻底重构了 Sequence 抽象及各个高阶函数,现在你可以自定义 Sequence
  4. 完整支持 Java 脚本 API,包括 Invocable 接口等,还提供了 SPI 实现,让 Java 调用 AviatorScript 更容易。
  5. 优化报错信息,减少不必要的堆栈。
  6. 函数 AviatorFunction 继承 RunnableCallable 接口,两个接口实现都将调用无参函数分支。这样函数可以直接作为 Runnable 或者 Callable 使用。
  7. 大量的内部函数重构和优化,包括:
    • seq.get(seq, x) 支持 Set ,如果存在元素 x,就返回元素自身,否则返回 nil
    • seq.contains_key(seq, index) 除了map 之外还支持数组和链表,如果 index 这个索引位置在数组或者链表有效范围内就返回 true,否则返回 false。
    • seq.addfiltersome 等函数都支持了所有 sequence 集合,特别是 map,元素类型为 Map.Entry
    • 现在字符串 String 也是一个 sequence,因此你可以用 map/reduce 等函数以及 for 循环去操作字符串,它的每个元素是单个字符组成的字符串,暂不支持字符类型。
  8. 新增函数:
    • 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。
  9. 性能优化:
    • include(seq, x) 对于 Set 现在是 O(1) 时间复杂度
    • 消除 BeanUtils 的全局锁,提升嵌套变量的并发访问和写入性能。
    • 通过脚本内符号表,提升变量访问性能
    • 优化字符串拼接性能,内部使用 StringBuilder 替代原先的加法运算。
  10. 其他:
    • 新增 AviatorEvaluatorInstance 管理 classloader 的 API,如 resetClassLoadersetAviatorClassLoader 等。
    • 编译脚本使用的 compileScript 系列方法
    • 编译结果缓存允许指定 caching key,减少内存占用,只要保证唯一即可。
    • Feature API,用于指定脚本引擎支持的语法特性。

5.0 的 breaking change 不兼容点有哪些?

已知的不兼容点包括:

  1. 废弃的: AviatorRuntimeJavaType(Object) 构造函数,请用 AviatorRuntimeJavaType.valueOf(Object)替代。
  2. 移除数个选项:
    • 删除 Options.ALWAYS_USE_DOUBLE_AS_DECIMAL ,请用 Options.ALWAYS_USE_DECIMAL_AS_FLOATING_POINT_NUMBER 替代。
    • 删除 Options.TRACE 选项,不再支持。
    • 删除 Options.DISABLE_ASSIGNMENT 选项,使用 Feature.Assignment 来替代用于启用或者禁用。
  3. 默认启用全脚本语法特性支持,包括赋值、 return 语句、fn 语法、 letif/else 以及 for/while 等默认都支持,不过可修改,见下一节。
  4. 变量被闭包捕获后,仍然可修改,这跟原来设定不符。
  5. 新增关键字:if, elsif, else, for, in, while, break, continue, fn, return, try, catch, finally, throw, new。如果你原来使用了这些关键字做函数或者变量名,可能会遇到问题。
  6. 部分函数修改了行为,特别是 seq 系列函数:
    1. nil =~/[a-zA-Z]+/; ## returns false when matching nil with regular pattern .
    2. count(nil); ## returns 0
    3. include(nil, element); ## returns false
    4. map(nil, lambda); ## returns nil
    5. sort(nil); ## returns nil
    6. reduce(nil, lambda, init); ## returns the init value
    7. filter(nil, lambda); ## returns nil
    8. seq.every(nil, lambda); ## returns true
    9. seq.not_any(nil, lambda); ## returns true
    10. seq.some(nil, lambda); ## returns nil

如果你发现有新的不兼容点,请在评论里回复,谢谢。

如何无缝升级到 5.0 ?

首先,请仔细阅读上一节的不兼容点,做针对性的修改。
其次,如果你想继续将 AviatorScript 保持在 5.0 之前的 EL 模式,你可以通过下列代码设置语法特性集为兼容模式:

  1. AviatorEvaluator.getInstance()
  2. .setOption(Options.FEATURE_SET, Feature.getCompatibleFeatures())

我们看下 Feature.getCompatibleFeatures() 返回的是什么:

  1. /**
  2. * Returns the feature set that is compatible with aviator early versions(before 5.0).
  3. *
  4. * @return
  5. */
  6. public static Set<Feature> getCompatibleFeatures() {
  7. return asSet(Feature.Assignment, Feature.Lambda, Feature.InternalVars);
  8. }

返回的仅有赋值、lambda 语法和内部变量特性( __env____intance__ 等内部变量),其他语法特性都不启用,这就跟 5.0 之前版本保持一致。

如果你想关闭赋值,你可以单独调用 disableFeature 方法:

  1. AviatorEvaluator.getInstance().disableFeature(Feature.Assignment);

启用就是 enableFeature 方法,比如我们想单独启用 return 语句:

  1. AviatorEvaluator.getInstance().enableFeature(Feature.Return);

关于 Opitons.FEATURE_SET 的完整说明,请阅读选项文档

交流支持请到 github 或者加入钉钉群: 30739878