将X编译器写入Y中的Z的一般规则


9

假设X是输入语言,Z是输出语言,则f是使用语言Y编写的编译器。

f = X -> Z

由于f只是一个程序,我认为Y可以是任何语言,对吗?这样我们就可以得到分别以Y1,Y2编写的编译器f1,f2。

f1 = f Y1    
f2 = f Y2

g = Z -> M
h = g . f    # We get a compiler X -> M

以cpython编译器为例,X是Python,Z是Python VM代码,Y是C。

cpython = Python -> PythonVMCode C
interpreter = PythonVMCode -> Nothing
interpreter2 = PythonVMCode -> MachineCode

Python源代码被编译为Python VM代码,.pyc文件,然后由解释器解释。看起来可能存在一个可以直接执行Python-> MachineCode的编译器,尽管很难实现:

   hardpython = interpreter2 . cpython 

我们还可以编写另一种编译器来工作Python-> PythonVMCode,用另一种语言表示Python本身。

mypython = Python -> PythonVMCode Python
mypython2 = Python -> PythonVMCode Ruby

现在,这是一个复杂的示例PyPy。我只是PyPy的新手,如果我错了,请纠正我:

PyPy doc http://doc.pypy.org/en/latest/architecture.html#pypy-the-translation-framework

我们的目标是为语言实现者的问题提供一种可能的解决方案:必须为l种动态语言和p平台(具有o个关键的设计决策)编写l * o * p个解释器。

我们可以认为l是X,p是Y。存在一个将所有RPython程序转换为C的程序:

 rpython_compiler = RPython -> C  Python

 pypy = Python -> Nothing RPython

 translate = compile the program pypy written in RPython using rpython_compiler

 py2rpy = Python -> RPython  Python
 py2c = Python -> C Python 
 py2c = rpython_compiler . py2rpy

RPython程序就像VM指令一样,rpython_compiler是VM。

q1。pypy是解释器,它是一个RPython程序,可以解释Python代码,没有输出语言,因此我们不能将其视为编译器,对吗?

添加:

  • 我只是发现,即使经过翻译,pypy仍然是解释器,但这只是用C编写的。
  • 如果我们深入研究解释器pypy,我相信必须存在某种编译器,它将Python源代码编译为AST,然后执行

像这样:

compiler_inside_pypy = Python -> AST_or_so

q2。编译器py2rpy可以存在,将所有Python程序都转换为RPython吗?它所用的语言是无关紧要的。如果是,我们得到另一个编译器py2c。pypy和py2rpy本质上有什么区别?py2rpy比pypy难写得多吗?

q3。是否有一些通用的规则或理论可用?

更多编译器:

gcc_c = C -> asm? C  # not sure, gimple or rtl?
g++ =   C++ -> asm? C
clang = C -> LLVM_IR  C++
jython = Python -> JVMCode java
ironpython = Python -> CLI C#

q4。给定f = X-> Z,这是用X编写的程序P。当我们想加快P的速度时,该怎么办?可能的方式:

  • 用更有效的算法重写P

  • 重写f以生成更好的Z

  • 如果解释了Z,则编写更好的Z解释器(PyPy在这里?)

  • 递归地加快用Z编写的程序的速度

  • 得到更好的机器

ps。这个问题不是关于如何编写编译器的技术问题,而是关于编写某种编译器的可行性和复杂性。


没有直接关系,但是有点类似的概念:en.wikipedia.org/wiki/Supercompilation
SK-logic

1
我不确定这个问题是否真的适合堆栈溢出,尤其是其中有很多子问题,但是我仍然很佩服这个问题。

4
尽管您可能学到了什么,但不需要AST-它只是某些编译器使用的策略。

1
可能这属于cstheory.stackexchange.com
9000

3
与大多数“解释器”一样,PyPy的Python实现实际上是一个字节码编译器,并且是一种针对该字节码格式的解释器。

Answers:


4

q1。pypy是解释器,它是一个RPython程序,可以解释Python代码,没有输出语言,因此我们不能将其视为编译器,对吗?

PyPy与CPython相似,都具有编译器+解释器。CPython有一个用C编写的编译器,它将Python编译成Python VM字节码,然后在用C编写的解释器中执行字节码.PyPy有一个用RPython编写的编译器,将Python编译成Python VM字节码,然后在用RPython编写的PyPy Interpreter中执行它。

q2。编译器py2rpy可以存在,将所有Python程序转换为RPython吗?它所用的语言是无关紧要的。如果是,我们得到另一个编译器py2c。pypy和py2rpy本质上有什么区别?py2rpy比pypy难写得多吗?

编译器py2rpy可以存在吗?理论上是。图灵完整性保证了这一点。

一种构造方法py2rpy是在生成的源代码中简单地包含用RPython编写的Python解释器的源代码。用Bash编写的py2rpy编译器示例:

// suppose that /pypy/source/ contains the source code for pypy (i.e. Python -> Nothing RPython)
cp /pypy/source/ /tmp/py2rpy/pypy/

// suppose $inputfile contains an arbitrary Python source code
cp $inputfile /tmp/py2rpy/prog.py

// generate the main.rpy
echo "import pypy; pypy.execfile('prog.py')" > /tmp/py2rpy/main.rpy

cp /tmp/py2rpy/ $outputdir

现在,每当需要将Python代码转换为RPython代码时,都调用此脚本,该脚本在$ outputdir中生成RPython main.rpy,RPython的Python解释器源代码和二进制blobprog.py。然后,您可以通过调用执行生成的RPython脚本rpython main.rpy

(注意:由于我对rpython项目不熟悉,因此调用rpython解释器的语法,导入pypy和执行pypy.execfile的能力以及.rpy扩展名完全是组成,但我想您明白了)

q3。是否有一些适用的一般规则或理论?

是的,理论上任何图灵完整的语言都可以翻译成任何图灵完整的语言。某些语言的翻译可能比其他语言难得多,但是如果问题是“可以吗?”,答案是“是”。

q4。...

毫无疑问。


您的py2rpy编译器确实很聪明。这使我想到了另一个主意。1.是否必须在编译器的RPython中编写pypy?您需要的是可以解释Python文件的东西,对吧?2.如果RPython支持os.system('python $ inputfile'),也可以使用。不知道它是否仍然可以被称为编译器,至少不能从字面上看。

pypy仍在使用Python VM吗?现在很清楚。pypy_the_compiler = Python-> PythonVMCode RPython,pypy_the_interpreter = PythonVMCode-> Nothing RPython,cpython_the_compiler = Python-> PythonVMCode C,cpython_the_interpreter = PythonVMCode-> Nothing C

@jaimechen:Does pypy have to be written in RPython in your compiler?不,它不需要用RPython编写,但是RPython必须能够告诉“辅助解释器” /“运行时”执行Python代码。是的,从实际意义上讲,这不是一个“编译器”,这确实是一个有建设性的证明,表明有可能写作Python -> RPythonIs pypy still using the Python VM?我相信pypy根本不使用CPython(我可能错了),相反,PyPy拥有自己的用RPython编写的“ Python VM”实现。
Lie Ryan

@jaimechen:一个更实用的编译器可能会分析输入文件中的代码序列,因为它知道如何分别编译和编译这些代码序列,并且是在“重新编译为RPython” Python和“解释器-辅助” Python。它还可能使用JIT编译中常用的技术来检测特定输入是否由于RPython和Python的语义差异以及在这些情况下的解释回退而产生不同的输出。所有这些都是复杂的,可以在更实用的Python -> RPython编译器中看到。
Lie Ryan

也许应该在这里添加一个约束:将状态机X转换为状态机Z,而无需借助现有的第3台机器。当X是全新的时,就是这种情况,到目前为止,还没有编译器或解释器。
贾梅琴

2

仅回答q2,有一本威廉·麦克基曼(William McKeeman)撰写的编译器书,其中通过T-图系统探索了用语言Y编写产生输出语言Z的语言X的编译器理论。发表于1970年代,书名不详,对不起。


是的,就是这样,谢谢。en.wikipedia.org/wiki/Tombstone_diagram
jaimechen

1

q1。通常,解释器不是编译器。编译器和解释器之间的主要区别在于,解释器每次都以源语言的源代码重新启动。如果您的pypy是pyAST或pyP代码,则您具有AST或P代码解释器,则可以将pyAST称为编译器。这就是旧的UCSD PASCAL编译器(以及许多其他编译器)的工作方式:它们编译为一些P代码,该P代码在程序运行时进行解释。(当生成的目标代码的紧凑性比速度重要得多时,.NET甚至提供了类似的功能。)

q2。当然是。参见UCSD PASCAL(以及其他一堆)。

q3。挖掘计算机科学中的经典著作。阅读Per Brinch-Hansen撰写的Concurrent PASCAL(如果有帮助的话)。关于编译器和代码生成的文章很多。生成与机器无关的伪代码通常比生成机器代码要容易得多:伪代码通常没有实际机器总是包含的怪癖。

q4。如果希望生成的对象运行得更快,则可以使编译器更智能,以实现更好的优化。如果对对象进行了解释,则考虑将更复杂的操作下推到原始的伪指令中(类比为CISC与RISC),那么您将尽最大努力优化解释器中的中断。

如果要使编译器运行得更快,则必须查看它所做的一切,包括重新考虑源代码。加载编译器本身之后,编译中最耗时的部分是始终将源代码读取到编译器中。(例如,考虑C ++。在所有其他条件都相对相等的情况下,编译器必须砍掉9,000(或50,000)行的#include文件才能编译一个简单的“ Hello,World”程序,其速度永远不会比以前快只需阅读四或五行。)

我不记得在哪里阅读它,但是ETH-Zurich的原始Oberon编译器具有非常复杂的符号表机制,非常优雅。沃思的编译器性能基准是编译器自行编译所花费的时间。一天早晨,他进去,拉出了华丽的多重链接超树符号表,然后将其替换为简单的线性数组和直线搜索。他所在小组的研究生被震惊了。更改之后,编译器变得更快,因为它所编译的模块总是足够小,以至于优雅的怪物比线性数组和线性搜索强加了更多的总开销。


1
谢谢。编译器“编译”而解释器“执行”,是否可以对这两种程序有更多的了解,例如它们的类型不同?
贾梅琴

1

您所说的问题使我相信,您真正想要/需要的是对什么是编译器,什么是解释器以及两者之间的区别的解释。

编译器将用X语言编写的程序映射到用Y语言编写的功能等效的程序。例如,从Pascal到C的编译器可能会进行编译

function Square(i: Integer)
begin
    Square := i * i
end

int Square(int i)
{
    return i * i;
}

大多数编译器都“向下”编译,因此他们将高级编程语言编译为低级语言,最终的低级语言是机器代码。

大多数编译器直接编译为机器代码,但有些(特别是Java和.NET语言)则编译为“字节码”(Java字节码CIL)。将字节码视为假设计算机的机器代码。然后,该字节码在运行时会被解释或JITted(稍后会详细介绍)。

解释器执行以某种语言Z编写的程序。解释器一点一点地读取程序,并随其执行。例如:

int i = 0;
while (i < 1)
{
    i++
}
return i;

想象一下,解释器在程序行中查找该行,检查该行,执行该行,然后查看下一行,依此类推。

解释器的最佳示例是计算机的CPU。它解释机器代码并执行它。CPU的工作方式由其物理构造方式指定。解释器程序的工作方式由其代码形式确定。因此,CPU解释并执行解释器程序,然后解释器程序解释并执行其输入。您可以通过这种方式链接翻译。

JITter是即时编译器。JITter是编译器。唯一的区别是执行时间:大多数程序被编写,编译,交付给用户然后执行,但是Java字节码和CIL首先交付给用户,然后在执行之前就将它们编译到机器上用户的代码。

C#->(编译)-> CIL->发给客户->(在执行之前进行编译)->机器代码->(执行)

您要了解的最后一件事是图灵完整性link)。如果一种编程语言可以计算出“ 图灵机 ”所能进行的所有事情,那么它就是图灵完备的,也就是说,它至少与图灵机一样强大。该教会图灵论题指出,图灵机是至少强大,我们都不能建立任何机器。因此,每种图灵完整语言都与图灵机一样强大,因此所有图灵完整语言都同样强大。

换句话说,只要您的编程语言是图灵完整的(几乎所有语言都具备),选择哪种语言都没有关系,因为它们都可以计算相同的内容。这也意味着选择哪种编程语言来编写编译器或解释器不是很重要。最后但并非最不重要的一点是,如果X和Y都完成了图灵运算,则意味着您始终可以从X语言编写语言为Y的编译器。

请注意,图灵完善并不能说明您的语言是否高效,也不表示您的CPU和其他硬件的所有实现细节,也不表示您使用该语言的编译器的质量。另外,您的操作系统可能会决定您的程序没有打开文件的权限,但这并不妨碍您计算任何东西的能力-我故意没有定义计算,因为那样会花费很多时间。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.