编译原理大家是怎么学习的?

2021-09-17 13:06:27 +08:00
 paranoiddemon

非科班,最近在看 Enginnering a compiler 。看第二章 scanner 部分讲正则和自动机还勉强能理解。 第三章 parser 讲 CFG 引出了一堆符号和概念,感觉完全看不明白。

不知道大家是怎么学的,有没有更基础的视频课程推荐或者其他更入门的书推荐的?

11190 次点击
所在节点    程序员
69 条回复
loryyang
2021-09-18 11:17:40 +08:00
@whosesmile #60 嗯,如果要了解这些,那编译原理很有用的
dog82
2021-09-18 11:24:47 +08:00
大学 72 课时的编译原理,只讲完了语法分析器,上课完全听不懂,我硬头皮啃书,因为如果挂科可能就没法毕业了。最后老师大放水给我打了 90 分。
chenyu0532
2021-09-18 11:42:38 +08:00
实话实说:我没看过 /学过编译原理,我是真的用不上啊,对我来说好深奥啊。
(感觉刷刷 lleetcode 挺好的)
2i2Re2PLMaDnghL
2021-09-18 11:54:28 +08:00
SICP 第五章我也没能仔细看下去(
agagega
2021-09-18 12:06:52 +08:00
通常编译器会分为前端、中端、后端三个部分,这种划分不是拍脑袋,而是因为三个阶段的目的有明确的不同:前端是把源代码字符串转换为结构化数据,中端是针对变形后的结构化数据反复做优化,后端是从优化后的数据生成真实的机器指令。

为什么你提到的定义这么复杂,是因为它是一种严格的数学表示。直观理解的话,这个文法就相当于一个树形结构,其中只有某些类型的节点可以作为根。不清楚楼主是否会写正则表达式,正则的本质是有限自动机,从上一个匹配到的字符到下一个,就是自动机状态的跳转,而* ? +这些特殊符号,表示的无非是这种跳转的方向不再是下一个而是自己。写一个最简单的正则匹配引擎可能也就一百行代码。正则表达式能够涵盖的文法被称作正则文法。

直观理解,上下文无关无法比正则文法复杂的本质在于,它支持递归,而正则文法不支持。所以在有限自动机之外,还需要一个栈,才能完整地保存当前解析的状态。一个最简单的例子:你有没有做过那道「检查字符串正反括号是否匹配」的编程练习题?这道题看起来不需要复杂的解析,是因为它的状态过于少,不需要用自动机表示。但如果把题目改成 [] () {}的任意嵌套,大概就可以领会这个语法解析的过程在干什么。

前面说了语法定义是一棵树,那按照树的结构从上往下自己写代码去逐个字符解析的过程,称为递归下降。在常见的语言中,函数的调用层次本身就暗含了栈,所以我们不再需要一个自定义栈去存储当前解析的层级状态。但由于代码是手写的,所以整个过程中我们可以夹带任意的数据结构,这也使得手写的解析器在实际应用中几乎能处理任何奇怪的语法。而书上提到的 LL/LR 这些理论,其实是为了将语法解析这件事通用化——给程序一套语法定义,程序就能按照你的定义去跑,最后返回结果。个中差别有些类似「写一段代码解析一个浮点数的字符串」和「写一个正则引擎,然后给它传一个浮点数的正则表达式,解析字符串」。在理论上,语法分析这块有很多经典的算法;而在实践上,也有很多实用的工具,从语法生成器到语法组合子,等等。

语法解析结束以后,我们必然想要一个表示源码内容的数据结构,而不仅仅是一个表示源码是否符合文法的布尔结果。拿到以后,编译器会做若干自定义的语义检查(比如类型是否匹配)和前端处理(比如在必要的地方插调试信息)。其实到这一步,通过对这颗语法树进行模式匹配,编译器已经可以生成出最后的指令了。但为了更高级的优化,编译器往往会引入一些新的中间表示形式。这些形式通常遵循某种特征(比如每个量只被赋值一次),以减少后续分析的复杂度。编译器的优化,其实更像是「写一段新的代码,比原来的代码更快,但功能保持一致」。很多抽象的优化发生在这个阶段:减少循环中的代码,把循环操作变成向量操作,重新调整分支的关系…

然后到了后端,就会依赖更多的平台信息了。这个阶段,会做很多可能每个硬件平台 /操作系统都不一样的事情:如果移位真的比乘法更快,那就把合适的乘法优化成移位;不同平台的 ABI 要求调用函数或者加载全局变量时有一些额外的指令,也是在这里加上;用一个复杂指令匹配一组复杂操作;更好的分配寄存器以减少寄存器复制和内存读写;重排指令以适配流水线等等。最后吐出的指令就是真的汇编了。

所以后端和中端的知识,理论上比前端更多。前端更大的成就感其实是自己写了一个解析器,拿到了这个语言的结构化表示,然后就可以做任何事情。而且后端和中端在简单情况下确实可以省略,比如你解析了一个 XML,想从它生成 JS 代码,那解析好以后直接遍历这棵树按照规则输出就好了。再往后面,其实就是玩各种新的算法,以及在实践上整新活。
Mistwave
2021-09-18 12:22:41 +08:00
@whosesmile 无他,就硬啃。啃三五本英文专业书之后,就会有质变了。
双语环境 /留学当然好,但是大多数人是没这个条件的😂
joydee
2021-09-18 13:57:31 +08:00
当代编译器最难啃的部分已经不是 scanner 和 parser 了,现在的难点都集中在后端的 Optimization 中了
namelosw
2021-09-18 14:55:20 +08:00
@whosesmile
> 所以借这个机会问一个一直以来的疑惑,你们的英文都怎么学的?是双语成长环境?还是留学有了氛围后天努力的?

你可以每个东西中文英文的资料都试着查一下,比较入门的知识可能网上到处都有,但是随着知识的深入你会发现英文虽然读起来稍微费力气,但是内容要好一些,经常深入浅出一语中的,所以坚持中英文混着搜索,就会发现英文越来越省时省力。

基本到后来很多词汇都是先知道的英文,根本不知道中文是啥,比如 tagless final 之类的。

> 基于字符串的模版引擎和 Vue 、React 这种基于数据结构的 DOM 引擎在程序上如何实现的

小小纠正一下,其实 React 没有模板,是靠 JSX 实现的,也就是一般用 babel 插件,除了很早期的原型之外早就没有自己的 transpiler 了(感觉 14 年就不咋用了)。也就是说 VDOM 和 「模板完全没有关系」,因为可以直接用 React.createElement 是完全一样的。
Remode
2021-09-19 14:01:39 +08:00
我当时是用 lex,yacc,c++,llvm 做了个 c 语言子集(自己定义)的编译器,算是基本用上了相关知识。。。收获还是不错的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://yangjunhui.monster/t/802520

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX