V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
pythonee
V2EX  ›  程序员

编程语言实现了自举是什么含义

  •  
  •   pythonee · 2019-12-24 11:15:47 +08:00 · 9360 次点击
    这是一个创建于 1827 天前的主题,其中的信息可能已经有所发展或是发生改变。
    假设语言 x
    那么它的编译器是 x 语言编写的,那么编译器的编译器也是用 x 编写的,那么谁来编译这个“编译器的编译器”呢
    感觉是无穷尽也

    应该最小的那个不可再分的是用别的低级语言写的吧,那么还可称自举吗
    30 条回复    2019-12-26 13:04:47 +08:00
    IsaacYoung
        1
    IsaacYoung  
       2019-12-24 11:16:59 +08:00
    ljpCN
        2
    ljpCN  
       2019-12-24 11:17:20 +08:00 via Android
    最小的不可再分的是不需要编译的,纸带打孔直接执行就行了
    lambdaq
        3
    lambdaq  
       2019-12-24 11:17:40 +08:00
    最老的肯定是手写的。
    momocraft
        4
    momocraft  
       2019-12-24 11:19:50 +08:00
    这语言又不是只能有一个编译器

    你可以用 tcc 编译 gcc1 用 gcc1 编译 gcc2 同样叫自举
    huobazi
        5
    huobazi  
       2019-12-24 11:20:19 +08:00
    最早那版肯定是别的语言写的
    levelworm
        6
    levelworm  
       2019-12-24 11:21:54 +08:00 via Android
    应该是其他语言比如汇编写个最小集合出来,然后用这个最小集合一点点加上去。具体技术细节就不知道了。
    pythonee
        7
    pythonee  
    OP
       2019-12-24 11:56:32 +08:00
    @momocraft
    @huobazi
    @levelworm

    应该是这个样子
    hehheh
        8
    hehheh  
       2019-12-24 12:04:09 +08:00
    最早的是打孔机,往后越来越高级。现在的编译过程就是一个文本分析,不过做的工作比这个简单的词多得多。

    具体点的话,最早的 c++编译器是用 c 实现的。c 最早的编译器应该是用汇编写的,汇编最早的编译器可能是用插线板插出来的。。。
    secondwtq
        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,也就是你不能一边实现新特性,一边在新特性的实现代码中使用新特性
    secondwtq
        10
    secondwtq  
       2019-12-24 12:44:02 +08:00 via iPhone
    另外楼主可以去看下 Futamura Projection …… 更绕,但是 Java 几年前靠这个实现了自举
    colors
        11
    colors  
       2019-12-24 12:44:27 +08:00   ❤️ 2
    0.拿 c 写一个 go 语言的编译器 C0, 这时候你就能编译 go 代码了
    1.用 go 语言写了个编译器, 用 C0 编译成一个可执行的文件, 这个就是 C1
    2.有了 C1,就可以把 C0 抛弃了, 这时候就算实现了自举
    Kaiv2
        12
    Kaiv2  
       2019-12-24 12:52:03 +08:00 via Android   ❤️ 1
    打铁的把铁打成打铁工具打铁🐶
    wizardoz
        13
    wizardoz  
       2019-12-24 12:58:32 +08:00
    先有鸡还是先有蛋的问题,不管先有哪个,但是这个世界上一旦出现了其中一种,它就可以无穷延续下去了
    geelaw
        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 自己产生的。
    fancy111
        15
    fancy111  
       2019-12-24 13:41:12 +08:00
    你弯绕错了。
    反正记住一点,所有语言都得变成 01 机器码,然后上面随便你什么语言实现了这个,再往上就可以自举了。比如 X 语言自身实现转机器码的操作,那么 X1、Y、Z 语言就能利用 X 语言进行编译。
    Cu635
        16
    Cu635  
       2019-12-24 14:12:15 +08:00
    @hehheh
    打住,汇编就已经是机器码了,汇编跟机器码基本上就是一一对应的,不存在“编译”的问题,你看到的那些字母组合叫做“助记符”,顶多有“展开”。
    hehheh
        17
    hehheh  
       2019-12-24 14:37:26 +08:00 via iPhone
    @Cu635 汇编是机器码?机器不用编译器能看懂 mov ?机器能看懂 add? 求问哪家的机器这么厉害?
    不存在编译的多的去了,python 也不存在编译,所以 python 也是机器码?
    ac2sherry
        18
    ac2sherry  
       2019-12-24 14:44:18 +08:00
    @hehheh @Cu635
    从理解的角度讲可以等价,每一行汇编都可以直接翻译成对应的机器码。不过编译器是存在的,比如 nasm.exe 。
    qinliming
        19
    qinliming  
       2019-12-24 15:05:30 +08:00
    @ac2sherry 这叫 assembler 叫做汇编器,编译器是 compiler
    misaka19000
        20
    misaka19000  
       2019-12-24 15:10:48 +08:00
    月经贴
    hehheh
        21
    hehheh  
       2019-12-24 15:16:55 +08:00
    @qinliming 这么说就太抠字眼了。。。如果编译器的最终产品是什么?可执行文件对吧? objective file 算啥?算是最终产品吗?可是他怎么执行啊。。。所以 linker 干的事情也不算编译了?
    pythonee
        22
    pythonee  
    OP
       2019-12-24 15:20:00 +08:00
    ac2sherry
        23
    ac2sherry  
       2019-12-24 15:30:32 +08:00
    @qinliming 说的对,是我混淆了。
    FrankHB
        24
    FrankHB  
       2019-12-24 15:36:37 +08:00   ❤️ 1
    某乎那个问题没到点,重点就是 @secondwtq 提的:最开始的 bootstrap 不要求编译。

    @Cu635 @qinliming
    汇编显然不是机器码,否则就没汇编器什么事了。
    严格意义上讲,编译器是允许以批处理形式执行的把源程序翻译成目标程序的翻译器,而汇编器是一种简化的编译器。现代的不少编译器工具链直接包含汇编器作为其中的一个组件。
    只不过历史上提出编译器概念的时候着重强调和机器无关,而当时的汇编普遍都是依赖机器的,所以在讨论高级语言的编译器时汇编器就往往被隐含排除了。

    @hehheh Python 还真有编译,.pyc 就是编译出来的东西,只不过编译出的目标代码是中间的字节码而不是体系结构相关的本机代码而已。
    注意编译器不一定要求输出可执行程序。而且,你现在在宿主语言实现看到的编译器也好单独汇编器也好,其实都是以编译器驱动(driver) 去调用链接器输出的,单独的不管是高级语言的翻译还是低级的汇编步骤都只生成包含目标代码的对象文件(object file) 而不是可执行程序。而排除编译器驱动和链接器的工具链仍然能叫编译器。

    @ac2sherry 预处理记号、伪指令、宏以及其它注记这些可选的部分都可以不对应目标代码。
    实际上,汇编语言也不一定需要是和机器相关的低级语言。用汇编器的形式实现高级汇编语言完全没有理论问题,只是大部分汇编语言都是硬件和个别适配具体体系结构的汇编器厂商提供的,很少有人这样设计而已——为什么放着高级语言的通用翻译技术而要纠结按传统汇编器那样使用助记符引导宏展开去实现一个编译器呢?
    hehheh
        25
    hehheh  
       2019-12-24 15:42:33 +08:00
    @FrankHB 刚想多杠几句来着,一看是帝球,算了算了。手动滑稽
    akira
        26
    akira  
       2019-12-24 23:08:49 +08:00
    @hehheh 机器码是给 cpu 执行的,汇编是给人看的,两者其实是同一个东西。

    举个例子,字符"a" 是给人看的,对应的 ascii 是 65,你非要说人看的 a 和 ascii 的 65 不是一回事,那也没办法
    CEBBCAT
        27
    CEBBCAT  
       2019-12-25 02:06:46 +08:00 via Android
    KeyboardManAnAn
        28
    KeyboardManAnAn  
       2019-12-25 12:10:09 +08:00
    @Kaiv2 很形象的比喻
    Cu635
        29
    Cu635  
       2019-12-25 12:57:08 +08:00
    @hehheh
    所有机器。因为什么 mov 什么 add 都是“助记符”而已。

    “这么说就太抠字眼了。。。”
    那不是“抠字眼”而是概念上的不同。

    @ac2sherry
    那叫汇编器 assembler 不叫编译器 compiler。

    @FrankHB
    因为汇编跟机器指令基本上是一一对应的,所以在实质和理解上是完全可以说“汇编就是机器码”的。只不过需要转换才要有汇编器来完成。
    都是搞计算机的,可以把这个看作进制转换的问题,一个是二进制数字,一个是十进制或者十六进制的数字这样:看着不一样但这是同一个数值的不同进制表示,反过来说虽然是同一个数值,但进制不同中间还是要转换的。

    另外,不是“……当时的汇编普遍都是依赖机器的,……”,而是汇编那些什么 MOV 什么 ADD 这些字符串就叫做助记符,是机器码一串 01 没法记忆,为了方便记忆才弄出来字符串的。

    别忘了一开始数据和指令的输入可都是打孔纸带呢,那个可就是人肉写机器码了。
    FrankHB
        30
    FrankHB  
       2019-12-26 13:04:47 +08:00
    @Cu635 现代的 ISA 中所谓的一一对应是一厢情愿的给 noob 灌输的简化,技术上就是错的。
    随便举个例子,AT&T 汇编助记符的后缀,你确定能怎么和任意 ISA 的机器码对应?
    再如,Intel IA-32 汇编的助记符是可以看成重载过的更高级的简化版本,但是光是指令前缀的使用就依赖处理器当前的操作模式,和机器码不可能简单对应。
    在常见的实现中指望一一对应,要么是不懂汇编,要么是不懂机器码,要么是两个都不懂。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1674 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 16:39 · PVG 00:39 · LAX 08:39 · JFK 11:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.