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的(外部)类型也可以使用结构初始化语法进行初始化。类和处理类型不能取消初始化。

(参阅 Structs, Classes, Handles ).

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).