在上一张《基本类型及运算》 我们介绍了算术运算符、比较运算符、逻辑运算符、三元运算符和正则匹配运算符,这一节我们将更加详细地考察这些运算符并且介绍如何重载运算符。

幂运算

从 5.1.3 开始,AviatorScript 引入幂运算符 ** ,原来使用 math.pow(a, b) 的代码都可以写成 a**b ,幂运算符的优先级较高,在单目运算符之上。

  1. p(2 ** 3);
  2. p(2 ** -3);
  3. p(2N ** 3);
  4. p(2M ** 3);
  5. p(2 ** 2.2);

输出:

  1. 8
  2. 0.125
  3. 8
  4. 8
  5. 4.59479341998814

幂运算的基本规则:

  • 基数和指数都为 long ,并且指数为正数,结果为 long
  • 基数和指数都为 long,并且指数为负数,结果为 double
  • 基数为 decimal,指数取整数部分(int value),等价于 BigDecimal#pow(int)
  • 基数为 bigint,指数取整数部分(int value),等价于 BigInteger#pow(int)
  • 基数或者指数任一为 double,结果为 double

位运算

我们还没有介绍的是数字的位运算,位运算仅支持整数 long,并且跟 Java 完全保持一致:

  • & 位与运算
  • | 或运算
  • ^ 异或运算
  • ~ 一元非运算
  • << 左移运算
  • >> 右移运算
  • >>> 无符号右移

看一个简单例子,比如我们通常用位运算来控制 flag 某个标记位:

  1. ## examples/bitwise.av
  2. let OPEN = 0x01;
  3. let flag = 0;
  4. flag = flag | OPEN;
  5. (flag & OPEN) == OPEN ? println("open") : println("close");
  6. flag = flag &~ OPEN;
  7. (flag & OPEN) == OPEN ? println("open") : println("close");
  8. println("flag is " + flag);

运算符优先级

完整的运算符优先级的优先顺序如下表,基本跟 java 保持一致,除了特别引入的正则匹配:

优先级 运算符 结合性
1 ( ) [ ]  . 从左到右
2 ** 从左到右
3 !  ~ 从右到左
4 *  /  % 从左到右
5 +  - 从左到右
6 <<  >>  >>> 从左到右
7 <  <=  >  >= 从左到右
8 ==  != 从左到右
9 & 从左到右
10 ^ 从左到右
11 | 从左到右
12 && 从左到右
13 || 从左到右
14 ? : 从左到右
15 = =~ 从右到左

这里还出现了我们还没谈到的运算符 [] 用于数组或者集合访问,后面我们在降到集合类型的时候会详细介绍。
记住这个优先级没有太大必要,无论如何,都推荐使用括号来明确表示优先级

运算符别名

5.3.1 开始,加入了逻辑运算符 &&||的别名支持,你可以为这两个运算符定义别名,比如改成 andor

  1. package com.googlecode.aviator.example;
  2. import com.googlecode.aviator.AviatorEvaluator;
  3. import com.googlecode.aviator.lexer.token.OperatorType;
  4. public class AliasOperatorExample {
  5. public static void main(final String[] args) {
  6. AviatorEvaluator.getInstance().aliasOperator(OperatorType.AND, "and");
  7. AviatorEvaluator.getInstance().aliasOperator(OperatorType.OR, "or");
  8. System.out.println(AviatorEvaluator.execute("1==1 and 2==3"));
  9. System.out.println(AviatorEvaluator.execute("true or false"));
  10. System.out.println(AviatorEvaluator.execute("true && 1==1 or false"));
  11. }
  12. }

通过 AviatorEvaluatorInstance.aliasOperator(OperatorType, String)方法来设置别名,目前仅支持 ANDOR操作符,未来会继续扩展。设置别名后,原始的操作符仍然继续可用。

重载运算符

前面我们已经看了运算符重载的例子,比如加减乘除,可以用于 long/double/bigint/decimal 等多种数字类型,这就是一个重载,比如加号 + 可以用于数字,也可以用于拼接字符串,这又是一个重载。

你也可以自定义任意运算符的行为,比如我们想将整数相除的结果修改为浮点数,那么可以:

  1. package com.googlecode.aviator.example;
  2. import java.util.Map;
  3. import com.googlecode.aviator.AviatorEvaluator;
  4. import com.googlecode.aviator.lexer.token.OperatorType;
  5. import com.googlecode.aviator.runtime.function.AbstractFunction;
  6. import com.googlecode.aviator.runtime.function.FunctionUtils;
  7. import com.googlecode.aviator.runtime.type.AviatorDouble;
  8. import com.googlecode.aviator.runtime.type.AviatorObject;
  9. import com.googlecode.aviator.runtime.type.AviatorType;
  10. /**
  11. * An example to demo custom division operator.
  12. *
  13. * @author dennis(killme2008@gmail.com)
  14. *
  15. */
  16. public class CustomDivideExample {
  17. public static void main(final String[] args) {
  18. AviatorEvaluator.getInstance().addOpFunction(OperatorType.DIV, new AbstractFunction() {
  19. @Override
  20. public AviatorObject call(final Map<String, Object> env, final AviatorObject arg1,
  21. final AviatorObject arg2) {
  22. if (arg1.getAviatorType() == AviatorType.Long
  23. && arg2.getAviatorType() == AviatorType.Long) {
  24. // If arg1 and arg2 are all long type.
  25. // Cast arg2 into double and divided by arg1.
  26. double d = FunctionUtils.getNumberValue(arg1, env).longValue()
  27. / FunctionUtils.getNumberValue(arg2, env).doubleValue();
  28. return AviatorDouble.valueOf(d);
  29. } else {
  30. // Otherwise, call aviatorscript's div function.
  31. return arg1.div(arg2, env);
  32. }
  33. }
  34. @Override
  35. public String getName() {
  36. return OperatorType.DIV.getToken();
  37. }
  38. });
  39. System.out.println(AviatorEvaluator.execute("1/2"));
  40. }
  41. }

通过 AviatorEvaluatorInstance#addOpFunction(opType, function) 就可以自定义运算符的行为,这个例子中如果我们发现 arg1/arg2 的类型都是 long ,那么我们就将 arg2 转成 double 并计算结果,结果需要包装成 AviatorDouble 类型返回;如果不是,我们继续调用原来的除法。

所有的运算符都可以在 OperatorType 找到,你都可以自定义他的行为。

从这个例子中我们也可以看出 AviatorScript 中的所有类型都是 AviatorObject 的子类,他们包括:

  • AviatorLong 表示整数 long 类型
  • AviatorDouble 表示浮点数 double 类型
  • AviatorBigInt 表示 bigint 大整数
  • AviatorDecimal 表示 decimal 类型
  • AviatorString 表示字符串
  • AviatorPattern 表示正则表达式
  • AviatorNil 特殊类型 nil,将在下一节变量中讲到
  • AviatorJavaType 表示变量,将在下一节讲到

可以参见 AviatorType 这个枚举类。所有运算符都实现为 AviatorObject 的一个方法,比如加法就是 add(arg2, env) ,减法就是 sub 等等,具体见 AviatorObject 文档。比较运算符的实现是基于 compare(arg2, env) 方法返回的结果,如果为 0 ,表示相等,大于返回正数,小于返回负数。