50

我说得对吗?PyPy 解释器是否真的解释自己然后翻译自己?

所以这是我目前的理解:

  • RPython 的工具链涉及部分执行要翻译的程序,以获得一种预处理版本来注释和翻译。
  • 运行在 CPython 之上的 PyPy 解释器执行以部分解释自身,此时它将控制权交给它的 RPython 一半,后者执行翻译?

如果这是真的,那么这是我见过的最令人费解的事情之一。

4

2 回答 2

66

PyPy 的翻译过程实际上在概念上比听起来要少得多。

实际上,它只是一个处理 Python 函数/类/其他对象(不是Python 源代码)并输出 C 代码的 Python 程序。但当然它不只处理任何Python 对象。它只能处理特定的形式,如果你用 RPython 编写你的待翻译代码,你就会得到这些。

由于翻译工具链是一个 Python 程序,您可以在任何 Python 解释器之上运行它,这显然包括 PyPy 的 Python 解释器。所以这没什么特别的。

由于它翻译 RPython 对象,因此您可以使用它来翻译 PyPy 的 Python 解释器,该解释器是用 RPython 编写的。

但是你不能在不是RPython的翻译框架本身上运行它。只有 PyPy 的 python 解释器本身是 RPython。

事情变得有趣是因为 RPython 代码也是 Python 代码(但不是相反),并且因为 RPython 永远不会“真正存在”在源文件中,而只是存在于一个工作 Python 进程内的内存中,该进程必然包含其他非 RPython 代码(例如,没有“纯 RPython”导入或函数定义,因为翻译器对已经定义和导入的函数进行操作)。

请记住,翻译工具链在内存中的 Python 代码对象上运行。Python 的执行模型意味着这些在一些 Python 代码运行之前不存在。如果您高度简化它,您可以想象开始翻译过程看起来有点像这样:

from my_interpreter import main
from pypy import translate

translate(main)

众所周知,仅导入main会运行大量 Python 代码,包括所有其他模块的my_interpreter导入。但是翻译过程开始分析函数对象 main;它永远不会看到,也不关心执行任何代码来生成main.

一种思考方式是“用 RPython 编程”意味着“编写一个 Python 程序,该程序生成一个 RPython 程序,然后将其提供给翻译过程”。这相对容易理解,并且类似于许多其他编译器的工作方式(例如,考虑用 C 编程的一种方式是,您实际上是在编写一个 C 预处理器程序,该程序生成一个 C 程序,然后将其馈送到C 编译器)。

在 PyPy 案例中事情只会变得混乱,因为所有 3 个组件(生成 RPython 程序的 Python 程序、RPython 程序和翻译过程)都加载到同一个 Python 解释器中。这意味着很有可能在使用某些参数调用而不是在使用其他参数调用时具有 RPython 函数,从翻译框架调用辅助函数作为生成 RPython 程序的一部分,以及许多其他奇怪的事情。所以情况变得相当模糊,你不一定能将你的源代码行清晰地划分为“要翻译的 RPython”、“Python 生成我的 RPython 程序”和“将 RPython 程序交给翻译框架”。


PyPy 解释器运行在 CPython 之上,执行部分解释自身

我认为您在这里暗示的是 PyPy 在翻译过程中使用流对象空间来进行抽象解释。即使这并不像起初看起来那样疯狂和令人费解。我对 PyPy 的这一部分知之甚少,但据我所知:

PyPy 通过将 Python 解释器的所有操作委托给“对象空间”来实现它们,该“对象空间”包含所有基本内置操作的实现。但是你可以插入不同的对象空间来获得不同的效果,只要它们实现相同的“对象空间”接口,解释器仍然能够“执行”Python代码。

PyPy 翻译工具链处理的 RPython 代码对象是可以由解释器执行的 Python 代码。因此,PyPy 通过插入流对象空间来重新使用其 Python 解释器的一部分作为翻译工具链的一部分。当用这个对象空间“执行”代码时,解释器实际上并不执行代码的操作,而是产生流程图,类似于许多其他编译器使用的那种中间表示;它只是代码的简单机器可操作表示,需要进一步处理。这就是常规 (R)Python 代码对象如何转化为其余翻译过程的输入。

由于通常用翻译过程翻译的是 PyPy 的 Python 解释器,它确实用流对象空间“解释自己”。但这真正意味着你有一个 Python 程序正在处理 Python 函数,包括那些进行处理的函数。就其本身而言,它并不比对自身应用装饰器或让包装类包装自身的实例(或包装类本身)更令人费解。


嗯,有点啰嗦了。无论如何,我希望它有所帮助,并且我希望我没有说任何不准确的话;如果我有,请纠正我。

于 2011-12-20T01:54:18.723 回答
14

免责声明:我不是 PyPy 方面的专家 - 特别是,我不了解 RPython 翻译的细节,我只是引用我以前读过的东西。有关 RPython 翻译如何工作的更具体的帖子请查看此答案

答案是,是的,它可以(但只有在它第一次使用 CPython 编译之后)。

更长的描述:

起初,这似乎是高度弯曲和自相矛盾的,但一旦你理解了它,就很容易了。在Wikipedia上查看答案。

程序开发中的自举开始于 1950 年代,当时每个程序都是在纸上用十进制代码或二进制代码逐位(1 和 0)构建的,因为没有高级计算机语言,没有编译器,没有汇编程序,也没有链接器。为一台新计算机(例如 IBM 650)手工编写了一个小型汇编程序,它将一些指令转换为二进制或十进制代码:A1。然后用刚刚定义的汇编语言重写了这个简单的汇编程序,但扩展了一些额外的助记符来处理更复杂的操作代码。

该过程称为软件引导。基本上,您构建一个工具,例如 C++ 编译器,使用已经制作的低级语言(在某一时刻,所有内容都必须从二进制编码),例如 ASM。现在您已经有了 C++,您现在可以用 C++ 编写 C++ 编译器,然后使用 ASM C++ 编译器来编译您的新编译器。编译完新编译器后,您现在可以使用它来编译自己。

所以基本上,通过手工编码来制作第一个计算机工具,用那个解释器制作另一个稍微好一点的工具,然后用那个解释器做一个更好的,......最终你得到了今天所有的复杂软件!:)

另一个有趣的案例是CoffeeScript语言,它是用... CoffeeScript 编写的。(虽然这个用例仍然需要使用外部解释器,即 Node.js)

在 CPython 之上运行的 PyPy 解释器执行以部分解释自身,此时它将控制权交给执行翻译的 RPython 一半?

您可以使用已编译的 PyPy 解释器编译 PyPy,也可以使用 CPython 来编译它。但是,由于 PyPy 现在有 JIT,使用它自己编译 PyPy 会比 CPython 更快。(在大多数情况下,PyPy 现在比 CPython 快)

于 2011-12-09T22:37:27.767 回答