PyPy 的翻译过程实际上在概念上没有听起来那么递归。
其实它只是一个处理 Python 函数/类/其他对象(不是 Python 源代码)并输出 C 代码的 Python 程序。但当然它不只处理 any 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 在翻译过程中使用the flow object space 来进行抽象解释。即使这并不像起初看起来那样疯狂和令人费解。我对 PyPy 的这一部分知之甚少,但据我所知:
PyPy 通过将 Python 解释器的所有操作委托给“对象空间”来实现它们,该“对象空间”包含所有基本内置操作的实现。但是你可以插入不同的对象空间来获得不同的效果,只要它们实现相同的“对象空间”接口,解释器仍然能够“执行”Python代码。
PyPy 翻译工具链处理的 RPython 代码对象是可以由解释器执行的 Python 代码。因此,PyPy 通过插入流对象空间来重新使用其 Python 解释器的一部分作为翻译工具链的一部分。当用这个对象空间“执行”代码时,解释器实际上并不执行代码的操作,而是产生流程图,类似于许多其他编译器使用的那种中间表示;它只是代码的简单机器可操作表示,需要进一步处理。这就是常规 (R)Python 代码对象如何转化为其余翻译过程的输入的方式。
由于通常用翻译过程翻译的东西是 PyPy 的 Python 解释器,它确实用流对象空间“解释自己”。但这真正意味着你有一个 Python 程序正在处理 Python 函数,包括那些进行处理的函数。就其本身而言,它并不比对自身应用装饰器或让包装类包装自身的实例(或包装类本身)更令人费解。
嗯,这有点漫不经心。无论如何,我希望它有所帮助,并且我希望我没有说任何不准确的话;如果有请指正。