Wait the light to fall

给 for 添加 else 分支

焉知非鱼

标准 Raku 中, for 关键字是没有 else 分支的, 但是 Raku 可以实现 for...else 方言, 新建一个模块, 命名为 Slang::ForElse, 其目录结构如下:

Slang/ForElse.pm6

其中 ForElse.pm6 的内容如下:

role ForElse::Grammar {
    rule statement_control:sym<for> {
        <sym><.kok> {}
        <.vetPerl5Syntax>
        <xblock(2)> {}
        [ 'else' <elseblock=pblock> ]?
    }
    rule vetPerl5Syntax {
        [ <?before 'my'? '$'\w+\s+'(' >
            <.typed_panic: 'X::Syntax::P5'> ]?
        [ <?before '(' <.EXPR>? ';' <.EXPR>? ';' <.EXPR>? ')' >
            <.obs('C-style "for (;;)" loop', '"loop (;;)"')> ]?
    }
}
role ForElse::Actions {
    use nqp;
    use QAST:from<NQP>;
    sub lookup(Mu \match, \key) {
        nqp::atkey(
                nqp::findmethod(match, 'hash')(match),
                key
                ).?ast
    }
    method statement_control:sym<for> (Mu $match) {
        my $forloop := callsame;
        if lookup($match, 'elseblock') -> $elseblock {
            $match.make:
                    QAST::Op.new: :op<unless>, $forloop,
                    QAST::Op.new: :op<call>,   $elseblock
        }
    }
}
sub EXPORT {
    $*LANG.define_slang:
            "MAIN",
            $*LANG.slangs<MAIN>         but ForElse::Grammar,
            $*LANG.slangs<MAIN-actions> but ForElse::Actions;
    return hash();
}

我们来测试一下这个模块, 在和 Slang 同一层级的目录中新建一个 for-else-example.pl6 的脚本, 其内容如下:

use lib 'Slang';
use ForElse;

my @values;
for @values -> $value {
    say "Got $value";
}
else {
    say "No values :("
}

发现打印出来是: No values :(