2.3. 语句

Daslang 程序是一个简单的语句序列:

stats ::= stat [';'|'\n'] stats

Daslang 中的语句与 C 系列语言(C/C++、Java、C# 等)中的语句相当:有赋值、函数调用、程序流控制结构等。 还有一些自定义语句,如 blocks、structs 和 initializers(本文档稍后将详细介绍)。 语句可以用换行符分隔,或者 ‘;’。

2.3.1. 可见性块

visibility_block ::= '{' (stat)* '}'

由大括号 ({ }) 分隔的语句序列称为 visibility_block。

2.3.2. 控制流语句

Daslang 实现最常见的控制流语句: if, while, for

2.3.2.1. true 和 false

Daslang 具有强布尔类型 (bool)。只有具有布尔类型的表达式才能成为控制语句中条件的一部分。

2.3.2.2. if/elif/else 语句

stat ::= 'if' ( exp ) visibility_block (['elif' ( exp ) visibility_block])*  ['else' visibility_block]

根据表达式的结果有条件地执行语句:

if ( a > b ) {
    a = b
} elif ( a < b ) {
    b = a
} else {
    print("equal")
}

一行 if 语句:

if ( a > b ) a = b  // 这是一个单行表示法
a = b if ( a < b )  // 这是一个后缀表示法

2.3.2.3. while 语句

stat ::= 'while' ( exp ) stat

在条件为 true 时执行语句:

while ( true ) {
    if ( a<0 ) {
        break
    }
}

2.3.3. 基于范围的循环

2.3.3.1. for

stat ::= 'for' ( iterator 'in' [rangeexp] ) visibility_block

按顺序为 expression 中的每个元素/迭代器执行一个循环体语句:

for ( i in range(0, 10) ) {
    print("{i}")       // 将打印 0 到 9 之间的数字
}

// 或

let arr: array<int>
resize(arr, 4)
for ( i in arr ) {
    print("{i}")       // 将从第一个元素到最后一个元素打印数组的内容
}

// 或

var a: array<int>
var b: int[10]
resize(a, 4)
for ( l, r in a, b ) {
    print("{l}=={r}")  // 将打印数组的内容和数组 B 的前 4 个元素
}

// 或

var tab: table<string; int>
for ( k, v in keys(tab), values(tab) ) {
    print("{k}:{v}")   // 将打印表格的内容,格式为 key:value
}

可迭代类型是通过迭代器实现的 (参阅 Iterators).

2.3.4. break

stat ::= 'break'

break 语句终止循环的执行 (forwhile).

2.3.5. continue

stat ::= 'continue'

continue 运算符跳转到循环的下一个迭代,跳过其余语句的执行。

2.3.6. return

stat ::= return [exp]
stat ::= return <- exp

return 语句终止当前函数、块或 lambda 的执行,并可选择返回表达式的结果。如果省略表达式,则函数将不返回任何内容,并且假定返回类型为 void。 从同一函数返回不匹配的类型是一个错误(即,所有返回都应返回相同类型的值)。 如果函数的返回类型是显式的,则返回表达式应返回相同的类型。

例:

def foo(a: bool) {
    if ( a ) {
      return 1
    } else {
      return 0.f  // 错误, 不同的返回类型
    }
}

def bar(a: bool): int {
    if ( a ) {
      return 1
    } else {
      return 0.f  // 错误, 不匹配的返回类型
    }
}

def foobar(a) {
    return a  // return type 将与 argument type 相同
}

在生成器块中,return 必须始终返回布尔表达式,其中 false 表示生成结束。

‘return <- exp’ 语法用于 move-on-return:

def make_array {
    var a: array<int>
    a.resize(10)  // 填充一些东西
    return <- a   // return 将返回
}

let a <- make_array() //创建填充make_array的数组

2.3.7. yield

Yield 的作用类似于生成器的 ``return``(参阅 Generators).

它类似于 return 语法,但只能在 generator 块中使用。

Yield 必须始终生成与生成器的值匹配的值:

var gen <- generator<int>() <| $ {
    yield 0         // int 0
    yield 1         // int 1
    return false
}

2.3.8. Finally 语句

stat ::= finally visibility-block

Finally 声明一个区块,该区块将对任何区块(包括控制语句)执行一次。 finally 块不能包含 break, continue, 或 return 语句。 它旨在确保在 “all are done ” 之后执行。请考虑以下:

def test(a: array<int>; b: int) {
    for ( x in a ) {
        if ( x == b ) {
            return 10
        }
    }
    return -1
} finally {
    print("print anyway")
}

def test(a: array<int>; b: int) {
    for ( x in a ) {
        if ( x == b ) {
            print("we found {x}")
            break
        }
    } finally {
         print("we print this anyway")
    }
}

最后,可用于资源取消分配。

可以使用 defer 宏将代码添加到块的 finally 语句中:

require daslib/defer

def foo {
    print("a\n")
} finally {
    print("b\n")
}

def bar {
    defer() {
        print("b\n")
    }
    print("a\n")
}

在上面的例子中,函数 foobar 在语义上是相同的。 多个 defer 语句以相反的顺序出现。

defer_delete 宏为其参数添加 delete 语句,并且不需要块。

2.3.9. 局部变量声明

initz ::= id [:type] [= exp]
initz ::= id [:type] [<- exp]
initz ::= id [:type] [:= exp]
scope ::= `inscope`
ro_stat ::= 'let' [scope] initz
rw_stat ::= 'var' [scope] initz

可以在函数中的任何位置声明局部变量。它们存在于其声明和声明它们的可见性块的末尾之间。 let 声明只读变量,而 var 声明可变 (读写) 变量。

复制 =、移动 -> 或克隆 := 语义指示如何初始化变量。

如果指定了 inscope , 则 delete id 语句被添加到块的 finally 部分,其中声明了变量。 它不能直接出现在 loop 块中,因为 loop 的 finally 部分只执行一次。

2.3.10. 函数声明

stat ::= 'def' id ['(' args ')'] [':' type ] visibility_block

arg_decl = [var] id (',' id)* [':' type]
args ::= (arg_decl)*

声明一个新函数。例子:

def hello {
    print("hello")
}

def hello(): bool {
    print("hello")
    return false
}

def printVar(i: int) {
    print("{i}")
}

def printVarRef(i: int&) {
    print("{i}")
}

def setVar(var i: int&) {
    i = i + 2
}

2.3.11. try/recover

stat ::= 'try' stat 'recover' visibility-block

try 语句包含一个代码块,其中可能发生 panic 情况,例如致命运行时错误或 panic 函数。try-recover 子句提供 panic 处理代码。

重要的是要理解 try/recover 不是正确的错误处理代码,也绝对不是实现 control-flow 的方法。 与 Go 语言非常相似,这实际上是一个无效的情况,通常不应该在生产环境中发生。 潜在异常的示例包括取消引用 null 指针、索引到越界数组等。

2.3.12. panic

stat ::= 'panic' '(' [string-exp] ')'

调用 panic 会导致运行时异常,并在日志中提供 string-exp。

2.3.13. 全局变量

stat ::= 'let|var' { shared } {private} '\n' id '=' expression
stat ::= 'let|var' { shared } {private} '\n' id '<-' expression
stat ::= 'let|var' { shared } {private} '\n' id ':=' expression

声明一个常量全局变量。 此变量在脚本初始化期间初始化一次(或每次手动调用脚本 init 时初始化)。

shared 表示该常量将初始化一次,并且其内存在 Daslang 上下文的多个实例之间共享。

private 表示该变量在其模块之外不可见。

2.3.14. enum

enumerations ::= ( 'id' ) '\n'
stat ::= 'enum' id { enumerations }

声明一个枚举 (参阅 Constants & Enumerations).

2.3.15. 表达式语句

stat ::= exp

在 Daslang 中,每个表达式也都允许是一个语句。 如果是这样,则表达式的结果将被丢弃。