Java使用Aviator表达式 记录(四)

2,461 阅读3分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

幂运算

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

p(2 ** 3);
p(2 ** -3);
p(2N ** 3);
p(2M ** 3);
p(2 ** 2.2);

输出:

8
0.125
8
8
4.59479341998814

幂运算的基本规则:

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

位运算

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

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

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

## examples/bitwise.av
let OPEN = 0x01;

let flag = 0;

flag = flag | OPEN;

(flag  & OPEN) == OPEN ? println("open") : println("close");

flag = flag &~ OPEN;

(flag  & OPEN) == OPEN ? println("open") : println("close");

println("flag is " + flag);

运算符优先级

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

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

这里还出现了我们还没谈到的运算符 [] 用于数组或者集合访问,后面我们在降到集合类型的时候会详细介绍。

记住这个优先级没有太大必要,无论如何,都推荐使用括号来明确表示优先级

自定义运算符

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

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

package com.googlecode.aviator.example;

import java.util.Map;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.lexer.token.OperatorType;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorDouble;
import com.googlecode.aviator.runtime.type.AviatorObject;
import com.googlecode.aviator.runtime.type.AviatorType;

/**
 * An example to demo custom division operator.
 *
 * @author dennis(killme2008@gmail.com)
 *
 */
public class CustomDivideExample {
  public static void main(final String[] args) {
    AviatorEvaluator.getInstance().addOpFunction(OperatorType.DIV, new AbstractFunction() {

      @Override
      public AviatorObject call(final Map<String, Object> env, final AviatorObject arg1,
          final AviatorObject arg2) {
        if (arg1.getAviatorType() == AviatorType.Long
            && arg2.getAviatorType() == AviatorType.Long) {
          // If arg1 and arg2 are all long type.
          // Cast arg2 into double and divided by arg1.
          double d = FunctionUtils.getNumberValue(arg1, env).longValue()
              / FunctionUtils.getNumberValue(arg2, env).doubleValue();
          return AviatorDouble.valueOf(d);
        } else {
          // Otherwise, call aviatorscript's div function.
          return arg1.div(arg2, env);
        }
      }

      @Override
      public String getName() {
        return OperatorType.DIV.getToken();
      }
    });

    System.out.println(AviatorEvaluator.execute("1/2"));
  }
}

通过 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 ,表示相等,大于返回正数,小于返回负数。