1
IsaacYoung 2019-12-24 11:16:59 +08:00
|
2
ljpCN 2019-12-24 11:17:20 +08:00 via Android
最小的不可再分的是不需要编译的,纸带打孔直接执行就行了
|
3
lambdaq 2019-12-24 11:17:40 +08:00
最老的肯定是手写的。
|
4
momocraft 2019-12-24 11:19:50 +08:00
这语言又不是只能有一个编译器
你可以用 tcc 编译 gcc1 用 gcc1 编译 gcc2 同样叫自举 |
5
huobazi 2019-12-24 11:20:19 +08:00
最早那版肯定是别的语言写的
|
6
levelworm 2019-12-24 11:21:54 +08:00 via Android
应该是其他语言比如汇编写个最小集合出来,然后用这个最小集合一点点加上去。具体技术细节就不知道了。
|
8
hehheh 2019-12-24 12:04:09 +08:00
最早的是打孔机,往后越来越高级。现在的编译过程就是一个文本分析,不过做的工作比这个简单的词多得多。
具体点的话,最早的 c++编译器是用 c 实现的。c 最早的编译器应该是用汇编写的,汇编最早的编译器可能是用插线板插出来的。。。 |
9
secondwtq 2019-12-24 12:42:06 +08:00 via iPhone 3
Rust 编译器最开始是 OCaml 写的——得到”OCaml 写的 Rust 编译器“,为简洁我们把这个编译器称为 P
注意 P 不需要做的非常完善,也不需要实现所有计划中的语言特性,只要能编译一些基本的程序就行,我们把 P 所实现的”原始版本的 Rust 语言”称为 R0 等 P 做到能用的程度,就用 R0 来写 R0 自己的编译器,我们把这个编译器的源码称为 S0,S0 可以直接使用 P 编译得到 E’(注意这里我们把源码和编译器分开了) 由于 E’ 也实现了 R0 语言,因此可以用 E’ 编译 S0,得到 E0 这时我们说 R0 实现了自举。 注意虽然一般默认 OCaml 是 AOT 编译的,但是 OCaml 也可以解释执行,P 也可以使用 JavaScript 等纯解释的语言实现,虽然 JavaScript 是比 Rust 更高层的语言,但是 P 仅用于实现 bootstrap,具体用什么语言实现不重要 在得到 E0 之后,P 就可以不要了,之后的开发全部在 S0 上进行(你现在去 Rust 的 GitHub 翻历史还能找到早期的 OCaml 编译器,不过在 bootstrap 之后早就被删了),之后编译器的第 n 个迭代版本的源码和可执行文件我们称为 Sn/En 注意此时 R0 依然是一个原始版本的语言,在完善语言设计和编译器实现的过程中会加入新特性或做出破坏兼容性的修改,而编译器的代码 Sn 本身可能也会使用到新特性,这时必须保证 E(n-1) 能够编译 Sn,也就是你不能一边实现新特性,一边在新特性的实现代码中使用新特性 |
10
secondwtq 2019-12-24 12:44:02 +08:00 via iPhone
另外楼主可以去看下 Futamura Projection …… 更绕,但是 Java 几年前靠这个实现了自举
|
11
colors 2019-12-24 12:44:27 +08:00 2
0.拿 c 写一个 go 语言的编译器 C0, 这时候你就能编译 go 代码了
1.用 go 语言写了个编译器, 用 C0 编译成一个可执行的文件, 这个就是 C1 2.有了 C1,就可以把 C0 抛弃了, 这时候就算实现了自举 |
12
Kaiv2 2019-12-24 12:52:03 +08:00 via Android 1
打铁的把铁打成打铁工具打铁🐶
|
13
wizardoz 2019-12-24 12:58:32 +08:00
先有鸡还是先有蛋的问题,不管先有哪个,但是这个世界上一旦出现了其中一种,它就可以无穷延续下去了
|
14
geelaw 2019-12-24 13:05:58 +08:00 via iPhone 1
考虑语言 X 和已经有的语言 Y。
用 Y 写出 X 的编译器 A,用 Y 的编译器编译 A 得到 U,再用 X 写出 X 的编译器 B,用 U 编译 B 得到 V,再用 V 编译 B,如果得到的结果二进制等于 V,则完成了自举,此时 V 既可以看成 Y 的编译器 - U - V 产生的,也可以看作 V 自己产生的。 |
15
fancy111 2019-12-24 13:41:12 +08:00
你弯绕错了。
反正记住一点,所有语言都得变成 01 机器码,然后上面随便你什么语言实现了这个,再往上就可以自举了。比如 X 语言自身实现转机器码的操作,那么 X1、Y、Z 语言就能利用 X 语言进行编译。 |
16
Cu635 2019-12-24 14:12:15 +08:00
@hehheh
打住,汇编就已经是机器码了,汇编跟机器码基本上就是一一对应的,不存在“编译”的问题,你看到的那些字母组合叫做“助记符”,顶多有“展开”。 |
17
hehheh 2019-12-24 14:37:26 +08:00 via iPhone
@Cu635 汇编是机器码?机器不用编译器能看懂 mov ?机器能看懂 add? 求问哪家的机器这么厉害?
不存在编译的多的去了,python 也不存在编译,所以 python 也是机器码? |
18
ac2sherry 2019-12-24 14:44:18 +08:00
|
20
misaka19000 2019-12-24 15:10:48 +08:00
月经贴
|
21
hehheh 2019-12-24 15:16:55 +08:00
@qinliming 这么说就太抠字眼了。。。如果编译器的最终产品是什么?可执行文件对吧? objective file 算啥?算是最终产品吗?可是他怎么执行啊。。。所以 linker 干的事情也不算编译了?
|
22
pythonee OP @misaka19000
sorry |
24
FrankHB 2019-12-24 15:36:37 +08:00 1
某乎那个问题没到点,重点就是 @secondwtq 提的:最开始的 bootstrap 不要求编译。
@Cu635 @qinliming 汇编显然不是机器码,否则就没汇编器什么事了。 严格意义上讲,编译器是允许以批处理形式执行的把源程序翻译成目标程序的翻译器,而汇编器是一种简化的编译器。现代的不少编译器工具链直接包含汇编器作为其中的一个组件。 只不过历史上提出编译器概念的时候着重强调和机器无关,而当时的汇编普遍都是依赖机器的,所以在讨论高级语言的编译器时汇编器就往往被隐含排除了。 @hehheh Python 还真有编译,.pyc 就是编译出来的东西,只不过编译出的目标代码是中间的字节码而不是体系结构相关的本机代码而已。 注意编译器不一定要求输出可执行程序。而且,你现在在宿主语言实现看到的编译器也好单独汇编器也好,其实都是以编译器驱动(driver) 去调用链接器输出的,单独的不管是高级语言的翻译还是低级的汇编步骤都只生成包含目标代码的对象文件(object file) 而不是可执行程序。而排除编译器驱动和链接器的工具链仍然能叫编译器。 @ac2sherry 预处理记号、伪指令、宏以及其它注记这些可选的部分都可以不对应目标代码。 实际上,汇编语言也不一定需要是和机器相关的低级语言。用汇编器的形式实现高级汇编语言完全没有理论问题,只是大部分汇编语言都是硬件和个别适配具体体系结构的汇编器厂商提供的,很少有人这样设计而已——为什么放着高级语言的通用翻译技术而要纠结按传统汇编器那样使用助记符引导宏展开去实现一个编译器呢? |
26
akira 2019-12-24 23:08:49 +08:00
@hehheh 机器码是给 cpu 执行的,汇编是给人看的,两者其实是同一个东西。
举个例子,字符"a" 是给人看的,对应的 ascii 是 65,你非要说人看的 a 和 ascii 的 65 不是一回事,那也没办法 |
28
KeyboardManAnAn 2019-12-25 12:10:09 +08:00
@Kaiv2 很形象的比喻
|
29
Cu635 2019-12-25 12:57:08 +08:00
@hehheh
所有机器。因为什么 mov 什么 add 都是“助记符”而已。 “这么说就太抠字眼了。。。” 那不是“抠字眼”而是概念上的不同。 @ac2sherry 那叫汇编器 assembler 不叫编译器 compiler。 @FrankHB 因为汇编跟机器指令基本上是一一对应的,所以在实质和理解上是完全可以说“汇编就是机器码”的。只不过需要转换才要有汇编器来完成。 都是搞计算机的,可以把这个看作进制转换的问题,一个是二进制数字,一个是十进制或者十六进制的数字这样:看着不一样但这是同一个数值的不同进制表示,反过来说虽然是同一个数值,但进制不同中间还是要转换的。 另外,不是“……当时的汇编普遍都是依赖机器的,……”,而是汇编那些什么 MOV 什么 ADD 这些字符串就叫做助记符,是机器码一串 01 没法记忆,为了方便记忆才弄出来字符串的。 别忘了一开始数据和指令的输入可都是打孔纸带呢,那个可就是人肉写机器码了。 |