Aviator 表达式语法
Aviator 语法主要借鉴了 Java 语言,因此对于 Java 开发者来说非常容易上手。
1. 基本运算
-
算术运算:
aviatorscript1 + 2 - 3 * 4 / 5 % 6 // 加、减、乘、除、取模 -10 // 负数
-
比较运算:
aviatorscripta > b a < b a >= b a <= b a == b // 相等 a != b // 不等
-
逻辑运算:
aviatorscripta && b // 逻辑与 (and) a || b // 逻辑或 (or) !a // 逻辑非 (not)
-
位运算:
aviatorscripta & b // 按位与 a | b // 按位或 a ^ b // 按位异或 ~a // 按位非 a << b // 左移 a >> b // 右移 a >>> b // 无符号右移
2. 数据类型和字面量
-
整数:
aviatorscript123 0xFF // 十六进制 077 // 八进制 (注意:在 Aviator 5.0+ 中,推荐使用 0o77 表示八进制)
-
浮点数 (double):
aviatorscript123.45 .5 1.2e3 // 科学计数法
-
长整数 (long):
aviatorscript123L // 以 L 结尾表示长整数
-
布尔值:
aviatorscripttrue false
-
字符串:
aviatorscript"hello world" 'hello world' // 单引号和双引号都支持 "a\"b" // 转义字符
-
BigDecimal (高精度浮点数):
aviatorscript1.23M // 以 M 结尾表示 BigDecimal 类型 100M // 整数也可以是 BigDecimal 类型
-
nil (空值):
aviatorscriptnil // 相当于 Java 的 null
-
日期:
aviatorscript"2023-01-01 12:00:00" // 字符串形式会被自动解析为 Date 类型(需要配置日期格式化器) // 通常通过传入 Date 类型的变量来处理日期
3. 变量引用
-
直接引用:
aviatorscriptmyVariable // 引用 Map 中 key 为 "myVariable" 的值
-
访问对象属性:
aviatorscriptuser.name // 访问 user 对象的 name 属性(等同于 user.getName()) order.customer.address.city // 链式访问
-
访问 Map 元素:
aviatorscriptmap.key // 访问 Map 中 key 为 "key" 的值 (等同于 map.get("key")) // 注意:如果 key 是非法的变量名(如包含特殊字符),需要用方括号: map['my-key']
4. 条件表达式 (三元运算符)
-
基本形式:
aviatorscriptbooleanCondition ? valueIfTrue : valueIfFalse
示例:
aviatorscriptscore >= 60 ? "及格" : "不及格"
5. if-else
语句 (代码块)
-
基本形式:
aviatorscriptif (condition) { // block for true } else { // block for false }
-
嵌套
if-else if-else
:aviatorscriptif (age < 18) { "青少年" } else { if (age >= 18 && age < 60) { "成年人" } else { "老年人" } }
注意: Aviator 的
else if
实际上是else { if { ... } else { ... } }
这种嵌套形式。每个if
和else
后都需要一个{}
包裹的代码块。
6. 内置函数
Aviator 提供了丰富的内置函数,例如:
-
数学函数:
aviatorscriptmath.abs(-10) math.max(a, b) math.min(a, b) math.round(3.14) math.pow(2, 3) math.sqrt(9)
-
字符串函数:
aviatorscriptstring.length("hello") string.contains("hello", "ell") string.startsWith("hello", "he") string.endsWith("hello", "lo") string.substring("hello", 1, 3) // 从索引1开始,长度为3 string.split("a,b,c", ",")
-
集合/序列操作:
-
count (统计元素数量):
aviatorscriptcount([1, 2, 3]) // 返回 3 count(users) // 如果 users 是 List/Set,返回其大小
-
**isEmpty (判断是否为空):
aviatorscriptisEmpty([]) // 返回 true isEmpty(nil) // 返回 true
-
**include (判断是否包含):
scssinclude([1, 2, 3], 2) // 返回 true include(myString, "sub") // 字符串包含子串
-
序列操作:
aviatorscriptseq.add([1,2], 3) // 返回 [1,2,3] seq.remove([1,2,3], 2) // 返回 [1,3] seq.contains([1,2,3], 2) // 返回 true seq.size([1,2,3]) // 返回 3
-
-
**类型转换函数:
aviatorscriptlong(10.5) // 转换为 long double(10) // 转换为 double str(123) // 转换为字符串 // 更多转换函数:bigdec(), date() 等
-
**其他常用函数:
aviatorscripttype(obj) // 获取变量的类型 nil_or_empty(obj) // 判断是否为 nil 或空(字符串、集合、Map等) // ...还有很多,请查阅 Aviator 官方文档
7. 自定义函数
你可以通过 AviatorEvaluator.addFunction()
方法注册自定义的 Java 函数,然后在表达式中像内置函数一样调用。
java
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorLong;
import com.googlecode.aviator.runtime.type.AviatorObject;
import java.util.Map;
// 自定义加法函数
class MyAddFunction extends AbstractFunction {
@Override
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
Number num1 = FunctionUtils.getNumberValue(arg1, env);
Number num2 = FunctionUtils.getNumberValue(arg2, env);
return AviatorLong.valueOf(num1.longValue() + num2.longValue());
}
@Override
public String getName() {
return "myAdd"; // 函数名
}
}
// 在 main 方法中注册
// AviatorEvaluator.addFunction(new MyAddFunction());
// 在表达式中调用
// myAdd(a, b)
8. 集合(List)和 Map 字面量
Aviator 5.0+ 支持 List 和 Map 的字面量表示:
-
List 字面量:
csharp[1, 2, "hello", true] [1 + 2, max(a, b)] // 元素可以是表达式
-
**Map 字面量:
shell#{ "name": "张三", "age": 30, "isValid": true } #{ key1: value1, key2: value2 } // key 也可以不加引号,如果它符合变量命名规则
9. 序列操作 (Seq 模块)
Aviator 5.0+ 引入了强大的序列操作,支持对 List、Set、数组等进行过滤、求和、映射等操作。
-
**过滤 (filter):
scssfilter(users, "user.age > 18 && user.gender == 'male'") // 过滤 age > 18 且 gender 为 male 的用户
-
**映射 (map):
cmap(users, "user.name") // 提取所有用户的 name 属性
-
求和 (sum):
scsssum(items, "item.price * item.quantity") // 计算所有商品的总价
-
**所有满足 (every):
scssevery(numbers, "num > 0") // 判断所有数字是否都大于0
-
**任一满足 (some):
scsssome(users, "user.vip == true") // 判断是否存在VIP用户
-
**排序 (sort):
scsssort(products, "product.price desc") // 按价格降序排序
注意: 序列操作的第二个参数是一个表达式字符串,其中可以通过
element
或你定义的变量名来引用当前元素。
10. lambda
表达式 (匿名函数)
Aviator 5.0+ 支持 lambda 表达式,可以作为函数参数传递。
python
filter(users, lambda user : user.age > 18 end) // 等同于上面的 filter 示例
map(items, lambda item : item.price * item.quantity end)
其中 lambda arg1, arg2 : expression end
定义了一个匿名函数。
11. 错误处理
Aviator 表达式在执行过程中如果遇到错误(如类型不匹配、变量不存在),会抛出 com.googlecode.aviator.exception.ExpressionRuntimeException
或其子类。
使用示例总结
一个典型的 Aviator 表达式使用流程:
- 定义表达式字符串。
- 通过
AviatorEvaluator.compile(expressionString)
编译表达式。 (强烈推荐,特别是重复执行的表达式) - 创建环境变量
Map<String, Object>
,将表达式中需要用到的变量放入其中。 - 通过
compiledExpression.execute(env)
执行表达式,获取结果。
数据类型
- Number类型:数字类型,支持两种类型,分别对应Java的Long和Double,也就是说任何整数都将被转换为Long,而任何浮点数都将被转换 为Double,包括用户传入的数值也是如此转换。不支持科学计数法,仅支持十进制。如-1、100、2.3等。
- String类型:字符串类型,单引号或者双引号括起来的文本串,如'helloworld',变量如果传入的是String或者Character也将转为String类型。任何类型和 String 相加还是 String。
- Bool类型:常量true和false,表示真值和假值,与java的Boolean.TRUE和Boolean.False对应。
- Pattern类型: 类似Ruby、perl的正则表达式,以//括起来的字符串,如/\d+/,内部实现为java.util.Pattern。
- 变量类型:与Java的变量命名规则相同,变量的值由用户传入,如"a"、"b"等
- nil类型:常量nil,类似java中的null,但是nil比较特殊,nil不仅可以参与==、!=的比较,也可以参与>、>=、<、<=的比较,Aviator规定任何类型 都n大于nil除了nil本身,nil==nil返回true。用户传入的变量值如果为null,那么也将作为nil处理,nil打印为null。
实战场景
该场景为电商系统中的价格计算,在用户结账的时候会涉及许多内容,包括是不是会员、是什么等级的会员、有无优惠卷、当前的营销活动等内容,最终的价格受这些因素的影响。以下,选取了三个场景进行模拟。
java
package cn.srw;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
// 1. 基础价格表达式 (商品单价 * 购买数量)
String basePriceExp = "price * quantity";
// ! 加M表示告诉表达式引擎 返回的是一个BigDecimal对象
// 2. 会员折扣表达式 (根据会员等级应用折扣)
String memberDiscountExp = "if (memberLevel == 'GOLD') { basePrice * 0.9M } " +
"else { " +
" if (memberLevel == 'PLATINUM') { basePrice * 0.8M } " +
" else { basePrice } " +
"}";
// 3. 满减活动表达式 (根据满足的金额减去相应金额)
String fullReductionExp = "if (totalAfterMemberDiscount >= 500) { totalAfterMemberDiscount - 60M } " +
"else { " +
" if (totalAfterMemberDiscount >= 200) { totalAfterMemberDiscount - 20M } " +
" else { totalAfterMemberDiscount } " +
"}";
// 4. 最终价格表达式 (减去优惠券面值)
String finalPriceExp = "totalAfterFullReduction - couponValue";
// --- 预编译表达式 (推荐做法,提高性能) ---
Expression compiledBasePriceExp = AviatorEvaluator.compile(basePriceExp);
Expression compiledMemberDiscountExp = AviatorEvaluator.compile(memberDiscountExp);
Expression compiledFullReductionExp = AviatorEvaluator.compile(fullReductionExp);
Expression compiledFinalPriceExp = AviatorEvaluator.compile(finalPriceExp);
// --- 模拟计算场景 ---
// 场景1: 普通用户,无优惠券
System.out.println("--- 场景1: 普通用户,无优惠券 ---");
Map<String, Object> env1 = new HashMap<>();
env1.put("price", new BigDecimal("100"));
env1.put("quantity", 3);
env1.put("memberLevel", "NONE"); // 普通用户
env1.put("couponValue", new BigDecimal("0"));
calculateAndPrintPrice(env1, compiledBasePriceExp, compiledMemberDiscountExp, compiledFullReductionExp, compiledFinalPriceExp);
// 预期结果: 100 * 3 = 300; 300 >= 200 减 20 = 280; 280 - 0 = 280
System.out.println("\n--- 场景2: 黄金会员,有优惠券 ---");
// 场景2: 黄金会员,有优惠券
Map<String, Object> env2 = new HashMap<>();
env2.put("price", new BigDecimal("100"));
env2.put("quantity", 5);
env2.put("memberLevel", "GOLD"); // 黄金会员
env2.put("couponValue", new BigDecimal("10"));
calculateAndPrintPrice(env2, compiledBasePriceExp, compiledMemberDiscountExp, compiledFullReductionExp, compiledFinalPriceExp);
// 预期结果: 100 * 5 = 500; 黄金会员 500 * 0.9 = 450; 450 >= 200 减 20 = 430; 430 - 10 = 420
System.out.println("\n--- 场景3: 白金会员,大额订单,无优惠券 ---");
// 场景3: 白金会员,大额订单,无优惠券
Map<String, Object> env3 = new HashMap<>();
env3.put("price", new BigDecimal("200"));
env3.put("quantity", 4);
env3.put("memberLevel", "PLATINUM"); // 白金会员
env3.put("couponValue", new BigDecimal("0"));
calculateAndPrintPrice(env3, compiledBasePriceExp, compiledMemberDiscountExp, compiledFullReductionExp, compiledFinalPriceExp);
// 预期结果: 200 * 4 = 800; 白金会员 800 * 0.8 = 640; 640 >= 500 减 60 = 580; 580 - 0 = 580
}
/**
* 计算并打印价格
*
* @param env 环境变量 Map
* @param compiledBasePriceExp 基础价格表达式
* @param compiledMemberDiscountExp 会员折扣表达式
* @param compiledFullReductionExp 满减活动表达式
* @param compiledFinalPriceExp 最终价格表达式
*/
private static void calculateAndPrintPrice(
Map<String, Object> env,
Expression compiledBasePriceExp,
Expression compiledMemberDiscountExp,
Expression compiledFullReductionExp,
Expression compiledFinalPriceExp) {
// 1. 计算基础价格
BigDecimal basePrice = (BigDecimal) compiledBasePriceExp.execute(env);
env.put("basePrice", basePrice); // 将结果放入环境变量供后续表达式使用
System.out.println(" 基础价格: " + basePrice);
// 2. 计算会员折扣后的价格
BigDecimal totalAfterMemberDiscount = (BigDecimal) compiledMemberDiscountExp.execute(env);
env.put("totalAfterMemberDiscount", totalAfterMemberDiscount);
System.out.println(" 会员折扣后价格: " + totalAfterMemberDiscount);
// 3. 计算满减活动后的价格
BigDecimal totalAfterFullReduction = (BigDecimal) compiledFullReductionExp.execute(env);
env.put("totalAfterFullReduction", totalAfterFullReduction);
System.out.println(" 满减活动后价格: " + totalAfterFullReduction);
// 4. 计算最终支付价格
BigDecimal finalPrice = (BigDecimal) compiledFinalPriceExp.execute(env);
System.out.println(" 最终支付价格: " + finalPrice);
}
}