一.摘要
最近在学习编译原理,所用的书籍以《编译原理及实践》,《flex与bison》这两本书为主,另外辅有《编译原理》(江湖人称编译“龙书”)和《编译器工程》(英文版为Engineering a Compiler Second Edition)等。其中,《编译原理及实践》这本书里头介绍了一种文法简单,适合编译原理初学者实现的语言,书中称之为TINY语言,本文主要讨论以《编译原理及实践》书中所讲各种方法,用附录里所给出的代码,简单拼凑,实现一个简单的递归下降TINY语言编译器前端(词法,语法部分)。
输入:由TINY语言编写的程序
输出:输入TINY代码的线性语法树
二.写在前面的一些话:
这只是作为初学编译原理初学者的我所做的一次尝试,试图用一些现成的原料来实现一个简单的编译器前端,并没有关于词法分析,语法分析等理论技术的具体实现(我也不会详细讲这些理论技术),这些具体实现C代码均是由我采自于Kenneth C. Louden所著的《编译原理及实践》这本书,另外TINY语言的定义也是出自于这本书。
刚刚开始学习时,老师曾经要求我们写过一个简单的词法分析器,我花了几个小时就写出来了,但那只是按照我自己的思路写出来的“残次品”,输出的结果根本不能作为语法分析器的输入(事实上连最基本的词素补充都没做到,那时还没有token的概念),原因是我当时连词法分析都没看完就开始写,完全忽略了编译器工程的线性与整体性,前端与后端,前面的输出作为后面的输入,更没有抽象语法树这类的概念,所以,这是一次过急的尝试,虽然失败了,但经验很重要,这使我能静下心来好好看一看理论。
在连续一个多月的理论学习后,我已经基本掌握了一些必要的词法、语法的前端分析技术(至少我自己这么认为,不知道老师怎么想),但就这些理论知识而言,我还是虚得很,为何而虚,就是因为缺少实践。让我用这些理论真刀真枪地从零开始做一个编译器出来?还是算了吧,我压根不知道从何处下手。但经过数天的思考,我选定了这样一种方式:逆向工程,就是我们军工界的路,二十年前沈飞引进SU-27,成飞学习SAAB-37,逆向工程了二十多年,于是我们有了歼-11,歼-10,到现在歼-16,歼-20都快出来了,青出于蓝而胜于蓝。这就是为什么我前面说,要用现有的代码拼凑出一个简单编译器前端(别管有多么简陋,再简单的编译器对我这个“小白”来说都很难,先解决从无到有),这就是逆向工程的第一步:进口SU-27后,把它拆了,拆了之后再拼起来,看看能不能飞,内部结构搞不懂没关系,照猫画虎先让它飞起来,飞起来第一步就成功了!其他的事,还是等飞起来后再说吧。
所以,本文的主要目的是:工程实践,对,就是以熟悉为目的初学者入门实践。真正从零开始的实践。
身为初学者的我希望本文也能对初学者们有所帮助,也希望能多多交流。
另外,如果是大神路过,欢迎指正我的错误与不足(如果能如您法眼的话)。
三.背景知识介绍
编译器的各个阶段可以根据其用途分成两个大阶段:词法分析、语法分析和语义分析重点在处理编程语言的符号系统上,统称为编译器的前端(front-end),而中间代码生成、规范化、指令选择、控制流分析、数据流分析、寄存器分配、指令流出、汇编、连结等着重处理代码计算逻辑的阶段统称为编译的后端(back-end)。
在本文中,由于我是初学者,所以只讨论前端的两个部分:词法分析和语法分析。
词法分析程序扫描输入文件,将文件中的各个语句的组成单词分离出来,称之为词素(lexeme),并为各个词素添加相关信息。将所生成的token信息作为语法分析器的输入。语法分析主要是从词法分析程序中获得token输入,对程序进行基本的语法分析,生成抽象语法树,作为下面语义分析的输入。语义分析及后端部分这里暂时不做介绍&#