运算符

Aria 提供了丰富的运算符,涵盖算术、赋值、比较、逻辑、位运算以及多种特殊运算符。

算术运算符

基本的数学运算:

a = 10 + 3      // 13.0
b = 10 - 3      // 7.0
c = 10 * 3      // 30.0
d = 10 / 3      // 3.333...
e = 10 % 3      // 1.0

除以 0 不抛异常,结果为 0.0

自增和自减支持前缀与后缀两种形式:

i = 5
i++                  // 后缀自增,表达式值为 5,之后 i = 6
++i                  // 前缀自增,i 先变为 7,表达式值为 7
i--                  // 后缀自减,表达式值为 7,之后 i = 6
--i                  // 前缀自减,i 先变为 5,表达式值为 5

++ / -- 按「加 1 / 减 1」的值模型工作,对非数字也适用(见下文特殊运算规则): x = 'a' 后执行 x++x 变为 'a1.0'(字符串拼接语义)。

赋值运算符

x = 10           // 基本赋值
x += 5           // x = x + 5  → 15.0
x -= 3           // x = x - 3  → 12.0
x *= 2           // x = x * 2  → 24.0
x /= 4           // x = x / 4  → 6.0
x %= 5           // x = x % 5  → 1.0

注意:裸名与 var. / val.完全隔离的命名空间(读写互不回退)。 var.x = 10 后写 x += 5 操作的是另一个变量(裸名 x,初值 none), var.x 仍是 10。复合赋值请与声明使用同一命名空间。详见变量系统

~= 是初始化或获取运算符,仅在变量未定义时赋值:

var.name ~= 'default'    // 如果 var.name 未定义,则赋值为 'default'

位运算赋值:

bits = 0b1100
bits &= 0b1010          // 按位与赋值 → 0b1000
bits |= 0b0011          // 按位或赋值 → 0b1011
bits ^= 0b1111          // 按位异或赋值 → 0b0100
bits <<= 2              // 左移赋值
bits >>= 1              // 右移赋值
bits >>>= 1             // 无符号右移赋值

比较运算符

a = 10 == 10     // true
b = 10 != 5      // true
c = 10 < 20      // true
d = 10 > 5       // true
e = 10 <= 10     // true
f = 10 >= 15     // false

== 的比较规则:

  • 双方都可作为数字时按数值比较:10 == '10'true
  • 任一侧是不可转数字的字符串时,双方都退化为字符串比较:true == 'true'true
  • 其余情况按值语义比较。
10 == '10'       // true(数值比较)
true == 'true'   // true(字符串回退)
true == 'yes'    // false

~~ 区间匹配运算符

~~ 检查左侧的值是否落在右侧的 Range 内。右侧不是 Range 时结果恒为 false (不退化为相等性检查):

x = 5
a = x ~~ Range(1, 10)    // true:Range 为双端闭区间 [1, 10],含 1 也含 10
b = 10 ~~ Range(1, 10)   // true(end 含入)
c = x ~~ 1..10           // 同上:`1..10` 是 `Range(1, 10)` 的字面量简写
d = x ~~ 5               // false!右侧非 Range 一律 false
e = x ~~ 'hello'         // false

a..b区间字面量,等价于 Range(a, b)(双端闭,步长 1);需要自定义步长时用 Range(a, b, step)。可直接用于 ~~for-in

Range 为双端闭区间:Range(1, 10)(或字面量 1..10)包含 1 到 10。 start > end 且未显式给步长时为空区间(不会自动倒序遍历); 显式 Range(10, 1, -1) 仍可倒序。与 for (i in Range(...)) 的遍历范围一致。

真值规则

条件位置(if / while / ? : / && / || / !)按以下规则求真值:

类型真值
booleantrue / false 本身
number大于 0 为真(0、负数为假)
string仅字符串 'true' 为真('yes''1''2' 均为假)
none
list/map非空为真
函数/对象一般为假(宿主对象默认 booleanValue = false
0.5 ? 'yes' : 'no'      // 'yes'(> 0)
-1 ? 'yes' : 'no'       // 'no'
'true' ? 'yes' : 'no'   // 'yes'
'2' ? 'yes' : 'no'      // 'no'!数字字符串不按数值取真值

逻辑运算符

a = true && false    // false(逻辑与)
b = true || false    // true(逻辑或)
c = !true            // false(逻辑非)

逻辑运算符支持短路求值:&& 在左侧为假时不计算右侧,|| 在左侧为真时不计算右侧。

位运算符

a = 0b1100 & 0b1010     // 0b1000(按位与)
b = 0b1100 | 0b1010     // 0b1110(按位或)
c = 0b1100 ^ 0b1010     // 0b0110(按位异或)
d = ~0b1100              // 按位取反

e = 1 << 3               // 8(左移)
f = 16 >> 2              // 4(右移)
g = -1 >>> 28            // 无符号右移 → 15

三元运算符

标准三元表达式:

result = (x > 0) ? 'positive' : 'non-positive'

Elvis 变体 — 省略中间部分或末尾部分:

a = x ? : defaultVal       // x 为真返回 x,否则返回 defaultVal
b = x ? 'yes' :            // x 为真返回 'yes',否则返回 none

可选链

?. 在对象为 none 时安全地返回 none,而不是抛出错误:

name = user?.name          // user 为 none 时返回 none
len = list?.length()       // list 为 none 时返回 none

空值合并

?? 在左侧为 none 时返回右侧的值:

name = user?.name ?? 'anonymous'
port = config?.port ?? 8080

成员检查运算符

in 检查键是否存在于 map 或对象中:

m = {'name': 'Aria', 'version': 1}
exists = 'name' in m          // true
missing = 'age' in m          // false

注意:对列表in 检查的是索引是否有效(而非值成员)。 如 0 in [10, 20, 30] 为 true(索引 0 存在),3 in [10, 20, 30] 为 false(长度 3,无索引 3)。 判断列表是否包含某个值用 list.contains(value)

instanceof 检查对象是否为指定类的实例:

class Animal {}
class Dog extends Animal {}

d = Dog()
isDog = d instanceof Dog       // true
isAnimal = d instanceof Animal // true(支持继承链)

展开运算符

... 用于展开列表或字典:

a = [1, 2, 3]
b = [0, ...a, 4]          // [0, 1, 2, 3, 4]

base = {'x': 1}
extended = {...base, 'y': 2}   // {'x': 1, 'y': 2}

在字典字面量中展开非 map 值(列表、数字、none 等)会抛出运行时错误。

函数箭头

-> 用于定义函数:

var.add = -> {
    return args[0] + args[1]
}

行首运算符续行

一行以特定运算符开头时会与上一行折叠为同一表达式。折叠集合为: . ? : && || , < ==(注意 + - * = ( [ { 折叠):

m = {'a': 1}
n = m
.size()          // 折叠为 m.size() → 1.0

x = true
? 1
: 2              // x = true ? 1 : 2 → 1.0

行首 ( 不折叠:x = f 与下一行的 (3) 是两条独立语句(后者为无效果的表达式语句)。

运算符优先级

从高到低排列:

优先级运算符说明
1(最高)() [] . ?.调用、索引、成员访问
2++ --(后缀)后缀自增自减
3! - ~ ++ --(前缀)一元运算符
4* / %乘法、除法、取模
5+ -加法、减法
6<< >> >>>位移
7< > <= >= ~~ in instanceof比较、区间/成员检查
8== !=相等
9&按位与
10^按位异或
11|按位或
12??空值合并
13&&逻辑与
14||逻辑或
15? :三元运算符
16(最低)= += -= *= /= %= ~=赋值

区间字面量 .. 的优先级介于位移(<< 等)与比较(< ~~ 等)之间:x ~~ 1..10 会先构造 1..10 再做区间判断,for (i in a..b) 亦同。

特殊运算规则

Aria 对不同类型之间的运算有特殊处理(与 Shimmer 引擎逐条对齐,含其历史怪癖)。

数字与字符串

  • 字符串的”数字性”:任何字符串都会尝试按 Double.parseDouble 判定能否作为数字, 拼接产生的新字符串会重新判定。因此 '' + 0'0.0'(可作数字),再 + 1 走数值加法得 1.0
  • number + string:字符串可转数字则数学加法(1 + '2'3.0); 否则数字先转字符串再拼接(1 + 'a''1.0a''slot' + 1'slot1.0'——数字恒为 N.0 格式)。
  • string + string:双方都可转数字则数值加法('2' + '3'5.0);否则拼接。
  • string - string:双方可转数字则减法('5' - '2'3.0); 否则从左侧字符串中删除右侧子串('aXbXc' - 'X''abc')。
  • number - 非数字字符串:从数字的字符串形式中删除该子串(1 - 'a''1.0',结果是字符串)。
  • 一元负号 -xx 为不可转数字的字符串时抛错(字符串内容非数字);对 list/map 抛 不支持的反转操作

布尔值参与算术

  • true + 12.0false + 11.0(布尔按 1/0 与数字相加);
  • true + true'truetrue'(布尔相加走字符串拼接);
  • true + '1''true1'(与数字字符串相加也走拼接);
  • true - 12.0(减法数字分支实际执行的是加法);
  • true - '1''true'true - 'x''true'(字符串分支按”删除子串”处理)。

列表与字典

  • list + list:把右侧元素原地追加到左侧列表并返回同一个列表(非新列表);
  • list + 元素:原地追加该元素,返回同一个列表;
  • list - number:按索引删除([1, 2, 3] - 0[2, 3]);
  • list - list:删除右侧列表中包含的元素;
  • list - 其它值:按 Math.abs(该值转整数) 当索引删除([10,20,30] - '1' 删索引 1);
  • map + map:合并两个字典,右侧覆盖左侧的同名键;
  • map - key:移除指定键;
  • map + 非 map:抛出运行时错误。

非法操作数

数字与 list/map 相加减(如 1 + [1])等无意义组合会抛出运行时错误 (number 类型不支持与 list 类型进行加法运算 等),错误格式见异常处理