语法分析-自上而下分析
编译原理第4章 语法分析——自上而下分析
17
例3.4.1 假定有文法G(S): (1) S→xAy (2) A→**|*
分析输入串x*y(记为)。
x*y
S
IP x A y **
18
例3.4.1 假定有文法G(S): (1) S→xAy (2) A→**|*
分析输入串x*y(记为)。
x*y
S
IP x A y **
19
例3.4.1 假定有文法G(S): (1) S→xAy (2) A→**|*
(4.3)
虽没有直接左递归,但S、Q、R都是左递归的
SQcRbcSabc
一个文法消除左递归的条件
丌含以为右部的产生式
丌含回路
PP
30
例 文法G(S): S→Qc|c Q→Rb|b R→Sa|a
(4.3)
虽没有直接左递归,但S、Q、R都是左递归的
SQcRbcSabc
Q
Q
ⅹ
S
R
S→Qc|c Q→Rb|b R→Sa|a
35
例 考虑文法G(S)
S→Qc|c Q→Rb|b R→Sa|a
消除S的直接左递归后: S→abcS | bcS | cS S→abcS | Q→Sab |ab | b R→Sa|a
关于Q和R的觃则已是多余的,化简为:
S→abcS | bcS | cS
S→abcS |
(4.4)
36
注意,由于对非终结符排序的丌同,最 后所得的文法在形式上可能丌一样。但 丌难证明,它们都是等价的。
分析输入串x*y(记为)。
x*y
S
IP
15
例3.4.1 假定有文法G(S): (1) S→xAy (2) A→**|*
分析输入串x*y(记为)。
04 语法分析-自上而下分析
待分析的输入串: 待分析的输入串:i+i
只有当a 只有当a是允许出 现在非终结符A 现在非终结符A后 面的终结符时, 面的终结符时, 才可能允许A 才可能允许A自动 匹配。 匹配。
尾随集的定义: VN尾随集的定义:
=*>…Aa Aa…, FOLLOW(A)={a|S =*> Aa , a∈VT}; 特别地,如果S=*> S=*>…A 那么# FOLLOW(A)。 特别地,如果S=*> A,那么# ∈FOLLOW(A)。
例子
文法: S→xAy A→**|* 文法: 输入串:x*y 输入串: S => => => => xAy x**y xAy x*y (S→ xAy) (A→**) 回溯) (回溯) (A→*)
带回溯自上而下分析面临的问题
问题: 问题: 文法的左递归问题 回溯问题 虚假匹配问题 出错位置不确定 低效
实现思想: 实现思想:
分析程序由一组递归过程组成。 分析程序由一组递归过程组成。每一过程 对应于一个非终结符号。 对应于一个非终结符号。 每一个过程的功能是:选择正确的右部。 每一个过程的功能是:选择正确的右部。 在右部中有非终结符号时, 在右部中有非终结符号时,调用该非终结 符号对应的过程。 符号对应的过程。
消除文法的左递归
文法不含回路(形如P=+> P推导 推导) 文法不含回路(形如P=+> P推导) 不含回路 前提: 前提: 不含以ε 也不含以ε 为右部的产生式 结论: 那么可以通过执行消除文法左递 结论: 那么可以通过执行消除文法左递 归的算法消除文法的一切左递归 归的算法消除文法的一切左递归 改写后的文法可能含有以ε (改写后的文法可能含有以ε 为右部的产生式)。 为右部的产生式)。
理学自上而下的语法分析
b∈follow(A),把A→α加至M[A][b]。 ⑤把所有未定义的M[A][c]标上“出错标志”
(c∈VT)。
4.7 预测分析法
㈢预测分析控制程序实现 ①数据结构 M:二维数组,存放预测分析表。 stack:符号栈,初始时为“#S”(S为开
中ε∈afi∈rsVt(XT ,)。则 a∈first(X) ; 若 X→ε , 则 ③观察每个产生式,若有X→Y……,其
中为fYir∈st(VYN),/ε)则加将到fifrisrts(tY(X)中)中的。非ε元素(记
4.5 first集和follow集
考虑更一般情况, Y2、…Yi-1∈VN。
始符号)。 X:表示栈顶符号 t.code:当前处理单词种别
4.7 预测分析法
②算法描述 预测分析控制程序任何时刻的动作,都
按照栈顶符号X和当前输入符号t.code行 事,控制程序每次执行下述三种可能的 动作之一(暂不考虑出错情况)。 l 若X 和 t.code 均为 '#',则分析成功, 输入串为合法句子,终止分析过程。
4.1 带回溯的自上而下分析法概 述
从文法的开始符号出发进行推导,最终 推出确定的输入串(由单词种别构成的 源程序)。
4.1 带回溯的自上而下分析法概 述
从根结点出发,试图用一切可能的办法, 自上而下地为输入串建立一棵语法树。 或者说,为输入串寻找一个最左推导。
4.1 带回溯的自上而下分析法概 述
A→α1|α2|…|αn|ε 设当前输入符号为a,根据下述原则
if (a∈first(αi))
用A→αi推导(1≤i≤n)
else if (a∈follow(A))
自上而下分析
实验二语法分析(自上而下分析)一、实习目的熟悉并设计一个表达式的语法分析器二、实验内容1.设计表达式的语法分析器算法2.编写代码并上机调试运行通过要求:输入表达式输出——表达式语法是否正确三、实验要求运用算术表达式的LL(1)分析算法来设计的表达式的语法分析器。
LL(1)文法是一个自上而下的语法分析方法,它是从文法的识别符号出发,生成句子的最左推导,从左到右扫描源程序,每次向前查看一个字符,便能确定当前应该选择的产生式。
LL(1)分析需要用一个分析表和一个符号栈。
分析表是一个矩阵,它的元素可以存放一个非终结符的产生式,表明当符号栈的栈顶元素非终结符遇到当前输入字符时,所应选择的产生式;分析表的元素还可以是存放一个出错标志,说明符号栈S 的栈顶元素非终结符不应该遇到当前输入符(终结符)。
重复调用LL(1)分析方法对每一个输入字符进行分析,直到输入栈为空为止。
四、运行结果注:此程序只能对由‘i’,’+’,’*’,’<’,’>’构成的以‘#’结速的字符串进行分析。
五、源程序(此程序只能在VC++中运行)#include<stdio.h>#include<stdlib.h>#include<string.h>#include<dos.h>char A[20];/*分析栈*/char B[20];/*剩余串*/char v1[20]={'i','+','*','(',')','#'};/*终结符*/ char v2[20]={'E','G','T','S','F'};/*非终结符*/int j=0,b=0,top=0,l;/*L为输入串长度*/typedef struct type/*产生式类型定义*/ {char origin;/*大写字符*/char array[5];/*产生式右边字符*/int length;/*字符个数*/}type;type e,t,g,g1,s,s1,f,f1;/*结构体变量*/type C[10][10];/*预测分析表*/void print()/*输出分析栈*/{int a;/*指针*/for(a=0;a<=top+1;a++)printf("%c",A[a]);printf("\t\t");}/*print*/void print1()/*输出剩余串*/{int j;for(j=0;j<b;j++)/*输出对齐符*/ printf(" ");for(j=b;j<=l;j++)printf("%c",B[j]);printf("\t\t\t");}/*print1*/void main(){int m,n,k=0,flag=0,finish=0;char ch,x;type cha;/*用来接受C[m][n]*//*把文法产生式赋值结构体*/ e.origin='E';strcpy(e.array,"TG");e.length=2;t.origin='T';strcpy(t.array,"FS");t.length=2;g.origin='G';strcpy(g.array,"+TG");g.length=3;g1.origin='G';g1.array[0]='^';g1.length=1;s.origin='S';strcpy(s.array,"*FS");s.length=3;s1.origin='S';s1.array[0]='^';s1.length=1;f.origin='F';strcpy(f.array,"(E)");f.length=3;f1.origin='F';f1.array[0]='i';f1.length=1;for(m=0;m<=4;m++)/*初始化分析表*/for(n=0;n<=5;n++)C[m][n].origin='N';/*全部赋为空*//*填充分析表*/C[0][0]=e;C[0][3]=e;C[1][1]=g;C[1][4]=g1;C[1][5]=g1;C[2][0]=t;C[2][3]=t;C[3][1]=s1;C[3][2]=s;C[3][4]=C[3][5]=s1;C[4][0]=f1;C[4][3]=f;printf("提示:本程序只能对由'i','+','*','(',')'构成的以'#'结束的字符串进行分析,\n");printf("请输入要分析的字符串:");do/*读入分析串*/{scanf("%c",&ch);if ((ch!='i') &&(ch!='+') &&(ch!='*')&&(ch!='(')&&(ch!=')')&&(ch!='#')){printf("输入串中有非法字符\n");exit(1);}B[j]=ch;j++;}while(ch!='#');l=j;/*分析串长度*/ch=B[0];/*当前分析字符*/A[top]='#'; A[++top]='E';/*'#','E'进栈*/printf("步骤\t\t分析栈\t\t剩余字符\t\t所用产生式\n"); do{x=A[top--];/*x为当前栈顶字符*/printf("%d",k++);printf("\t\t");for(j=0;j<=5;j++)/*判断是否为终结符*/if(x==v1[j]){flag=1;break;}if(flag==1)/*如果是终结符*/{if(x=='#'){finish=1;/*结束标记*/printf("acc!\n");/*接受*/getchar();getchar();exit(1);}/*if*/if(x==ch){print();print1();printf("%c匹配\n",ch);ch=B[++b];/*下一个输入字符*/flag=0;/*恢复标记*/}/*if*/else/*出错处理*/{print();print1();printf("%c出错\n",ch);/*输出出错终结符*/exit(1);}/*else*/}/*if*/else/*非终结符处理*/{for(j=0;j<=4;j++)if(x==v2[j]){m=j;/*行号*/break;}for(j=0;j<=5;j++)if(ch==v1[j]){n=j;/*列号*/break;}cha=C[m][n];if(cha.origin!='N')/*判断是否为空*/ {print();print1();printf("%c->",cha.origin);/*输出产生式*/for(j=0;j<cha.length;j++)printf("%c",cha.array[j]);printf("\n");for(j=(cha.length-1);j>=0;j--)/*产生式逆序入栈*/A[++top]=cha.array[j];if(A[top]=='^')/*为空则不进栈*/top--;}/*if*/else/*出错处理*/{print();print1();printf("%c出错\n",x);/*输出出错非终结符*/exit(1);}/*else*/}/*else*/}while(finish==0);}/*main*/。
第四章 语法分析——自上而下分析
解二: 规定顺序:S、Q、R
则等价的无左递归的文法: SQc | c QRb| b RbcaR’ | caR’ | aR’ R’bcaR’ |
RSa | a RQca | ca | a
RRbca|bca | ca | a
RbcaR’|caR’ | aR’ R’ bcaR’|
(因为不需要试探某个候选式,而是准确地指派 某个候选式)
17
终结首符集FIRST
令文法G不含左递归,对它的所有非终结符的每 个候选式定义终结首符集 FIRST(): * FIRST()={a | a , a∈VT }
特别地 * 若 ,则规定 ∈ FIRST()
显然, FIRST()是从推导出的所有可能的开头终 结符a或 。
3
§4.2 自上而下分析面临的问题
一、带‚回溯‛的自上而下分析方法:
自上而下分析方法,就是对任何输入串,试 图用一切可能的方法,从文法的开始符号出发, 自上而下地为输入串建立一个语法树(或最左推 导)。 这种分析过程实质上是一种试探过程,即反 复使用不同的产生式以求能匹配输入串。
4
例4.1 设有文法: SxAy
解: S iCtSA | a
A | eS
C b
22
4.3.3 LL(1)分析条件 当一个文法不含左递归,并且满足每个非终结 符的所有候选首符集两两不相交,是不是一定能进 行有效的自上而下的语法分析呢?
若存在 ∈ FIRST() ,则问题较复杂,需要进 一步考虑。 定义:非终结符A的 FOLLOW 集:
* FOLLOW(A)= { a| S …Aa… ,a∈VT } 特别地, * 若S …A,则规定,构造FIRST(X)
a) 若X∈VT,则 FIRST(X)={X}。
第五章自上而下语法分析
第五章⾃上⽽下语法分析第五章⾃上⽽下语法分析1、教学⽬的及要求:本章介绍编译程序的第⼆个阶段语法分析的设计⽅法和实现原理,包括⾃上⽽下分析的⽆回朔的递归下降分析、 LL(1)分析法。
要求理解递归下降分析、LL(1)⽂法的基本概念;掌握⽆回朔的递归下降分析的设计和实现、LL(1)分析表的构造与分析⽅法。
◇能够对⼀个给定的⽂法判断是否是LL(1)⽂法;◇能构造预测分析表;◇能⽤预测分析⽅法判断给定的输⼊符号串是否是该⽂法的句⼦;◇能对某些⾮LL(1)⽂法做等价变换:①消除左递归②提取左公共因⼦可能会变成LL(1)⽂法。
这样可扩⼤⾃顶向下分析⽅法的应⽤。
2、教学内容:语法分析器的功能,⾃上⽽下语法分析(递归下降分析法,预测分析程序),LL(1)分析法,递归下降分析程序构造,预测分析程序。
3、教学重点:递归下降⼦程序,预测分析表构造,LL(1)⽂法。
4、教学难点:对⼀个⽂法如何判断是否是LL(1)⽂法,由于在判断 LL(1)⽂法时⽤到⽂法符号串的开始符号集合(FIRST集)和⾮终结符后跟符号集合(FOLLOW集)的计算,⽽⼀般学⽣往往因概念不清或不够细⼼对这两个集合的计算常常出错,导致判断和分析结果的错误。
5、课前思考为了了解⾃顶向下(⾃上⽽下)分析的⼀般过程和问题,请学⽣⾸先回顾本章之前介绍的有关基本概念:◇句⼦、句型和语⾔的定义是什么?◇什么叫最左推导?◇什么叫最右推导和规范推导?◇什么叫确定的⾃顶向下语法分析?◇⾃顶向下语法分析是从⽂法的开始符号出发,反复使⽤各种产⽣式,寻找与输⼊符号匹配的推导。
◇确定的⾃顶向下语法分析中⽤的是哪种推导?◇在确定的⾃顶向下语法分析过程中,当以同⼀个⾮终结符为左部的产⽣式有多个不同右部时,如何选择⽤哪个产⽣式的右部替换当前的⾮终结符?◇确定的⾃顶向下语法分析对⽂法有何限制?6、章节内容第⼀节概述第⼆节 LL(1)分析⽅法第三节递归下降分析法5.1 概述LL分析法确定的⾃上⽽下分析⾃上⽽下分析递归下降分析法语法分析不确定的⾃上⽽下分析——即带回溯的分析⽅法算符优先分析⾃下⽽上分析LR分析⼀、带回溯的⾃顶向下分析⽅法是⾃顶向下分析的⼀般⽅法,即对任⼀输⼊符号串,试图⽤⼀切可能的办法,从树根结点(识别符号)出发,根据⽂法⾃上⽽下地为输⼊串建⽴⼀棵语法树,或者说,从识别符号开始,根据⽂法为输⼊串建⽴⼀个推导序列,这种分析过程本质上是⼀种试探过程,是反复使⽤不同规则谋求匹配输⼊串的过程。
语法分析—自上而下分析
9
§4.2 自上而下面临的问题
例:文法 SxAy A**|*
输入串α :x*y
S x Ay
S x Ay **
S x Ay
*
10
注:
• 回溯法也称试探法,它的基本思想是:从问题的 某一种状态(初始状态)出发,搜索从这种状态 出发所能达到的所有“状态”,当一条路走到“ 尽头”的时候(不能再前进),再后退一步或若 干步,从另一种可能“状态”出发,继续搜索, 直到所有的“路径”(状态)都试探过。这种不 断“前进”、不断“回溯”寻找解的方法,就称 作“回溯法”。
三、分析条件
1.当一个文法不含左递归,并且满足每个非终结符的 所有候选首符集两两不相交的条件,是不是就一定能 进行有效的自上而下分析了呢?
28
§4.3 LL(1)分析法
例:文法
EE+T|T TT*F|F F(E)|i
E T E’
经消去直接左递归后变成 ETE’ E’+TE’|ℇ TFT’ T’*FT’|ℇ F(E) |i
FIRST(A)
2.A→ε
3.A→X1 X2......XK
*
(1) * a..X2....... FIRST(X1)/{ε}
(2) * ε X2.......
FIRST(X2)/{ε}
ε
(3) * εε......ε
36
FIRST集
1.First(X)集合构造,X∈VT∪VN
例:求下题的FIRST集
25
§4.3 LL(1)分析法
3.提取公共左因子 A.事实上,许多文法均存在这样的非终结符,
其所有候选的终结首符集并非两两不相交。
编译原理-自上而下的语法分析
高效性
由于从文法的最顶端开始分析, 一旦发现不匹配,就可以立即终 止当前分支的搜索,避免不必要 的计算,提高了编译器的效率。
易于处理左递归文
法
自上而下的分析方法可以很方便 地处理含有左递归的文法,而左 递归是许多实际编程语言的重要 特征。
局限性
无法处理左边界问题
自上而下的分析方法在处理某些含有左边界的文法时可能 会遇到问题,因为这种方法会优先匹配最左边的符号,而 左边界问题需要从右往左匹配符号。
案例三
在编译器优化中,自上而下的语法分析被用 于识别和修改源代码中的冗余和低效的语法 成分。例如,在C编译器的实现中,自上而 下的语法分析可以用于优化循环结构,减少 不必要的循环次数,提高程序的执行效率。
自上而下的语法分析还可以用于代码生成和 代码生成器的实现。通过识别和解析源代码 中的语法成分,可以生成更高效、更安全的 机器代码或字节码,提高程序的执行效率和
安全性。
THANKS
感谢观看
详细描述:递归下降分析算法易于理解,每个产生式规 则对应一个函数,函数的实现相对简单明了。
详细描述:对于每个产生式规则,需要编写相应的递归 函数,可能会导致代码冗余。
移入-规约分析算法
总结词
基于栈的算法
详细描述
移入-规约分析算法是一种自上而下的语法分 析算法,它将目标语句从左到右依次读入, 并根据文法的产生式规则进行移入或规约操 作,直到找到目标语句的语法结构。
词法分析
词法分析是编译过程的第一步,也称为扫描或词法扫描。它的任务是从左 到右读取源代码,将其分解成一个个的记号或符号。
词法分析器通常使用正则表达式或有限自动机来识别和生成记号,这些记 号可以是关键字、标识符、运算符、标点符号等。
语法分析最常用的两类方法
LL分析法和LR分析法。
1、自上而下语法分析方法(LL分析法)
给定文法G和源程序串r。
从G的开始符号S出发,通过反复使用产生式对句型中的非终结符进行替换(推导),逐步推导出r 。
是一种产生的方法,面向目标的方法。
分析的主旨为选择产生式的合适的侯选式进行推导,逐步使推导结果与r匹配。
2、自下而上语法分析方法(LR分析法)
从给定的输入串r开始,不断寻找子串与文法G中某个产生式P的候选式进行匹配,并用P的左部代替(归约)之,逐步归约到开始符号S。
是一种辨认的方法,基于目标的方法。
分析的主旨为寻找合适的子串与P的侯选式进行匹配,直到归约到G的S为止。
扩展资料
LALR分析器可以对上下无关文法进行语法分析。
LALR即“Look-AheadLR”。
其中,Look-Ahead为“向前看”,L代表对输入进行从左到右的检查,R代表反向构造出最右推导序列。
LALR分析器可以根据一种程序设计语言的正式语法的产生式而对一段文本程序输入进行语法分析,从而在语法层面上判断输入程序是否合法。
实际应用中的LALR分析器并不是由人手工写成的,而是由类似于yacc和GNU Bison之类的LALR语法分析器生成工具构成。
由机器自动生成的代码相比较于程序员手工的代码,拥有更好的运行效率而且减少了程序员的工作量。
编译原理实验三-自下而上语法分析报告及语义分析报告.docx
上海电力学院编译原理课程实验报告实验名称:实验三自下而上语法分析及语义分析院系:计算机科学与技术学院专业年级:学生姓名:学号:指导老师:实验日期:实验三自上而下的语法分析一、实验目的:通过本实验掌握LR分析器的构造过程,并根据语法制导翻译,掌握属性文法的自下而上计算的过程。
二、实验学时:4学时。
三、实验内容根据给出的简单表达式的语法构成规则(见五),编制LR分析程序,要求能对用给定的语法规则书写的源程序进行语法分析和语义分析。
对于正确的表达式,给出表达式的值。
对于错误的表达式,给出出错位置。
四、实验方法采用LR分析法。
首先给出S-属性文法的定义(为简便起见,每个文法符号只设置一个综合属性,即该文法符号所代表的表达式的值。
属性文法的定义可参照书137页表6.1),并将其改造成用LR分析实现时的语义分析动作(可参照书145页表6.5)。
接下来给出LR分析表。
然后程序的具体实现:●LR分析表可用二维数组(或其他)实现。
●添加一个val栈作为语义分析实现的工具。
●编写总控程序,实现语法分析和语义分析的过程。
注:对于整数的识别可以借助实验1。
五、文法定义简单的表达式文法如下:(1)E->E+T(2)E->E-T(3)E->T(4)T->T*F(5)T->T/F(6)T->F(7)F->(E)(8)F->i五、处理程序例和处理结果例示例1:20133191*(20133191+3191)+ 3191#六、源代码【cifa.h】//cifa.h#include<string> using namespace std;//单词结构定义struct WordType{int code;string pro;};//函数声明WordType get_w();void getch();void getBC();bool isLetter();bool isDigit();void retract();int Reserve(string str); string concat(string str); 【Table.action.h】//table_action.hclass Table_action{int row_num,line_num;int lineName[8];string tableData[16][8]; public:Table_action(){row_num=16;line_num=8;lineName[0]=30;lineName[1]=7;lineName[2]=13;lineName[3]=8;lineName[4]=14;lineName[5]=1;lineName[6]=2;lineName[7]=15;lineName[8]=0;for(int m=0;m<row_num;m++) for(int n=0;n<line_num;n++) tableData[m][n]="";tableData[0][0]="S5";tableData[0][5]="S4";tableData[1][1]="S6";tableData[1][2]="S12";tableData[1][7]="acc";tableData[2][1]="R3";tableData[2][2]="R3";tableData[2][3]="S7";tableData[2][6]="R3"; tableData[2][7]="R3"; tableData[3][1]="R6"; tableData[3][2]="R6"; tableData[3][3]="R6"; tableData[3][4]="R6"; tableData[3][6]="R6"; tableData[3][7]="R6"; tableData[4][0]="S5"; tableData[4][5]="S4"; tableData[5][1]="R8"; tableData[5][2]="R8"; tableData[5][3]="R8"; tableData[5][4]="R8"; tableData[5][6]="R8"; tableData[5][7]="R8"; tableData[6][0]="S5"; tableData[6][5]="S4"; tableData[7][0]="S5"; tableData[7][5]="S4"; tableData[8][1]="S6";tableData[8][6]="S11"; tableData[9][1]="R1"; tableData[9][2]="R1"; tableData[9][3]="S7"; tableData[9][4]="S13"; tableData[9][6]="R1"; tableData[9][7]="R1"; tableData[10][1]="R4"; tableData[10][2]="R4"; tableData[10][3]="R4"; tableData[10][4]="R4"; tableData[10][6]="R4"; tableData[10][7]="R4"; tableData[11][1]="R7"; tableData[11][2]="R7"; tableData[11][3]="R7"; tableData[11][4]="R7"; tableData[11][6]="R7"; tableData[11][7]="R7"; tableData[12][0]="S5"; tableData[12][5]="S4";tableData[13][5]="S4";tableData[14][1]="R2";tableData[14][2]="R2";tableData[14][3]="S7";tableData[14][4]="S13";tableData[14][6]="R2";tableData[14][7]="R2";tableData[15][1]="R5";tableData[15][2]="R5";tableData[15][3]="R5";tableData[15][4]="R5";tableData[15][5]="R5";tableData[15][6]="R5";tableData[15][7]="R5";}string getCell(int rowN,int lineN){int row=rowN;int line=getLineNumber(lineN);if(row>=0&&row<row_num&&line>=0&&line<=line_num) return tableData[row][line];elsereturn"";}int getLineNumber(int lineN){for(int i=0;i<line_num;i++)if(lineName[i]==lineN)return i;return -1;}};【Table_go.h】//table_go.hclass Table_go{int row_num,line_num;//行数、列数string lineName[3];int tableData[16][3];public:Table_go(){row_num=16;line_num=3;lineName[0]="E";lineName[1]="T";lineName[2]="F";for(int m=0;m<row_num;m++) for(int n=0;n<line_num;n++)tableData[m][n]=0;tableData[0][0]=1;tableData[0][1]=2;tableData[0][2]=3;tableData[4][0]=8;tableData[4][1]=2;tableData[4][2]=3;tableData[6][1]=9;tableData[6][2]=3;tableData[7][2]=10;tableData[12][1]=14;tableData[12][2]=3;tableData[13][2]=15;}int getCell(int rowN,string lineNa){int row=rowN;int line=getLineNumber(lineNa);if(row>=0&&row<row_num&&line<=line_num) return tableData[row][line];elsereturn -1;}int getLineNumber(string lineNa){for(int i=0;i<line_num;i++)if(lineName[i]==lineNa)return i;return -1;}};【Stack_num.h】class Stack_num{int i; //栈顶标记int *data; //栈结构public:Stack_num() //构造函数{data=new int[100];i=-1;}int push(int m) //进栈操作{i++;data[i]=m;return i;}int pop() //出栈操作{i--;return data[i+1];}int getTop() //返回栈顶{return data[i];}~Stack_num() //析构函数{delete []data;}int topNumber(){return i;}void outStack(){for(int m=0;m<=i;m++)cout<<data[m];}};【Stack_str.h】class Stack_str{int i; //栈顶标记string *data; //栈结构public:Stack_str() //构造函数{data=new string[50];i=-1;}int push(string m) //进栈操作{i++;data[i]=m;return i;}int pop() //出栈操作{data[i]="";i--;return i;}string getTop() //返回栈顶{return data[i];}~Stack_str() //析构函数{delete []data;int topNumber(){return i;}void outStack(){for(int m=0;m<=i;m++)cout<<data[m];}};【cifa.cpp】//cifa.cpp#include<iostream>#include<string>#include"cifa.h"using namespace std;//关键字表和对应的编码stringcodestring[10]={"main","int","if","then","else","return","void","cout","endlint codebook[10]={26,21,22,23,24,25,27,28,29};//全局变量char ch;int flag=0;/*//主函数int main(){WordType word;cout<<"请输入源程序序列:";word=get_w();while(word.pro!="#")//#为自己设置的结束标志{cout<<"("<<word.code<<","<<"“"<<word.pro<<"”"<<")"<<endl;word=get_w();};return 0;}*/WordType get_w(){string str="";int code;WordType wordtmp;getch();//读一个字符getBC();//去掉空白符if(isLetter()){ //以字母开头while(isLetter()||isDigit()){str=concat(str);getch();}retract();code=Reserve(str);if(code==-1){wordtmp.code=0;wordtmp.pro=str;}//不是关键字else{wordtmp.code=code;wordtmp.pro=str;}//是关键字}else if(isDigit()){ //以数字开头while(isDigit()){str=concat(str);getch();}retract();wordtmp.code=30;wordtmp.pro=str;}else if(ch=='(') {wordtmp.code=1;wordtmp.pro="(";} else if(ch==')') {wordtmp.code=2;wordtmp.pro=")";} else if(ch=='{') {wordtmp.code=3;wordtmp.pro="{";} else if(ch=='}') {wordtmp.code=4;wordtmp.pro="}";} else if(ch==';') {wordtmp.code=5;wordtmp.pro=";";} else if(ch=='=') {wordtmp.code=6;wordtmp.pro="=";} else if(ch=='+') {wordtmp.code=7;wordtmp.pro="+";} else if(ch=='*') {wordtmp.code=8;wordtmp.pro="*";} else if(ch=='>') {wordtmp.code=9;wordtmp.pro=">";} else if(ch=='<') {wordtmp.code=10;wordtmp.pro="<";} else if(ch==',') {wordtmp.code=11;wordtmp.pro=",";} else if(ch=='\'') {wordtmp.code=12;wordtmp.pro="\'";} else if(ch=='-') {wordtmp.code=13;wordtmp.pro="-";} else if(ch=='/') {wordtmp.code=14;wordtmp.pro="/";} else if(ch=='#') {wordtmp.code=15;wordtmp.pro="#";} else if(ch=='|') {wordtmp.code=16;wordtmp.pro="|";}else {wordtmp.code=100;wordtmp.pro=ch;}return wordtmp;}void getch(){if(flag==0) //没有回退的字符ch=getchar();else //有回退字符,用回退字符,并设置标志flag=0;}void getBC(){while(ch==' '||ch=='\t'||ch=='\n')ch=getchar();}bool isLetter(){if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z')return true;elsereturn false;}bool isDigit(){if(ch>='0'&&ch<='9')return true;elsereturn false;}string concat(string str){return str+ch;}void retract(){flag=1;}int Reserve(string str){int i;for(i=0;i<=8;i++){if(codestring[i]==str) //是某个关键字,返回对应的编码return codebook[i];}if(i==9) //不是关键字return -1;}【LR.cpp】#include<iostream>#include<string>#include<cstdlib>#include"cifa.h"#include"stack_num.h"#include"stack_str.h"#include"table_action.h"#include"table_go.h"using namespace std;void process(){int stepNum=1;int topStat;Stack_num statusSTK; //状态栈Stack_str symbolSTK; //符号栈Stack_num valueSTK; //值栈WordType word;Table_action actionTAB; //行为表Table_go goTAB; //转向表cout<<"请输入源程序,以#结束:";word=get_w();//总控程序初始化操作symbolSTK.push("#");statusSTK.push(0);valueSTK.push(0);cout<<"步骤\t状态栈\t符号栈\t值栈\t当前词\t动作\t转向"<<endl;//分析while(1){topStat=statusSTK.getTop(); //当前状态栈顶string act=actionTAB.getCell(topStat,word.code);//根据状态栈顶和当前单词查到的动作//输出cout<<stepNum++<<"\t";statusSTK.outStack(); cout<<"\t";symbolSTK.outStack(); cout<<"\t";valueSTK.outStack(); cout<<"\t";cout<<word.pro<<"\t";//行为为“acc”,且当前处理的单词为#,且状态栈里就两个状态//说明正常分析结束if(act=="acc"&&word.pro=="#"&&statusSTK.topNumber()==1){cout<<act<<endl;cout<<"分析成功!"<<endl;cout<<"结果为:"<<valueSTK.getTop()<<endl;return;}//读到act表里标记为错误的单元格else if(act==""){cout<<endl<<"不是文法的句子!"<<endl;cout<<"错误的位置为单词"<<word.pro<<"附近。
my第04章-语法分析:自上而下
自上而下分析法的一般问题
3.
带回溯的自上而下分析法的缺陷 1)如果文法存在左递归,语法分析会无限循环下去。 2)若产生式存在多个候选式,选择哪个进行推导完全 是盲目的。 3)回溯会引起时间和空间的打量消耗 4)如果被识别的语句是错的,算法无法指出错误的确 切位置。
第四章 语法分析--自上而下分析
直接左递归,和非直接左递归的消除方法均在必须掌握之列。这里我们 切不可被形式描述消除左递归的算法吓倒,多做几个例题后再来理解是很 有好处的: [例4.3]: 考虑文法:SQc|c Q Rb|b R Sa|a 消除左递归。 解:将终结符排序为R、Q、S。对于R不存在直接左递归。把R带入到Q 中有关的候选式: Q Sab|ab|b 现在Q同样不含直接左递归,把它带入S的有关候选式: S Sabc|abc|bc|c 经消除S的直接左递归后我们们得到整个文法 S abcS’|bcS’|cS’ S’ abcS’| Q Sab|ab|b R Sa|a 由于关于Q,R的规则式多余的则可化简
本节要 掌握对给定文法构造出每个非终结符的 FIRST和FOLLOW集合。
第四章 语法分析--自上而下分析
掌握LL(1)预测分析表的构造,请参看4。5。1 预测分析程序的 工作过程(P76)和 4。5。2预测分析表的构造(P78)。 现在举一些例子帮助同学们理解: [例4.7 ]对于文法 ETE’ E’ +TE’| T FT’ T’ *FT’| F (E)| i 我们构造每个非终结符的FIRST和FOLLOW集合 解:FIRST(E) = { (, i } FOLLOW(E) ={ ), # } FIRST(E’) = {+, } FOLLOW(E’) = { ), #} FIRST(T) = {(, i } FOLLOW(T) = {+, ), # } FIRST(T’) = {*, } FOLLOW(T’) ={+ , ), # } FIRST(F) = {(, i } FOLLOW(F) ={*, +, ) , # } 在这里我们要注意FOLLOW(F)的求解过程其中: FOLLOW(F)=FIRST(T’)={*}; 因为T’ ,所以将FOLLOE(T)加 到FOLLOW(F)中 (由于TFT’), 则: FOLLOW(F)=FOLLOW(T)=FIRST(E‘)={+}
自上而下语法分析
从语法树的角度看,从根节点出发,反复使用 所有可能的产生式,谋求输入串的匹配,试图 向下构造一棵语法树,其末端节点正好与输入 符号串相同。
需要反复试探。
复习
句型的推导
最左(最右)推导:在推导的任何一步α β , 其中α 、β 是句型,都是对α 中的最左(右)非 终结符进行替换 最右推导被称为规范推导。 由规范推导所得的句型称为规范句型。
例1:判定输入串(i+i)*i是否是下述文法的句子?
G = ({E}, {i, +, *, (, ) } , P , E) P: E E + E E E*E E (E) Ei
解:使用最左推导:
E E*E (E)*E (E + E)*E (i + E)*E
(i + i)*E (i + i)*i
高级语言的语法结构适合用上下文无关文法 来描述,上下文无关文法是语法分析的基础。
语法分析在编译系统中所处的位置
语法分析的接口设计
源程序 词法分析器 token串
语法分析器 语法分析树
编译程序 后续部分
• 语法分析器的输入
– Token序列:词法分析的输出,是各个单词都正 确的源程序的变换形式,是一个有限序列
• 语法分析器的输出
– 分析树:表示方法? 见教材 P89 – 错误处理信息:定位、继续编译
语法分析器的功能
按照语言的语法构成规则, 识别输入的符号 串能否构成一个句子。这些规则是用文法的 产生式来定义的。
问题
对给定的一个输入串,如何判定它是不是一 个句子?
方法
编译原理第四章语法分析-自上而下分析
• 例 4.4
4.4 递归下降分析程序构造
• 递归下降分析器:
这个分析程序由一组递归过程组成的,每个过程对应 文法的一个非终结符。 E→TE’ E’→+TE’| T→FT’ T’→*FT’| F→(E)|i
PROCEDURE E BEGIN T ; E’ END PROCEDURE E’ IF SYM=‘+’THEN BEGIN ADVANCE ; T ; E’ END
4.2 自上而下分析面临的问题
• 例4.1 假定有文法
(1) SxAy (2)A**|*
对输入串x*y,构造语法树。 • 构造过程:
(1)把S作为根 (2)用S的产生式构造子树 (3)让输入串指示器IP指向输入串的第一个符号。
S x A y x
S
A y x
S
A y
*
*
*
(4)调整输入串指示器IP与叶结点进行匹配。 (5)如果为非终结符,用A的下一个产生式构建子树。 (6)如果匹配成功则结束;否则,回溯到步骤(4)。
• 一个反例:
– 文法:SQc|c;QRb|b;RSa|a虽然不是直接 左递归,但S、Q、R都是左递归。
• 消除左递归算法:
– 算法的思想是:
• • • • 首先构造直接左递归; 再利用一般转换规则,消除直接左递归 化简文法。 下面算法在不含PP,也不含在右部产生式时可以消除 左递归。
• 消除一个文法的左递归算法:
(1) 把文法 G 的所有非终结符按任一种顺利排列成 P1…Pn;按此顺序执行; (2) FOR i:=1 TO n DO
BEGIN FOR j:=1 TO i-1 DO 把形如Pj+1→Pj 的规则改写成 Pj+11|1|…k| 。其中 Pj1|1|…k 是关于 Pj 的 所有规则; 消除关于Pi规则的直接左递归性。 END 化简由(2)所得的文法。即去除那些从开始符号出发永 远无法到达的非终结符的产生规则。
语法分析--自上而下分析的基本问题
语法分析--⾃上⽽下分析的基本问题语法分析基本概念语法分析的前提:对语⾔的语法结构进⾏描述,采⽤正规式和有限⾃动机描述和识别语⾔的单词符号,⽤上下⽂⽆关⽂法来描述语法规则语法分析的任务:分析⼀个⽂法的句⼦的结构语法分析器的功能:按照⽂法的产⽣式(语⾔的语法规则),识别输⼊符号串是否为⼀个句⼦(合式程序)⾃下⽽上(Bottom-up):从输⼊串开始,逐步进⾏归约,直到⽂法的开始符号,归约:根据⽂法的产⽣式规则,把串中出现的产⽣式的右部替换成左部符号,从树叶节点开始,构造语法树,算符优先分析法、LR分析法⾃上⽽下(Top-down):从⽂法的开始符号出发,反复使⽤各种产⽣式,寻找"匹配"的推导,推导:根据⽂法的产⽣式规则,把串中出现的产⽣式的左部符号替换成右部,从树的根开始,构造语法树,递归下降分析法、预测分析程序⾃上⽽下分析⾯临的问题基本思想:从⽂法的开始符号出发,向下推导,推出句⼦,针对输⼊串,试图⽤⼀切可能的办法,从⽂法开始符号(根结点)出发,⾃上⽽下地为输⼊串建⽴⼀棵语法树多个产⽣式候选带来的问题,回溯问题:分析过程中,当⼀个⾮终结符⽤某⼀个候选匹配成功时,这种匹配可能是暂时的,出错时,不得不“回溯”⽂法左递归问题:⼀个⽂法是含有左递归的,如果存在⾮终结符P⾯临的问题⽂法左递归问题回溯问题构造不带回溯的⾃上⽽下分析算法消除⽂法的左递归性消除回溯消除⽂法的左递归直接左递归的消除间接左递归的消除消除左递归的算法由于对⾮终结符排序的不同,最后所得的⽂法在形式上可能不⼀样。
但不难证明,它们都是等价的消除回溯为了消除回溯必须保证:对⽂法的任何⾮终结符,当要它去匹配输⼊串时,能够根据它所⾯临的输⼊符号准确地指派它的⼀个候选去执⾏任务,并且此候选的⼯作结果应是确信⽆疑的FIRST和FOLLOW集合的构造FIRST集合令G是⼀个不含左递归的⽂法,对G的所有⾮终结符的每个候选α定义它的终结⾸符集FIRST(α)为:提取公共左因⼦FOLLOW集合L: 从左到右扫描输⼊串 L: 最左推导 1:每⼀步只需向前查看⼀个符号FIRST和FOLLOW集合的构造构造每个⽂法符号的FIRST集合构造FOLLOW(A)构造每个⾮终结符的FOLLOW集合对最后的FIRST、FOLLOW集合有点迷,真的有点迷,晚上不该看这个的!!o(╥﹏╥)o。
第五部分语法分析自下而上分析-
IF Xi为非终结符而Xi+1为终结符 THEN FOR LASTVT(Xi)中的每个a DO
置 a Xi+1
END
国防科技大学计算机系602教研室
例: 考虑下面的文法G(E): (1) E→E+T | T (2) T→T*F | F (3) F→P F | P (4) P→(E) | i
e
dBB
b
cccc
bAAAAAAA
aaaaaaaaaS
国防科技大学计算机系602教研室
S
a A c Be
A
bd
b
分析树和语法树不一定一致。 自下而上分析过程:边输入单词符号,边 归约。 核心问题:识别可归约串
国防科技大学计算机系602教研室
5.1.2 规范归约
定义:令G是一个文法,S是文法的开始符 号,假定是文法G的一个句型,如果有
国防科技大学计算机系602教研室
5.1.1 归约
采用“移进-归约”思想进行自下而上分析 。
基本思想:用一个寄存符号的先进后出栈, 把输入符号一个一个地移进到栈里,当栈顶 形成某个产生式的候选式时,即把栈顶的这 一部分替换成(归约为)该产生式的左部符号 。
国防科技大学计算机系602教研室
步骤: 1 2 3 4 5 6 7 8 9 10 动作: 进a 进b 归(2) 进b 归(3) 进c 进d 归(4) 进e 归(1)
栈STACK,把所有初值为真的数组元素F[P, a]的符号对(P,a)全都放在STACK之中。
国防科技大学计算机系602教研室
运算:
如果栈STACK不空,就将顶项逐出,记此 项为(Q,a)。对于每个形如 P→Q… 的产生式,若F[P,a]为假,则变其值为真 且将(P,a)推进STACK栈。
Ch4语法分析---自上而下分析
语法分析依据的是语言的语法规则,即描述程 序结构的规则,通过语法分析确定整个输入串 是否构成一个语法上正确的程序。
语法规则通常用上下文无关文法描述。
语法分析方法有自上而下和自下而上两类。
本章和下一章将介绍编译程序构造中的一些典 型的语法分析方法。
2021/4/6
3
典型的语法分析方法
自上而下语法分析方法 第四章介绍
12
文法的左递归问题
文法的左递归性
直接左递归:文法存在产生式 P → Pα
间接左递归:存在推导 P + Pα
文法具有左递归性,采用自上而下方法分析,
可能会陷入无限循环,分析不下去。 S
例如:文法有左递归产生式 A→Ax
…
分析中会遇到试图展开A,却又立即
Ax
遇 到 A, 并 将 永 远 循 环 下 去 。 在 没 有
Ax
识别任何输入符号的情况下又得重新 要求A去进行新的匹配---消左递归!
2021/4/6
Ax
…
13
候选式的确定与回溯问题
自上而下分析是一种反复用可 能的候选式去进行试探的过程, 不能预知本次试探是否会成功, 若不成功则需要回溯。
S xAy **
例如文法: S→xAy A→**|* 不成功,回溯
9
自上而下分析(2)
序号 ip指向 语法树 最左推导 说明
④ x*y ⑤ x*y
⑥ x*y
2021/4/6
S xAy **
S xAy **
S x Ay
SxAy x**y
SxAy
试用A→**展开A
*得匹配, 移动ip 但y得不到匹配 用A→**展开失败 回溯: 回到第③步
10
编译原理-自下而上的语法分析
自上而下的语法分析
特点
从高层次的文法规则开始,通过不断展开和推导,直到生成目标字符串。
优点
易于理解和实现,可以生成详细的错误报告。
自下而上的语法分析
1
自底向上的语法分析方法概述
通过以输入的标记为起点,逐步推导文法规则,直到生成目标字符串。
2
LR语法分析
一种常用的自底向上的语法分析方法,通过构建一个LR分析表进行推导。
3
LALR语法分析
是LR语法分析的一种变体,通过合并相同状态来降低分析表的复杂度。
自下而上的语法分析的优点和局限性
优点
适用于大型文法,能够处理更广泛的语言结构。
局限性
分析过程复杂,容易产生冲突,需要较大的存储空 间。
自下而上的语法分析的实现
词法分分析器的生成
根据文法规则,构建分析表或语法分析器的数据结构。
语法制导翻译的实现
在语法分析过程中,将源代码转换为目标代码。
自下而上的语法分析的应用
1
编译器中的语法分析
语法分析是编译器中的重要组成部分,用于将源代码转换为中间代码或目标代码。
2
解析器生成器
自下而上的语法分析技术被广泛应用于解析器生成器中,用于自动生成语法分析 器。
结论
自下而上的语法分析是编译原理中重要的一环,虽然实现复杂,但却具有广 泛的应用价值。
编译原理-自下而上的语 法分析
编译原理是研究程序在计算机上的自动翻译过程,语法分析是其中的重要步 骤。自下而上的语法分析是一种常用的语法分析方法。
语法分析的定义和目的
1 定义
语法分析是编译器中的一个阶段,用于验证 和分析程序语法的正确性。
2 目的
语法分析的目的是将源代码转换为语法树, 为后续的编译过程提供基础。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第四章语法分析—自上而下分析知识结构:带回溯分析法回溯自上而下分析面临的问题左递归问题的解决语法分析-求FIRST、FOLLOW集合的算法自上而下分析LL(1)分析法证明LL(1)文法构造LL(1)分析表递归子程序的构造思想递归子程序法递归子程序的特点递归子程序的设计第一节语法分析综述一、语法分析的任务按照语言即定的语法规则,对字符串形式的源程序进行语法检查,并识别出相应的语法成分。
即语法结构是否符合语法规则。
二、语法分析器在编译程序中的地位(一遍扫描)三、语法分析方法通常把语法分析方法分为两大类,既自上而下分析与自下而上分析。
1、自上而下分析方法实际上是一种产生的方法,分析过程是一个推导过程。
⑴自上而下分析过程从文法G的开始符号S出发,通过反复使用产生式,逐步推导出与输入的符号串完全相匹配的句子。
采用最左推导,以文法开始符号为根结点,逐步为输入串自上而下地构造一棵语法树。
面临的输入符号为a,A所有的产生式:A12n①若a FISRT(i),则指派去执行匹配任务。
②若a不属于任何一个候选首字符集,则:a、若属于某个FISRT(i)且a FOLLOW(A),则让A 与自动匹配;b、否则,a的出现是一种错误。
例:设有文法G和输入符号串W:a*a+aG:S aA aA BaAB +-*/推导过程:S aA aBaA a*aA a*aBaA a*a+aA a*a+a=W 构造语法树:Sa AB a A* B a A+⑵自上而下分析法自上而下分析法又可分为确定和不确定的两种。
①不确定的分析法(带回溯)是一种穷举的试探方法,效率低、代价高,极少使用。
②确定的分析法(不带回溯)实现方法简单、直观,便于手工构造或自动生成语法分析器,是目前常用的方法之一。
但是对文法有一定的限制。
2、自下而上分析法⑴自下而上分析过程分析过程是归约过程。
从给定的输入串W开始,不断寻找与文法G中某个产生式P的侯选式(右部)进行匹配,并用P代替也称为归约。
⑵自下而上分析法①算符优先分析法定义算符(广义讲是文法的终结符号)之间的某种优先和结合关系,借助这种关系来寻找并确定可归约字符串,并进行归约。
②LR分析法是一类自左向右对输入串进行扫描的自下而上分析方法,分析过程是规范归约的序列。
适用于语法分析器的自动构造。
第二节自上而下分析面临的问题一、不确定的自上而下分析方法是从文法的开始S出发,试图用一切可能的方法向下推导,产生句子,这种分析过程的本质是一种试探推导过程。
例:文法G⑴S aAd⑵A ab a构造W=aad的最左推导:S aAd aad。
构造语法树:①产生树的根结点,即文法的开始符号。
②选用文法G的文法规则去延伸树。
③判断当前延伸的子结与输入串扫描到的字符是否匹配。
④若不匹配注销掉当前延伸的子树,选用文法规则的另一个产生式延伸分析树。
⑤直到输入串与语法树末端结点相匹配,分析结束。
S S Sa A d a A d a A da b这种试探识别句子的过程,只会使分析的过程不确定。
二、不确定性的原因由于分析过程中选择的侯选式不确定,造成输入串匹配的假象,甚至会导致算法实现的失败。
1、左递归问题由于采用最左推导,左递归将使得输入串的分析过程陷入无限循环之中。
2、回溯问题⑴采用试探的方法,如匹配不成功回溯到前面分析的某一步。
⑵可能出现假匹配,造成对输入串识别的失败。
⑶不能准确报告输入串的出错位置。
三、确定的自上而下分析方法1、确定的自上而下分析方法的必要条件⑴消除文法中的左递归;⑵消除文法中的回溯问题。
2、消除文法的左递归文法的左递归可以通过对文法产生式进行改写,使之不含有左递归的非终结符号。
左递归一般有两种情况,直接左递归和间接左递归。
⑴直接左递归如果文法中任意一个非终结符P,若P P(V N U V T),并且在最左推导中有P P形式,称为直接左递归。
⑵间接左递归在最左推导中有P +=>P形式,称为间接左递归。
①消除直接左递归P→Pα |改写为:P→PP P例:表达式文法E E+T T改写为:E TEE+TET T*F F改写为:T FTT*FTF(E) i②消除文法的左递归一般规则P→P1P2…… P m12……n i≠ε改写为:P→1P2P………n PP→1P2P………m P③消除间接左递归A→B…B→C…间接左递归: A B…C…A…C→A…例:文法GS→Qc|cQ→Rb|bR→Sa|a最左推导:S⇒Qc⇒Rbc⇒Sabc(间接左递归)⑶清除间接左递归非终结符排序为R,Q,S。
R不存在直接左递归,把R代入Q的规则:Q→Sab | ab | b再把Q代入S:S→Sabc | abc | bc | c消除S的左递归:S→abcS| bcS| cSS→abcS| εQ和R的产生式不再被引用,将Q和R删除。
非终结符排序为S,Q,R。
S不存在直接左递归,Q的产生式不包含S,再把S代入得到R:R→Rbca | bca | ca | a消除S的左递归:R→bcaR| caR| aRR→bcaR| ε改写为:S→Qc|c 不能删除Q→Rb|b 不能删除R→bcaR|caR|aRR→bcaR|ε3、消除回溯,提取左因子⑴消除回朔文法G不包含左递归,则G中非终结符号的每个候选式首字符集FIRST()为:FIRST()={a *=>a…,a V T,(V N U V T)*}若*=>,则∈ FIRST()。
⑵提取最左公共因子采用提取最左公共因子的方法改写文法,使得所有侯选式的首字符集两两不相交。
A→12……n12……m提取公因子:A→(12……n) 12……m改写后为:A→A12……mA→12……n4、确定的自上而下分析方法⑴预测分析法(LL(1)分析法)。
⑵递归子程序法。
第三节预测分析法(LL(1)分析法)一、LL(1)分析方法1、是按自左(第一个“L”)向右的顺序扫描输入字符串;2、在分析过程中产生句子的最左(第二个“L”)推导;3、“1”表示在分析过程中,每一步推导,最多只能向前查看(向右扫描)一个字符。
二、LL(K)分析方法如果分析过程的每一步推导,要向前查看K个输入字符,则称为LL(K)分析法。
三、LL(1)文法的定义该文法是上下文无关的一个子集,是自上而下分析技术的一类文法。
四、LL(1)分析法的必要条件1、文法中的非终结符号不包含左递归;2、对于文法中的每一个非终结符A的各个产生式的侯选首字符集两两不相交。
对于产生式A:⑴若ε,证明侯选式,的首字符集是否相交。
FIRST()∩FIRST()= Ф⑵若=ε,证明FIRST(A)和FOLLOW(A)是否相交。
FIRST(A)∩FOLLOW(A)=φ五、构造FIRST集合的算法对每一文法符号X(V N U V T)*1、若X V T ,则FIRST(X)={X}。
2、若X V N ,且有产生式X a,a V T,则a FIRST (X)。
3、若X V N ,X,则FIRST(X)4、若X V N,且Y1,Y2,,Y i V N,而有产生式X Y1,,Y n。
当Y1,Y2,,Y i-1都* =>ε时,(其中1≤i≤n),则FIRST(Y1,)-{},,FIRST(Y i-1)-{},FIRST(Y i)都包含在FIRST(X)中。
若FIRST(Y j)包含把加到FIRST(X)中。
例文法GE →T E`E`→+TE`|εT →F T`T`→*FT`|εF→(E)|iFIRST(E)=FIRST(T)=FIRST(F)={(,i )}FIRST(E)={ +,ε}FIRST(T)={ *,ε}六、构造FOLLOW集合的算法FOLLOW (A)={a | S *=>….Aa…. , a V T }若S *=>…A, 则#FOLLOW (A)FOLLOW (A)为所有句型中紧跟在非终结符A后面的所有终结符集合。
构造算法:1、对文法开始符号S,将“#”置于FOLLOW(S)中。
即FOLLOW(S) = # 。
2、若A →αBβ是一个产生式,则把FIRST(β)-{ε}加至FOLLOW(B)中。
3、若A →αB是一个产生式,或A→αBβ是一个产生式,而β⇒ε(即FIRST());则把FOLLOW(A)加至FOLLOW(B)中。
例文法GE→T E`E`→+TE`|εT →F T`T`→*FT`|εF→(E)|i求FOLLOW(E):因为F→(E),所以FIRST())加入FOLLOW(E)中,FOLLOW(E) = );又因为E→TE`,E是文法的开始符号,则#加至FOLLOW(E)中,所以FOLLOW(E)= # U FOLLOW(E)=#,) 。
求FOLLOW(E`):因为E→TE`, 满足算法(3)若A →αB是一个产生式,则把FOLLOW(A)加至FOLLOW(B)中,所以FOLLOW(E`)= FOLLOW(E)U FOLLOW(E)= #,)求FOLLOW(T):因为E→T E`, 满足算法⑵若A →αBβ是一个产生式,则把FIRST(β)\{ε}加至FOLLOW(B)中,所以FOLLOW(T) = FOLLOW(T) U FIRST(E`)\{ε}= {+}又因为E`⇒ε,满足算法(3)若A→αBβ是一个产生式,而β⇒ε,则把FOLLOW(A)加至FOLLOW(B)中。
所以FOLLOW(T) = FOLLOW(E) U FOLLOW(T)= #,) U+= #,),+求FOLLOW(T`):因为T→F T`,满足算法(3)若A →αB是一个产生式,则把FOLLOW(A)加至FOLLOW(B)中,所以FOLLOW(T) = FOLLOW(T) U FOLLOW(T`)= #,),+求FOLLOW(F):因为T→F T`, 满足算法⑵若A →αBβ是一个产生式,则把FIRST(β)\{ε}加至FOLLOW(B)中,所以FOLLOW(F) = FOLLOW(F) U FIRST(T`)\{ε}= {*}又因为T`⇒ε,满足算法(3)若A→αBβ是一个产生式,而β⇒ε,则把FOLLOW(A)加至FOLLOW(B)中。
所以FOLLOW(F) = FOLLOW(T) U FOLLOW(F)= #,),+U*= #,),+,*连续使用上述三条规则,直到每个FOLLOW不再增大为止。
FOLLOW(E)= FOLLOW(E)= #,)FOLLOW(T)= FOLLOW(T)= #,),+FOLLOW(F)= #,),+,*七、证明上述文法是否为LL(1)文法对于产生式A:1、若ε,证明侯选式,的首字符集是否相交。
FIRST()∩FIRST()= Ф例:F(E)iFIRST(()∩FIRST(i)= Ф2、若=ε,证明FIRST(A)和FOLLOW(A)是否相交。