仅在使用 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。但我喜欢尽量减少错误示例。
猜测我想我能看出是怎么回事了
-
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>
评论:
要进入球道,请输入任意一行(不只是空格),然后按 Enter。
挑对铁;使用 {
打开一个块,声明一些命名类型,然后按 Enter。 REPL 通过显示 *
多行提示来指示您处于绿色状态。
要下沉球,只需按 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 循环大概是这样的:
输入每一行时,将其传递给编译器,编译器编译代码并在出现任何错误时抛出异常。
如果抛出异常:
2.1 如果处于多行模式(有*
提示),则退出多行模式(回到>
提示)并显示异常信息。
2.2 否则(因此不在多行模式下),如果对输入代码异常 and/or 的分析(可能非常基本)表明多行模式有用,则进入该模式(使用 *
提示)。在多行模式下,每次用户按 Enter 时都会重新编译到目前为止的整个多行代码。
2.3 否则,显示异常信息。
(显然还有一些与初始化相关的事情正在发生,因为需要从一些评估开始以显示此错误,但这很可能是一个完全不同的问题。)
正在搜索
我浏览了 GH 上匹配 'repl' 的新旧队列中所有未解决的 Rakudo 问题。我选择了四个来说明 REPL 在维护会话状态方面遇到的困难范围:
REPL loses custom operators。 “有趣的是,如果像这样的 postfix 运算符由 REPL 加载的模块导出,REPL 可以成功解析该运算符一次,之后它将失败并出现类似于上述的错误。”这是否与此 SO 关注的 bug 在 second 或更晚的评估之前不会出现的方式有关?
Perl6 REPL forgets the definition of infix sub。看起来像是上述问题的骗局,但包含来自 Brian Duggan 的额外调试工具。 ❤️
-
-
我还没有做的一件事是检查这些错误是否仍然存在。我的猜测是他们这样做。还有很多其他人像他们一样。也许他们有一些共同的原因?我不知道。也许我们需要看看代码...
洞穴探险
在 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 改天。
只有在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。但我喜欢尽量减少错误示例。
猜测我想我能看出是怎么回事了
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>
评论:
要进入球道,请输入任意一行(不只是空格),然后按 Enter。
挑对铁;使用
{
打开一个块,声明一些命名类型,然后按 Enter。 REPL 通过显示*
多行提示来指示您处于绿色状态。要下沉球,只需按 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 循环大概是这样的:
输入每一行时,将其传递给编译器,编译器编译代码并在出现任何错误时抛出异常。
如果抛出异常:
2.1 如果处于多行模式(有
*
提示),则退出多行模式(回到>
提示)并显示异常信息。2.2 否则(因此不在多行模式下),如果对输入代码异常 and/or 的分析(可能非常基本)表明多行模式有用,则进入该模式(使用
*
提示)。在多行模式下,每次用户按 Enter 时都会重新编译到目前为止的整个多行代码。2.3 否则,显示异常信息。
(显然还有一些与初始化相关的事情正在发生,因为需要从一些评估开始以显示此错误,但这很可能是一个完全不同的问题。)
正在搜索
我浏览了 GH 上匹配 'repl' 的新旧队列中所有未解决的 Rakudo 问题。我选择了四个来说明 REPL 在维护会话状态方面遇到的困难范围:
REPL loses custom operators。 “有趣的是,如果像这样的 postfix 运算符由 REPL 加载的模块导出,REPL 可以成功解析该运算符一次,之后它将失败并出现类似于上述的错误。”这是否与此 SO 关注的 bug 在 second 或更晚的评估之前不会出现的方式有关?
Perl6 REPL forgets the definition of infix sub。看起来像是上述问题的骗局,但包含来自 Brian Duggan 的额外调试工具。 ❤️
我还没有做的一件事是检查这些错误是否仍然存在。我的猜测是他们这样做。还有很多其他人像他们一样。也许他们有一些共同的原因?我不知道。也许我们需要看看代码...
洞穴探险
在 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 改天。