常用运算符
运算符是 Flurry 语言中用于执行计算、比较、逻辑组合等操作的特殊符号。Flurry 提供了一组旨在清晰且富有表现力的运算符集合。
算术运算符
用于执行基本的数学运算。这些运算符通常可以被用户定义的类型通过实现特定的 Trait 来重载。
- 加法 (
+
):a + b
- 减法 (
-
):a - b
- 乘法 (
*
):a * b
- 除法 (
/
):a / b
(整数除法通常向零截断,浮点数除法遵循 IEEE 754) - 取模 (
%
):a % b
(计算除法的余数)
空格敏感性: 如词法结构中所述,这些二元算术运算符必须在其两侧使用空格,以便被词法分析器正确识别为标准算术运算符 token(例如 _+_
)。a+b
不会被识别为标准的加法运算。
let sum = count + 1;
let difference = total - tax;
let area = width * height;
let quotient = dividend / divisor;
let remainder = value % modulus;
比较运算符
用于比较两个值,结果通常是一个布尔值 (bool
)。它们也可以被重载。
- 等于 (
==
):a == b
- 不等于 (
!=
):a != b
- 小于 (
<
):a < b
- 小于等于 (
<=
):a <= b
- 大于 (
>
):a > b
- 大于等于 (
>=
):a >= b
空格敏感性: 小于 (<
) 和大于 (>
) 运算符同样具有空格敏感性。当用于比较时,它们两侧必须有空格(对应 _<_
和 _>_
token)。这避免了与泛型参数列表等场景中使用的尖括号 (<
, >
) 产生词法歧义。
let is_equal = response_code == 200;
let is_not_empty = size != 0;
let in_range = value >= min_value and value <= max_value; -- 注意 'and' 关键字
let needs_update = current_version < latest_version;
逻辑运算符
用于组合布尔值。Flurry 使用关键字而非符号来表示逻辑与和或。
- 逻辑与 (
and
):a and b
(短路求值:如果a
为false
,则不评估b
) - 逻辑或 (
or
):a or b
(短路求值:如果a
为true
,则不评估b
) - 逻辑非 (
not
):not a
(一元运算符)
let both_conditions_met = is_ready and has_permission;
let either_option_valid = is_primary or is_fallback;
let is_invalid = not is_valid;
赋值运算符
用于将值赋给变量绑定。
- 简单赋值 (
=
):variable = value
- 复合赋值:
+=
,-=
,*=
,/=
,%=
(例如a += b
等价于a = a + b
)
let score = 0;
score += 10; -- score 现在是 10
注意:由于 Flurry 可能默认绑定是可变的,let
声明的 score
可以被复合赋值操作修改。
其他重要操作符/结构
Flurry 还包含一些具有特殊语法和用途的操作符或结构,其中许多采用后缀形式(详见下一节):
- 选择运算符 (
.
): 用于访问成员、调用方法、限定路径等。 - 解引用 (
.*
): 后缀操作符,用于访问指针指向的值。 - 取引用 (
.ref
): 后缀操作符,用于获取一个值的指针。 - 取像 (
'
): 后缀操作符,用于expr ' image
操作。 - 调用操作符:
()
(函数调用),[]
(索引调用),{}
(expr object
调用/记录调用),<>
(泛型/钻石调用)。 - 错误/可选处理:
!
(错误传播/处理),?
(可选类型处理)。 - 效应处理:
#
(效应处理块)。 - 属性标记 (
^
): 用于附加属性。 - 管道操作符 (
|
,|>
): 用于函数式风格的数据流处理。
编译时连接运算符 (++
)
++
: 这个运算符专用于编译时,用于连接两个集合类的值。- 适用类型: 其具体行为取决于操作数类型所实现的特定 Trait(可重载)。常见的应用包括:
- 连接编译时字符串 (
str
)。 - 连接编译时列表 (
meta.List
)。 - 连接编译时集合 (
meta.Set
)。 - 连接
bitvec
抽象视图(编译器会将其优化为位操作)。
- 连接编译时字符串 (
- 限制: 不能直接用于运行时的值(除了
bitvec
抽象视图映射这种特殊情况,其本质也是编译时指导的优化)。
comptime {
const part1 = "Hello, ";
const part2 = "Flurry!";
const message = part1 ++ part2; -- message == "Hello, Flurry!"
const list1 = meta.List.new(1, 2);
const list2 = meta.List.new(3, 4);
const combined_list = list1 ++ list2; -- combined_list == [1, 2, 3, 4]
-- let runtime_vec1 = Vec.new();
-- let runtime_vec2 = Vec.new();
-- let result = runtime_vec1 ++ runtime_vec2; -- 编译错误!++ 不能用于运行时 Vec
}
关于位运算符
值得再次强调,Flurry 不提供内建的按位运算符(如 C/Java/Rust 中的 &
, |
, ^
, ~
, <<
, >>
)。进行位级别的操作需要:
- 使用
bitvec
抽象视图: 通过取像操作value'bitvec
获取值的位向量表示,然后对其进行连接 (++
) 或其他位操作(这些操作会被编译器优化)。 - 使用标准库函数: Flurry 计划提供一个内置的
math
(或其他)包,其中包含执行位移、按位与/或/异或等操作的函数(例如math.bit_and(a, b)
)。
这种设计选择旨在减少操作符集合的歧义,并将底层位操作封装在更明确的抽象(bitvec
)或库函数中。
运算符优先级与结合性
Flurry 使用 Pratt 解析器常用的绑定力 (Binding Power) 或等效的优先级方案来确定复杂表达式中运算符的计算顺序。您提供的 OpTable
Scala 代码片段清晰地展示了不同 token 对应的优先级数值(数值越高,通常绑定得越紧密)。
例如,根据该表:
- 乘法/除法/取模/
++
(70
) 的优先级高于加法/减法 (60
)。 - 加法/减法 (
60
) 的优先级高于比较运算符 (40
)。 - 比较运算符 (
40
) 的优先级高于逻辑与 (30
) 和逻辑或 (20
)。 - 后缀操作符如选择 (
.
, 100)、取像 ('
, 100)、调用 (()
,[]
,{}
,<>
, 90) 和可能的字面量拓展 (id
, 110) 具有非常高的优先级。
开发者通常不需要死记硬背完整的优先级表,而是遵循数学和逻辑运算的常规顺序,并在必要时使用圆括号 ()
来明确指定求值顺序。
let result = a + b * c; -- 等价于 a + (b * c),因为 * 优先级更高
let flag = x > 0 and y < 10; -- 等价于 (x > 0) and (y < 10)
let complex = (a + b) * (c - d); -- 使用括号强制优先级