first集和follow集生成算法模拟

合集下载

【编译原理】语法分析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个以空格分隔的字符串为产⽣式体。

编译原理模拟题2

编译原理模拟题2

一、是非题(请在括号内,正确的划√,错误的划×)(每个2分,共20分)1.“ 用高级语言书写的源程序都必须通过编译,产生目标代码后才能投入运行”这种说法。

( )2.若一个句型中出现了某产生式的右部,则此右部一定是该句型的句柄。

( )3.一个句型的句柄一定是文法某产生式的右部。

( )4.在程序中标识符的出现仅为使用性的。

( )5.仅考虑一个基本块,不能确定一个赋值是否真是无用的。

( )6.削减运算强度破坏了临时变量在一基本块内仅被定义一次的特性。

( )7.在中间代码优化中循环上的优化主要有不变表达式外提和削减运算强度。

( )8.算符优先关系表不一定存在对应的优先函数。

( )9.数组元素的地址计算与数组的存储方式有关。

( )10.编译程序与具体的机器有关,与具体的语言无关。

( )参考答案:1、×2、×3、√4、×5、√6、√7、×8、×9、×10、×二、选择题(请在前括号内选择最确切的一项作为答案划一个勾,多划按错论)(每个4分,共40分)1.通常一个编译程序中,不仅包含词法分析,语法分析,中间代码生成,代码优化,目标代码生成等五个部分,还应包括_____。

A.( ) 模拟执行器 B.( ) 解释器C.( ) 表格处理和出错处理 D.( ) 符号执行器2.文法 G[N]= ( {b} , {N , B} , N ,{N→b│bB ,B→bN} ),该文法所描述的语言是A.( ) L(G[N])={bi│i≥0} B.( ) L(G[N])={b2i│i≥0}C.( ) L(G[N])={b2i+1│i≥0} D.( ) L(G[N])={b2i+1│i≥1}3.一个句型中的最左_____称为该句型的句柄。

A.( ) 短语 B.( ) 简单短语C.( ) 素短语 D.( ) 终结符号4.设 G 是一个给定的文法, S 是文法的开始符号,如果 S->x( 其中x∈V*), 则称 x 是文法 G 的一个_____。

构造FIRST集和FOLLOW集的方法

构造FIRST集和FOLLOW集的方法
构造 FIRST 集和 FOLLOW 集的方法
1、构造 FIRST 集的算法 (1) 对于 G 中的每个文法符号 X,为求 FIRST(X),反复应用如下规则,直到集合不再增大: ① 若 X∈VT,则 FIRST(X)是{X} ② 若 X∈VN ,且 X→aα (a∈VT ),则{ a } FIRST(X) X→ε, 则{ε} FIRST(X) ③ 若 X->Y1Y2 … Yi-1 Yi … YK∈P,Y1∈VN ,则 FIRST(Y1)-{ε} FIRST(X)
则 FIRST(Xi) -{} FIRST() 特别是,若所有的 FIRST(Xj)均含有,1jn,则{} FIRST()。 显然,若=则 FIRST()={}。
2、构造 FOLLOW 集的算法 对于 G 中的每一 AFOLLOW 集不再增大为止: ① 对于文法的开始符号 S,令# ∈ FOLLOW(S)。 ② 对于每一 A→αBβ∈P, 令 FIRST(β) - {ε} FOLLOW(B) 。 ③ 对于每一 A→αB∈P, 或 A→αBβ∈P,且ε∈FIRST(β), 则令 FOLLOW(A) FOLLOW(B) 。

而对所有的 j(1≤j ≤i-1), Yj ∈VN,且 Yj⇒ ε,则令 FIRST(Yj)-{ε} FIRST(X) (1≤j ≤i) 特别,当ε∈FIRST(Yj) (1≤j ≤k)时,令ε∈FIRST(X)
(2) 对文法 G 的任何符号串=X1X2…Xn 构造集合 FIRST() ① 置 FIRST(X1)-{} FIRST() ② 若对任何 1ji-1,FIRST(Xj),

编译原理大题

编译原理大题

五、语法分析——自底向上分析法已知文法G:EE+TE TTT*FTFF(E)Fi(1)求文法G中每个非终结符的First集和Follow集。

(2)构造文法G的SLR(1)预测分析表。

(20分)首先构造增广文法:SEEE+TE TTT*FTFF(E)FiFirst(S)=First(E)=First(T)=First(F)={(,I)Follow(S)={#} Follow(E)={+,#,}}Follow(T)={+,},#,*} Follow(F)={+,},#,*}状态Action Gotoi + * ( ) # E T F0 S5 S4 1 2 31 S6 Acc2 r 2 S7 r 2 r 23 r4 r 4 r 4 r44 S5 S4 8 2 35 r6 r 66 S5 9 37 S5 108 S6 S119 r 1 S7 r 1 r 110 r 3 r 3 r 3 r 311 r 5 r 5 r 5 r 5注:识别可归前缀的DFA共12项。

词法分析——确定性有穷自动机为以下字符集编写正规表达式,并构造与之等价的最简DFA(写出详细的具体过程):在字母表{a,b}上的包含偶数个a且含有任意数目b的所有字符串。

(15分)(b*ab*ab*)*b a b1a状态Action GOTOa b d e f $ S R T0 S3 11 acc2 r2 S3 r2 r2 53 S6 S4 24 r4 r4 r4 r45 S10 96 77 S88 r3 r3 r3 r39 r1 r1 r110 r6 S6 S4 r6 r6 1111 S1212 r5 r5 r5五、语法分析——自底向上分析法已知文法G:S’SS bRSTS bRRdSaR eTfRaTf(1)求文法G中每个非终结符的First集和Follow集。

(2)构造文法G的SLR(1)预测分析表。

(20分)frist(s’)={b} follow(s’)={$}frist(s)={b} follow(s)={f,a, $}frist(R) ={d,e} follow( R )={a,b,f, $}frist(T)={t} follow (T)={a,f,#}五、对下面的文法(15分)S->UTa|TbT->S|Sc|dU->US|e判断是否为LR(0),SLR(1),说明理由,并构造相应的分析表。

编译原理 FIRST集和FOLLOW集的求法

编译原理  FIRST集和FOLLOW集的求法

First集合的求法:First集合最终是对产生式右部的字符串而言的,但其关键是求出非终结符的First集合,由于终结符的First集合就是它自己,所以求出非终结符的First集合后,就可很直观地得到每个字符串的First集合。

1. 直接收取:对形如U-a…的产生式(其中a是终结符),把a收入到First(U)中2. 反复传送:对形入U-P…的产生式(其中P是非终结符),应把First(P)中的全部内容传送到First(U)中。

Follow集合的求法:Follow集合是针对非终结符而言的,Follow(U)所表达的是句型中非终结符U所有可能的后随终结符号的集合,特别地,“#”是识别符号的后随符。

1. 直接收取:注意产生式右部的每一个形如“…Ua…”的组合,把a直接收入到Follow(U)中。

2.直接收取:对形如“…UP…”(P是非终结符)的组合,把First(P)除ε直接收入到Follow(U)中。

3.反复传送:对形如P-…U的产生式(其中U是非终结符),应把Follow(P)中的全部内容传送到Follow(U)中。

(或 P-…UB且First(B)包含ε,则把First(B)除ε直接收入到Follow(U)中,并把Follow(P)中的全部内容传送到Follow(U)中)例1:判断该文法是不是LL(1)文法,说明理由 S→ABc A→a|ε B→b|ε?First集合求法就是:能由非终结符号推出的所有的开头符号或可能的ε,但要求这个开头符号是终结符号。

如此题A可以推导出a和ε,所以FIRST(A)={a,ε};同理FIRST (B)={b,ε};S可以推导出aBc,还可以推导出bc,还可以推导出c,所以FIRST(S)={a,b,c}。

Follow集合的求法是:紧跟随其后面的终结符号或#。

但文法的识别符号包含#,在求的时候还要考虑到ε。

具体做法是把所有包含你要求的符号的产生式都找出来,再看哪个有用。

FIRST集和FOLLOW集求法

FIRST集和FOLLOW集求法

FIRST集和FOLLOW集求法龙书算法:First:(1)、如果X是终结符,那么First(X) = X;(2)、如果X是⾮终结符,且XàY1Y2......Yk是⼀个产⽣式,其中k>=1;那么如果对于某个I, a在First(Yi)中,且#(空串)在所有的First(Y1)…..First(Yi-1)中,就吧a加⼊到First(X)中。

(3)、如果Xà#(空串)是⼀个产⽣式,那么将#加⼊到First(X)中。

Follow:(1)、将$放⼊到Follow(S)中,其中S是开始符号,⽽$是输⼊右端结束的标记。

(2)、如果存在⼀个产⽣式AàaBb,那么First(b)中除#(空串)外地所有符号都在Follow(B)中。

(3)、如果存在⼀个产⽣式AàaB, 或存在AàaBb且First(b)包含#(空串),那么Follow(A)中的所有符号都在Follow(B)中。

⾃⼰理解:First:(看X的产⽣式)(1)、如果X是终结符,那么First(X)= X;(2)、如果X是⾮终结符,且XàY1Y2......Yk,i=1;1)、将First(Yi)加⼊到First(X)中,2)、如果#包含着First(Yi)中,i++,重复1);3)、如果#不包含在First(Yi)中,First(X)计算完成;(3)、如果Xà#(空串)是⼀个产⽣式,那么将#加⼊到First(X)中。

Follow:(看在右边有B的产⽣式)(1)、将$放⼊到Follow(S)中,其中S是开始符号,⽽$是输⼊右端结束的标记。

(2)、如果存在⼀个产⽣式AàaBb,那么First(b)中除#(空串)外地所有符号都在Follow(B)中。

(3)、如果存在⼀个产⽣式AàaB, 或存在AàaBb且First(b)包含#(空串),那么Follow(A)中的所有符号都在Follow(B)中。

编译原理(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}。

C++求文法的first和follow集合

C++求文法的first和follow集合

1.#include "stdafx.h"2.#include "LR.h"3.#include "MLR1.h"4.5.#ifdef _DEBUG6.#undef THIS_FILE7.static char THIS_FILE[]=__FILE__;8.#define new DEBUG_NEW9.#endif10.//----调试部分使用的代码11.CString MLR1::GetFirst(int i){12.if(i<0||i>=GetIdentNum())return"";13.return FirstSet5(m_first[i].Fi,m_first[i].flag&2);14.}15.CString MLR1::GetFollow(int i){16.if(i<0||i>=GetIdentNum())return"";17.return FollowSet1(m_first[i].Fo,m_first[i].flag&0x08);18.}19.//----构造部分20.MLR1::MLR1(){21.}22.MLR1::~MLR1(){23.}24.void MLR1::ReSet(FILE* pf){25.//使用文件指针pf来重新驱动程序26.int i;27. p_file=pf;28. list_Express.RemoveAll();29. list_Ident.RemoveAll();30.for(i=0;i<MAP_SIZE;I++) p="(char*)m_first+sizeof(s_first)*MAX_IDENT-1;"for(char* bit_map[i]="0;">=(char*)m_first;p--)31. *p=0;32. Lex3();33. FirstSet6();34.// FollowSet3();35.}36.//----输入分析部分37.bool MLR1::Lex1(){38.//截取一个分号段到tocken中39.//功能字符取其负数40.char ch=0;41.bool end=false;42. token_len=0;43.if(feof(p_file))return false;44.while(!end&&!feof(p_file)){45.if(token_len>=LINE_LENGTH)break;46.if(fread(&ch,1,1,p_file)<=0)break;47.if(ch<=0)goto error;48.switch(ch){49.case';':50. end=true;51.case'<':52.case'>':53.case'=':54. ch=-ch;55.break;56.case'\\':57. fread(&ch,1,1,p_file);58.if(ch<=0)goto error;59.break;60. }61. token[token_len++]=ch;62. }63. token[token_len]=0;64.return true;65.error:66. fprintf(stderr,"must be 1--127");67.return false;68.}69.int MLR1::Lex2_1(char*&s,bool isUse){70.//识别非终结符并加入list_Ident71.char ident[ID_LENGTH+1];72.int t=0;73.if((int)*s++!=-'<')return 0;74.if(isalpha(*s))ident[t++]=*s++;75.else return 0;76.while(isalpha(*s)||isdigit(*s))ident[t++]=*s++;77.while(*s=='\'')ident[t++]=*s++;78.if((int)*s++!=-'>')return 0;79.if(t==0)return 0;80. ident[t]=0;81.for(t=list_Ident.GetSize()-1;t>=0;t--)82.if(list_Ident[t]==(CString)ident)break;83.if(t<0){84.if(list_Ident.GetSize()>=MAX_IDENT)return false;85. list_Ident.Add((CString)ident);86. t=list_Ident.GetSize()-1;87.if(isUse)bit_map[t/8]|=1<<(t%8);88. }89.if(!isUse)bit_map[t/8]&=~(1<<(t%8));90.return t+1;91.}92.bool MLR1::Lex2(){93.//将token中的非终结符用(-1) -- (-127)表示94.//进行语法判断<终结符>=符号表;95.register char *s,*d;96.char * end;97.int i;98. s=d=token;99. end=&token[token_len];100.if(i=Lex2_1(s))*d++=-i;101.else return false;102.if(*s++!=-'=')return false;103.while(s<END){ if(*p while(*p!="0){" *p="X+1;"char const判断表达式X能否推出LR_NULL *X){ MLR1::FirstSet1(const bool * 首先判断某非终结符能否推出LR_NULL ----First集 } true; return false; if(bit_map[i]!="0)return" i="0;i<MAP_SIZE;i++ )"for(int else list_Express.Add((CString)token); if(Lex2()) if(token_ len="=0)continue;"while(Lex1()){ 判断bit_map是否为全零,如果不是则表示有未定义的非终结符循环调用Lex1读入一句,调用Lex2进行语法分析 MLR1::Lex3(){ s<end; *d="0;" *d++="*s++;" }else if(i="Lex2_1(s,true ))*d++=-i;"if((int)*s="=-'<'){"if(*s="=-';')break;">=1){104.return false;105. }else{106.if(*p==*X)return false;107.if(!FirstSet2(*p))108.return false;109. }110. p++;111. }112.return true;113.}114.bool MLR1::FirstSet2(const char X){115.//判断非终结符X能否推出LR_NULL116. CString temp;117.if(m_first[-X-1].flag&0x40)return false;118.if(m_first[-X-1].flag&1)119.return (m_first[-X-1].flag&2)!=0;120. m_first[-X-1].flag|=0x40;121.for(int i=list_Express.GetSize();i>0;i--){122. temp=list_Express.GetAt(i-1);123.if(temp[0]==X){124.if(FirstSet1((LPCSTR)temp)){125. m_first[-X-1].flag|=3;126.return true;127. }128. }129. }130. m_first[-X-1].flag|=1;131. m_first[-X-1].flag^=0x40;132.return false;133.}134.bool MLR1::FirstSet3(const char *X,char*Fi){135.//求产生式X的First集放在F中,如果LR_NULL在First集中则返回true 136.//如果要求符号串的First集,就将X[0]设为0137.//假设X中不出现LR_NULL,LR_EOF和LR_EOS138.//假设F的长度为MAP_SIZE,有128b139.const char *p=X;140. X++;141.while(*X!=0){142.if(*X>=1){143. Fi[(*X)/8]|=1<<(*X)%8;144.return false;145. }else{146.if(*X==*p){147.if(!FirstSet2(*X))148.return false;149. }else if(!FirstSet4(*X,Fi)){150.return false;151. }152. }153. X++;154. }155.return true;156.}157.bool MLR1::FirstSet4(char const X,char*Fi){158.//求非终结符X的First集放在F中159.//如果LR_NULL在其中则返回true160. CString temp;161.if(m_first[-X-1].flag&0x40)return false;162.if((m_first[-X-1].flag&4)==0){163. m_first[-X-1].flag|=0x40;164.for(int i=list_Express.GetSize();i>0;i--){165. temp=list_Express.GetAt(i-1);166.if(temp[0]==X)167. FirstSet3((LPCSTR)temp,m_first[-X-1].Fi);168. }169. m_first[-X-1].flag|=4;170. m_first[-X-1].flag^=0x40;171. }172.if(Fi!=m_first[-X-1].Fi){173.for(int i=0;i<MAP_SIZE;I++) *p="t;"char } return for(i="0;i< MAP_SIZE;i++){" i; int为每个非终结符求First集 MLR1::FirstSet6(){ void (CString)t; if(has_null)*p++="LR_NULL;" *p+ +="i*8+j;"if(Fi[i]&(1<<j)) if(Fi[i])for(j="0;j<8;j++)" i,j; t[128];将集合表示的First变为字符串式 has_null){ char*Fi,bool MLR1::FirstSet5(const CString (m_first[-X-1 ].flag&2); Fi[i]|="m_first[-X-1].Fi[i];">0;i--){174.if((m_first[i-1].flag&1)==0)175. FirstSet2(-i);176. }177.for(i=list_Ident.GetSize();i>0;i--){178.if((m_first[i-1].flag&4)==0)179. FirstSet4(-i,m_first[i-1].Fi);180. }181.}182.CString MLR1::FollowSet1(const char*Fo,bool has_eof){183.//将集合表示的Follow变为字符串式184.char t[128];185.char *p=t;186.int i,j;187.for(i=0;i<MAP_SIZE;I++){ *p="0;"char bool * } return int (CString)t; *p++="i*8+j;" i,j; if(X 如果是非法符号则退出 *p; temp[LINE_LENGTH]; flag,rc="true;" flag.b5该符号的Follow集正在被计算 flag为真表示已经找到X,将找后继的第一个字符必须在执行前将LR_EOF加入识别符号的Follow集中只有在X的Follow集未被计算时才调用该函数求非终结符X的Follow集 Fo){ X,char MLR1::FollowSet2(const if(has_eof)*p++="LR_NULL;"if(Fo[i]&(1<<j)) if(Fo[i])for(j="0;j<8;j++)">=0||X<-MAX_IDENT)return true;188.//如果该符号正被计算则退出189.if(m_first[-X-1].flag&0x20)return false;190.//如果该符号为被计算则计算191.if((m_first[-X-1].flag&0x10)==0){192. m_first[-X-1].flag|=0x20;193.for(i=list_Express.GetSize();i>0;i--){194. sprintf(temp,list_Express.GetAt(i-1));195. flag=false;196. p=temp+1;197.while(*p!=0){198.if(!flag){199.if(*p==X){200.//表达式中出现了符号X201. flag=true;}202. }else{203.if(*p>0){204.//规则2:X后碰上终结符205. flag=false;206. Fo[*p/8]|=1<<(*p%8);207. }else{208.//规则2:X后碰上非终结符则并上它的First集209.for(j=0;j<MAP_SIZE;J++) } return for(i="0;i<M AP_SIZE;i++)" i; int void m_first[0].flag|="0x08;"求各非终结符的Follow 集 MLR1::FollowSet3(){ rc; Fo[i]|="m_first[-X-1].Fo[i];"if(Fo!="m_fir st[-X-1].Fo){" m_first[-X-1].flag^="0x20;"if(rc)m_first[-X-1].flag|="0x10;" m_first[-X-1].flag|="0x08;"if(m_first[-temp[0]-1].flag&0x08) F o[j]|="m_first[-*p-1].Fi[j];"for(j="0;j<MAP_SIZE;j++)" rc="false;"if (!FollowSet2(temp[0],m_first[-temp[0]-1].Fo)) 如果是自反关系则不计算规则3:将temp[0]的Follow集并到Fo上if(flag&&(X!="temp[0])){" p++; flag="false;"if((m_first[-*p-1].fla g&2)="=0)"如果X可以推出LR_NULL则继续>0;i--){210.if((m_first[i-1].flag&0x10)==0){211. FollowSet2(-i,m_first[i-1].Fo);212. }213. }214.}。

follow集合的求法(3篇)

follow集合的求法(3篇)

第1篇摘要:Follow集合在数据库查询中具有重要的应用价值,本文旨在介绍Follow集合的概念、求法以及在数据库查询中的应用。

通过详细阐述Follow集合的原理和方法,帮助读者深入理解其重要性,并学会在实际应用中有效利用Follow集合。

一、引言在数据库查询过程中,我们常常需要关注某些记录的关联记录,即所谓的“后续记录”。

为了方便查询,我们可以利用Follow集合来表示这些后续记录。

本文将介绍Follow集合的概念、求法以及在数据库查询中的应用。

二、Follow集合的概念1. 定义:Follow集合是指在一个数据表中,对于某一记录,所有与之存在关联关系的记录的集合。

2. 特点:(1)无序性:Follow集合中的记录没有特定的顺序;(2)唯一性:对于某一记录,其Follow集合是唯一的;(3)动态性:Follow集合随着查询条件的改变而改变。

三、Follow集合的求法1. 算法概述求Follow集合的基本思路是:从某一记录出发,遍历整个数据表,查找与之存在关联关系的记录,并将其加入Follow集合。

具体算法如下:(1)初始化Follow集合为空;(2)遍历数据表中的所有记录;(3)对于每一条记录,查找其关联记录,并将其加入Follow集合;(4)重复步骤(2)和(3),直到遍历完所有记录。

2. 算法实现以下是一个基于Python的Follow集合求法示例:```pythondef follow_set(data, record_id):"""求Follow集合的函数:param data: 数据表:param record_id: 记录ID:return: Follow集合"""follow_set = set()for record in data:if record['id'] == record_id:for relation in record['relations']:follow_set.add(relation)else:for relation in record['relations']:if relation in follow_set:follow_set.add(record['id'])return follow_set```3. 算法优化在实际情况中,数据表可能非常大,导致Follow集合求法效率低下。

编译原理 第4章 语法分析—自顶向下分析

编译原理 第4章 语法分析—自顶向下分析

例 S::=aABbcd|ε,A::=ASd|ε,B::=SAh|eC|ε,
C::=Sf|Cg|ε,求此文法的每一个非终结符号的
FOLLOW集。
解:FOLLOW(S)={#}∪FIRST(d) ∪(FIRST(Ah)-{ε}) ∪FIRST(f)
={#}∪{d}∪{a,d,h}∪{f} = {a,d,h,f,#}
4)若对于一切1≤i≤n,ε∈FIRST(Xi),则将ε符号加 进FIRST(α)。
例4-1(P62) 有文法: E→TE′ E′→+TE′ E′→ε T→FT′ T′→*FT′ T′→ε F→(E)|i 求文法中非 终结符号以及各 产生式右部符号 串的FIRST集。
解:该文法的非终结符号有E、E′、 T、T′和F。 FIRST(E)=FIRST(TE′) =FIRST(FT′E′)={ ( ,i } FIRST(+TE′)={ + } FIRST(ε)={ε} FIRST(E′)=FIRST(+TE′) ∪FIRST(ε)={+ ,ε} FIRST(T)=FIRST(FT′)={ ( ,i } FIRST(*FT′)={ * } FIRST(T′)=FIRST(*FT′) ∪FIRST(ε)={* ,ε} FIRST((E))={ ( } FIRST(i)={ i } FIRST(F) =FIRST((E)) ∪FIRST(i)={( ,i}
分析法算符优先分析法简单优先分析法优先分析法自底向上带回溯递归下降分析法分析法不带回溯自顶向下语法分析lr回溯示例41p61自顶向下的分析方法就是从文法的开始符号出发按最左推导方式向下推导试图推导出要分析的输开始符号输入符号串自底向上的分析方法从输入符号串开始按最左归约方式向上归约到文法的开始符号

计算first集合和follow集合--编译原理

计算first集合和follow集合--编译原理

计算first 集合和follow 集合姓名:彦清 学号:E10914127一、实验目的输入:任意的上下文无关文法。

输出:所输入的上下文无关文法一切非终结符的first 集合和follow 集合。

二、实验原理设文法G[S]=(V N ,V T ,P ,S ),则首字符集为:FIRST (α)={a | α⇒*a β,a ∈V T ,α,β∈V *}。

若α⇒*ε,ε∈FIRST (α)。

由定义可以看出,FIRST (α)是指符号串α能够推导出的所有符号串中处于串首的终结符号组成的集合。

所以FIRST 集也称为首符号集。

设α=x 1x 2…x n ,FIRST (α)可按下列方法求得:令FIRST (α)=Φ,i =1;(1) 若x i ∈V T ,则x i ∈FIRST (α);(2) 若x i ∈V N ;① 若ε∉FIRST (x i ),则FIRST (x i )∈FIRST (α);② 若ε∈FIRST (x i ),则FIRST (x i )-{ε}∈FIRST (α);(3) i =i+1,重复(1)、(2),直到x i ∈V T ,(i =2,3,…,n )或x i∈V N 且若ε∉FIRST (x i )或i>n 为止。

当一个文法中存在ε产生式时,例如,存在A →ε,只有知道哪些符号可以合法地出现在非终结符A 之后,才能知道是否选择A →ε产生式。

这些合法地出现在非终结符A 之后的符号组成的集合被称为FOLLOW 集合。

下面我们给出文法的FOLLOW 集的定义。

设文法G[S]=(V N ,V T ,P ,S ),则FOLLOW (A )={a | S ⇒… Aa …,a ∈V T }。

若S *…A ,#∈FOLLOW (A )。

由定义可以看出,FOLLOW (A )是指在文法G[S]的所有句型中,紧跟在非终结符A 后的终结符号的集合。

FOLLOW 集可按下列方法求得:(1) 对于文法G[S]的开始符号S ,有#∈FOLLOW (S );(2) 若文法G[S]中有形如B →xAy 的规则,其中x ,y ∈V *,则FIRST(y )-{ε}∈FOLLOW (A );(3) 若文法G[S]中有形如B →xA 的规则,或形如B →xAy 的规则且ε∈FIRST (y ),其中x ,y ∈V *,则FOLLOW (B )∈FOLLOW (A );三、源程序#include<iostream.h>#include<string.h>//产生式struct css{char left;char zhuan;//用“-”表示箭头char right[20];};//空标志struct kong{int kongzuo;int kongyou;};struct biaoji//第三步扫描式子的右部标记号{int r[100];};struct first//初步求first 集合时用{ char fjihe[200];};struct first2//保存最终的first 集合{ char fjihe2[200];};struct follow//初步求follow 集合时用{ char fow[200];};struct follow2//保存最终的follow 集合{ char fow2[200];};void main(){ int i,n,k;//产生式条数css shizi[100];kong kongshi[100];cout<<"请输入产生式的条数n(n<100):"<<endl;cin>>n;cout<<"请从开始符输入产生式(空用“#”表示,产生式由字母组成):"<<endl;for(i=0;i<n;i++){cin>>shizi[i].left>>shizi[i].zhuan>>shizi[i].right;}int l,m,j,h,g,f;for(l=0;l<n;l++)for(m=0;m<sizeof(shizi[l].right);m++){ if(shizi[l].right[m]=='#'){kongshi[l].kongzuo=1;break;}else while(shizi[l].right[m]>='a' && shizi[l].right[m]<='z' ){kongshi[l].kongyou=0; break;}}for(j=0;j<=n;j++)for(h=0;h<n;h++){ if(j==h)break;if(shizi[j].left==shizi[h].left){if(kongshi[j].kongyou==0 && kongshi[h].kongyou==0)kongshi[j].kongzuo=kongshi[h].kongzuo=0;break;}}int d,s,a,q,w,e;char linshi;biaoji biaoyou[100];for(d=0;d<n;d++){if(!(kongshi[d].kongzuo==1||kongshi[d].kongyou==0)){for(s=0;shizi[d].right[s]!='\0';s++)for(a=0;a<=n;a++){ linshi=shizi[d].right[s];if(linshi==shizi[a].left && kongshi[a].kongzuo==1){ biaoyou[d].r[s]=1;}else {kongshi[d].kongyou=0;}}}}int sum,t,y;//第三部-1sum=0;for(e=0;e<n;e++){for(q=0;shizi[e].right[q]!='\0';q++){ t=biaoyou[e].r[q];if(t==1){ for(sum;shizi[e].right[q]!='\0';){sum++;y=sum-1;if(q==y)kongshi[e].kongzuo=1;break;}}else break;}}int a1,a2;/*第二次扫描判断转为否的式子*/for(a1=0;a1<=n;a1++)for(a2=0;a2<n;a2++){ if(a1==a2)break;if(shizi[a1].left==shizi[a2].left&&kongshi[a1].kongzuo!=1&&kongsh i[a2].kongzuo!=1){if(kongshi[a1].kongyou==0 && kongshi[a2].kongyou==0)kongshi[a1].kongzuo=kongshi[a2].kongzuo=0;break;}}//计算first集合first fji[100];int u,a3,a5,a6,a7,a8;//char linshi2[2]="-";//for(u=0;u<n;u++){ fji[u].fjihe[0]='\0';}for(a3=0;a3<=n;a3++){if(shizi[a3].right[0]>='a' && shizi[a3].right[0]<='z') {linshi2[0]=shizi[a3].right[0];strcat(fji[a3].fjihe,linshi2);}else { if(kongshi[a3].kongzuo==1){ strcat(fji[a3].fjihe,"#");}}}for(a5=0;a5<=n;a5++)for(a6=0;shizi[a5].right[a6]!='\0';a6++)if(shizi[a5].right[a6]>='A' && shizi[a5].right[a6]<='Z'){ if(shizi[a5].right[0]>='a' && shizi[a5].right[0]<='z')break;for(a7=0;a7<n;a7++)if(a5!=a7 && shizi[a5].right[a6]==shizi[a7].left){ {if(kongshi[a7].kongzuo!=1)strcat(fji[a5].fjihe,fji[a7].fjihe);if(a6==(strlen(shizi[a5].right)-1))for(a8=0;a8<n;a8++)if(a5!=a8 && shizi[a5].right[a6]==shizi[a8].left)if(kongshi[a5].kongzuo!=1){strcat(fji[a5].fjihe,fji[a8].fjihe);}else{strcat(fji[a5].fjihe,fji[a8].fjihe);strcat(fji[a5].fjihe,"#");}}}}//求follow集合follow fw[100];int b1,b2,b3,b4,b5,b6,b7,b8,b9,b10;char linshi5[2];for(b1=0;b1<n;b1++){ fw[b1].fow[0]='\0';}fw[0].fow[0]='#';fw[0].fow[1]='\0';for(b8=0;b8<n;b8++){ if(shizi[b8].left==shizi[0].left)fw[b8].fow[0]='#';fw[b8].fow[1]='\0';}int e1;for(e1=0;e1<2;e1++)for(b2=0;b2<n;b2++)for(b3=0;b3<n;b3++){ if(shizi[b2].right[b3]>='A'&&shizi[b2].right[b3]<='Z')if(shizi[b2].right[b3+1]>='a'&&shizi[b2].right[b3+1]<='z'){ linshi5[0]=shizi[b2].right[b3+1];linshi5[1]='\0';for(b9=0;b9<n;b9++){if(shizi[b2].right[b3]==shizi[b9].left)strcat(fw[b9].fow,linshi5);}}if(shizi[b2].right[b3+1]>='A'&&shizi[b2].right[b3+1]<='Z'){ for(b4=0;b4<n;b4++){if(shizi[b2].right[b3+1]==shizi[b4].left){ if(kongshi[b4].kongzuo!=1){for(b10=0;b10<n;b10++){if(shizi[b2].right[b3]==shizi[b10].left)strcat(fw[b10].fow,fji[b4].fjihe);}}else { for(b5=0;b5<n;b5++)if(shizi[b2].right[b3]==shizi[b5].left)strcat(fw[b5].fow,fw[b2].fow);}}}}if((b3+1)==strlen(shizi[b2].right)){ for(b7=0;b7<n;b7++)if(shizi[b2].right[b3]==shizi[b7].left)strcat(fw[b7].fow,fw[b2].fow);}}first2 fji2[100];int a11,a12,a13;for(a11=0;a11<n;a11++){ fji2[a11].fjihe2[0]='\0';}for(a12=0;a12<=n;a12++)for(a13=0;a13<n;a13++){ if(a12!=a13 && shizi[a12].left==shizi[a13].left)strcat(fji[a12].fjihe,fji[a13].fjihe);}char linshi3[100];char linshi4[2];int a15,a16,a17=0,a19=0,a21,a18;//for(a15=0;a15<n;a15++){ {for(a21=0;a21<99;a21++)linshi3[a21]='\0';}{for(a16=0;a16<strlen(fji[a15].fjihe);a16++){if(a16==0){linshi4[0]=fji[a15].fjihe[a16];linshi4[1]='\0';strcat(linshi3,linshi4);a16++;}for(a17=0;a17<=strlen(linshi3);a17++)if(linshi3[a17]==fji[a15].fjihe[a16])break;//if(linshi3[a17]=='\0'){ linshi4[0]=fji[a15].fjihe[a16];linshi4[1]='\0';strcat(linshi3,linshi4);}}}strcat(fji2[a15].fjihe2,linshi3);}follow2 fw2[100];int b11,b12,b13;for(b11=0;b11<n;b11++){ fw2[b11].fow2[0]='\0';}for(b12=0;b12<=n;b12++)for(b13=0;b13<n;b13++){ if(b12!=b13 && shizi[b12].left==shizi[b13].left) strcat(fw[b12].fow,fw[b13].fow);}char linshi6[100];char linshi7[2];int b15,b16,b17,b19=0,b21,b18;//for(b15=0;b15<n;b15++){ { {for(b21=0;b21<99;b21++)linshi6[b21]='\0';}{for(b16=0;b16<strlen(fw[b15].fow);b16++){if(b16==0){linshi7[0]=fw[b15].fow[b16];linshi7[1]='\0';strcat(linshi6,linshi7);b16++;}for(b17=0;b17<=strlen(linshi6);b17++)if(linshi6[b17]==fw[b15].fow[b16])break;//if(linshi6[b17]=='\0'){ linshi7[0]=fw[b15].fow[b16];linshi7[1]='\0';strcat(linshi6,linshi7);}}}}strcat(fw2[b15].fow2,linshi6);}int c1,c2;cout<<"非终结符"<<" "<<"first集合"<<endl;cout<<" "<<shizi[0].left<<" "<<fji2[0].fjihe2<<endl;for(c1=1;c1<n;c1++){for(c2=0;c2<c1;c2++){ if(shizi[c1].left!=shizi[c2].left&&c2==(c1-1))cout<<" "<<shizi[c1].left<<" "<<fji2[c1].fjihe2<<endl;}}int d1,d2;cout<<"非终结符"<<" "<<"follow集合"<<endl;cout<<" "<<shizi[0].left<<" "<<fw2[0].fow2<<endl;for(d1=1;d1<n;d1++){for(d2=0;d2<d1;d2++){ if(shizi[d1].left!=shizi[d2].left&&d2==(d1-1))cout<<" "<<shizi[d1].left<<" "<<fw2[d1].fow2<<endl;}}}四、运行截图。

编译原理实验二:LL(1)语法分析器

编译原理实验二:LL(1)语法分析器

编译原理实验⼆:LL(1)语法分析器⼀、实验要求 1. 提取左公因⼦或消除左递归(实现了消除左递归) 2. 递归求First集和Follow集 其它的只要按照课本上的步骤顺序写下来就好(但是代码量超多...),下⾯我贴出实验的⼀些关键代码和算法思想。

⼆、基于预测分析表法的语法分析 2.1 代码结构 2.1.1 Grammar类 功能:主要⽤来处理输⼊的⽂法,包括将⽂法中的终结符和⾮终结符分别存储,检测直接左递归和左公因⼦,消除直接左递归,获得所有⾮终结符的First集,Follow集以及产⽣式的Select集。

#ifndef GRAMMAR_H#define GRAMMAR_H#include <string>#include <cstring>#include <iostream>#include <vector>#include <set>#include <iomanip>#include <algorithm>using namespace std;const int maxn = 110;//产⽣式结构体struct EXP{char left; //左部string right; //右部};class Grammar{public:Grammar(); //构造函数bool isNotTer(char x); //判断是否是终结符int getTer(char x); //获取终结符下标int getNonTer(char x); //获取⾮终结符下标void getFirst(char x); //获取某个⾮终结符的First集void getFollow(char x); //获取某个⾮终结符的Follow集void getSelect(char x); //获取产⽣式的Select集void input(); //输⼊⽂法void scanExp(); //扫描输⼊的产⽣式,检测是否有左递归和左公因⼦void remove(); //消除左递归void solve(); //处理⽂法,获得所有First集,Follow集以及Select集void display(); //打印First集,Follow集,Select集void debug(); //⽤于debug的函数~Grammar(); //析构函数protected:int cnt; //产⽣式数⽬EXP exp[maxn]; //产⽣式集合set<char> First[maxn]; //First集set<char> Follow[maxn]; //Follow集set<char> Select[maxn]; //select集vector<char> ter_copy; //去掉$的终结符vector<char> ter; //终结符vector<char> not_ter; //⾮终结符};#endif 2.1.2 AnalyzTable类 功能:得到预测分析表,判断输⼊的⽂法是否是LL(1)⽂法,⽤预测分析表法判断输⼊的符号串是否符合刚才输⼊的⽂法,并打印出分析过程。

语法分析器实验报告

语法分析器实验报告

语法分析器的设计实验报告一、实验内容语法分析程序用LL(1)语法分析方法。

首先输入定义好的文法书写文件(所用的文法可以用LL(1)分析),先求出所输入的文法的每个非终结符是否能推出空,再分别计算非终结符号的FIRST集合,每个非终结符号的FOLLOW集合,以及每个规则的SELECT集合,并判断任意一个非终结符号的任意两个规则的SELECT集的交集是不是都为空,如果是,则输入文法符合LL(1)文法,可以进行分析。

对于文法:G[E]:E->E+T|TT->T*F|FF->i|(E)分析句子i+i*i是否符合文法。

二、基本思想1、语法分析器实现语法分析是编译过程的核心部分,它的主要任务是按照程序的语法规则,从由词法分析输出的源程序符号串中识别出各类语法成分,同时进行词法检查,为语义分析和代码生成作准备。

这里采用自顶向下的LL(1)分析方法。

语法分析程序的流程图如图5-4所示。

语法分析程序流程图该程序可分为如下几步:(1)读入文法(2)判断正误(3)若无误,判断是否为LL(1)文法(4)若是,构造分析表;(5)由句型判别算法判断输入符号串是为该文法的句型。

三、核心思想该分析程序有15部分组成:(1)首先定义各种需要用到的常量和变量;(2)判断一个字符是否在指定字符串中;(3)读入一个文法;(4)将单个符号或符号串并入另一符号串;(5)求所有能直接推出&的符号;(6)求某一符号能否推出‘& ’;(7)判断读入的文法是否正确;(8)求单个符号的FIRST;(9)求各产生式右部的FIRST;(10)求各产生式左部的FOLLOW;(11)判断读入文法是否为一个LL(1)文法;(12)构造分析表M;(13)句型判别算法;(14)一个用户调用函数;(15)主函数;下面是其中几部分程序段的算法思想:1、求能推出空的非终结符集Ⅰ、实例中求直接推出空的empty集的算法描述如下:void emp(char c){ 参数c为空符号char temp[10];定义临时数组int i;for(i=0;i<=count-1;i++)从文法的第一个产生式开始查找{if 产生式右部第一个符号是空符号并且右部长度为1,then将该条产生式左部符号保存在临时数组temp中将临时数组中的元素合并到记录可推出&符号的数组empty中。

【编译原理】FIRST集、FOLLOW集算法原理和实现

【编译原理】FIRST集、FOLLOW集算法原理和实现

【编译原理】FIRST集、FOLLOW集算法原理和实现书中⼀些话,不知是翻译的原因。

还是我个⼈理解的原因感觉不是⾮常好理解。

个⼈重新整理了⼀下。

不过相对于消除左递归和提取左公因,FIRST集和FOLLOW集的算法相对来说⽐较简单。

书中的重点给出:FIRST:⼀个⽂法符号的FIRST集就是这个符号能推导出的第⼀个终结符号的集合, 包括空串。

例: A -> abc | def | ε那么FIRST(A) 等于 { a, d, ε }。

FOLLOW:蓝线画的部分很重要。

特别是这句话:请注意,在这个推导的某个阶段,A和a之间可能存在⼀些⽂法符号。

单如果这样,这些符号会推导得到ε并消失。

这句话的意思就是好⽐说: S->ABa B->c | ε 这个⽂法 FOLLOW(A)的值应该是FIRST(B)所有的终结符的集合(不包含ε),但是FIRST(B)是包含ε的,说明B是可空的,既然B是可空的S->ABa 也可以看成 S->Aa。

那么a就可以跟在A的后⾯.所以在这种情况下,FOLLOW(A)的值是包含a的。

换句话说就是。

⼀个⽂法符号A的FOLLOW集合就是它的下⼀个⽂法符号B的FIRST集合。

如果下⼀个⽂法符号B的FIRST集合包含ε,那么我们就要获取下⼀个⽂法符号B的FOLLOW集添加到FOLLOW(A)中代码中的注释已经很详细// 提取First集合func First(cfg []*Production, sym *Symbolic) map[string] *Symbolic {result := make(map[string] *Symbolic)// 规则⼀如果符号是⼀个终结符号,那么他的FIRST集合就是它⾃⾝if sym.SymType() == SYM_TYPE_TERMINAL || sym.SymType() == SYM_TYPE_NIL {result[sym.Sym()] = symreturn result}// 规则⼆如果⼀个符号是⼀个⾮终结符号// (1) A -> XYZ 如果 X 可以推导出nil 那么就去查看Y是否可以推导出nil// 如果 Y 推导不出nil,那么把Y的First集合加⼊到A的First集合// 如果 Y 不能推导出nil,那么继续推导 Z 是否可以推导出nil,依次类推// (2) A -> XYZ 如果XYZ 都可以推导出 nil, 那么说明A这个产⽣式有可能就是nil,这个时候我们就把nil加⼊到FIRST(A)中for _, production := range cfg {if production.header == sym.Sym() {nilCount := 0for _, rightSymbolic := range production.body { // 对于⼀个产⽣式ret := First(cfg, rightSymbolic) // 获取这个产⽣式体的First集合hasNil := falsefor k, v := range ret {if v.SymType() == SYM_TYPE_NIL { // 如果推导出nil, 标识当前产⽣式体的符号可以推导出nilhasNil = true} else {result[k] = v}}if false == hasNil { // 当前符号不能推导出nil, 那么这个产⽣式的FIRST就计算结束了,开始计算下⼀个产⽣式break}// 当前符号可以推导出nil,那么开始推导下⼀个符号nilCount++if nilCount == len(production.body) { // 如果产⽣式体都可以推导出nil,那么这个产⽣式就可以推导出nilresult["@"] = &Symbolic{sym: "@", sym_type: SYM_TYPE_NIL}}}}}return result}// 提取FOLLOW集合func Follow(cfg []*Production, sym string) [] *Symbolic {fmt.Printf("Follow ------> %s\n", sym)result := make([] *Symbolic, 0)// ⼀个⽂法符号的FOLLOW集就是可能出现在这个⽂法符号后⾯的终结符// ⽐如 S->ABaD, 那么FOLLOW(B)的值就是a。

first集合和follow集合的求法

first集合和follow集合的求法

first集合和follow集合的求法编译原理是计算机专业中的重要学科,其中语法分析是编译原理的基础。

而语法分析器(Parser)的核心就是构建语法分析表格。

而在构建语法分析表格的过程中,first集合和follow集合的求法是一个非常重要的问题,本文就将详细介绍first集合和follow集合的求法。

一、first集合first集合指的是文法中每个非终结符号的经过一次推导得到的所有终结符号的集合,也就是最小前缀(First)的集合。

例如对于一个简单的文法表达式E→E+T|T,其中E和T是非终结符号,+是终极符号。

那么开始寻找E的first集合时,我们应该先判断E能够推导出哪些符号,根据文法表达式,E可以推导出E+T和T。

接着我们可以判断E+T和T 所能推导出的所有终结符号,并将这些终结符号加入到E的first集合中。

具体步骤可以参考下面的推导过程:E → E + TE → TT → a那么最终E的first集合就是{a,+}。

二、follow集合follow集合指的是文法中每个非终结符号在所有推导过程中后跟的符号的集合。

例如对于一个简单的文法表达式E→E+T|T,其中E和T是非终结符号,+是终极符号。

求解E的follow集合时,首先要考虑的是E出现在了哪些地方。

通过分析E在文法表达式中的位置,我们可以发现E出现在了三种不同的情况下:1. E是文法的起始符号,此时E的follow集合中必须包含结束符$。

2. E出现在某些规则的右侧,此时E的follow集合中必须包含右侧的符号的first集合,但是需要注意的是,如果推导出空串,则应该将右侧的非终结符号所在位置的follow集合添加进来。

3. E的右侧是其所在规则的最末尾,此时需要将E所在规则的左侧符号所在位置的follow集合添加到E的follow集合中。

根据以上三种情况,我们可以结合上面的文法表达式来推导出E的follow集合。

具体步骤可以参考下面的推导过程:S → EE → E + TE → TT → a1. $ ∈ follow(E)2. follow(T) = {+, $}follow(E) = first(T) ∪ {+, $}follow(E) = {+, a, $}3. follow(E) = {+, $}那么最终E的follow集合就是{+, a, $}。

求文法的集first 和follow集

求文法的集first 和follow集

#include<iostream>#include"string.h"#define MAX 100using namespace std;//产生式结构体struct product{int rl;char l,r[20];}p[100];//first 和follow 集struct set{int n;//元素数量char elm[100];}first[MAX],follow[MAX];int table[MAX][MAX];//预测分析表char v[100],t[100];//变量和终结符int n,vnum,tnum;//产生式数量,变量数量和终结符数量//判断是否为终结符inline bool isterminal(char x){if(x>='A'&&x<='Z')return false;return true;}//判断符号x是否从未出现过bool ex(char x){int i;if(isterminal(x)){for(i=1;i<=tnum;i++)if(t[i]==x) return true;return false;}for(i=1;i<=vnum;i++)if(v[i]==x) return true;return false;}//读入文法void load(){int i,j,k;char tmp[25];//printf("输入产生式的数量:"); scanf("%d",&n);for(vnum=tnum=0,i=1;i<=n;i++) {scanf("%s",tmp);p[i].l=tmp[0];if(!ex(tmp[0])) v[++vnum]=tmp[0]; for(k=0,j=3;tmp[j];j++){p[i].r[k++]=tmp[j];if(isterminal(tmp[j])){if(!ex(tmp[j]))t[++tnum]=tmp[j];}else if(!ex(tmp[j]))v[++vnum]=tmp[j];}p[i].r[k]=0,p[i].rl=k-1;}t[++tnum]=v[++vnum]='#';}//输出用户输入的文法void show(){int i;for(i=1;i<=n;i++)printf("%c->%s\n",p[i].l,p[i].r);}//把符号x变为对应的编号int cid(char x){int i;if(!isterminal(x)){for(i=1;i<=vnum;i++)if(v[i]==x) return i;}for(i=1;i<=tnum;i++)if(t[i]==x) return i+1000;return -1;}//判断集合st里面是否包含符号idtbool inclu(struct set &st,char idt){int i;for(i=1;i<=st.n;i++)if(st.elm[i]==idt)return true;return false;}//把符号e添加到集合st里面inline void add(struct set &st,char e){st.n++;st.elm[st.n]=e;}//求first集void makefirst(){int i,j,k,idl,idr;bool inc;inc=true;while(inc){inc=false;for(i=1;i<=n;i++) //遍历所有产生式{idl=cid(p[i].l);for(j=0;p[i].r[j];j++){idr=cid(p[i].r[j]);//如果当前为终结符//并且first[idl]中不包含这个终结符就把这个终结符加入first[idl] if(idr>1000){if(!inclu(first[idl],p[i].r[j])){add(first[idl],p[i].r[j]);inc=true;}break;}//否则把该变量的first集里面的元素加入first[idl]else{for(k=1;k<=first[idr].n;k++)if(!inclu(first[idl],first[idr].elm[k])){add(first[idl],first[idr].elm[k]);inc=true;}}//.....if(!inclu(first[idl],'~')) break;}}// 若idl可以转换为空,则‘~’应属于first[idl]if(p[i].r[j]==0&&!inclu(first[idl],'~')){add(first[idl],'~');inc=true;}}}}//输出集合,flag用于表示是first还是followvoid print(struct set *st,int flag){int i,j;char *s;puts("\n");flag==0? s="FIRST":s="FOLLOW";for(i=1;i<=vnum;i++){printf("%s(%c): ",s,v[i]);for(j=1;j<=st[i].n;j++)printf("%c ",st[i].elm[j]);puts("");}}//求follow集void makefollow(){int i,j,k,idl,idr,idf;bool flag,inc=true;add(follow[1],'#');//把结束标志"#"加入起始符的follow集while(inc){inc=false;for(i=1;i<=n;i++)idl=cid(p[i].l);for(flag=true,j=p[i].rl;j>=0;j--){idr=cid(p[i].r[j]);if(idr>1000){flag=false; continue;}if(flag){for(k=1;k<=follow[idl].n;k++){if(!inclu(follow[idr],follow[idl].elm[k])){add(follow[idr],follow[idl].elm[k]);inc=true;}}}if(j<p[i].rl) idf=cid(p[i].r[j+1]);else continue;if(idf>1000){if(!inclu(follow[idr],p[i].r[j+1]))add(follow[idr],p[i].r[j+1]);continue;}for(k=1;k<=first[idf].n;k++){if(!inclu(follow[idr],first[idf].elm[k])&&first[idf].elm[k]!='~') {add(follow[idr],first[idf].elm[k]);inc=true;}}}}}}void maketable(){int i,j,k,idl,idr,idt;char ch;bool flag;memset(table,0,sizeof(table));table[vnum][tnum]=-1;for(i=1;i<=n;i++){idl=cid(p[i].l);for(j=0;j<=p[i].rl;j++){ch=p[i].r[j];idr=cid(ch);if(idr>1000){idr-=1000;if(ch!='~'){if(table[idl][idr]) goto end;table[idl][idr]=i;}else{for(k=1;k<=follow[idl].n;k++){idt=cid(follow[idl].elm[k])-1000;if(table[idl][idt]) goto end;table[idl][idt]=i;}}break;}for(flag=false,k=1;k<=first[idr].n;k++) {idt=cid(first[idr].elm[k])-1000;if(first[idr].elm[k]=='~') flag=true; if(table[idl][idt]) goto end;table[idl][idt]=i;}if(!flag) break;}if(j>p[i].rl){for(k=1;k<=follow[idl].n;k++){idt=cid(follow[idl].elm[k])-1000;if(table[idl][idt]) goto end;table[idl][idt]=i;}}}return;end: printf("It's not a LL(1) language,and if you want to use this program to compile you may get a wrong answer!\n");return;}void compile(char *exp){int i,j,top,idl,idr,t,step[MAX],c=0;char stack[MAX];top=1;t=strlen(exp);stack[0]='#',stack[1]=v[1];exp[t]='#',exp[t+1]=0;for(i=0;;){idl=cid(stack[top]);idr=cid(exp[i])-1000;if(idl>1000) goto fail;t=table[idl][idr];if(t==0) goto fail;step[++c]=t;for(top--,j=p[t].rl;j>=0;j--){if(p[t].r[j]!='~')stack[++top]=p[t].r[j];}while(top>=0&&exp[i]&&stack[top]==exp[i]){if(stack[top]=='#') goto pass;top--,i++;}}pass: printf("Accept!\n");for(i=1;i<=c;i++)printf("%c->%s\n",p[step[i]].l,p[step[i]].r);return;fail: printf("Compile Error!\n");return;}int main(){char exp[MAX];freopen("LL(1).txt","r",stdin); load();show();makefirst();print(first,0);makefollow();print(follow,1);maketable();while(scanf("%s\n",exp)!=EOF) compile(exp);return 0;}。

FIRST FOLLOW SELECT 的求法

FIRST FOLLOW SELECT 的求法

FIRST FOLLOW SELECT 的求法(一)求FIRST(α)的算法(α=x1x2…xn):根据定义计算①根据定义计算由定义4.1 FIRST(α)={a|αaβ,a∈VT,α,β∈V*},若αε,则规定ε∈FIRST(α)对每一文法符号X∈V 计算FIRST(X):(a) 若X∈VT,则FIRST(X)={X}。

(b) 若X∈VN,且有产生式X→a…,a∈VT,则a∈FIRST(X)。

(c) 若X∈VN,X→ε,则ε∈FIRST(X)。

(d) 若X∈VN;Y1,Y2,…,Yi∈VN,且有产生式X→Y1 Y2 … 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)∪{ε}反复使用上述(b)~(e)步直到每个符号的FIRST集合不再增大为止。

求出每个文法符号的FIRST集合后也就不难求出一个符号串的FIRST集合。

若符号串α∈V*,α=X1 X2 … Xn,当X1不能ε,则置FIRST(α)= FIRST(X1)。

若对任何j(1≤j≤i-1,2≤i≤n), ε∈FIRST(Xj), 则FIRST(α)=(FIRST(Xj)\{ε})∪FIRST(Xi)当所有FIRST(Xj)(1≤j≤n)都含有{ε}时,则FIRST(α)=(FIRST(Xj))∪{ε}例4.7:文法G7[S]为:S→ABS→bCA→εA→bB→εB→aDC→ADC→bD→aSD→c解:FIRST(S)={FIRST(A)\{ε}}∪{FIRST(B)\{ε}}∪{ε}∪{b}={b,a,ε} FIRST(A)={b}∪{ε}={ b,ε}FIRST(B)={ε}∪{a}={a,ε}FIRST(C)={FIRST(A) \{ε}}∪FIRST(D)∪FIRST(b)={b,a,c}FIRST(D)={a}∪{c}={a,c}所以最终求得:FIRST(S)={a,b,ε}FIRST(A)={b,ε}FIRST(B)={a,ε}FIRST(C)={a,b,c}FIRST(D)={a,c}每个产生式的右部符号串的开始符号集合为:FIRST(AB)={a,b,ε}FIRST(bC)={b}FIRST(ε)={ε}FIRST(b)={b}FIRST(aD)={a}FIRST(AD)={a,b,c}FIRST(b)={b}FIRST(aS)={a}FIRST(c)={c}或:1.若n=0,即α=ε,则令FIRST(α)={ε};2.否则,对1≤i≤n,求FIRST(xi)3.若n=1,则令FIRST(α)=FIRST(x1);4.若n≥2且对一切j=1,2,…,i-1都有ε∈FIRST(xj),则令FIRST(xi )-{ε} FIRST(α),其中2≤i≤n;若对一切j=1,2,…,n都有ε∈FIRST(xj),则令ε∈FIRST(α)。

C++求文法的first和follow集合

C++求文法的first和follow集合

1.#include "stdafx.h"2.#include "LR.h"3.#include "MLR1.h"4.5.#ifdef _DEBUG6.#undef THIS_FILE7.static char THIS_FILE[]=__FILE__;8.#define new DEBUG_NEW9.#endif10.//----调试部分使用的代码11.CString MLR1::GetFirst(int i){12.if(i<0||i>=GetIdentNum())return"";13.return FirstSet5(m_first[i].Fi,m_first[i].flag&2);14.}15.CString MLR1::GetFollow(int i){16.if(i<0||i>=GetIdentNum())return"";17.return FollowSet1(m_first[i].Fo,m_first[i].flag&0x08);18.}19.//----构造部分20.MLR1::MLR1(){21.}22.MLR1::~MLR1(){23.}24.void MLR1::ReSet(FILE* pf){25.//使用文件指针pf来重新驱动程序26.int i;27. p_file=pf;28. list_Express.RemoveAll();29. list_Ident.RemoveAll();30.for(i=0;i<MAP_SIZE;I++) p="(char*)m_first+sizeof(s_first)*MAX_IDENT-1;"for(char* bit_map[i]="0;">=(char*)m_first;p--)31. *p=0;32. Lex3();33. FirstSet6();34.// FollowSet3();35.}36.//----输入分析部分37.bool MLR1::Lex1(){38.//截取一个分号段到tocken中39.//功能字符取其负数40.char ch=0;41.bool end=false;42. token_len=0;43.if(feof(p_file))return false;44.while(!end&&!feof(p_file)){45.if(token_len>=LINE_LENGTH)break;46.if(fread(&ch,1,1,p_file)<=0)break;47.if(ch<=0)goto error;48.switch(ch){49.case';':50. end=true;51.case'<':52.case'>':53.case'=':54. ch=-ch;55.break;56.case'\\':57. fread(&ch,1,1,p_file);58.if(ch<=0)goto error;59.break;60. }61. token[token_len++]=ch;62. }63. token[token_len]=0;64.return true;65.error:66. fprintf(stderr,"must be 1--127");67.return false;68.}69.int MLR1::Lex2_1(char*&s,bool isUse){70.//识别非终结符并加入list_Ident71.char ident[ID_LENGTH+1];72.int t=0;73.if((int)*s++!=-'<')return 0;74.if(isalpha(*s))ident[t++]=*s++;75.else return 0;76.while(isalpha(*s)||isdigit(*s))ident[t++]=*s++;77.while(*s=='\'')ident[t++]=*s++;78.if((int)*s++!=-'>')return 0;79.if(t==0)return 0;80. ident[t]=0;81.for(t=list_Ident.GetSize()-1;t>=0;t--)82.if(list_Ident[t]==(CString)ident)break;83.if(t<0){84.if(list_Ident.GetSize()>=MAX_IDENT)return false;85. list_Ident.Add((CString)ident);86. t=list_Ident.GetSize()-1;87.if(isUse)bit_map[t/8]|=1<<(t%8);88. }89.if(!isUse)bit_map[t/8]&=~(1<<(t%8));90.return t+1;91.}92.bool MLR1::Lex2(){93.//将token中的非终结符用(-1) -- (-127)表示94.//进行语法判断<终结符>=符号表;95.register char *s,*d;96.char * end;97.int i;98. s=d=token;99. end=&token[token_len];100.if(i=Lex2_1(s))*d++=-i;101.else return false;102.if(*s++!=-'=')return false;103.while(s<END){ if(*p while(*p!="0){" *p="X+1;"char const判断表达式X能否推出LR_NULL *X){ MLR1::FirstSet1(const bool * 首先判断某非终结符能否推出LR_NULL ----First集 } true; return false; if(bit_map[i]!="0)return" i="0;i<MAP_SIZE;i++ )"for(int else list_Express.Add((CString)token); if(Lex2()) if(token_ len="=0)continue;"while(Lex1()){ 判断bit_map是否为全零,如果不是则表示有未定义的非终结符循环调用Lex1读入一句,调用Lex2进行语法分析 MLR1::Lex3(){ s<end; *d="0;" *d++="*s++;" }else if(i="Lex2_1(s,true ))*d++=-i;"if((int)*s="=-'<'){"if(*s="=-';')break;">=1){104.return false;105. }else{106.if(*p==*X)return false;107.if(!FirstSet2(*p))108.return false;109. }110. p++;111. }112.return true;113.}114.bool MLR1::FirstSet2(const char X){115.//判断非终结符X能否推出LR_NULL116. CString temp;117.if(m_first[-X-1].flag&0x40)return false;118.if(m_first[-X-1].flag&1)119.return (m_first[-X-1].flag&2)!=0;120. m_first[-X-1].flag|=0x40;121.for(int i=list_Express.GetSize();i>0;i--){122. temp=list_Express.GetAt(i-1);123.if(temp[0]==X){124.if(FirstSet1((LPCSTR)temp)){125. m_first[-X-1].flag|=3;126.return true;127. }128. }129. }130. m_first[-X-1].flag|=1;131. m_first[-X-1].flag^=0x40;132.return false;133.}134.bool MLR1::FirstSet3(const char *X,char*Fi){135.//求产生式X的First集放在F中,如果LR_NULL在First集中则返回true 136.//如果要求符号串的First集,就将X[0]设为0137.//假设X中不出现LR_NULL,LR_EOF和LR_EOS138.//假设F的长度为MAP_SIZE,有128b139.const char *p=X;140. X++;141.while(*X!=0){142.if(*X>=1){143. Fi[(*X)/8]|=1<<(*X)%8;144.return false;145. }else{146.if(*X==*p){147.if(!FirstSet2(*X))148.return false;149. }else if(!FirstSet4(*X,Fi)){150.return false;151. }152. }153. X++;154. }155.return true;156.}157.bool MLR1::FirstSet4(char const X,char*Fi){158.//求非终结符X的First集放在F中159.//如果LR_NULL在其中则返回true160. CString temp;161.if(m_first[-X-1].flag&0x40)return false;162.if((m_first[-X-1].flag&4)==0){163. m_first[-X-1].flag|=0x40;164.for(int i=list_Express.GetSize();i>0;i--){165. temp=list_Express.GetAt(i-1);166.if(temp[0]==X)167. FirstSet3((LPCSTR)temp,m_first[-X-1].Fi);168. }169. m_first[-X-1].flag|=4;170. m_first[-X-1].flag^=0x40;171. }172.if(Fi!=m_first[-X-1].Fi){173.for(int i=0;i<MAP_SIZE;I++) *p="t;"char } return for(i="0;i< MAP_SIZE;i++){" i; int为每个非终结符求First集 MLR1::FirstSet6(){ void (CString)t; if(has_null)*p++="LR_NULL;" *p+ +="i*8+j;"if(Fi[i]&(1<<j)) if(Fi[i])for(j="0;j<8;j++)" i,j; t[128];将集合表示的First变为字符串式 has_null){ char*Fi,bool MLR1::FirstSet5(const CString (m_first[-X-1 ].flag&2); Fi[i]|="m_first[-X-1].Fi[i];">0;i--){174.if((m_first[i-1].flag&1)==0)175. FirstSet2(-i);176. }177.for(i=list_Ident.GetSize();i>0;i--){178.if((m_first[i-1].flag&4)==0)179. FirstSet4(-i,m_first[i-1].Fi);180. }181.}182.CString MLR1::FollowSet1(const char*Fo,bool has_eof){183.//将集合表示的Follow变为字符串式184.char t[128];185.char *p=t;186.int i,j;187.for(i=0;i<MAP_SIZE;I++){ *p="0;"char bool * } return int (CString)t; *p++="i*8+j;" i,j; if(X 如果是非法符号则退出 *p; temp[LINE_LENGTH]; flag,rc="true;" flag.b5该符号的Follow集正在被计算 flag为真表示已经找到X,将找后继的第一个字符必须在执行前将LR_EOF加入识别符号的Follow集中只有在X的Follow集未被计算时才调用该函数求非终结符X的Follow集 Fo){ X,char MLR1::FollowSet2(const if(has_eof)*p++="LR_NULL;"if(Fo[i]&(1<<j)) if(Fo[i])for(j="0;j<8;j++)">=0||X<-MAX_IDENT)return true;188.//如果该符号正被计算则退出189.if(m_first[-X-1].flag&0x20)return false;190.//如果该符号为被计算则计算191.if((m_first[-X-1].flag&0x10)==0){192. m_first[-X-1].flag|=0x20;193.for(i=list_Express.GetSize();i>0;i--){194. sprintf(temp,list_Express.GetAt(i-1));195. flag=false;196. p=temp+1;197.while(*p!=0){198.if(!flag){199.if(*p==X){200.//表达式中出现了符号X201. flag=true;}202. }else{203.if(*p>0){204.//规则2:X后碰上终结符205. flag=false;206. Fo[*p/8]|=1<<(*p%8);207. }else{208.//规则2:X后碰上非终结符则并上它的First集209.for(j=0;j<MAP_SIZE;J++) } return for(i="0;i<M AP_SIZE;i++)" i; int void m_first[0].flag|="0x08;"求各非终结符的Follow 集 MLR1::FollowSet3(){ rc; Fo[i]|="m_first[-X-1].Fo[i];"if(Fo!="m_fir st[-X-1].Fo){" m_first[-X-1].flag^="0x20;"if(rc)m_first[-X-1].flag|="0x10;" m_first[-X-1].flag|="0x08;"if(m_first[-temp[0]-1].flag&0x08) F o[j]|="m_first[-*p-1].Fi[j];"for(j="0;j<MAP_SIZE;j++)" rc="false;"if (!FollowSet2(temp[0],m_first[-temp[0]-1].Fo)) 如果是自反关系则不计算规则3:将temp[0]的Follow集并到Fo上if(flag&&(X!="temp[0])){" p++; flag="false;"if((m_first[-*p-1].fla g&2)="=0)"如果X可以推出LR_NULL则继续>0;i--){210.if((m_first[i-1].flag&0x10)==0){211. FollowSet2(-i,m_first[i-1].Fo);212. }213. }214.}。

LL1语法分析,first集,follow集,分析表Java实现

LL1语法分析,first集,follow集,分析表Java实现

import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.table.DefaultTableModel;import java.sql.*;import java.util.Vector;public class LL1 extends JFrame implements ActionListener { /****/private static final long serialVersionUID = 1L;JTextField tf1;JTextField tf2;JLabel l;JButton b0;JPanel p1, p2, p3;JTextArea t1, t2, t3;JButton b1, b2, b3;JLabel l0, l1, l2, l3, l4;JTable table;Statement sta;Connection conn;ResultSet rs;DefaultTableModel dtm;String Vn[] = null;Vector<String> P = null;int firstComplete[] = null;// 存储已判断过first的数据char first[][] = null;// 存储最后first结果int followComplete[] = null;// 存储已判断过follow的数据char follow[][] = null;// 存储最后follow结果char select[][] = null;// 存储最后select结果int LL = 0;// 标记是否为LL(1)String vt_tou[] = null;// 储存VtObject shuju[][] = null;// 存储表达式数据char yn_null[] = null;// 存储能否推出空LL1() {setLocation(100, 0);setSize(700, 780);tf1 = new JTextField(13);tf2 = new JTextField(13);l = new JLabel(">>");l0 = new JLabel("输入字符串:");l1 = new JLabel("输入的文法为:");l2 = new JLabel(" ");l3 = new JLabel("分析的结果:");l4 = new JLabel("预测分析表:");// p1=new JPanel();p2 = new JPanel();p3 = new JPanel();t1 = new JTextArea(24, 20);t2 = new JTextArea(1, 30);t3 = new JTextArea(24, 40);b0 = new JButton("确定(S为开始)");b1 = new JButton(" 判断文法 ");b2 = new JButton("输入");b3 = new JButton("清空");table = new JTable();JScrollPane jp1 = new JScrollPane(t1);JScrollPane jp2 = new JScrollPane(t2);JScrollPane jp3 = new JScrollPane(t3);p2.add(tf1);p2.add(l);p2.add(tf2);p2.add(b0);p2.add(b1);p2.add(l0);p2.add(l2);p2.add(jp2);p2.add(b2);p2.add(b3);p2.add(l1);p2.add(l3);p2.add(jp1);p2.add(jp3);p3.add(l4);p3.add(new JScrollPane(table));add(p2, "Center");add(p3, "South");b0.addActionListener(this);b1.addActionListener(this);b2.addActionListener(this);b3.addActionListener(this);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);table.setPreferredScrollableViewportSize(new Dimension(660, 200));setVisible(true);}////////////////////界面设计public void actionPerformed(ActionEvent e) {if (e.getSource() == b0) {String a = tf1.getText();String b = tf2.getText();t1.append(a + '→' + b + '\n');}if (e.getSource() == b1) {t3.setText("");int Vnnum = 0, k;Vn = new String[100];P = new Vector<String>();String s[] = t1.getText().split("\n");for (int i = 0; i < s.length; i++) {if (s.length < 2) {t3.setText("文法输入有误,请重新输入");// 判断长度是否符合return;}if (s[i].charAt(0) <= 'Z' && s[i].charAt(0) >= 'A'&& s[i].charAt(1) == '→') {for (k = 0; k < Vnnum; k++) {if (Vn[k].equals(s[i].substring(0, 1))) {break;}}if (Vnnum == 0 || k >= Vnnum) {Vn[Vnnum] = s[i].substring(0, 1);// 存入Vn数据Vnnum++;}P.add(s[i]);} else {t3.setText("文法输入有误,请重新输入");return;}}yn_null = new char[100];first = new char[Vnnum][100];int flag = 0;String firstVn[] = null;firstComplete = new int[Vnnum];for (int i = 0; Vn[i] != null; i++) // 依次求 FIRST**{flag = 0;firstVn = new String[20];if ((flag = add_First(first[i], Vn[i], firstVn, flag)) == -1) return;firstComplete[i] = 1;}t3.append("first集:" + "\n"); // 显示FIRST**for (int i = 0; Vn[i] != null; i++) {t3.append("first(" + Vn[i] + ")={ ");for (int j = 0; first[i][j] != '\0'; j++) {t3.append(first[i][j] + " , ");}t3.append("}" + "\n");}follow = new char[Vnnum][100];String followVn[] = null;followComplete = new int[Vnnum];for (int i = 0; Vn[i] != null; i++) // 求FOLLOW**{flag = 0;followVn = new String[20];if(i==0){}else if ((flag = tianjiaFollow(follow[i], Vn[i], followVn, flag)) == -1)return;followComplete[i] = 1;}t3.append("follow集:" + "\n"); // 显示FOLLOW**for (int i = 0; Vn[i] != null; i++) {t3.append("follow(" + Vn[i] + ")={ ");for (int j = 0; follow[i][j] != '\0'; j++) {t3.append(follow[i][j] + " , ");}t3.append("}" + "\n");}select = new char[P.size()][100];for (int i = 0; i < P.size(); i++) // 求SELECT**{flag = 0;tianjiaSelect(select[i], (String) P.elementAt(i), flag);}t3.append("select集:" + "\n"); // 显示SELECT**for (int i = 0; i < P.size(); i++) {t3.append("select(" + (String) P.elementAt(i) + ")={ ");for (int j = 0; select[i][j] != '\0'; j++) {t3.append(select[i][j] + " , ");}t3.append("}" + "\n");}for (int i = 0; Vn[i] != null; i++)// 判断select交集是否为空{int biaozhi = 0;char save[] = new char[100];for (int j = 0; j < P.size(); j++) {String t = (String) P.elementAt(j);if (t.substring(0, 1).equals(Vn[i])) {for (k = 0; select[j][k] != '\0'; k++) {if (puanduanChar(save, select[j][k])) {save[biaozhi] = select[j][k];biaozhi++;} else// 当有交集时,不为LL(1)文法{t3.append("不是LL(1)文法!!" + "\n");return;}}}}}char Vt[] = new char[100];int biaozhi = 0;for (int i = 0; i < P.size(); i++) {String t = (String) P.elementAt(i);for (int j = 2; j < t.length(); j++)// 提取表达式右侧的终结符存入Vt{if (t.charAt(j) > 'Z' || t.charAt(j) < 'A') {if (puanduanChar(Vt, t.charAt(j))) {Vt[biaozhi] = t.charAt(j);biaozhi++;}}}}if (puanduanChar(Vt, '#'))// 若可推出空集,则将#加入Vt。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
(3) 输出First集;
(4)输出由文法G构造FOLLOW集的算法;
(5)输出FOLLOW集。
2.测试数据:
输入文法G[E]:
E → TE’
E’ → +TE’|ε
T → FT’
T’ → *FT’|ε
F->(E)|i
3.实现提示:
用数据库存储多行文法,用LIST控件显示算法,用GRID类依据算法进行作图。并实现算法与生成过程的关联。
{
p[i].left=strings.substr(0,j);
p[i].right=strings.substr(j+2,strings.length()-j);
}
}
}
//对每个文法符号求first集
string Letter_First(STR *p,char ch)
{
int t;
if(!(Vt.find(ch)>100))
FIRST(α)={a | α aβ,a∈VT,α,β∈V*}。
若α ε,ε∈FIRST(α)。
由定义可以看出,FIRST(α)是指符号串α能够推导出的所有符号串中处于串首的终结符号组成的集合。所以FIRST集也称为首符号集。
设α=x1x2…xn,FIRST(α)可按下列方法求得:
令FIRST(α)=Φ,i=1;
Vt +=p[i].left[j];
}
}
for(j=0;j<(int)p[i].right.length();j++)
{
if(!(p[i].right[j]>='A'&&p[i].right[j]<='Z'))
{
if(Vt.find(p[i].right[j])>100)
Vt +=p[i].right[j];
{
if(First[Vn.find(ch)].find(p[i].right[0])>100)
{
First[Vn.find(ch)]+=p[i].right[0];
}
}
if(p[i].right[0]=='*')
{
if(First[Vn.find(ch)].find('*')>100)
{
First[Vn.find(ch)]+='*';
(5)论文撰写(20分):优( )、良( )、中( )、一般( )、差( );
评阅人:职称:讲师
2015年6月19日
中文摘要
随着计算机科学的飞速发展,形式语言与自动机理论和方法研究也越来越收到人们的重视,但前者已经成为计算机科学的理论基础。此次的课程设计主要任务是研究自动机在编译方面的应用,并将重点放在求FIRST集和FOLLOW集。
课程设计(论文)任务书
软件学院学 院软件测试专 业1班
一、课程设计(论文)题目first集和follow集生成算法模拟
二、课程设计(论文)工作自2015年6月16日起至2013年6月19日止。
三、课程设计(论文)地点:软 件 学 院 实 训 中 心
四、课程设计(论文)内容要求:
1.本课程设计的目的
进一步培养学生编译器设计的思想,加深对编译原理和应用程序的理解,针对编
(3)若),其中x,y∈V*,则FOLLOW(B)∈FOLLOW(A);
3.实验内容:计算FIRST集和FOLLOW集
4.
二、需求分析
1.基本要求:
动态模拟算法的基本功能是:
(1)输入一个文法G;
(2)输出由文法G构造FIRST集的算法;
(2)输入由文法G构造First集的算法
(3)输出First集
(4)输入由文法G构造Follow集的算法
(5)输出Follow集
2.实验目的:
输入:任意的上下文无关文法。
输出:所输入的上下文无关文法一切非终结符的first集合和follow集合。
3.设文法G[S]=(VN,VT,P,S),则首字符集为:
若S …A,#∈FOLLOW(A)。
由定义可以看出,FOLLOW(A)是指在文法G[S]的所有句型中,紧跟在非终结符A后的终结符号的集合。
FOLLOW集可按下列方法求得:
(1)对于文法G[S]的开始符号S,有#∈FOLLOW(S);
(2)若文法G[S]中有形如B→xAy的规则,其中x,y∈V*,则FIRST(y)-{ε}∈FOLLOW(A);
{
first[Vt.find(ch)]="ch";
return first[Vt.find(ch)-1];
}
if(!(Vn.find(ch)>100))
{
for(int i=0;i<N;i++)
{
if(p[i].left[0]==ch)
{
if(!(Vt.find(p[i].right[0])>100))
}
else
{
if(Vn.find(p[i].right[j])>100)
Vn+=p[i].right[j];
}
}
}
}
void getlr(STR *p,int i)
{
int j;
for(j=0;j<strings.length();j++)
{
if(strings[j]=='-'&&strings[j+1]=='>')
高的问题。最后我想到了用递归方式。
下面总结此次课程设计的一些收获:
1.对程序设计理解,算法的设计,有了进一不的提高。
2.对程序调试的技巧收获不小。因为该程序主要是算法研究,所以程序分支较复杂。断点调试是必不可缺并且很实用的工作。
3.对程序到软件过程的理解。这次也是我第一次将自己做的程序制作成一个可自定义安装过程的小软件。从而将程序的运行与IDE脱离开来。
关键字:FIRST集,FOLLOW集,算法。
目录
八、附录.......................................11
一、课程设计任务及要求
1.任务:设计一个由正规文法生成First集和Follow集并进行简化的算法动态模拟。
First集和Follow集生成模拟算法的基本功能:
(1)输入一个文法G
三、设计思路
1.识别终结符集和非终结符集
Y
N
识别终结符集
识别非终结符集
2.计算所有非终结符的First集
N
Y
Y
N
3.计算所有非终结符的Follow集
N
Y N
Y

四、详细设计
2.具体设计
通过分析输入的文法,分析出文法肿的非终结符和终结符,然后计算出每个非终结符的FIRST集和FOLLOW集。
当一个文法中存在ε产生式时,例如,存在A→ε,只有知道哪些符号可以合法地出现在非终结符A之后,才能知道是否选择A→ε产生式。这些合法地出现在非终结符A之后的符号组成的集合被称为FOLLOW集合。下面我们给出文法的FOLLOW集的定义。
设文法G[S]=(VN,VT,P,S),则
FOLLOW(A)={a | S … Aa …,a∈VT}。
2)创新要求:
动态模拟算法的基本功能是:
(1)输入一个文法G
(2)输出由文法G构造的FIRST集算法
(3)输出FIRST算法
(4)输出由文法G构造的FOLLOW集算法
(5)输出FOLLOW集
3)课程设计论文编写要求
(1)课程设计任务及要求
(2)设计思路--工作原理、功能规划
(3)详细设计---数据分析、算法思路、功能实现(含程序流程图、主要代码及注
释)、界面等。
(4)运行调试与分析讨论---给出运行屏幕截图,分析运行结果,有何改进想法等。
(5)设计体会与小结---设计遇到的问题及解决办法,通过设计学到了哪些新知识,
巩固了哪些知识,有哪些提高。
(6)报告按规定排版打印,要求装订平整,否则要求返工;
(7)课设报告的装订顺序如下:封面---任务书---中文摘要---目录----正文---附录
(1)若xi∈VT,则xi∈FIRST(α);
(2)若xi∈VN;
① 若ε FIRST(xi),则FIRST(xi)∈FIRST(α);
② 若ε∈FIRST(xi),则FIRST(xi)-{ε}∈FIRST(α);
(3)i=i+1,重复(1)、(2),直到xi∈VT,(i=2,3,…,n)或xi∈VN且若ε FIRST(xi)或i>n为止。
(代码及相关图片)
(8)严禁抄袭,如有发现,按不及格处理。
4)课程设计评分标准:
(1)学习态度:20分;
(2)系统设计:20分;
(3)编程调试:20分;
(4)回答问题:20分;
(5)论文撰写:20分。
5)参考文献:
(1)张素琴,吕映芝.编译原理[M].,清华大学出版社
(2)蒋立源、康慕宁等,编译原理(第2版)[M],西安:西北工业大学出版社
}
}
if(!(Vn.find(p[i].right[0])>100))
{
if(p[i].right.length()==1)
{
string ff;
ff=Letter_First(p,p[i].right[0]);
for(int i_i=0;i_i<ff.length();i_i++)
{
for(j=0;j<(int)p[i].left.length();j++)
相关文档
最新文档