第一章
编译程序其实是一个翻译程序,在两种语言中实现转换而已。对于源语言,源程序,目标语言
,目标程序就没有什么好说的了。至于宿主语言和宿主机的概念,联想到那些细菌,病毒的生
存方式,基本也就能了解了,离开了宿主,只能是死路一条,在这里当然是指编译程序的实现
语言和环境了。
编译程序的分类说是分成解释程序,编译程序,汇编程序,其实这三者没有本质的区别。解释
就好比口译,翻译一次只能听一次,要想再听还需要再翻译。实在是个累人的活,不过挣钱也
多。编译程序呢,就好比是笔译,翻译一次就能出版,大家N年后照样能看到。汇编与上面两者
唯一的区别就是把汇编语言翻译成机器语言,而且也是笔译。用翻译来理解这三者之间的关系
和区别再好不过了。现总结如下:解释是口译,听一遍就over了,而且一般人还听不懂,毕竟
没有翻译成最直接的机器语言;编译是笔译,翻译后可以多次使用,可惜也不是机器语言,我
们老百姓读起来还是很吃力;汇编呢,既是笔译又翻译的很彻底,适合所有人读,只不过要求
书之前已经翻译的差不多了。忘了补充一句,这里把人比作机器了。
编译执行和解释执行和上面的例子差不多,编译一次就能解决问题,完全可以把原先那些资料
丢了,解释可不行,必须一行一行的读,而且原先的资料不能丢。
编译程序的结构分为词法分析,语法分析,语义处理中间代码生成,代码优化和代码生成五个
步骤。词法分析就是识别单词,并对单词进行标记说明(属性)。语法分析就是根据程序的语
法规则对词法分析的结果进行扫描,好比英语中判断一个句子写得是不是正确,光每个单词正
确而读起来狗屁不通肯定是不行的。语义分析和中间代码生成就是对语法分析的结果进行初步
翻译,好比我们拿到一句英文,肯定要在脑中先想想它表达的大概是什么意思,我想绝对没有
高人能拿到句子立刻就能把译句写出来的。思考这个过程是很重要的。可以说,这步就是决定
翻译是否成功的关键,也是考察一个人英语水平的重点所在。代码优化不用多说,消去那些冗
余的词汇,让句子变得精干简洁,我知道谁都想这样做,除非没有时间,这一步能给你带来不
少好处,不光是代码运行时间能缩短,而且考试时分数也会略有提高。目标代码生成,好了,
你可以把你草稿纸上面的句子工工整整地写在answer sheettwo上面了。恭喜你了,翻译的不错
。除了上面五步,表格管理和出错处理也是编译过程中的组成部分,表格管理好比是先把翻译
过程中可能会遇到的单词写下来,防止自己一紧张忘了这个单词该怎么写。出错呢,就是把自
己翻译的初稿先读一遍,标记处感觉不对头的地方,例如做个下划线之类的标记,这样在进行
优化时就能知道自己该注意什么了。
编译中有一个“遍”的概念。其实就是对源程序和中间代码的扫描次数的问题,既然上面已经提
到编译过程的五个步骤,我们需要判断是否是一遍就把这五个步骤都执行了,还是分几遍来做
。通俗的说,当你翻译一本书的时候,你是24小时通宵把这本书译稿赶出来,还是分好几次来
做。一次的好处在于一步到位,可惜你到深更半夜时感到头有些晕,无法冷静,此时翻译出来
的东西读起来不如刚开始的好。多次的坏处在于你接着上次未完成的任务继续往下干时,自己
需要把之前的内容重读一遍,不仅仅是为了防止自己遗忘,也为了进入书中的意境,这样才能
翻译得更好,虽然时间漫长,但所有这些工作都是在你头脑清醒的时候完成的,质量肯定就好
。
编译程序的构造感觉和上面有些重复,源语言,目标语言,编译方法、技术、工具,这显然是
必须具备的东西,有米有锅才能煮出一锅好饭嘛。
编译程序生成有人编程(累死人),移植(类似云计算),自编译(源语言做宿主语言)和自
动生成(很看好)四种。我这里只想说说移植,这需要两台计算机进行连接,A中的源程序需要
B中的宿主语言来编译成A中需要的目标程序。两个机器构成一个团体,完成了A自身不可能完成
事。但如果B中也没有呢?那就可能需要C机了,结果可能是A中的源程序调用B,B再调用C,C
完成把结果返回给B,B再把结果进行处理后返回给A。两个三个如此,那N个,不可想象了,团
结的力量就是大啊。云计算难道就是类似这样的变种么?
完成编译原理第一章读后感。祝大家愚人节快乐。
第二章
语言是由语法和语义构成的,其中语法是语言的描述规则,语义则是语言的含义。可以说语法
起到了产生语言的作用。简而言之,语言是由单词按一定规则组成句子来表达特定的意思,规
则即是语法,表达的意思即是语义。
元语言是描述另一个语言的语言,一般情况下元语言符号只可能含有EBNF中的这几种,“<>”,“
→”,“|”以及花括号,圆括号,方括号。我们在考试的时候元语言符号集中的元素一般就是上
面列出来的这些。
字母表是一个集合,其中包括需要用到的字母,这些字母也可以称为符号,所以字母表也可以
称为符号集。用大写英文字母或者Σ来表示一个字母表,其中的符号可以用枚举的形式列出。
符号串来源于字母表,一个符号串就是由字母表中的元素通过组合而形成的,只不过符号串的
概念超越了句子,只要是有穷的符号都可以称为符号串,哪怕是一本书的字母量。符号串的长
度就是由其中构成的符号个数决定,至于不包含任何符号的符号串,想都不用想肯定是空串,
用ε表示,这个符号貌似很熟悉,极限中用到,那时是指任意小但不是为0的一个常量,现在则
是名副其实的0了。
符号串的前缀后缀就是从前面或者后面删去0个或若干个字符留下了的字符串,真前缀自然就是
至少删去一个符号了。不过当从一个符号串又删去前缀又删去后缀的时候,剩下的称为子串。
感觉这里关系挺乱,前缀是删去后面的符号剩下的东西,现在又把前缀删了,剩下后面的东西
,把这个东西称为子串,其实这个东西也可以叫后缀。唉,感觉起这么多名字实在是没用。
文法的定义貌似不是很难,一个四元组,里面包括非终结符号集(用大写字母),终结符号集
(小写字母),公理(起始的符号串),产生式集就是描述文法规则的地方,也是非终结符号
集通过此得到终结符号集的地方。
符号串的运算这里全都是连接运算,记住这点就OK了,虽然他也有方幂和乘积这些运算,但全
都是字符串的拼接。这些都没有特别重要的地方,但{ε}A= A{ε}=A,XA= AX=X(X为空集),X={}≠{ε}
。
符号串集合的闭包是个稍稍复杂一点的概念,闭包运算好像是无穷多项,反正是只要是其中字
符组成的元素都能看成是符号串集合的闭包。对于自反闭包,它仅仅比正闭包多了一个{ε}而已
。
直接推导和直接推导序列是在文法规则中用到的一种描述方式罢了,一个是一步就能推导,一
个是经过N步推导出W,两个相差不多。
最左推导和最右推导从字面上看就能理解其中的含义,一个每次都是从最左边的非终结符开始
替换,另一个是从最右边的非终结符开始替换。这两者对于没有二义性的文法而言最终都能得
到相同的语法树。
句子可以说是包含在句型之中的,句子中没有非终结符,而句型中是可以包含非终结符的。对
于规范推导,规范句型,规范规约,这些都与最右推导有关,这感觉很不符合中国人的习惯。
我们国人都喜欢从左向右慢慢来,看来这编译原理还是引进国外的啊。
对于句子的二义性问题,可以说除了最左推导和最右推导外,没有什么好的办法解决。至于让
你把一个有二义性的句子改写成没有二义性的句子,我问过老师,这没有什么比较好的算法或
者思想,只能靠一个人的思考了,思考到位了,立刻就能写出来,幸好考试不考,我们都不是
天才。
递归文法的作用肯定是用来产生无穷多的字符串的,如果一个句子中出现N的指数这样的形式,
可以说肯定是用到了递归文法。
语法树终于出现了,有一个概念需要弄清楚,一棵语法树包括了所有可能的推导过程。细想也
对,既然是树了,肯定比线性的字符序列要强大不少,而且一棵树如果没有涵盖所有的推导,
那要树干嘛,画一棵树比写一个推导过程麻烦多了。所以,树的功能还是很强大的。至于二义
性的问题,这就与树是有关的,如果出现二义性,则树的画法不止一种。
二义性的原因,绝大部分是因为运算符“+”和“*”未体现优先级造成的。比如E→E+E|E*E|a,这里
就有二义性,“+”和“*”号左侧和右侧没有什么区别。
文法的分类包括0,1,2,3型文法。0型文法可以用图灵机来解决,可惜图灵机只是理论中的东西,
人们折腾到了现在也没有造出来,如果造出来那可真牛叉了。0型文法是应用最广泛的文法,
1,2,3型文法都只是它的子集罢了。1型文法对应得是线性有界自动机,2型是非确定下推自动机
,3型是确定有限自动机。基于目前的专业水平,我们本科期间把有限自动机学好就不错了。所
以3型文法十分重要。最后补充一点从0型到3型文法,产生式限制越来越严,但描述的功能却原
来越弱。正所谓Every coin has two sides.
确定的有限自动机可能是最简单的自动机了,这里只有一个初态,输入的每一个字符都能跳转到某个固定的状态而不会产生二义性。五元组中最重要的或许就属转换函数f了,如果最后跳转到的状态不是终态,那该字符串就不能被结束,必须被抛弃掉。关于DFA有三个注意点,第一个是DFA是具有离散输入、输出系统的一个纯数学模型;第二个是DFA的技巧在于状态的设置;最后是DFA映射的唯一性和初态的唯一性。
对于字接受问题,可以理解为如果存在一条从初态到某一终态间的路径,且该路径上所有弧的标记符连接的字等于该字,这该字被DFA所识别(亦称接受)。当DFA仅具有一个状态结,如果该状态既是初态又是终态,则空字集合也能被DFA所接受。一个DFA所能接受的字的全体就构成了改自动机识别的语言,可以说和文法有一致性。
至于非确定自动机NFA,映射函数就是一个多值函数而已,初始值可能有多个,其他几乎和DFA没有什么区别。
关于DFA和NFA,都可以用状态转换图和状态转换矩阵来表示。不过对于每一个NFA都存在一个DFA,使得DFA和NFA识别的语言一致。这也就引出了NFA的确定化问题。
子集法,这是个解决NFA确定化最好的方法,操作简单,只不过需要借助状态转换矩阵。重新命名一下状态即可。最小化,这是另一个算法,是为了对确定化的DFA进行集合归并,得到最简单的DFA而进行的优化算法。该算法的核心思想是找到等价状态,从而对等价状态进行合并。其实等价状态就是q1和q2,如果从这两个状态出发,总是同时抵达接收状态或拒绝状态之中,称这两个状态等价。不过我感觉好像并不是只有接收和拒绝这么简单,应该这两个状态能同时到达同一个集合中的两种状态才行。只要到达态在同一个集合中,那就是等价的。可能这个条件更严格了,但目前没有找出反例来证明它的错误。
无关状态是指从DFA初始状态开始,任何输入序列都不能到达的状态,这些状态需要剔除掉。如果DFA没有无关状态,也没有彼此等价的状态,则称DFA是规约的(估计是不能再小了)。
划分法是寻找且合并等价状态。即:将给定的DFA划分为互不相交的子集,使得任何不同两个子集的状态都是可区分的,而同一个子集的任何两个状态都是等价的。这也没什么好说的。
正规式与正规集其实可以说两个相互对应的概念,只不过符号表述不一样而已。正规式用的是离散数学中的与或非符号,而正规集
用的是集合中的交并符号。至于如何产生一个正规式或正规集用到三条准则,这个在书上都能找到,和集合的产生差不了多少。下
面做几点说明:
1.正规式与相应的正规集是等价的,正规集给出了相应正规式所描述的全部单词(句子)。
2.正规式的运算结果是正规集。
3.正规式不是集合,其运算结果正规集是集合。但空集是特例。
正规式一般有如下四种描述:关键字,标识符,无符号整常数,关系运算符。这四种估计任何程序设计语言中都有。
下面是有限自动机,正规式,正规集三者之间的关系:有限自动机所接受的语言其实是一个正规集,有限自动机其实和正规式是相
互对应的。这样听起来好像有些矛盾,可以理解为正规集是句子,有限自动机和正规式是文法,这三者可以说是相互对应。
正规式与有限自动机的替换应该来说是一个比较重要的内容。主要是通过状态转换图来进行替换,其中用到替换规则一和替换规则
二,都是需要添加起始节点和终止节点的,然后通过三大替换规则,最终就能实现两者之间的互换。不知道如果写一个正规式,直
接画状态转换图能否画出来,估计题目特别简单的时候能做做,一旦流程多了,那就难办了。
第三章
词法分析
词法分析是最基本的分析程序,如果源程序连词法分析都通过不了,那可以说此源程序错误会
不少。其实词法分析的功能很简单,就是接受字符串形式的源程序,按照源程序输入的次序依
次扫描源程序,识别出最基本的构成部分--单词。
关于词法分析以下几点说明很重要:
1.词法分析是编译程序的第一个阶段且是必要阶段,这个是废话,单词识别不出来,后面再怎
么弄也是白搭。
2.词法分析的核心任务是扫描、识别单词且对识别出的单词给出定性、定长的处理。
单词是语言中具有独立意义的最小语法单位。这里面两点比较重要:具有独立的意义,最小的
语法单位。独立意义不消多说,单词不需要和其他单词组合才能表达出一个意思,那些短语看
似必须由两三个单词构成,其实每个单词都发挥着其特定的作用,如表示方向等。最小的语法
单位其实默认就是单词了。
常用的程序设计语言的单词类如下:
关键字(C语言中if while等等)
标识符(C语言中char a[100],后面遇到a都联系到数组)
常量(阿拉伯数字)
运算符(各种符号,加减乘除大小等于等)
界限符(逗号,分号等)
属性字可以说是识别单词过程中用到的参考系。每个单词在属性字中都对应一个相同的语言码
(例如pascl语言),这样再识别过程中就能通过语言码来准确判断是哪个单词了。由于属性字
的长度还算可以,故可存储单词的相应属性,包括是否是关键字,是否是常量等等。前半部分
是刻画单词类别的编码,后半部分是单词的内码值。这个就和居民身份证一样,在单词世界中
凭它作为标识。常见类单词属性字包括标识符属性字,关键字属性字(每个关键字对应一个属
性类别码,内码值部分为空),常量属性字,运算符和界限符属性字(优先级相同的对应一个
属性类别码,优先级高的类别编码值大)。从上面可以看出,属性字是可以自己设定的,只要
自己用起来方便就行。
输入缓冲区的概念应该来说比较简单,为了防止输入字符串过长,而导致溢出缓冲区识别不出
来的情况,所以缓冲区需要在空间用完的情况下刷新,清除其中所有的字符。但因此也产生了
一个问题,如果一个单词刚好输入了一半缓冲区满了,那该怎么办?于是就有了对半互补缓冲
区,刷新时每次只刷新一半。不知道有没有单词长度能达到半个缓冲区那么长,如果有,这种
方法也会让单词识别不出来。
预处理程序作用:
1.减少内存空间占用
2.减轻扫描器实质性处理的负担。
以上两点可以看出预处理的好处,先执行一遍,该用的东西都先准备好,真正处理的时候只要
拿过来用就行了。至于预处理程序的主要任务,首先是滤掉源程序中的非单词成分,还进行实
际的预处理工作(滤掉住宿,宏替换,文件包含嵌入,条件编译嵌入)。
在识别单词的时候,我们常常用到FA,因为它能直观的展现一个单词的被识别过程,但在实际
的处理过程中,我们不可能画图,必须借助数据结构来实现FA,于是涉及到状态矩阵转换为二
级目录表的问题。其中主表中数据项存储的是状态和分表地址或子程序入口,当状态为终态时
,分表地址为子程序入口,当状态不是终态时,这就是分表入口。在分表中数据项存储的是当
前输入字符和转换状态。
扫描自动生成器主要有LEX和Flex两种,前者收费,功能还算可以,后者免费,但功能比前者强
大。LEX编译器接收LEX源程序,由LEX编译器处理LEX源程序,产生一个词法分析器作为输出。
在UNIX环境中,LEX编译器的输出是一个具有标准文件名lex.yy.c的C程序,经过C编译器的编译
产生a.out文件,a.out是一个实际可以运行的词法分析器。这些考试不作要求,可以略过了。
第四章
语法分析
语法分析程序的功能:按照源语言的文法,从源程序串识别出相应的语法范畴,同时进行语法
检查.
语法分析方法:自上而下语法分析方法(从开始符号推导)这是一种产生的方法,面向目标的
方法。自下而上语法分析方法(从给定的字符串开始)是一种辨认的方法,基于目标的方法。
短语与直接短语:这个需要理解什么叫相对于A的短语,在这里都是指X是句型YXZ相对于M的短
语,如果M能推导出X,至于直接短语就是指M能通过一步推导得到X。这个概念以后会经常用到
。自上而下分析法的核心是不断寻找可匹配串对句型中最左的非终结符进行推导的过程;自下
而上分析法的核心是不断寻找可规约串与P的候选式匹配,并用P的左部的非终结符代替之。
句柄:一个句型的最左直接短语。这个概念有两层含义,首先是直接短语,然后还要是最左的
。至于是不是最小的,这尚且还不知道。不过句子的句柄是语法树中最左子树的所有叶节点从
左到右的排列或在句子的规范推导序列中,最后使用的产生式的右部。按照树的知识来看,应
该是最左下方的子树,但不一定是层数最多的。
左递归:这个是自上而下分析法中存在的最大问题,如果一个文法存在左递归,那算法的开销
就会非常大,而且常常得不到结果。需要把左递归转换为非左递归,至于是否是右递归,那无
所谓,这不会给算法造成任何影响。有些左递归是可以直接看出来的,但还有些一眼看不出来
,为此需要进行推导,可能需要三步推导以上才能找到左递归的痕迹,此时有些文法推导式可
能已经没有用了。
回溯:这是自上而下分析中的第二个问题,如果某个推导式候选式很多的话,那需要一个一个
去比较,这样效率很低下。于是引入了First集这个概念,列出每个候选式的第一个字母,查看
First集从而就能判断是不是该用这个候选式进行推导。不待回溯的条件就是First集两两彼此互
不相交。如果相交,则是左公因子导致的,这时需要提取相同的公因子,从而确保First集的独
立性。在后面的LL(1)文法中,左递归和左公因子是比较好的排除一个文法不是LL(1)文法的方法
。
LL(1):这是本章最重要的内容了,首先需要理解什么是LL(1),通俗的说,LL(1)是预测分析器,
第一个L代表扫描模式是自左向右的,第二个L代表分析模式是最左推导。至于括号中的1代表在
分析中最多向前看一个输入字符。LL(1)的组成包括总控程序,分析栈和分析表,其中分析表是
LL(1)分析器的核心。First集合以及后面讲到的Follow集合都与分析表有关。
分析栈:存放分析过程中的文法符号(待匹配和已经推导的串)。
分析表:判断当前用那条文法进行推导
总控程序:做大的调度工作,匹配之类等待
Follow集:Follow(A)的含义是指,在文法G的一切句型中,能够紧跟在A之后的一切终结符或#(
结束标志)
满足LL(1)文法的必要条件:FIRST(α1)∩(FIRST(α2 )∪FOLLOW(A))=Φ,其中A →α1 |α2
这里给一个抽象一点的定义:一部文法G,若它的LL(1)分析表M不含多重定义入口,则称它是
一个LL(1)文法。由LL(1)文法产生的语言称为LL(1)语言。至于后面提到的LL(K)文法,只不过是
向前多看几个字符罢了,在实际中用的不多,因为难度较大。
关于LL(1)文法的性质如下:
⑴任何LL(1)文法是无二义性的。
⑵若一文法中的非终结符含有左递归,则它必然是非LL(1)文法。
⑶非LL(1)语言是存在的。
⑷存在一种算法,它能判定任一文法是否为LL(1)文法。
⑸存在一种算法,它能判定任意两个LL(1)文法是否产生相同的语言。
⑹不存在这样的算法,它能判定上下文无关语言能否由LL(1)文法产生。
第五章
自下而上分析
前一章介绍的是自上而下分析,这一章讲的是自下而上分析。一般说来,自下而上分析算法比较复杂,但效率高,所以得到普遍采用。自下而上的思想是从给定的输入串$开始,不断寻找子串与文法中的某个产生式P的候选式进行匹配,并用P的左部代替(规约)之,逐步规约到S。关键在于确定可归约串(归约条件),如何归约(归约原则)。
算法优先分析:定义算符之间的优先关系,借助这种关系寻找“句柄”进行归约。至于算符文法的定义,是说如果文法中任何一条规则中都不含有连续出现的两个非终结符,则可以称为算符文法,简称为OG文法。
算符优先文法:这个比算符文法更严格,对任意两个终结符最多只满足优先级三种关系之一才可以。a的优先级等于b表示a和b同时由一条文法推出,并且ab相邻;a优先级小于b表示a比b先出来,b可能要进行再一次推导才能得到,a优先级大于b正好相反。这类文法称为OPG文法。
FIRSTVT集和LASTVT集在判断两个终结符的优先级时有很大作用,其中FIRSTVT类似于自上而下分析法中的FIRST集,LASTVT和follow集可不是一回事,正如字面意义所理解的那样,FISRTVT就是规则中第一个推出来终结符,LASTVT是规则中推出来最后一个终结符。
素短语:a是句型bac关于A的短语且a至少含有一个终结符号,并且除自身外不再含有任何更小的带终结符号的短语,则称a是句型bac关于A的素短语。最左边的素短语称为最左素短语。另外还有一个判断最左素短语的方法,理解起来比较困难。
优先函数表:这又回到了算符优先文法上来了。对于给定的文法G,若其终结符个数为N,则G的优先关系表的大小为(N+1)*(N+1)。而如果引入优先函数表,那么优先关系表的大小就变成了2*(N+1),这能大大地节省空间。
优先函数表:引入两个优先函数f和g。其中f(θ),称为栈内优先函数,g(θ)称为比较优先函数(栈外优先函数)。将每个终结符号与两个优先函数f(θ)和g(θ) 相对应,由于函数值都是自然是,优先级比较就成为了自然数值大小的比较。
算符优先分析器:由三部分组成,分别是总控程序,分析栈,优先函数表。这个和自上向下分析构造差不多,只不过换了优先函数表而已,核心当然就是它了。
LR分析技术是编译系统中语法分析器实现最常用和有效的一种分析方法,原因在于理论上比较完善,适用性强,对文法的要求限定少,同时也便于自动生成。
LR(k):L是指扫描模式是自左向右,R是指分析模式是最右推导的逆序(即规范归约),k是指最多再向前看k个输入字符,就能确定适合于文法规则的句柄是否已经在栈顶形成,从而确定当前的分析动作是移进还是规约。其分析器组成包括总控程序,分析栈和LR分析表。分析栈中不光有文法符号,也包含状态,这里的状态记录分析过程中每一步“历史”或“展望”的信息。LR中分析表包括分析动作表和状态转换表。其中分析动作表中存放的是终结符,状态转换表中存放的是非终结符。
前缀:一个句型的任意首部,称为该句型的一个前缀。而规范句型的一个前缀,称为该句型的一个活前缀。其中活前缀的特点是不包含句柄只后的任何符号。在LR分析中,必需使栈中符号始终是活前缀,这样再从输入串中读入几个符号后,构成刚好包含句柄的活前缀,进而实施规约。
LR(0)项目:在文法G的每个产生式的右部的任何位置添加一个圆点,所构成的每个产生式称为LR(0)项目。LR(0)项目可分成四类:规约项目,接受项目,移进项目和待约项目。
LR(0)项目规范族:识别文法G活前缀的DFA项目集的全体称为项目集规范族。
LR(0)文法:若一个文法G的识别活前缀的DFA的每一个项目不存在:即含移进项目又含规约项目或含有多个规约项目,则每个项目集的项目相容,可以说G是一个LR(0)文法。
SLR(1)文法:具有SLR(1)分析表的文法G称为SLR(1)文法。其中SLR(1)分析表是指每个入口不包含多重定义。使用SLR(1)分析表的语法分析器称作SLR(1)分析器。
LR(k)项目:[A→α·β,a1a2…ak],其中前面的推导式是LR(0)项目,后面的a1a2...ak是搜索符串。LR(1)有效项目就是搜索符串只有一个字母,这里补充一点,搜索符仅针对某个活前缀是有效的,换了搜索符,那规约就可能出错。
LR(1)文法:具有LR(1)分析表的文法G称为LR(1)文法,LR(1)分析表也是每个入口不含多重定义。使用LR(1)分析表的语法分析器称作LR(1)分析器。
对比三种分析器,分析功能从强到弱为:LR(1)>SLR(1)>LR(0)分析器开销从多到少为:LR(1)>SLR(1),LR(0)。为了既能降低内存开销又能具有LR(1)那样的分析功能,为此引入了LALR(1)分析器。其中LALR(1)中的分析表状态数和LR(0)的项目集规范簇C是一样的,
同心项目集:对文法G的LR(1)项目集规范族,若存在两个(包括以上)项目集i0i1,其中i0,i1项目集中的LR(0)项目相同,仅搜索符不同,则称i0,i1为G的LR(1)的同心项目集。
LALR(1)文法:具有LALR(1)分析表的文法G称为LALR(1)文法,LALR(1)是按照LALR(1)项目集规范族构造LALR(1)分析表,每个入口不含多重定义。使用LALR(1)分析表的语法分析器称作LALR(1)分析器。
总的来说,如果要构造一个LALR(1)分析法,首先要构造文法G的LR(1)的项目集规范族C,然后合并C中所有同心的LR(1)项目集,可以用M来表示,则M称为LALR(1)的项目集规范族,最后要验证M有没有冲突,如果没有冲突,可以根据M来构造文法G的LALR(1)的分析表。
判断一个文法是什么文法:
1。构造G的LR(0)的C,判断是否有冲突,没有就是LR(0)文法
2。有冲突使用SLR(1)方法加以判断,如果能解决冲突,那就是SLR(1)文法
3。如果冲突仍然不能解决,则构造G的LR(1)的C,判断是否冲突,如果还是冲突,G为非四类LR
4。没有冲突情况下,构造G的LALR(1)的C,如果没有冲突,则G是LALR(1)文法,如果有冲突,则G是LR(1)文法。
重要结论:
任何一个二义文法都不是一个LR文法,因为二义文法会导致语法分析的二义性,但二义文法的有用之处在于可以缩小文法的规模。
编译中的错误种类:词法错误,语法错误,语义错误,违法环境限制的错误。
错误处理的基本目标:清晰准确地报告错误,迅速从每个错误中恢复过来,以便诊断后面的错误,不使正确程序的处理效率降低。
错误处理与恢复策略:紧急恢复方式,短语级恢复,出错产生式和全局纠正。