在上一张《基本类型及运算》 我们介绍了算术运算符、比较运算符、逻辑运算符、三元运算符和正则匹配运算符,这一节我们将更加详细地考察这些运算符并且介绍如何重载运算符。
幂运算
从 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 | = =~ | 从右到左 |
这里还出现了我们还没谈到的运算符 []
用于数组或者集合访问,后面我们在降到集合类型的时候会详细介绍。
记住这个优先级没有太大必要,无论如何,都推荐使用括号来明确表示优先级。
运算符别名
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() {
@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 ,表示相等,大于返回正数,小于返回负数。