主要子程序

时间:2019-07-15 13:41:14

标签: main perl6 subroutine raku

场景

想象一下,您有一个模块X,其功能可通过命令行界面供用户使用。这种模块本身并不能做很多事情,但是它允许其他人创建类似于插件的模块,他们可以将其连接到模块X。理想情况下,可以通过X的CLI使用这些插件。

我的问题是

您需要做什么才能连接任何功能 插件可以提供给X的CLI?

这意味着插件将需要提供一些描述命令的结构,需要调用的内容以及希望该插件的用法消息。因此,当您运行X的CLI时,插件的命令和帮助消息会显示在常规X的CLI的帮助消息中。

示例

main.p6

use Hello;
use Print;

multi MAIN('goodbye') {
    put 'goodbye'
}

lib/Hello.pm6

unit module Hello;

our %command =  %(
    command => 'hello',
    routine => sub { return "Hello" },
    help    => 'print hello.'
);

lib/Print.pm6

unit module Print;

our %command =  %(
    command => 'print',
    routine => sub { .print for 1..10 },
    help    => 'print numbers 1 through 10.'
);

程序main.p6是此方案的简化版本。我想通过以下方式整合Hello.pm6Print.pm6提供的信息 将它们各自的%command变量放入MAIN中两个新的main.p6子中。

这可能吗?如果是这样,我将如何实现它?

1 个答案:

答案 0 :(得分:3)

这看起来有点像StackOverflow问题,但是无论如何我都会尝试一下。这里有几个问题。首先是这样注册命令,以便MAIN可以发出一条消息,说“这可以做到”,其次是实际执行命令。如果两者在编译时都可以知道,则可能可以解决。但是,让我们看看实际的代码将如何运行。我只做第一部分,其余的作为练习。

第一件事是%command需要以某种方式导出。您无法像现在那样做。首先,因为它没有显式导出;如果是这样,您最终将在外部范围中得到几个具有相同名称的符号。因此,我们需要将其更改为一个类,以使实际符号对该类而言是词法,并且不会污染外部作用域。

unit class Hello;

has %.command =  %(
    command => 'hello',
    routine => sub { return "Hello" },
    help    => 'print hello.'
);

(同样适用于Print

只要有,其余的就不那么困难了,只需要我们进行内省以了解实际的情况,就像一个小小的黑客一样:

use Hello;
use Print;

my @packages=  MY::.keys.grep( /^^<upper> <lower>/ );
my @commands = do for @packages -> $p {
    my $foo = ::($p).new();
    $foo.command()<command>
};

multi MAIN( $command where * eq any(@commands) ) {
    say "We're doing $command";
}

我们检查符号表,查找以大写字母开头,然后是其他非大写字母开头的软件包。碰巧唯一的软件包就是我们感兴趣的软件包,但是,当然,如果您想将其用作插件机制,则应使用对它们唯一的任何模式。 然后,我们创建这些新软件包的实例,并调用命令自动生成的方法以获取命令的名称。这正是我们用来检查MAIN子例程中是否执行正确命令的方法,方法是使用where签名来检查我们使用的字符串是否实际上在已知列表中命令。

由于@packages也提供了功能和其余功能,因此实际调用它们(或给出附加消息或其他内容)作为练习。

更新:您可能希望签出this other StackOveflow answer作为模块中签名的替代机制。