第一批编译器是如何制作的?


70

我总是对此感到疑惑,也许我需要一门关于编程语言的良好历史课程。但是,由于当今大多数编译器都是用C编写的,那么最早的编译器是如何编写的(在C之前是AKA),或者是所有语言都只是被解释了?

话虽这么说,我还是什至不了解第一种汇编语言是如何完成的,我理解什么是汇编语言,但是我看不到他们如何使非常第一的汇编语言起作用(例如,他们是如何使第一命令(如mov R21)或w / e设置为等效的二进制代码?


9
我的团队中曾经有一个滑稽可笑的程序员,他所做的只是抱怨C#。我们曾经开玩笑说他发明了一种叫克鲁克的虚构语言。关于Crunk的一个鲜为人知的事实是,编译器也是用Crunk编写的第一种语言。:)
maple_shaft

2
为什么有人会抱怨C#?他从来没有使用过Smalltalk或Lisp吗?大声笑


4
@maple_shaft:公平地说,gcc编译器是用C编写的。如果您有一个很好的交叉编译器来编译第一个版本,那实际上不是问题。当然,第一个C编译器必须用另一种语言编写。
Scott Whitlock

Answers:


89

哈,我已经做到了。许多CPU具有固定大小的简单指令,这些指令只有几个字节长。例如,对于像Motorola 6800这样的简单CPU,您可以将其所有说明放在一张纸上。每个指令都将有一个与它关联的两字节操作码和参数。您可以通过查找每个指令的操作码来手工汇编程序。然后,您需要在纸上编写程序,并用相应的操作码注释每个指令。编写完程序后,可以将每个操作码依次刻录到EPROM然后会存储您的程序。仅在正确的地址使用正确的指令将EPROM连接到CPU,您便有了一个简单的工作程序。并回答您的下一个问题,是的。这很痛苦(我们在高中时就这么做了)。但是我不得不说,连接8位计算机中的每个芯片并手动编写程序使我对计算机体系结构有了更深入的了解,而我可能无法以其他任何方式实现。

更高级的芯片(例如x86)很难手动编码,因为它们通常具有可变长度的指令。像Itanium这样的VLIW / EPIC处理器几乎不可能有效地手动编码,因为它们处理的指令包是由高级编译器优化和汇编的。对于新体系结构,几乎总是先在另一台计算机上编写和组装程序,然后再将其加载到新体系结构中。实际上,对于像Intel这样真正在构建CPU的公司来说,他们可以通过在模拟器上运行尚不存在的架构来运行实际程序。但是我离题了...

对于编译器,最简单的说就是“剪切并粘贴”程序。您可以编写一种非常简单的,非优化的“高级语言”,而无需进行大量工作即可将简单的汇编语言指令聚集在一起。

如果您需要编译器和编程语言的历史记录,建议您转到FORTRAN的历史记录。


27
。。。而不应该是“ ...我建议您将JMP推向历史……”
Binary Worrier

2
我非常非常抱歉 但是我不得不。我只是...吃过 到...
Dave Markle

9
@Dave:您意识到自己被Velociraptor注定要死吗?
Binary Worrier

7
他们之所以“知道”,是因为当他们看到给定指令的信号101010100时,他们实际上很难进行该操作。他们实际上有一个片上单元负责指令解码指令:en.wikipedia.org/wiki/Decoder
Dave Markle

7
值得补充的是:当一种新语言的编译器使用相同的新语言编写时,有时会用用另一种语言编写的“原型编译器”进行编译,这会产生明显正确但效率低下的代码。如此编译后,便可以自行运行以生成一个相当快的编译器。比较冯·诺依曼机器。:D
BMDan 2011年

54

这就是编译器引导的内容(因为没有人提到它的调用方式=)。

用要编译的目标编程语言编写编译器(或汇编器)的过程。应用此技术将导致自托管编译器。

引导了许多编程语言的许多编译器,包括BASIC,ALGOL,C,Pascal,PL / I,Factor,Haskell,Modula-2,Oberon,OCaml,Common Lisp,Scheme,Java,Python,Scala等的编译器。 。

鸡肉和鸡蛋问题

如果需要语言X的编译器来获得语言X(以语言X编写)的编译器,那么第一个编译器是如何编写的?解决这种鸡肉或鸡蛋问题的可能方法包括:

  • 用语言Y实现语言X的解释器或编译器。Niklaus Wirth报告说他在Fortran中编写了第一个Pascal编译器。
  • X的另一种解释器或编译器已经用另一种语言Y编写;这就是Scheme经常被引导的方式。
  • 较早版本的编译器是用X的子集编写的,而X的子集还存在其他一些编译器。这就是引导Java,Haskell和最初的Free Pascal编译器的某些超集的方式。
  • X的编译器是从存在X的编译器的另一体系结构交叉编译而成的。这就是C编译器通常如何移植到其他平台的方式。这也是初始引导后用于Free Pascal的方法。
  • 用X编写编译器;然后从源代码手工编译它(很可能以一种非优化的方式),然后在代码上运行它以获得优化的编译器。唐纳德·克努斯(Donald Knuth)将其用于他的WEB文学编程系统...

良好的链接还可以带您到en.wikipedia.org/wiki/History_of_compiler_writing。通常,我认为原始编译器是用汇编语言(en.wikipedia.org/wiki/Assembly_language)编写的。直到后来才有了引导或自我托管的想法。
Michael Levy

1
+1!奇怪的是,这只是第三高评价的答案。是的,引导程序。那就是答案
Adam Rackis

15

最终,所有计算机都以二进制代码运行,然后将其输入CPU。这些二进制代码对于CPU来说是完全自然的,但对人类却完全没有用。编写程序的最早方法之一是在卡上打孔。孔的位置表示一个单词中的特定位位置,并且孔的存在与否被解释为零或一。将这些卡按正确的顺序放入一个盒子中,然后送入读卡器中,该读卡器将它们有效地转换为用于CPU的二进制代码(如果您丢下盒子,您的生命将被完全丧失)。

显然,最早的程序员一步一步地制作出了二进制代码,并拥有一台机器来打卡。这实际上是您双手和膝盖上的汇编语言编程。一旦有了它,就可以从中创建所有其他内容:一个简单的文本编辑器,一个汇编语言编译器(用于将文本汇编语句转换为二进制代码),一个链接器和一个加载器。而其余的,正如他们所说,是历史。


4
在插卡之前,您需要使用一组地址开关,一组数据字和一个用于加载数据的开关。您可以通过用二进制表示形式设置地址和数据开关来分别编程每个存储器地址,然后轻按负载开关,然后将其关闭。它花了很长时间,但是程序只有几个字长-那时还没有发明字节。
uɐɪ

4
...在此之前,您必须重新接线。Funfunfun!
Michael K

是的,但是当您必须这样做时,它并不是我们真正想作为现代计算机的东西,因为尚未发明冯·诺依曼体系结构。
Dave Markle

7

从40年代后期开始,略微谷歌搜索就增加了EDSAC的初始订单。由于它是第一个汇编程序,因此可能是用机器语言编写的。

后来出现了其他机器的汇编程序,例如IBM 650的SOAP I和II。尽管我还没有找到明确的陈述,但SOAP I也可能用机器语言编码。

后来又出现了IBM 704的Fortran(公式转换器)。大概是704的汇编程序编写的。701的早期汇编程序归功于Nathan Rochester

如果您想了解如何使用机器语言对计算机编程,请查看我最喜欢的站点之一,Harry Porter的中继计算机


糟糕的东西,哈里·波特(几乎说哈利·波特哈哈)家用电脑真是太好了。我想我明白了这样的事情怎么建:(。

1
@索伦:哈里·波特最想告诉你的是什么。在那一页上,他有一个制作精巧的幻灯片,解释了这一切。它确实假定您具有一些电路的基本知识,但这并不是很难获得。
Mike Dunlavey

我知道我只是一团糟^ _ ^,不管这是一台非常出色的机器,而且我肯定会花很多时间在向导上:)。

6

可能(如果很乏味)编写直接的机器代码。也许您将程序写在汇编器中的一张纸上,然后手动将其转换为数字机器码指令,然后将其输入到机器存储器中。如果您已记住所有机器代码指令的数值,则甚至可以跳过纸上汇编程序的步骤-在那些日子里并不罕见,信不信由你!

最初的计算机是通过切换物理开关直接以二进制方式编程的。当硬件发展到允许程序员(或数据输入助手)通过小键盘以十六进制数字输入代码时,这极大地提高了生产率!

仅当有更多内存可用时软件汇编器才有意义(因为汇编器代码比原始机器代码占用更多的空间),并且硬件发展为允许字母数字输入。因此,第一批汇编程序是由精通机器代码的人员直接编写的。

当您拥有汇编器时,可以在汇编器中编写用于高级语言的编译器。

C的故事有多个步骤。第一个C编译器是用B(C的前身)编写的,而B又是用BCPL编写的。BCPL是一种非常简单的语言(例如,它根本没有类型),但是仍然比原始汇编程序更先进。因此,您将了解如何逐渐将更复杂的语言以更简单的语言构建,一直到汇编程序。从今天的标准来看,C本身是一种非常小巧的语言。

如今,一种新语言的第一个编译器通常是用C编写的,但是当该语言达到一定成熟度时,通常会“本身”进行重写。第一个Java编译器是用C编写的,但后来用Java重写了。第一个C#编译器是用C ++编写的,但是最近它已经用C#重写。Python编译器/解释器是用C编写的,但是PyPy项目试图用Python重写它。

但是,用语言本身编写语言的编译器/解释器并不总是可行的。存在用JavaScript编写的JavaScript解释器,但是出于性能方面的考虑,当前浏览器中的编译器/解释器仍然使用C或C ++编写。用JavaScript编写的JavaScript太慢了。

但是您不必使用C作为编译器的“起始语言”。第一个F#编译器是使用OCaml编写的,这是与F#关系最密切的另一种语言。编译器完成后,它已用F#重写。Perl 6的第一个编译器是用Haskell(与Perl 完全不同的纯功能语言)编写的,但是现在有用C编写的编译器。

Rust是一个有趣的案例,其中第一个编译器是用OCaml编写的(现在已经用Rust重写了)。这是值得注意的,因为OCaml通常被认为比Rust(一种更接近金属系统的语言)的级别更高。因此,不一定总是用较低层的语言来实现较高层的语言,反之亦然。


3

假设您从一个简单的指令集开始,而没有其他任何事情,那么首先要创建一个最小的,功能刚刚好的汇编程序或编译器,该程序可以加载文件,解析目标语言的最小子集并生成可执行文件通过使用十六进制编辑器或类似工具编写原始机器代码,将文件作为输出。

然后,您将使用功能几乎刚刚好的编译器或汇编器来实现功能更强大的编译器或汇编器,以识别目标语言的更大子集。泡沫,冲洗,重复,直到得到最终产品。


2

它看起来并不困难。在童年时期;)我想到了一些x86拆卸。

您甚至不需要特别学习它。当您能够在ASM中进行编程,然后尝试使用交互式反汇编程序修复第三方二进制文件时,就会发生这种情况。或者在编写自己的代码加密保护时。

即有时您甚至毫无疑问地从语言迁移到代码。


1

最初的编译器是使用汇编语言实现的。第一批汇编程序是通过二进制编码程序实现的。


不久之前,二进制编程仍然是人们使用的一项技能。

当我还是一个本科生的时候,我记得做过一个编程练习,那就是用PDP-8(我认为)机器代码编写一个小程序,通过前面板开关输入它,然后运行它。几年后,我给自己买了一个6502系统开发套件,该套件具有一个用于输入程序的十六进制键盘以及4k字节的RAM。


-3

一个非常简单的答案假设我们编写了一个有线程序并将其存储在ROM中。它可以视为编译器。所以我只想说,第一个编译器是硬连线的。随着技术的进步,这些简单的编译器随后被用于编写高级编译器。

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.