用java语言怎么发明新的方法_程序语言都是怎么发明的?

对编程语言最大的误解之一,可能就是【实现一门高级语言,必须要靠更低级的语言】了。其实我们完全可以用 JavaScript 实现一个 C 编译器。毕竟编译器的工作,不就是把 if else 之类的源码字符串,转换成 JMP 之类的汇编码字符串或字节码数组吗?这个输入字符串,输出字符串或数组的过程,也就是算法比较复杂,有哪门主流语言干不了吗?

可是,编译器明明生成的是机器指令,怎么可能只是倒腾字符串这么简单呢?其实像我们日常用的 gcc 就只是个壳,里面会替你依次调用预编译器 cc1、汇编器 as 和链接器 ld。经典的词法、语法、语义分析和目标代码优化等部分,都是 cc1 里做的,这时输出就是 .S 格式的汇编码字符串。至于二进制机器码的产生,靠的则是【经典编译过程结束后】汇编和链接部分的脏活。

知道了编译器的输入和输出都是字符串以后,你应该就能明白,任何图灵完备的编程语言,理论上都是能用来写编译器的。例如我们刚刚说的那种用 JavaScript 编译 C 的逆向操作,就还真有人这么干(话说还有什么是 JavaScript 不能写的吗):

但是,相信题主真正想问的应该是这个问题:在没有其它高级语言的时候,那些高级语言的编译器又是怎么写出来的呢?譬如 C++ 的编译器是 C++ 写的,那第一个 C++ 编译器要怎么编译自己呢?这就是个先有鸡还是先有蛋的经典问题了。

在计算机科学里,这个问题对应于所谓的自举 (Bootstrapping) 概念。一般来说,某种新语言 X 的第一个编译器 C1,需要用另一种语言来编写。接下来你就可以用 C1 编译出一个部分用 X 来写的新编译器 C2,而 C2 又可以编译出一个支持更多 X 特性的 C3,然后 C3 编译出 X 支持更好的 C4……该过程重复 114514 次即可进化到完全自己 X 自己(误)。

更通俗的理解,是将编译器想像成一个加工零件的机床。这个机床既可以加工出普通零件,也可以加工出高级零件,最后组装出一台新的机床。第一台机床的零件都需要手工打造,造出的零件比较粗糙,但用第一台机床加工出的第二台机床,可能还有一些零件要靠手工,但机械化比例肯定比第一台机床高一些,加工效果也会更好一些。多重复几次这种【用粗糙机床制造更高精度机床】的过程后,即便制造第一台机床的手工技术失传,我们仍然可以标准化地用机床来制造机床。

历史上,最早的 C 编译器就是汇编实现的。而最早的 C++ 编译器,则是用 C with Class 这种介于 C 和 C++ 之间的语言来实现的。当时已经有了能编译 C with Class 的编译器,用这种编译器编译出来的第一个 C++ 编译器还比较简陋,只是把 C++ 转成 C,然后再用 C 编译器去编译。但有了这个起点后,人们就能用 C++ 来开发越来越强的 C++ 编译器了。所谓道生一,一生二,二生三,三生万物,大抵如此。

为了防误解,附上两个备注:自举在技术上其实也不是必须的。理论上你也可以一直用 C 甚至汇编来实现 C++ 编译器,可是这样你干嘛还要发明 C++ 呢?但对于解释器运行时性能已经被 C++ 优化到极致的脚本语言,就没什么必要玩自举这一套。例如 JavaScript 就是典型的不需要自举的语言,简称不举(误)。

只要你够牛逼,你还可以直接用自己设计的语言写出这门语言的编译器源码,然后用你的脑子把这份源码编译成该语言的第一个编译器。远古天尊 Donald Knuth 就这么干过。思考题:虽然 JavaScript 不举,但 TypeScript 和 CoffeeScript 都是自举的,为什么呢?

直到今天,我们还能看到自举的影子。例如 gcc 编译器就把自举当作了对自身的测试用例。你可以用大版本为 N-1 的旧版 gcc 构建出大版本为 N 的新版 gcc,但这个过程一共会编译三遍:用旧版 gcc 编译出新版 gcc

用新版 gcc 编译自己

重复一次步骤 2,对比结果是否完全一致

理论依据:NewCompiler1 和 NewCompiler2 都是用相同的 NewCompiler 源码编译出的程序。它们都给定了 NewCompiler 这份源码作为相同的输入,因此不管它们自己是用什么编译器编译的(如 NewCompiler1 用的 OldCompiler 和 NewCompiler2 用的 NewCompiler1),它们所生成的输出都应该一致。所以步骤 2 和 3 所输出的 NewCompiler2 和 NewCompiler3 就也应该一致。

继续开个脑洞,假设外星人删掉了世界上所有的二进制程序和全部的代码提交历史,只留下了写在纸上的源码,那么人类的代码还怎么运行呢?表面看来,你必须从最早的 C 编译器开始写,把历史上这么多编译器陆续都实现出来自举一遍,才能最后拯救计算机文明。但是,Fabric Bellard 大神写的 TCC (Tiny CC) 就相当于一条捷径,或者说文明的火种。这个编译器的代码很少,并且不光能编译出自己,还能编译出 Linux 内核。这样,只要徒手用汇编弄出一个能编译 TCC 的更简单的 TTCC (TinyTiny CC),我们就能先用 TTCC 来编译 TCC,然后用自举后的 TCC 直接编译出 Linux 了——感觉是个程序员拯救世界的硬核科幻题材啊。

理解了编译器的自举之后,再去看看哲学上的因果困境、经济学上的工业化,甚至生物学上的物种演化,是不是都能感到某种相通之处呢?私以为这就是计算机科学的乐趣所在吧~

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值