在学到java编译器的时候,当听到说javac是由java编写时其实是感到诧异的。原因很简单,如果javac本身就是一个java程序,那么一定需要一个编译器先把它编译成可执行的文件。可是这款已经编译好的编译器又是哪里来的呢?而且,既然我们已经有了一款编译器,那还要javac做什么呢?
这个问题自然很容易搜索到答案:最初的java编译器的确不是java写的,所以这个“鸡生蛋还是蛋生鸡”的问题自然不必再困惑。而写出javac作为通用的编译器则是为了使java满足一项特性:自举(Bootstrapping)。自举的意思是说,一种编程语言的程序可以用这种语言本身编写的编译器来编译。
知乎上的轮子哥是这样简要地描述自举的过程的:
你想创造一门V语言而且用V语言来写V编译器的话,你得按照下面的方法做:
1、用C++把那个编译器(A)写出来,顺便留下很多测试用例。
2、用V语言把那个编译器写(B)出来,用A.exe来编译B,修改直到所有测试用例都通过为止。
3、B.exe来编译B自己得到B2.exe,修改直到B2.exe所有测试用例都通过为止。这是为了保证,就算B本身有很多bug,至少编译自己是没有bug的,从而你就可以走到第四步。
4、当你觉得有信心了,用A.exe把B编译一遍,就得到了B.exe。然后A的代码和A.exe都在也不需要存在了,删掉他们。以后你就不断的用B.exe来编译下一个版本的B就好了。就自举了。
作者:vczh
链接:https://www.zhihu.com/question/28513473/answer/41094452
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
不同的编程语言由于其诞生和发展的经历不尽相同,完成自举的过程也各自有所差异。一般来说,该语言最开始的编译器是用其它已经成熟的语言编写的,比如Pascal最初的编译器是Fortran写的;当然比较厉害的人也可以把自己写的编译器手动编译,比如Donald Knuth就自己手动编译了WEB语言的编译器。不过,这些繁琐的过程仅仅需要在该语言刚刚诞生时操作完。一旦自举完成,最初始的编译过程就可以扫入历史的垃圾堆,就好像从未发生过一样了。
那么为什么一定要完成自举,而不是使用最初始的编译器呢?可能当我们自己去设身处地地去想一下初始编译器的状态,就可以大致明白了:最初的编译器往往只在乎能正确工作,而不会考虑最优。举一个极端的例子,Donald Knuth即便再是大牛,如果要亲手编译每个WEB程序员编写的每个程序,那也是效率极其低下的。
一般来说,完成自举有如下的优点:
1. 这是一种对该语言的极好的测试;
2. 编译器的开发者只需掌握这一门语言即可,不需要再掌握其它语言;
3. 开发编译器的环境和使用这门语言开发的所有其它程序一致;
4. 对编译器后端的优化不仅会优化以后所有编译出来的其它程序的效率,也会优化编译器本身的效率。
网友评论