MCFPP设计——Lambda

语法设计

采用for-in Expression and Lambda Expression 未来的Lambda表达式设计 · MinecraftFunctionPlusPlus/MCFPP · Discussion #33中的设计,敲定Lambda语法如下:

func test(Func<int,int> process){
    dynamic int a = 10;
    print(a);
    a = process(a);
    print(a);
}

func lamb(x){
    return x + 1;
}

func main(){
    test((x){return x + 1;}); # OK
    test(x){return x + 1;} # OK
    test(lamb); # OK
    var qwq = lamb;
    test(qwq); # OK
}

lambda在MCFPP对应两种类型:Func<>类型和Action<>类型。前者表示一个拥有返回值的lambda,其泛型参数列表的最后一个是返回值,其余是lambda的参数。例如Func<int, int>表示一个返回值为int,同时接收一个int类型作为参数的lambda。而Action表示没有返回值的lambda,其泛型参数列表就表示lambda接收的参数类型。例如Action<int, int>表示接收两个int类型作为参数,没有返回值的lambda。

实现

无论是Func还是Action,在MCFPP中的实现方式都是类似的。变量储存方面,lambda类型将会被储存为一个NBT复合标签。假设我们有一个如下的Lambda:

func main(){
    int x = 50;
    int y = 50;
    test {
        print(x);
        print(y);
    }
}

func test(Action action){
    action();
}

结构如下:

{
    %id: "default:lambda_1",    //mcfunction函数的命名空间ID
    x: 50,    //捕获到的外部函数的x值
    y: 50     //捕获到的外部函数的y值
}

譬如在执行到test函数的时候,首先构造lambda。注意此时编译已经完成,我们讨论的是执行的过程。首先将要捕获的xy变量传入lambda NBT中,也就是上面NBT中描述的。随后,将整个lambda NBT作为参数传递给test方法。随后,根据action中的内容,调用对应的lambda函数,同时把lambda NBT中的变量传入lambda函数的栈中。在调用完毕后,还需要将栈中被修改过的lambda NBT中的变量传回lambda NBT,从而更新修改。

对于编译器来说,已知的Lambda变量和未知的Lambda变量的唯一区别,就是Lambda中包含的函数是否是已知的。对于未知的变量来说,将会用宏的方式,使用lambda NBT中的%id字段调用函数,而对于已知的变量来说,则会直接使用function命令调用函数。

类似文章

发表回复