编译原理学习导论
大学课程为什么要开设编译原理呢?这门课程关注的是编译器方面的产生原理和技术问题,似乎和计算机的基础领域不沾边,但是编译原理却一直作为大学本科的必修课程,同一时候也成为了研究生入学考试的必考内容。编译原理及技术从本质上来讲就是一个算法问题而已,当然因为这个问题十分复杂,其解决算法也相对复杂。我们学的数据结构与算法分析也是讲算法的,只是讲的基础算法,换句话说讲的是算法导论,而编译原理这门课程讲的就是比較专注解决一种的算法了。在20世纪50年代,编译器的编写一直被觉得是十分困难的事情,第一Fortran的编译器据说花了18年的时间才完毕。在人们尝试编写编译器的同一时候,诞生了很多跟编译相关的理论和技术,而这些理论和技术比一个实际的编译器本身价值更大。就宛如数学家们在解决著名的哥德巴赫猜想一样,尽管没有终于解决这个问题,但是其间诞生不少名著的相关数论。
推荐參考书
尽管编译理论发展到今天,已经有了比較成熟的部分,可是作为一个大学生来说,要自己写出一个像Turboc C,Java那样的编译器来说还是太难了。不仅写编译器困难,学习编译原理这门课程也比較困难。
正是由于编译原理学习相对困难,那么就要求有好的教师和好的教材。教师方面不是我们能自己更改的,而在教材方面我们却能够按自己的意愿来阅读。我以下推荐几本好的编译原理的教材。我推荐的书籍都是国外的经典教材,由于在国内的教材中,确实还没发现什么让人惬意的。
第一本书的原名叫《Compilers Principles,Techniques,and Tools》,另外一个响亮的名字就是龙书。原因是这本书的封面上有条红色的龙,也由于这本书在编译原理基础领域确实太有名气了,所以非常多国外的学者都直接取名为龙书。近期机械工业出版社已经出版了此书的中文版,名字就叫《编译原理》。该书出的比較早,大概是在85或86年编写完毕的,作者之中的一个还是著名的贝尔实验室的科学家。里面解说的核心编译原理至今都没有变过,所以一直到今天,它的价值都非凡。这本书最大的特点就是一開始就通过一个实际的小样例,把编译原理的大致内容罗列出来,让非常多编译原理的刚開始学习的人非常快心里有了个底,也知道为什么会有这些理论,怎么运用这些理论。而这一点是我感觉国内的教材缺乏的东西,所以国内的教材都不是写给愿意自学的读者,总之让人看了半天,却不知道里面的东西有什么用。
第二本书的原名叫《Modern Compiler Design》,中文名字叫做《现代编译程序设计》。该书由人民邮电出版社所出。此书比較关注的是编译原理的实践,书中给出了不少的实际程序代码,还有非常多实际的编译技术问题等等。此书另外一个特点就是其“现代”而字。在传统的编译原理教材中,你是不可能看到如同Java中的“垃圾回收”等算法的。由于Java这种解释运行语言是在近几年才流行起来的东西。假设你想深入学习编译原理的理论知识,那么你肯定得看前面那本龙书,假设你想自己动手做一个先进的编译器,那么你得看这本《现代编译程序设计》。
第三本书就是非常多国内的编译原理学者都推荐的那本《编译原理及实践》。也许是这本书引入国内比較早吧,我记得我是在高中就买了这本书,只是也是在前段时间才把整本书看完。此书作为新手教程也的确是个不错的选择。书中给出的编译原理解说也相当仔细,尽管不如前面的龙书那么深入,可是非常多地方都是点到为止,作为大学本科教学已经是十分深入了。该书的特点就是注重实践,只是感觉还不如前面那本《现代编译程序设计》的实践味道更重。此书的重点还是在原理上的实践,而非前面那本那样的技术实践。《编译原理及实践》在解说编译原理的各个部分的同一时候,也在逐步实践一个现代的编译器Tiny C.等你把整本书看完,差点儿相同自己也能够写一个Tiny C了。作者还对Lex和Yacc这两个经常使用的编译相关的工具进行了非常具体的说明,这一点也是非常难在国内的教材中看到的。
推荐了这三本教材,都有英文版和中文版的。非常多英文好的同学仅仅喜欢看原版的书,不我的感觉是这三本书的翻译都非常不错,没有必要特别去买英文版的。理解理论的实质比理解表面的文字更为重要。
编译原理的实质
前面已经说过,学习编译原理事实上也就是学习算法而已,没什么特别的。仅仅只是这些算法的产生已经形成了一套理论。以下我来看看编译原理里面究竟有什么高深的理论吧。
差点儿每本编译原理的教材都是分成词法分析,语法分析(LL算法,递归下降算法,LR算法),语义分析,执行时环境,中间代码,代码生成,代码优化这些部分。事实上如今非常多编译原理的教材都是依照85,86出版的那本龙书来安排教学内容的,所以那本龙书的内容格式差点儿成了如今编译原理教材的定式,包含国内的教材也是如此。一般来说,大学里面的本科教学是不可能把上面的全部部分都认真讲完的,而是比較偏重于前面几个部分。像代码优化那部分东西,就像个无底洞一样,假设要认真讲,就是单独开一个学期的课也不可能讲得清楚。所以,一般对于本科生,对词法分析和语法分析掌握要求就相对要高一点了。
词法分析相对来说比較简单。可能是词法分析程序本身实现起来非常easy吧,非常多没有学过编译原理的人也相同能够写出各种各样的词法分析程序。只是编译原理在解说词法分析的时候,重点把正則表達式和自己主动机原理加了进来,然后以一种十分标准的方式来解说词法分析程序的产生。这种做法道理非常明显,就是要让词法分析从程序上升到理论的地步。
语法分析部分就比較麻烦一点了。如今一般有两种语法分析算法,LL自顶向下算法和LR自底向上算法。LL算法还好说,到了LR算法的时候,困难就来了。非常多自学编译原理的都是遇到LR算法的理解成问题后就放弃了自学。事实上这些东西都是仅仅要大家理解就能够了,又不是像词法分析那样非得自己写出来才算真正的会。像LR算法的语法分析器,一般都是用工具Yacc来生成,实践中全然没有比較自己来实现。对于LL算法中特殊的递归下降算法,由于事实上践十分简单,那么就应该要求每一个学生都能自己写。当然,如今也有不少好的LL算法的语法分析器,只是要是换在非C平台,比方Java,Delphi,你不能运用YACC工具了,那么你就仅仅有自己来写语法分析器。
等学到词法分析和语法分析时候,你可能会出现这样的疑问:“词法分析和语法分析究竟有什么?”就从编译器的角度来讲,编译器须要把程序猿写的源程序转换成一种方便处理的数据结构(抽象语法树或语法树),那么这个转换的过程就是通过词法分析和语法分析的。事实上词法分析并不是一開始就被列入编译器的必备部分,仅仅是我们为了简化语法分析的过程,就把词法分析这样的繁琐的工作单独提取出来,就成了如今的词法分析部分。除了编译器部分,在其他地方,词法分析和语法分析也是实用的。比方我们在DOS,Unix,Linux下输入命令的时候,程序怎样分析你输入的命令形式,这也是简单的应用。总之,这两部分的工作就是把不“规则”的文本信息转换成一种比較好分析优点理的数据结构。那么为什么编译原理的教程都终于把要分析的源分析转换成“树”这样的数据结构呢?数据结构中有Stack, Line,List…这么多数据结构,各自都有各自的特点。可是Tree这样的结构有非常强的递归性,也就是说我们能够把Tree的不论什么结点Node提取出来后,它依然是一颗完整的Tree。这一点符合我们如今编译原理分析的形式语言,比方我们在函数里面使用函树,循环中使用循环,条件中使用条件等等,那么就能够非常直观地表示在Tree这样的数据结构上。相同,我们在运行形式语言的程序的时候也是如此的递归性。在编译原理后面的代码生成的部分,就会介绍一种堆栈式的中间代码,我们能够依据分析出来的抽象语法树,非常easy,非常机械地运用递归遍历抽象语法树就能够生成这样的指令代码。而这样的代码事实上也被广泛运用在其他的解释型语言中。像如今流行的Java,.NET,其底层的字节码bytecode,能够说就是这中基于堆栈的指令代码的。
关于语义分析,语法制导翻译,类型检查等等部分,事实上都是一种完好前面得到的抽象语法树的过程。比方说,我们写C语言程序的时候,都知道,假设把一个浮点数直接赋值给一个整数,就会出现类型不匹配,那么C语言的编译器是怎么知道的呢?就是通过这一步的类型检查。像C++语言这中支持多态函数的语言,这部分要处理的问题就很多其它更复杂了。大部编译原理的教材在这部分都是解说一些比較好的处理策略而已。由于新的问题总是在发生,旧的办法不见得足够解决。
本来说,作为一个编译器,起作用的部分就是用户输入的源程序到终于的代码生成。可是在解说终于代码生成的时候,又不得不解说机器执行环境等内容。由于假设你不知道机器是怎么执行终于代码的,那么你当然无法知道怎样生成合适的终于代码。这部分内容我自我感觉其意义甚至超过了编译原理本身。由于它会把一个计算机的程序的执行过程都通通排在你面前,你将来可能不会从事编译器的开发工作,可是仅仅要是和计算机软件开发相关的领域,都会涉及到程序的执行过程。执行时环境的解说会让你更清楚一个计算机程序是怎么存储,怎么装载,怎么执行的。关于部分的内容,我强烈建议大家看看龙书上的解说,作者从最主要的存储组织,存储分配策略,非局部名字的訪问,參数传递,符号表到动态存储分配(malloc,new)都作了十分具体的说明。这些东西都是我们编写寻常程序的时候常常要做的事情,可是我们却少去探求其内部是怎样完毕。
关于中间代码生成,代码生成,代码优化部分的内容就实在不好说了。国内非常多教材到了这部分都会非常简单地走马观花讲过去,学生听了也仅仅是作为了解,不知道怎样运用。只是这部分内容的东西假设要认真讲,单独开一学期的课程都讲不完。在《编译原理及实践》的书上,对于这部分的解说就恰到优点。作者主要解说的还是一种以堆栈为基础的指令代码,十分通俗易懂,让人看了后,非常easy模仿,自己下来后就能够写自己的代码生成。当然,对于其他代码生成技术,代码优化技术的解说就十分简单了。假设要细致研究代码生成技术,事实上另外还有本叫做《Advance Compiler Desgin and Implement》,那本书如今由机械工业出版社引进的,十分厚重,并且是英文原版。只是这本书我没有把它列为推荐书给大家,毕竟能把龙书的内容搞清楚,在中国已经就算非常不错的高手了,到那个时候再看这本《Advance Compiler Desgin and Implement》也不迟。代码优化部分在大学本科教学中还是一个不太重要的部分,就是算是实践过程中,相信大家也不太运用得到。毕竟,自己做的编译器能正确生成运行代码已经非常不错了,还谈什么优化呢?
关于实践
编译原理的课程毕竟还仅仅是解说原理的课程,不是专门的编译技术课程。这两门课程是有非常大的差别的。编译技术更关注实际的编写编译器过程中运用到的技术,而原理的课关注解说其基本理论。可是计算机科学本身就是一门实践性非常强的课程,假设可以学以致用,那才叫真正的学会。李阳在解说疯狂英语的时候就说到,仅仅要当你会实际中运用一个单词一个词组的时候你才干叫学会了这个单词或者词组,而不是仅仅是知道了它的拼写和意思。事实上不论什么学习都是一样的,假设缺少了实践的结合,你不能算学会。
编译原理的课程主要就是解说编译器产生的理论和原理,那么非常easy,自己写个编译器就是最好的实践过程了。只是你得小心,编译系统可能是全部软件系统中最复杂的系统之中的一个,不然为什么大学里面还会把编译器的编写开成一门叫做编译原理的课程来讲?我非常佩服那些学了操作系统原理就開始自己写操作系统,学了编译原理就開始自己写编译器的人们,确实,在中国,敢这么做的学生太少了。且无论你这样做能不能做成功,至少有了这个尝试,会让你的程序设计,系统规划安排的功底增进不少。我以下给出一些关于实践过程中可能会遇到的困难,希望可以在你陷入困境的前帮你一把。
1. Lex和Yacc. 这两工具是作为词法分析非常语法分析的工具。假设你自己写一个编译器,我十分不建议你连词法分析这样的事情都亲手来写。Lex和Yacc应该是作为每本编译原理的教材的必备内容,但是在国内的教材中缺非常少看到。这两个工具是Unix系统下的小东西,假设你要在Windows中运用,那么你最好去下在cygwin这个软件。它是个在Windows下模拟Unix的东东,里面就包括了flex.exe和bison.exe(yacc)这两个工具.这两个工具使用起来还挺麻烦的(事实上unix 下的非常多十分实用的工具都是这样), 只是在《编译原理与实践》这本书上对于这两个工具的解说十分具体,还列举了不少实际的样例。
2. 做解释型语言比做生成机器代码的编译器简单。尽管说,做解释型的编译器,像Java那样的,你还得自己去写解释器,只是这样你就不必去查找机器代码的资料了。假设你做生成的终于机器代码编译器可能会遇到问题还有就是寄存器为基础的代码生成方法。前面说过,假设你生成的是以堆栈为基础的代码,那么其代码生成过程十分简单,须要考虑的东西也不多,假设你考虑终于的机器代码生成的话,你必须考虑机器的寄存器怎样分配等麻烦的问题。
3. 考虑用别人已经生成的语法文件,尽量不要自己动手写词法文件和语法文件.以前一个朋友以前说过,写出一个好的程序语言的语法定义,就差点儿完毕了一个编译器的一半.确实是这样,语法文件的编写是个非常难的事情.如今网上到处都能够找到比方C语言,C++,Java, Tiny C,Minus C等语言的词法文件和语法文件,你全然能够自己下下来来用.
在《编译原理及实践》的书中,作者给出了一个Tiny C的所有代码.我自我感觉作者的这个编译器做得非常不错,相对于其他php,perl等语言的源码来说,简单得多,easy看懂,并且非常清晰地展现了一个完毕的编译系统的实现过程.其源码能够在作者的站点上下载.
后话
编译原理的学习可能算是一个困难的历程,特别是对于那些不正确编译系统感兴趣的同学来说.既然它已经作为了大学本科的必修课程,那么就说明的它引申出来的一套理论在整个计算机科学领域还是占有相对重要的地位.
假设我们考究一下历史,就会发现非常多被称为程序设计大师的人都是编译领域的高手.写出第一个微型机上执行的Basic语言的比尔盖茨,设计出Delphi的Borland的”世界上最厉害的程序猿”, Sun的JAVA之父, 贝尔实验室的C++之父…