2.4. 表达式
2.4.1. 赋值
exp := exp '=' exp
exp := exp '<-' exp
exp := exp ':=' exp
Daslang 实现 3 种赋值:复制赋值 (=):
a = 10
复制赋值等同于 C++ memcpy操作:
template <typename TT, typename QQ>
__forceinline void das_copy ( TT & a, const QQ b ) {
static_assert(sizeof(TT)<=sizeof(QQ),"can't copy from smaller type");
memcpy(&a, &b, sizeof(TT));
}
“移动” 分配:
var b = new Foo
var a: Foo?
a <- b
移动分配使源 (b) 无效。 它的主要目的是正确移动所有权,并在您不需要繁重类型(例如数组、表)的 source 时优化复制。 某些外部处理类型可以是不可分配的,但仍然可以移动。
移动赋值等同于 C++ memcpy + memset操作:
template <typename TT, typename QQ>
__forceinline void das_move ( TT & a, const QQ & b ) {
static_assert(sizeof(TT)<=sizeof(QQ),"can't move from smaller type");
memcpy(&a, &b, sizeof(TT));
memset((TT *)&b, 0, sizeof(TT));
}
“克隆” 分配:
a := b
克隆分配是调用 clone(var a: auto&; b: auto&) (如果存在)或 POD 类型的基本分配的语法糖。 它还为 das_string、数组和表类型实现,并创建一个 ‘deep’ 副本。
某些外部处理类型可以是不可分配的,但仍然可以克隆 (参阅 Clone).
2.4.2. 运算符
2.4.2.1. .. 运算符
expr1 \.\. expr2
这相当于 interval(expr1,expr2)。默认情况下, interval(a,b:int) 被实现为 range(a,b),而 interval(a,b:uint) 被实现为 urange(a,b)。用户可以定义自己的区间函数或泛型。
2.4.2.2. ?: 运算符
exp := exp_cond '?' exp1 ':' exp2
这将根据表达式的结果有条件地计算表达式。 如果 expr_cond 为 true,则仅评估 exp1。同样,如果为 false,则只有 exp2.
2.4.2.3. ?? Null 合并运算符
exp := exp1 '??' exp2
根据 exp1 的结果有条件地评估 exp2。 给定的代码等效于:
exp := (exp1 '!=' null) '?' *exp1 ':' exp2
它计算表达式,直到第一个非 null 值(就像第一个 ‘true’ 值的 |运算符一样)。
运算符优先级也遵循 C# 设计,因此 ?? 的优先级低于 |
2.4.2.4. ?. 和 ?[ - Null 传播运算符
exp := value '?.' key
如果值不为 null,则取消引用 struct 的字段 ‘key’,否则返回 null。
struct TestObjectFooNative {
fooData : int
}
struct TestObjectBarNative {
fooPtr: TestObjectFooNative?
barData: float
}
def test {
var a: TestObjectFooNative?
var b: TestObjectBarNative?
var idummy: int
var fdummy: float
a?.fooData ?? idummy = 1 // 将返回对 idummy 的引用,因为 a 为 null
assert(idummy == 1)
a = new TestObjectFooNative
a?.fooData ?? idummy = 2 // 将返回对 a.fooData 的引用,因为 a 现在不为 null
assert(a.fooData == 2 & idummy == 1)
b = new TestObjectBarNative
b?.fooPtr?.fooData ?? idummy = 3 // 将返回对 idummy 的引用,因为 B 不是 null,而是 b。barData 仍为 null
assert(idummy == 3)
b.fooPtr <- a
b?.fooPtr?.fooData ?? idummy = 4 // 将返回对 b.fooPtr.fooData 的引用
assert(b.fooPtr.fooData == 4 & idummy == 3)
}
此外,索引 ?[ 可与表格一起使用:
var tab <- { "one"=>1, "two"=> 2 }
let i = tab?["three"] ?? 3
print("i = {i}\n")
它检查容器指针和密钥的可用性。
2.4.2.5. 算术
exp:= 'exp' op 'exp'
Daslang 支持标准算术运算符 +, -, *, / and %
。
它还支持紧凑运算符 +=, -=, *=, /=, %=
以及递增和递减运算符 ++ and --
:
a += 2
// 与写入相同
a = a + 2
x++
// 与写入相同
x = x + 1
所有运算符都是为数值和向量类型定义的,即 (u)int* 和 float* 和 double。
2.4.2.6. 关系
exp:= 'exp' op 'exp'
Daslang 中的关系运算符是: ==, <, <=, >, >=, !=
.
如果表达式为 false,则这些运算符返回 true,如果表达式为 true,则返回与 true 不同的值。
2.4.2.7. 逻辑
exp := exp op exp
exp := '!' exp
Daslang 中的逻辑运算符是 : &&, ||, ^^, !, &&=, ||=, ^^=
.
运算符 &&
(逻辑 and) 如果其第一个参数为 false,则返回 false,否则返回其第二个参数。
运算符 ||
(逻辑 OR) 如果不同于 false,则返回其第一个参数,否则返回第二个参数。
如果参数不同,则运算符 ^^
(逻辑异 or) 返回 true,否则返回 false。
理解&&和||不一定会“评估”他们的所有参数。 与它们的 C++ 等效项不同,&&= 和 ||= 也将取消右侧的评估。
“!”(否定)运算符如果给定值为 true,则返回 false,否则返回 false。
2.4.2.8. 按位运算符
exp:= 'exp' op 'exp'
exp := '~' exp
Daslang 支持标准的类 C 位运算符 &, |, ^, ~, <<, >>, <<<, >>>
。
这些运算符仅适用于整数值。
2.4.2.9. 管道作器
exp:= 'exp' |> 'exp'
exp:= 'exp' <| 'exp'
Daslang 支持 pipe 运算符。管道运算符类似于 ‘call’ 表达式,其中另一个表达式是第一个参数。
def addX(a, b) {
assert(b == 2 || b == 3)
return a + b
}
def test {
let t = 12 |> addX(2) |> addX(3)
assert(t == 17)
return true
}
def addOne(a) {
return a + 1
}
def test {
let t = addOne() <| 2
assert(t == 3)
}
lpipe
宏允许管道连接到上一行:
require daslib/lpipe
def main {
print()
lpipe() <| "this is string constant"
}
在上面的示例中,字符串 constant 将通过管道传输到上一行的 print 表达式。 这允许管道多个块,同时仍然使用重要的空格语法。
2.4.2.10. 运算符优先级
2.4.3. 数组初始化器
exp := fixed_array '(' [explist] ')'
exp := fixed_array '<' type '>' '(' [explist] ')'
创建新的固定大小数组:
let a = fixed_array<int>(1, 2) // 创建包含两个元素的数组
var a = fixed_array(1,2) // 完全根据推断自己的类型创建
exp := array '(' [explist] ')'
exp := array '<' type '>' '(' [explist] ')'
exp := '[' [explist] ']'
例如:
let a <- [1,2,3] // 创建并初始化推断类型 (int) 的数组
let a <- array(1,2,3) // 与上一行相同
let a <- array<int>(1,2,3) // 相同,但显式键入
let q <- [for x in range(0, 10); x * x] // 初始化数组<int>的推导式
(参阅 Arrays, Comprehensions).
2.4.4. Struct、Class 和 Handled 类型初始值设定项
struct Foo {
x: int = 1
y: int = 2
}
let fExplicit = Foo(x = 13, y = 11) // x = 13, y = 11
let fPartial = Foo(x = 13) // x = 13, y = 2
let fComplete = Foo(uninitialized x = 13) // x = 13, y = 2 with 'construct' syntax
let aArray <- array struct<Foo>((x=11,y=22),(x=33),(y=44)) // 具有 'construct' 语法的 Foo 的动态数组
类和Handled的(外部)类型也可以使用结构初始化语法进行初始化。类和处理类型不能取消初始化。
2.4.5. 元组初始化器
创建新元组:
let a = tuple<a:int;b:float;c:string>(a=1, b=2.0, c="3") // 创建具有命名字段的类型化元组
let b = tuple(1, 2.0, "3") // 推断元组类型
let c = (1, 2.0, "3") // 创建推断类型的元组
(参阅 Tuples).
2.4.6. 变体初始化器
变体是使用类似于结构体的语法创建的:
variant Foo {
i : int
f : float
}
let x = Foo(i = 3)
let y = Foo(f = 4.0)
let a = array variant<i:int;f:float>(i=3, f=4.0) // 无名数组
Variant 是一种弱类型,因此也可以声明为 alias:
typedef Foo = variant<i:int;f:float> // 与 structure-like 声明相同
(参阅 Variants).
2.4.7. 表初始化器
通过指定 key => 值对(以冒号分隔)来创建表:
var a <- { 1=>"one", 2=>"two" }
var a <- { 1=>"one", 2=>2 } // 错误、类型不匹配
(参阅 Tables).