基础语法
安装配置
Aviator也是一种基于JVM运行的脚本语言,和Groovy这个大哥相比,它就是个更轻巧的小弟弟。
Aviator没有class
(类)、没有interface
(接口)、没有trait
(特质)、没有Lambda表达式
,有的只是最基本的一等公民function
(函数)和稍微高级一点的closure
(闭包),它完全就是为运行脚本而生的。
运行Aviator程序代码有几种方式。
- 一是用
com.googlecode.aviator.AviatorEvaluator
类来解释脚本,这个在实际开发和生产环境中用的比较多,就比如下面的代码所展示的那样。
先引入依赖。
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.3.3</version>
</dependency>
然后就可以写一个Aviator的小栗子了。
package com.itechthink.aviator;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import java.util.HashMap;
import java.util.Map;
/**
* Aviator Demo
*
*/
public class AviatorDemo {
public static void main(String[] args) {
// 算术运算
String exp1 = "1 + 2 + 3";
Long result = (Long) AviatorEvaluator.execute(exp1);
System.out.println("1. " + result);
String exp2 = "1.1 + 2.2 + 3.3";
Double result2 = (Double) AviatorEvaluator.execute(exp2);
System.out.println("2. " + result2);
// 逻辑运算
String exp3 = "(1 > 0 || 0 < 1) && 1 != 0";
System.out.println("3. " + AviatorEvaluator.execute(exp3));
// 三元运算
String exp4 = "2 > 1 ? \"2 > 1\" : none";
System.out.println("4. " + AviatorEvaluator.execute(exp4));
// 变量传入
String exp5 = "a > b";
Map<String, Object> map = new HashMap<String, Object>();
map.put("a", 1);
map.put("b", 2);
System.out.println("5. " + AviatorEvaluator.execute(exp5, map));
}
}
- 二是使用命令行,也就是基于AviatorScript来运行,调试起来比较麻烦,一般不考虑。
可以在AviatorScript先下载最新发布的版本,然后配置环境变量,再用aviator
命令执行.av
脚本文件就行了。

先新建一个Maven项目,再新建一个.av
脚本文件,写几行Aviator代码之后,就可以单击鼠标右键在弹出的菜单中选择Run AviatorScript
运行程序了。

变量与数据类型
不像Java可以仅仅只声明变量,Aviator在声明变量的时还必须一并赋值。
## aviator的注释是这么写的,不能使用//、/* */或/** */
## aviator对将关键字声明为变量名的控制没有那么严格
## aviator声明变量时不用也不能指明数据类型
string = "hello world";
println(type(string));
## 有些关键字还是不能用的,比如int
## int = 0;
## println(type(int));
i = -1;
println(type(i));
## aviator没有int,只有long
long = 1;
println(type(long));
## 声明时不允许指定类型
b = true;
println(type(b));
c = 3.14;
println(type(c));
d = 99999999999999999999999999999999999999999999999999999999999999999999;
println(type(d));
## 以M结尾的数字都是BigDecimal
e = 1.414213562373095M;
println(type(e));
f = 1M;
println(type(f));
let pattern = /(?i)hello\s+(.*)/;
println(type(pattern));
if string =~ pattern {
println($1);
}
## 布尔运算都是常规操作
println("3 > 1 is " + (3 > 1));
## 三元运算符
## boolean的值就是true或false
println(2 > 1 ? true : false);
## 或者这样写
let c = 1 > 2 ? println("1 > 2") : println("1 <= 2");
## 这里的c值是null
println(c);
## null是不能直接用来赋值的,否则会报错
## n = null;
## println(n);
## println(type(n));
除了注释的说明以外,上面这段代码还说明了这几个问题。
声明变量可以加
let
,也可以不加,但必须要有初始值。每条语句都必须严格加上
;
,但最后一行语句可以不加。Aviator没有包装类型,只有六种基本类型和一个
正则表达式
类型:string
、boolean
、long
、bigint
、double
、decimal
和pattern
(当然,null
算是一种特殊的类型)。
## 字符串可以用单引号,也可以用双引号
string1 = "hello world";
string2 = 'hello world';
string = 'hello aviator';
println(type(string1));
println(type(string2));
println(type(string));
## 字符串长度
println(string.length(string1));
## 变量名和类型名混在一起,aviator可以正确地处理
println(string.length(string));
## 字符串拼接和转义字符
newstr = string1 + " - " + string2 + " \" " + string;
println(newstr);
## 字符串可以用+拼接,但不能用-去除
## println(newstr - "hello aviator");
正则表达式
在Aviator是一等公民,正像上面的代码显示的那样,正规表达式是一个被/ /
括起来的java.util.Pattern
实例,只有逻辑运算符和=~
能进行正则运算。
let pattern = /^(.*)\.av$/;
## 逻辑运算符
println(pattern >= pattern);
println(pattern == pattern);
println(pattern != pattern);
## 用于判断是否匹配正则,不能这样写:pattern =~ "hello aviator.av"
println("hello aviator.av" =~ pattern);
## 也不能这样写
## println("hello aviator.av" =~ /^(.*)\.av$/);
## 如果匹配成功,Aviator会将正则中匹配的分组放入$0, $1, $2, ...等变量中。其中$0表示匹配的整个字符串,$1表示第一个分组,以此类推
println("$0=" + $0);
println("$1=" + $1);
println("$2=" + $2);
println("$3=" + $3);
Aviator对Java数据类型做了一些调整,但各种运算符,如算术运算符、逻辑运算符、三元运算符等的规则不变。
对不不同类型数据操作的转换,Aviator的转换规则如下。
long | bigint | decimal | double | |
---|---|---|---|---|
long | long | bigint | decimal | double |
bigint | bigint | decimal | double | |
decimal | decimal | double | ||
double | double |
运算符
除了已有的算术运算符、逻辑运算符、位运算符和正则运算符,Aviator从5.13
版本开始,引入了幂运算符
(**
),并且可以自定义重载这些运算符,或者给运算符起个别名(个人认为起别名
的功能意义不大)。
## 官方说明幂运算的基本规则如下:
## 基数和指数都为long ,并且指数为正数,结果为long ✕
println(type(math.pow(2, 3))); ## 结果为double
## 除了可以用math.pow(2, 3),还能用**运算符
println(type(2 ** 3)); ## 结果为long
println(type(p(2 ** 3))); ## 结果为8和nil
## 基数和指数都为long,并且指数为负数,结果为double ✓
println(type(math.pow(-2, 3))); ## 结果为double
## 基数为decimal,指数取整数部分(int value),等价于BigDecimal#pow(int)
println(type(math.pow(2M, 3))); ## 结果为decimal ✓
## 基数为bigint,指数取整数部分(int value),等价于BigInteger#pow(int)
println(type(math.pow(9999999999999999999999999999999999999999, 2))); ## 结果为bigint ✓
## 基数或者指数任一为double,结果为double ✓
println(type(math.pow(3.1415926, 3.1415926))); ## 结果为double
从结果可以看到,除了第一个有点问题之外,其他都正确。看来Aviator还存在语言层面的BUG
啊。
如果需要重定义操作符的话,一般都是通过Java代码实现的。
package com.itechthink.aviator;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.lexer.token.OperatorType;
import java.util.Map;
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;
/**
* 对运算符的操作
*/
public class AliasOperatorExample {
/**
* 给运算符起别名
*/
public static void alias() {
// && 别名 and
AviatorEvaluator.getInstance().aliasOperator(OperatorType.AND, "and");
// || 别名 or
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"));
}
/**
* 重载/运算符
*
*/
public static void overload() {
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) {
// 如果arg1和arg2都是long
double d = FunctionUtils.getNumberValue(arg1, env).longValue() / FunctionUtils.getNumberValue(arg2, env).doubleValue();
return AviatorDouble.valueOf(d);
} else {
// 调用aviator的除法功能
return arg1.div(arg2, env);
}
}
@Override
public String getName() {
return OperatorType.DIV.getToken();
}
});
System.out.println(AviatorEvaluator.execute("1/10"));
}
public static void main(final String[] args) {
alias();
overload();
}
}
变量作用域
Aviator有像Java那样的代码块,所以就有了变量作用域一说。
let a = 1;
## 加上这一行,打印出的 b in global scope 结果为9
## 没有这一行,打印出的 b in global scope 结果为null
b = 2;
{
a = 4;
## let并没有覆盖全局变量的值,打印出的 a in global scope 结果为4
let a = 5;
b = 6;
{
a = 8;
b = 9;
## 这里不管用不用let,最外层的 c in global scope 结果都是null
let c = 10;
println("a in scope2:" + a); ## 8
println("b in scope2:" + b); ## 9
println("c in scope2:" + c); ## 10
}
println("a in scope1:" + a); ## 8
println("b in scope1:" + b); ## 9
println("c in scope1:" + c); ## null
}
println("a in global scope:" + a); ## 4
println("b in global scope:" + b); ## 9,之所以是这个结果,是因为 b = 2; 和 b = 9; 这两行共同的作用?
println("c in global scope:" + c); ## null
运行的结果还是有些不太好理解。
条件判断
Aviator的条件语句有几个特点。
代码块都必须用大括号包起来,哪怕是单行语句也要如此。
if
后面连着的表达式的括号可以忽略。else if
变成了elsif
。
if a > 1000 {
println("呵呵");
} elsif a > 100 {
println("哈哈");
} elsif a > 10 {
println("嘿嘿");
} else {
println("!");
}
循环语句
Aviator有两种for
循环形式。
## 默认步长=1
for i in range(0, 10) {
println(i);
}
## 设置步长=2
for i in range(0, 10, 2) {
println(i);
}
Aviator中的for..in
可以用于任何集合结构,比如数组
、java.util.List
和java.util.Map
等。
## 遍历map
s1 = seq.map("a", 1, "b", 2, "c", 3);
for x in s1 {
println(x.key + "=" + x.value);
}
## 访问map元素
println("s1['a'] = " + s1['a'] + ", s1['b'] = " + s1['b'] + ", s1['c'] = " + s1['c']);
## 遍历list
list = seq.list(1, 2, 3, 4, 5, 6, 7, 8, 9);
sum = 0;
for x in list {
sum = sum + x;
}
println(sum);
## 遍历tuple
t = tuple(1, 3, 6, 2, 7, 4, 9, 8, 5);
for i, x in t {
print(i + "\t" + x + "\t" + t[i] + "\n");
}
## aviator也有continue/break/return,但return会从整个脚本(或者函数)中断执行并返回,而不仅仅是跳出循环
for i in range(0, 5) {
if i > 2 {
return;
}
println(i);
}
println("Can't reach here");
while
循环的使用和Java没有区别,所以略过。
其他语义
## 关于分号:如果想返回表达式的值,最后一个表达式不加分号即可
a = 1;
b = 2;
c = a + b
## 可以使用new来创建对象,但非java.lang下的类,都需要加上完整的包名前缀
d = new java.util.Date();
println(d);
## 不知道这个为啥不行
## t = new Srting("hello world");
## println(t);
## 必须要使用use来引入包,否则会报错
use java.util.*;
array = new java.util.ArrayList();
## 这种添加数据的形式很奇怪
seq.add(array, 1);
seq.add(array, 2);
println(array);
感谢支持
更多内容,请移步《超级个体》。