编译原理笔记10 自上而下分析-预测分析程序与LL(1)文法

合集下载

编译原理课程设计 LL(1)文法分析器

编译原理课程设计 LL(1)文法分析器

编译原理课程设计 LL(1)文法分析器编译原理课程设计ll(1)文法分析器目录开场白...............................................................1第一章详述.....................................................41.1设计内容....................................................41.2设计建议...................................................4第二章设计的基本原理...........................................42.1预测分析表的形成原理.......................................42.2预测分析程序的分解成.........................................5第三章程序设计.................................................53.1总体方案设计...............................................63.2各模块设计.................................................6第四章程序测试.................................................7第三章程序清单. (8)1课程设计设计题目ll(1)文法分析器学生姓名学号专业班级指导教师2021年12月2合肥工业大学课程设计任务书设计题目ll(1)文法分析器成绩预测分析表中自动构造程序的同时实现设计内容及建议:对于任一输出的一个ll(1)文法,结构其预测分析表中。

建议:首先同时实现子集first(x)结构算法和子集follow(a)结构算法,再同时实现教材主要内容p.79得出的预测分析表中结构算法。

【编译原理】语法分析LL(1)分析法的FIRST和FOLLOW集

【编译原理】语法分析LL(1)分析法的FIRST和FOLLOW集

【编译原理】语法分析LL(1)分析法的FIRST和FOLLOW集 近来复习编译原理,语法分析中的⾃上⽽下LL(1)分析法,需要构造求出⼀个⽂法的FIRST和FOLLOW集,然后构造分析表,利⽤分析表+⼀个栈来做⾃上⽽下的语法分析(递归下降/预测分析),可是这个FIRST集合FOLLOW集看得我头⼤。

教课书上的规则如下,⽤我理解的语⾔描述的:任意符号α的FIRST集求法:1. α为终结符,则把它⾃⾝加⼊FIRSRT(α)2. α为⾮终结符,则:(1)若存在产⽣式α->a...,则把a加⼊FIRST(α),其中a可以为ε(2)若存在⼀串⾮终结符Y1,Y2, ..., Yk-1,且它们的FIRST集都含空串,且有产⽣式α->Y1Y2...Yk...,那么把FIRST(Yk)-{ε}加⼊FIRST(α)。

如果k-1抵达产⽣式末尾,那么把ε加⼊FIRST(α) 注意(2)要连续进⾏,通俗地描述就是:沿途的Yi都能推出空串,则把这⼀路遇到的Yi的FIRST集都加进来,直到遇到第⼀个不能推出空串的Yk为⽌。

重复1,2步骤直⾄每个FIRST集都不再增⼤为⽌。

任意⾮终结符A的FOLLOW集求法:1. A为开始符号,则把#加⼊FOLLOW(A)2. 对于产⽣式A-->αBβ: (1)把FIRST(β)-{ε}加到FOLLOW(B) (2)若β为ε或者ε属于FIRST(β),则把FOLLOW(A)加到FOLLOW(B)重复1,2步骤直⾄每个FOLLOW集都不再增⼤为⽌。

⽼师和同学能很敏锐地求出来,⽽我只能按照规则,像程序⼀样⼀条条执⾏。

于是我把这个过程写成了程序,如下:数据元素的定义:1const int MAX_N = 20;//产⽣式体的最⼤长度2const char nullStr = '$';//空串的字⾯值3 typedef int Type;//符号类型45const Type NON = -1;//⾮法类型6const Type T = 0;//终结符7const Type N = 1;//⾮终结符8const Type NUL = 2;//空串910struct Production//产⽣式11 {12char head;13char* body;14 Production(){}15 Production(char h, char b[]){16 head = h;17 body = (char*)malloc(strlen(b)*sizeof(char));18 strcpy(body, b);19 }20bool operator<(const Production& p)const{//内部const则外部也为const21if(head == p.head) return body[0] < p.body[0];//注意此处只适⽤于LL(1)⽂法,即同⼀VN各候选的⾸符不能有相同的,否则这⾥的⼩于符号还要向前多看⼏个字符,就不是LL(1)⽂法了22return head < p.head;23 }24void print() const{//要加const25 printf("%c -- > %s\n", head, body);26 }27 };2829//以下⼏个集合可以再封装为⼀个⼤结构体--⽂法30set<Production> P;//产⽣式集31set<char> VN, VT;//⾮终结符号集,终结符号集32char S;//开始符号33 map<char, set<char> > FIRST;//FIRST集34 map<char, set<char> > FOLLOW;//FOLLOW集3536set<char>::iterator first;//全局共享的迭代器,其实觉得应该⽤局部变量37set<char>::iterator follow;38set<char>::iterator vn;39set<char>::iterator vt;40set<Production>::iterator p;4142 Type get_type(char alpha){//判读符号类型43if(alpha == '$') return NUL;//空串44else if(VT.find(alpha) != VT.end()) return T;//终结符45else if(VN.find(alpha) != VN.end()) return N;//⾮终结符46else return NON;//⾮法字符47 }主函数的流程很简单,从⽂件读⼊指定格式的⽂法,然后依次求⽂法的FIRST集、FOLLOW集1int main()2 {3 FREAD("grammar2.txt");//从⽂件读取⽂法4int numN = 0;5int numT = 0;6char c = '';7 S = getchar();//开始符号8 printf("%c", S);9 VN.insert(S);10 numN++;11while((c=getchar()) != '\n'){//读⼊⾮终结符12 printf("%c", c);13 VN.insert(c);14 numN++;15 }16 pn();17while((c=getchar()) != '\n'){//读⼊终结符18 printf("%c", c);19 VT.insert(c);20 numT++;21 }22 pn();23 REP(numN){//读⼊产⽣式24 c = getchar();25int n; RINT(n);26while(n--){27char body[MAX_N];28 scanf("%s", body);29 printf("%c --> %s\n", c, body);30 P.insert(Production(c, body));31 }32 getchar();33 }3435 get_first();//⽣成FIRST集36for(vn = VN.begin(); vn != VN.end(); vn++){//打印⾮终结符的FIRST集37 printf("FIRST(%c) = { ", *vn);38for(first = FIRST[*vn].begin(); first != FIRST[*vn].end(); first++){39 printf("%c, ", *first);40 }41 printf("}\n");42 }4344 get_follow();//⽣成⾮终结符的FOLLOW集45for(vn = VN.begin(); vn != VN.end(); vn++){//打印⾮终结符的FOLLOW集46 printf("FOLLOW(%c) = { ", *vn);47for(follow = FOLLOW[*vn].begin(); follow != FOLLOW[*vn].end(); follow++){48 printf("%c, ", *follow);49 }50 printf("}\n");51 }52return0;53 }主函数其中⽂法⽂件的数据格式为(按照平时做题的输⼊格式设计的):第⼀⾏:所有⾮终结符,⽆空格,第⼀个为开始符号;第⼆⾏:所有终结符,⽆空格;剩余⾏:每⾏描述了⼀个⾮终结符的所有产⽣式,第⼀个字符为产⽣式头(⾮终结符),后跟⼀个整数位候选式的个数n,之后是n个以空格分隔的字符串为产⽣式体。

软件工程 编译原理 第五章 自顶向下的语法分析方法

软件工程 编译原理 第五章 自顶向下的语法分析方法

P→1P | 2P |… | mP |
(2)消除间接左递归
对于间接左递归的消除需先将间接左递归变为直接左 递归,然后再按a)消除直接左递归。
例:文法G为例: (1) A→aB (2) A→Bb (3) B→Ac (4) B→d 用产生式(1)、(2)的右部 代替产生式(3)中的非终 结符A得到左部为B的产 生式为: (1) B→aBc (2) BG的产生式为: (1) S→aSb (2) S→aS (3) S→ε 请提取文法中的左公因子
对产生式(1)、(2)提取左公因子后得: S→ aS(b|ε) S→ε 进一步变换为文法G′: S→aSA A→b A→ε S→ε
例2:若文法G的产生式为: (1) A→ad (2) A→Bc (3) B→aA (4) B→bB 请提取文法中的隐式左公因子。 对文法G2分别用(3)、(4)的右 部替换(2)中的B,可得: 提取产生式(1)、(2)的左 (1) A→ad 公共因子得: (2) A→aAc A→a(d|Ac) (3) A→bBc A→bBc (4) B→aA B→aA (5) B→bB B→bB
由上面所举例子可以说明以下问题:
① 不一定每个文法的左公共因子都能在有限的步骤内 替换成无左公共因子的文法,上面文法G4就是如此。 ② 一个文法提取了左公共因子后,只解决了相同左部 产生式右部的FIRST集不相交问题,当改写后的文法 不含空产生式,且无左递归时,则改写后的文法是 LL(1)文法,否则还需用LL(1)文法的判别方式进行判 断才能确定是否为LL(1)文法。
例:文法G(E):
E→TE E→+TE | T→FT T→*FT | F→(E) | i
每个非终结符有对应的子程序的定义, 首先在分析过程中,当需要从某个非终 结符出发进行展开(推导)时,就调用这 个非终结符对应的子程序。

编译原理第4章语法分析自上而下

编译原理第4章语法分析自上而下
Yn;当Y1 Y2 … Yi-1都ε时,(其中1≤i≤n),则FIRST(Y1){ε}、FIRST(Y2) -{ε} 、…、FIRST(Yi-1)- {ε},FIRST(Yi) 都包含在FIRST(X)中
(e) 当(d)中所有Yi * ε,(i=1,2,…n),则 FIRST(X)=FIRST(Y1)∪FIRST(Y2)∪…∪FIRST(Yn)∪{ε}
一 . 自上而下语法分析方法
给定文法G和源程序串$。从G的开始符 号S出发,通过反复使用产生式对句型中的 非终结符进行替换(推导),逐步推导出$ 。
是一种产生的方法,面向目标的方法。 分析的主旨是选择产生式的合适的侯选 式进行推导,逐步使推导结果与$匹配。
Ch4 语法分析 4.1 语法分析程序综述 4.1.2 语法分析的方法
计算Select集:
B ε | aD C AD | b
每个产生式的Select集合计算为:D aS | c
Select(SAB)= (first (AB) -{ε}) ∪Follow(S)={b,a,#}
Select(S bC)= first (bC)={b}
因为A B
Select(Aε)=(first (ε) -{}) ∪Follow (A)={c,a,#}
A ε | b B ε | aD C AD | b D aS | c
first(C)={first(A)-{}} ∪first(D) ∪first(b)={a,b, c}
first(D)={a} ∪{c}={a,c}
➢求出每个文法符号的FIRST集合后也就不难求出一个符号 串的FIRST集合
✓若符号串α∈V*,α=X1 X2 … Xn,当X1不能
∪{ε}
ε*,则置 ∈

编译原理自上而下语法分析

编译原理自上而下语法分析

编译原理
基本架构(1)

变量: sym:当前符号 函数:advance( ):读输入串下一符号 对于每个非终结符号U→α 1|α 2|…|α n处理的方法如下: P(U) {
if sym ∈FIRST(α1 )then P(α1) //处理α1的程序部分 else if sym ∈FIRST(α2 )then P(α2) //处理α2的程序部分 … else if sym ∈FIRST(αn )then P(αn) else if sym ∈FOLLOW(U)then return //处理空产生式情况 else error }
编译原理
间接左递归举例
S→Qc|c Q→Rb|b R→Sa|a 以上文法不含直接左递归,但S,Q,R 都是左递归的,因为: S=>Qc =>Rbc =>Sabc Q =>Rb =>Sab =>Qabc R =>Sa =>Qca =>Rbca
编译原理
消除文法的左递归
前提:如果一个文法不含回路(形如P⇒+ P 的推导),也不含以ε 为右部的产生式, 那么可以通过执行消除文法左递归的算 法消除文法的一切左递归(改写后的文 法可能含有以ε 为右部的产生式)。
FIRST(u)包含了u对应的字的所有可能的首终结符号。 FOLLOW(U)表示了句型中可能紧跟再U后面的终结符号。
编译原理
FIRST(α) 构造算法
对于X∈(VN∪VT),FIRST(X)
的构造 1:若XVT,则FIRST(X)={X} 2:若XVN,且有产生式X→a…, a VT ,则a FIRST(X);如果X→ ,那么 FIRST(X)。 3:若有产生式X→ Y…,Y VN ,则FIRST(Y)\ {} ⊏ FIRST(X); 如果有产生式X→Y1Y2…YK,其中Y1,Y2,Yi-1 VN且Y1Y2…Yi-1 => * , 则FIRST(Yi) \ {} ⊏ FIRST(X);若Y1Y2…YK => * ,则 FIRST(X)。

编译原理第章自上而下语法分析

编译原理第章自上而下语法分析
当预测失败时,需要回溯到之前的步骤并尝试其 他可能的分支,这可能导致效率低下。
3
对输入符号的依赖
自上而下的语法分析需要在分析过程中不断查看 输入符号,因此对输入符号的依赖较强。
编译原理的发展趋势与挑战
静态代码分析与优化
通过静态代码分析技术,编译器可以在编译时检测出更多的潜在错误,并进行 相应的优化。
非递归预测分析的基本思想
消除左递归
通过改写文法规则,消除直接和间接左递归,使得分析过程能够 顺利进行。
构造预测分析表
根据文法规则和非终结符的FOLLOW集,构造预测分析表,用于 指导分析过程。
栈和输入符号的同步处理
使用栈保存分析过程中的信息,并根据输入符号和栈顶信息进行同 步处理,实现语法分析。
非递归预测分析表的构造
介绍LL(1)文法的定义和判别方法,包括FIRST集和 FOLLOW集的构造。
递归下降分析算法设计
介绍递归下降分析算法的设计方法,包括递归子程序的 编写和错误处理机制。
02
CATALOGUE
语法分析基础
语法分析器的作用
识别语法错误
语法分析器能够检查源代码是否 符合语言的语法规则,识别出语 法错误并提供相应的错误信息。
在推导过程中,如果遇到非终 结符,则根据相应的产生式规 则进行替换,直到最终得到终
结符序列为止。
递归下降分析器的构造
分析函数根据当前输入符号和文 法规则判断应该执行的操作。
如果当前输入符号与文法规则中的某 个终结符相匹配,则消耗掉该输入符 号,并继续分析下一个输入符号。
如果当前输入符号与文法规则中 的某个非终结符相匹配,则调用 相应的分析函数进行递归分析。
预测分析算法的实现
初始化分析栈

编译原理(3)语法_4(自顶向下语法分析:LL(1)分析法)

编译原理(3)语法_4(自顶向下语法分析:LL(1)分析法)
2first集确定了每一个非终结符在扫描输入串时所允许遇到的输入符号及所应采用的推导产生式集确定了每一个非终结符在扫描输入串时所允许遇到的输入符号及所应采用的推导产生式该非终结符所对应的产生式中的哪一个候选式33自顶向下的语法分析式中的哪一个候选式3follow集是针对文法中形如a这样的产生式的即在使用这样的产生式的即在使用a的产生式进行推导时面临输入串中哪些输入符号时有一空字的产生式进行推导时面临输入串中哪些输入符号时有一空字即匹配而不出错
课本例题3.8 第二步:计算非终结符的FOLLOW集合
G[E]: E→TE' E'→ + TE' | ε T→FT' T'→*FT' | ε F→(E) | i ③由E→TE' 知FOLLOW(E) ⊂ FOLLOW(E' ), 即FOLLOW(E' ) = {),#}; 由E→TE ' 且E ' → ε知FOLLOW(E)FOLLOW(T),即 FOLLOW(T) = {+,),#};
特别是当Y1~Yk均含有ε产生式时,应把ε也加到FIRST(X)中。
课本例题3.8 第一步:计算非终结符的FIRST集合 例3.8 试构造表达式文法G[E]的LL(1)分析表,其中: G[E]: E→TE' E'→ + TE' | ε T→FT' T'→*FT' | ε F→(E) | i
[解答] 首先构造FIRST集,步骤如下: ① FIRST(E') = {+, ε}; FIRST(T') = {*, ε}; FIRST(F) = {(, i}; ② T→F… 和E→T…知:FIRST(F) ⊂ FIRST(T) ⊂ FIRST(E) 即有FIRST(F) = FIRST(T) = FIRST(E) = {(,i}。

编译原理LL(1)分析实验报告

编译原理LL(1)分析实验报告

青岛科技大学LL(1)分析编译原理实验报告学生班级__________________________学生学号__________________________学生姓名________________________________年 ___月 ___日一、实验目的LL(1)分析法的基本思想是:自项向下分析时从左向右扫描输入串,分析过程中将采用最左推导,并且只需向右看一个符号就可决定如何推导。

通过对给定的文法构造预测分析表和实现某个符号串的分析,掌握LL(1)分析法的基本思想和实现过程。

二、实验要求设计一个给定的LL(1)分析表,输入一个句子,能根据LL(1)分析表输出与句子相应的语法数。

能对语法数生成过程进行模拟。

三、实验内容(1)给定表达式文法为:G(E’): E’→#E# E→E+T | T T→T*F |F F→(E)|i(2)分析的句子为:(i+i)*i四、模块流程五、程序代码#include<iostream>#include<stdio.h>#include <string>#include <stack>using namespace std;char Vt[]={'i','+','*','(',')','#'}; /*终结符*/char Vn[]={'E','e','T','t','F'}; /*非终结符*/ int LENVt=sizeof(Vt);void showstack(stack <char> st) //从栈底开始显示栈中的内容{int i,j;char ch[100];j=st.size();for(i=0;i<j;i++){ch[i]=st.top();st.pop();}for(i=j-1;i>=0;i--){cout<<ch[i];st.push(ch[i]);}}int find(char c,char array[],int n) //查找函数,返回布尔值{int i;int flag=0;for(i=0;i<n;i++){if(c==array[i])flag=1;}return flag;}int location(char c,char array[]) //定位函数,指出字符所在位置,即将字母转换为数组下标值{int i;for(i=0;c!=array[i];i++);return i;}void error(){cout<<" 出错!"<<endl;}void analyse(char Vn[],char Vt[],string M[5][6],string str){int i,j,p,q,h,flag=1;char a,X;stack <char> st; //定义堆栈st.push('#');st.push(Vn[0]); //#与识别符号入栈j=0; //j指向输入串的指针h=1;a=str[j];cout<<"步骤"<<"分析栈"<<"剩余输入串"<<" 所用产生式"<<endl;while(flag==1){cout<<h<<" "; //显示步骤h++;showstack(st); //显示分析栈中内容cout<<" ";for(i=j;i<str.size();i++) cout<<str[i]; //显示剩余字符串X=st.top(); //取栈顶符号放入X if(find(X,Vt,LENVt)==1) //X是终结符if(X==a) //分析栈的栈顶元素和剩余输入串的第一个元素相比较if (X!='#'){cout<<" "<<X<<"匹配"<<endl;st.pop();a=str[++j]; //读入输入串的下一字符}else{ cout<<" "<<"acc!"<<endl<<endl; flag=0;}else{error();break;}else{p=location(X,Vn); //实现下标的转换(非终结符转换为行下标)q=location(a,Vt); //实现下标的转换(终结符转换为列下标)string S1("NULL"),S2("null");if(M[p][q]==S1 || M[p][q]==S2) //查找二维数组中的产生式{error();break;} //对应项为空,则出错else{string str0=M[p][q];cout<<" "<<X<<"-->"<<str0<<endl; //显示对应的产生式st.pop();if(str0!="$") //$代表"空"字符for(i=str0.size()-1;i>=0;i--) st.push(str0[i]);//产生式右端逆序进栈}}}}main(){string M[5][6]={"Te" ,"NULL","NULL","Te", "NULL","NULL","NULL","+Te" ,"NULL","NULL","$", "$","Ft", "NULL","NULL","Ft", "NULL","NULL","NULL","$", "*Ft", "NULL","$", "$","i", "NULL","NULL","(E)", "NULL","NULL"}; //预测分析表j string str;int errflag,i;cout<<"文法:E->E+T|T T->T*F|F F->(E)|i"<<endl;cout<<"请输入分析串(以#结束):"<<endl;do{ errflag=0;cin>>str;for(i=0;i<str.size();i++)if(!find(str[i],Vt,LENVt)){ cout<<"输入串中包含有非终结符"<<str[i]<<"(输入错误)!"<<endl;errflag=1;}} while(errflag==1); //判断输入串的合法性analyse(Vn, Vt, M,str);return 0;}六、实验结果七、实验总结。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

预测分析程序与LL(1)文法
一、预测分析程序
1.带预测分析的PDA:
1)在PDA中加入预测分析之后,可以消除自上而下分析中出现回溯的现象,此时PDA可以改造为:
2)注:a、改造后,整个分析过程都在预测分析程序控制下工作。

B、预测分析程序用了一个预测分析表,它是预测分析程序分析时的主要依据。

3)预测分析表:预测分析表是一矩阵M[A,a],其中行标A是非终结符,列标a是终结符或串结束符;矩阵元素M[A,a]是存放A的一个候选式,指出当前栈顶符号为A 且面临读入符号为a时应选的候选式;或者存放“出错标志”,指出A不该面临读入符号a。

2.预测分析程序算法描述
设栈顶符号为X,读入符号为a,则
1)若X=a=‘#’,则表示识别成功,退出分析程序;
2)若X=a=‘#’,则表示匹配,弹出栈顶符号X,读头前进一格,让读头指向下一个符号,以读入下一个符号;若X是终结符,但X<>a,则调用error处理;
3)若X属于非终结符,则查预测分析表M。

若M[X,a]中存放着关于X的产生式,则弹出X,且将相应产生式右部以自右向左的顺序压入栈,在输出带上记下产生式编号;
若M[X,a]中存放着出错标记,则调用相应Error处理。

二、求串α的终结首符集和非终结符A的随符集
a) 求串α的终结首符集First(α)
i. 定义:假定α是文法G的一个符号串,α属于星闭包,则First(α)={a | α广
义推导出a......a,a属于终结符}
注:1)若α推导出空串,那么空串就属于First(α)。

2)First(α)集合是α的所有可能推导出的开头终结符或空串所组成的集合。

ii. 算法
具体步骤:
b) 求非终结符A的随符集Follow(A)
i. 定义:假定S是文法G的开始符号,对于G的任何非终结符A,定义:
ii. 算法
1. 对文法开始符号S,将‘#’加入到Follow(S)中;
2. 若B->αAβ是文法G的一个产生式,则将First(β)-空串加入到Folow(A)
中;
3. 若B->αA是文法G的一个产生式,或B->αAβ是文法G的一个产生式,
且β推导出空串,则将Follow(B)加入到Follow(A)中;
注:这里的文法必须消除左递归且提取了左因子后的文法。

三、构造预测分析表
a) 基本思想
b) 构造算法
预测分析表的形式化:
四、LL(1)文法、
a) 定义:若文法G的预测分析表M中不含有多重定义项,则称G为LL(1)文法。

注:1)LL(1)文法是无二义的,二义文法一定不是LL(1)文法。

2)LL的含义是从左到右扫描输入串,采用最做推导分析句子。

3)数字1表示分析句子时需向前看一个输入符号。

4)有LL(1)就有LL(k),LL(k)向前看k个输入符号,选择候选式更加准确,
但M的尺寸会以n的k次方增长,其中对程序设计语言去k=1就够
了。

b) 证明定理
文法G是LL(1)文法当且仅当对于G的每个非终结符A的任何两个不同产生式A->
α|β有:
五、状态表:。

相关文档
最新文档