【问题标题】:Compilation error only when using the repl仅在使用 repl 时出现编译错误
【发布时间】:2021-09-26 07:31:10
【问题描述】:

只有在 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?)

此屏幕截图可能会使其更清晰。在左边我只是粘贴代码,在右边我一行一行地输入它。代码仍在运行,但导致错误的原因是什么?

由于某种原因,我无法仅使用一个类来重现这一点。

【问题讨论】:

  • 事实上,我在第一种形式中也遇到了错误,至少在最新版本中是这样。
  • @jjmerelo:是的,我刚刚尝试了最新版本,即使粘贴了它也会报告错误。我安装的版本是一个月前构建的,只有在逐行输入时才会报告错误。所以这是一个错误?
  • 看起来很奇怪,我不能只使用一个类来重现这个。
  • 如下所示,它可能不是一个错误,而只是 REPL 没有准备(或应该准备)的东西。您可能希望将其作为错误提交,但在这种情况下,我的建议是使用 IDE。
  • 顺便说一句,它不会抱怨,如果你用}} one-liner 关闭这些

标签: raku read-eval-print-loop rakudo


【解决方案1】:

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

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

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

无论是直接粘贴还是分段粘贴。它总是第二类。这可能与EVAL 的工作方式有关,但我真的不知道。归根结底,REPL 只能带您到此为止,我不完全确定这是否在用例范围内。您可能想要使用Comma 或任何其他IDE,如emacs,来处理比一行更复杂的任何事情;逗号还为评估表达式甚至语法提供帮助。

【讨论】:

  • 我是通过emacs 发现的。当前用于 emacs 的 raku-mode 似乎对 repl 没有太多支持。当我尝试加载文件时,它将整个缓冲区逐行发送到repl。我用require 破解了一些文件,但是eval-region 等东西需要repl 有这个容量。
【解决方案2】:

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

  • 高尔夫你的例子是一个绰绰有余的MRE。但我喜欢尽量减少错误示例。

  • 推测我想我可以看到发生了什么。

  • 搜索问题队列 Rakudo 在 GH 上有 两个 问题队列:oldnew

  • Spelunking 编译器代码 Rakudo 主要是用 Raku 编写的;也许我们可以弄清楚 REPL 代码(这是编译器的一部分)中的这个问题是什么?

打高尔夫球

首先,bug:

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 会话期间尝试维护声明/状态(变量和类型等)。

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

据推测,除了空白行之外,其他任何内容都被视为之前的代码。

一些/任何先前代码的存在会以某种方式影响 REPL 正在维护的状态和/或它要求编译器执行的操作。

也许吧。


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

大概是 REPL 和/或编译器区分这两种声明。

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


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

推测该类型编译成功,然后才抛出异常(因为缺少右括号,代码不完整)。

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

这种重复的编译尝试再次包括类型声明,该类型声明,因为来自先前尝试编译多行代码的编译状态以某种方式被保留,编译器将其视为重复 em> 声明,导致它抛出异常,导致REPL退出多行模式并报错。


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

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

  2. 如果抛出异常:

    2.1 如果处于多行模式(带有* 提示符),则退出多行模式(返回&gt; 提示符)并显示异常消息。

    2.2 否则(因此不在多行模式下),如果对异常和/或输入的代码的分析(可能非常基本)表明多行模式很有用,则进入该模式(带有* 提示) .在多行模式下,每次用户按 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 表示Liz added this a couple months ago。这与此错误非常相干,但我猜名称中带有 ctx 的方法和变量是事物的核心,因此希望这是开始思考这个问题的好方法。

  • method repl-eval。 30行左右。

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

今晚就可以了。我会发布这个,然后改天再回来。

【讨论】:

  • "要进入球道,请输入任何不只是空格的行,然后按 Enter。"巧合的是,就在本周,Raku REPL 中只有反斜杠的行有一个线程(也就是当你认为你正在输入一个连续字符但你没有输入时):nntp.perl.org/group/perl.perl6.users/2021/07/msg10039.html
  • @jubilatious1 ❶ 您的评论让我尝试输入反斜杠。正如您讨论的主题,它给出了{} 响应。但它也没有让我上球道。与编译器拒绝的任何代码相同。再试验几秒钟后,我的猜测是反斜杠会导致正在被吃掉的警告(“你不能反斜杠”)。所以: ❷ 使第 1 步类似于“输入任何编译时没有异常的行——错误或警告”。 ❸ 不到 500 行稀疏且看起来很简单的 Raku 代码,似乎值得研究 REPL.pm,所以这就是我希望达到的目标。
猜你喜欢
  • 2012-01-14
  • 2020-09-19
  • 2013-06-16
  • 2013-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多