首页 驱动下载 正文

pascal语言编译器 第一个 C 语言编译器是怎样编写的?

第一个 C 语言编译器是怎样编写的?

首先向C语言之父Dennis Ritchie致敬!

当今几乎所有的实用的编译器/解释器(以下统称编译器)都是用C语言编写的,有一些语言比如Clojure,Jython等是基于JVM或者说是用Java实现的,IronPython等是基于.NET实现的,但是Java和C#等本身也要依靠C/C++来实现,等于是间接调用了C。所以衡量某种高级语言的可移植性其实就是在讨论ANSI/ISO C的移植性。

C语言是很低级的语言,很多方面都近似于汇编语言,在《Intel 32位汇编语言程序设计》一书中,甚至介绍了手工把简单的C语言翻译成汇编的方法。对于编译器这种系统软件,用C语言来编写是很自然不过的,即使是像Python这样的高级语言依然在底层依赖于C语言(举Python的例子是因为Intel的黑客正在尝试让Python不需要操作系统就能运行——实际上是免去了BIOS上的一次性C代码)。现在的学生,学过编译原理后,只要有点编程能力的都可以实现一个功能简单的类C语言编译器。

可是问题来了,不知道你有没有想过,大家都用C语言或基于C语言的语言来写编译器,那么世界上第一个C语言编译器又是怎么编写的呢?这不是一个“鸡和蛋”的问题……

还是让我们回顾一下C语言历史:1970年Tomphson和Ritchie在BCPL(一种解释型语言)的基础上开发了B语言,1973年又在B语言的基础上成功开发出了现在的C语言。在C语言被用作系统编程语言之前,Tomphson也用过B语言编写过操作系统。可见在C语言实现以前,B语言已经可以投入实用了。因此第一个C语言编译器的原型完全可能是用B语言或者混合B语言与PDP汇编语言编写的。我们现在都知道,B语言的执行效率比较低,但是如果全部用汇编语言来编写,不仅开发周期长、维护难度大,更可怕的是失去了高级程序设计语言必需的移植性。所以早期的C语言编译器就采取了一个取巧的办法:先用汇编语言编写一个C语言的一个子集的编译器,再通过这个子集去递推完成完整的C语言编译器。详细的过程如下:

先创造一个只有C语言最基本功能的子集,记作C0语言,C0语言已经足够简单了,可以直接用汇编语言编写出C0的编译器。依靠C0已有的功能,设计比C0复杂,但仍然不完整的C语言的又一个子集C1语言,其中C0属于C1,C1属于C,用C0开发出C1语言的编译器。在C1的基础上设计C语言的又一个子集C2语言,C2语言比C1复杂,但是仍然不是完整的C语言,开发出C2语言的编译器……如此直到CN,CN已经足够强大了,这时候就足够开发出完整的C语言编译器的实现了。至于这里的N是多少,这取决于你的目标语言(这里是C语言)的复杂程度和程序员的编程能力——简单地说,如果到了某个子集阶段,可以很方便地利用现有功能实现C语言时,那么你就找到N了。下面的图说明了这个抽象过程:

那么这种大胆的子集简化的方法,是怎么实现的,又有什么理论依据呢?先介绍一个概念,“自编译”Self-Compile,也就是对于某些具有明显自举性质的强类型(所谓强类型就是程序中的每个变量必须声明类型后才能使用,比如C语言,相反有些脚本语言则根本没有类型这一说法)编程语言,可以借助它们的一个有限小子集,通过有限次数的递推来实现对它们自身的表述,这样的语言有C、Pascal、Ada等等,至于为什么可以自编译,可以参见清华大学出版社的《编译原理》,书中实现了一个Pascal的子集的编译器。总之,已经有计算机科学家证明了,C语言理论上是可以通过上面说的CVM的方法实现完整的编译器的,那么实际上是怎样做到简化的呢?这张图是不是有点熟悉?对了就是在讲虚拟机的时候见到过,不过这里是CVM(C Language Virtual Machine),每种语言都是在每个虚拟层上可以独立实现编译的,并且除了C语言外,每一层的输出都将作为下一层的输入(最后一层的输出就是应用程序了),这和滚雪球是一个道理。用手(汇编语言)把一小把雪结合在一起,一点点地滚下去就形成了一个大雪球,这大概就是所谓的0生1,1生C,C生万物吧?

下面是C99的关键字:

仔细看看,其实其中有很多关键字是为了帮助编译器进行优化的,还有一些是用来限定变量、函数的作用域、链接性或者生存周期(函数没有)的,这些在编译器实现的早期根本不必加上,于是可以去掉auto, restrict, extern, volatile, const, sizeof, static, inline, register, typedef,这样就形成了C的子集,C3语言,C3语言的关键字如下:

再想一想,发现C3中其实有很多类型和类型修饰符是没有必要一次性都加上去的,比如三种整型,只要实现int就行了,因此进一步去掉这些关键词,它们是:unsigned, float, short, char(char 是 int), signed, _Bool, _Complex, _Imaginary, long,这样就形成了我们的C2语言,C2语言关键字如下:

继续思考,即使是只有18个关键字的C2语言,依然有很多高级的地方,比如基于基本数据类型的复合数据结构,另外我们的关键字表中是没有写运算符的,在C语言中的复合赋值运算符->、运算符的++、– 等过于灵活的表达方式此时也可以完全删除掉,因此可以去掉的关键字有:enum, struct, union,这样我们可以得到C1语言的关键字:

接近完美了,不过最后一步手笔自然要大一点。这个时候数组和指针也要去掉了,另外C1语言其实仍然有很大的冗杂度,比如控制循环和分支的都有多种表述方法,其实都可简化成一种,具体的来说,循环语句有while循环,do…while循环和for循环,只需要保留while循环就够了;分支语句又有if…{}, if…{}…else, if…{}…else if…, switch,这四种形式,它们都可以通过两个以上的if…{}来实现,因此只需要保留if,…{}就够了。可是再一想,所谓的分支和循环不过是条件跳转语句罢了,函数调用语句也不过是一个压栈和跳转语句罢了,因此只需要goto(未限制的goto)。因此大胆去掉所有结构化关键字,连函数也没有,得到的C0语言关键字如下:

只有5个关键字,已经完全可以用汇编语言快速的实现了。通过逆向分析我们还原了第一个C语言编译器的编写过程,也感受到了前辈科学家们的智慧和勤劳!我们都不过是巨人肩膀上的灰尘罢了!0生1,1生C,C生万物,实在巧妙!

活动推荐: AWS Summit AWS技术峰会2015(上海)

( 责编/王鑫贺 )

订阅“AWS中文技术社区”微信公众号,实时掌握AWS技术及产品消息!

AWS中文技术社区为广大开发者提供了一个Amazon Web Service技术交流平台,推送AWS最新资讯、技术视频、技术文档、精彩技术博文等相关精彩内容,更有AWS社区专家与您直接沟通交流!快加入AWS中文技术社区,更快更好的了解AWS云计算技术。

第九届中国大数据技术大会将于2015年12月10-12日在北京隆重举办。在主会之外,会议还设立了16大分论坛,包含数据库、深度学习、推荐系统、安全等6大技术论坛,金融、制造业、交通旅游、互联网、医疗健康、教育等7大应用论坛和3大热点议题论坛,票价折扣中预购从速。

Pascal 之父、图灵奖得主 Niklaus Wirth 逝世!发明多款编程语言,首提「算法+数据结构=程序」

整理 | 屠敏出品 | CSDN(ID:CSDNnews)

1 月 4 日,Eiffel 编程语言的创造者 Bertrand Meyer 在 Twitter 上发布了一则讣告,“我们是失去了一位编程语言、编程方法、软件工程和硬件设计领域的巨星。Niklaus Wirth 于 1 月 1 日去世。我们对这位先驱、同事、导师和朋友的离去深感哀痛。”

Niklaus Wirth,这位被很多人尊称为编程祖师爷的人物,先后开发了 Euler、Algol-W、Pascal、Modula、Modula-2、Oberon、Oberon-2 和 Oberon-07 多款具有创新性的语言。其中,最让人熟知、也是很多程序员入门语言的 Pascal,不仅具有重要教学意义,而且为未来的计算机语言、系统和体系结构研究提供了基础。

与此同时,Niklaus Wirth 对计算机语言和软件开发有着清晰而深思熟虑的观点,他在 1976 年提出来的「算法+数据结构=程序」的结构化公式直至现在也在影响无数应用。

生平经历:凭借 Euler、Algol-W 在计算机领域崭露头角

回顾一代巨擘的成长经历,从小在瑞士长大的 Niklaus Wirth,大部分生涯是在苏黎世的瑞士联邦理工学院 (ETH) 度过的。

1959 年,Niklaus Wirth 在苏黎世的瑞士联邦理工学院获得学士学位后,前往加拿大拉瓦尔大学攻读研究生,并在 1960 年取得硕士学位。而后他继续进入加州大学伯克利分校深造,于 1963 年获得博士学位。

在整个学生生涯中,从小便具有极强动手能力的 Niklaus Wirth 一直没闲着,也做了很多计算机科学的研究工作。时至 1960 年,Wirth 正在撰写博士论文时,一篇《算法语言 Algol 60 报告》(https://dl.acm.org/doi/10.1145/367236.367262)横空出世,吸引了不少学者的关注。

Algol 60 是最早实现函数定义(可以递归调用)的语言之一,其函数定义可以相互嵌套,具有词法作用域。在很多人看来,Algol 60 是业界第一个清晰定义的语言。

不过,在当时那个环境下,也有一些学者认识到,清晰的规格说明对于可靠而有效的实现是必需的,但还不够。还有人从报告中发现了一些不足。

后来 Wirth 在和 Algol 的主要设计者 Andrian van Wijngaarden 多次交流之后,决定对 Algol 60 做进一步优化,并以此为自己的博士论文课题,后来就诞生了一款通用编程语言 Euler。

正是有了上述经历,斯坦福大学看中了 Wirth,这也是为什么随着博士毕业,Niklaus Wirth 直接进入斯坦福大学担任计算机科学助理教授的原因。

除此之外,这一次的尝试也得到了 Algol 项目组的青睐,随后也加入了 Algol 语言完善与扩充的工作小组工作。

1966 年,Wirth 向小组提交了一份提案,该提案要求下一代 Algol 是受到他自己的语言 Euler 影响的 Algol 60 的扩展和改进版。遗憾的是,Algol 委员会反对这项提案,并采用一个更复杂、更先进的设计,而不是 Wirth 所提出经过简化的 Algol 60。

当然,让 Algol 委员会没想到的是,其后来推出更复杂的 Algol 68 版本与Algol 60 存在很大不同,因此也遭到了业界不少的批评。

与之形成对比,选择与 Tony Hoare(1980 年图灵奖得主)合作,将被拒绝的提案转变为新的非官方 Algol 版本——Algol-W,它在斯坦福大学的第一台 IBM 360 上成功实现并正式应用。

值得一提的是,IBM 360 当时只提供汇编语言和 FORTRAN 语言,但 Wirth 和他的学生都觉得这两者并不适宜于作为设计编译器的工具。后来,Wirth 和他的学生开发了一款新的系统编程语言 PL360,用于 IBM System/360 系列计算机。而 Wirth 也在 1968 年选择从 Algol 小组辞职。

Wirth 的这项早期工作在语法和语义的正式分离、新颖的实现技术以及通过特定解析方法进行高效实现的精细语言设计方面开辟了新天地。

Pascal 之父

Wirth 使用 Algol-W 作为他最具影响力的创造语言 Pascal 的基础。

1970 年,Wirth 设计了 Pascal,最初的目的是作为一种教学工具,以推广另一位领先的计算先驱 Edsger W. Dijkstra 的结构化编程风格,但它很快就超越了其最初的设想范围,成为一种通用语言。

Wirth 曾在接受外媒采访时说道,“只要程序员们珍惜他们的自由,不仅可以设计出自己聪明的软件,而且还可以按照自己的喜好修改已被采用的软件,那么就不可能有适当的设计纪律。只要公司暗中珍视复杂性,将其作为防止被抄袭的有效保护措施,那么大幅提高技术水平的希望就渺茫。”

所以,简单性是 Wirth 一直遵循的原则。Pascal 就是遵循他的个人审美、简单、灵活等准则的产物,旨在快速编译为高效代码。它保留了 Algol 的代码结构、逻辑完整性和对递归的支持,但剥离了一些复杂性并增加了对用户定义数据类型的支持。

Pascal 于 1971 年在 ETH 中被采用用于教学,并很快就得到了大学的认可。

为了帮助在各种计算机上实现 Pascal,Wirth 创建了一种新型编译器,该编译器是为虚拟机编写的,并生成在虚拟机上运行的代码。在新计算机上模拟这个虚拟机可以轻松移植他的编译器。

可要说 Pascal 之所以会成为一门通用语言,Wirth 表示,“位于圣克鲁斯的 Borland Inc. 的 Philippe Kahn 为我们的编译器提供了一个简单的操作系统、一个文本编辑器以及用于错误发现和诊断的程序。他们以 50 美元的价格出售这个软盘包 (Turbo Pascal)。因此 Pascal 立即传播开来,尤其是在学校,它成为许多人进入编程和计算机科学的切入点。”

Borland Turbo Pascal 的第一个历史性广告

Modula 之父

20 世纪 70 年代中期,为适应并发程序设计的需要,Wirth 又成功开发了一个获得广泛应用的语言 Modula。

Modula 除了提供并发程序设计功能之外,另外一个重要特征是引进了模块概念(这也是这个语言叫做Modula的原因)。此外,它还引进了“进程”(process)这一和并发程序相联系而产生的重要概念。

但是,比 Modula 具有更加重大意义的却是它的第二个版本 Modula-2。

彼时 Wirth 在 1976 年至 1977 年期间于施乐帕洛阿尔托研究中心 (PARC) 度过了一个假期,期间接触到了新的工作站技术,即在 ETH 领导一个项目,旨在构建一个新的图形工作站。它被命名为 Lilith,是一个完整的计算环境,具有操作系统 (Medos)、高速局域网、文本和图形编辑器以及激光打印机等应用程序。

Wirth 开发新的编程语言 Modula-2 扩展了 Pascal,支持并发性和更高的代码模块化性。在 Wirth 指导下,一款由 Modula-2 模块构建的单用户、面向对象的操作系统的 Medos-2 诞生了。

第一批 Lilith 系统便是搭载了上述的系统,并于 1980 年开始运行,使这些功能在商业产品出现之前就已成为 ETH(以及其他几所大学)的标准。

几年后,Wirth 又领导了 Lilith 的后续版本 Ceres 工作站的开发,以及其操作系统以及新的面向对象的 Oberon 编程语言和操作系统的开发。

从 20 世纪 80 年代中期到 20 世纪 90 年代,ETH 曾多次使用这些标准。Modula-2 和 Oberon 被转移到商业机器上并用于计算机科学教学应用,尽管两者都没有 Pascal 的广泛影响。

Oberon 是为完全不同的机器实现的,实现了后来因 Java 而闻名的程序可移植性。

硬件设计的重要参与者

和很多计算机科学家有所不同,Niklaus Wirth 在进行软件开发时,同样意识到硬件的重要性与挑战性。

在后来很长一段时间里,Wirth 将自己的精力分一半聚焦到硬件维度,他还利用 FPGA 开发了语言和工具来根据高级规范,对其进行有效配置。

不仅如此,他也将自己毕生所学倾囊相授,推动 ETH 大学内部所有计算机科学专业的学生和教师使用内部生产的硬件、操作系统和编程工具。

在 Wirth 看来,学生在尝试编写自己的系统之前应该阅读并理解真实系统的代码。

1984 年,因为开发了这些语言带来了极大的影响力,Wirth 荣获了图灵奖。此外,他也是集诸多奖项于一身,如 IEEE 计算机协会计算机先驱奖(1988 年);1988 年 IBM 欧洲科技奖(1989 年);瑞士工程院院士(1992 年);美国工程院外籍院士(1994 年);Orden Pour le merite(1996 年);ACM SIGSOFT 软件工程杰出研究奖(1999 年)。

出版物

除此之外,Wirth 一直也对编程方法有着很深的研究。早些时候,他参加了 IFIP 编程方法论工作组,提出了逐步细化代码的想法,作为对他们提出的“结构化编程”各种愿景的补充。

他出版的书《系统编程》和《算法 + 数据结构 = 程序》 是对编程方法和概念文献最有影响力的贡献之一。

向 Niklaus Wirth 致敬

Niklaus Wirth 不仅是一位优秀的图灵奖得主,更是让很多程序员感到十分亲切的良师与挚友。正如 tasty_freeze 留言道:

除了对语言设计的贡献,他还创作了有史以来最好的双关语之一。他的姓氏正确的发音是“Virt”,但在美国,大家都叫他“Worth”。

这让他调侃道:“在欧洲,人们叫我名字,但在美国,人们叫我价值”。

Niklaus Wirth 的离世,一代巨星陨落,让很多人感到难过。

Google AI 首席科学家 Jeff Dean 表示:

听到很伤心。Niklaus Wirth 对计算领域做出了许多巨大的贡献。Pascal 是我认真使用的第一种语言(最初在 UCSD p-System 上,后来通过 Turbo Pascal),我在中学时接触到了他写的这本伟大的书。后来,我喜欢使用 Modula-2 来做一些事情(他对 Modula-3 的间接影响很大)。

当被问到如何发音他的名字时,我也喜欢这个轶事:

“欧洲人通常以正确的方式发音我的名字(‘Ni-klows Wirt’),而美国人总是把它改成‘Nick-les Worth’。这就是说,欧洲人用名字来称呼我,而美国人则用价值来称呼我。

lukego 表示:

除了数不胜数的成就之外,他还是 Joe Armstrong 心目中的英雄,并对他的简约品牌产生了重大影响。

Joe 经常引用 Wirth 的话说,是的,重叠窗口可能比平铺窗口更好,但好的不足以证明其实施复杂性的代价是值得的。

他是我心目中的英雄,因为在他于 ETH 举办的 80 岁生日研讨会上,他展示了他将 Oberon 移植到自制 CPU 上的新成果,该 CPU 可在带有 USB peropherals 的随机 FPGA 开发板上运行。我的理想就是有一天也能成为这样的 80 岁老人。

斯人已逝,其光永存。R.I.P

参考:

https://amturing.acm.org/award_winners/wirth_1025774.cfm

https://news.ycombinator.com/item?id=38858012

https://shidian.baike.com/wikiid/4811571837582058102?anchor=4

相关问答

PASCAL语言 有用吗?

在某些领域还有用PASCAL语言曾经非常有用,它是第一种被广泛使用的结构化编程语言之一,具有严谨的语法和层次结构。它在当时被认为是阳春白雪的编程语言之一,...

mac平台有 pascal编译器 吗怎么使用安装?

FreePascalforMac是一个编译器,不在图形界面下使用的你要做的就是自己创建一个.pas文件,放进你的程序,然后在命令行里敲fpc你的文件.pasFreePascalfor....

什么叫开源,都说JAVA是开源的?

开源就是开放程序源代码。就是把程序源代码发放出来,让程序的用户可以获得。开源和收不收费没有关系,开源软件同样可以收费。编程语言本身没有开不开源的问...

编译器 有什么用途呢?

编译器是一种计算机程序,它会将某种编程语言写成的源代码转换成另一种编程语言。它主要的目的是将便于人编写、阅读、维护的高级计算机语言所写作的源代码程...

delphi技术?

Delphi,是Windows平台下著名的快速应用程序开发工具(RapidApplicationDevelopment,简称RAD)。它的前身,即是DOS时代盛行一时的“BorlandTurbo...

在哪些领域只能由C/C++实现或者C/C++做得很好,而其他 语言 完全实现不了或者效果很差?

C语言,在开发最底层应用的时候,有不可替代性。而C++则属于结合底层和上层技术的语言,不可替代性没有那么强了。下面探讨一下这个问题。底层应用嵌入式单片机...

程序编辑学什么 语言 最好最容易学..._网络编辑_帮考网

对于广大编程初学者来说,存在着这样普遍的认识:学习程序设计选择编程语言很重要,开口闭口就是VisualBasic、VisualC++、Delphi、PowerBuilder等...

Java是编译型 语言 还是解释型 语言 ?

JAVA是一门尴尬而强悍的编程语言!为什么尴尬?因为JAVA不算纯粹的解释型语言,也不能通过编译就直接运行!我们一般编写的JAVA源码是.java后缀的文件,然后在...JA...

未来Rust编程 语言 会成为主流吗?

《2021年将成为系统程序员的“RUST”年》——Rust提供了业界创建安全系统编程的最佳选择Rust是一种开源系统编程语言,专注于速度、内存安全性和并行性。Rust编...

C++编程软件叫什么-ZOL问答

MicrosoftVisualC++6.0,简称VC6.0,是微软推出的一款C++编译器,将“高级语言”翻译为“机器语言(低级语言)”的程序。VisualC++是一个功能强大的可视化软件.....