跳转至

控制流程

分支包含:

if (条件表达式) {
    代码块
} elif (条件表达式) {
    代码块
} else {
    代码块
}

代码 if 条件表达式;

条件表达式 then 代码;

与 C 类似的部分,不会过多赘述

分支

条件分支 if

if 语句是一种条件分支,根据条件执行不同的代码块。

if (条件表达式) {
    代码块
}

if (条件表达式) 代码;

也可以后置 if 语句。

代码 if 条件表达式;

可以在 if 中写多个表达式,用 , 隔开。条件表达式为最后一个表达式。
表达式中可以定义变量,其作用域为 if 语句内

if (表达式, 表达式, 条件表达式) {
    代码块
}
if (var a = my_func(), a != null) return a;
// 等效于
if (var a = my_func()) return a;

条件分支 else

else 语句是一个可选的标签,用于处理没有匹配的情况。

if (条件表达式) {
    代码块1
} else {
    代码块2
}

条件分支 then

then 用于表达式和语句之间,在表达式为真时执行语句。

条件表达式 then 代码;
a > 0 then println("a is positive.");

多条件分支 elif

elif 语句是一个可选的标签,用于处理多个条件的情况。

if (条件表达式1) {
    代码块1
} elif (条件表达式2) {
    代码块2
} else {
    代码块3
}

多条件分支 switch

switch 语句是一种多条件分支,根据条件执行不同的代码块。
实际上的实现就是 if - elif - else 序列,只是更加简洁。
由于现代编译器的优化,在编译时将 switch 展开为 if 并不会导致低性能

switch (表达式) {
    常量表达式1: {
        代码块1
    }
    常量表达式2: {
        代码块2
    }
    @default: {
        代码块3
    }
}

switch 中的每个条目由 常量表达式 + : + 语句或代码块 组成。

为什么不使用 case

Lumos 不会像 C 语言那样只把 case 处当作跳转的标签(要和普通标签区分开),需要在下一段代码前手动 break,所以不需要和普通标签区分,直接出现在 switch 内的必定是条件表达式。

leave 语句用于跳出 switch 语句。
@next 语句用于跳到下一个条件的处理过程。
@default 是一个可选的标签,用于处理没有匹配的情况。

不是?你属性咋跑这来了

switch (num) {
    1: {
        for (var i in [2, 3, 5, 7, 11]) {
            println(i);
        }
    } // 代码块结束,自动跳出
    2: @next; // 向下跳
    3: @next; // 向下跳
    4: {
        println("2 或 3 或 4");
    }
    @default: leave; // 跳出 switch
}

任何数据类型,只要重载 == 运算符就能支持 switch,只要重载 \hash 运算符就能进行匹配优化。

多条件分支 match

switch 类似,match 语句是一种多条件分支,根据条件执行不同的代码块,但条件为匹配表达式而不是匹配值。
match 语句是 switch 语句的增强版

match (表达式) {
    匹配表达式1: {
        代码块1
    }
    匹配表达式2: {
        代码块2
    }
    @default: {
        代码块3
    }
}

无初始表达式的 match 语句。
可以当作另一种 if - elif - else 使用。

match {
    条件表达式1: {
        代码块1
    }
    条件表达式2: {
        代码块2
    }
    @default: {
        代码块3
    }
}

相当于:

if (条件表达式1) {
    代码块1
} elif (条件表达式2) {
    代码块2
} else {
    代码块3
}

循环

@limit(次数) 属性可以限制循环的最大执行次数,防止出现无限循环,其实现为循环达到指定次数后自动调用 break

@limit(10)
for (int i = 0; i < 100; i++) {
    println(i);
} breaked {
    println("Limit reached.");
}

@unroll 属性可以展开编译时已知次数的循环,减少循环的开销。

@unroll
for (int i = 0; i < 10; i++) {
    println(i);
}

条件循环 while

while 循环是一种条件循环,只要条件为真,循环就会一直执行。

int i = 0;
while (i < 10) {
    println(i);
    i++;
}
while (条件表达式) {
    循环体
}

条件循环 do while

do while 循环是一种条件循环,先执行循环体,再判断条件。

int i = 0;
do {
    println(i);
    i++;
} while (i < 10);
do {
    循环体
} while (条件表达式);

计数循环 for

for 循环是一种计数循环,可以在循环体内使用计数器。

for (int i = 0; i < 10; i++) {
    println(i);
}
for (初始化语句; 条件表达式; 更新语句) {
    循环体
}

当我们不需要计数器变量时可以直接:

for (迭代次数) {
    循环体
}

遍历循环 for

for 循环是一种遍历循环,可以在循环体内遍历容器。

for (val i : [1, 2, 3, 4, 5]) {
    println(i);
}

for (val i : 1 .. 5) {
    println(i);
}
for (变量 : 容器) {
    循环体
}

循环标签

给循环添加标签可以用于快速跳出多层循环。break

loop:
for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) {
        if (j == 5) break loop;
        println(j);
    }
}

跳转

跳过本次循环 continue

continue 用于跳过本次循环,跳到下一次循环。

有等效的 goto 语句:

for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) continue;
    println(i);
}

for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) goto next;
    println(i);
  next:
}

跳出循环 break

break 用于跳出循环。

有等效的 goto 语句:

for (int i = 0; i < 10; i++) {
    if (i == 5) break;
    println(i);
}

for (int i = 0; i < 10; i++) {
    if (i == 5) goto end;
    println(i);
}
end:

跳出多层循环

利用 break 加循环标签可以快速跳出多层循环。

loop:
for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) {
        if (i == 5 && j == 5) break loop;
        println(i, j);
    }
}

利用 break 加层数可以快速跳出多层循环。
默认的跳出一层就是 break 1;

for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) {
        if (i == 5 && j == 5) break 2;
        println(i, j);
    }
}

跳出代码块 leave

leave 用于跳出代码块。
必须是使用 {} 包裹的代码块。下面的示例语句不算是代码块。

leave 也可以跳出 if switch 的代码块。

if (表达式) leave;

使用示例:

{
    int a = 1;
    if (a == 1) leave;
    println("Hello, World!");
}   // 不会打印文本

如果是可求值代码块,leave 后跟代码块的求值表达式。类似 return

let a = val {
    int b = 1;
    if (b == 1) leave 0;
    b; // 不会执行到此处
}
println(a); // 0

作为函数体的代码块只能 return 而不能 leave

fn my_func(int a) {
    if (a == 1) leave; // 会报错,请使用 return
    println("Hello, World!");
}

跳出后

当循环被中断时 breaked

不过有人提议使用 terminated

breaked 用于在循环被 break 中断时执行代码。
减少逆天的嵌套

for (int i = 0; i < 10; i++) {
    if (i == 5) break;
    println(i);
} breaked {
    println(`Loop breaked with i = $i.`);
}

可以在 breakedcontinue 重新进入循环。
本次循环会被跳过

for (int i = 0; i < 10; i++) {
    if (i == 5) break;
    println(i);
} breaked {
    println(`Loop breaked with i = $i.`);
    continue;
}

当循环正常结束时 then

then 用于在循环正常结束时执行代码。

for (int i = 0; i < 10; i++) {
    println(i);
} then { // 会执行
    println(`Loop finished with i = $i.`);
}
for (int i = 0; i < 10; i++) {
    if (i == 5) break;
    println(i);
} then { // 不会执行
    println(`Loop finished with i = $i.`);
}

以上两个可以同时使用

breakedthen 中都可以使用迭代变量

跳转

标签跳转 goto

goto 用于跳转到指定标签。

  loop:
    println("Hello world!");
    goto loop;

对于标签跳转,无法跳过变量的初始化。

    if (某些条件) goto label; // 这是不行的,因为跳转到 label 后无法确定 my_var 的值。
    size_t my_var = 10;
    println(my_var);
  label:
    println("Hello world!");

任意地址跳转 goto

goto 用于跳转到任意地址,对应汇编中的 jmp 指令。

goto 0x12345678;

对标签取地址后为其后一行代码地址,类型为 void*

  loop:
    println("Hello world!");
    let addr = &loop;
    goto addr;