编译原理之LL(1)语法分析实验(附完整C/C++代码与测试)

bb0a72383ced421f80c3cafd1ad0ef58.png

一、实验内容与要求

  • 先从键盘读入要分析的文法,由程序自动构造FIRST、FOLLOW 集以及SELECT集合,判断是否为LL (1)文法。

分析文法为G[E]:

(0)E→ TE’  

(1)E’→ +TE’

(2)E’→ε   

(3)T→ FT’

(4)T’→ *FT’  

(5)T’→ε    

(6)F→ (E)  

 (7)F→a

  • 若符合LL (1)文法,由程序自动构造LL (1)分析表;
  • 由算法判断给定的输入符号串a*(a+a)是否为该文法的句型。

二、实验代码

#include<iostream>
#include<stdio.h>
#include<vector>
#include<string>
#include<stack>
#include<map>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include <bits/ios_base.h>
using namespace std;
map<char,int>getnum;
vector<char>getzf;  
vector<string>proce(10);
vector<string>first(20);
vector<string>follow(20);
int table[100][100];      //预测分析表
int num;
int numv;//终结符的数量-1
char j[2];//读取终结符、非终结符与产生式 
void  read()
{char c;int i=0;int n=0;cout<<"******************************LL(1)语法分析****************************"<<endl; cout<<"注意:E'用H代替,T‘用J代替,空串用@代替 "<<endl;cout<<"请输入产生式的集合(空用'@'表示),输入一条产生式后换行,最后以'end'结束:"<<endl;string ss;string dd;int j=0;int y=0;while(cin>>ss&&ss!="end"){		 dd.clear();dd+=ss[0];proce[j]+=dd;for(i=3;i<ss.length();i++){	if(ss[i]!='|'){dd.clear();dd+=ss[i];proce[j]+=dd;} else{dd.clear();dd+=ss[0];dd+=ss[++i];proce[++j]+=dd;}	}j++;
}
getnum['#']=0;//#代表结束标志 
//	getzf[0]='#';没有定义数组大小的时候这样输入是错误的 
getzf.push_back('#');
//终结符压栈 
for(int i=0;i<proce.size();i++)
{for(int k=0;k<proce[i].length();k++){if(proce[i][k]!='-'&&proce[i][k]!='|'){if(proce[i][k]<64||proce[i][k]>90){for( y=0;y<getzf.size();y++){if(proce[i][k]==getzf[y]) break;}if(y==getzf.size()&&k!=2){//这里让k!=2是不让第三位置的>进去 getnum[proce[i][k]]=++n;getzf.push_back(proce[i][k]);}}}	  }
}getnum['@']=++n;numv=n;//终结符的数量等于当前n的值 getzf.push_back('@');//非终结符压栈 for(int i=0;i<proce.size();i++){for(int k=0;k<proce[i].length();k++){if(proce[i][k]!='-'&&proce[i][k]!='|'&&proce[i][k]!='>'){if(proce[i][k]>64&&proce[i][k]<91){for( y=0;y<getzf.size();y++){if(proce[i][k]==getzf[y]) break;}if(y==getzf.size()){getnum[proce[i][k]]=++n;num=n;//终结符加非终结符的数量等于当前i的值 getzf.push_back(proce[i][k]);}}}	  }}
}//给终结符的first数组赋值
void get_firstT()
{int i;//不能在下面int //先给终结符的first数组赋值for( i=1;i<=numv;i++) {itoa(i,j,10);first[i]=j;//之前写的是first[i].push_back(j[0])是错的,字符串数组的输入不需要加下标,且如果是j[0]一个字符不能装到一个字符串当中去 }
}//给非终结符的first数组赋值
string get_firstF(int *a)
{//犯了一个错误,下面的a没有加* for(int i=0;i<proce.size();i++){if(getnum[proce[i][0]]==*a){if(getnum[proce[i][1]]<=numv){itoa(getnum[proce[i][1]],j,10); first[*a]+=j;}else{ //first[getnum[proce[i][0]]].clear();first[getnum[proce[i][0]]]=get_firstF(&getnum[proce[i][1]]);}} 	}return first[*a]; 
} //求follow集 
void  get_follow(int *a){
//犯了一个错误,以stirng开头但是没有返回值 int i,j1;int flag=0;for(i=0;i<proce.size();i++){for(j1=1;j1<proce[i].length();j1++){if(getnum[proce[i][j1]]==*a)//这地方应该是j1我写成了k {if(j1==proce[i].length()-1){if(getnum[proce[i][j1]]!=getnum[proce[i][0]])follow[*a]+=follow[getnum[proce[i][0]]]; }else {if(getnum[proce[i][j1+1]]<=numv)	{itoa(getnum[proce[i][j1+1]],j,10);follow[*a]+=j;}else{for(int jj=0;jj<first[getnum[proce[i][j1+1]]].size();jj++) {if(atoi(&first[getnum[proce[i][j1+1]]][jj])==numv)//等于空时follow[*a]+=follow[getnum[proce[i][0]]];elsefollow[*a]+=first[getnum[proce[i][j1+1]]][jj];}flag=1;//标志位,如果已经找到*a后面的非终结符就可以停止了 }}}} if(flag==1) break; //停止寻找 }
}//求预测分析表
void get_table()          
{memset(table,-1,sizeof(table));//刚开始tableM没有初始化,导致本该是空格的地方出现E->TA for(int i=0;i<proce.size();i++)   //扫所有产生式{if(proce[i][1]=='@')     //直接推出空字的,特判下(follow集=产生式左边的vn中元素填){string flw=follow[getnum[proce[i][0]]];for(int k=0;k<flw.size();k++){table[getnum[proce[i][0]]][flw[k]-'0']=i;}}string temps=first[getnum[proce[i][1]]];for(int j=0;j<temps.size();j++)               //考察first集{if(atoi(&temps[j])!=numv) {// table[getnum[proce[i][0]]][atoi(&temps[j])]=i;//atoi不能这么用,他表示的是从当前位一直到末位 table[getnum[proce[i][0]]][temps[j]-'0']=i;}else                                     //有空字的,考察follw集{string flw=follow[getnum[proce[i][1]]];for(int k=0;k<flw.size();k++){table[getnum[proce[i][0]]][flw[k]-'0']=i;}}}}
}//由对应下标获得对应产生式
string get_proce(int i) 
{if(i<0)return " ";    //无该产生式string ss;ss+=proce[i][0];ss+="->";		//把->要加上 for(int j=1;j<proce[i].size();j++)ss+=proce[i][j];return ss;
}//输出预测分析表 
void print_table()
{cout<<"该文法对应的预测分析表如下:"<<endl;cout<<"________________________________________________________"<<endl;for(int i=0;i<numv;i++)cout<<'\t'<<getzf[i];cout<<endl;for(int i=numv+1;i<=num;i++){cout<<endl<<"________________________________________________________"<<endl;cout<<getzf[i];for(int j=0;j<numv;j++){cout<<'\t'<<get_proce(table[i][j])<<"";}}cout<<endl<<"________________________________________________________"<<endl;cout<<endl;
}//打印栈中元素
void print_stack(stack<char>sta){int length=sta.size();//栈中元素数目vector<char>temp;for(int i=0;i<length;i++){//cout<<sta.top();temp.push_back(sta.top());sta.pop();} for(int j=length-1;j>=0;j--){cout<<temp[j];}cout<<"\t\t";
} //打印判定串的当前元素
void print_string(string str,int index){int length=str.size();for(int i=index;i<length;i++){cout<<str[i];}cout<<'#';cout<<"\t\t";
}//分析word符号串的合法性(关键)
string word;
bool analyze()       
{stack<char>sta;//分析栈 sta.push('#');  //#最先进栈 sta.push(proce[0][0]);//将开始符压入分析栈中进行初始化 int i=0;int count=1;//步骤计数  printf("步骤\t\t分析栈\t\t判定串\t\t使用规则\n");//表头 while(!sta.empty()){cout<<count<<"\t\t";print_stack(sta);//打印当前分析栈 print_string(word,i);//打印当前判定串 int cur=sta.top();//取出分析栈的栈顶元素 sta.pop();if(cur==word[i])       //如果分析栈的栈顶元素与判定串的第一个元素相同(即是终结符的话),则匹配成功,分析栈弹出栈顶元素 {i++;printf("匹配出栈\n");}else  if(cur=='#')   //如果分析栈的栈顶元素为#则表面分析结束{return 1;}else  if(table[getnum[cur]][getnum[word[i]]]!=-1) //查找对应的预测分析表 {int k=table[getnum[cur]][getnum[word[i]]];cout<<proce[k][0]<<"->";for(int j=1;j<proce[k].size();j++)cout<<proce[k][j];cout<<endl;//将使用的产生式逆序加入分析栈中,以取代上一个出栈的元素 for(int j=proce[k].size()-1;j>0;j--)  {  if(proce[k][j]!='@')sta.push(proce[k][j]);}}else     {return 0;}count++;}return 1;
}//输入待判断的符号串 
void scanf()  
{cout<<"请输入待判定的符号串:";cin>>word;cout<<"判断分析表如下:"<<endl; if(analyze())cout<<endl<<"综上:该符号串有效,是该文法的句型!"<<endl;else   cout<<endl<<"出错,该符号串不是该文法的句型!"<<endl;
}int main()
{int k;int j;read();//终结符的first集 get_firstT();//非终结符的first集 for(k=numv+1;k<=num;k++) //犯了一个错误,numv的位置写成7 {get_firstF(&k);}follow[numv+1]+='0'; //这地方加的是0而不是# for(k=numv+1;k<=num;k++) //犯了一个错误,numv的位置写成7 {get_follow(&k);}cout<<endl;  get_table();print_table();scanf();	return 0;
}
/*测试数据: 
注意:E'用H代替,T‘用J代替,空串用@代替 
E->TH
H->+TH
H->@
T->FJ
J->*FJ
J->@
F->(E)
F->a
end
*/

三、实验结果

87d6fee48cbc499c8891fd72cefb43c5.png

c35605fdd63f43abbb3037cb6c947d11.pngEND.

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/164316.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件开发王者搭配:80%低代码+20%高代码

数字化领域从来不缺新概念&#xff0c;前两年市场大谈云原生、技术中台、业务中台等概念&#xff0c;企业更多聚焦在业务与IT架构的升级。而这两年&#xff0c;随着低代码、生成式AI的盛行&#xff0c;大家则开始挖掘数字化应用的低成本建设模式。 在过去&#xff0c;开发一套系…

Linux 是否被过誉了?

Linux 是否被过誉了&#xff1f; 有些人眼里&#xff0c;电脑这种东西就应该是华丽丽的桌面&#xff0c;手握鼠标戳戳按钮&#xff0c;键盘只为偶尔打打字&#xff0c;仿佛windows式的桌面形式才是理所应当&#xff0c;GUI才是理所应当&#xff0c;x86才是理所应当&#xff0c…

使用 NVProf 检测 CUDA kernel 的 bank conflict

使用 NVProf 检测 CUDA kernel 的 bank conflict NVProf 指令 使用 NVProf 可以对 bank conflict 进行检测: nvprof --events shared_ld_bank_conflict,shared_st_bank_conflict <app> [args...]其中: --events 选项指定的 shared_ld_bank_conflict,shared_st_bank_c…

python -opencv 中值滤波 ,均值滤波,高斯滤波实战

python -opencv 中值滤波 &#xff0c;均值滤波&#xff0c;高斯滤波实战 cv2.blur-均值滤波 cv2.medianBlur-中值滤波 cv2.GaussianBlur-高斯滤波 直接看代码吧&#xff0c;代码很简单&#xff1a; import copy import math import matplotlib.pyplot as plt import matp…

c++的更严格的类型转换要求

C有更严格的类型转换要求 C中对类型转换有严格的要求&#xff0c;需要的类型和给的类型不 一致时可能会编译报错 例如&#xff1a; C语言中 #include<stdio.h> #include<stdlib.h> //全局变量 //C语言中的函数的形参的类型可以不写&#xff0c;没有返回值可以返回&…

联发科正在改写全球高端手机芯片市场格局

全球高端手机芯片市场正在重塑。 11 月 21 日&#xff0c;联发科发布了新一代卓越 5G 生成式 AI 移动芯片天玑 8300。 这款定位于中端机档位的芯片&#xff0c;无论在技术架构还是在实际性能表现上&#xff0c;都实现了对前代旗舰芯片的赶超&#xff0c;彻底打破了业内长期存…

相机和滤镜应用程序Nevercenter CameraBag Photo mac软件特点说明

Nevercenter CameraBag Photo mac是一款相机和滤镜应用程序&#xff0c;它提供了一系列先进的滤镜、调整工具和预设&#xff0c;可以帮助用户快速地优化和编辑照片。 Nevercenter CameraBag Photo mac软件特点 1. 滤镜&#xff1a;Nevercenter CameraBag Photo提供了超过200种…

复费率电表和预付费电表有哪些区别?

随着科技的发展和能源管理的日益严格&#xff0c;电表技术也在不断更新换代。复费率电表和预付费电表作为两种主流的智能电表&#xff0c;各自具有独特的优势和应用场景。接下来&#xff0c;小编来为大家详细解析这两种电表的区别及其应用场景。 一、复费率电表 1.定义及工作原…

计算机精度导致各种误差,大数吃小数

如果 p ∗ p^* p∗是p的近似, ∣ p ∗ − p ∣ |p^*-p| ∣p∗−p∣是绝对误差, ∣ p ∗ − p ∣ / ∣ p ∣ |p^*-p|/|p| ∣p∗−p∣/∣p∣是相对误差 舍入误差,就是数据表示精度不足带来的误差 a0.1234564≈0.123456fl(a) b0.1234546≈0.123455fl(b) 在上面发生了舍入误差 f…

力扣labuladong一刷day15天K个一组翻转链表与回文链表

力扣labuladong一刷day15天K个一组翻转链表与回文链表 一、25. K 个一组翻转链表 题目链接&#xff1a;https://leetcode.cn/problems/reverse-nodes-in-k-group/ 思路&#xff1a;k个一组翻转链表&#xff0c;每k个翻转抽取出一个单独的方法reverse&#xff0c;翻转a到b&…

力扣刷题第二十九天--二叉树

前言 问问自己&#xff0c;刷题的效果真的达到了吗&#xff1f; 内容 一、翻转二叉树 226.翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 递归 func invertTree(root *TreeNode) *TreeNode {if rootnil{return root}…

Vue中的$nextTick的作用

在 Vue 中&#xff0c;当某些数据发生变化时&#xff0c;DOM 并不会立即更新。相反&#xff0c;Vue 会在下一个事件循环周期&#xff08;microtask&#xff09;中异步执行更新&#xff0c;这样可以避免频繁的 DOM 操作。然而&#xff0c;有时候我们需要在 DOM 更新后执行一些操…

2024-NeuDS-数据库题目集

一.判断题 1.在数据库中产生数据不一致的根本原因是冗余。T 解析&#xff1a;数据冗余是数据库中产生数据不一致的根本原因&#xff0c;因为当同一数据存储在多个位置时&#xff0c;如果其中一个位置的数据被修改&#xff0c;其他位置的数据就不一致了。因此&#xff0c;在数据…

11.docker的网络-docker0的理解及bridge网桥模式的介绍与实例

1.docker0的基本理解 安装完docker服务后&#xff0c;我们首先查看一下宿主机的网络配置 ifconfig我们可以看到&#xff0c;docker服务会默认在宿主机上创建一个虚拟网桥docker0&#xff0c;该网桥网络的名字称为docker0。它在内核层连通了其他物理或者虚拟网卡&#xff0c;这…

ubuntu22.04系统下载程序和依赖,并拷贝到指定路径下

脚本1 apt install aptitude apt-get -d install xxx #xxx是待下载的安装包 mv /var/cache/apt/archives/* /home/tuners/1apt install aptitude apt-get -d install xxx mv /var/cache/apt/archives/*.deb /home/tuners/1 xxx 为程序包名称 /home/tuners/1为保存程序包的…

从零开始的搭建指南:开发高效的抖音预约服务小程序

预约服务小程序提高了效率&#xff0c;节省了用户时间。下文&#xff0c;小编将与大家一同探讨如何从零开始打造预约服务小程序。 第一步&#xff1a;明确需求和目标 确定你的小程序主要服务领域是什么&#xff1f;是医疗预约、美容美发、餐厅预订还是其他行业&#xff1f;明…

Python 如何开发出RESTful Web接口,DRF框架助力灵活实现!

Django Rest Framework&#xff08;DRF&#xff09;是构建强大且灵活的Web API的优秀工具。它基于Django&#xff0c;提供了一套用于构建Web API的组件和工具&#xff0c;简化了API开发过程&#xff0c;同时保留了Django的优雅和强大。 一、Web应用模式 在开发Web应用时&…

Android组件化搭建学习

什么是组件化&#xff1f; 为什么要用组件化&#xff1f;在项目的开发过程中&#xff0c;随着开发人员的增多及功能的增加&#xff0c;如果提前没有使用合理的开发架构&#xff0c;那么代码会越来臃肿&#xff0c;功能间代码耦合也会越来越严重&#xff0c;这时候为了保证项目…

C# 忽略大小写

在 C# 中&#xff0c;你可以通过以下几种方式来忽略大小写&#xff1a; 使用 ToLower 或 ToUpper 方法将字符串转换为全小写或全大写&#xff0c;然后进行比较。使用 Compare 或 CompareOrdinal 方法&#xff0c;并传入正确的 StringComparer 实例以指示比较应该忽略大小写。使…

Android 开发Java调用Kotlin提示包不存在

在kotlin代码所在module的build.gradle设置 plugins {id org.jetbrains.kotlin.android }