运算、运算符¶
Lumos 使用 \ 加运算名的方式定义运算符重载,比 operator+ 的写法更简洁。
优先顺序:
- 后缀
- 前缀
- 二元
- 乘、除、取余
- 加、减
- 移位
- 按位与
- 按位异或
- 按位或
- 逻辑与 (
and) - 逻辑异或 (
xor) - 逻辑或 (
or) - 比较
- 赋值
前缀运算¶
| 运算 | 名称 | 解释 | 注释 |
|---|---|---|---|
| +a | pos | 正号 | |
| -a | neg | 负号 | |
| ~a | flip | 按位取反 | |
| !a | not | 逻辑取反 | |
| &a | addr | 取地址 | 不可重载 |
后缀运算¶
| 运算 | 名称 | 解释 |
|---|---|---|
a[] |
deref | 解引用 |
a[b] |
index | 数组索引 |
对于整数索引,应当是 isize 或 usize 类型。
对于键值索引,可以是任意类型。
对于数组索引,推荐的重载方式:
def \index(usize i) -> i32 {
return (array + i)[];
}
二元运算¶
算术运算符¶
| 运算 | 名称 | 解释 | 注释 |
|---|---|---|---|
| a + b | add | 加法 | |
| a - b | sub | 减法 | |
| a * b | mul | 乘法 | |
| a / b | div | 除法 | |
| a % b | rem | 取余 | 符号与被除数相同 |
| mod | 取模 | 符号与除数相同 |
取余与取模¶
Lumos 区分取余(rem)与取模(mod)两种运算,它们在负数情况下行为不同:
| 表达式 | rem |
mod |
|---|---|---|
5, 3 |
2 |
2 |
-5, 3 |
-2 |
1 |
5, -3 |
2 |
-1 |
-5, -3 |
-2 |
-2 |
rem:结果符号与被除数相同,等同于 C/C++ 的%。mod:结果符号与除数相同,等同于数学取模。
对于浮点数,对应的命名为 frem(浮点取余)和 fmod(浮点取模)。
除法扩展¶
| 运算 | 名称 | 解释 | 注释 |
|---|---|---|---|
| a / b | div | 普通除法 | |
| a /% b | divrem | 带余数除法 | 返回商与余数元组 |
| a /+ b | divceil | 向上取整除法 | |
| a /- b | divfloor | 向下取整除法 |
算术运算扩展¶
对于整数运算,Lumos 提供溢出行为可控的扩展运算符:
| 运算 | 名称 | 解释 |
|---|---|---|
| a +^ b | addo | 饱和加法(溢出时返回类型最大值或最小值) |
| a +% b | addw | 环绕加法(溢出时截断为类型位宽) |
| a +! b | addc | 进位加法(返回 (结果, 进位) 元组) |
| a -^ b | subo | 饱和减法 |
| a -% b | subw | 环绕减法 |
| a -! b | subc | 借位减法(返回 (结果, 借位) 元组) |
| a *^ b | mulo | 饱和乘法 |
| a *% b | mulw | 环绕乘法 |
| a *! b | mulc | 宽乘法(返回 (结果, 高位) 元组) |
普通运算符(+, -, *)在整数溢出时抛出异常;编译器会尽可能在编译期检查,若能证明不溢出则不生成运行时检查代码。相关优化假设详见 控制流。
浮点数运算¶
浮点数使用与整数相同的运算符(+、-、*、/、%),但其内部重载名称不同:
| 运算 | 浮点名称 | 解释 |
|---|---|---|
| a + b | fadd | 浮点加法 |
| a - b | fsub | 浮点减法 |
| a * b | fmul | 浮点乘法 |
| a / b | fdiv | 浮点除法 |
| a % b | frem | 浮点取余 |
浮点数模运算(符号与除数相同)使用 fmod,通过 .mod(b) 调用。
浮点数不支持位运算,请将浮点数转换为 bits 类型后再操作(见位运算符节)。
对齐运算¶
对于需要内存对齐的场景,Lumos 提供以下命名运算:
| 名称 | 解释 |
|---|---|
alignup |
向上对齐(将值调整为不小于给定对齐量的最小倍数) |
aligndown |
向下对齐(将值调整为不大于给定对齐量的最大倍数) |
alignnear |
向最近对齐(四舍五入到最近的对齐倍数) |
isaligned |
判断值是否已对齐到给定对齐量 |
对于取整运算:
ceil:向上取整,仅对浮点数有效。在编译期已知为整数类型时会导致编译错误。floor:向下取整,仅对浮点数有效。在编译期已知为整数类型时会导致编译错误。
ceil 与 floor 在整数类型上也有定义(返回其原值),但会触发编译错误以防止误用。
位运算符¶
| 运算 | 名称 | 解释 |
|---|---|---|
| a & b | band | 按位与 |
| a | b | bor | 按位或 |
| a ^ b | bxor | 按位异或 |
| a << b | shl | 左移 |
| a >> b | shr | 右移 |
| a <<< b | ushl | 无符号左移 |
| a >>> b | ushr | 无符号右移 |
| a <<> b | rol | 循环左移 |
| a >>< b | ror | 循环右移 |
注意浮点数不能进行位运算,请将浮点数转换为 bits 类型:
f32 value = (123.456 as b32 << 1) as f32;
复合赋值运算符¶
| 运算 | 名称 | 解释 |
|---|---|---|
| a += b | iadd | 加并赋值 |
| a -= b | isub | 减并赋值 |
| a *= b | imul | 乘并赋值 |
| a /= b | idiv | 除并赋值 |
| a %= b | irem | 取余并赋值 |
| a &= b | iband | 按位与并赋值 |
| a |= b | ibor | 按位或并赋值 |
| a ^= b | ibxor | 按位异或并赋值 |
| a <<= b | ishl | 左移并赋值 |
| a >>= b | ishr | 右移并赋值 |
比较运算符¶
| 运算 | 名称 | 解释 | 注释 |
|---|---|---|---|
| a == b | eq | 等于 | |
| a != b | ne | 不等于 | |
| a < b | lt | 小于 | |
| a <= b | le | 小于等于 | |
| a > b | gt | 大于 | |
| a >= b | ge | 大于等于 | |
| a <=> b | cmp | 比较 | |
| a === b | seq | 严格相等 | 可重载 |
| a !== b | sne | 严格不等 | 可重载 |
| a $== b | beq | 二进制相等 | 不可重载 |
| a ~== b | bne | 二进制不等 | 不可重载 |
| a &== b | same | 相同 | 不可重载 |
| a &!= b | diff | 不同 | 不可重载 |
-
<=>
比较运算符,返回 -1、0、1 表示小于、等于、大于。对于重载,实现
<=>即可,==、!=、<、<=、>、>=会自动调用<=>。当然你也可以手动实现其它函数,Lumos 会自动将未实现的比较运算符转换为已实现的。
-
===!==
严格相等,仅当两个对象的类型和值都相同时返回 true。你没听错,严格相等是可重载的,但你不能重载不同类型的严格相等运算。
对于严格相等运算,你可以使用与==不同的逻辑。struct MyInt { i32 value; def \eq(MyInt rhs) -> bool { return value == rhs.value; } def \seq(MyInt rhs) -> bool { return value === rhs.value; } def \seq(f64 rhs) -> bool; // 这是不行的,不同类型间的严格相等不可重载 }
$==~==
二进制相等,仅当两个对象的二进制表示完全相同时返回 true。
注意二进制表示的比较包含位数,例如i32 1 $== i64 1为 false。
-
&==&!=
判断左右是否是相同的对象,不比较值。
类似于&a == &b,任意一边为字面量时始终得到 false。
&==与&!=是唯一可以比较引用的运算符。i32 a = 1; ref i32 b = a; i32 c = a; assert(a &== b); // true assert(a &== c); // false
逻辑运算符¶
| 运算 | 名称 | 解释 | 注释 |
|---|---|---|---|
| a and b | and | 逻辑与(有短路) | |
| a or b | or | 逻辑或(有短路) | |
| a xor b | xor | 逻辑异或 |
and/or 支持短路求值:a and b 中若 a 为假则不对 b 求值;a or b 中若 a 为真则不对 b 求值。前缀运算符 not a 等价于 !a。
对于逻辑异或,应使用 a xor b 而非 a != b。
bool 类型还可以使用位运算符 &/| 进行无短路的逻辑与/或运算:
| 运算 | 解释 |
|---|---|
| a & b | 逻辑与(无短路,仅限 bool) |
| a | b | 逻辑或(无短路,仅限 bool) |
其他运算符¶
| 运算 | 名称 | 解释 | 注释 |
|---|---|---|---|
a as type |
cast | 类型转换 | 不可重载 |
sizeof(a) |
sizeof | 计算大小 | 不可重载 |
typeof(a) |
typeof | 获取类型 | 不可重载 |
typenameof(a) |
typenameof | 获取类型名称 | 不可重载 |
a->b |
arrow | 自定义成员访问 | 可重载 |
注意 , 和 . 不是运算符,而作为分隔符。
-
typeof运算结果为类型,可以直接当作类型使用。i32 a = 1; typeof(a) b = 2;
typenameof的运算结果为字符串。
可空传播 ?¶
后缀运算符 ? 用于标记一个可空表达式。当 ? 标记的值为空(null 或 none)时,整个含 ? 的前方完整表达式跳转到 : 后的后备值。
my_func(a?) : b // 若 a 为空则返回 b,否则返回 my_func(a)
my_func(a?, c?) : b // 若 a 或 c 任意一个为空则返回 b,否则返回 my_func(a, c)
my_func(a?) + 1 : b // : b 修饰前方整个表达式;若 a 为空则返回 b,否则返回 my_func(a) + 1
(my_func(a?) : b) + 1 // 使用括号显式控制范围
? 同时进行类型窄化:传递给函数时参数类型从 T? 窄化为 T,无需显式解包。
等价展开示例:
my_func(a?, c?) : b
// 等价于
if (a == null or c == null) b else my_func(a, c)
运算律¶
复合赋值运算符应与基本运算符等效:
a = a 运算符 b; ⟺ a 运算符= b;
对应规则:
a = a + b⟺a += ba = a - b⟺a -= ba = a * b⟺a *= ba = a / b⟺a /= b
自定义运算符会自动生成对应的复合赋值运算符。
Lumos 不支持 ++ 和 -- 运算符,请改用 a += 1 或 a -= 1。
逻辑运算符交换律¶
符合交换律:
a and b ⟺ b and a
a or b ⟺ b or a
a xor b ⟺ b xor a
运算符重载¶
与 C++ 中 operator+ 一类的写法不同,Lumos 使用反斜杠加运算名来代表运算符,例如 \add。
| 运算 | 名称 | 运算 | 名称 | 运算 | 名称 |
|---|---|---|---|---|---|
a + b |
add | a & b |
band | a and b |
and |
a - b |
sub | a \| b |
bor | a or b |
or |
a * b |
mul | a ^ b |
bxor | a xor b |
xor |
a / b |
div | a << b |
shl | a == b |
eq |
a % b |
rem | a >> b |
shr | a != b |
ne |
-a |
neg | ~a |
flip | !a |
not |
a |
get | a < b |
lt | a <= b |
le |
a = b |
set | a > b |
gt | a >= b |
ge |
a <=> b |
cmp | ||||
a[] |
deref |
所有类似 a += b 的运算名称都是 i 加上对应的运算符名称,例如 add 的增量运算符是 iadd。
\ 开头的运算符¶
一些没有对应符号的重要运算符:
\hash计算哈希值\next获取下一个值\prev获取上一个值\clone克隆对象\sizeof获取对象大小\alignof获取对象对齐值\get获取值(读取操作,配合\set用于属性语义)\set设置值(写入操作)\as类型转换(返回类型可重载,代表显式转换语义)
优化假设¶
运算律¶
对于运算律,我们将它们实现为特性:
Commutative交换律Associative结合律Distributive分配律
实现了这些特性的运算符可以被编译器用来进行优化。
默认情况下我们要求 add mul 实现交换律与结合律,mul 实现分配律。
比较传递性¶
对于比较运算符,我们将传递性实现为特性:
TransitiveEq等于传递性TransitiveCmp比较传递性SelfConsistent自反性
对于浮点数,我们还实现了可能无法比较的特性:
PartialEq部分等于性PartialOrd部分有序性
对应运算符为 peq、pord 等。我们为浮点数提供这些实现,并将 == 和 <=> 等运算符与之绑定。
浮点默认遵循 IEEE 754 标准的比较规则,但也同时实现全序浮点类型 totalf32 totalf64。
实现了这些特性的比较运算符可以被编译器用来进行优化。
一般情况下我们要求 eq 实现等于传递性与自反性,cmp 实现比较传递性与自反性。
在比较上实现自反性需要对于对象
ab,如果a > b为 true 则b < a必须为 true,反之亦然。
相关内容:表达式语法