编译原理
大作业
题 目: LL(1)文法预测分析表的实现
班 级:
计算机科学与技术学院
2019年
、、
目录
1目的12要求13分工14设计15代码46程序运行截图14
目的
通过本次大作业,使学生对自顶向下语法分析的基本概念、基本原理、基本方法等充分理解并掌握预测分析方法。
要求
- 给定无左递归和无回溯的文法,计算FIRST集合和FOLLOW集合,并构造出预测分析表。
文法如下:(在这里用e来表示空串ε)
E->TA A->+TA A->e T->FB
B->*FB B->e F->(E) F->i - 根据预测分析表,判断该文法是否为LL(1)文法。
分工
本次大作业以5-6人为一组,各组成员按照实际完成情况填写下表
求出能推出ε的非终结符 | 计算FIRST集 | 计算FOLLOW集 | 计算SELECT集 | 构造预测分析表 | 判断LL(1)文法 | |
负责人 |
设计
对给定的无左递归和无回溯的上下文无关文法,按以下步骤执行(每个步骤写出具体算法):
- 初始化文法
从文档中读取文法,然后用结构体把文法存储,非终结符通常用大写字母表示,所以通常有26个,为了后期的遍历方便,所以选用哈希表进行非终结符的存储:
Item** pGram = (Item**)malloc(sizeof(Item*)*26);//用于存储文法
typedef struct item //用于记录非终结符推出的项
{
char val[20];//非终结符推出的内容
item* pItemNext;
}Item;
如图:
- 求出能推出ε的非终结符
如果A->B,B->e 时,则A->e;
所以求推出ε的非终结符需要依赖其他非终结符,所以采用递归算法。 - 构造FOLLOW集合算法
3.1FOLLOW求法
①S为开始符号,则加入FOLLOW(S)
②则 的非空元素加入FOLLOW(B)中
若则将加入FOLLOW(B)中。
3.2FOLLOW集结构
FOLLOW集结构是一个包含26个大写英文字母的指针数组,每一个数组元素为每个非终结符的头指针。FOLLOW集由链表构成。即邻接表形式。由于用到开始符号,所以将文法产生式结构的第26个元素用于求开始符号。
3.3构造FOLLOW集的算法。
构造FOLLOW用到了两个函数GetFollow,和Follow以及ischangefollow数组。其中GetFollow为总入口创建Follow集以及初始化,调用Follow求每一个非终结符的Follow集。ischangfollow判断是否此次求Follow是否有新的添加项,有则再求一次。Follow经过一轮无更新后,求取完毕。
①GetFollow:
Node** GetFollow(Item**pGramCon ,char *pNULLKey,Node** pFirst)
这个函数是获得所有非终结符的FOLLOW集,要用到文法pGramCon,非终结符能否走到空pPULLKEY,以及First集pFirst。
此函数的流程图如下所示:
②Follow:
Node* Follow(char key, Item**pGramCon,char *pNULLKey,Node** pFirst,Node** pFollow)
这个是得到单个非终结符的Follow集,并维护ischangefollow数组。
- 构造SELECT集合算法
4.1Select求法
①若经过零步或多步推不出则产生式的select集为产生式的右部的first集合。
②能推出则select集为产生式右部非空并上follow产生式左部非终结符集。
4.2Select集结构
由于是对每个产生式求Select集,所以之前构造First集和Follow集的结构不适用。所以我在文法处添加了valnum标记每个产生式的位置。用全局变量GenCount记录产生式的个数。依旧为邻接表形式结构,但每个元素头代表产生式的valnum标记。即第0号元素为第0号产生式的Select集开头。
4.3Select求法
用到了两个函数,GetSelect为总入口,Select为求每个产生式的Select集。GetSelect函数就是遍历文法对每个产生式调用Select函数。Select函数的流程图如下所示:
- 构造预测分析表M
5.1预测分析表的结构
由于不知是否文法是LL1文法,故无法确定预测分析表每一个元素仅为一个产生式。故将预测分析表构造成二维指针数组形式。每一个元素为此点符合要求的产生式的链表头部指针。
5.2预测分析表的构造
大概方法就是遍历Select集,对预测分析表进行赋值。
5.3预测分析表的输出
其中产生式前的零代表,此处符合要求的产生式为一。假若产生式不唯一,避免显示效果的不美观,仅显示第一个产生式。
- 判断是否为LL(1)文法
6.1判断方法
判断左部相同产生式的select集合是否有交集不为空,有则不是LL(1)文法。
6.2判断方法实现
select集合形象的表示在了预测分析表中,通过第五步够造的预测分析表即可判断是否为LL(1)文法。判断方法为如果预测分析表节点所表示的链表有两个产生式的结点。即交集不为空,就不是LL(1)文法。
代码
- 初始化文法的主要代码
Item** Init()//初始化文法--------从文档中读取
{
FILE* file = fopen("Grammer_Content.txt","r");
if(!file)
{
printf("文件打开失败!!!\n");
exit(-1);
}
Item** pGram = (Item**)malloc(sizeof(Item*)*26);//用于存储文法
memset(pGram,0,sizeof(Item*)*26);
char key;
char val[20] = {0};
while((fscanf(file,"%c %s",&key,val)) != EOF)
{
Item*pTemp = pGram[key-'A'];
Item* pMark = (Item*)malloc(sizeof(Item));
Node* pNode = NULL;
Node* p = NULL;
memset(pMark,0,sizeof(Item));
//找到项的放置位置
if(pTemp == NULL)
{
pGram[key-'A'] = pMark;
}
else
{
while(pTemp->pItemNext)
{
pTemp = pTemp->pItemNext;
}
pTemp->pItemNext = pMark;
}
sprintf(pMark->val,"%s",val);
fgetc(file);
}
return pGram;
} - 求出能推出ε的非终结符
int IsToNULL(char key,Item** pGramCon)//判断非终结符能否直接推出空(‘e’)
{
if(key > 'Z' || key < 'A' ||pGramCon[key-'A'] == NULL) return 0;
Item* pItem = pGramCon[key-'A'];
while(pItem)
{
if(pItem->val[0] == 'e')
return 1;
pItem = pItem->pItemNext;
}
return 0;
}
int IsStrToNULL(char c,char*pNullArr,Item** pGramCon)//判断非终结符能否经多次推到-》空(‘e’)
{
if(pGramCon[c-'A'] == NULL)return 0;
if(pNullArr[c-'A'] != 0) return 1;
Item *pItem = pGramCon[c-'A'];
int i;
while(pItem)
{
for(i = 0; i<strlen(pItem->val); i++ )
//终结符
if(!(pItem->val[i] >= 'A'&& pItem->val[i] <= 'Z') && pItem->val[i]!= 'e')
break;
//str[i]为非终结符,是否能推出e;
else if(IsStrToNULL(pItem->val[i],pNullArr,pGramCon) == 0)
{
break;
}
if(i == strlen(pItem->val))
return 1;
pItem = pItem->pItemNext;
}
return 0;
}
3.求解FIRST(X)代码:
void InsertFirst(char key,Node** pFirst) //把终结符插入first集中
{
Node *pTemp = (Node*)malloc(sizeof(pTemp));
pTemp->m_key = key;
pTemp->pNext = NULL;
if(*pFirst == NULL)
{
*pFirst = pTemp;
return;
}
Node* pMark = *pFirst;
while( pMark->m_key != key && pMark->pNext)
{
pMark = pMark->pNext;
}
if(pMark->m_key == key)
{
free(pTemp);
pTemp == NULL;
return;
}
pMark->pNext = pTemp;
}
Node* First(char key, Item**pGramCon,char *pNULLKey,Node** pFirst) //递归得到非终结符的FIRST集
{
if(pFirst[key-'A'] != NULL) return pFirst[key-'A'];
Item* pItem = pGramCon[key-'A'];
Node* pMark = NULL;
while(pItem)
{
for(int i =0 ; i<strlen(pItem->val); i++)
{
pMark = NULL;
//如果为非终结符
if(pItem->val[i] >= 'A' && pItem->val[i] <= 'Z')
{
pMark = First(pItem->val[i],pGramCon,pNULLKey,pFirst);
if(pMark != NULL)
{
while(pMark)
{
InsertFirst(pMark->m_key,&pFirst[key-'A']);
pMark = pMark->pNext;
}
}
//如果能推出e(空)
if(pNULLKey[pItem->val[i]-'A']!=0)
continue;
else
break;
}
//为终结符且非空
else if(!(pItem->val[i] >= 'A' && pItem->val[i] <= 'Z') && pItem->val[i] != 'e')
{
InsertFirst(pItem->val[i],&pFirst[key-'A']);
break;
}
}
pItem = pItem->pItemNext;
}
if(pNULLKey[key-'A'] != 0)
{
InsertFirst('e',&pFirst[key-'A']);
}
return pFirst[key-'A'];
}
Node** GetFirst(Item**pGramCon ,char *pNULLKey) //得到所有非终结符的FIRST集
{
Node** pFirst = (Node**)malloc(sizeof(Node*)*26); //用于存储非终结符的FIRST集
memset(pFirst,0,sizeof(Node*)*26);
Node* pTemp = NULL;
Node* pMark = NULL;
char c;
for(int i = 0; i<26; i++)
{
if(pGramCon[i] == NULL)
continue;
if(pFirst[i] == NULL)
First(i+'A',pGramCon,pNULLKey,pFirst);
}
return pFirst;
}
void PrintFirst(Node** pFirst) //打印FIRST集
{
Node* pNode;
for(int i= 0; i< 26; i++)
{
if(pFirst[i] != NULL)
{
printf("First(%c)={",i+'A');
pNode = pFirst[i];
while(pNode->pNext)
{
printf("%c,",pNode->m_key);
pNode = pNode->pNext;
}
printf("%c",pNode->m_key);
printf("}\n");
}
} - 求Follow集主要代码
bool ischangefollow[26]={0};
Node* Follow(char key, Item**pGramCon,char *pNULLKey,Node** pFirst,Node** pFollow) //得到非终结符的Follow集
{
Item* pItem= NULL;
for(int i=0;i<26;i++){
if(pGramCon[i]!=NULL){
pItem = pGramCon[i];
while (pItem!=NULL)
{
for(int j=strlen(pItem->val)-1;j>=0;j--){
if(pItem->val[j]==key){
bool isexec = false;
if(isupper(pItem->val[j+1])){
if(insertFirst_To_Follow(pFirst[pItem->val[j+1]-'A'],pFollow[key-'A'])){
ischangefollow[key-'A']=true;
}
if(pNULLKey[pItem->val[j+1]-'A']!=0){
isexec=true;
}
}else if(pItem->val[j+1]=='\0'){
isexec=true;
}else{
if(InsertNode(pItem->val[j+1],&pFollow[key-'A'])){
ischangefollow[key-'A']=true;
}
}
if(isexec){
if(i+'A'==key)continue;
Node *pNode =pFollow[i];
while(pNode!=NULL){
if(InsertNode(pNode->m_key,&pFollow[key-'A'])){
ischangefollow[key-'A']=true;
}
pNode=pNode->pNext;
}
}
}
}
pItem=pItem->pItemNext;
}
}
}
return pFollow[key-'A'];
}
Node** GetFollow(Item**pGramCon ,char *pNULLKey,Node** pFirst) //获得所有非终结符的FOLLOW
{
Node** pFollow = (Node**)malloc(sizeof(Node*)*26); //用于存储非终结符的FOLLOW集
memset(pFollow,0,sizeof(Node*)*26);
InsertNode('#',&pFollow[pGramCon[26]->val[0]-'A']);//#加入开始符号的Follow集中
bool ischange = true;
while (ischange)
{
for(int i=0;i<26;i++){
if(pGramCon[i]!=NULL)
Follow(i+'A',pGramCon,pNULLKey,pFirst,pFollow);
}
ischange=false;
for(int i=0;i<26;i++){
if(ischangefollow[i]){
ischangefollow[i]=false;
ischange=true;
}
}
}
return pFollow;
} - 求Select集主要代码
void Select(char key,Item* pKey /*产生式索引*/, Item**pGramCon,char *pNULLKey,Node** pFirst,Node** pFollow,Node** pSelect)
{
bool can_To_e=true;
int len =strlen(pKey->val) ;
for(int i=0;i<len && can_To_e;i++){
if(pKey->val[i]!='e'){
if(isupper(pKey->val[i])&&pNULLKey[pKey->val[i]-'A']==0)can_To_e=false;
if(!isupper(pKey->val[i]))can_To_e=false;
}
}
for(int i=0;i<len;i++){
if(pKey->val[i]!='e' && !isupper(pKey->val[i])){//是终结符
InsertNode(pKey->val[i],&pSelect[pKey->valnum]);
break;
}else if(isupper(pKey->val[i])){//是非终结符
Node *pNode = pFirst[pKey->val[i]-'A'];
while (pNode)
{
if(pNode->m_key!='e')
InsertNode(pNode->m_key,&pSelect[pKey->valnum]);
pNode=pNode->pNext;
}
break;
}
}
if(can_To_e){//产生式右部能推出空,加入产生式左部
Node* pNode=pFollow[key-'A'];
while (pNode)
{
InsertNode(pNode->m_key,&pSelect[pKey->valnum]);
pNode=pNode->pNext;
}
}
}
Node** GetSelect(Item**pGramCon ,char *pNULLKey,Node** pFirst,Node** pFollow) //获得所有非终结符的Select
{
Node** pSelect = (Node**)malloc(sizeof(Node*)*GenCount); //用于存储非终结符的Select集
memset(pSelect,0,sizeof(Node*)*GenCount);
for(int i=0;i<26;i++){
if(pGramCon[i]!=NULL){
Item * pItem = pGramCon[i];
while (pItem)
{
Select(i+'A',pItem,pGramCon,pNULLKey,pFirst,pFollow,pSelect);
pItem=pItem->pItemNext;
}
}
}
return pSelect;
} - 求预测分析表主要代码
char findLeft(int num,Item**pGramCon,Item**pWenFa)//根据产生式的索引标记找到产生式左部非终结符
{
Item* pItem=NULL;
for(int i=0;i<26;i++){
if(pGramCon[i]!=NULL){
pItem=pGramCon[i];
while (pItem)
{
if(pItem->valnum==num){
sprintf((*pWenFa)->val,"%s",pItem->val);
(*pWenFa)->valnum=num;
return i+'A';
}
pItem=pItem->pItemNext;
}
}
}
}
bool InsertGramCon(Item* pT,Item**pItem) //把终结符插入预测分析表中
{
if(*pItem == NULL)
{
*pItem = pT;
return true;
}
Item* pMark = *pItem;
while( strcmp(pMark->val,pT->val) && pMark->pItemNext)
{
pMark = pMark->pItemNext;
}
if(strcmp(pMark->val,pT->val))
{
free(pT);
pT = NULL;
return false;
}
pMark->pItemNext = pT;
return true;
}
Item*(*GetPredictTable(Item**pGramCon,Node**pSelect))[128]//表:26行为非终结符,128列为终结符,返回预测分析表指针
{
Node*pNode=NULL;
Item*(*pPredictTable)[128] = (Item* (*)[128])malloc(sizeof(Item*)*26*128);
memset(pPredictTable,0,sizeof(Item*)*26*128);
for(int i=0;i<GenCount;i++){//对Select集合遍历找出终结符
pNode = pSelect[i];
while (pNode)
{
Item*pItem = (Item*)malloc(sizeof(Item));
pItem->pItemNext=NULL;
char ch = findLeft(i,pGramCon,&pItem);//返回左部非终结符,以及将pItem赋值初始化
//printf("%c %c ->%s\n",ch,pNode->m_key,pItem->val);
InsertGramCon(pItem,&pPredictTable[ch-'A'][pNode->m_key]);
Item*ppp = pPredictTable[ch-'A'][pNode->m_key];
pNode=pNode->pNext;
}
}
return pPredictTable;
} - 判断文法是否为LL(1)文法
bool JudgePredictTable(Item*(*pPredictTable)[128])
{
bool isLL1=true;
for(int i=0;i<26;i++){//遍历预测分析表
for(int j=0;j<128;j++){
if(pPredictTable[i][j]!=NULL){
Item*pItem=pPredictTable[i][j];
if(pItem->pItemNext!=NULL){//如果预测分析表某一位置包含两个文法产生式可以抵达
//则不是LL(1)文法
isLL1=false;
break;
}
}
}
}
return isLL1;
}