仅在使用 repl 时出现编译错误

Compilation error only when using the repl

只有在repl中逐行输入代码时,我才收到错误。当一次粘贴整个程序或从命令行粘贴时,它会起作用。

class A {
    method a () {
    return 1;
    }
}

class B {
    method b () {
    return 2;
    }
}

这是错误陈述:

===SORRY!=== Error while compiling:
Package 'B' already has a method 'b' (did you mean to declare a multi method?)

这个屏幕截图可能会更清楚。左边我只是贴了代码,右边是我一行一行输入的。代码仍然有效,但导致错误的原因是什么?

出于某种原因,我无法仅使用一个 class。

我可以重现该错误,看起来像是 REPL 错误,或者只是 REPL 不准备做的事情。例如,这也会引发异常:

class A {
    method a() {
    return 1;
    }
};

class foo {
    has $.bar = 3;
};

两种形式,要么直接粘贴,要么分段粘贴。永远是第二个class。这可能与 EVAL 的工作方式有关,但我真的不知道。归根结底,REPL 只能带你走这么远,我不确定这是否在用例内。您可能想使用 Comma 或任何其他 IDE,例如 emacs,用于任何比一行更复杂的东西;逗号还为计算表达式甚至语法提供帮助。

我认为Comma是蜜蜂的膝盖。而且我几乎从不使用repl。但我喜欢:

  • 打高尔夫球 你的例子已经足够了MRE。但我喜欢尽量减少错误示例。

  • 猜测我想我能看出是怎么回事了

  • 正在搜索问题队列 Rakudo 在 GH 上有 两个 问题队列:old and new.

  • Spelunking 编译器代码 Rakudo 主要是用 Raku 编写的;也许我们可以找出 REPL 代码(编译器的一部分)中的问题所在?

打高尔夫球

首先,错误:

Welcome to ™ v2021.03.
Implementing the ™ programming language v6.d.
Built on MoarVM version 2021.03.

To exit type 'exit' or '^D'
> # 42
Nil
> { subset a
* 
===SORRY!=== Error while compiling:
Redeclaration of symbol 'a'.
at line 3
------> <BOL>⏏<EOL>

评论:

  1. 要进入球道,请输入任意一行(不只是空格),然后按 Enter。

  2. 挑对铁;使用 { 打开一个块,声明一些命名类型,然后按 Enter。 REPL 通过显示 * 多行提示来指示您处于绿色状态。

  3. 要下沉球,只需按 Enter。


二、打高尔夫球助投机:

> # 42
Nil
> { BEGIN say 99
99
* }
99
> 

BEGIN 标记在编译过程中编译器遇到它时要 运行 的代码。)

推测

为什么初始 # 42 评估很重要?据推测,REPL 试图在 REPL 会话期间维护声明/状态(变量和类型等)。

作为其中的一部分,它可能会记住会话中所有以前的代码。

大概它看到的除了空白行以外的任何内容都算作以前的代码。

仅存在 some/any 以前的代码就会以某种方式影响 REPL 维护的状态 and/or 它要求编译器做什么。

也许吧。


为什么类型声明很重要,而变量声明不重要?

大概是 REPL and/or 编译器在区分这两种声明。

忽略 REPL,在编译代码时,重复的 my 声明只会引发 警告 ,而重复的类型声明是错误的。很可能这就是为什么?


为什么类型声明有这种效果?

推测该类型成功编译,并且仅在此之后抛出异常(因为缺少右大括号导致代码不完整)。

然后 REPL 要求编译器 再次 尝试编译到目前为止输入的多行代码,以及用户输入的任何附加代码(在我输入的高尔夫版本中什么都没有,只需按 Enter,不再添加任何代码)。

这种重复的编译尝试再次包含类型声明,由于先前尝试编译多行代码的编译状态以某种方式被保留,该类型声明被编译器视为 重复声明,导致抛出异常导致REPL退出多行模式并报错


换句话说,REPL 循环大概是这样的:

  1. 输入每一行时,将其传递给编译器,编译器编译代码并在出现任何错误时抛出异常。

  2. 如果抛出异常:

    2.1 如果处于多行模式(有*提示),则退出多行模式(回到>提示)并显示异常信息。

    2.2 否则(因此不在多行模式下),如果对输入代码异常 and/or 的分析(可能非常基本)表明多行模式有用,则进入该模式(使用 *提示)。在多行模式下,每次用户按 Enter 时都会重新编译到目前为止的整个多行代码。

    2.3 否则,显示异常信息。

  3. (显然还有一些与初始化相关的事情正在发生,因为需要从一些评估开始以显示此错误,但这很可能是一个完全不同的问题。)

正在搜索

我浏览了 GH 上匹配 'repl' 的新旧队列中所有未解决的 Rakudo 问题。我选择了四个来说明 REPL 在维护会话状态方面遇到的困难范围:

我还没有做的一件事是检查这些错误是否仍然存在。我的猜测是他们这样做。还有很多其他人像他们一样。也许他们有一些共同的原因?我不知道。也许我们需要看看代码...

洞穴探险

在 Rakudo 资源中搜索 'repl' 很快找到了 REPL 模块。不到 500 行高级 Raku 代码! \o/(现在,让我们假装我们几乎可以忽略深入研究它调用的代码...)

在我最初的浏览器中,我会提请注意:

  • 一个sub repl:

    sub repl(*%_) {
      my $repl := REPL.new(nqp::getcomp("Raku"), %_, True);
      nqp::bindattr($repl,REPL,'$!save_ctx',nqp::ctxcaller(nqp::ctx));
      $repl.repl-loop(:no-exit);
    }
    

    Blame shows that Liz added this a couple months ago。它与这个错误非常相切,但我猜测名称中带有 ctx 的方法和变量对事物非常重要,因此希望这是开始思考的好方法。

  • method repl-eval。 30行左右。

  • REPL: loop { ... }。 60行左右。

今晚就到此为止。我会 post 这个然后 return 改天。