在上一张《基本类型及运算》 我们介绍了算术运算符、比较运算符、逻辑运算符、三元运算符和正则匹配运算符,这一节我们将更加详细地考察这些运算符并且介绍如何重载运算符。
幂运算
从 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);
输出:
80.125884.59479341998814
幂运算的基本规则:
- 基数和指数都为 long ,并且指数为正数,结果为 long
- 基数和指数都为 long,并且指数为负数,结果为 double
- 基数为 decimal,指数取整数部分(int value),等价于
BigDecimal#pow(int)。 - 基数为 bigint,指数取整数部分(int value),等价于
BigInteger#pow(int)。 - 基数或者指数任一为 double,结果为 double
位运算
我们还没有介绍的是数字的位运算,位运算仅支持整数 long,并且跟 Java 完全保持一致:
&位与运算|或运算^异或运算~一元非运算<<左移运算>>右移运算>>>无符号右移
看一个简单例子,比如我们通常用位运算来控制 flag 某个标记位:
## examples/bitwise.avlet 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 | = =~ | 从右到左 |
这里还出现了我们还没谈到的运算符 [] 用于数组或者集合访问,后面我们在降到集合类型的时候会详细介绍。
记住这个优先级没有太大必要,无论如何,都推荐使用括号来明确表示优先级。
运算符别名
5.3.1 开始,加入了逻辑运算符 &&和||的别名支持,你可以为这两个运算符定义别名,比如改成 and和 or:
package com.googlecode.aviator.example;import com.googlecode.aviator.AviatorEvaluator;import com.googlecode.aviator.lexer.token.OperatorType;public class AliasOperatorExample {public static void main(final String[] args) {AviatorEvaluator.getInstance().aliasOperator(OperatorType.AND, "and");AviatorEvaluator.getInstance().aliasOperator(OperatorType.OR, "or");System.out.println(AviatorEvaluator.execute("1==1 and 2==3"));System.out.println(AviatorEvaluator.execute("true or false"));System.out.println(AviatorEvaluator.execute("true && 1==1 or false"));}}
通过 AviatorEvaluatorInstance.aliasOperator(OperatorType, String)方法来设置别名,目前仅支持 AND和 OR操作符,未来会继续扩展。设置别名后,原始的操作符仍然继续可用。
重载运算符
前面我们已经看了运算符重载的例子,比如加减乘除,可以用于 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() {@Overridepublic 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);}}@Overridepublic 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 ,表示相等,大于返回正数,小于返回负数。
