我认为Comma 是蜜蜂的膝盖。而且我几乎从不使用repl。但我喜欢:
-
高尔夫你的例子是一个绰绰有余的MRE。但我喜欢尽量减少错误示例。
-
推测我想我可以看到发生了什么。
-
搜索问题队列 Rakudo 在 GH 上有 两个 问题队列:old 和 new。
-
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>
评论:
-
要进入球道,请输入任何不只是空格的行,然后按 Enter。
-
选择合适的熨斗;使用{ 打开一个块,声明一些命名类型,然后按 Enter。 REPL 通过显示* 多行提示来指示您处于绿色状态。
-
要击沉球,只需按 Enter。
第二,打高尔夫球辅助投机:
> # 42
Nil
> { BEGIN say 99
99
* }
99
>
(BEGIN 标记在编译期间只要编译器遇到就运行的代码。)
推测
为什么最初的# 42 评估很重要?据推测,REPL 会在 REPL 会话期间尝试维护声明/状态(变量和类型等)。
作为其中的一部分,它可能会记住会话中所有以前的代码。
据推测,除了空白行之外,其他任何内容都被视为之前的代码。
一些/任何先前代码的存在会以某种方式影响 REPL 正在维护的状态和/或它要求编译器执行的操作。
也许吧。
为什么类型声明重要,而变量声明不重要?
大概是 REPL 和/或编译器区分这两种声明。
忽略REPL,在编译代码时,重复的my 声明只会引发警告,而重复的类型声明是错误。很可能这就是为什么?
为什么类型声明会有这种效果?
推测该类型编译成功,然后才抛出异常(因为缺少右括号,代码不完整)。
然后 REPL 要求编译器再次尝试编译到目前为止输入的多行代码,以及用户输入的任何附加代码(在我的高尔夫版本中,我什么都不输入,然后按 Enter ,不再添加代码)。
这种重复的编译尝试再次包括类型声明,该类型声明,因为来自先前尝试编译多行代码的编译状态以某种方式被保留,编译器将其视为重复 em> 声明,导致它抛出异常,导致REPL退出多行模式并报错。
换句话说,REPL 循环大概是这样的:
-
输入每一行后,将其传递给编译器,编译器会编译代码并在出现任何错误时抛出异常。
-
如果抛出异常:
2.1 如果处于多行模式(带有* 提示符),则退出多行模式(返回> 提示符)并显示异常消息。
2.2 否则(因此不在多行模式下),如果对异常和/或输入的代码的分析(可能非常基本)表明多行模式很有用,则进入该模式(带有* 提示) .在多行模式下,每次用户按 Enter 时,都会重新编译到目前为止的整个多行代码。
2.3 否则,显示异常信息。
-
(鉴于需要从一些评估开始以显示此错误,显然还有与初始化相关的其他事情,但这很可能是一个完全不同的问题。)
搜索中
我浏览了 GH 上与“repl”匹配的新旧队列中所有未解决的 Rakudo 问题。我选择了四个来说明 REPL 在维护会话状态方面遇到的困难:
我没有做的一件事是检查这些错误是否仍然存在。我的猜测是他们这样做。像他们这样的人还有很多。也许他们有一些共同的原因?我不知道。或许我们需要看一下代码……
洞穴探险
在 Rakudo 资源中搜索“repl”很快就找到了REPL 模块。不到 500 行高级 Raku 代码! \o/(现在,让我们假设我们几乎可以忽略挖掘它调用的代码......)
在我最初的浏览器中,我会提请注意:
今晚就可以了。我会发布这个,然后改天再回来。