- 实验名称
LL(1)分析技术识别字符串
- 实验目的
- 掌握自顶向下语法分析方法的原理
- 编程完成预测分析法
- 实验内容和要求
用LL(1)分析技术对输入串进行句型分析
- 实验环境
VS 2022
- 算法设计思想
主函数由两个函数语句和一个简单的用户交互组成。LL_creat函数负责接收非终结符集和终结符集以及LL表的元素,然后创建LL表。接下来进行分析,分析函数为Analysis函数。
分析过程首先创建一个char类栈,通过for循环,对每个待分析字符进行分析,每次分析涉及多个分支,分支判断用到vn_vt_or_no()函数,是#号返回标记3,是终结符返回标记,是非终结符返回标记2,错误返回标记0,通过对不同的返回值,做相应的处理。详解都在附件代码里,自请看注释。
- 主要问题与解决方法
问题:对字符栈path栈顶元素的处理
解决:不同的元素类型做不同的标记,是#号返回标记3,是终结符返回标记,是非终结符返回标记2,错误返回标记0。返回不同的标记供分析时做不同的处理。
- 实验结果
匹配成功与失败:
- 体会、质疑、建议
代码中的奥妙,其乐无穷!!!
- 源代码
#include<iostream>
#include<iomanip>
#include<stack>
#define N 100
#include<vector>
#define error() {cout << endl<<"Sorry!!! The string can not be analysed.Try it again!" << endl;return;}
using namespace std;
/***************************************定义LL(1)表*******************************************/
struct LL
{
string production;//利用结构体,节点里存储一个string字符串production
}LL[N][N];
/***************************************函数声明**********************************************/
void LL_craet();
int vn_vt_or_no(char elem);
void Printf_path(stack<char>path, string& p);
void Analysis();
/***************************************定义全局字符串****************************************/
string VN;
string VT;
/***************************************函数模块区********************************************/
int main()
{
/***********************************主函数实现********************************************/
int choice = 0;
LL_craet();
Analysis();
while (1) {
cout << "1.Try to analyse new string/2.Exit" << endl << "Press which:";
cin >> choice;
if (choice == 1)Analysis();
else exit(1);
}
return 0;
}
int vn_vt_or_no(char elem)
{
/*********************判断栈顶元素属于非终结符还是终结符还是#号***************************************/
if (elem == '#')return 3; //是#号返回标记3
if (VT.find(elem) < VT.size())return 1; //是终结符返回标记1
if (VN.find(elem) < VN.size())return 2; //是非终结符返回标记2
return 0; //错误返回标记0
}
void Printf_path(stack<char>path, string& p)
{
/*********************栈无法直接输出,通过处理传引用复制到字符串,用string类输出**********************/
p.clear(); //第一步将p清空,才能存东西
while (!path.empty()) { //path.empty()判断栈是否为空,empty是bool类型,空返回true,非空返回false
p.insert(0, 1, path.top()); //利用插入方式,达到将栈元素存至p中,p.insert(0,1,path.top())表示,“0”位置,插入“1”个“path.top()”元素
path.pop(); //插完之后栈顶移除一个元素
}
}
void LL_craet()
{
/******************************************终结和非终结字符输入***************************************/
cout << "Please input the VN:" << endl;
cin >> VN;
cout << "Please input the VT:" << endl;
cin >> VT;
VT.push_back('#'); //终结符需加入#号
/******************************************LL(1)文法输入*********************************************/
cout << "Please input LL rule:" << endl;
for (int i = 0;i < VN.size();i++)
for (int j = 0;j < VT.size();j++)
cin >> LL[i][j].production;
/******************************************LL(1)文法输出*********************************************/
cout << "The LL(1) analyse table is as followed:" << endl << ' ';
for (auto i : VT)cout << right << setw(10) << i;// 先将第一行终结符输出
cout << endl;
for (int i = 0;i < VN.size();i++) { //而后逐行输出首位非终结符和LL表
cout << VN.at(i);
for (int j = 0;j < VT.size();j++) {
if (LL[i][j].production == "-")cout << right << setw(11) << "ε";
else cout << right << setw(10) << LL[i][j].production;
}
cout << endl;
}
}
void Analysis()
{
stack<char> path; //创建栈
string str, production; //创建字符串str,production
/********************************读取要分析的字符串*************************************************/
cout << "Please input the string ready to be analysed:" << endl;
cin >> str;
str.push_back('#'); //待分析字符串末尾加上#号
/******************************入栈初始化***********************************************************/
path.push('#');
path.push(VN.front());
/*******************************LL(1)分析过程输出***************************************************/
cout << "The analysis process is as follows:" << endl;
cout << left << setw(14) << "步骤" << setw(16) << "分析栈" << setw(20) << "剩余字符串" << "所用产生式" << endl;
for (int step = 1;;step++) { //通过for循环
Printf_path(path, production); //利用此函数处理,得到production,以此将栈输出
cout << left << setw(14) << step << setw(16) << production << right << setw(10) << str << setw(10) << ' ';
switch (vn_vt_or_no(path.top())) { //分支,按返回,做相应处理,返回值看vn_vt_or_no函数内部分析
case 0: //返回0,意味着出错,直接退出系统
cout << endl << "Wrong!!!" << endl;
exit(1);
case 1: //返回1,path.top()栈顶元素为终结符
if (path.top() == str.front()) { //如果栈顶元素path.top()和待分析字符串首字符str.front()相等
cout << path.top() << "匹配" << endl; //相等即成功匹配
str.erase(0, 1); //匹配已成功,移除待分析字符串首元素,str.erase(0,1)意思是删除str字符串“0”位置开始往后“1”个元素
path.pop(); //同样,匹配成功之后,栈也要移除栈顶元素
}
else error() //不相等则报错,并且return,退出当前Analysis函数
break;
case 2: //返回2,path.top()栈顶元素为非终结符
if (VT.find(str.front()) > VT.size()) error() //如果待分析字符出错,则报错
production = LL[VN.find(path.top())][VT.find(str.front())].production;//取VN.find(path.top())行,VT.find(str.front())列,的LL[][]表中的字符串,赋值
if (production == "-") { //如果该字符串为“-”,则是产生式推空
cout << path.top() << "->ε" << endl; //手动输出ε
path.pop(); //分析完,移除栈顶元素
}
else if (production == "NULL") error() //如果该字符串是NULL,意味着LL表中无该产生式,同样报错处理,并退出Analysis函数
else
{ //如果前两种情况都不是,则意味着正确,那么后面步骤即输出“分析产生式”即可
cout << path.top() << "->" << production << endl; //输出当前栈顶元素(因为当前正在分析该栈顶元素),然后->,然后production字符串,即产生式后半部分
path.pop(); //同样分析完之后,移除栈顶元素
for (string::iterator i = production.end();i != production.begin();)path.push(*(--i));//移除完栈顶元素之后,需要将刚才产生式后半部分入栈,这里用到迭代器
}
break;
case 3: //返回3,path.top()栈顶元素为#号,意味着即将分析结束
if (path.top() == str.front()) { //此时栈顶元素已经是#号
cout << "接受" << endl; //成功接收
cout << endl << "Congratulation!!! The string is analysed successfully!" << endl;
return; //接收完之后,return退出Analysis函数,进行后续操作
}
else error() //如果不等于str.front(),则意味着str.front()不是#号,即待分析字符串还没分析完,出错
break;
}
}
}
/*
* 案例一
SABC
abde
aA
bB
NULL
NULL
NULL
SBe
SBe
-
-
-
NULL
NULL
dC
e
NULL
NULL
bC
-
-
-
abede
* 案例二
STRD
abde
RT
RT
RT
eT
RT
DR
DR
NULL
NULL
-
-
-
dR
NULL
-
a
bd
NULL
NULL
NULL
ddbdd
*/