【问题标题】:"Too few positionals" in macro definition宏定义中的“位置太少”
【发布时间】:2018-12-20 19:40:17
【问题描述】:

我正在尝试创建一些以这种方式使用(实验性)宏的示例:

use experimental :macros;
macro cards_vars() { 
  (<hearts clubs diamonds spades> X~ 1..10).map: { "my \$$^x = False;" }  
};
cards_vars(); 
say $hearts1;

这将创建并运行宏,然后检查定义的变量之一是否存在。但我得到这个错误:

Too few positionals passed; expected 3 arguments but got 2

我什至不知道那个错误是从哪里来的。我认为它在cards_vars() 中,但我不知道是不是这样。声明不带括号的宏会产生同样的错误。

【问题讨论】:

    标签: macros raku


    【解决方案1】:

    更新:需要明确的是,宏不仅是实验性的,而且非常初级和有缺陷。如需了解更多信息,请参阅Moritz's answer to another macros question


    让我们从你的代码开始吧:

    use experimental :macros;
    macro foo { 42 }
    foo
    

    这会产生相同的编译时错误:

    Too few positionals passed; expected 3 arguments but got 2
    

    如果从宏中返回 AST 对象以外的任何对象,则会出现此错误。1 这是合理的,因为这是 macro 构造的全部要点。错误消息不太好——但宏又是一个实验性功能。


    所以你需要返回一个 AST。这是一种方法:

    use experimental :macros;
    macro foo { AST.new }
    foo
    

    这会产生 run-time 错误:

    Useless use of constant value foo in sink context (line 3)
    

    这表明宏已经完成了它的工作。编译器已完成编译并进入运行时。


    显式返回AST 对象是错误的处理方式。第一个原因在AST doc 页面上给出:

    目前还没有为 AST 定义 API。希望这将成为the work on macros 的一部分。

    第二个原因是构造AST对象的更高级别的构造,即文档页面和Scimon提到的quasi { ... }构造3

    use experimental :macros;
    macro foo { quasi { 42 } }
    say foo; # 42
    

    quasi 块告诉编译器将包含的语句编译成 AST 形式。

    请注意,结果不是对应于块的 AST。在上面它是一个对应于42的AST对象。


    所以,最后,回到你想要做的事情。

    简化:

    use experimental :macros;
    macro foo { quasi { my $bar } }
    foo;
    say $bar
    

    产量:

    Error while compiling ... Variable '$bar' is not declared
    

    鉴于:

    use experimental :macros;
    macro foo { quasi { class bar {} } }
    foo;
    say bar; # (bar)
    

    因此,最重要的是,词法声明目前无法坚持。

    实验还没有走到那一步。

    Rakudo 实验宏的创建者 Carl Mäsak 正在007 继续实验。如果您在其 GH 回购中留下问题,Carl 会回答。


    1 当编译器遇到对它知道是宏的例程的调用2 时,它会将宏调用替换为其结果。更详细的编译器:

    • 将所有宏的参数编译成AST形式;

    • 调用宏(在编译时)以 AST 形式传递参数;

    • 运行预期返回 AST 对象的宏例程(呃,"ASTish");

    • 将生成的 AST(ish) 对象拼接到正在构建的整体 AST 中。

    如果您不返回 AST(ish) 对象,则最后一步会出现错误,并显示标题中的错误消息。

    2 目前,如果您将宏定义放在调用之后,那么编译器不会意识到它是一个宏并将其视为普通例程:

    use experimental :macros;
    say foo; # 42
    macro foo { 42 }
    say bar; # AST.new
    macro bar { quasi { 42 } }
    say bar; # 42
    

    如果这会导致编译时错误可能会更好。

    3 宏冠军 Carl 喜欢 quasi 这个词。它借鉴了 lisp 的传统,而后者又来自于数学的传统。 Imo 这是一个“太可爱”的选择,不适合 P6。我的理由是 Perl 哲学建议新手选择具有熟悉含义的关键字。 “准”这个词有一个众所周知的正常英语含义,意思是“有点,但不是真的”。这根本没有帮助。 (我向 Carl 推荐了 toAST,但他更喜欢 quasi。)

    【讨论】:

      【解决方案2】:

      所以有点戳,我发现了这篇旧的万圣节文章:https://perl6advent.wordpress.com/2012/12/23/day-23-macros/

      这给出了一些指针。您需要使用quasi 突出显示您要输出的代码。但是让它创建一个变量?没有把握。如果可能的话,我还在设置。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-19
        • 2015-05-06
        • 2012-07-01
        相关资源
        最近更新 更多